Skip to main content
The Xenovia Python SDK (xenovia-sdk) sends an agent’s actions to Xenovia for a decision before they happen: tool calls and tool results, memory writes, context summaries, and — for agents that call their model directly — the model request and reply. Each check runs the guardrails configured on the agent for that stage; model_request, model_response, and tool_call checks also run the agent’s policies. Use it alongside the agent endpoint: the endpoint governs model traffic, and the SDK governs the steps your code takes around it. Tool, memory, and summary guardrails only run when the SDK sends those stages. Install:
Requirements: Python ≥ 3.9. No runtime dependencies (standard library only).

How it works

Every SDK call is a POST /v1/execute to the Xenovia runtime, authenticated with the agent’s xe_ API key. The key identifies the agent, so the request body does not name it.
The decision is one of: decision["escalated"] is true when a guardrail escalated the step. The step is still allowed, and an escalation is queued for review (see Escalations).

Initialise the client

Constructor parameters

unreachable_mode values:
The "block" and "allow" fallbacks are produced by the SDK on your machine, not by Xenovia. You can tell them apart from a real decision: decision["rule_id"] is SDK_UNREACHABLE_BLOCK or SDK_UNREACHABLE_ALLOW, trace_id is None, and no trace appears in the platform.

Hooks

Hooks are the main API. Each one sends one stage and returns a XenoviaResponse. Hooks never skip work for you: check the response and act on it. Every hook also accepts session_id=.
A redacted tool call returns the masked arguments in payload["arguments"]; a redacted tool result, memory write, or reply returns masked text in payload["content"] or payload["response"].
If your model calls already go through the agent endpoint, leave model_pre and model_post off: the runtime checks the model request and reply there. Use them only for agents that call their model provider directly.
model_pre accepts messages with string content or a list of content parts (OpenAI or Anthropic style), and tools as flat {name, description} objects or OpenAI function tools. Only text is checked: text parts, refusal parts, and the text of Anthropic tool_result, mcp_tool_result and search_result blocks (string content or nested text parts) are joined. Media and file parts, the model’s own tool calls (tool_use, server_tool_use, mcp_tool_use) and thinking, and Anthropic server tool results (web search, web fetch, code execution) are not sent, so their content is not checked. On success, payload["messages"] and payload["tools"] are your own objects, with any redaction written back into the part it came from. model_pre, and model_post when you pass messages, raise ValueError, and send nothing, when messages are not a list of dicts, a role is not a string, content is not a string, a list, or None, or a content part is malformed or of a type not listed above. model_pre also raises it when a tool is not a dict or has no name.

execute() — a single check

execute() is the call the hooks are built on. Use it for actions that are not a tool, memory, or model step.
stage is one of model_request, model_response, tool_call, tool_result, memory_write, or context_summary. If operation is omitted, the SDK uses the caller’s module.function name.
capability= was renamed to operation= in 0.2.0. In 0.2.1 capability= still works on execute() and @guard() as a deprecated alias and emits a DeprecationWarning.

@guard() — decorator

@guard() checks a function call as a tool_call before the body runs. The decorated function returns a XenoviaResponse; read .result for the function’s return value.
  • The payload sent is {"arguments": {...}}, with every bound argument (including defaults).
  • On allow, the function runs with its original arguments. On redact, it runs with the masked arguments. On deny or error, it does not run.
  • async def functions are supported.
  • mode="enforce" is the only mode. The SDK has no observe mode: to trial a rule without blocking, set the policy or guardrail to Monitor in the console. Monitor evaluations return allow.

Roles and subagents

Set agent_role on the client, or create a child client with subagent():
subagent() returns a new client with the same key and endpoint, the given role, and agent_depth one higher. It gets its own session, but because it uses the same key it is the same Xenovia agent: its checks run that agent’s policies and guardrails. Give a subagent its own agent and key when it needs a separate identity.

XenoviaResponse

The decision never contains the rule or the reason. Look up trace_id in the platform to see which policy or guardrail fired.

Sessions

The runtime issues the session and returns it in the X-Xenovia-Session-Id response header; the SDK stores it on the client.
  • With auto_session=True, the first call lets the runtime create a session and later calls reuse it.
  • Otherwise, pass session_id= (a UUID) on each call, or share a session ID with your agent endpoint traffic so SDK steps and model turns appear in one session.
  • If the runtime returns a different session than the one you sent, the SDK raises XenoviaExecutionError.
  • next_turn() only advances a local counter. To report a turn number, pass turn= to execute().

Error handling

Malformed input raises ValueError before any request is sent, for example a model_pre message part the SDK cannot read.

Wire format

session_id, agent_role, and agent_depth are sent only when set.

Security

  • HTTPS is required unless allow_insecure_http=True, which is limited to loopback addresses.
  • The API key is sent only in the Authorization header, never in payloads or logs.
  • Debug output prints the decision outcome and trace ID, not payloads.