diff --git a/sentry_sdk/integrations/pydantic_ai/__init__.py b/sentry_sdk/integrations/pydantic_ai/__init__.py index eb40732d7a..26434109e7 100644 --- a/sentry_sdk/integrations/pydantic_ai/__init__.py +++ b/sentry_sdk/integrations/pydantic_ai/__init__.py @@ -1,6 +1,5 @@ from sentry_sdk.integrations import DidNotEnable, Integration - try: import pydantic_ai # type: ignore # noqa: F401 except ImportError: diff --git a/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py b/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py index eaa4385834..cc6bd602b0 100644 --- a/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +++ b/sentry_sdk/integrations/pydantic_ai/patches/agent_run.py @@ -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__() @@ -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) diff --git a/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py b/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py index 6e638505a6..dd75f912f2 100644 --- a/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +++ b/sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py @@ -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, @@ -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 @@ -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 diff --git a/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py b/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py index dc95acad45..e376f8097c 100644 --- a/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +++ b/sentry_sdk/integrations/pydantic_ai/spans/ai_client.py @@ -17,7 +17,6 @@ _should_send_prompts, _get_model_name, get_current_agent, - get_is_streaming, ) from .utils import ( _serialize_binary_content_item, @@ -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) diff --git a/sentry_sdk/integrations/pydantic_ai/utils.py b/sentry_sdk/integrations/pydantic_ai/utils.py index 62d36fb912..c6dd658d20 100644 --- a/sentry_sdk/integrations/pydantic_ai/utils.py +++ b/sentry_sdk/integrations/pydantic_ai/utils.py @@ -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) @@ -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. diff --git a/tests/integrations/pydantic_ai/test_pydantic_ai.py b/tests/integrations/pydantic_ai/test_pydantic_ai.py index 6776a45039..fd87a209cc 100644 --- a/tests/integrations/pydantic_ai/test_pydantic_ai.py +++ b/tests/integrations/pydantic_ai/test_pydantic_ai.py @@ -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