> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zespan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK — zespan

> Install and configure the Zespan Python SDK to trace OpenAI, Anthropic, Google, Bedrock, Mistral, Groq, and LiteLLM calls, manage agent workflows, and flush events in serverless environments.

The Zespan Python SDK instruments your Python LLM application with a single `init()` call. It uses a background daemon thread to flush events without blocking your application, and registers an `atexit` handler to flush on process exit.

## Installation

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
pip install zespan
```

## Initialization

Call `zespan.init()` once at startup before making any LLM calls. All parameters are keyword arguments.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan

zespan.init(
    api_key="zsp_your_api_key_here",
    environment="production",
    store_prompts=True,
    sample_rate=1.0,
    debug=False,
)
```

**Parameters:**

<ParamField body="api_key" type="string" required>
  Your Zespan API key. Must start with `zsp_`. Find this in your project settings.
</ParamField>

<ParamField body="environment" type="string" default="production">
  Environment label attached to every event. Use `"staging"` or `"development"` to separate traces by environment.
</ParamField>

<ParamField body="store_prompts" type="boolean" default="True">
  When `True` (default), prompt and completion text are stored alongside traces with PII redaction applied before transmission. Set to `False` to disable prompt storage entirely.
</ParamField>

<ParamField body="sample_rate" type="float" default="1.0">
  Fraction of events to send, between `0.0` and `1.0`. Set to `0.1` to trace 10% of calls.
</ParamField>

