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

1import os
2
3from pydantic_ai import Agent
4from respan import Respan
5from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor
6
7respan_api_key = os.environ["RESPAN_API_KEY"]
8respan_base_url = os.getenv("RESPAN_BASE_URL", "https://api.respan.ai/api").rstrip("/")
9gateway_api_key = os.getenv("RESPAN_GATEWAY_API_KEY", respan_api_key)
10model = os.getenv("RESPAN_MODEL", "gpt-4o")
11
12os.environ["OPENAI_BASE_URL"] = os.getenv("RESPAN_GATEWAY_BASE_URL", respan_base_url).rstrip("/")
13os.environ["OPENAI_API_KEY"] = gateway_api_key
14
15respan = Respan(
16 api_key=respan_api_key,
17 base_url=respan_base_url,
18 instrumentations=[PydanticAIInstrumentor()],
19)
20
21agent = Agent(
22 model=f"openai:{model}",
23 system_prompt="You are a helpful assistant.",
24)
25
26result = agent.run_sync("What is the capital of France?")
27print(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:

1import os
2
3from opentelemetry import trace
4from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
5from opentelemetry.sdk.resources import Resource
6from opentelemetry.sdk.trace import TracerProvider
7from opentelemetry.sdk.trace.export import BatchSpanProcessor
8from pydantic_ai import Agent
9
10exporter = OTLPSpanExporter(
11 endpoint="https://api.respan.ai/api/v2/traces",
12 headers={"Authorization": f"Bearer {os.environ['RESPAN_API_KEY']}"},
13)
14provider = TracerProvider(
15 resource=Resource.create({"service.name": "pydantic-ai-app"}),
16)
17provider.add_span_processor(BatchSpanProcessor(exporter))
18trace.set_tracer_provider(provider)
19
20Agent.instrument_all()
21
22agent = Agent(
23 model="openai:gpt-4o",
24 system_prompt="You are a helpful assistant.",
25)
26result = agent.run_sync("What is the capital of France?")
27print(result.output)
28provider.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:

1from pydantic_ai import Agent
2from respan import Respan
3from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor
4
5agent = Agent(model="openai:gpt-4o")
6
7respan = Respan(
8 instrumentations=[PydanticAIInstrumentor(agent=agent)],
9)

Attributes

In Respan()

Set defaults at initialization — these apply to all spans.

Python
1from respan import Respan
2from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor
3
4respan = Respan(
5 instrumentations=[PydanticAIInstrumentor()],
6 customer_identifier="user_123",
7 metadata={"service": "assistant-api", "version": "1.0.0"},
8)

With propagate_attributes

Override per-request using a context scope.

Python
1from pydantic_ai import Agent
2from respan import Respan, propagate_attributes
3from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor
4
5respan = Respan(
6 instrumentations=[PydanticAIInstrumentor()],
7)
8
9agent = Agent(
10 model="openai:gpt-4o",
11 system_prompt="You are a helpful assistant.",
12)
13
14def handle_request(user_id: str, message: str):
15 with propagate_attributes(
16 customer_identifier=user_id,
17 thread_identifier="conv_abc_123",
18 metadata={"plan": "pro"},
19 ):
20 result = agent.run_sync(message)
21 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.

1from pydantic_ai import Agent
2from respan import Respan, workflow, task
3from respan_instrumentation_pydantic_ai import PydanticAIInstrumentor
4
5respan = Respan(
6 instrumentations=[PydanticAIInstrumentor()],
7)
8
9agent = Agent(
10 model="openai:gpt-4o",
11 system_prompt="You are a helpful travel assistant.",
12)
13
14@task(name="fetch_destination_info")
15def fetch_destination_info(destination: str) -> str:
16 result = agent.run_sync(f"Give me a one-sentence summary of {destination}.")
17 return result.output
18
19@workflow(name="travel_planning_workflow")
20def travel_planning_workflow(destination: str) -> str:
21 return fetch_destination_info(destination)
22
23print(travel_planning_workflow("Paris"))

Examples

Tool calls

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

1from pydantic_ai import Agent
2
3agent = Agent(
4 model="openai:gpt-4o",
5 system_prompt=(
6 "You are a calculator assistant. You must use the provided tools for any arithmetic. "
7 "Never compute numbers yourself; always call the add tool when asked to add numbers."
8 ),
9)
10
11@agent.tool_plain
12def add(a: int, b: int) -> int:
13 return a + b
14
15result = agent.run_sync(
16 "Use your add tool to compute 15 + 27, then reply with the result."
17)
18print(result.output)

Structured output

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

1from pydantic import BaseModel
2from pydantic_ai import Agent
3
4class TravelAnswer(BaseModel):
5 city: str
6 summary: str
7
8agent = Agent(
9 model="openai:gpt-4o",
10 system_prompt="You are a helpful travel assistant.",
11 output_type=TravelAnswer,
12)
13
14result = agent.run_sync("Recommend a weekend trip to Paris.")
15print(result.output)