Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
02af5df
fix(openai-agents): Patch tool functions following library refactor
alexander-alderman-webb Feb 11, 2026
a7f08cc
.
alexander-alderman-webb Feb 11, 2026
3679c96
add early return
alexander-alderman-webb Feb 11, 2026
0047eb3
remove indirection
alexander-alderman-webb Feb 11, 2026
722bf00
merge master
alexander-alderman-webb Feb 12, 2026
b9e9d6b
fix(openai-agents): Patch model functions following library refactor
alexander-alderman-webb Feb 12, 2026
0a2ede8
.
alexander-alderman-webb Feb 12, 2026
e2987b8
merge
alexander-alderman-webb Feb 12, 2026
0970319
.
alexander-alderman-webb Feb 12, 2026
ec2b633
fix(openai-agents): Patch run_single_turn() following library refactor
alexander-alderman-webb Feb 12, 2026
bba10b3
fix(openai-agents): Patch run_single_turn_streamed functions followin…
alexander-alderman-webb Feb 12, 2026
a753a6e
fix(openai-agents): Patch execute_handoffs functions following librar…
alexander-alderman-webb Feb 12, 2026
168e405
.
alexander-alderman-webb Feb 12, 2026
2010b39
Merge branch 'webb/openai-agents/model-patches' into webb/openai-agen…
alexander-alderman-webb Feb 12, 2026
85e8c34
.
alexander-alderman-webb Feb 12, 2026
1ed70f0
Merge branch 'webb/openai-agents/run-single-turn' into webb/openai-ag…
alexander-alderman-webb Feb 12, 2026
c96a268
.
alexander-alderman-webb Feb 12, 2026
ed9e312
Merge branch 'webb/openai-agents/run-single-turn-streamed' into webb/…
alexander-alderman-webb Feb 12, 2026
9c9973a
.
alexander-alderman-webb Feb 12, 2026
18cb737
.
alexander-alderman-webb Feb 12, 2026
48aff66
.
alexander-alderman-webb Feb 12, 2026
e44024a
Merge branch 'webb/openai-agents/model-patches' into webb/openai-agen…
alexander-alderman-webb Feb 12, 2026
8d0aff5
Merge branch 'webb/openai-agents/run-single-turn' into webb/openai-ag…
alexander-alderman-webb Feb 12, 2026
802979f
Merge branch 'webb/openai-agents/run-single-turn-streamed' into webb/…
alexander-alderman-webb Feb 12, 2026
3dd7818
merge master
alexander-alderman-webb Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions sentry_sdk/integrations/openai_agents/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
_get_all_tools,
_run_single_turn,
_run_single_turn_streamed,
_execute_handoffs,
_create_run_wrapper,
_create_run_streamed_wrapper,
_patch_agent_run,
Expand All @@ -32,10 +33,11 @@
try:
# AgentRunner methods moved in v0.8
# https://github.com/openai/openai-agents-python/commit/3ce7c24d349b77bb750062b7e0e856d9ff48a5d5#diff-7470b3a5c5cbe2fcbb2703dc24f326f45a5819d853be2b1f395d122d278cd911
from agents.run_internal import run_loop, turn_preparation
from agents.run_internal import run_loop, turn_preparation, turn_resolution
except ImportError:
run_loop = None
turn_preparation = None
turn_resolution = None

from typing import TYPE_CHECKING

