Skip to main content
LangChain is an open-source Python framework for building applications with language models. Its create_agent API connects a model to a set of tools and runs the agent loop until the model produces an answer. The langchain-neuraltrust middleware evaluates messages and tool activity inside that loop. Because it runs in the application, it can block a tool call before execution.

Integration capabilities

Pass the agent’s tool list to the middleware to include tool declarations in the evaluation payload. This allows tool-poisoning rules to inspect tool names, descriptions, and schemas.

Before you start

Create the policy in Observe mode. Observe records decisions in Activity without enforcing them. Review the results, then switch the policy to Enforce. See Policies.

1. Install

The package is langchain-neuraltrust. It does not require changes to the model provider configuration.

2. Add the middleware

Load credentials from the environment instead of placing them in the constructor:
Pass your tool list as payload_tools, not tools. LangChain defines AgentMiddleware.tools as additional tools registered by the middleware, so setting it changes the agent’s tool set. payload_tools adds the schemas only to the input evaluation payload.
Other settings have defaults and environment-variable fallbacks. See Configuration.

3. Choose what gets evaluated

Four independent flags map to hooks in the agent loop: check_tool_results is skipped when check_input is also on because the conversation payload already contains those tool messages. This avoids evaluating the same text twice. Enable check_tool_calls when a policy must stop a tool call before it runs. It adds one evaluation round trip per tool call. Both sync (invoke) and async (ainvoke) paths are implemented for every hook.

4. Verify

With a jailbreak rule in Enforce mode, the final message is the blocked AIMessage. The model is not called, and on_violation prints the trace_id. Use it to match the run to the finding in Activity. Call close() after invoke, or await aclose() after ainvoke, when the middleware owns its HTTP clients.

Reference

Coverage

Ask. The middleware does not implement an ask flow. It treats ask as an unknown verdict and fails closed, so the result is a block. Implement any human approval step in the application. Limits. The middleware inspects only the configured agent. A service that calls the provider directly bypasses it. On streaming routes, output evaluation occurs after delivery. See Streaming. Detectors evaluate text and tool calls, but non-text content parts are not stripped from the payload: a message whose content is a list of blocks is forwarded to /v1/evaluate as-is, so the image data available to the agent is also transmitted for evaluation. Non-text parts cannot be rewritten. Tool-call redaction requires check_tool_calls=True. A compatible transform verdict replaces the pending tool arguments before dispatch; it does not rename the tool. Each guarded stage adds a round trip, and the hooks run on every pass through the agent loop. With check_input, check_output, and check_tool_calls enabled, a turn that calls two tools in parallel and then answers makes six evaluation calls: input and output on each model pass, plus one per tool call. Sequential tool calls add further evaluations. Set timeout to limit the effect of a stalled request.

What is evaluated

Every stage calls POST /v1/evaluate with the collector key, and the policy’s detectors decide the verdict.

Verdicts

The middleware applies each verdict to agent state as follows: At message stages, preserving the ID ensures that a transformed message replaces the original in the thread instead of being appended to it. exit_behavior decides what a block does:
On the input stage, replace overwrites every non-system message in the conversation, including earlier turns. By comparison, end drops only the current turn. A checkpointer persists the overwrite. On the output stage, the span is the last AI message. On tool results, it is the originating AI message and its ToolMessages.
These behaviors do not remove SystemMessages. Under end and replace, the middleware also keeps the thread structurally valid. It clears tool_calls from a blocked AIMessage and converts a blocked ToolMessage so it does not leave an orphaned tool response. Otherwise, the next model call would fail because the thread contains a tool call without a corresponding response. On the tool path a block returns a ToolMessage(status="error") and the tool is never called. A transform dispatches the tool with the rewritten arguments. on_violation is synchronous and fires from both invoke and ainvoke. Exceptions from the callback propagate unchanged, so handle callback failures in your application if they should not interrupt the agent.

Configuration

Every setting falls back to an environment variable: When session_id is unset, the middleware uses the LangGraph thread_id. This groups turns in Activity by the same conversation identifier as the application. Fail-open or fail-closed. unreachable_fallback applies only when TrustGuard cannot be reached because of a connection error, timeout, HTTP 502/504 response, or HTTP 429 response after retries are exhausted. Those are retried with backoff, honoring Retry-After, before the fallback applies. The following cases fail closed even when fail_open is configured:
  • HTTP 401/403, and 503 entitlement failures
  • any other 4xx/5xx
  • a non-JSON 200, or an unknown verdict, including ask
  • a transformed_payload that cannot be applied safely
  • TLS failures. An expired or untrusted certificate is a connect error, but the middleware treats it as a configuration fault rather than an outage, so fail_open never covers it. A corporate MITM proxy without a trusted CA will therefore block every turn even with fail_open set.
The middleware refuses a transformed payload with a different message count, changed role, rewritten tool name or ID, or injected non-text content block. It fails closed instead of applying a partial redaction.

Streaming

after_model runs on the assembled AIMessage, once the model call has finished.
With stream=True, tokens have already reached the client by then. An output-side block or transform therefore records the result but cannot prevent delivery. Input and tool-call checks still run before the model or tool call.
For preventive response enforcement, disable streaming or buffer the stream until a verdict is available. Buffering increases time to first token. You can also place TrustGate in front of the route to inspect the stream at the gateway.

Attributes

  • session_id: the LangGraph thread_id unless you set it
  • model_name: the runtime context model unless you set it
  • trace_id: included in every verdict and the matching finding in Activity
on_violation(verdict, stage) receives the stage name alongside the verdict, so alerting can distinguish an input finding from a tool-call finding without parsing the payload. Reconcile a run with the console on trace_id.

Troubleshooting