Prerequisites
Install and initialize the TypeScript SDK or Python SDK. The examples use that client asfw. 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
- TypeScript
- Python
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.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. Passhooks 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, setparallel so input and tool-result checks run beside the
model call instead of ahead of it.
- TypeScript
- Python
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 theconversationId (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.