AI Monitoring — Sentry Python SDK
Minimum SDK:
sentry-sdk2.1.0+ (core AI spans); 2.45.0+ for auto-enabling all integrations
Prerequisites
Tracing must be enabled — AI spans require an active transaction:
sentry_sdk.init(dsn="...", traces_sample_rate=1.0)Integration Matrix
| Integration | Package | Min Library | Auto-Enabled | Status |
|---|---|---|---|---|
| OpenAI | sentry-sdk |
openai 1.0+ | ✅ Yes | Stable |
| Anthropic | sentry-sdk |
anthropic 0.16.0+ | ✅ Yes | Stable |
| LangChain | sentry-sdk |
langchain 0.1.0+ | ✅ Yes | Stable |
| LangGraph | sentry-sdk |
langgraph 0.6.6+ | ✅ Yes | Stable |
| OpenAI Agents SDK | sentry-sdk |
agents 0.0.19+ | ✅ Yes | ⚠️ Beta |
| Google GenAI | sentry-sdk |
google-genai 1.29.0+ | ✅ Yes | Stable |
| HuggingFace Hub | sentry-sdk |
huggingface_hub 0.24.7+ | ✅ Yes | Stable |
| LiteLLM | sentry-sdk |
litellm 1.77.5+ | ❌ No | Stable |
| MCP | sentry-sdk |
mcp 1.15.0+ | ✅ Yes | Stable |
| Pydantic AI | sentry-sdk |
pydantic-ai 1.0.0+ | ✅ Yes | ⚠️ Beta |
LiteLLM MUST be explicitly added to integrations=[].
PII Control
Every integration follows the same two-layer control:
send_default_pii |
include_prompts |
Prompts/outputs sent? |
|---|---|---|
False (default) |
True (default) |
❌ No |
True |
True (default) |
✅ Yes |
True |
False |
❌ No |
Set send_default_pii=True to capture prompts. Use include_prompts=False per-integration to override.
Configuration Examples
Auto-enabled integrations (OpenAI, Anthropic, LangChain, etc.)
import sentry_sdk
sentry_sdk.init(
dsn="https://<key>@<org>.ingest.sentry.io/<project>",
traces_sample_rate=1.0,
send_default_pii=True, # required to capture prompts/outputs
)
# OpenAI, Anthropic, LangChain, LangGraph, HuggingFace Hub activate automaticallyExplicit configuration with include_prompts override
import sentry_sdk
from sentry_sdk.integrations.openai import OpenAIIntegration
from sentry_sdk.integrations.anthropic import AnthropicIntegration
sentry_sdk.init(
dsn="...",
traces_sample_rate=1.0,
send_default_pii=True,
integrations=[
OpenAIIntegration(
include_prompts=True,
tiktoken_encoding_name="o200k_base", # for gpt-4o streaming token counts
),
AnthropicIntegration(include_prompts=True),
],
)Integrations that require explicit registration
import sentry_sdk
from sentry_sdk.integrations.litellm import LiteLLMIntegration
sentry_sdk.init(
dsn="...",
traces_sample_rate=1.0,
send_default_pii=True,
integrations=[
LiteLLMIntegration(include_prompts=True), # 100+ providers via proxy
],
)Usage examples
from openai import OpenAI
import sentry_sdk
client = OpenAI()
with sentry_sdk.start_transaction(name="AI inference", op="ai-inference"):
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Say hello"}],
)import anthropic, sentry_sdk
client = anthropic.Anthropic()
with sentry_sdk.start_transaction(name="claude-request"):
message = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain async/await"}],
)Manual Instrumentation — gen_ai.* Spans
Use when the library isn't supported, or for wrapping custom AI logic.
gen_ai.request — Raw LLM call
import sentry_sdk, json
messages = [{"role": "user", "content": "Tell me a joke"}]
with sentry_sdk.start_span(op="gen_ai.request", name="chat gpt-4o") as span:
span.set_data("gen_ai.request.model", "gpt-4o")
span.set_data("gen_ai.request.messages", json.dumps(messages)) # must JSON-stringify
span.set_data("gen_ai.request.temperature", 0.7)
span.set_data("gen_ai.request.max_tokens", 500)
result = my_llm_client.chat(model="gpt-4o", messages=messages)
span.set_data("gen_ai.response.text", json.dumps([result.choices[0].message.content]))
span.set_data("gen_ai.usage.input_tokens", result.usage.prompt_tokens)
span.set_data("gen_ai.usage.output_tokens", result.usage.completion_tokens)
span.set_data("gen_ai.usage.total_tokens", result.usage.total_tokens)gen_ai.invoke_agent — Full agent lifecycle
import sentry_sdk
with sentry_sdk.start_span(op="gen_ai.invoke_agent",
name="invoke_agent Weather Agent") as span:
span.set_data("gen_ai.request.model", "gpt-4o")
span.set_data("gen_ai.agent.name", "Weather Agent")
final_output = my_agent.run(task="What's the weather in Paris?")
span.set_data("gen_ai.response.text", str(final_output))
span.set_data("gen_ai.usage.input_tokens", my_agent.usage.input_tokens)
span.set_data("gen_ai.usage.output_tokens", my_agent.usage.output_tokens)gen_ai.execute_tool — Tool/function call
import sentry_sdk, json
with sentry_sdk.start_span(op="gen_ai.execute_tool",
name="execute_tool get_weather") as span:
span.set_data("gen_ai.tool.name", "get_weather")
span.set_data("gen_ai.tool.type", "function") # "function"|"extension"|"datastore"
span.set_data("gen_ai.tool.input", json.dumps({"location": "Paris"}))
result = get_weather(location="Paris")
span.set_data("gen_ai.tool.output", json.dumps(result))gen_ai.handoff — Agent-to-agent transition
import sentry_sdk
with sentry_sdk.start_span(op="gen_ai.handoff",
name="handoff Billing → Refund Agent") as span:
span.set_data("gen_ai.agent.name", "Refund Agent")
result = refund_agent.run(context=billing_context)Span Attribute Reference
Common attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
gen_ai.request.model |
string | ✅ | Model identifier (e.g., gpt-4o, claude-opus-4-5) |
gen_ai.operation.name |
string | No | Human-readable operation label |
gen_ai.agent.name |
string | No | Agent name (for agent spans) |
Model config attributes
| Attribute | Type |
|---|---|
gen_ai.request.temperature |
float |
gen_ai.request.max_tokens |
int |
gen_ai.request.top_p |
float |
gen_ai.request.frequency_penalty |
float |
gen_ai.request.presence_penalty |
float |
Content attributes (PII-gated — only when send_default_pii=True + include_prompts=True)
| Attribute | Type | Description |
|---|---|---|
gen_ai.request.messages |
string | JSON-stringified message array |
gen_ai.request.available_tools |
string | JSON-stringified tool definitions |
gen_ai.response.text |
string | JSON-stringified response array |
gen_ai.response.tool_calls |
string | JSON-stringified tool call array |
⚠️ Span attributes only accept primitives — arrays/objects must be JSON-stringified before calling
span.set_data().
Token usage attributes
| Attribute | Type | Description |
|---|---|---|
gen_ai.usage.input_tokens |
int | Total input tokens (including cached) |
gen_ai.usage.input_tokens.cached |
int | Subset served from cache |
gen_ai.usage.input_tokens.cache_write |
int | Tokens written to cache (Anthropic) |
gen_ai.usage.output_tokens |
int | Total output tokens (including reasoning) |
gen_ai.usage.output_tokens.reasoning |
int | Subset for chain-of-thought reasoning |
gen_ai.usage.total_tokens |
int | Sum of input + output |
⚠️ Cached and reasoning tokens are subsets of totals, not additive. Incorrect reporting produces wrong cost calculations in the dashboard.
Agent Workflow Hierarchy
Transaction
└── gen_ai.invoke_agent "Weather Agent"
├── gen_ai.request "chat gpt-4o"
├── gen_ai.execute_tool "get_weather"
├── gen_ai.request "chat gpt-4o" ← follow-up
└── gen_ai.handoff "→ Report Writer"
└── gen_ai.invoke_agent "Report Writer"
├── gen_ai.request "chat gpt-4o"
└── gen_ai.execute_tool "format_report"This populates the AI Agents Dashboard in Sentry with per-agent latency, tool call rates, token consumption, and model cost attribution.
Conversation tracking (Alpha)
Requires SDK ≥ 2.51.0
import sentry_sdk
# Link spans across turns in a multi-turn conversation
sentry_sdk.ai.set_conversation_id("user-session-abc123")
# All subsequent AI spans carry gen_ai.conversation.id = "user-session-abc123"Streaming
| Integration | Streaming | Token counts in streams |
|---|---|---|
| OpenAI | ✅ | Requires tiktoken>=0.3.0; set tiktoken_encoding_name |
| Anthropic | ✅ | Automatic |
| LangChain | ✅ | Tracked |
| LiteLLM | ✅ | Tracked |
Manual gen_ai.* |
✅ | Set token counts after stream completes |
Unsupported Providers
| Provider | Workaround |
|---|---|
| Cohere | Use LiteLLMIntegration or manual gen_ai.* spans |
| AWS Bedrock | Manual instrumentation |
| Mistral | LiteLLMIntegration |
| Groq | LiteLLMIntegration |
| Vertex AI | GoogleGenAIIntegration or LiteLLMIntegration |
Troubleshooting
| Issue | Solution |
|---|---|
| No AI spans appearing | Verify traces_sample_rate > 0; wrap calls in a transaction |
| Prompts not captured | Set send_default_pii=True and verify include_prompts=True (default) |
| LiteLLM not tracked | LiteLLM is NOT auto-enabled — add LiteLLMIntegration to integrations=[] explicitly |
| Token counts missing for OpenAI streaming | Install tiktoken>=0.3.0 and set tiktoken_encoding_name |
| AI Agents Dashboard empty | Wrap agent runs in gen_ai.invoke_agent spans |
| Wrong cost calculations | Ensure cached/reasoning token counts are subsets of totals, not additions |