Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion sentry_sdk/integrations/pydantic_ai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from sentry_sdk.integrations import DidNotEnable, Integration


try:
import pydantic_ai # type: ignore # noqa: F401
except ImportError:
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/pydantic_ai/patches/agent_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ async def __aenter__(self) -> "Any":

# Push agent to contextvar stack after span is successfully created and entered
# This ensures proper pairing with pop_agent() in __aexit__ even if exceptions occur
push_agent(self.agent, self.is_streaming)
push_agent(self.agent)

# Enter the original context manager
result = await self.original_ctx_manager.__aenter__()
Expand Down Expand Up @@ -113,7 +113,7 @@ async def wrapper(self: "Any", *args: "Any", **kwargs: "Any") -> "Any":
) as span:
# Push agent to contextvar stack after span is successfully created and entered
# This ensures proper pairing with pop_agent() in finally even if exceptions occur
push_agent(self, is_streaming)
push_agent(self)

try:
result = await original_func(self, *args, **kwargs)
Expand Down
5 changes: 5 additions & 0 deletions sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from functools import wraps

from sentry_sdk.integrations import DidNotEnable
from sentry_sdk.consts import SPANDATA

from ..spans import (
ai_client_span,
Expand Down Expand Up @@ -65,6 +66,8 @@ async def wrapped_model_request_run(self: "Any", ctx: "Any") -> "Any":
messages, model, model_settings = _extract_span_data(self, ctx)

with ai_client_span(messages, None, model, model_settings) as span:
span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, False)

result = await original_model_request_run(self, ctx)

# Extract response from result if available
Expand Down Expand Up @@ -97,6 +100,8 @@ async def wrapped_model_request_stream(self: "Any", ctx: "Any") -> "Any":

# Create chat span for streaming request
with ai_client_span(messages, None, model, model_settings) as span:
span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, True)

# Call the original stream method
async with original_stream_method(self, ctx) as stream:
yield stream
Expand Down
4 changes: 0 additions & 4 deletions sentry_sdk/integrations/pydantic_ai/spans/ai_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
_should_send_prompts,
_get_model_name,
get_current_agent,
get_is_streaming,
)
from .utils import (
_serialize_binary_content_item,
Expand Down Expand Up @@ -264,9 +263,6 @@ def ai_client_span(
_set_agent_data(span, agent)
_set_model_data(span, model, model_settings)

# Set streaming flag from contextvar
span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, get_is_streaming())

# Add available tools if agent is available
agent_obj = agent or get_current_agent()
_set_available_tools(span, agent_obj)
Expand Down
16 changes: 4 additions & 12 deletions sentry_sdk/integrations/pydantic_ai/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
)


def push_agent(agent: "Any", is_streaming: bool = False) -> None:
"""Push an agent context onto the stack along with its streaming flag."""
def push_agent(agent: "Any") -> None:
"""Push an agent context onto the stack."""
stack = _agent_context_stack.get().copy()
stack.append({"agent": agent, "is_streaming": is_streaming})
stack.append(agent)
_agent_context_stack.set(stack)


Expand All @@ -37,18 +37,10 @@ def get_current_agent() -> "Any":
"""Get the current agent from the contextvar stack."""
stack = _agent_context_stack.get()
if stack:
return stack[-1]["agent"]
return stack[-1]
return None


def get_is_streaming() -> bool:
"""Get the streaming flag from the contextvar stack."""
stack = _agent_context_stack.get()
if stack:
return stack[-1].get("is_streaming", False)
return False


def _should_send_prompts() -> bool:
"""
Check if prompts should be sent to Sentry.
Expand Down
2 changes: 1 addition & 1 deletion tests/integrations/pydantic_ai/test_pydantic_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ async def test_agent_run_stream_events(sentry_init, capture_events, test_agent):
assert len(chat_spans) == 1

for chat_span in chat_spans:
assert chat_span["data"]["gen_ai.response.streaming"] is False
assert chat_span["data"]["gen_ai.response.streaming"] is True


@pytest.mark.asyncio
Expand Down
Loading