Pydantic AI (tracing)

Pydantic AI 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.

Create an account at platform.respan.ai and grab an API key.

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

See Pydantic AI gateway setup to route this integration through the Respan gateway.

Setup

1

Install packages

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

Set environment variables

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.

3

Initialize and run

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)
4

View your trace

Open the Traces page 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 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:

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

Set the model-provider and Respan API keys:

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:

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

ParameterTypeDefaultDescription
api_keystr | NoneNoneFalls back to RESPAN_API_KEY env var.
base_urlstr | NoneNoneFalls back to RESPAN_BASE_URL env var.
instrumentationslist[]Plugin instrumentations to activate (e.g. PydanticAIInstrumentor()).
customer_identifierstr | NoneNoneDefault customer identifier for all spans.
metadatadict | NoneNoneDefault metadata attached to all spans.
environmentstr | NoneNoneEnvironment tag (e.g. "production").

PydanticAIInstrumentor options

ParameterTypeDefaultDescription
agentAgent | NoneNoneInstrument a single agent. If None, all agents are instrumented globally.
include_contentboolTrueInclude message content in telemetry.
include_binary_contentboolTrueInclude binary content in telemetry.
versionint4Pydantic 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)

ParameterTypeDefaultDescription
includeNativeSpansbooleantrueInclude Pydantic AI-native span shapes.
includeOpenInferenceSpansbooleantrueInclude Pydantic AI-scoped OpenInference span shapes.

Instrument a single agent

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

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
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
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)
AttributeTypeDescription
customer_identifierstrIdentifies the end user in Respan analytics.
thread_identifierstrGroups related messages into a conversation.
metadatadictCustom 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.

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.

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.

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)