Skip to content

Commit 40c7270

Browse files
ref(openai-agents): Remove handoff spans
1 parent 9879c76 commit 40c7270

4 files changed

Lines changed: 1 addition & 308 deletions

File tree

sentry_sdk/integrations/openai_agents/patches/agent_run.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from sentry_sdk.utils import capture_internal_exceptions, reraise
88

99
from ..spans import (
10-
handoff_span,
1110
invoke_agent_span,
1211
update_invoke_agent_span,
1312
)
@@ -212,16 +211,9 @@ async def _execute_handoffs(
212211
context_wrapper: "Optional[agents.RunContextWrapper]" = kwargs.get(
213212
"context_wrapper"
214213
)
215-
run_handoffs = kwargs.get("run_handoffs")
216214
# openai-agents >= 0.14 renamed `agent` to `public_agent`.
217215
agent: "Optional[agents.Agent]" = kwargs.get("public_agent", kwargs.get("agent"))
218216

219-
# Create Sentry handoff span for the first handoff (agents library only processes the first one)
220-
if run_handoffs:
221-
first_handoff = run_handoffs[0]
222-
handoff_agent_name = first_handoff.handoff.agent_name
223-
handoff_span(context_wrapper, agent, handoff_agent_name)
224-
225217
# Call original method with all parameters
226218
try:
227219
result = await original_execute_handoffs(*args, **kwargs)

sentry_sdk/integrations/openai_agents/spans/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from .ai_client import ai_client_context, update_ai_client_span # noqa: F401
22
from .execute_tool import execute_tool_span, update_execute_tool_span # noqa: F401
3-
from .handoff import handoff_span # noqa: F401
43
from .invoke_agent import (
54
invoke_agent_span, # noqa: F401
65
update_invoke_agent_span, # noqa: F401

sentry_sdk/integrations/openai_agents/spans/handoff.py

Lines changed: 0 additions & 30 deletions
This file was deleted.

tests/integrations/openai_agents/test_openai_agents.py

Lines changed: 1 addition & 269 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
Usage,
1717
)
1818
from agents.computer import Computer
19-
from agents.exceptions import MaxTurnsExceeded, ModelBehaviorError
19+
from agents.exceptions import ModelBehaviorError
2020
from agents.items import (
2121
ResponseFunctionToolCall,
2222
ResponseOutputMessage,
@@ -1618,274 +1618,6 @@ def test_agent_invocation_span_sync(
16181618
)
16191619

16201620

1621-
@pytest.mark.asyncio
1622-
async def test_handoff_span(
1623-
sentry_init,
1624-
capture_items,
1625-
get_model_response,
1626-
):
1627-
"""
1628-
Test that handoff spans are created when agents hand off to other agents.
1629-
"""
1630-
client = AsyncOpenAI(api_key="test-key")
1631-
model = OpenAIResponsesModel(model="gpt-4-mini", openai_client=client)
1632-
1633-
# Create two simple agents with a handoff relationship
1634-
secondary_agent = agents.Agent(
1635-
name="secondary_agent",
1636-
instructions="You are a secondary agent.",
1637-
model=model,
1638-
)
1639-
1640-
primary_agent = agents.Agent(
1641-
name="primary_agent",
1642-
instructions="You are a primary agent that hands off to secondary agent.",
1643-
model=model,
1644-
handoffs=[secondary_agent],
1645-
)
1646-
1647-
handoff_response = get_model_response(
1648-
Response(
1649-
id="resp_tool_123",
1650-
output=[
1651-
ResponseFunctionToolCall(
1652-
id="call_handoff_123",
1653-
call_id="call_handoff_123",
1654-
name="transfer_to_secondary_agent",
1655-
type="function_call",
1656-
arguments="{}",
1657-
)
1658-
],
1659-
parallel_tool_calls=False,
1660-
tool_choice="none",
1661-
tools=[],
1662-
created_at=10000000,
1663-
model="gpt-4",
1664-
object="response",
1665-
usage=ResponseUsage(
1666-
input_tokens=10,
1667-
input_tokens_details=InputTokensDetails(
1668-
cached_tokens=0,
1669-
cache_write_tokens=0,
1670-
),
1671-
output_tokens=20,
1672-
output_tokens_details=OutputTokensDetails(
1673-
reasoning_tokens=5,
1674-
),
1675-
total_tokens=30,
1676-
),
1677-
),
1678-
serialize_pydantic=True,
1679-
)
1680-
1681-
final_response = get_model_response(
1682-
Response(
1683-
id="resp_final_123",
1684-
output=[
1685-
ResponseOutputMessage(
1686-
id="msg_final",
1687-
type="message",
1688-
status="completed",
1689-
content=[
1690-
ResponseOutputText(
1691-
text="I'm the specialist and I can help with that!",
1692-
type="output_text",
1693-
annotations=[],
1694-
)
1695-
],
1696-
role="assistant",
1697-
)
1698-
],
1699-
parallel_tool_calls=False,
1700-
tool_choice="none",
1701-
tools=[],
1702-
created_at=10000000,
1703-
model="gpt-4",
1704-
object="response",
1705-
usage=ResponseUsage(
1706-
input_tokens=10,
1707-
input_tokens_details=InputTokensDetails(
1708-
cached_tokens=0,
1709-
cache_write_tokens=0,
1710-
),
1711-
output_tokens=20,
1712-
output_tokens_details=OutputTokensDetails(
1713-
reasoning_tokens=5,
1714-
),
1715-
total_tokens=30,
1716-
),
1717-
),
1718-
serialize_pydantic=True,
1719-
)
1720-
with patch.object(
1721-
primary_agent.model._client._client,
1722-
"send",
1723-
side_effect=[handoff_response, final_response],
1724-
) as _:
1725-
sentry_init(
1726-
integrations=[OpenAIAgentsIntegration()],
1727-
disabled_integrations=[StdlibIntegration],
1728-
traces_sample_rate=1.0,
1729-
)
1730-
1731-
items = capture_items("span")
1732-
1733-
result = await agents.Runner.run(
1734-
primary_agent,
1735-
"Please hand off to secondary agent",
1736-
run_config=test_run_config,
1737-
)
1738-
1739-
assert result is not None
1740-
1741-
sentry_sdk.flush()
1742-
spans = [item.payload for item in items]
1743-
handoff_span = next(
1744-
span
1745-
for span in spans
1746-
if span["attributes"].get("sentry.op") == OP.GEN_AI_HANDOFF
1747-
)
1748-
1749-
# Verify handoff span was created
1750-
assert handoff_span is not None
1751-
assert handoff_span["name"] == "handoff from primary_agent to secondary_agent"
1752-
assert handoff_span["attributes"]["gen_ai.operation.name"] == "handoff"
1753-
1754-
1755-
@pytest.mark.asyncio
1756-
async def test_max_turns_before_handoff_span(
1757-
sentry_init,
1758-
capture_items,
1759-
get_model_response,
1760-
):
1761-
"""
1762-
Example raising agents.exceptions.AgentsException after the agent invocation span is complete.
1763-
"""
1764-
client = AsyncOpenAI(api_key="test-key")
1765-
model = OpenAIResponsesModel(model="gpt-4-mini", openai_client=client)
1766-
1767-
# Create two simple agents with a handoff relationship
1768-
secondary_agent = agents.Agent(
1769-
name="secondary_agent",
1770-
instructions="You are a secondary agent.",
1771-
model=model,
1772-
)
1773-
1774-
primary_agent = agents.Agent(
1775-
name="primary_agent",
1776-
instructions="You are a primary agent that hands off to secondary agent.",
1777-
model=model,
1778-
handoffs=[secondary_agent],
1779-
)
1780-
1781-
handoff_response = get_model_response(
1782-
Response(
1783-
id="resp_tool_123",
1784-
output=[
1785-
ResponseFunctionToolCall(
1786-
id="call_handoff_123",
1787-
call_id="call_handoff_123",
1788-
name="transfer_to_secondary_agent",
1789-
type="function_call",
1790-
arguments="{}",
1791-
)
1792-
],
1793-
parallel_tool_calls=False,
1794-
tool_choice="none",
1795-
tools=[],
1796-
created_at=10000000,
1797-
model="gpt-4",
1798-
object="response",
1799-
usage=ResponseUsage(
1800-
input_tokens=10,
1801-
input_tokens_details=InputTokensDetails(
1802-
cached_tokens=0,
1803-
cache_write_tokens=0,
1804-
),
1805-
output_tokens=20,
1806-
output_tokens_details=OutputTokensDetails(
1807-
reasoning_tokens=5,
1808-
),
1809-
total_tokens=30,
1810-
),
1811-
),
1812-
serialize_pydantic=True,
1813-
)
1814-
1815-
final_response = get_model_response(
1816-
Response(
1817-
id="resp_final_123",
1818-
output=[
1819-
ResponseOutputMessage(
1820-
id="msg_final",
1821-
type="message",
1822-
status="completed",
1823-
content=[
1824-
ResponseOutputText(
1825-
text="I'm the specialist and I can help with that!",
1826-
type="output_text",
1827-
annotations=[],
1828-
)
1829-
],
1830-
role="assistant",
1831-
)
1832-
],
1833-
parallel_tool_calls=False,
1834-
tool_choice="none",
1835-
tools=[],
1836-
created_at=10000000,
1837-
model="gpt-4",
1838-
object="response",
1839-
usage=ResponseUsage(
1840-
input_tokens=10,
1841-
input_tokens_details=InputTokensDetails(
1842-
cached_tokens=0,
1843-
cache_write_tokens=0,
1844-
),
1845-
output_tokens=20,
1846-
output_tokens_details=OutputTokensDetails(
1847-
reasoning_tokens=5,
1848-
),
1849-
total_tokens=30,
1850-
),
1851-
),
1852-
serialize_pydantic=True,
1853-
)
1854-
with patch.object(
1855-
primary_agent.model._client._client,
1856-
"send",
1857-
side_effect=[handoff_response, final_response],
1858-
) as _:
1859-
sentry_init(
1860-
integrations=[OpenAIAgentsIntegration()],
1861-
disabled_integrations=[StdlibIntegration],
1862-
traces_sample_rate=1.0,
1863-
)
1864-
1865-
items = capture_items("span")
1866-
1867-
with pytest.raises(MaxTurnsExceeded):
1868-
await agents.Runner.run(
1869-
primary_agent,
1870-
"Please hand off to secondary agent",
1871-
run_config=test_run_config,
1872-
max_turns=1,
1873-
)
1874-
1875-
sentry_sdk.flush()
1876-
spans = [item.payload for item in items]
1877-
handoff_span = next(
1878-
span
1879-
for span in spans
1880-
if span["attributes"].get("sentry.op") == OP.GEN_AI_HANDOFF
1881-
)
1882-
1883-
# Verify handoff span was created
1884-
assert handoff_span is not None
1885-
assert handoff_span["name"] == "handoff from primary_agent to secondary_agent"
1886-
assert handoff_span["attributes"]["gen_ai.operation.name"] == "handoff"
1887-
1888-
18891621
@pytest.mark.parametrize("user_hooks", [True, False])
18901622
@pytest.mark.asyncio
18911623
async def test_tool_execution_span(

0 commit comments

Comments
 (0)