ref(openai-agents): Use first class agent hooks when available - #7029
ref(openai-agents): Use first class agent hooks when available#7029alexander-alderman-webb wants to merge 38 commits into
Conversation
Codecov Results 📊✅ 99162 passed | ⏭️ 6479 skipped | Total: 105641 | Pass Rate: 93.87% | Execution Time: 353m 35s 📊 Comparison with Base Branch
All tests are passing successfully. ✅ Patch coverage is 89.11%. Project has 2500 uncovered lines. Files with missing lines (3)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 89.96% 90.01% +0.05%
==========================================
Files 193 193 —
Lines 24954 25015 +61
Branches 9004 9030 +26
==========================================
+ Hits 22448 22515 +67
- Misses 2506 2500 -6
- Partials 1434 1439 +5Generated by Codecov Action |
| if use_run_hooks: | ||
| run_hooks = kwargs.get("hooks") | ||
| if run_hooks is not None: |
There was a problem hiding this comment.
To confirm my understanding of what's happening here - are run hooks something that are passed in by users (which is why they could potentially be None)?
| and `on_tool_end()` | ||
| Responsible for creating Execute Tool and Invoke Agent spans. Both | ||
| spans can also be closed by hooks, and Execute Tool spans are exclusively | ||
| closed in the hook. |
There was a problem hiding this comment.
When you say "the hook" here - is there are specific hook that you're referring to? Or do you mean this as just a generalized "run hook"?
| update_invoke_agent_span( | ||
| span=invoke_agent_span, | ||
| usage=context_wrapper.usage, | ||
| agent=agent, |
There was a problem hiding this comment.
Bug: In _create_run_wrapper, if no hooks are provided, the local hooks variable remains None. The exception handler then fails to access the span, causing a span leak on AgentsException.
Severity: MEDIUM
Suggested Fix
In the exception handler within _create_run_wrapper, access the hooks instance from kwargs.get("hooks") instead of the local hooks variable. Alternatively, ensure the local hooks variable is updated when a new _SentryRunHooks instance is created and added to kwargs.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: sentry_sdk/integrations/openai_agents/patches/runner.py#L279-L282
Potential issue: When `agents.Runner.run()` is called without a `hooks` parameter and
`use_run_hooks` is true, a new `_SentryRunHooks` instance is created and stored in
`kwargs["hooks"]`. However, a local `hooks` variable remains `None`. If an
`AgentsException` is raised, the exception handler attempts to retrieve a span from the
local `hooks` variable. Since it's `None`, `getattr(hooks, "_sentry_invoke_agent_span",
None)` returns `None`, and the span created in `on_agent_start` is never closed, leading
to a span leak. The `capture_internal_exceptions` context manager suppresses the
potential `AttributeError`, hiding the issue.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 327a4d8. Configure here.
| ) | ||
|
|
||
| invoke_agent_span.__exit__(None, None, None) | ||
| delattr(run_result.context_wrapper, "_sentry_agent_span") |
There was a problem hiding this comment.
Default hooks left unbound
Medium Severity
When no user hooks are provided, a new _SentryRunHooks is assigned only to kwargs["hooks"], while the local hooks variable stays None. The AgentsException cleanup path then cannot find or finish the active invoke-agent span.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 327a4d8. Configure here.
| ) -> "SingleStepResult": | ||
| return await _execute_handoffs(original_execute_handoffs, *args, **kwargs) | ||
| return await _execute_handoffs( | ||
| original_execute_handoffs, use_run_hooks, *args, **kwargs |
There was a problem hiding this comment.
Exception in _execute_handoffs leaks invoke agent span when hooks are active
In patches/agent_run.py, _execute_handoffs has an early-return path when use_run_hooks is true. The except Exception block on that path only calls _close_streaming_workflow_span(agent); it never finishes the _sentry_invoke_agent_span stored on run_hooks. This is inconsistent with _run_single_turn and _run_single_turn_streamed, whose exception handlers both explicitly look up run_hooks._sentry_invoke_agent_span and finish it when use_run_hooks=True.
Evidence
patches/agent_run.pyline 288:_execute_handoffsenters an early-return path whenuse_run_hooks=True.- Line 295: the
except Exceptionblock in that path only calls_close_streaming_workflow_span(agent). - It never accesses
kwargs.get("hooks")or finishesrun_hooks._sentry_invoke_agent_span. - By contrast,
_run_single_turn(line 133) and_run_single_turn_streamed(line 235) each haveexcept Exceptionlogic that looks uprun_hooks._sentry_invoke_agent_spanand finishes it whenuse_run_hooks=True.
Identified by Warden · code-review · CJL-QQK


Description
Add
on_agent_start(),on_agent_end()andon_handoff()hooks.Unlike tool hooks, the Invoke Agent span is stored on the
RunHooksinstance.If the hooks are active, start and finish Invoke Agent spans in the hooks in successful executions. If an exception bubbles up,
on_agent_end()does not run. Modify monkey-patches so that the span stored on theRunHooksinstance is finished if the hooks are present.Make the signatures of
invoke_agent_span(),update_invoke_agent_span()and_maybe_start_agent_span()more minimal so that they can be re-used in theon_agent_start()andon_agent_end()hooks.Issues
Closes #6989
Reminders
uv run ruff.feat:,fix:,ref:,meta:)