Skip to content

Commit d3e2c88

Browse files
fix(openai-agents): Patch execute_final_output() functions following library refactor (#5453)
`RunImpl.execute_final_output()` was moved to `agents.run_internal.turn_resolution.execute_final_output()`. Patch the new function by reusing existing wrapper logic.
1 parent f71a604 commit d3e2c88

File tree

3 files changed

+52
-48
lines changed

3 files changed

+52
-48
lines changed

sentry_sdk/integrations/openai_agents/__init__.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
_execute_handoffs,
1212
_create_run_wrapper,
1313
_create_run_streamed_wrapper,
14-
_patch_agent_run,
14+
_execute_final_output,
1515
_patch_error_tracing,
1616
)
1717

@@ -60,9 +60,6 @@ def _patch_runner() -> None:
6060
agents.run.DEFAULT_AGENT_RUNNER.run_streamed
6161
)
6262

63-
# Creating the actual spans for each agent run (works for both streaming and non-streaming).
64-
_patch_agent_run()
65-
6663

6764
class OpenAIAgentsIntegration(Integration):
6865
"""
@@ -82,7 +79,7 @@ class OpenAIAgentsIntegration(Integration):
8279
- A Model instance is created at the start of the loop by calling the `Runner._get_model()`. We patch the Model instance using `patches._get_model()`.
8380
- Available tools are also deteremined at the start of the loop, with `Runner._get_all_tools()`. We patch Tool instances by iterating through the returned tools in `patches._get_all_tools()`.
8481
- In each loop iteration, `run_single_turn()` or `run_single_turn_streamed()` is responsible for calling the Responses API, patched with `patches._run_single_turn()` and `patches._run_single_turn_streamed()`.
85-
4. On loop termination, `RunImpl.execute_final_output()` is called. The function is patched with `patched_execute_final_output()`.
82+
4. On loop termination, `RunImpl.execute_final_output()` is called. The function is patched with `patches._execute_final_output()`.
8683
8784
Local tools are run based on the return value from the Responses API as a post-API call step in the above loop.
8885
Hosted MCP Tools are run as part of the Responses API call, and involve OpenAI reaching out to an external MCP server.
@@ -155,6 +152,20 @@ async def new_wrapped_execute_handoffs(
155152
new_wrapped_execute_handoffs
156153
)
157154

155+
original_execute_final_output = turn_resolution.execute_final_output
156+
157+
@wraps(turn_resolution.execute_final_output)
158+
async def new_wrapped_final_output(
159+
*args: "Any", **kwargs: "Any"
160+
) -> "SingleStepResult":
161+
return await _execute_final_output(
162+
original_execute_final_output, *args, **kwargs
163+
)
164+
165+
agents.run_internal.turn_resolution.execute_final_output = (
166+
new_wrapped_final_output
167+
)
168+
158169
return
159170

160171
original_get_all_tools = AgentRunner._get_all_tools
@@ -216,3 +227,17 @@ async def old_wrapped_execute_handoffs(
216227
agents._run_impl.RunImpl.execute_handoffs = classmethod(
217228
old_wrapped_execute_handoffs
218229
)
230+
231+
original_execute_final_output = agents._run_impl.RunImpl.execute_final_output
232+
233+
@wraps(agents._run_impl.RunImpl.execute_final_output.__func__)
234+
async def old_wrapped_final_output(
235+
cls: "agents.Runner", *args: "Any", **kwargs: "Any"
236+
) -> "SingleStepResult":
237+
return await _execute_final_output(
238+
original_execute_final_output, *args, **kwargs
239+
)
240+
241+
agents._run_impl.RunImpl.execute_final_output = classmethod(
242+
old_wrapped_final_output
243+
)

sentry_sdk/integrations/openai_agents/patches/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
_run_single_turn,
66
_run_single_turn_streamed,
77
_execute_handoffs,
8-
_patch_agent_run,
8+
_execute_final_output,
99
) # noqa: F401
1010
from .error_tracing import _patch_error_tracing # noqa: F401

sentry_sdk/integrations/openai_agents/patches/agent_run.py

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -204,49 +204,28 @@ async def _execute_handoffs(
204204
return result
205205

206206

207-
def _patch_agent_run() -> None:
207+
async def _execute_final_output(
208+
original_execute_final_output: "Callable[..., SingleStepResult]",
209+
*args: "Any",
210+
**kwargs: "Any",
211+
) -> "SingleStepResult":
208212
"""
209-
Patches AgentRunner methods to create agent invocation spans.
210-
This directly patches the execution flow to track when agents start and stop.
213+
Patched execute_final_output that
214+
- ends the agent invocation span.
215+
- ends the workflow span if the response is streamed.
211216
"""
212217

213-
# Store original methods
214-
original_execute_final_output = agents._run_impl.RunImpl.execute_final_output
218+
agent = kwargs.get("agent")
219+
context_wrapper = kwargs.get("context_wrapper")
220+
final_output = kwargs.get("final_output")
215221

216-
@wraps(
217-
original_execute_final_output.__func__
218-
if hasattr(original_execute_final_output, "__func__")
219-
else original_execute_final_output
220-
)
221-
async def patched_execute_final_output(
222-
cls: "agents.Runner", *args: "Any", **kwargs: "Any"
223-
) -> "Any":
224-
"""
225-
Patched execute_final_output that
226-
- ends the agent invocation span.
227-
- ends the workflow span if the response is streamed.
228-
"""
229-
230-
agent = kwargs.get("agent")
231-
context_wrapper = kwargs.get("context_wrapper")
232-
final_output = kwargs.get("final_output")
233-
234-
try:
235-
result = await original_execute_final_output(*args, **kwargs)
236-
finally:
237-
with capture_internal_exceptions():
238-
if (
239-
agent
240-
and context_wrapper
241-
and _has_active_agent_span(context_wrapper)
242-
):
243-
end_invoke_agent_span(context_wrapper, agent, final_output)
244-
# For streaming, close the workflow span (non-streaming uses context manager in _create_run_wrapper)
245-
_close_streaming_workflow_span(agent)
246-
247-
return result
248-
249-
# Apply patches
250-
agents._run_impl.RunImpl.execute_final_output = classmethod(
251-
patched_execute_final_output
252-
)
222+
try:
223+
result = await original_execute_final_output(*args, **kwargs)
224+
finally:
225+
with capture_internal_exceptions():
226+
if agent and context_wrapper and _has_active_agent_span(context_wrapper):
227+
end_invoke_agent_span(context_wrapper, agent, final_output)
228+
# For streaming, close the workflow span (non-streaming uses context manager in _create_run_wrapper)
229+
_close_streaming_workflow_span(agent)
230+
231+
return result

0 commit comments

Comments
 (0)