LangChain (JS / TS)
The official @safeprompt.dev/langchain package is a LangChain callback handler. Add it to any chain or agent and every prompt is screened before it reaches your LLM. With agents it also screens tool outputs, which is where indirect prompt injection hides.
Install
npm install @safeprompt.dev/langchainPeer dependency: @langchain/core ^0.3.0. You will also need a SafePrompt API key from the dashboard.
Quick Start
Pass a SafePromptCallbackHandler in the callbacks array of any chain. It fires before each LLM call and throws SafePromptBlockedError when a prompt is unsafe, so you handle it exactly like any other error from chain.call().
import { LLMChain } from 'langchain/chains';
import { ChatOpenAI } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';
import {
SafePromptCallbackHandler,
SafePromptBlockedError,
} from '@safeprompt.dev/langchain';
const chain = new LLMChain({
llm: new ChatOpenAI({ model: 'gpt-4o-mini' }),
prompt: PromptTemplate.fromTemplate('Answer: {input}'),
callbacks: [
new SafePromptCallbackHandler({
apiKey: process.env.SAFEPROMPT_API_KEY!,
userIP: req.ip, // end-user IP from your web framework
}),
],
});
try {
const { text } = await chain.call({ input: userInput });
return res.json({ text });
} catch (err) {
if (err instanceof SafePromptBlockedError) {
return res.status(400).json({
error: 'Prompt blocked for safety',
threats: err.result.threats,
});
}
throw err;
}Configuration
new SafePromptCallbackHandler({
apiKey: process.env.SAFEPROMPT_API_KEY!,
userIP: '203.0.113.1', // REQUIRED — end-user IP
provider: 'https://api.safeprompt.dev', // default
mode: 'balanced', // 'fast' | 'balanced' | 'strict'
enforcement: 'block', // 'block' | 'log'
onProviderError: 'fail-closed', // 'fail-closed' | 'fail-open'
sampleRate: 1.0, // 0..1 — fraction of prompts to screen
onBlock: (prompt, result) => {
console.warn('[safeprompt] blocked', result.threats);
},
onError: (prompt, err) => {
console.error('[safeprompt] provider error', err.message);
},
});onProviderError decides what happens if SafePrompt itself is unreachable: fail-closed blocks the request, fail-open lets it through and fires onError. Choose deliberately for your risk profile.
Roll Out in Log Mode First
Set enforcement: 'log' to run the handler without ever aborting a chain. You still get onBlock events for anything it would have blocked, so you can watch real traffic in your logs or the SafePrompt dashboard, tune custom lists and sensitivity, then flip to enforcement: 'block' once you are confident.
new SafePromptCallbackHandler({
apiKey: process.env.SAFEPROMPT_API_KEY!,
userIP: req.ip,
enforcement: 'log', // observe only — never throws
onBlock: (prompt, result) => {
logger.warn({ threats: result.threats }, 'would block');
},
});Agents & Indirect Injection
When you attach the handler to a LangChain agent, it also screens tool outputsthe moment a tool returns content that will be fed back to the LLM. This is the key defense against indirect prompt injection: instructions hidden inside a web page, a retrieved document, or an API response that the agent is about to read. Direct user input and untrusted tool output are both screened by the same handler, no extra wiring.
import { AgentExecutor, createReactAgent } from 'langchain/agents';
import { SafePromptCallbackHandler } from '@safeprompt.dev/langchain';
const executor = new AgentExecutor({
agent: await createReactAgent({ llm, tools, prompt }),
tools,
callbacks: [
new SafePromptCallbackHandler({
apiKey: process.env.SAFEPROMPT_API_KEY!,
userIP: req.ip,
}),
],
});