> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/_mcp/server.

# Pydantic AI (tracing)

> Trace Pydantic AI agent runs with Respan — auto-instrumented spans, gateway routing, and full observability.

[Pydantic AI](https://ai.pydantic.dev/) is a Python agent framework from the creators of Pydantic. It provides a type-safe way to build agents with tools, structured outputs, and multi-model support. Respan gives you full observability over every agent run, model call, and tool invocation — and gateway routing through the OpenAI-compatible Respan endpoint.

For TypeScript workloads that emit Pydantic AI-compatible OTEL spans, Respan also supports normalization through `@respan/instrumentation-pydantic-ai`.

#### Set up Respan

Create an account at [platform.respan.ai](https://platform.respan.ai) and grab an [API key](https://platform.respan.ai/platform/api/api-keys).

Run `npx @respan/cli setup` to set up with your coding agent.

#### Use Respan Gateway

See [Pydantic AI gateway setup](/docs/gateway/pydantic-ai) to route this integration through the Respan gateway.

#### Example projects

* [Python examples](https://github.com/respanai/respan-example-projects/tree/main/python/tracing/pydantic-ai)
* [TypeScript examples](https://github.com/respanai/respan-example-projects/tree/main/typescript/pydantic-ai)

## Setup

#### Install packages

```bash Python
# Install Pydantic AI first to avoid excessive dependency backtracking in pip.
pip install pydantic-ai
pip install respan-ai respan-instrumentation-pydantic-ai
```

```bash TypeScript
npm install @respan/respan @respan/instrumentation-pydantic-ai
```

#### Set environment variables

```bash
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
# Optional overrides
export RESPAN_BASE_URL="https://api.respan.ai/api"
export RESPAN_MODEL="gpt-4o"
```

The examples route model calls through the Respan gateway by setting provider-compatible environment aliases in application code, so no separate provider API key is required.

For TypeScript OTEL-compatible workloads, use your OpenAI-compatible client environment and export traces through `@respan/instrumentation-pydantic-ai`.

#### Initialize and run

```python Python
import os

from pydantic_ai import Agent
from respan import Respan
from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor

respan_api_key = os.environ["RESPAN_API_KEY"]
respan_base_url = os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api").rstrip("/")
gateway_api_key = os.getenv("RESPAN_GATEWAY_API_KEY", respan_api_key)
model = os.getenv("RESPAN_MODEL", "gpt-4o")

os.environ["OPENAI_BASE_URL"] = os.getenv("RESPAN_GATEWAY_BASE_URL", respan_base_url).rstrip("/")
os.environ["OPENAI_API_KEY"] = gateway_api_key

respan = Respan(
    api_key=respan_api_key,
    base_url=respan_base_url,
    instrumentations=[PydanticAIInstrumentor()],
)

agent = Agent(
    model=f"openai:{model}",
    system_prompt="You are a helpful assistant.",
)

result = agent.run_sync("What is the capital of France?")
print(result.output)
```

```typescript TypeScript
import { Respan } from "@respan/respan";
import { PydanticAIInstrumentor } from "@respan/instrumentation-pydantic-ai";
import { trace } from "@opentelemetry/api";

const respan = new Respan({
  apiKey: process.env.RESPAN_API_KEY,
  baseURL: process.env.RESPAN_BASE_URL,
  instrumentations: [new PydanticAIInstrumentor()],
});
await respan.initialize();

// The TypeScript package normalizes Pydantic AI-compatible spans you emit via OTEL.
const tracer = trace.getTracer("pydantic-ai-compatible-example");
const span = tracer.startSpan("chat completion", {
  attributes: {
    "gen_ai.operation.name": "chat",
    "gen_ai.system": "openai",
    "gen_ai.request.model": "gpt-4o",
    "gen_ai.input.messages": JSON.stringify([
      { role: "system", content: "You are a helpful assistant." },
      { role: "user", content: "What is the capital of France?" },
    ]),
  },
});
span.end();

await respan.flush();
```

#### View your trace

Open the [Traces page](https://platform.respan.ai/platform/traces) to see your agent run with model spans, tool calls, tokens, and cost.

## Native OpenTelemetry (without Logfire)

Pydantic AI can emit its native OpenTelemetry spans directly to Respan without configuring Logfire or installing the Respan Pydantic AI plugin. This follows Pydantic's [OTel without Logfire](https://pydantic.dev/docs/ai/integrations/logfire/#otel-without-logfire) setup and exports the current Pydantic AI GenAI semantic conventions.

Choose one Pydantic AI instrumentation path. Do not combine this setup with `PydanticAIInstrumentor()` or `logfire.instrument_pydantic_ai()`; they configure the same Pydantic AI instrumentation and can overwrite each other's tracer provider, content, and format settings.

Install Pydantic AI and the standard OTLP/HTTP exporter:

```bash
pip install pydantic-ai opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
```

Set the model-provider and Respan API keys:

```bash
export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
```

Configure an OpenTelemetry provider, enable Pydantic AI's native instrumentation, and export directly to Respan:

```python
import os

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from pydantic_ai import Agent

exporter = OTLPSpanExporter(
    endpoint="https://api.respan.ai/api/v2/traces",
    headers={"Authorization": f"Bearer {os.environ['RESPAN_API_KEY']}"},
)
provider = TracerProvider(
    resource=Resource.create({"service.name": "pydantic-ai-app"}),
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)

Agent.instrument_all()

agent = Agent(
    model="openai:gpt-4o",
    system_prompt="You are a helpful assistant.",
)
result = agent.run_sync("What is the capital of France?")
print(result.output)
provider.force_flush()
```

This path exports Pydantic AI's native version 5 spans as-is. Use the `PydanticAIInstrumentor()` setup above when you need Respan-specific field normalization, `propagate_attributes`, or the instrumentor's content controls.

## Configuration

| Parameter             | Type           | Default | Description                                                            |
| --------------------- | -------------- | ------- | ---------------------------------------------------------------------- |
| `api_key`             | `str \| None`  | `None`  | Falls back to `RESPAN_API_KEY` env var.                                |
| `base_url`            | `str \| None`  | `None`  | Falls back to `RESPAN_BASE_URL` env var.                               |
| `instrumentations`    | `list`         | `[]`    | Plugin instrumentations to activate (e.g. `PydanticAIInstrumentor()`). |
| `customer_identifier` | `str \| None`  | `None`  | Default customer identifier for all spans.                             |
| `metadata`            | `dict \| None` | `None`  | Default metadata attached to all spans.                                |
| `environment`         | `str \| None`  | `None`  | Environment tag (e.g. `"production"`).                                 |

### PydanticAIInstrumentor options

| Parameter                | Type            | Default | Description                                                                               |
| ------------------------ | --------------- | ------- | ----------------------------------------------------------------------------------------- |
| `agent`                  | `Agent \| None` | `None`  | Instrument a single agent. If `None`, all agents are instrumented globally.               |
| `include_content`        | `bool`          | `True`  | Include message content in telemetry.                                                     |
| `include_binary_content` | `bool`          | `True`  | Include binary content in telemetry.                                                      |
| `version`                | `int`           | `4`     | Pydantic AI instrumentation settings version used for emitted GenAI semantic conventions. |

The processor normalizes Pydantic AI v2 message parts, tool definitions, and response formats into JSON-safe string attributes before export, so newer structured chat payloads render correctly in Respan.

### PydanticAIInstrumentor options (TypeScript)

| Parameter                   | Type      | Default | Description                                           |
| --------------------------- | --------- | ------- | ----------------------------------------------------- |
| `includeNativeSpans`        | `boolean` | `true`  | Include Pydantic AI-native span shapes.               |
| `includeOpenInferenceSpans` | `boolean` | `true`  | Include Pydantic AI-scoped OpenInference span shapes. |

### Instrument a single agent

By default, `PydanticAIInstrumentor()` instruments all Pydantic AI agents globally. To instrument only one agent:

```python
from pydantic_ai import Agent
from respan import Respan
from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor

agent = Agent(model="openai:gpt-4o")

respan = Respan(
    instrumentations=[PydanticAIInstrumentor(agent=agent)],
)
```

## Attributes

### In Respan()

Set defaults at initialization — these apply to all spans.

```python Python
from respan import Respan
from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor

respan = Respan(
    instrumentations=[PydanticAIInstrumentor()],
    customer_identifier="user_123",
    metadata={"service": "assistant-api", "version": "1.0.0"},
)
```

### With propagate\_attributes

Override per-request using a context scope.

```python Python
from pydantic_ai import Agent
from respan import Respan, propagate_attributes
from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor

respan = Respan(
    instrumentations=[PydanticAIInstrumentor()],
)

agent = Agent(
    model="openai:gpt-4o",
    system_prompt="You are a helpful assistant.",
)

def handle_request(user_id: str, message: str):
    with propagate_attributes(
        customer_identifier=user_id,
        thread_identifier="conv_abc_123",
        metadata={"plan": "pro"},
    ):
        result = agent.run_sync(message)
        print(result.output)
```

| Attribute             | Type   | Description                                           |
| --------------------- | ------ | ----------------------------------------------------- |
| `customer_identifier` | `str`  | Identifies the end user in Respan analytics.          |
| `thread_identifier`   | `str`  | Groups related messages into a conversation.          |
| `metadata`            | `dict` | Custom key-value pairs. Merged with default metadata. |

## Decorators (optional)

Decorators are not required. Pydantic AI model spans and tool calls are auto-traced by the instrumentor. Use `@workflow` and `@task` when you want to add structure around one or more agent runs.

```python
from pydantic_ai import Agent
from respan import Respan, workflow, task
from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor

respan = Respan(
    instrumentations=[PydanticAIInstrumentor()],
)

agent = Agent(
    model="openai:gpt-4o",
    system_prompt="You are a helpful travel assistant.",
)

@task(name="fetch_destination_info")
def fetch_destination_info(destination: str) -> str:
    result = agent.run_sync(f"Give me a one-sentence summary of {destination}.")
    return result.output

@workflow(name="travel_planning_workflow")
def travel_planning_workflow(destination: str) -> str:
    return fetch_destination_info(destination)

print(travel_planning_workflow("Paris"))
```

## Examples

### Tool calls

Tool calls are automatically captured as spans with inputs, outputs, and timing.

```python
from pydantic_ai import Agent

agent = Agent(
    model="openai:gpt-4o",
    system_prompt=(
        "You are a calculator assistant. You must use the provided tools for any arithmetic. "
        "Never compute numbers yourself; always call the add tool when asked to add numbers."
    ),
)

@agent.tool_plain
def add(a: int, b: int) -> int:
    return a + b

result = agent.run_sync(
    "Use your add tool to compute 15 + 27, then reply with the result."
)
print(result.output)
```

### Structured output

Structured outputs are traced the same way as normal agent runs.

```python
from pydantic import BaseModel
from pydantic_ai import Agent

class TravelAnswer(BaseModel):
    city: str
    summary: str

agent = Agent(
    model="openai:gpt-4o",
    system_prompt="You are a helpful travel assistant.",
    output_type=TravelAnswer,
)

result = agent.run_sync("Recommend a weekend trip to Paris.")
print(result.output)
```