|
16 | 16 | Usage, |
17 | 17 | ) |
18 | 18 | from agents.computer import Computer |
19 | | -from agents.exceptions import MaxTurnsExceeded, ModelBehaviorError |
| 19 | +from agents.exceptions import ModelBehaviorError |
20 | 20 | from agents.items import ( |
21 | 21 | ResponseFunctionToolCall, |
22 | 22 | ResponseOutputMessage, |
@@ -1618,274 +1618,6 @@ def test_agent_invocation_span_sync( |
1618 | 1618 | ) |
1619 | 1619 |
|
1620 | 1620 |
|
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 | | - |
1889 | 1621 | @pytest.mark.parametrize("user_hooks", [True, False]) |
1890 | 1622 | @pytest.mark.asyncio |
1891 | 1623 | async def test_tool_execution_span( |
|
0 commit comments