Built against OWASP LLM01Benchmark suite v2.0 · 150 promptsOpen-source SDK · MIT

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/langchain

Peer 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,
    }),
  ],
});

Next Steps