From c16daa0e6de9ad4dee75cff0fe73d424e5ba172d Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 7 Sep 2026 13:40:39 +0200 Subject: [PATCH 1/5] fix(openai-agents): Use isinstance to determine tool type --- sentry_sdk/integrations/openai_agents/patches/tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/openai_agents/patches/tools.py b/sentry_sdk/integrations/openai_agents/patches/tools.py index 8ba398a27e..2ddf7e7592 100644 --- a/sentry_sdk/integrations/openai_agents/patches/tools.py +++ b/sentry_sdk/integrations/openai_agents/patches/tools.py @@ -15,6 +15,7 @@ try: import agents + from agents import FunctionTool except ImportError: raise DidNotEnable("OpenAI Agents not installed") @@ -33,7 +34,7 @@ async def _get_all_tools( wrapped_tools = [] for tool in tools: # Wrap only the function tools (for now) - if tool.__class__.__name__ != "FunctionTool": + if not isinstance(tool, FunctionTool): wrapped_tools.append(tool) continue From c34c88501416510749f37b52c50b75add5eebc0a Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 7 Sep 2026 13:51:57 +0200 Subject: [PATCH 2/5] fix(openai-agents): Stop setting response model based on the request model --- sentry_sdk/integrations/openai_agents/patches/models.py | 4 ---- sentry_sdk/integrations/openai_agents/spans/ai_client.py | 2 -- sentry_sdk/integrations/openai_agents/utils.py | 2 -- 3 files changed, 8 deletions(-) diff --git a/sentry_sdk/integrations/openai_agents/patches/models.py b/sentry_sdk/integrations/openai_agents/patches/models.py index 8f708dafce..c671b40f11 100644 --- a/sentry_sdk/integrations/openai_agents/patches/models.py +++ b/sentry_sdk/integrations/openai_agents/patches/models.py @@ -71,10 +71,6 @@ def _get_model( # because we only patch its direct methods, all underlying data can remain unchanged. model = copy.copy(original_get_model(agent, run_config)) - # Capture the request model name for spans (agent.model can be None when using defaults) - request_model_name = model.model if hasattr(model, "model") else str(model) - agent._sentry_request_model = request_model_name - # Wrap _fetch_response if it exists (for OpenAI models) to capture response model if hasattr(model, "_fetch_response"): original_fetch_response = model._fetch_response diff --git a/sentry_sdk/integrations/openai_agents/spans/ai_client.py b/sentry_sdk/integrations/openai_agents/spans/ai_client.py index d075d3c9a8..0e94822999 100644 --- a/sentry_sdk/integrations/openai_agents/spans/ai_client.py +++ b/sentry_sdk/integrations/openai_agents/spans/ai_client.py @@ -205,8 +205,6 @@ def ai_client_span( model_name = None if agent.model: model_name = agent.model.model if hasattr(agent.model, "model") else agent.model - elif hasattr(agent, "_sentry_request_model"): - model_name = agent._sentry_request_model client_options = sentry_sdk.get_client().options diff --git a/sentry_sdk/integrations/openai_agents/utils.py b/sentry_sdk/integrations/openai_agents/utils.py index fa9237e5e9..b5e24eafea 100644 --- a/sentry_sdk/integrations/openai_agents/utils.py +++ b/sentry_sdk/integrations/openai_agents/utils.py @@ -68,8 +68,6 @@ def _set_agent_data( model_name = None if agent.model: model_name = agent.model.model if hasattr(agent.model, "model") else agent.model - elif hasattr(agent, "_sentry_request_model"): - model_name = agent._sentry_request_model if model_name: set_on_span(SPANDATA.GEN_AI_REQUEST_MODEL, model_name) From 9c66d1275e938d3c4247b07a8dfc689c876d655e Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 7 Sep 2026 14:03:29 +0200 Subject: [PATCH 3/5] ci: Add openai-agents to uv typing group --- pyproject.toml | 1 + .../integrations/openai_agents/__init__.py | 44 +++++++++---------- .../openai_agents/patches/agent_run.py | 28 +++++++----- .../openai_agents/patches/error_tracing.py | 6 ++- .../openai_agents/patches/models.py | 6 +-- .../openai_agents/patches/runner.py | 8 ++-- .../openai_agents/patches/tools.py | 4 +- .../openai_agents/spans/ai_client.py | 10 ++--- .../openai_agents/spans/execute_tool.py | 2 +- .../openai_agents/spans/handoff.py | 10 +++-- .../openai_agents/spans/invoke_agent.py | 8 ++-- .../integrations/openai_agents/utils.py | 12 ++--- uv.lock | 19 ++++++++ 13 files changed, 95 insertions(+), 63 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4bb596988a..21fea6d9f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,6 +79,7 @@ typing = [ "openai>=3.7.0", "tiktoken>=0.14.0", "aiohttp>=3.14.3", + "openai-agents>=0.22.0", ] test = [ "dataclasses ; python_full_version < '3.7'", diff --git a/sentry_sdk/integrations/openai_agents/__init__.py b/sentry_sdk/integrations/openai_agents/__init__.py index 94c5ecc7a5..a4488d169b 100644 --- a/sentry_sdk/integrations/openai_agents/__init__.py +++ b/sentry_sdk/integrations/openai_agents/__init__.py @@ -34,9 +34,9 @@ # https://github.com/openai/openai-agents-python/commit/3ce7c24d349b77bb750062b7e0e856d9ff48a5d5#diff-7470b3a5c5cbe2fcbb2703dc24f326f45a5819d853be2b1f395d122d278cd911 from agents.run_internal import run_loop, turn_preparation, turn_resolution except ImportError: - run_loop = None - turn_preparation = None - turn_resolution = None + run_loop = None # type: ignore[assignment] + turn_preparation = None # type: ignore[assignment] + turn_resolution = None # type: ignore[assignment] from typing import TYPE_CHECKING @@ -50,13 +50,13 @@ def _patch_runner(use_run_hooks: "bool") -> None: # Create the root span for one full agent run (including eventual handoffs) # Note agents.run.DEFAULT_AGENT_RUNNER.run_sync is a wrapper around # agents.run.DEFAULT_AGENT_RUNNER.run. It does not need to be wrapped separately. - agents.run.DEFAULT_AGENT_RUNNER.run = _create_run_wrapper( + agents.run.DEFAULT_AGENT_RUNNER.run = _create_run_wrapper( # type: ignore[method-assign] agents.run.DEFAULT_AGENT_RUNNER.run, use_run_hooks=use_run_hooks, ) # Patch streaming runner - agents.run.DEFAULT_AGENT_RUNNER.run_streamed = _create_run_streamed_wrapper( + agents.run.DEFAULT_AGENT_RUNNER.run_streamed = _create_run_streamed_wrapper( # type: ignore[method-assign] agents.run.DEFAULT_AGENT_RUNNER.run_streamed, use_run_hooks=use_run_hooks, ) @@ -175,9 +175,9 @@ async def new_wrapped_final_output( return if not use_run_hooks: - original_get_all_tools = AgentRunner._get_all_tools + original_get_all_tools = AgentRunner._get_all_tools # type: ignore[attr-defined] - @wraps(AgentRunner._get_all_tools.__func__) + @wraps(AgentRunner._get_all_tools.__func__) # type: ignore[attr-defined] async def old_wrapped_get_all_tools( cls: "agents.Runner", agent: "agents.Agent", @@ -187,35 +187,35 @@ async def old_wrapped_get_all_tools( original_get_all_tools, agent, context_wrapper ) - agents.run.AgentRunner._get_all_tools = classmethod( - old_wrapped_get_all_tools + agents.run.AgentRunner._get_all_tools = classmethod( # type: ignore[attr-defined] + old_wrapped_get_all_tools # type: ignore[arg-type] ) - original_get_model = AgentRunner._get_model + original_get_model = AgentRunner._get_model # type: ignore[attr-defined] - @wraps(AgentRunner._get_model.__func__) + @wraps(AgentRunner._get_model.__func__) # type: ignore[attr-defined] def old_wrapped_get_model( cls: "agents.Runner", agent: "agents.Agent", run_config: "agents.RunConfig" ) -> "agents.Model": return _get_model(original_get_model, agent, run_config) - agents.run.AgentRunner._get_model = classmethod(old_wrapped_get_model) + agents.run.AgentRunner._get_model = classmethod(old_wrapped_get_model) # type: ignore[arg-type,attr-defined] - original_run_single_turn = AgentRunner._run_single_turn + original_run_single_turn = AgentRunner._run_single_turn # type: ignore[attr-defined] - @wraps(AgentRunner._run_single_turn.__func__) + @wraps(AgentRunner._run_single_turn.__func__) # type: ignore[attr-defined] async def old_wrapped_run_single_turn( cls: "agents.Runner", *args: "Any", **kwargs: "Any" ) -> "SingleStepResult": return await _run_single_turn(original_run_single_turn, *args, **kwargs) - agents.run.AgentRunner._run_single_turn = classmethod( - old_wrapped_run_single_turn + agents.run.AgentRunner._run_single_turn = classmethod( # type: ignore[attr-defined] + old_wrapped_run_single_turn # type: ignore[arg-type] ) - original_run_single_turn_streamed = AgentRunner._run_single_turn_streamed + original_run_single_turn_streamed = AgentRunner._run_single_turn_streamed # type: ignore[attr-defined] - @wraps(AgentRunner._run_single_turn_streamed.__func__) + @wraps(AgentRunner._run_single_turn_streamed.__func__) # type: ignore[attr-defined] async def old_wrapped_run_single_turn_streamed( cls: "agents.Runner", *args: "Any", **kwargs: "Any" ) -> "SingleStepResult": @@ -223,8 +223,8 @@ async def old_wrapped_run_single_turn_streamed( original_run_single_turn_streamed, *args, **kwargs ) - agents.run.AgentRunner._run_single_turn_streamed = classmethod( - old_wrapped_run_single_turn_streamed + agents.run.AgentRunner._run_single_turn_streamed = classmethod( # type: ignore[attr-defined] + old_wrapped_run_single_turn_streamed # type: ignore[arg-type] ) original_execute_handoffs = agents._run_impl.RunImpl.execute_handoffs @@ -236,7 +236,7 @@ async def old_wrapped_execute_handoffs( return await _execute_handoffs(original_execute_handoffs, *args, **kwargs) agents._run_impl.RunImpl.execute_handoffs = classmethod( - old_wrapped_execute_handoffs + old_wrapped_execute_handoffs # type: ignore[arg-type] ) original_execute_final_output = agents._run_impl.RunImpl.execute_final_output @@ -250,5 +250,5 @@ async def old_wrapped_final_output( ) agents._run_impl.RunImpl.execute_final_output = classmethod( - old_wrapped_final_output + old_wrapped_final_output # type: ignore[arg-type] ) diff --git a/sentry_sdk/integrations/openai_agents/patches/agent_run.py b/sentry_sdk/integrations/openai_agents/patches/agent_run.py index 71883b2eef..76611283d6 100644 --- a/sentry_sdk/integrations/openai_agents/patches/agent_run.py +++ b/sentry_sdk/integrations/openai_agents/patches/agent_run.py @@ -13,7 +13,7 @@ ) if TYPE_CHECKING: - from typing import Any, Awaitable, Callable, Optional, Union + from typing import Any, Awaitable, Callable, Coroutine, Optional, Union from agents.run_internal.run_steps import SingleStepResult @@ -46,8 +46,8 @@ def _close_streaming_workflow_span(agent: "Optional[agents.Agent]") -> None: def _maybe_start_agent_span( - context_wrapper: "agents.RunContextWrapper", - agent: "agents.Agent", + context_wrapper: "Optional[agents.RunContextWrapper]", + agent: "Optional[agents.Agent]", should_run_agent_start_hooks: bool, span_kwargs: "dict[str, Any]", is_streaming: bool = False, @@ -74,10 +74,10 @@ def _maybe_start_agent_span( delattr(context_wrapper, "_sentry_agent_span") # Store the agent on the context wrapper so we can access it later - context_wrapper._sentry_current_agent = agent + context_wrapper._sentry_current_agent = agent # type: ignore[attr-defined] span = invoke_agent_span(context_wrapper, agent, span_kwargs) - context_wrapper._sentry_agent_span = span - agent._sentry_agent_span = span + context_wrapper._sentry_agent_span = span # type: ignore[attr-defined] + agent._sentry_agent_span = span # type: ignore[attr-defined] if not is_streaming: return span @@ -102,12 +102,14 @@ async def _run_single_turn( """ # openai-agents >= 0.14 passes `bindings: AgentBindings` instead of `agent`. bindings = kwargs.get("bindings") - agent = ( + agent: "Optional[agents.Agent]" = ( getattr(bindings, "public_agent", None) if bindings is not None else kwargs.get("agent") ) - context_wrapper = kwargs.get("context_wrapper") + context_wrapper: "Optional[agents.RunContextWrapper]" = kwargs.get( + "context_wrapper" + ) should_run_agent_start_hooks = kwargs.get("should_run_agent_start_hooks", False) span = _maybe_start_agent_span( @@ -224,7 +226,7 @@ async def _run_single_turn_streamed( async def _execute_handoffs( - original_execute_handoffs: "Callable[..., SingleStepResult]", + original_execute_handoffs: "Callable[..., Coroutine[Any, Any, SingleStepResult]]", *args: "Any", **kwargs: "Any", ) -> "SingleStepResult": @@ -235,10 +237,12 @@ async def _execute_handoffs( - ends the workflow span if the response is streamed and an exception is raised in `execute_handoffs()`. """ - context_wrapper = kwargs.get("context_wrapper") + context_wrapper: "Optional[agents.RunContextWrapper]" = kwargs.get( + "context_wrapper" + ) run_handoffs = kwargs.get("run_handoffs") # openai-agents >= 0.14 renamed `agent` to `public_agent`. - agent = kwargs.get("public_agent", kwargs.get("agent")) + agent: "Optional[agents.Agent]" = kwargs.get("public_agent", kwargs.get("agent")) # Create Sentry handoff span for the first handoff (agents library only processes the first one) if run_handoffs: @@ -282,7 +286,7 @@ async def _execute_handoffs( async def _execute_final_output( - original_execute_final_output: "Callable[..., SingleStepResult]", + original_execute_final_output: "Callable[..., Coroutine[Any, Any, SingleStepResult]]", *args: "Any", **kwargs: "Any", ) -> "SingleStepResult": diff --git a/sentry_sdk/integrations/openai_agents/patches/error_tracing.py b/sentry_sdk/integrations/openai_agents/patches/error_tracing.py index 68dadb3101..2135ecd753 100644 --- a/sentry_sdk/integrations/openai_agents/patches/error_tracing.py +++ b/sentry_sdk/integrations/openai_agents/patches/error_tracing.py @@ -69,6 +69,8 @@ def sentry_attach_error_to_current_span( # Call the original function return original_attach_error(error, *args, **kwargs) - error_tracing_module.attach_error_to_current_span = ( - sentry_attach_error_to_current_span + setattr( + error_tracing_module, + "attach_error_to_current_span", + sentry_attach_error_to_current_span, ) diff --git a/sentry_sdk/integrations/openai_agents/patches/models.py b/sentry_sdk/integrations/openai_agents/patches/models.py index c671b40f11..0ab283f316 100644 --- a/sentry_sdk/integrations/openai_agents/patches/models.py +++ b/sentry_sdk/integrations/openai_agents/patches/models.py @@ -79,7 +79,7 @@ def _get_model( async def wrapped_fetch_response(*args: "Any", **kwargs: "Any") -> "Any": response = await original_fetch_response(*args, **kwargs) if hasattr(response, "model") and response.model: - agent._sentry_response_model = str(response.model) + agent._sentry_response_model = str(response.model) # type: ignore[attr-defined] return response model._fetch_response = wrapped_fetch_response @@ -110,7 +110,7 @@ async def wrapped_get_response(*args: "Any", **kwargs: "Any") -> "Any": return result - model.get_response = wrapped_get_response + model.get_response = wrapped_get_response # type: ignore[method-assign] # Also wrap stream_response for streaming support if hasattr(model, "stream_response"): @@ -173,6 +173,6 @@ async def wrapped_stream_response(*args: "Any", **kwargs: "Any") -> "Any": span, streaming_response, response_model, agent ) - model.stream_response = wrapped_stream_response + model.stream_response = wrapped_stream_response # type: ignore[method-assign] return model diff --git a/sentry_sdk/integrations/openai_agents/patches/runner.py b/sentry_sdk/integrations/openai_agents/patches/runner.py index 870dc91325..48857bf382 100644 --- a/sentry_sdk/integrations/openai_agents/patches/runner.py +++ b/sentry_sdk/integrations/openai_agents/patches/runner.py @@ -37,7 +37,7 @@ TContext = TypeVar("TContext") -class _SentryRunHooks(RunHooks[TContext]): # type: ignore[misc] +class _SentryRunHooks(RunHooks[TContext]): """ Responsible for creating and managing Execute Tool spans. These spans are stored on the ToolContext reference that is shared between `on_tool_start()` @@ -120,9 +120,9 @@ async def on_tool_end( await sentry_hooks.on_tool_end(context, agent, tool, result) await original_on_tool_end(context, agent, tool, result) - hooks._sentry_is_patched = True - hooks.on_tool_start = on_tool_start - hooks.on_tool_end = on_tool_end + hooks._sentry_is_patched = True # type: ignore[attr-defined] + hooks.on_tool_start = on_tool_start # type: ignore[method-assign] + hooks.on_tool_end = on_tool_end # type: ignore[method-assign] def _create_run_wrapper( diff --git a/sentry_sdk/integrations/openai_agents/patches/tools.py b/sentry_sdk/integrations/openai_agents/patches/tools.py index 2ddf7e7592..66d02e7a00 100644 --- a/sentry_sdk/integrations/openai_agents/patches/tools.py +++ b/sentry_sdk/integrations/openai_agents/patches/tools.py @@ -31,7 +31,7 @@ async def _get_all_tools( # Get the original tools tools = await original_get_all_tools(agent, context_wrapper) - wrapped_tools = [] + wrapped_tools: "list[agents.Tool]" = [] for tool in tools: # Wrap only the function tools (for now) if not isinstance(tool, FunctionTool): @@ -42,7 +42,7 @@ async def _get_all_tools( original_on_invoke = tool.on_invoke_tool def create_wrapped_invoke( - current_tool: "agents.Tool", current_on_invoke: "Callable[..., Any]" + current_tool: "FunctionTool", current_on_invoke: "Callable[..., Any]" ) -> "Callable[..., Any]": @wraps(current_on_invoke) async def sentry_wrapped_on_invoke_tool( diff --git a/sentry_sdk/integrations/openai_agents/spans/ai_client.py b/sentry_sdk/integrations/openai_agents/spans/ai_client.py index 0e94822999..abe80f924a 100644 --- a/sentry_sdk/integrations/openai_agents/spans/ai_client.py +++ b/sentry_sdk/integrations/openai_agents/spans/ai_client.py @@ -25,23 +25,23 @@ try: from agents import ApplyPatchTool, ShellTool except ImportError: - ShellTool = None - ApplyPatchTool = None + ShellTool = None # type: ignore[assignment,misc] + ApplyPatchTool = None # type: ignore[assignment,misc] try: from agents import ToolSearchTool except ImportError: - ToolSearchTool = None + ToolSearchTool = None # type: ignore[assignment,misc] try: from agents import CustomTool except ImportError: - CustomTool = None + CustomTool = None # type: ignore[assignment,misc] try: from agents import ProgrammaticToolCallingTool except ImportError: - ProgrammaticToolCallingTool = None + ProgrammaticToolCallingTool = None # type: ignore[assignment,misc] from ..consts import SPAN_ORIGIN diff --git a/sentry_sdk/integrations/openai_agents/spans/execute_tool.py b/sentry_sdk/integrations/openai_agents/spans/execute_tool.py index 734789b030..49d75d6d83 100644 --- a/sentry_sdk/integrations/openai_agents/spans/execute_tool.py +++ b/sentry_sdk/integrations/openai_agents/spans/execute_tool.py @@ -17,7 +17,7 @@ def execute_tool_span( - tool: "agents.Tool", *args: "Any", **kwargs: "Any" + tool: "agents.FunctionTool", *args: "Any", **kwargs: "Any" ) -> "Union[sentry_sdk.tracing.Span, StreamedSpan]": span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options) diff --git a/sentry_sdk/integrations/openai_agents/spans/handoff.py b/sentry_sdk/integrations/openai_agents/spans/handoff.py index ea91464afb..8c54ecfabb 100644 --- a/sentry_sdk/integrations/openai_agents/spans/handoff.py +++ b/sentry_sdk/integrations/openai_agents/spans/handoff.py @@ -7,16 +7,20 @@ from ..consts import SPAN_ORIGIN if TYPE_CHECKING: + from typing import Optional + import agents def handoff_span( - context: "agents.RunContextWrapper", from_agent: "agents.Agent", to_agent_name: str + context: "Optional[agents.RunContextWrapper]", + from_agent: "Optional[agents.Agent]", + to_agent_name: str, ) -> None: span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options) if span_streaming: with sentry_sdk.traces.start_span( - name=f"handoff from {from_agent.name} to {to_agent_name}", + name=f"handoff from {from_agent.name} to {to_agent_name}", # type: ignore[union-attr] attributes={ "sentry.op": OP.GEN_AI_HANDOFF, "sentry.origin": SPAN_ORIGIN, @@ -30,7 +34,7 @@ def handoff_span( else: with sentry_sdk.start_span( op=OP.GEN_AI_HANDOFF, - name=f"handoff from {from_agent.name} to {to_agent_name}", + name=f"handoff from {from_agent.name} to {to_agent_name}", # type: ignore[union-attr] origin=SPAN_ORIGIN, ) as span: span.set_data(SPANDATA.GEN_AI_OPERATION_NAME, "handoff") diff --git a/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py b/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py index f52181abea..eef59bdf0a 100644 --- a/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +++ b/sentry_sdk/integrations/openai_agents/spans/invoke_agent.py @@ -20,7 +20,7 @@ from ..utils import _set_agent_data, _set_usage_data if TYPE_CHECKING: - from typing import Any, Union + from typing import Any, Optional, Union import agents @@ -110,12 +110,12 @@ def invoke_agent_span( def update_invoke_agent_span( span: "Union[sentry_sdk.tracing.Span, StreamedSpan]", - context: "agents.RunContextWrapper", - agent: "agents.Agent", + context: "Optional[agents.RunContextWrapper]", + agent: "Optional[agents.Agent]", output: "Any" = None, ) -> None: # Add aggregated usage data from context_wrapper - if hasattr(context, "usage"): + if context and hasattr(context, "usage"): _set_usage_data(span, context.usage) client = sentry_sdk.get_client() diff --git a/sentry_sdk/integrations/openai_agents/utils.py b/sentry_sdk/integrations/openai_agents/utils.py index b5e24eafea..f16e19a112 100644 --- a/sentry_sdk/integrations/openai_agents/utils.py +++ b/sentry_sdk/integrations/openai_agents/utils.py @@ -26,7 +26,7 @@ ) if TYPE_CHECKING: - from typing import Any, Union + from typing import Any, Optional, Union from agents import TResponseInputItem, Usage @@ -34,7 +34,7 @@ try: import agents - + from agents import Model except ImportError: raise DidNotEnable("OpenAI Agents not installed") @@ -65,9 +65,11 @@ def _set_agent_data( set_on_span(SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, agent.model_settings.max_tokens) # 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: - model_name = agent.model.model if hasattr(agent.model, "model") else agent.model + model_name: "Optional[str]" = None + if isinstance(agent.model, Model) and hasattr(agent.model, "model"): + model_name = agent.model.model + elif isinstance(agent.model, str): + model_name = agent.model if model_name: set_on_span(SPANDATA.GEN_AI_REQUEST_MODEL, model_name) diff --git a/uv.lock b/uv.lock index 5075cc06f9..7636625088 100644 --- a/uv.lock +++ b/uv.lock @@ -55,6 +55,7 @@ typing = [ { name = "mcp", specifier = ">=2.0.0b1" }, { name = "mypy" }, { name = "openai", specifier = ">=3.7.0" }, + { name = "openai-agents", specifier = ">=0.22.0" }, { name = "openfeature-sdk" }, { name = "opentelemetry-distro", extras = ["otlp"] }, { name = "pydantic", specifier = ">=2.13.4" }, @@ -1789,6 +1790,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d3/9d/77e0f43f04ae51a3d21bb08bd372a752856b86ee22648b1458c93a623927/openai-3.7.0-py3-none-any.whl", hash = "sha256:008fa33e0a01dc71039c27355ef8f476eed690e8e48bec18183878dcd32fb841", size = 1699594, upload-time = "2026-09-02T01:30:52.445Z" }, ] +[[package]] +name = "openai-agents" +version = "0.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffelib" }, + { name = "mcp" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/17/2a92451547a66d56bd5f10128aa64aa0fc282420421775c390902e7dfa42/openai_agents-0.22.0.tar.gz", hash = "sha256:6c3d7b9e34d3ca4bf763d4557d01ec844685290f0d80cb72e10a304029c0d7ee", size = 6390140, upload-time = "2026-08-19T13:45:22.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/88/220a5a9283fba72107db7cb549fd127a0e532dbcbc89dda82cd28b9765a1/openai_agents-0.22.0-py3-none-any.whl", hash = "sha256:985a74a8024123980c2d4dc329d19b2332a0f86919fa7b3f6b9c2abaae022680", size = 1117078, upload-time = "2026-08-19T13:45:19.876Z" }, +] + [[package]] name = "openfeature-sdk" version = "0.10.0" From 5bb18fd7413c1d8f57f9c9e72da5f32a4325199f Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 7 Sep 2026 14:08:56 +0200 Subject: [PATCH 4/5] remove comments --- sentry_sdk/integrations/openai_agents/spans/ai_client.py | 1 - sentry_sdk/integrations/openai_agents/utils.py | 1 - 2 files changed, 2 deletions(-) diff --git a/sentry_sdk/integrations/openai_agents/spans/ai_client.py b/sentry_sdk/integrations/openai_agents/spans/ai_client.py index 0e94822999..cd6a7fec66 100644 --- a/sentry_sdk/integrations/openai_agents/spans/ai_client.py +++ b/sentry_sdk/integrations/openai_agents/spans/ai_client.py @@ -201,7 +201,6 @@ def ai_client_span( agent: "Agent", get_response_kwargs: "dict[str, Any]" ) -> "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: model_name = agent.model.model if hasattr(agent.model, "model") else agent.model diff --git a/sentry_sdk/integrations/openai_agents/utils.py b/sentry_sdk/integrations/openai_agents/utils.py index b5e24eafea..f36341ee28 100644 --- a/sentry_sdk/integrations/openai_agents/utils.py +++ b/sentry_sdk/integrations/openai_agents/utils.py @@ -64,7 +64,6 @@ def _set_agent_data( if agent.model_settings.max_tokens: set_on_span(SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, agent.model_settings.max_tokens) - # 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: model_name = agent.model.model if hasattr(agent.model, "model") else agent.model From 900112cedc5381e66762c805ec151888c5ed2f1b Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 7 Sep 2026 14:26:25 +0200 Subject: [PATCH 5/5] revert request model changes --- sentry_sdk/integrations/openai_agents/patches/models.py | 4 ++++ sentry_sdk/integrations/openai_agents/spans/ai_client.py | 2 ++ sentry_sdk/integrations/openai_agents/utils.py | 2 ++ 3 files changed, 8 insertions(+) diff --git a/sentry_sdk/integrations/openai_agents/patches/models.py b/sentry_sdk/integrations/openai_agents/patches/models.py index 0ab283f316..c2b030c42a 100644 --- a/sentry_sdk/integrations/openai_agents/patches/models.py +++ b/sentry_sdk/integrations/openai_agents/patches/models.py @@ -71,6 +71,10 @@ def _get_model( # because we only patch its direct methods, all underlying data can remain unchanged. model = copy.copy(original_get_model(agent, run_config)) + # Capture the request model name for spans (agent.model can be None when using defaults) + request_model_name = model.model if hasattr(model, "model") else str(model) + agent._sentry_request_model = request_model_name # type: ignore[attr-defined] + # Wrap _fetch_response if it exists (for OpenAI models) to capture response model if hasattr(model, "_fetch_response"): original_fetch_response = model._fetch_response diff --git a/sentry_sdk/integrations/openai_agents/spans/ai_client.py b/sentry_sdk/integrations/openai_agents/spans/ai_client.py index a15188a300..697f0d6cf7 100644 --- a/sentry_sdk/integrations/openai_agents/spans/ai_client.py +++ b/sentry_sdk/integrations/openai_agents/spans/ai_client.py @@ -204,6 +204,8 @@ def ai_client_span( model_name = None if agent.model: model_name = agent.model.model if hasattr(agent.model, "model") else agent.model + elif hasattr(agent, "_sentry_request_model"): + model_name = agent._sentry_request_model client_options = sentry_sdk.get_client().options diff --git a/sentry_sdk/integrations/openai_agents/utils.py b/sentry_sdk/integrations/openai_agents/utils.py index b675812980..01303e0e58 100644 --- a/sentry_sdk/integrations/openai_agents/utils.py +++ b/sentry_sdk/integrations/openai_agents/utils.py @@ -69,6 +69,8 @@ def _set_agent_data( model_name = agent.model.model elif isinstance(agent.model, str): model_name = agent.model + elif hasattr(agent, "_sentry_request_model"): + model_name = agent._sentry_request_model if model_name: set_on_span(SPANDATA.GEN_AI_REQUEST_MODEL, model_name)