|
12 | 12 | from sentry_sdk.integrations.logging import LoggingIntegration |
13 | 13 | from sentry_sdk.integrations.openai_agents import OpenAIAgentsIntegration |
14 | 14 | from sentry_sdk.integrations.openai_agents.utils import _set_input_data, safe_serialize |
15 | | -from sentry_sdk.utils import parse_version |
| 15 | +from sentry_sdk.utils import parse_version, package_version |
16 | 16 |
|
17 | 17 | from openai import AsyncOpenAI |
18 | 18 | from agents.models.openai_responses import OpenAIResponsesModel |
|
37 | 37 | from agents.exceptions import MaxTurnsExceeded, ModelBehaviorError |
38 | 38 | from agents.version import __version__ as OPENAI_AGENTS_VERSION |
39 | 39 |
|
| 40 | +OPENAI_VERSION = package_version("openai") |
| 41 | + |
40 | 42 | from openai.types.responses import ( |
41 | 43 | ResponseCreatedEvent, |
42 | 44 | ResponseTextDeltaEvent, |
@@ -1256,18 +1258,22 @@ def simple_test_tool(message: str) -> str: |
1256 | 1258 | assert ai_client_span1["data"]["gen_ai.usage.output_tokens"] == 5 |
1257 | 1259 | assert ai_client_span1["data"]["gen_ai.usage.output_tokens.reasoning"] == 0 |
1258 | 1260 | assert ai_client_span1["data"]["gen_ai.usage.total_tokens"] == 15 |
1259 | | - assert ai_client_span1["data"]["gen_ai.response.tool_calls"] == safe_serialize( |
1260 | | - [ |
1261 | | - { |
1262 | | - "arguments": '{"message": "hello"}', |
1263 | | - "call_id": "call_123", |
1264 | | - "name": "simple_test_tool", |
1265 | | - "type": "function_call", |
1266 | | - "id": "call_123", |
1267 | | - "status": None, |
1268 | | - } |
1269 | | - ] |
1270 | | - ) |
| 1261 | + |
| 1262 | + tool_call = { |
| 1263 | + "arguments": '{"message": "hello"}', |
| 1264 | + "call_id": "call_123", |
| 1265 | + "name": "simple_test_tool", |
| 1266 | + "type": "function_call", |
| 1267 | + "id": "call_123", |
| 1268 | + "status": None, |
| 1269 | + } |
| 1270 | + |
| 1271 | + if OPENAI_VERSION >= (2, 25, 0): |
| 1272 | + tool_call["namespace"] = None |
| 1273 | + |
| 1274 | + assert json.loads(ai_client_span1["data"]["gen_ai.response.tool_calls"]) == [ |
| 1275 | + tool_call |
| 1276 | + ] |
1271 | 1277 |
|
1272 | 1278 | assert tool_span["description"] == "execute_tool simple_test_tool" |
1273 | 1279 | assert tool_span["data"]["gen_ai.agent.name"] == "test_agent" |
@@ -2507,75 +2513,6 @@ def calculator(a: int, b: int) -> int: |
2507 | 2513 | assert invoke_agent_span["data"]["gen_ai.usage.output_tokens.reasoning"] == 3 |
2508 | 2514 |
|
2509 | 2515 |
|
2510 | | -@pytest.mark.asyncio |
2511 | | -async def test_response_model_not_set_when_unavailable( |
2512 | | - sentry_init, capture_events, test_agent |
2513 | | -): |
2514 | | - """ |
2515 | | - Test that response model is not set if the API response doesn't have a model field. |
2516 | | - The request model should still be set correctly. |
2517 | | - """ |
2518 | | - |
2519 | | - with patch.dict(os.environ, {"OPENAI_API_KEY": "test-key"}): |
2520 | | - with patch( |
2521 | | - "agents.models.openai_responses.OpenAIResponsesModel._fetch_response" |
2522 | | - ) as mock_fetch_response: |
2523 | | - # Create a mock response without a model field |
2524 | | - mock_response = MagicMock() |
2525 | | - mock_response.model = None # No model in response |
2526 | | - mock_response.id = "resp_123" |
2527 | | - mock_response.output = [ |
2528 | | - ResponseOutputMessage( |
2529 | | - id="msg_123", |
2530 | | - type="message", |
2531 | | - status="completed", |
2532 | | - content=[ |
2533 | | - ResponseOutputText( |
2534 | | - text="Response without model field", |
2535 | | - type="output_text", |
2536 | | - annotations=[], |
2537 | | - ) |
2538 | | - ], |
2539 | | - role="assistant", |
2540 | | - ) |
2541 | | - ] |
2542 | | - mock_response.usage = MagicMock() |
2543 | | - mock_response.usage.input_tokens = 10 |
2544 | | - mock_response.usage.output_tokens = 20 |
2545 | | - mock_response.usage.total_tokens = 30 |
2546 | | - mock_response.usage.input_tokens_details = InputTokensDetails( |
2547 | | - cached_tokens=0 |
2548 | | - ) |
2549 | | - mock_response.usage.output_tokens_details = OutputTokensDetails( |
2550 | | - reasoning_tokens=0 |
2551 | | - ) |
2552 | | - |
2553 | | - mock_fetch_response.return_value = mock_response |
2554 | | - |
2555 | | - sentry_init( |
2556 | | - integrations=[OpenAIAgentsIntegration()], |
2557 | | - traces_sample_rate=1.0, |
2558 | | - ) |
2559 | | - |
2560 | | - events = capture_events() |
2561 | | - |
2562 | | - result = await agents.Runner.run( |
2563 | | - test_agent, "Test input", run_config=test_run_config |
2564 | | - ) |
2565 | | - |
2566 | | - assert result is not None |
2567 | | - |
2568 | | - (transaction,) = events |
2569 | | - spans = transaction["spans"] |
2570 | | - _, ai_client_span = spans |
2571 | | - |
2572 | | - # Response model should NOT be set when API doesn't return it |
2573 | | - assert "gen_ai.response.model" not in ai_client_span["data"] |
2574 | | - # But request model should still be set |
2575 | | - assert "gen_ai.request.model" in ai_client_span["data"] |
2576 | | - assert ai_client_span["data"]["gen_ai.request.model"] == "gpt-4" |
2577 | | - |
2578 | | - |
2579 | 2516 | @pytest.mark.asyncio |
2580 | 2517 | async def test_invoke_agent_span_includes_response_model( |
2581 | 2518 | sentry_init, capture_events, test_agent |
|
0 commit comments