<ParamField body="redact_keys" type="list[str]" default="[&#x22;password&#x22;, &#x22;secret&#x22;, &#x22;token&#x22;, &#x22;api_key&#x22;]">
  Keys whose values are redacted before any data is stored. Applied regardless of `store_prompts`. Passing this option replaces the default list rather than adding to it — see [PII redaction](#pii-redaction) below.
</ParamField>

<ParamField body="redact_pii" type="boolean" default="False">
  Opt-in pattern-based PII detection (emails, SSNs, credit cards, and similar) layered on top of key-based `redact_keys` matching. Tune it with `pii_preset`, `pii_redaction_mode`, `pii_whitelist`, `pii_categories`, and `pii_confidence_threshold`.
</ParamField>

<ParamField body="batch_size" type="int" default="50">
  Number of events to accumulate before flushing.
</ParamField>

<ParamField body="flush_interval" type="float" default="2.0">
  Seconds between automatic flushes. The SDK also flushes on process exit.
</ParamField>

<ParamField body="max_queue_size" type="int" default="1000">
  Maximum number of events held in the in-memory queue. Once exceeded, the oldest queued event is dropped to make room for new ones.
</ParamField>

<ParamField body="endpoint" type="string" default="https://api.zespan.com/v1/ingest">
  Full ingest URL. Prompts, datasets, and guardrail checks all derive their base URL from this value. Override it for self-hosted deployments.
</ParamField>

<ParamField body="base_url" type="string" default="https://api.zespan.com">
  Root API URL used only by the config-propagation client (see [Config propagation](#config-propagation)). For a self-hosted deployment, set this to the same host as `endpoint`.
</ParamField>

<ParamField body="project_id" type="string">
  Your Zespan project ID. Required, together with `enable_zespan_pilot=True`, to receive live config changes pushed from ZespanPilot or the dashboard.
</ParamField>

<ParamField body="enable_zespan_pilot" type="boolean" default="False">
  When `True` and `project_id` is set, the SDK applies config changes (model overrides, sample rate, guardrail toggles) pushed from ZespanPilot or the dashboard. See [Config propagation](#config-propagation).
</ParamField>

<ParamField body="enable_otel" type="boolean" default="False">
  When `True`, also exports spans to an OpenTelemetry-compatible backend. Requires `otel_endpoint`.
</ParamField>

<ParamField body="otel_endpoint" type="string">
  OTel collector endpoint URL. Required when `enable_otel=True`.
</ParamField>

<ParamField body="otel_service_name" type="string" default="zespan-sdk">
  Service name attached to exported OTel spans.
</ParamField>

<ParamField body="debug" type="boolean" default="False">
  When `True`, logs internal flush errors to stdout. Enable during integration testing.
</ParamField>

## Auto-patching all providers

`autopatch()` detects which LLM libraries are installed and patches them all in one call. Use this instead of calling individual patch functions.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.autopatch()
```

Covers: OpenAI, Anthropic, Google Generative AI (legacy `google-generativeai` package), AWS Bedrock, Mistral, Groq, LiteLLM — whichever of these packages are importable in your environment.

<Note>
  `autopatch()` does not patch the new Google GenAI SDK (`google-genai`) or OpenRouter. Call `zespan.patch_google_genai()` or `zespan.patch_openrouter()` explicitly for those.
</Note>

## Provider patches

### OpenAI

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import openai
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai()

client = openai.OpenAI()

# Sync
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "What is the capital of France?"}],
)

# Async
import asyncio

async def main():
    async_client = openai.AsyncOpenAI()
    response = await async_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Translate 'hello' to Spanish."}],
    )

asyncio.run(main())
```

### Anthropic

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import anthropic
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_anthropic()

client = anthropic.Anthropic()
message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Explain monads in plain English."}],
)
```

### Google Generative AI (legacy SDK)

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import google.generativeai as genai
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_google()

genai.configure(api_key="your_google_api_key")
model = genai.GenerativeModel("gemini-2.5-flash")
response = model.generate_content("What is quantum entanglement?")
```

Also patches `genai.embed_content()` automatically — embedding calls emit `span_kind: embedding`. Image generation models (e.g. `gemini-3.1-flash-image`) are detected from the response and emit `span_kind: image_gen`.

### Google GenAI (new SDK — recommended)

The `google-genai` package is Google's current Python SDK. `patch_google_genai()` patches `Client.__init__` so every client instance you create is automatically traced.

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
pip install google-genai
```

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
from google import genai
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_google_genai()

client = genai.Client(api_key="your_google_api_key")

# Text generation
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Explain quantum entanglement.",
)

# Embeddings (span_kind: embedding)
embed_response = client.models.embed_content(
    model="gemini-embedding-2",
    contents="The quick brown fox.",
)

# Image generation (span_kind: image_gen)
img_response = client.models.generate_images(
    model="imagen-3.0-generate-001",
    prompt="A photorealistic sunset over the ocean.",
)

# Video generation — Veo (span_kind: video_gen)
vid_op = client.models.generate_videos(
    model="veo-3.1-generate-preview",
    prompt="A slow-motion close-up of rain hitting a still lake.",
)
```

### AWS Bedrock

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import boto3
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_bedrock()

client = boto3.client("bedrock-runtime", region_name="us-east-1")
response = client.converse(
    modelId="amazon.nova-lite-v1:0",
    messages=[{"role": "user", "content": [{"text": "Summarize this document."}]}],
)
```

### Mistral

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
from mistralai import Mistral
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_mistral()

client = Mistral(api_key="your_mistral_api_key")
response = client.chat.complete(
    model="mistral-small-latest",
    messages=[{"role": "user", "content": "What is the capital of France?"}],
)
```

### Groq

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
from groq import Groq
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_groq()

client = Groq(api_key="your_groq_api_key")
response = client.chat.completions.create(
    model="llama-3.3-70b-versatile",
    messages=[{"role": "user", "content": "Explain gradient descent."}],
)
```

### LiteLLM

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import litellm
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_litellm()

response = litellm.completion(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello from LiteLLM!"}],
)
```

### OpenRouter

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import openai
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openrouter()

client = openai.OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key="your_openrouter_api_key",
)
response = client.chat.completions.create(
    model="anthropic/claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Hello from OpenRouter!"}],
)
```

## Framework and agent integrations

Beyond the direct provider patches above, the SDK integrates with popular agent and orchestration frameworks. Each has its own dedicated guide:

* **LangChain** — `ZespanCallbackHandler` traces chains, agents, tools, and retrievers. See [LangChain](/sdk/integrations/langchain).
* **LlamaIndex** — `ZespanLlamaIndexCallbackHandler` traces query engines and retrievers. See [LlamaIndex](/sdk/integrations/llamaindex).
* **AutoGen / AG2** — `wrap_autogen_agent` traces `ConversableAgent` replies. See [AutoGen](/sdk/integrations/autogen).
* **CrewAI** — `wrap_crew` traces crew task execution. See [CrewAI](/sdk/integrations/crewai).
* **Google ADK** — `wrap_adk_agent` (or `ZespanADKTracer`) traces ADK agent runs. See [Google ADK](/sdk/integrations/google-adk).
* **Haystack** — `ZespanHaystackTracer` traces pipeline components. See [Haystack](/sdk/integrations/haystack).
* **Semantic Kernel** — `instrument_semantic_kernel` traces kernel function invocations. See [Semantic Kernel](/sdk/integrations/semantic-kernel).

## Context enrichment

Use `with_zespan_context` to attach a `user_id`, `session_id`, or custom `tags` to all traces generated within a function scope.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan
from zespan import with_zespan_context

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai()

import openai
client = openai.OpenAI()

def handle_request(user_id: str, session_id: str, message: str) -> str:
    with with_zespan_context(user_id=user_id, session_id=session_id, tags={"feature": "chat"}):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": message}],
        )
        return response.choices[0].message.content
```

