Skip to main content
LiteLLM is an open-source proxy that puts one OpenAI-compatible API in front of many model providers: an application calls a single endpoint, and the proxy routes the request to whichever provider is configured for that model. It centralizes provider keys, spend, and routing. The custom guardrail evaluates requests and responses that pass through the proxy. Traffic sent directly to a provider bypasses this integration. It also does not see local agent actions, such as shell commands or MCP tools invoked on a developer machine. For those, use a client integration such as Cursor or Claude Code.

Integration capabilities

The proxy can also carry the conversation, so policy applies across turns rather than to one message at a time. That requires the caller to send a session key — see Multi-turn conversations.

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.

Set up the custom guardrail file

The guardrail uses the httpx client included with LiteLLM, so it requires no additional dependency. The supported enforcement actions are Monitor and Block. This integration does not support redaction.

Mount it and declare it

trustguard_guardrail.TrustGuard is resolved relative to the directory the proxy runs from, so the file has to sit next to your config.yaml, which is /app in the official image. Mount it read-only as a volume, ship it as a ConfigMap with subPath, or bake it into your image. Then declare it in config.yaml:
Here TRUSTGUARD_API_BASE is the full endpoint, {TRUSTGUARD_BASE_URL}/v1/evaluate, not only the host.

Multi-turn conversations

A multi-turn attack spreads its intent across several messages. Each one reads as harmless on its own; the escalation, the reinforcement, or the reassembled instruction exists only across turns. See multi-turn attacks for the techniques involved — Crescendo, Echo Chamber, Multi-Turn Manipulation, and Payload Splitting. Their common target is a filter that inspects each message independently, which is what this guardrail does until you give it the conversation. Two inputs give TrustGuard the conversation, and they are complementary rather than alternatives: Neither happens by default. /v1/evaluate synthesizes a session_id when the field is omitted, so a proxy whose callers send no conversation key produces one session per request. Nothing errors and no finding goes missing, so the gap is only visible as single-turn conversations in Activity.

Send a session ID

LiteLLM does not invent a conversation key. The caller supplies one on every request of the conversation, and the proxy makes it available to the guardrail before the pre_call hook runs. A header always overrides a body value, so audit whatever sits in front of the proxy before telling application teams to use the body form. With the OpenAI SDK, send the header on every call of the conversation:
Or on the request itself:
The guardrail reads the resolved value with _get_session_id_from_request_data, which checks the top-level litellm_session_id field, then metadata.session_id, then litellm_metadata.session_id, and sends the first non-empty one as session_id. The post_call hook receives the same request data, so the output evaluation carries the same session as the input evaluation.
metadata: {"litellm_session_id": "…"} does not work. Inside metadata the key is session_id; litellm_session_id is only recognized as a top-level body field.

Send conversation history

/v1/chat/completions is stateless, so messages holds whatever the caller sent — for a normal chat client or agent loop, the whole conversation. scope decides how much of it the guardrail forwards. Tool results must keep role: "tool" because the indirect prompt injection detector uses that role. Flattening every message to user disables that check. Agent transcripts can also be large, so measure latency with representative payloads.
On /v1/responses, previous_response_id does not supply either input. The guardrail sees only the new turn’s input, because LiteLLM rehydrates the earlier turns after the hook has already run, and the recovered identifier is not the one the guardrail reads. The session headers work normally on that route, so send x-litellm-session-id explicitly, and put the transcript in the request if a single call needs the history.

Verify

Assign an Enforce policy with a Block rule that matches the test prompt, then send a non-streaming request through the proxy:
The guardrail returns HTTP 400 with error: "Blocked by TrustGuard", the guardrail name, findings, and trace_id. It does not return request_id. Use trace_id to find the same decision in Activity. To verify monitoring before enforcement, set the policy to Observe and LITELLM_LOG=INFO, then send a request that matches a rule. A non-streaming request with both hooks enabled logs an input line followed by an output line:
An input-side block logs status=block without an output line because LiteLLM does not call the model. To verify an output-side block, use a non-streaming request and an Output rule; LiteLLM returns HTTP 400 after the model responds but before returning the completion to the client.

Reference

Coverage

The custom guardrail supports monitoring and blocking for chat-style requests. It does not support redaction, embeddings, image generation, or audio routes. For streaming requests, LiteLLM invokes the output hook with the assembled response after the stream closes. The result is recorded, but it cannot stop tokens that have already been delivered. Input evaluation still runs before the model call. Tool content is covered only when LiteLLM includes it in the messages selected by scope. Tool results must retain role: "tool"; tool declarations and tool calls are not evaluated as separate lifecycle events. Enforcement remains request-level, so a finding in tool content blocks the complete LiteLLM request. With pre_call and post_call enabled, a successful chat request with text input and output adds two calls to TrustGuard. For streaming requests, the output call occurs after the stream closes. Set timeout according to the latency requirements of the proxy.

What is evaluated

The pre_call and post_call hooks call POST /v1/evaluate. The assigned policy’s detectors determine the verdict. Configure both hooks to evaluate input and output.
With stream: true, LiteLLM calls the output hook with the assembled ModelResponse after the stream closes. TrustGuard evaluates and records that output, but a block verdict cannot recall tokens already sent to the client. Input enforcement still occurs before the model call.

Verdict handling

Use Monitor or Block actions with this integration. A block response exposes the findings to the caller. Use trace_id to correlate the response with Activity.

Configuration

Define TRUSTGUARD_API_BASE and TRUSTGUARD_API_KEY in the proxy environment, then restart LiteLLM after changing the guardrail file or config.yaml. Failure behavior. The custom guardrail handles connection errors, timeouts, and every non-200 TrustGuard response according to fail_open:
fail_open: true applies to every non-200 response, including 401, 403, 429, and 503. Monitor the TrustGuard unreachable, failing open (traffic NOT inspected) warning if you enable this setting.

Attributes

  • session_id: the conversation key the caller supplied, resolved by LiteLLM from a session header or a body field. LiteLLM does not generate one. See Multi-turn conversations
  • consumer_id: derived from the virtual key’s key_alias, user_email, user_id, or team_alias, then from request metadata if those fields are empty
The guardrail sends these values to TrustGuard when it finds a non-empty value. They support conversation grouping and per-consumer attribution in Activity.

Troubleshooting

Experimental: BerriAI/litellm#37165 proposes a native neuraltrust guardrail, configured with guardrail: neuraltrust instead of a file. It is open, is not in any released LiteLLM version, and is not part of the setup described on this page. It resolves session_id the same way, from the same caller-supplied header or body field, so the guidance in Multi-turn conversations applies to either path. It has no scope setting and sends the full message array.