| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- import pytest
- from agents.items import MessageOutputItem, ToolCallItem
- from openai.types.responses.response_file_search_tool_call import (
- ResponseFileSearchToolCall,
- Result as FileSearchResult,
- )
- from openai.types.responses.response_function_web_search import ActionSearch, ResponseFunctionWebSearch
- from openai.types.responses.response_output_message import ResponseOutputMessage, ResponseOutputText
- from agency_swarm.agent.core import Agent
- from agency_swarm.messages import MessageFormatter
- @pytest.mark.asyncio
- async def test_web_search_results_have_metadata():
- """Verify web search results are returned as user messages with metadata."""
- agent = Agent(name="MetaAgent", instructions="Test")
- web_call = ResponseFunctionWebSearch(
- id="1",
- action=ActionSearch(query="hello", type="search"),
- status="completed",
- type="web_search_call",
- )
- assistant_msg = ResponseOutputMessage(
- id="m1",
- content=[ResponseOutputText(annotations=[], text="result", type="output_text")],
- role="assistant",
- status="completed",
- type="message",
- )
- run_items = [
- ToolCallItem(agent, web_call),
- MessageOutputItem(agent, assistant_msg),
- ]
- results = MessageFormatter.extract_hosted_tool_results(
- agent,
- run_items,
- caller_agent="Researcher",
- )
- assert results, "Expected hosted tool result"
- result = results[0]
- assert result.get("agent") == agent.name
- assert result.get("callerAgent") == "Researcher"
- assert "WEB_SEARCH_RESULTS" in result.get("content", "")
- def test_extract_no_results_returns_empty():
- """Ensure empty list is returned when no hosted tool calls present."""
- agent = Agent(name="EmptyAgent", instructions="Test")
- results = MessageFormatter.extract_hosted_tool_results(
- agent,
- [],
- caller_agent="AnyAgent",
- )
- assert results == []
- def test_web_search_results_deduplicated():
- """Only one synthetic result should be created for multiple assistant messages."""
- agent = Agent(name="MetaAgent", instructions="Test")
- web_call = ResponseFunctionWebSearch(
- id="1",
- action=ActionSearch(query="hello", type="search"),
- status="completed",
- type="web_search_call",
- )
- assistant_msgs = [
- ResponseOutputMessage(
- id="m1",
- content=[ResponseOutputText(annotations=[], text="result1", type="output_text")],
- role="assistant",
- status="completed",
- type="message",
- ),
- ResponseOutputMessage(
- id="m2",
- content=[ResponseOutputText(annotations=[], text="result2", type="output_text")],
- role="assistant",
- status="completed",
- type="message",
- ),
- ]
- run_items = [ToolCallItem(agent, web_call)] + [MessageOutputItem(agent, m) for m in assistant_msgs]
- results = MessageFormatter.extract_hosted_tool_results(agent, run_items) # type: ignore[arg-type]
- assert len(results) == 1
- assert "result1" in results[0]["content"]
- assert "result2" not in results[0]["content"]
- def test_multiple_web_searches_get_distinct_results():
- """Each web search should get its own corresponding assistant message content."""
- agent = Agent(name="SearchAgent", instructions="Test")
- # First web search and its result
- web_call1 = ResponseFunctionWebSearch(
- id="search_1",
- action=ActionSearch(query="python", type="search"),
- status="completed",
- type="web_search_call",
- )
- assistant_msg1 = ResponseOutputMessage(
- id="msg_1",
- content=[ResponseOutputText(annotations=[], text="Python results", type="output_text")],
- role="assistant",
- status="completed",
- type="message",
- )
- # Second web search and its result
- web_call2 = ResponseFunctionWebSearch(
- id="search_2",
- action=ActionSearch(query="javascript", type="search"),
- status="completed",
- type="web_search_call",
- )
- assistant_msg2 = ResponseOutputMessage(
- id="msg_2",
- content=[ResponseOutputText(annotations=[], text="JavaScript results", type="output_text")],
- role="assistant",
- status="completed",
- type="message",
- )
- # Build run items in order: search1, msg1, search2, msg2
- run_items = [
- ToolCallItem(agent, web_call1),
- MessageOutputItem(agent, assistant_msg1),
- ToolCallItem(agent, web_call2),
- MessageOutputItem(agent, assistant_msg2),
- ]
- results = MessageFormatter.extract_hosted_tool_results(agent, run_items)
- # Should create two synthetic results
- assert len(results) == 2, "Expected two results for two web searches"
- # First result should have Python content
- assert "search_1" in results[0]["content"]
- assert "Python results" in results[0]["content"]
- assert "JavaScript results" not in results[0]["content"]
- # Second result should have JavaScript content
- assert "search_2" in results[1]["content"]
- assert "JavaScript results" in results[1]["content"]
- assert "Python results" not in results[1]["content"]
- def test_file_search_results_only_persist_for_executing_agent():
- """Ensure hosted tool preservation is only emitted by the agent that ran the tool."""
- ceo = Agent(name="CEO", instructions="Test")
- worker = Agent(name="Worker", instructions="Test")
- tool_call = ResponseFileSearchToolCall(
- id="fs_unique",
- queries=["favorite books"],
- status="completed",
- type="file_search_call",
- results=[
- FileSearchResult(
- file_id="file-1",
- filename="favorite_books.txt",
- score=0.9,
- text="Books list",
- )
- ],
- )
- hosted_run_items = [ToolCallItem(ceo, tool_call)]
- ceo_results = MessageFormatter.extract_hosted_tool_results(
- ceo,
- hosted_run_items,
- caller_agent="Worker",
- )
- assert ceo_results, "Executing agent should persist hosted tool results"
- worker_results = MessageFormatter.extract_hosted_tool_results(
- worker,
- hosted_run_items,
- caller_agent="Worker",
- )
- assert worker_results == [], "Non-executing agent must not duplicate hosted tool preservation"
- def test_web_search_results_only_persist_for_executing_agent():
- """Ensure web search preservation is written only by the executing agent."""
- ceo = Agent(name="CEO", instructions="Test")
- worker = Agent(name="Worker", instructions="Test")
- web_call = ResponseFunctionWebSearch(
- id="web_unique",
- action=ActionSearch(query="web search", type="search"),
- status="completed",
- type="web_search_call",
- )
- assistant_msg = ResponseOutputMessage(
- id="web_msg",
- content=[ResponseOutputText(annotations=[], text="Search content", type="output_text")],
- role="assistant",
- status="completed",
- type="message",
- )
- run_items = [
- ToolCallItem(ceo, web_call),
- MessageOutputItem(ceo, assistant_msg),
- ]
- ceo_results = MessageFormatter.extract_hosted_tool_results(
- ceo,
- run_items,
- caller_agent="Worker",
- )
- assert ceo_results, "Executing agent should persist web search results"
- assert "WEB_SEARCH_RESULTS" in ceo_results[0]["content"]
- worker_results = MessageFormatter.extract_hosted_tool_results(
- worker,
- run_items,
- caller_agent="Worker",
- )
- assert worker_results == [], "Non-executing agent must not duplicate web search preservation"
|