diff --git a/sentry_sdk/integrations/openai_agents/patches/models.py b/sentry_sdk/integrations/openai_agents/patches/models.py index 634c9fdca1..97d11c5cd7 100644 --- a/sentry_sdk/integrations/openai_agents/patches/models.py +++ b/sentry_sdk/integrations/openai_agents/patches/models.py @@ -88,6 +88,15 @@ def _get_model( request_model_name = model.model if hasattr(model, "model") else str(model) agent._sentry_request_model = request_model_name + # Map model class name -> gen_ai operation name. + # This avoids importing internal model classes and is resilient to package + # restructuring while remaining explicit about every supported type. + _OPERATION_BY_MODEL_CLASS = { + "OpenAIResponsesModel": "responses", + "OpenAIChatCompletionsModel": "chat", + } + _operation = _OPERATION_BY_MODEL_CLASS.get(type(model).__name__, "chat") + # Wrap _fetch_response if it exists (for OpenAI models) to capture response model if hasattr(model, "_fetch_response"): original_fetch_response = model._fetch_response @@ -112,7 +121,7 @@ async def wrapped_get_response(*args: "Any", **kwargs: "Any") -> "Any": tool for tool in mcp_tools if isinstance(tool, HostedMCPTool) ] - with ai_client_span(agent, kwargs) as span: + with ai_client_span(agent, kwargs, operation=_operation) as span: for hosted_tool in hosted_tools: _inject_trace_propagation_headers(hosted_tool, span=span) @@ -151,7 +160,7 @@ async def wrapped_stream_response(*args: "Any", **kwargs: "Any") -> "Any": tool for tool in mcp_tools if isinstance(tool, HostedMCPTool) ] - with ai_client_span(agent, span_kwargs) as span: + with ai_client_span(agent, span_kwargs, operation=_operation) as span: for hosted_tool in hosted_tools: _inject_trace_propagation_headers(hosted_tool, span=span) diff --git a/sentry_sdk/integrations/openai_agents/spans/ai_client.py b/sentry_sdk/integrations/openai_agents/spans/ai_client.py index f4f02cb674..fef96a61f6 100644 --- a/sentry_sdk/integrations/openai_agents/spans/ai_client.py +++ b/sentry_sdk/integrations/openai_agents/spans/ai_client.py @@ -20,9 +20,10 @@ def ai_client_span( - agent: "Agent", get_response_kwargs: "dict[str, Any]" + agent: "Agent", + get_response_kwargs: "dict[str, Any]", + operation: str = "chat", ) -> "Union[sentry_sdk.tracing.Span, StreamedSpan]": - # TODO-anton: implement other types of operations. Now "chat" is hardcoded. # Get model name from agent.model or fall back to request model (for when agent.model is None/default) model_name = None if agent.model: @@ -33,21 +34,20 @@ def ai_client_span( span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options) if span_streaming: span = sentry_sdk.traces.start_span( - name=f"chat {model_name}", + name=f"{operation} {model_name}", attributes={ "sentry.op": OP.GEN_AI_CHAT, "sentry.origin": SPAN_ORIGIN, - SPANDATA.GEN_AI_OPERATION_NAME: "chat", + SPANDATA.GEN_AI_OPERATION_NAME: operation, }, ) else: span = sentry_sdk.start_span( op=OP.GEN_AI_CHAT, - name=f"chat {model_name}", + name=f"{operation} {model_name}", origin=SPAN_ORIGIN, ) - # TODO-anton: remove hardcoded stuff and replace something that also works for embedding and so on - span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "chat") + span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, operation) _set_agent_data(span, agent) _set_input_data(span, get_response_kwargs) diff --git a/tests/integrations/openai_agents/test_openai_agents.py b/tests/integrations/openai_agents/test_openai_agents.py index cb3a18582f..fae6239549 100644 --- a/tests/integrations/openai_agents/test_openai_agents.py +++ b/tests/integrations/openai_agents/test_openai_agents.py @@ -246,8 +246,8 @@ async def test_agent_invocation_span_no_pii( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -309,8 +309,8 @@ async def test_agent_invocation_span_no_pii( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -362,8 +362,8 @@ async def test_agent_invocation_span_no_pii( assert invoke_agent_span["data"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["data"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["description"] == "chat gpt-4" - assert ai_client_span["data"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["description"] == "responses gpt-4" + assert ai_client_span["data"]["gen_ai.operation.name"] == "responses" assert ai_client_span["data"]["gen_ai.system"] == "openai" assert ai_client_span["data"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["data"]["gen_ai.request.max_tokens"] == 100 @@ -652,8 +652,8 @@ async def test_agent_invocation_span( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -722,8 +722,8 @@ async def test_agent_invocation_span( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -788,8 +788,8 @@ async def test_agent_invocation_span( assert invoke_agent_span["data"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["data"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["description"] == "chat gpt-4" - assert ai_client_span["data"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["description"] == "responses gpt-4" + assert ai_client_span["data"]["gen_ai.operation.name"] == "responses" assert ai_client_span["data"]["gen_ai.system"] == "openai" assert ai_client_span["data"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["data"]["gen_ai.request.max_tokens"] == 100 @@ -854,7 +854,7 @@ async def test_client_span_custom_model( span for span in spans if span["attributes"]["sentry.op"] == OP.GEN_AI_CHAT ) - assert ai_client_span["name"] == "chat my-custom-model" + assert ai_client_span["name"] == "responses my-custom-model" assert ai_client_span["attributes"]["gen_ai.request.model"] == "my-custom-model" else: with patch.object( @@ -880,7 +880,7 @@ async def test_client_span_custom_model( spans = transaction["spans"] ai_client_span = next(span for span in spans if span["op"] == OP.GEN_AI_CHAT) - assert ai_client_span["description"] == "chat my-custom-model" + assert ai_client_span["description"] == "responses my-custom-model" assert ai_client_span["data"]["gen_ai.request.model"] == "my-custom-model" @@ -958,8 +958,8 @@ def test_agent_invocation_span_sync_no_pii( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -1018,8 +1018,8 @@ def test_agent_invocation_span_sync_no_pii( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -1070,8 +1070,8 @@ def test_agent_invocation_span_sync_no_pii( assert invoke_agent_span["data"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["data"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["description"] == "chat gpt-4" - assert ai_client_span["data"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["description"] == "responses gpt-4" + assert ai_client_span["data"]["gen_ai.operation.name"] == "responses" assert ai_client_span["data"]["gen_ai.system"] == "openai" assert ai_client_span["data"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["data"]["gen_ai.request.max_tokens"] == 100 @@ -1344,8 +1344,8 @@ def test_agent_invocation_span_sync( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -1406,8 +1406,8 @@ def test_agent_invocation_span_sync( assert invoke_agent_span["attributes"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["name"] == "chat gpt-4" - assert ai_client_span["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["name"] == "responses gpt-4" + assert ai_client_span["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span["attributes"]["gen_ai.system"] == "openai" assert ai_client_span["attributes"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["attributes"]["gen_ai.request.max_tokens"] == 100 @@ -1466,8 +1466,8 @@ def test_agent_invocation_span_sync( assert invoke_agent_span["data"]["gen_ai.request.temperature"] == 0.7 assert invoke_agent_span["data"]["gen_ai.request.top_p"] == 1.0 - assert ai_client_span["description"] == "chat gpt-4" - assert ai_client_span["data"]["gen_ai.operation.name"] == "chat" + assert ai_client_span["description"] == "responses gpt-4" + assert ai_client_span["data"]["gen_ai.operation.name"] == "responses" assert ai_client_span["data"]["gen_ai.system"] == "openai" assert ai_client_span["data"]["gen_ai.agent.name"] == "test_agent" assert ai_client_span["data"]["gen_ai.request.max_tokens"] == 100 @@ -2006,8 +2006,8 @@ def simple_test_tool(message: str) -> str: assert agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 assert agent_span["attributes"]["gen_ai.system"] == "openai" - assert ai_client_span1["name"] == "chat gpt-4" - assert ai_client_span1["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span1["name"] == "responses gpt-4" + assert ai_client_span1["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span1["attributes"]["gen_ai.system"] == "openai" assert ai_client_span1["attributes"]["gen_ai.agent.name"] == "test_agent" @@ -2080,9 +2080,9 @@ def simple_test_tool(message: str) -> str: assert ( tool_span["attributes"]["gen_ai.tool.output"] == "Tool executed with: hello" ) - assert ai_client_span2["name"] == "chat gpt-4" + assert ai_client_span2["name"] == "responses gpt-4" assert ai_client_span2["attributes"]["gen_ai.agent.name"] == "test_agent" - assert ai_client_span2["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span2["attributes"]["gen_ai.operation.name"] == "responses" ai_client_span2_available_tool = json.loads( ai_client_span2["attributes"]["gen_ai.request.available_tools"] @@ -2238,8 +2238,8 @@ def simple_test_tool(message: str) -> str: assert agent_span["attributes"]["gen_ai.request.top_p"] == 1.0 assert agent_span["attributes"]["gen_ai.system"] == "openai" - assert ai_client_span1["name"] == "chat gpt-4" - assert ai_client_span1["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span1["name"] == "responses gpt-4" + assert ai_client_span1["attributes"]["gen_ai.operation.name"] == "responses" assert ai_client_span1["attributes"]["gen_ai.system"] == "openai" assert ai_client_span1["attributes"]["gen_ai.agent.name"] == "test_agent" @@ -2312,9 +2312,9 @@ def simple_test_tool(message: str) -> str: assert ( tool_span["attributes"]["gen_ai.tool.output"] == "Tool executed with: hello" ) - assert ai_client_span2["name"] == "chat gpt-4" + assert ai_client_span2["name"] == "responses gpt-4" assert ai_client_span2["attributes"]["gen_ai.agent.name"] == "test_agent" - assert ai_client_span2["attributes"]["gen_ai.operation.name"] == "chat" + assert ai_client_span2["attributes"]["gen_ai.operation.name"] == "responses" ai_client_span2_available_tool = json.loads( ai_client_span2["attributes"]["gen_ai.request.available_tools"] @@ -2463,8 +2463,8 @@ def simple_test_tool(message: str) -> str: assert agent_span["data"]["gen_ai.request.top_p"] == 1.0 assert agent_span["data"]["gen_ai.system"] == "openai" - assert ai_client_span1["description"] == "chat gpt-4" - assert ai_client_span1["data"]["gen_ai.operation.name"] == "chat" + assert ai_client_span1["description"] == "responses gpt-4" + assert ai_client_span1["data"]["gen_ai.operation.name"] == "responses" assert ai_client_span1["data"]["gen_ai.system"] == "openai" assert ai_client_span1["data"]["gen_ai.agent.name"] == "test_agent" @@ -2529,9 +2529,9 @@ def simple_test_tool(message: str) -> str: assert tool_span["data"]["gen_ai.tool.input"] == '{"message": "hello"}' assert tool_span["data"]["gen_ai.tool.name"] == "simple_test_tool" assert tool_span["data"]["gen_ai.tool.output"] == "Tool executed with: hello" - assert ai_client_span2["description"] == "chat gpt-4" + assert ai_client_span2["description"] == "responses gpt-4" assert ai_client_span2["data"]["gen_ai.agent.name"] == "test_agent" - assert ai_client_span2["data"]["gen_ai.operation.name"] == "chat" + assert ai_client_span2["data"]["gen_ai.operation.name"] == "responses" ai_client_span2_available_tool = json.loads( ai_client_span2["data"]["gen_ai.request.available_tools"] @@ -3075,7 +3075,7 @@ async def test_error_handling( invoke_agent_span["attributes"]["sentry.origin"] == "auto.ai.openai_agents" ) - assert ai_client_span["name"] == "chat gpt-4" + assert ai_client_span["name"] == "responses gpt-4" assert ai_client_span["attributes"]["sentry.origin"] == "auto.ai.openai_agents" assert ai_client_span["status"] == "error" elif stream_gen_ai_spans: @@ -3122,7 +3122,7 @@ async def test_error_handling( invoke_agent_span["attributes"]["sentry.origin"] == "auto.ai.openai_agents" ) - assert ai_client_span["name"] == "chat gpt-4" + assert ai_client_span["name"] == "responses gpt-4" assert ai_client_span["attributes"]["sentry.origin"] == "auto.ai.openai_agents" assert ai_client_span["status"] == "error" else: @@ -3167,7 +3167,7 @@ async def test_error_handling( assert invoke_agent_span["description"] == "invoke_agent test_agent" assert invoke_agent_span["origin"] == "auto.ai.openai_agents" - assert ai_client_span["description"] == "chat gpt-4" + assert ai_client_span["description"] == "responses gpt-4" assert ai_client_span["origin"] == "auto.ai.openai_agents" assert ai_client_span["status"] == "internal_error" assert ai_client_span["tags"]["status"] == "internal_error" @@ -3238,7 +3238,7 @@ async def test_error_captures_input_data( s for s in spans if s["attributes"].get("sentry.op", "") == "gen_ai.chat" ][0] - assert ai_client_span["name"] == "chat gpt-4" + assert ai_client_span["name"] == "responses gpt-4" assert ai_client_span["status"] == "error" assert "gen_ai.request.messages" in ai_client_span["attributes"] @@ -3282,7 +3282,7 @@ async def test_error_captures_input_data( spans = transaction["spans"] ai_client_span = [s for s in spans if s["op"] == "gen_ai.chat"][0] - assert ai_client_span["description"] == "chat gpt-4" + assert ai_client_span["description"] == "responses gpt-4" assert ai_client_span["status"] == "internal_error" assert ai_client_span["tags"]["status"] == "internal_error" @@ -3963,7 +3963,7 @@ async def test_ai_client_span_includes_response_model( ) # Verify ai_client span has response model from API response - assert ai_client_span["name"] == "chat gpt-4" + assert ai_client_span["name"] == "responses gpt-4" assert "gen_ai.response.model" in ai_client_span["attributes"] assert ( ai_client_span["attributes"]["gen_ai.response.model"] @@ -3994,7 +3994,7 @@ async def test_ai_client_span_includes_response_model( ai_client_span = next(span for span in spans if span["op"] == OP.GEN_AI_CHAT) # Verify ai_client span has response model from API response - assert ai_client_span["description"] == "chat gpt-4" + assert ai_client_span["description"] == "responses gpt-4" assert "gen_ai.response.model" in ai_client_span["data"] assert ai_client_span["data"]["gen_ai.response.model"] == "gpt-4.1-2025-04-14"