Skip to main content

Prerequisites

Install and initialize the TypeScript SDK or Python SDK. The examples use that client as fw. Set OPENAI_API_KEY for the OpenAI model examples. Use TypeScript SDK 0.8.2 or later or Python SDK 0.8.1 or later for automatic thread identity. SDK 0.9.1 qualifies the TypeScript integration with @langchain/core >=0.3.80 <1 or >=1.1.8. The Python langchain extra retains langchain-core >=0.3.85 <1 or >=1.3.3 <2. These are bounded compatibility floors, not general dependency-upgrade guidance. Choose Shadow or Warn explicitly for observation. These examples use the client’s effective mode. In Block mode, callbacks raise a blocking exception. See modes and enforcement.

Protect a standalone runnable

Wrap a model, tool, or other standalone runnable once. Reuse the returned runnable with the normal invocation config; invoke, batch, and streaming keep the caller’s config and callbacks.For a LangGraph application, the callback handler can also be supplied through graph invocation config:
graph and state belong to the LangGraph application. Shadow-mode overrides for this handler are on the TypeScript SDK page.
The returned protected object supports standard Runnable methods. Configure model-specific capabilities such as bindTools() or bind_tools() before wrapping the model. For Deep Agents, use the protected agent constructor so checks are installed inside its subagent graphs as well.

Expected result

From SDK 0.10.0, callback handlers enable model-start, tool-start, tool-end, and model-end checks by default; retriever hooks are opt-in. Earlier versions leave the model-end check opt-in. Pass hooks to choose a different set. The model-start check inspects the latest user message without rescanning prior tool messages, and the model-end check classifies the generated output. With streaming, the model-end check runs after tokens are emitted: a block stops the run and cannot recall them. Each classification has a distinct metadata.silmaril.request_id. The LangChain run ID is correlation metadata at metadata.langgraph.run_id. The standalone runnable wrapper captures the native configurable.thread_id from each invocation before callbacks run and sends it as metadata.conversationId, including on tool and model completion checks. LangGraph callbacks and Deep Agents middleware resolve the identity from their native invocation context. Reuse the handler across conversations. Reuse a native thread ID to continue a conversation or resume its checkpoint; use a different ID for a separate conversation. Concurrent invocations keep their own thread identities. Invocations without a native thread ID remain independent checks: the SDK omits metadata.conversationId and does not substitute a run ID or generated UUID. Your application still supplies standalone model message history; graph checkpointers manage stateful graph history.

Callback-only integrations

A bare root model invocation in Python or TypeScript may expose its configured thread ID only to tracing. A nested bare model that overrides its thread ID may expose only its parent’s ambient config to ordinary callbacks. A callback-only registration cannot recover the hidden child identity. Use the standalone runnable wrapper above when a model or tool has its own invocation config. When no native identity is available to the callback, conversation identity is omitted. Native LangGraph execution and Deep Agents middleware expose their invocation context directly.

Run checks in parallel

By default each check finishes before the model or tool it guards continues. From SDK 0.10.0, set parallel so input and tool-result checks run beside the model call instead of ahead of it.
A block that has already arrived is raised at the next callback, including the next streamed token. Callbacks cannot cancel a model call, so a late block discards that call’s output instead of stopping generation. Checks that share a native thread ID are sent in the order they started, so the conversation sequence is preserved. A failed run drops the checks it had not yet joined. In Shadow mode, callback handlers classify in the background without parallel, and onClassify or on_classify runs after the callback returns. Set failOpen: false in TypeScript or fail_open=False in Python to keep Shadow checks inline so classification errors still surface.

Migrate to SDK 0.8.0

Remove the conversationId (TypeScript) or conversation_id (Python) constructor option from LangChain handlers and Deep Agents middleware. These options are removed and rejected. Pass only your framework’s normal configurable.thread_id at invocation time; no Silmaril-specific conversation configuration is needed. For standalone runnables, replace callback-only setup with protectLangChainRunnable or protect_langchain_runnable as shown above. Direct SDK classify() metadata is unchanged. Mode resolution for callback handlers and Deep Agents middleware is an explicit handler or middleware override, then the client setting, then an optional response mode, then Block. Current classify responses omit mode, so an integration without a local override uses the Block fallback. Set Shadow or Warn explicitly for observation. In Block mode, LangChain callbacks raise a blocking exception, while Deep Agents replaces denied content as described in the Deep Agents guide.

Deep Agents

Use the Deep Agents guide to protect root agents, tools, and subagents.

Verify coverage

Verify your integration with a new model call and tool interaction. Reuse one handler across two native threads and confirm that model and tool checks keep the matching conversation identities. Review modes and enforcement before enabling blocking. LangChain documentation · LangGraph documentation