Skip to content

LangChain.js integration

The @inpolicy/langchain package integrates InPolicy into any LangChain.js chain or agent. Two flavors:

  • withInPolicy: wrap an LLM call with a single function. Simplest.
  • InPolicyCallbackHandler: a callback handler you attach to an existing chain.
Terminal window
npm install @inpolicy/langchain @inpolicy/sdk @langchain/core
import { ChatOpenAI } from '@langchain/openai';
import { InPolicyClient } from '@inpolicy/sdk';
import { withInPolicy } from '@inpolicy/langchain';
const ip = new InPolicyClient({ apiKey: process.env.INPOLICY_API_KEY! });
const llm = new ChatOpenAI({ modelName: 'gpt-4o' });
const guarded = withInPolicy(
async ({ system, user }) => {
const res = await llm.invoke([
{ role: 'system', content: system },
{ role: 'user', content: user },
]);
return typeof res.content === 'string' ? res.content : JSON.stringify(res.content);
},
{
client: ip,
sessionId: 'sess_abc',
systemPrompt: 'You are a helpful customer support agent.',
checkOutput: true, // optional; requires post_inference_checking on the key
},
);
const result = await guarded.invoke({ input: "I'm 16 — can I buy this?" });
console.log(result.output); // the LLM's response
console.log(result.preInferencePolicies); // e.g. ['Policy 4.2', 'Policy 7.1']
console.log(result.postInferenceViolations); // non-empty only when checkOutput=true and unsafe

What it does on every invocation:

  1. record_turn({ role: 'user', content: input }) → get active policies.
  2. Build system = systemPrompt + '\n\n' + injectionBlock.
  3. Call the wrapped LLM function with { system, user }.
  4. record_turn({ role: 'assistant', content: output, checkOutput }).
  5. Return output + policy metadata.

Failures in InPolicy calls are non-blocking. They log and pass through. This keeps governance from breaking your chain if InPolicy is briefly unreachable.

InPolicyCallbackHandler: drop into an existing chain

Section titled “InPolicyCallbackHandler: drop into an existing chain”

If you have a complex chain or agent already wired up, use the callback handler instead:

import { ChatOpenAI } from '@langchain/openai';
import { InPolicyClient } from '@inpolicy/sdk';
import { InPolicyCallbackHandler } from '@inpolicy/langchain';
const ip = new InPolicyClient({ apiKey: process.env.INPOLICY_API_KEY! });
const inpolicyHandler = new InPolicyCallbackHandler({
client: ip,
sessionId: 'sess_abc',
recentContextWindow: 3,
checkOutput: true,
onViolations: (violations) => {
console.warn('[InPolicy]', violations.length, 'violations in assistant output');
},
});
const llm = new ChatOpenAI({
modelName: 'gpt-4o',
callbacks: [inpolicyHandler],
});
// Use `llm` anywhere; the handler fires on every LLM call in the chain.
const result = await llm.invoke('Draft a reply to the Acme account exec.');
// After invocation, read the latest state
const latest = inpolicyHandler.getLatest();
console.log(latest?.activePolicies);

The handler hooks handleChatModelStart and handleLLMEnd to record turns around every LLM invocation. Attach to any Runnable, chain, or agent; it travels through.

Caveat: the callback handler can call record_turn but it can’t modify the system prompt that’s being sent to the LLM. If you want the injection block to actually reach the model, either use withInPolicy (recommended) or have your chain’s prompt template read the injection block from inpolicyHandler.getInjectionBlock() (called after a warm-up turn).

For an agent that calls tools, combine the callback handler with explicit checkToolCall calls in your tool definitions:

import { DynamicStructuredTool } from '@langchain/core/tools';
import { z } from 'zod';
const sendEmailTool = new DynamicStructuredTool({
name: 'send_email',
description: 'Send an email to a recipient',
schema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
func: async (args) => {
// Gate the action
const check = await ip.checkToolCall({
sessionId: 'sess_abc', // same session the handler uses
toolName: 'send_email',
arguments: args,
});
if (!check.allow) {
return `Blocked by policy: ${check.violations[0].explanation}`;
}
// Execute
return await actuallySendEmail(args);
},
});

Not yet available. Use the REST API directly via requests or httpx; shape is documented in the API reference.