@inpolicy/langchain
v0.1.1
Published
LangChain.js integration for InPolicy — drop-in policy injection and output checking for chains, agents, and runnables.
Readme
@inpolicy/langchain
LangChain.js integration for InPolicy — drop-in policy injection and output checking for chains, agents, and runnables.
Two integration patterns; pick whichever fits your chain:
withInPolicy— wrap an LLM-like function in aRunnablethat doesrecord_turnbefore + after each call.InPolicyCallbackHandler— a LangChain callback handler that attaches to any existing chain via.callbacks.
Install
npm install @inpolicy/langchain @inpolicy/sdk @langchain/core
# or
pnpm add @inpolicy/langchain @inpolicy/sdk @langchain/core@langchain/core is a peer dependency.
Pattern A — wrap an LLM with withInPolicy
Use when you control the LLM call. The wrapper handles policy injection into the system prompt and post-inference checking automatically.
import { ChatOpenAI } from '@langchain/openai';
import { withInPolicy } from '@inpolicy/langchain';
import { InPolicyClient } from '@inpolicy/sdk';
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_user_123',
systemPrompt: 'You are a helpful customer service agent.',
checkOutput: true,
},
);
const result = await guarded.invoke({ input: 'Can I share our pricing with this prospect?' });
// result.output: model response
// result.preInferencePolicies: policy citations the model saw
// result.postInferenceViolations: violations from post-inference (when checkOutput is true)
// result.traceId: for log correlationWithInPolicyOptions
| Field | Default | Notes |
|---|---|---|
| client | — | Required. An InPolicyClient instance. |
| sessionId | — | Required. Stable id for the conversation; reuse across turns. |
| systemPrompt | '' | Your base system prompt. The injectionBlock is prepended automatically. |
| endUserAttributes | — | Optional attributes (role, tier, region) for policy matching. |
| policyAreaIds | — | Optional policy area scope. |
| checkOutput | false | When true, also run post-inference check on the assistant turn. |
InPolicy call failures are logged to stderr and do not break the chain — the user always gets a response. Set checkOutput: true to surface violations on the assistant turn.
Pattern B — drop into an existing chain via InPolicyCallbackHandler
Use when you already have a chain or agent and want governance without restructuring it.
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { InPolicyCallbackHandler } from '@inpolicy/langchain';
import { InPolicyClient } from '@inpolicy/sdk';
const ip = new InPolicyClient({ apiKey: process.env.INPOLICY_API_KEY! });
const handler = new InPolicyCallbackHandler({
client: ip,
sessionId: 'sess_user_123',
checkOutput: true,
onTurn: (result) => {
console.log('Active policies:', result.activePolicies.length);
},
onViolations: (violations) => {
console.warn('Violations:', violations.map((v) => v.policyId));
},
});
const prompt = ChatPromptTemplate.fromMessages([
['system', 'You are a helpful assistant.'],
['user', '{input}'],
]);
const llm = new ChatOpenAI({ modelName: 'gpt-4o' });
const chain = prompt.pipe(llm);
await chain.invoke({ input: 'Hello' }, { callbacks: [handler] });
// At any point: get the latest injection block for the next prompt build
const injectionBlock = handler.getInjectionBlock();InPolicyCallbackHandlerOptions
| Field | Default | Notes |
|---|---|---|
| client | — | Required. |
| sessionId | — | Required. |
| recentContextWindow | 3 | Recent turns passed to record_turn as context. |
| checkOutput | false | Run post-inference check on each assistant turn. |
| onTurn | — | Hook invoked with the RecordTurnResult after each turn. |
| onViolations | — | Hook invoked when post-inference check surfaces violations. Non-blocking — return value ignored. |
| endUserAttributes | — | Optional attributes for policy matching. |
| policyAreaIds | — | Optional policy area scope. |
The handler instruments handleChatModelStart, handleLLMStart, and handleLLMEnd. It plays well with any chain that emits those events.
When to use which
withInPolicywhen you want the injection block automatically merged into the system prompt and policy state surfaced in the runnable's return value. Best for single-step LLM wrappers.InPolicyCallbackHandlerwhen you have a complex chain (RAG, agent with tools) and want governance telemetry + post-inference checks without changing the chain structure. ConsumegetInjectionBlock()yourself when building subsequent prompts.
Related packages
@inpolicy/sdk— the underlying TypeScript SDK (peer dep)@inpolicy/mcp-server— MCP server for Claude Desktop, Claude Code, Cursor@inpolicy/cli— CLI for GitOps / CI / shell
License
MIT