<Tip>
  Set `user_id` and `session_id` on every request that involves a logged-in user. This enables per-user cost breakdown and session replay in the Zespan dashboard.
</Tip>

## Agent tracing with `with_agent`

Use the `with_agent` context manager to trace a multi-step agent workflow. It creates an agent span and exposes methods to log plans, trace tool calls, and record handoffs to other agents.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan
from zespan import with_agent

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai()

with with_agent(
    name="CustomerSupportAgent",
    role="specialist",
    framework="custom",
    tools=[{"name": "lookup_order", "description": "Lookup order by id"}],
) as agent:
    agent.log_plan(["Lookup order", "Check refund policy", "Draft response"])

    order = agent.trace_tool(
        "lookup_order",
        {"order_id": "123"},
        lambda: {"id": "123", "status": "delivered", "total": 49.99},
    )

    agent.delegate_to("RefundPolicyAgent", "refund requested")
```

**`with_agent` parameters:**

<ParamField body="name" type="string" required>
  Display name for this agent in traces and the agent registry.
</ParamField>

<ParamField body="role" type="string" default="specialist">
  Role label such as `"coordinator"`, `"specialist"`, or `"planner"`.
</ParamField>

<ParamField body="framework" type="string" default="custom">
  Framework name, e.g. `"custom"`, `"langchain"`, `"google-adk"`.
</ParamField>

<ParamField body="tools" type="object[]">
  List of tool definition objects with `name` and `description` fields.
</ParamField>

**`AgentContext` methods:**

* `agent.log_plan(steps: list[str])` — records a planning span
* `agent.trace_tool(name, args, callable)` — wraps a callable, records args and return value as a tool span
* `agent.delegate_to(target_name, reason)` — records a handoff span

## Manual spans with `start_span`

Use `start_span` to instrument any function as a custom span and attach evaluation scores. It returns a `ManualSpan` — enter `span.run()` to propagate trace context to nested calls, then call `span.end()` when the work completes to emit the event.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan
from zespan import start_span

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai()

import openai
client = openai.OpenAI()

span = start_span(name="rag-pipeline")
with span.run():
    docs = retrieve_documents("user query")
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": f"Use these docs: {docs}"},
            {"role": "user", "content": "user query"},
        ],
    )
    span.set_eval_score("relevance", 0.92)
span.end()
```

## Prompt management

The `PromptClient` fetches versioned prompts from the Zespan prompt library at runtime. Results are cached locally for 5 minutes.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
from zespan import PromptClient, get_client
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai()

prompts = PromptClient(get_client())

prompt = prompts.get("support-reply", label="production")
text = prompts.compile(prompt, {
    "customer_name": "Alex",
    "order_id": "ORD-7821",
})

import openai
client = openai.OpenAI()
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": text},
        {"role": "user", "content": "I need help with my order."},
    ],
)
```

See the [Prompt management](/sdk/prompt-management) page for the full API reference — the Python `PromptClient` exposes the same methods: `get`, `list`, `create`, `update_labels`, `compile`, and `clear_cache`. `get()` also accepts a `fallback` dict so calls degrade gracefully if the prompt library is unreachable, and `compile()` accepts a `placeholders` dict for prompts that splice in reusable message-list segments.

<Note>
  In Python, method and parameter names use `snake_case`: `update_labels`, `clear_cache`, `prompt_type`, `commit_message`, `placeholders`, `fallback`.
</Note>

## Dataset experiment runs

Use `DatasetsClient` (`client.datasets`) to run your own pipeline against a Zespan dataset, link each result back to Zespan via the trace ID your code already produces, then score and compare runs in the dashboard.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan
from zespan import get_client, get_current_context, start_span

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai()

datasets = get_client().datasets

items = datasets.get_items("support-qa-v2")

run = datasets.create_run(
    "support-qa-v2",
    "gpt-4o-baseline",
    description="Baseline run before the prompt rewrite",
)

import openai
client = openai.OpenAI()

for item in items:
    span = start_span(name="dataset-item")
    with span.run():
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": item["input"]}],
        )
        trace_id = get_current_context()["trace_id"]
    span.end()

    run.link(dataset_item_id=item["id"], trace_id=trace_id)
```

**`DatasetsClient` methods:**

<ParamField body="get_items(dataset_name)" type="method">
  Lists a dataset's items by dataset name.
</ParamField>