Expand Down Expand Up @@ -86,7 +88,7 @@ class OpenAIAgentsIntegration(Integration):
Hosted MCP Tools are run as part of the Responses API call, and involve OpenAI reaching out to an external MCP server.
An agent can handoff to another agent, also directed by the return value of the Responses API and run post-API call in the loop.
Handoffs are a way to switch agent-wide configuration.
- Handoffs are executed by calling `RunImpl.execute_handoffs()`. The method is patched in `patched_execute_handoffs()`
- Handoffs are executed by calling `RunImpl.execute_handoffs()`. The method is patched with `patches._execute_handoffs()`
"""

identifier = "openai_agents"
Expand Down Expand Up @@ -139,6 +141,20 @@ async def new_wrapped_run_single_turn_streamed(

agents.run.run_single_turn_streamed = new_wrapped_run_single_turn_streamed

original_execute_handoffs = turn_resolution.execute_handoffs

@wraps(original_execute_handoffs)
async def new_wrapped_execute_handoffs(
*args: "Any", **kwargs: "Any"
) -> "SingleStepResult":
return await _execute_handoffs(
original_execute_handoffs, *args, **kwargs
)

agents.run_internal.turn_resolution.execute_handoffs = (
new_wrapped_execute_handoffs
)

return

original_get_all_tools = AgentRunner._get_all_tools
Expand Down Expand Up @@ -188,3 +204,15 @@ async def old_wrapped_run_single_turn_streamed(
agents.run.AgentRunner._run_single_turn_streamed = classmethod(
old_wrapped_run_single_turn_streamed
)

original_execute_handoffs = agents._run_impl.RunImpl.execute_handoffs

@wraps(agents._run_impl.RunImpl.execute_handoffs.__func__)
async def old_wrapped_execute_handoffs(
cls: "agents.Runner", *args: "Any", **kwargs: "Any"
) -> "SingleStepResult":
return await _execute_handoffs(original_execute_handoffs, *args, **kwargs)

agents._run_impl.RunImpl.execute_handoffs = classmethod(
old_wrapped_execute_handoffs
)
7 changes: 6 additions & 1 deletion sentry_sdk/integrations/openai_agents/patches/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from .models import _get_model # noqa: F401
from .tools import _get_all_tools # noqa: F401
from .runner import _create_run_wrapper, _create_run_streamed_wrapper # noqa: F401
from .agent_run import _run_single_turn, _run_single_turn_streamed, _patch_agent_run # noqa: F401
from .agent_run import (
_run_single_turn,
_run_single_turn_streamed,
_execute_handoffs,
_patch_agent_run,
) # noqa: F401
from .error_tracing import _patch_error_tracing # noqa: F401
80 changes: 38 additions & 42 deletions sentry_sdk/integrations/openai_agents/patches/agent_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,55 +166,52 @@ async def _run_single_turn_streamed(
return result


def _patch_agent_run() -> None:
async def _execute_handoffs(
original_execute_handoffs: "Callable[..., SingleStepResult]",
*args: "Any",
**kwargs: "Any",
) -> "SingleStepResult":
"""
Patches AgentRunner methods to create agent invocation spans.
This directly patches the execution flow to track when agents start and stop.
Patched execute_handoffs that
- creates and manages handoff spans.
- ends the agent invocation span.
- ends the workflow span if the response is streamed and an exception is raised in `execute_handoffs()`.
"""

# Store original methods
original_execute_handoffs = agents._run_impl.RunImpl.execute_handoffs
original_execute_final_output = agents._run_impl.RunImpl.execute_final_output
context_wrapper = kwargs.get("context_wrapper")
run_handoffs = kwargs.get("run_handoffs")
agent = kwargs.get("agent")

@wraps(
original_execute_handoffs.__func__
if hasattr(original_execute_handoffs, "__func__")
else original_execute_handoffs
)
async def patched_execute_handoffs(
cls: "agents.Runner", *args: "Any", **kwargs: "Any"
) -> "Any":
"""
Patched execute_handoffs that
- creates and manages handoff spans.
- ends the agent invocation span.
- ends the workflow span if the response is streamed and an exception is raised in `execute_handoffs()`.
"""
# Create Sentry handoff span for the first handoff (agents library only processes the first one)
if run_handoffs:
first_handoff = run_handoffs[0]
handoff_agent_name = first_handoff.handoff.agent_name
handoff_span(context_wrapper, agent, handoff_agent_name)

context_wrapper = kwargs.get("context_wrapper")
run_handoffs = kwargs.get("run_handoffs")
agent = kwargs.get("agent")
# Call original method with all parameters
try:
result = await original_execute_handoffs(*args, **kwargs)
except Exception:
exc_info = sys.exc_info()
with capture_internal_exceptions():
_close_streaming_workflow_span(agent)
reraise(*exc_info)
finally:
# End span for current agent after handoff processing is complete
if agent and context_wrapper and _has_active_agent_span(context_wrapper):
end_invoke_agent_span(context_wrapper, agent)

# Create Sentry handoff span for the first handoff (agents library only processes the first one)
if run_handoffs:
first_handoff = run_handoffs[0]
handoff_agent_name = first_handoff.handoff.agent_name
handoff_span(context_wrapper, agent, handoff_agent_name)
return result

# Call original method with all parameters
try:
result = await original_execute_handoffs(*args, **kwargs)
except Exception:
exc_info = sys.exc_info()
with capture_internal_exceptions():
_close_streaming_workflow_span(agent)
reraise(*exc_info)
finally:
# End span for current agent after handoff processing is complete
if agent and context_wrapper and _has_active_agent_span(context_wrapper):
end_invoke_agent_span(context_wrapper, agent)

return result
def _patch_agent_run() -> None:
"""
Patches AgentRunner methods to create agent invocation spans.
This directly patches the execution flow to track when agents start and stop.
"""

# Store original methods
original_execute_final_output = agents._run_impl.RunImpl.execute_final_output

@wraps(
original_execute_final_output.__func__
Expand Down Expand Up @@ -250,7 +247,6 @@ async def patched_execute_final_output(
return result

# Apply patches
agents._run_impl.RunImpl.execute_handoffs = classmethod(patched_execute_handoffs)
agents._run_impl.RunImpl.execute_final_output = classmethod(
patched_execute_final_output
)
Loading