Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.xenovia.io/llms.txt

Use this file to discover all available pages before exploring further.

Setup

export XENOVIA_API_KEY=xe_...
export XENOVIA_PROXY_ID=your-proxy-id
pip install openai
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["XENOVIA_API_KEY"],
    base_url=f"https://runtime.xenovia.io/a/{os.environ['XENOVIA_PROXY_ID']}/openai/v1"
)

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}]
)
print(response.choices[0].message.content)

Streaming

Streaming works without any additional configuration. The runtime stamps X-Xenovia-Session-Id and X-Xenovia-Trace-Id into the response headers before the first byte.
stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Count to 5"}],
    stream=True
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

Tool calling

Tool definitions pass through unchanged. Xenovia records tool call names and arguments in the trace and evaluates them against your request-stage Rego policy before forwarding.
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string"}
                },
                "required": ["location"]
            }
        }
    }
]

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "What's the weather in London?"}],
    tools=tools
)
If a tool name matches a block rule in your Rego policy, the request returns 403 before reaching the upstream LLM.

Sessions

Pass X-Xenovia-Session-Id (a valid UUID) to group related calls into a session. All turns under that session ID appear together in Traces with a per-session turn counter.
import uuid

client = OpenAI(
    api_key=os.environ["XENOVIA_API_KEY"],
    base_url=f"https://runtime.xenovia.io/a/{os.environ['XENOVIA_PROXY_ID']}/openai/v1",
    default_headers={"X-Xenovia-Session-Id": str(uuid.uuid4())}
)
If you omit the header, Xenovia resolves a session automatically using message fingerprinting or the user field. See Runtime Architecture for the full five-strategy chain.

Custom trace properties

Attach up to 20 X-Xenovia-Property-* headers to tag traces with application context. Keys must be ≤ 64 chars; values ≤ 512 chars; the policy_ prefix is reserved.
client = OpenAI(
    api_key=os.environ["XENOVIA_API_KEY"],
    base_url=f"https://runtime.xenovia.io/a/{os.environ['XENOVIA_PROXY_ID']}/openai/v1",
    default_headers={
        "X-Xenovia-Session-Id": str(uuid.uuid4()),
        "X-Xenovia-Property-user-tier": "enterprise",
        "X-Xenovia-Property-feature": "document-summary",
        "X-Xenovia-Session-Path": "workflows/summarise"
    }
)

Policy blocks

When a request is blocked by policy, the runtime returns 403 Forbidden. The OpenAI SDK raises this as PermissionDeniedError. The X-Xenovia-Trace-Id response header identifies the blocking trace.
from openai import PermissionDeniedError

try:
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Delete everything"}],
        tools=tools
    )
except PermissionDeniedError as e:
    print(f"Blocked by policy: {e.message}")

Reading trace headers

The response always includes trace headers you can log for correlation:
import httpx
from openai import OpenAI

# Use httpx to access response headers
with httpx.Client() as http:
    resp = http.post(
        f"https://runtime.xenovia.io/a/{os.environ['XENOVIA_PROXY_ID']}/openai/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {os.environ['XENOVIA_API_KEY']}",
            "Content-Type": "application/json"
        },
        json={
            "model": "gpt-4o-mini",
            "messages": [{"role": "user", "content": "Hello"}]
        }
    )
    trace_id = resp.headers.get("X-Xenovia-Trace-Id")
    session_id = resp.headers.get("X-Xenovia-Session-Id")
    print(f"Trace: {trace_id}, Session: {session_id}")