<ParamField body="create_run(dataset_name, run_name, description=None)" type="method">
  Creates or fetches a named run against a dataset. Idempotent — safe to call every time a job starts. Returns a run handle.
</ParamField>

**Run handle methods:**

<ParamField body="run.link(dataset_item_id, trace_id, observation_id=None)" type="method">
  Links a dataset item to this run via a trace ID your own code already produced. Pass `observation_id` to link a specific span instead of the whole trace.
</ParamField>

<Tip>
  This links raw traces to a run — it doesn't score or compare anything itself. See [Datasets](/dashboard/datasets) for the full dashboard-side walkthrough of scoring linked runs and comparing them against each other.
</Tip>

## Flushing in serverless environments

In short-lived processes such as AWS Lambda, Vercel Functions, or Cloud Run, call `zespan.get_client().flush()` explicitly before the handler returns to guarantee delivery.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai()

def lambda_handler(event, context):
    response = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": event["prompt"]}],
    )
    result = response.choices[0].message.content

    zespan.get_client().flush()
    return {"statusCode": 200, "body": result}
```

<Warning>
  Omitting `zespan.get_client().flush()` in serverless environments is the most common cause of missing traces. Always call it before your handler returns.
</Warning>

## PII redaction

Zespan automatically redacts values from `tags` and `metadata` fields before they leave your application. The key is preserved; the value is replaced with `"[REDACTED]"`.

**Default redacted keys** (applied whenever `redact_keys` is not passed at all): `password`, `secret`, `token`, `api_key`.

Pass your own `redact_keys` list at initialization to protect additional fields:

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan

zespan.init(
    api_key="zsp_your_api_key_here",
    redact_keys=["email", "phone", "address", "ip_address", "dob"],
)
```

<Warning>
  `redact_keys` **replaces** the default list rather than adding to it. If you pass your own list, include `password`, `secret`, `token`, and `api_key` explicitly if you still want them redacted.
</Warning>

<Note>
  Key-based redaction applies to `tags` and `metadata` fields, and to stored prompt and completion text. Prompt storage is on by default — set `store_prompts=False` to disable it entirely.
</Note>

## Pattern-based PII detection (opt-in)

Set `redact_pii=True` to layer pattern-based detection (emails, SSNs, credit cards, and similar) on top of key-based `redact_keys` matching:

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan

zespan.init(
    api_key="zsp_your_api_key_here",
    redact_pii=True,
    pii_preset="gdpr",             # gdpr | hipaa | ccpa | pci-dss | soc2 | finance | education | transportation
    pii_redaction_mode="mask-middle",  # placeholder | mask-middle | mask-all
    pii_confidence_threshold=0.7,
    pii_whitelist=["support@yourcompany.com"],
)
```

<ParamField body="pii_preset" type="string">
  Named bundle of PII categories to detect, e.g. `"gdpr"` or `"hipaa"`.
</ParamField>

<ParamField body="pii_categories" type="list[str]">
  Explicit list of PII categories to detect, as an alternative to `pii_preset`.
</ParamField>

<ParamField body="pii_redaction_mode" type="string" default="placeholder">
  How matched values are redacted: `"placeholder"` (replace entirely), `"mask-middle"`, or `"mask-all"`.
</ParamField>

<ParamField body="pii_whitelist" type="list[str]">
  Values that should never be redacted even if they match a PII pattern.
</ParamField>

<ParamField body="pii_confidence_threshold" type="float" default="0.7">
  Minimum detector confidence, between `0.0` and `1.0`, required before a match is redacted.
</ParamField>

## Guardrails

Guardrails run content safety checks before sending a prompt to the LLM (pre-check) and before returning the completion (post-check). Pass `guardrails=True` to any patch function to enable both phases.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan

zespan.init(api_key="zsp_your_api_key_here")
zespan.patch_openai(guardrails=True)
```

See [Guardrails](/sdk/guardrails) for the full reference — parameter defaults, all fields, the direct `check_guardrails()` API, and a multi-wrapper example.

## Config propagation

Zespan can push configuration changes — model overrides, fallback models, retry/timeout policies, sample rate, guardrail toggles, and more — to your running application without a redeployment. Changes made via ZespanPilot or the dashboard are picked up on the next event flush (default every 2 seconds), as long as both `project_id` and `enable_zespan_pilot=True` are set at init:

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan

zespan.init(
    api_key="zsp_your_api_key_here",
    project_id="proj_your_project_id",
    enable_zespan_pilot=True,
)
```

See [Config propagation](/sdk/config-propagation) for the full list of rule types and the programmatic `ConfigClient` API.
