test_agent_capabilities.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. """Unit tests for agent capability detection."""
  2. from agents import (
  3. CodeInterpreterTool,
  4. FileSearchTool,
  5. HostedMCPTool,
  6. ModelSettings,
  7. WebSearchTool,
  8. )
  9. from agents.mcp import MCPServerStdio
  10. from openai.types.responses.tool_param import CodeInterpreter, Mcp
  11. from openai.types.shared import Reasoning
  12. from agency_swarm import Agent, BaseTool, function_tool
  13. from agency_swarm.utils.model_utils import get_agent_capabilities
  14. class SampleTool(BaseTool):
  15. """Sample custom tool for testing."""
  16. def run(self) -> str:
  17. return "sample"
  18. @function_tool
  19. def sample_function_tool() -> str:
  20. """Sample function tool for testing."""
  21. return "sample"
  22. def test_agent_capabilities_case_table() -> None:
  23. """Capability detection should stay stable for key tool/model combinations."""
  24. mcp_server = MCPServerStdio(
  25. name="test_server",
  26. params={
  27. "command": "npx",
  28. "args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
  29. },
  30. client_session_timeout_seconds=20,
  31. )
  32. hosted_mcp = HostedMCPTool(tool_config=Mcp(server_label="test", server_url="https://example.com"))
  33. cases: list[tuple[Agent, set[str]]] = [
  34. (Agent(name="EmptyAgent", instructions="Test"), {"reasoning"}),
  35. (Agent(name="BaseToolAgent", instructions="Test", tools=[SampleTool]), {"tools", "reasoning"}),
  36. (Agent(name="FunctionToolAgent", instructions="Test", tools=[sample_function_tool]), {"tools", "reasoning"}),
  37. (
  38. Agent(name="HostedToolsAgent", instructions="Test", tools=[FileSearchTool(vector_store_ids=["vs_123"])]),
  39. {"file_search", "reasoning"},
  40. ),
  41. (
  42. Agent(name="CodeAgent", instructions="Test", tools=[CodeInterpreterTool(tool_config=CodeInterpreter())]),
  43. {"code_interpreter", "reasoning"},
  44. ),
  45. (Agent(name="WebAgent", instructions="Test", tools=[WebSearchTool()]), {"web_search", "reasoning"}),
  46. (Agent(name="HostedMcpAgent", instructions="Test", tools=[hosted_mcp]), {"hosted_mcp", "reasoning"}),
  47. (Agent(name="McpServerAgent", instructions="Test", mcp_servers=[mcp_server]), {"tools", "reasoning"}),
  48. (
  49. Agent(
  50. name="MixedAgent",
  51. instructions="Test",
  52. tools=[
  53. SampleTool,
  54. FileSearchTool(vector_store_ids=["vs_123"]),
  55. CodeInterpreterTool(tool_config=CodeInterpreter()),
  56. ],
  57. ),
  58. {"tools", "file_search", "code_interpreter", "reasoning"},
  59. ),
  60. ]
  61. for agent, expected in cases:
  62. assert set(get_agent_capabilities(agent)) == expected
  63. def test_reasoning_capability_from_model_or_settings() -> None:
  64. """Reasoning capability should be detected from either model name or explicit settings."""
  65. cases: list[tuple[Agent, bool]] = [
  66. (Agent(name="O1Agent", instructions="Test", model="o1-preview"), True),
  67. (Agent(name="O3Agent", instructions="Test", model="o3"), True),
  68. (Agent(name="Gpt5Agent", instructions="Test", model="gpt-5.4-mini"), True),
  69. (Agent(name="NonReasoningAgent", instructions="Test", model="gpt-4.1"), False),
  70. (
  71. Agent(
  72. name="ReasoningSettingAgent",
  73. instructions="Test",
  74. model="gpt-4.1",
  75. model_settings=ModelSettings(reasoning=Reasoning(effort="high")),
  76. ),
  77. True,
  78. ),
  79. ]
  80. for agent, expected in cases:
  81. capabilities = get_agent_capabilities(agent)
  82. assert ("reasoning" in capabilities) is expected
  83. def test_capabilities_order_and_uniqueness() -> None:
  84. """Capability order should be deterministic and not duplicate values."""
  85. agent = Agent(
  86. name="OrderedAgent",
  87. instructions="Test",
  88. model="gpt-5.4-mini",
  89. tools=[WebSearchTool(), SampleTool, CodeInterpreterTool(tool_config=CodeInterpreter())],
  90. )
  91. capabilities = get_agent_capabilities(agent)
  92. assert capabilities == ["tools", "reasoning", "code_interpreter", "web_search"]
  93. assert len(capabilities) == len(set(capabilities))
  94. def test_agent_with_all_capabilities() -> None:
  95. """Agent with custom + hosted + reasoning capabilities should expose the full set."""
  96. agent = Agent(
  97. name="FullAgent",
  98. instructions="Test",
  99. model="gpt-5.4-mini",
  100. tools=[
  101. SampleTool,
  102. FileSearchTool(vector_store_ids=["vs_123"]),
  103. CodeInterpreterTool(tool_config=CodeInterpreter()),
  104. WebSearchTool(),
  105. ],
  106. model_settings=ModelSettings(reasoning=Reasoning(effort="high")),
  107. )
  108. assert set(get_agent_capabilities(agent)) == {"tools", "reasoning", "file_search", "code_interpreter", "web_search"}
  109. def test_files_folder_capabilities_without_openai_side_effects(tmp_path, monkeypatch) -> None:
  110. monkeypatch.setenv("DRY_RUN", "1")
  111. files = tmp_path / "files"
  112. files.mkdir()
  113. (files / "report.pdf").write_text("report", encoding="utf-8")
  114. (files / "chart.png").write_bytes(b"png")
  115. agent = Agent(name="FilesAgent", instructions="Test", files_folder=str(files))
  116. assert {"file_search", "code_interpreter"} <= set(get_agent_capabilities(agent))