TraceAI Pydantic AI Instrumentation
OpenTelemetry instrumentation for Pydantic AI, providing comprehensive tracing for AI agent executions, tool calls, and model interactions.
Installation
pip install traceai-pydantic-ai
Quick Start
from traceai_pydantic_ai import PydanticAIInstrumentor
# Initialize instrumentation (do this once at startup)
PydanticAIInstrumentor().instrument()
# Use Pydantic AI as normal - tracing is automatic
from pydantic_ai import Agent
agent = Agent('openai:gpt-4o', instructions='Be concise and helpful.')
result = agent.run_sync('What is the capital of France?')
print(result.output)
Features
- Agent Run Tracing: Automatic tracing for
run(),run_sync(), andrun_stream()methods - Tool Call Tracking: Traces tool function executions with inputs and outputs
- Model Attribution: Automatic detection of model provider (OpenAI, Anthropic, Google, etc.)
- Usage Metrics: Token usage tracking (input, output, total)
- Streaming Support: Full tracing for streaming responses with chunk counting
- Error Handling: Comprehensive error recording and exception tracking
- Structured Outputs: Support for Pydantic model result types
- Conversation Memory: Message history length tracking
- Multi-model Support: Traces across different model providers
Configuration
Basic Setup
from traceai_pydantic_ai import PydanticAIInstrumentor
# Use default tracer provider
PydanticAIInstrumentor().instrument()
With Custom Tracer Provider
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from traceai_pydantic_ai import PydanticAIInstrumentor
# Setup tracer provider
provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4317"))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# Instrument with provider
PydanticAIInstrumentor().instrument(tracer_provider=provider)
Convenience Function
from traceai_pydantic_ai import instrument_pydantic_ai
# One-liner instrumentation
instrumentor = instrument_pydantic_ai()
Using Built-in Instrumentation
Pydantic AI has its own OpenTelemetry support. You can optionally use that instead:
from traceai_pydantic_ai import instrument_pydantic_ai
# Use Pydantic AI's built-in OTEL (via Agent.instrument_all())
instrument_pydantic_ai(use_builtin=True)
Examples
Basic Agent
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent
# Initialize instrumentation
PydanticAIInstrumentor().instrument()
# Create agent
agent = Agent(
'openai:gpt-4o',
instructions='You are a helpful assistant.'
)
# Synchronous execution
result = agent.run_sync('Hello, how are you?')
print(result.output)
Async Agent
import asyncio
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent
PydanticAIInstrumentor().instrument()
agent = Agent('anthropic:claude-3-sonnet')
async def main():
result = await agent.run('Tell me a joke')
print(result.output)
asyncio.run(main())
Agent with Tools
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent, RunContext
PydanticAIInstrumentor().instrument()
agent = Agent('openai:gpt-4o')
@agent.tool
def get_weather(ctx: RunContext, city: str) -> str:
"""Get the weather for a city."""
return f"The weather in {city} is sunny and 72°F"
@agent.tool
def calculate(ctx: RunContext, expression: str) -> str:
"""Evaluate a mathematical expression."""
return str(eval(expression))
result = agent.run_sync('What is the weather in San Francisco?')
print(result.output)
Structured Outputs
from pydantic import BaseModel
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent
PydanticAIInstrumentor().instrument()
class CityInfo(BaseModel):
name: str
country: str
population: int
agent = Agent('openai:gpt-4o', result_type=CityInfo)
result = agent.run_sync('Tell me about Paris')
city = result.output
print(f"{city.name}, {city.country} - Population: {city.population:,}")
Streaming
import asyncio
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent
PydanticAIInstrumentor().instrument()
agent = Agent('openai:gpt-4o')
async def main():
async with agent.run_stream('Write a haiku about Python') as stream:
async for chunk in stream.stream_text():
print(chunk, end='', flush=True)
asyncio.run(main())
RAG Pipeline
from typing import List
from pydantic import BaseModel, Field
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent, RunContext
PydanticAIInstrumentor().instrument()
class RAGResponse(BaseModel):
answer: str = Field(description="The answer to the question")
sources: List[str] = Field(description="Source documents used")
confidence: float = Field(description="Confidence score 0-1")
agent = Agent('openai:gpt-4o', result_type=RAGResponse)
@agent.tool
def search_documents(ctx: RunContext, query: str) -> str:
"""Search the knowledge base."""
# In production: call vector database
return "Found relevant documents..."
result = agent.run_sync("What are Python best practices?")
print(f"Answer: {result.output.answer}")
print(f"Sources: {result.output.sources}")
Conversation with Memory
import asyncio
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent
PydanticAIInstrumentor().instrument()
agent = Agent('openai:gpt-4o', instructions='Remember details from our conversation.')
async def main():
history = []
# First turn
result = await agent.run("My name is Alice.")
history.append({"role": "user", "content": "My name is Alice."})
history.append({"role": "assistant", "content": str(result.output)})
# Second turn with history
result = await agent.run(
"What's my name?",
message_history=history
)
print(result.output) # Should remember "Alice"
asyncio.run(main())
Multi-Model Routing
from traceai_pydantic_ai import PydanticAIInstrumentor, get_model_provider
from pydantic_ai import Agent
PydanticAIInstrumentor().instrument()
# Route to different models based on task
def get_agent_for_task(task_type: str) -> Agent:
if task_type == "coding":
return Agent("anthropic:claude-3-sonnet")
elif task_type == "analysis":
return Agent("openai:gpt-4o")
else:
return Agent("openai:gpt-4o-mini") # Cost-effective default
agent = get_agent_for_task("coding")
result = agent.run_sync("Write a Python function to calculate fibonacci")
print(f"Provider: {get_model_provider('anthropic:claude-3-sonnet')}")
print(result.output)
Error Handling
from traceai_pydantic_ai import PydanticAIInstrumentor
from pydantic_ai import Agent, RunContext
PydanticAIInstrumentor().instrument()
agent = Agent('openai:gpt-4o')
@agent.tool
def risky_operation(ctx: RunContext, value: int) -> int:
"""Perform a risky operation."""
if value == 0:
raise ValueError("Cannot process zero!")
return value * 2
# Errors are captured in spans with full context
try:
result = agent.run_sync("Process the value 0")
except Exception as e:
print(f"Error captured in trace: {e}")
Traced Attributes
Agent Run Spans
| Attribute | Description |
|---|---|
pydantic_ai.span_kind |
Type of span (agent_run, tool_call, stream) |
pydantic_ai.run.id |
Unique identifier for the run |
pydantic_ai.run.method |
Method used (run, run_sync, run_stream) |
pydantic_ai.run.prompt |
Input prompt |
pydantic_ai.run.result |
Output result |
pydantic_ai.run.is_structured |
Whether output is structured |
pydantic_ai.run.message_history_length |
Number of messages in history |
pydantic_ai.agent.name |
Agent name (if set) |
pydantic_ai.agent.instructions |
Agent instructions |
pydantic_ai.agent.result_type |
Expected result type |
gen_ai.request.model |
Model name |
gen_ai.system |
Model provider |
Usage Metrics
| Attribute | Description |
|---|---|
gen_ai.usage.input_tokens |
Input/prompt tokens |
gen_ai.usage.output_tokens |
Output/completion tokens |
gen_ai.usage.total_tokens |
Total tokens used |
Tool Call Spans
| Attribute | Description |
|---|---|
pydantic_ai.tool.name |
Tool function name |
pydantic_ai.tool.description |
Tool description |
pydantic_ai.tool.args |
Tool arguments (JSON) |
pydantic_ai.tool.result |
Tool return value |
pydantic_ai.tool.is_error |
Whether tool raised error |
pydantic_ai.tool.error_message |
Error message if failed |
pydantic_ai.tool.duration_ms |
Tool execution time |
Streaming Spans
| Attribute | Description |
|---|---|
pydantic_ai.stream.chunk_count |
Number of chunks received |
pydantic_ai.stream.is_structured |
Whether streaming structured data |
Error Attributes
| Attribute | Description |
|---|---|
pydantic_ai.is_error |
Whether an error occurred |
pydantic_ai.error.type |
Exception type |
pydantic_ai.error.message |
Error message |
Performance Attributes
| Attribute | Description |
|---|---|
pydantic_ai.duration_ms |
Total execution time |
pydantic_ai.time_to_first_token_ms |
Time to first token (streaming) |
Model Provider Detection
The instrumentor automatically detects model providers from model names:
| Pattern | Provider |
|---|---|
gpt-*, o1-*, o3-* |
openai |
claude-* |
anthropic |
gemini* |
|
mistral* |
mistral |
deepseek* |
deepseek |
groq* |
groq |
cohere* |
cohere |
ollama* |
ollama |
together* |
together |
openai:* |
openai |
anthropic:* |
anthropic |
bedrock:* |
aws |
vertex:* |
Uninstrumenting
from traceai_pydantic_ai import PydanticAIInstrumentor
instrumentor = PydanticAIInstrumentor()
instrumentor.instrument()
# ... use Pydantic AI ...
# Remove instrumentation
instrumentor.uninstrument()
Integration with FutureAGI
For the best experience, use with FutureAGI's observability platform:
from fi_instrumentation import register
from fi_instrumentation.fi_types import ProjectType
from traceai_pydantic_ai import PydanticAIInstrumentor
# Register with FutureAGI
trace_provider = register(
api_key="your-api-key",
project_type=ProjectType.OBSERVE,
project_name="my-pydantic-ai-app",
)
# Instrument Pydantic AI
PydanticAIInstrumentor().instrument(tracer_provider=trace_provider)
Running Tests
# Run unit tests
pytest tests/
# Run with verbose output
pytest tests/ -v
# Run integration tests (requires pydantic-ai and API key)
OPENAI_API_KEY=your-key pytest tests/ -m integration
Example Scripts
The examples/ directory contains ready-to-run scripts:
| Script | Description |
|---|---|
basic_agent.py |
Simple agent usage |
agent_with_tools.py |
Tool/function calling |
structured_output.py |
Pydantic model outputs |
async_streaming.py |
Streaming responses |
multi_model.py |
Multiple model providers |
rag_pipeline.py |
RAG with retrieval tools |
conversation_memory.py |
Multi-turn conversations |
error_handling.py |
Error capture patterns |
futureagi_integration.py |
FutureAGI platform setup |
Requirements
- Python >= 3.9
- pydantic-ai >= 0.0.1
- opentelemetry-api >= 1.0.0
- opentelemetry-sdk >= 1.0.0
License
MIT License - see LICENSE file for details.
Metadata
Release files for traceai-pydantic-ai 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| traceai_pydantic_ai-0.1.0.tar.gz | 29.2 kB | Without provenance | Details for traceai_pydantic_ai-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| traceai_pydantic_ai-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for traceai_pydantic_ai-0.1.0-py3-none-any.whl |
Total release size: 42.7 kB
Release files / traceai_pydantic_ai-0.1.0.tar.gz
| Download URL | traceai_pydantic_ai-0.1.0.tar.gz |
|---|---|
| Size | 29.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ab5f87d5029b45e176a1f6e7a78f338440142aee89ee80299e5ec9978409d252
|
|
BLAKE2b-256 checksum How to use checksums |
35d91116846504afa5acd5caea3fe2640d4ddd1d1c5593c7984c525f5a36f9f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / traceai_pydantic_ai-0.1.0-py3-none-any.whl
| Download URL | traceai_pydantic_ai-0.1.0-py3-none-any.whl |
|---|---|
| Size | 13.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5573639fbb4995728b77fec4fd623a663913b71b19525cc6b4b205782ebb89fa
|
|
BLAKE2b-256 checksum How to use checksums |
dc9f74fce12b55f644bd121d24c1bc9418ffd9456f399f393e71efbd2b203a65
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for traceai-pydantic-ai 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.