How do you use Sentry?
Sentry Saas (sentry.io)
Version
2.63.0
Steps to Reproduce
Function-tool spans (gen_ai.execute_tool) are emitted for Runner.run(...) but not for Runner.run_streamed(...). gen_ai.chat spans are present in both, so streamed agent traces show the model calls but none of the tool calls.
Minimal repro (one tool, tool_choice="required" so a tool call is guaranteed; spans inspected via before_send_transaction):
import asyncio
import sentry_sdk
from sentry_sdk.integrations.openai_agents import OpenAIAgentsIntegration
from agents import Agent, ModelSettings, Runner, function_tool
captured: list[str] = []
def record(event, _hint):
captured.extend(s.get("op") for s in event.get("spans", []) if s.get("op"))
return event
sentry_sdk.init(
dsn="<your-dsn>",
traces_sample_rate=1.0,
integrations=[OpenAIAgentsIntegration()],
before_send_transaction=record,
)
@function_tool
def get_tool(x: int) -> str:
"""Return an illustrative value."""
return f"${x}"
agent = Agent(
name="probe",
instructions="Use the get_tool tool, then state the result.",
model="gpt-4o-mini",
tools=[get_tool],
model_settings=ModelSettings(tool_choice="required"), # force a tool call
)
async def show(label, coro_factory):
captured.clear()
await coro_factory()
sentry_sdk.get_client().flush()
print(f"{label:16} {sorted(set(captured))}")
async def run_nonstreaming():
await Runner.run(agent, input="Use tool.")
async def run_streaming():
result = Runner.run_streamed(agent, input="Use tool.")
async for _ in result.stream_events():
pass
asyncio.run(show("run():", run_nonstreaming))
asyncio.run(show("run_streamed():", run_streaming))
(Requires OPENAI_API_KEY.)
Expected Result
Both runs emit a gen_ai.execute_tool span:
run(): ['gen_ai.chat', 'gen_ai.execute_tool', 'gen_ai.invoke_agent']
run_streamed(): ['gen_ai.chat', 'gen_ai.execute_tool', 'gen_ai.invoke_agent']
Actual Result
The streamed run is missing gen_ai.execute_tool (and gen_ai.invoke_agent):
run(): ['gen_ai.chat', 'gen_ai.execute_tool', 'gen_ai.invoke_agent']
run_streamed(): ['gen_ai.chat']
Root cause
OpenAIAgentsIntegration.setup_once (the openai-agents >= 0.8 branch) patches the tool wrapper onto agents.run.get_all_tools:
agents.run.get_all_tools = new_wrapped_get_all_tools # wraps run_loop.get_all_tools
…but it never patches agents.run_internal.run_loop.get_all_tools. In openai-agents 0.17.2 the two run paths resolve tools through different references:
- non-streaming →
agents/run.py calls the (patched) get_all_tools → function tools get their on_invoke_tool wrapped → gen_ai.execute_tool spans ✅
- streaming →
agents/run_internal/run_loop.py calls its own module-level get_all_tools, which is left unpatched → function tools are never wrapped → no spans ❌
Note get_model is patched at the run_loop level (agents.run_internal.run_loop.get_model = ...), which is why gen_ai.chat still works under streaming — get_all_tools was simply missed there. The streamed runner wrapper also doesn't appear to finish gen_ai.invoke_agent spans, which likely explains the second missing span.
Suggested fix
Apply the same _get_all_tools wrapper to the symbol the streamed loop calls, e.g. also patch agents.run_internal.run_loop.get_all_tools in setup_once. Confirmed working as a monkeypatch:
from functools import wraps
import agents.run_internal.run_loop as run_loop
from sentry_sdk.integrations.openai_agents.patches import _get_all_tools
_orig = run_loop.get_all_tools
@wraps(_orig)
async def _wrapped(agent, context_wrapper):
return await _get_all_tools(_orig, agent, context_wrapper)
run_loop.get_all_tools = _wrapped
With this applied, run_streamed() emits gen_ai.execute_tool as expected.
Environment
- sentry-sdk: 2.63.0 (also reproduced on 2.59.0; 2.63.0 is the latest on PyPI)
- openai-agents: 0.17.2
- Python: 3.12
- Not affected by the
stream_gen_ai_spans option (reproduces with it on or off).
How do you use Sentry?
Sentry Saas (sentry.io)
Version
2.63.0
Steps to Reproduce
Function-tool spans (
gen_ai.execute_tool) are emitted forRunner.run(...)but not forRunner.run_streamed(...).gen_ai.chatspans are present in both, so streamed agent traces show the model calls but none of the tool calls.Minimal repro (one tool,
tool_choice="required"so a tool call is guaranteed; spans inspected viabefore_send_transaction):(Requires
OPENAI_API_KEY.)Expected Result
Both runs emit a
gen_ai.execute_toolspan:Actual Result
The streamed run is missing
gen_ai.execute_tool(andgen_ai.invoke_agent):Root cause
OpenAIAgentsIntegration.setup_once(theopenai-agents >= 0.8branch) patches the tool wrapper ontoagents.run.get_all_tools:…but it never patches
agents.run_internal.run_loop.get_all_tools. In openai-agents 0.17.2 the two run paths resolve tools through different references:agents/run.pycalls the (patched)get_all_tools→ function tools get theiron_invoke_toolwrapped →gen_ai.execute_toolspans ✅agents/run_internal/run_loop.pycalls its own module-levelget_all_tools, which is left unpatched → function tools are never wrapped → no spans ❌Note
get_modelis patched at therun_looplevel (agents.run_internal.run_loop.get_model = ...), which is whygen_ai.chatstill works under streaming —get_all_toolswas simply missed there. The streamed runner wrapper also doesn't appear to finishgen_ai.invoke_agentspans, which likely explains the second missing span.Suggested fix
Apply the same
_get_all_toolswrapper to the symbol the streamed loop calls, e.g. also patchagents.run_internal.run_loop.get_all_toolsinsetup_once. Confirmed working as a monkeypatch:With this applied,
run_streamed()emitsgen_ai.execute_toolas expected.Environment
stream_gen_ai_spansoption (reproduces with it on or off).