mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
renamed all (#3207)
This commit is contained in:
@@ -26,8 +26,8 @@ from a2a.types import Message as A2AMessage
|
||||
from a2a.types import Part as A2APart
|
||||
from a2a.types import Role as A2ARole
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
ChatMessage,
|
||||
@@ -193,11 +193,11 @@ class A2AAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Get a response from the agent.
|
||||
|
||||
This method returns the final result of the agent's execution
|
||||
as a single AgentRunResponse object. The caller is blocked until
|
||||
as a single AgentResponse object. The caller is blocked until
|
||||
the final result is available.
|
||||
|
||||
Args:
|
||||
@@ -212,7 +212,7 @@ class A2AAgent(BaseAgent):
|
||||
"""
|
||||
# Collect all updates and use framework to consolidate updates into response
|
||||
updates = [update async for update in self.run_stream(messages, thread=thread, **kwargs)]
|
||||
return AgentRunResponse.from_agent_run_response_updates(updates)
|
||||
return AgentResponse.from_agent_run_response_updates(updates)
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
@@ -220,11 +220,11 @@ class A2AAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Run the agent as a stream.
|
||||
|
||||
This method will return the intermediate steps and final results of the
|
||||
agent's execution as a stream of AgentRunResponseUpdate objects to the caller.
|
||||
agent's execution as a stream of AgentResponseUpdate objects to the caller.
|
||||
|
||||
Args:
|
||||
messages: The message(s) to send to the agent.
|
||||
@@ -245,7 +245,7 @@ class A2AAgent(BaseAgent):
|
||||
if isinstance(item, Message):
|
||||
# Process A2A Message
|
||||
contents = self._parse_contents_from_a2a(item.parts)
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=contents,
|
||||
role=Role.ASSISTANT if item.role == A2ARole.agent else Role.USER,
|
||||
response_id=str(getattr(item, "message_id", uuid.uuid4())),
|
||||
@@ -260,7 +260,7 @@ class A2AAgent(BaseAgent):
|
||||
for message in task_messages:
|
||||
# Use the artifact's ID from raw_representation as message_id for unique identification
|
||||
artifact_id = getattr(message.raw_representation, "artifact_id", None)
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=message.contents,
|
||||
role=message.role,
|
||||
response_id=task.id,
|
||||
@@ -269,7 +269,7 @@ class A2AAgent(BaseAgent):
|
||||
)
|
||||
else:
|
||||
# Empty task
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[],
|
||||
role=Role.ASSISTANT,
|
||||
response_id=task.id,
|
||||
|
||||
@@ -21,8 +21,8 @@ from a2a.types import (
|
||||
)
|
||||
from a2a.types import Role as A2ARole
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
ChatMessage,
|
||||
DataContent,
|
||||
ErrorContent,
|
||||
@@ -131,7 +131,7 @@ async def test_run_with_message_response(a2a_agent: A2AAgent, mock_a2a_client: M
|
||||
|
||||
response = await a2a_agent.run("Hello agent")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 1
|
||||
assert response.messages[0].role == Role.ASSISTANT
|
||||
assert response.messages[0].text == "Hello from agent!"
|
||||
@@ -146,7 +146,7 @@ async def test_run_with_task_response_single_artifact(a2a_agent: A2AAgent, mock_
|
||||
|
||||
response = await a2a_agent.run("Generate a report")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 1
|
||||
assert response.messages[0].role == Role.ASSISTANT
|
||||
assert response.messages[0].text == "Generated report content"
|
||||
@@ -165,7 +165,7 @@ async def test_run_with_task_response_multiple_artifacts(a2a_agent: A2AAgent, mo
|
||||
|
||||
response = await a2a_agent.run("Generate multiple outputs")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 3
|
||||
|
||||
assert response.messages[0].text == "First artifact content"
|
||||
@@ -185,7 +185,7 @@ async def test_run_with_task_response_no_artifacts(a2a_agent: A2AAgent, mock_a2a
|
||||
|
||||
response = await a2a_agent.run("Do something with no output")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.response_id == "task-empty"
|
||||
|
||||
|
||||
@@ -357,13 +357,13 @@ async def test_run_stream_with_message_response(a2a_agent: A2AAgent, mock_a2a_cl
|
||||
mock_a2a_client.add_message_response("msg-stream-123", "Streaming response from agent!", "agent")
|
||||
|
||||
# Collect streaming updates
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in a2a_agent.run_stream("Hello agent"):
|
||||
updates.append(update)
|
||||
|
||||
# Verify streaming response
|
||||
assert len(updates) == 1
|
||||
assert isinstance(updates[0], AgentRunResponseUpdate)
|
||||
assert isinstance(updates[0], AgentResponseUpdate)
|
||||
assert updates[0].role == Role.ASSISTANT
|
||||
assert len(updates[0].contents) == 1
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ from ag_ui.core import (
|
||||
ToolCallStartEvent,
|
||||
)
|
||||
from agent_framework import (
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponseUpdate,
|
||||
FunctionApprovalRequestContent,
|
||||
FunctionCallContent,
|
||||
FunctionResultContent,
|
||||
@@ -81,9 +81,9 @@ class AgentFrameworkEventBridge:
|
||||
self.should_stop_after_confirm: bool = False # Flag to stop run after confirm_changes
|
||||
self.suppressed_summary: str = "" # Store LLM summary to show after confirmation
|
||||
|
||||
async def from_agent_run_update(self, update: AgentRunResponseUpdate) -> list[BaseEvent]:
|
||||
async def from_agent_run_update(self, update: AgentResponseUpdate) -> list[BaseEvent]:
|
||||
"""
|
||||
Convert an AgentRunResponseUpdate to AG-UI events.
|
||||
Convert an AgentResponseUpdate to AG-UI events.
|
||||
|
||||
Args:
|
||||
update: The agent run update to convert.
|
||||
|
||||
@@ -646,11 +646,11 @@ class DefaultOrchestrator(Orchestrator):
|
||||
yield end_event
|
||||
|
||||
if response_format and all_updates:
|
||||
from agent_framework import AgentRunResponse
|
||||
from agent_framework import AgentResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
logger.info(f"Processing structured output, update count: {len(all_updates)}")
|
||||
final_response = AgentRunResponse.from_agent_run_response_updates(
|
||||
final_response = AgentResponse.from_agent_run_response_updates(
|
||||
all_updates, output_format_type=response_format
|
||||
)
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ The package uses a clean, orchestrator-based architecture:
|
||||
- **AgentFrameworkAgent**: Lightweight wrapper that delegates to orchestrators
|
||||
- **Orchestrators**: Handle different execution flows (default, human-in-the-loop, etc.)
|
||||
- **Confirmation Strategies**: Domain-specific confirmation messages (extensible)
|
||||
- **AgentFrameworkEventBridge**: Converts AgentRunResponseUpdate to AG-UI events
|
||||
- **AgentFrameworkEventBridge**: Converts AgentResponseUpdate to AG-UI events
|
||||
- **Message Adapters**: Bidirectional conversion between AG-UI and Agent Framework message formats
|
||||
- **FastAPI Endpoint**: Streaming HTTP endpoint with Server-Sent Events (SSE)
|
||||
|
||||
@@ -198,10 +198,10 @@ def my_tool(param: str) -> str:
|
||||
|
||||
def my_custom_agent(chat_client: ChatClientProtocol) -> AgentFrameworkAgent:
|
||||
"""Create a custom agent with the specified chat client.
|
||||
|
||||
|
||||
Args:
|
||||
chat_client: The chat client to use for the agent
|
||||
|
||||
|
||||
Returns:
|
||||
A configured AgentFrameworkAgent instance
|
||||
"""
|
||||
@@ -211,7 +211,7 @@ def my_custom_agent(chat_client: ChatClientProtocol) -> AgentFrameworkAgent:
|
||||
chat_client=chat_client,
|
||||
tools=[my_tool],
|
||||
)
|
||||
|
||||
|
||||
return AgentFrameworkAgent(
|
||||
agent=agent,
|
||||
name="MyCustomAgent",
|
||||
@@ -302,13 +302,13 @@ from agent_framework.ag_ui import AgentFrameworkAgent, ConfirmationStrategy
|
||||
class CustomConfirmationStrategy(ConfirmationStrategy):
|
||||
def on_approval_accepted(self, steps: list[dict[str, Any]]) -> str:
|
||||
return "Your custom approval message!"
|
||||
|
||||
|
||||
def on_approval_rejected(self, steps: list[dict[str, Any]]) -> str:
|
||||
return "Your custom rejection message!"
|
||||
|
||||
|
||||
def on_state_confirmed(self) -> str:
|
||||
return "State changes confirmed!"
|
||||
|
||||
|
||||
def on_state_rejected(self) -> str:
|
||||
return "State changes rejected!"
|
||||
|
||||
@@ -349,7 +349,7 @@ class MyCustomOrchestrator(Orchestrator):
|
||||
def can_handle(self, context: ExecutionContext) -> bool:
|
||||
# Return True if this orchestrator should handle the request
|
||||
return context.input_data.get("custom_mode") == True
|
||||
|
||||
|
||||
async def run(self, context: ExecutionContext):
|
||||
# Custom execution logic
|
||||
yield RunStartedEvent(...)
|
||||
|
||||
@@ -12,7 +12,7 @@ from ag_ui.core import (
|
||||
ToolCallResultEvent,
|
||||
ToolCallStartEvent,
|
||||
)
|
||||
from agent_framework import AgentRunResponseUpdate, FunctionCallContent, FunctionResultContent, TextContent
|
||||
from agent_framework import AgentResponseUpdate, FunctionCallContent, FunctionResultContent, TextContent
|
||||
|
||||
from agent_framework_ag_ui._events import AgentFrameworkEventBridge
|
||||
|
||||
@@ -28,7 +28,7 @@ async def test_tool_call_flow():
|
||||
arguments={"location": "Seattle"},
|
||||
)
|
||||
|
||||
update1 = AgentRunResponseUpdate(contents=[tool_call])
|
||||
update1 = AgentResponseUpdate(contents=[tool_call])
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
|
||||
# Should have: ToolCallStartEvent, ToolCallArgsEvent
|
||||
@@ -49,7 +49,7 @@ async def test_tool_call_flow():
|
||||
result="Weather in Seattle: Rainy, 52°F",
|
||||
)
|
||||
|
||||
update2 = AgentRunResponseUpdate(contents=[tool_result])
|
||||
update2 = AgentResponseUpdate(contents=[tool_result])
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Should have: ToolCallEndEvent, ToolCallResultEvent
|
||||
@@ -78,7 +78,7 @@ async def test_text_with_tool_call():
|
||||
arguments={"location": "San Francisco", "days": 3},
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[text_content, tool_call])
|
||||
update = AgentResponseUpdate(contents=[text_content, tool_call])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should have: TextMessageStart, TextMessageContent, ToolCallStart, ToolCallArgs
|
||||
@@ -107,7 +107,7 @@ async def test_multiple_tool_results():
|
||||
FunctionResultContent(call_id="tool-3", result="Result 3"),
|
||||
]
|
||||
|
||||
update = AgentRunResponseUpdate(contents=results)
|
||||
update = AgentResponseUpdate(contents=results)
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should have 3 pairs of ToolCallEndEvent + ToolCallResultEvent = 6 events
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"""Tests for document writer predictive state flow with confirm_changes."""
|
||||
|
||||
from ag_ui.core import EventType, StateDeltaEvent, ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent
|
||||
from agent_framework import AgentRunResponseUpdate, FunctionCallContent, FunctionResultContent, TextContent
|
||||
from agent_framework import AgentResponseUpdate, FunctionCallContent, FunctionResultContent, TextContent
|
||||
|
||||
from agent_framework_ag_ui._events import AgentFrameworkEventBridge
|
||||
|
||||
@@ -26,7 +26,7 @@ async def test_streaming_document_with_state_deltas():
|
||||
name="write_document_local",
|
||||
arguments='{"document":"Once',
|
||||
)
|
||||
update1 = AgentRunResponseUpdate(contents=[tool_call_start])
|
||||
update1 = AgentResponseUpdate(contents=[tool_call_start])
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
|
||||
# Should have ToolCallStartEvent and ToolCallArgsEvent
|
||||
@@ -35,7 +35,7 @@ async def test_streaming_document_with_state_deltas():
|
||||
|
||||
# Second chunk - incomplete JSON, should try partial extraction
|
||||
tool_call_chunk2 = FunctionCallContent(call_id="call_123", name="write_document_local", arguments=" upon a time")
|
||||
update2 = AgentRunResponseUpdate(contents=[tool_call_chunk2])
|
||||
update2 = AgentResponseUpdate(contents=[tool_call_chunk2])
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Should emit StateDeltaEvent with partial document
|
||||
@@ -76,7 +76,7 @@ async def test_confirm_changes_emission():
|
||||
result="Document written.",
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[tool_result])
|
||||
update = AgentResponseUpdate(contents=[tool_result])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should have: ToolCallEndEvent, ToolCallResultEvent, StateSnapshotEvent, confirm_changes sequence
|
||||
@@ -116,7 +116,7 @@ async def test_text_suppression_before_confirm():
|
||||
|
||||
# Text content that should be suppressed
|
||||
text = TextContent(text="I have written a story about pirates.")
|
||||
update = AgentRunResponseUpdate(contents=[text])
|
||||
update = AgentResponseUpdate(contents=[text])
|
||||
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
@@ -151,7 +151,7 @@ async def test_no_confirm_for_non_predictive_tools():
|
||||
result="Sunny, 72°F",
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[tool_result])
|
||||
update = AgentResponseUpdate(contents=[tool_result])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should NOT have confirm_changes
|
||||
@@ -180,7 +180,7 @@ async def test_state_delta_deduplication():
|
||||
name="write_document_local",
|
||||
arguments='{"document":"Same text"}',
|
||||
)
|
||||
update1 = AgentRunResponseUpdate(contents=[tool_call1])
|
||||
update1 = AgentResponseUpdate(contents=[tool_call1])
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
|
||||
# Count state deltas
|
||||
@@ -194,7 +194,7 @@ async def test_state_delta_deduplication():
|
||||
name="write_document_local",
|
||||
arguments='{"document":"Same text"}', # Identical content
|
||||
)
|
||||
update2 = AgentRunResponseUpdate(contents=[tool_call2])
|
||||
update2 = AgentResponseUpdate(contents=[tool_call2])
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Should NOT emit state delta (same value)
|
||||
@@ -221,7 +221,7 @@ async def test_predict_state_config_multiple_fields():
|
||||
name="create_post",
|
||||
arguments='{"title":"My Post","body":"Post content"}',
|
||||
)
|
||||
update = AgentRunResponseUpdate(contents=[tool_call])
|
||||
update = AgentResponseUpdate(contents=[tool_call])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit StateDeltaEvent for both fields
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import json
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponseUpdate,
|
||||
FunctionApprovalRequestContent,
|
||||
FunctionCallContent,
|
||||
FunctionResultContent,
|
||||
@@ -19,7 +19,7 @@ async def test_basic_text_message_conversion():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[TextContent(text="Hello")])
|
||||
update = AgentResponseUpdate(contents=[TextContent(text="Hello")])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
assert len(events) == 2
|
||||
@@ -35,8 +35,8 @@ async def test_text_message_streaming():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update1 = AgentRunResponseUpdate(contents=[TextContent(text="Hello ")])
|
||||
update2 = AgentRunResponseUpdate(contents=[TextContent(text="world")])
|
||||
update1 = AgentResponseUpdate(contents=[TextContent(text="Hello ")])
|
||||
update2 = AgentResponseUpdate(contents=[TextContent(text="world")])
|
||||
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
@@ -61,7 +61,7 @@ async def test_skip_text_content_for_structured_outputs():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread", skip_text_content=True)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[TextContent(text='{"result": "data"}')])
|
||||
update = AgentResponseUpdate(contents=[TextContent(text='{"result": "data"}')])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# No events should be emitted
|
||||
@@ -74,9 +74,9 @@ async def test_skip_text_content_for_empty_text():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update1 = AgentRunResponseUpdate(contents=[TextContent(text="Hello ")])
|
||||
update2 = AgentRunResponseUpdate(contents=[TextContent(text="")]) # Empty chunk
|
||||
update3 = AgentRunResponseUpdate(contents=[TextContent(text="world")])
|
||||
update1 = AgentResponseUpdate(contents=[TextContent(text="Hello ")])
|
||||
update2 = AgentResponseUpdate(contents=[TextContent(text="")]) # Empty chunk
|
||||
update3 = AgentResponseUpdate(contents=[TextContent(text="world")])
|
||||
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
@@ -105,7 +105,7 @@ async def test_tool_call_with_name():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[FunctionCallContent(name="search_web", call_id="call_123")])
|
||||
update = AgentResponseUpdate(contents=[FunctionCallContent(name="search_web", call_id="call_123")])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
assert len(events) == 1
|
||||
@@ -121,17 +121,15 @@ async def test_tool_call_streaming_args():
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
# First chunk: name only
|
||||
update1 = AgentRunResponseUpdate(contents=[FunctionCallContent(name="search_web", call_id="call_123")])
|
||||
update1 = AgentResponseUpdate(contents=[FunctionCallContent(name="search_web", call_id="call_123")])
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
|
||||
# Second chunk: arguments chunk 1 (name can be empty string for continuation)
|
||||
update2 = AgentRunResponseUpdate(
|
||||
contents=[FunctionCallContent(name="", call_id="call_123", arguments='{"query": "')]
|
||||
)
|
||||
update2 = AgentResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_123", arguments='{"query": "')])
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Third chunk: arguments chunk 2
|
||||
update3 = AgentRunResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_123", arguments='AI"}')])
|
||||
update3 = AgentResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_123", arguments='AI"}')])
|
||||
events3 = await bridge.from_agent_run_update(update3)
|
||||
|
||||
# First update: ToolCallStartEvent
|
||||
@@ -169,9 +167,9 @@ async def test_streaming_tool_call_no_duplicate_start_events():
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
# Simulate streaming tool call: first chunk has name, subsequent chunks have name=""
|
||||
update1 = AgentRunResponseUpdate(contents=[FunctionCallContent(name="get_weather", call_id="call_789")])
|
||||
update2 = AgentRunResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_789", arguments='{"loc":')])
|
||||
update3 = AgentRunResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_789", arguments='"SF"}')])
|
||||
update1 = AgentResponseUpdate(contents=[FunctionCallContent(name="get_weather", call_id="call_789")])
|
||||
update2 = AgentResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_789", arguments='{"loc":')])
|
||||
update3 = AgentResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_789", arguments='"SF"}')])
|
||||
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
@@ -195,7 +193,7 @@ async def test_tool_result_with_dict():
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
result_data = {"status": "success", "count": 42}
|
||||
update = AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result=result_data)])
|
||||
update = AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result=result_data)])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit ToolCallEndEvent + ToolCallResultEvent
|
||||
@@ -216,7 +214,7 @@ async def test_tool_result_with_string():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result="Search complete")])
|
||||
update = AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result="Search complete")])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
assert len(events) == 2
|
||||
@@ -231,7 +229,7 @@ async def test_tool_result_with_none():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result=None)])
|
||||
update = AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result=None)])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
assert len(events) == 2
|
||||
@@ -247,7 +245,7 @@ async def test_multiple_tool_results_in_sequence():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionResultContent(call_id="call_1", result="Result 1"),
|
||||
FunctionResultContent(call_id="call_2", result="Result 2"),
|
||||
@@ -284,7 +282,7 @@ async def test_function_approval_request_basic():
|
||||
function_call=func_call,
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[approval])
|
||||
update = AgentResponseUpdate(contents=[approval])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit: ToolCallEndEvent + CustomEvent
|
||||
@@ -312,7 +310,7 @@ async def test_empty_predict_state_config():
|
||||
)
|
||||
|
||||
# Tool call with arguments
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="write_doc", call_id="call_1", arguments='{"content": "test"}'),
|
||||
FunctionResultContent(call_id="call_1", result="Done"),
|
||||
@@ -347,7 +345,7 @@ async def test_tool_not_in_predict_state_config():
|
||||
)
|
||||
|
||||
# Different tool name
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="search_web", call_id="call_1", arguments='{"query": "AI"}'),
|
||||
FunctionResultContent(call_id="call_1", result="Results"),
|
||||
@@ -376,7 +374,7 @@ async def test_state_management_tracking():
|
||||
)
|
||||
|
||||
# Streaming tool call
|
||||
update1 = AgentRunResponseUpdate(
|
||||
update1 = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="write_doc", call_id="call_1"),
|
||||
FunctionCallContent(name="", call_id="call_1", arguments='{"content": "Hello"}'),
|
||||
@@ -389,7 +387,7 @@ async def test_state_management_tracking():
|
||||
assert bridge.pending_state_updates["document"] == "Hello"
|
||||
|
||||
# Tool result should update current_state
|
||||
update2 = AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")])
|
||||
update2 = AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")])
|
||||
await bridge.from_agent_run_update(update2)
|
||||
|
||||
# current_state should be updated
|
||||
@@ -413,7 +411,7 @@ async def test_wildcard_tool_argument():
|
||||
)
|
||||
|
||||
# Complete tool call with dict arguments
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(
|
||||
name="create_recipe",
|
||||
@@ -503,7 +501,7 @@ async def test_state_snapshot_after_tool_result():
|
||||
)
|
||||
|
||||
# Tool call with streaming args
|
||||
update1 = AgentRunResponseUpdate(
|
||||
update1 = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="write_doc", call_id="call_1"),
|
||||
FunctionCallContent(name="", call_id="call_1", arguments='{"content": "Test"}'),
|
||||
@@ -512,7 +510,7 @@ async def test_state_snapshot_after_tool_result():
|
||||
await bridge.from_agent_run_update(update1)
|
||||
|
||||
# Tool result should trigger StateSnapshotEvent
|
||||
update2 = AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")])
|
||||
update2 = AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")])
|
||||
events = await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Should have: ToolCallEnd, ToolCallResult, StateSnapshot, ToolCallStart (confirm_changes), ToolCallArgs, ToolCallEnd
|
||||
@@ -528,12 +526,12 @@ async def test_message_id_persistence_across_chunks():
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
# First chunk
|
||||
update1 = AgentRunResponseUpdate(contents=[TextContent(text="Hello ")])
|
||||
update1 = AgentResponseUpdate(contents=[TextContent(text="Hello ")])
|
||||
events1 = await bridge.from_agent_run_update(update1)
|
||||
message_id = events1[0].message_id
|
||||
|
||||
# Second chunk
|
||||
update2 = AgentRunResponseUpdate(contents=[TextContent(text="world")])
|
||||
update2 = AgentResponseUpdate(contents=[TextContent(text="world")])
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Should use same message_id
|
||||
@@ -548,14 +546,14 @@ async def test_tool_call_id_tracking():
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
# First chunk with name
|
||||
update1 = AgentRunResponseUpdate(contents=[FunctionCallContent(name="search", call_id="call_1")])
|
||||
update1 = AgentResponseUpdate(contents=[FunctionCallContent(name="search", call_id="call_1")])
|
||||
await bridge.from_agent_run_update(update1)
|
||||
|
||||
assert bridge.current_tool_call_id == "call_1"
|
||||
assert bridge.current_tool_call_name == "search"
|
||||
|
||||
# Second chunk with args but no name
|
||||
update2 = AgentRunResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_1", arguments='{"q":"AI"}')])
|
||||
update2 = AgentResponseUpdate(contents=[FunctionCallContent(name="", call_id="call_1", arguments='{"q":"AI"}')])
|
||||
events2 = await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Should still track same tool call
|
||||
@@ -576,7 +574,7 @@ async def test_tool_name_reset_after_result():
|
||||
)
|
||||
|
||||
# Tool call
|
||||
update1 = AgentRunResponseUpdate(
|
||||
update1 = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="write_doc", call_id="call_1"),
|
||||
FunctionCallContent(name="", call_id="call_1", arguments='{"content": "Test"}'),
|
||||
@@ -587,7 +585,7 @@ async def test_tool_name_reset_after_result():
|
||||
assert bridge.current_tool_call_name == "write_doc"
|
||||
|
||||
# Tool result with predictive state (should trigger confirm_changes and reset)
|
||||
update2 = AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")])
|
||||
update2 = AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")])
|
||||
await bridge.from_agent_run_update(update2)
|
||||
|
||||
# Tool name should be reset
|
||||
@@ -613,7 +611,7 @@ async def test_function_approval_with_wildcard_argument():
|
||||
),
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[approval_content])
|
||||
update = AgentResponseUpdate(contents=[approval_content])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit StateSnapshotEvent with entire parsed args as value
|
||||
@@ -639,7 +637,7 @@ async def test_function_approval_missing_argument():
|
||||
function_call=FunctionCallContent(name="process", call_id="call_1", arguments='{"other_field": "value"}'),
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[approval_content])
|
||||
update = AgentResponseUpdate(contents=[approval_content])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should not emit StateSnapshotEvent since argument not found
|
||||
@@ -654,7 +652,7 @@ async def test_empty_predict_state_config_no_deltas():
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread", predict_state_config={})
|
||||
|
||||
# Tool call with arguments
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="search", call_id="call_1"),
|
||||
FunctionCallContent(name="", call_id="call_1", arguments='{"query": "test"}'),
|
||||
@@ -678,7 +676,7 @@ async def test_tool_with_no_matching_config():
|
||||
)
|
||||
|
||||
# Tool call for different tool
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="search_web", call_id="call_1"),
|
||||
FunctionCallContent(name="", call_id="call_1", arguments='{"query": "test"}'),
|
||||
@@ -698,7 +696,7 @@ async def test_tool_call_without_name_or_id():
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
# This should not crash but log an error
|
||||
update = AgentRunResponseUpdate(contents=[FunctionCallContent(name="", call_id="", arguments='{"arg": "val"}')])
|
||||
update = AgentResponseUpdate(contents=[FunctionCallContent(name="", call_id="", arguments='{"arg": "val"}')])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit ToolCallArgsEvent with generated ID
|
||||
@@ -717,7 +715,7 @@ async def test_state_delta_count_logging():
|
||||
|
||||
# Emit multiple state deltas with different content each time
|
||||
for i in range(15):
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(name="", call_id="call_1", arguments=f'{{"text": "Content variation {i}"}}'),
|
||||
]
|
||||
@@ -739,7 +737,7 @@ async def test_tool_result_with_empty_list():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result=[])])
|
||||
update = AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_123", result=[])])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
assert len(events) == 2
|
||||
@@ -761,7 +759,7 @@ async def test_tool_result_with_single_text_content():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[FunctionResultContent(call_id="call_123", result=[MockTextContent("Hello from MCP tool!")])]
|
||||
)
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
@@ -785,7 +783,7 @@ async def test_tool_result_with_multiple_text_contents():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionResultContent(
|
||||
call_id="call_123",
|
||||
@@ -813,7 +811,7 @@ async def test_tool_result_with_model_dump_objects():
|
||||
|
||||
bridge = AgentFrameworkEventBridge(run_id="test_run", thread_id="test_thread")
|
||||
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[FunctionResultContent(call_id="call_123", result=[MockModel(value=1), MockModel(value=2)])]
|
||||
)
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
"""Tests for human in the loop (function approval requests)."""
|
||||
|
||||
from agent_framework import AgentRunResponseUpdate, FunctionApprovalRequestContent, FunctionCallContent
|
||||
from agent_framework import AgentResponseUpdate, FunctionApprovalRequestContent, FunctionCallContent
|
||||
|
||||
from agent_framework_ag_ui._events import AgentFrameworkEventBridge
|
||||
|
||||
@@ -27,7 +27,7 @@ async def test_function_approval_request_emission():
|
||||
function_call=func_call,
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[approval_request])
|
||||
update = AgentResponseUpdate(contents=[approval_request])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit ToolCallEndEvent + CustomEvent for approval request
|
||||
@@ -66,7 +66,7 @@ async def test_function_approval_request_with_confirm_changes():
|
||||
function_call=func_call,
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[approval_request])
|
||||
update = AgentResponseUpdate(contents=[approval_request])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit: ToolCallEndEvent, CustomEvent, and confirm_changes (Start, Args, End) = 5 events
|
||||
@@ -129,7 +129,7 @@ async def test_multiple_approval_requests():
|
||||
function_call=func_call_2,
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[approval_1, approval_2])
|
||||
update = AgentResponseUpdate(contents=[approval_1, approval_2])
|
||||
events = await bridge.from_agent_run_update(update)
|
||||
|
||||
# Should emit ToolCallEndEvent + CustomEvent for each approval (4 events total)
|
||||
@@ -174,7 +174,7 @@ async def test_function_approval_request_sets_stop_flag():
|
||||
function_call=func_call,
|
||||
)
|
||||
|
||||
update = AgentRunResponseUpdate(contents=[approval_request])
|
||||
update = AgentResponseUpdate(contents=[approval_request])
|
||||
await bridge.from_agent_run_update(update)
|
||||
|
||||
assert bridge.should_stop_after_confirm is True
|
||||
|
||||
@@ -6,7 +6,7 @@ from collections.abc import AsyncGenerator
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import AgentRunResponseUpdate, FunctionInvocationConfiguration, TextContent, ai_function
|
||||
from agent_framework import AgentResponseUpdate, FunctionInvocationConfiguration, TextContent, ai_function
|
||||
|
||||
from agent_framework_ag_ui._agent import AgentConfig
|
||||
from agent_framework_ag_ui._orchestrators import DefaultOrchestrator, ExecutionContext
|
||||
@@ -36,9 +36,9 @@ class DummyAgent:
|
||||
thread: Any,
|
||||
tools: list[Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncGenerator[AgentRunResponseUpdate, None]:
|
||||
) -> AsyncGenerator[AgentResponseUpdate, None]:
|
||||
self.seen_tools = tools
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="ok")], role="assistant")
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="ok")], role="assistant")
|
||||
|
||||
|
||||
class RecordingAgent:
|
||||
@@ -59,9 +59,9 @@ class RecordingAgent:
|
||||
thread: Any,
|
||||
tools: list[Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncGenerator[AgentRunResponseUpdate, None]:
|
||||
) -> AsyncGenerator[AgentResponseUpdate, None]:
|
||||
self.seen_messages = messages
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="ok")], role="assistant")
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="ok")], role="assistant")
|
||||
|
||||
|
||||
async def test_default_orchestrator_merges_client_tools() -> None:
|
||||
|
||||
@@ -9,7 +9,7 @@ from types import SimpleNamespace
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponseUpdate,
|
||||
ChatMessage,
|
||||
TextContent,
|
||||
ai_function,
|
||||
@@ -55,7 +55,7 @@ async def test_human_in_the_loop_json_decode_error() -> None:
|
||||
|
||||
agent = StubAgent(
|
||||
default_options={"tools": [approval_tool], "response_format": None},
|
||||
updates=[AgentRunResponseUpdate(contents=[TextContent(text="response")], role="assistant")],
|
||||
updates=[AgentResponseUpdate(contents=[TextContent(text="response")], role="assistant")],
|
||||
)
|
||||
context = TestExecutionContext(
|
||||
input_data=input_data,
|
||||
@@ -451,7 +451,7 @@ async def test_structured_output_processing() -> None:
|
||||
agent = StubAgent(
|
||||
default_options=DEFAULT_OPTIONS,
|
||||
updates=[
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[TextContent(text='{"ingredients": ["tomato"], "message": "Added tomato"}')],
|
||||
role="assistant",
|
||||
)
|
||||
@@ -691,7 +691,7 @@ async def test_confirm_changes_closes_active_message_before_finish() -> None:
|
||||
from agent_framework import FunctionCallContent, FunctionResultContent
|
||||
|
||||
updates = [
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(
|
||||
name="write_document_local",
|
||||
@@ -700,7 +700,7 @@ async def test_confirm_changes_closes_active_message_before_finish() -> None:
|
||||
)
|
||||
]
|
||||
),
|
||||
AgentRunResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")]),
|
||||
AgentResponseUpdate(contents=[FunctionResultContent(call_id="call_1", result="Done")]),
|
||||
]
|
||||
|
||||
orchestrator = DefaultOrchestrator()
|
||||
@@ -792,9 +792,9 @@ async def test_agent_protocol_fallback_paths() -> None:
|
||||
thread: Any = None,
|
||||
tools: list[Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncGenerator[AgentRunResponseUpdate, None]:
|
||||
) -> AsyncGenerator[AgentResponseUpdate, None]:
|
||||
self.messages_received = messages
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="response")], role="assistant")
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="response")], role="assistant")
|
||||
|
||||
from agent_framework import ChatMessage, TextContent
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ from typing import Any, Generic
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseChatClient,
|
||||
ChatMessage,
|
||||
@@ -81,7 +81,7 @@ class StubAgent(AgentProtocol):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
updates: list[AgentRunResponseUpdate] | None = None,
|
||||
updates: list[AgentResponseUpdate] | None = None,
|
||||
*,
|
||||
agent_id: str = "stub-agent",
|
||||
agent_name: str | None = "stub-agent",
|
||||
@@ -91,7 +91,7 @@ class StubAgent(AgentProtocol):
|
||||
self.id = agent_id
|
||||
self.name = agent_name
|
||||
self.description = "stub agent"
|
||||
self.updates = updates or [AgentRunResponseUpdate(contents=[TextContent(text="response")], role="assistant")]
|
||||
self.updates = updates or [AgentResponseUpdate(contents=[TextContent(text="response")], role="assistant")]
|
||||
self.default_options: dict[str, Any] = (
|
||||
default_options if isinstance(default_options, dict) else {"tools": None, "response_format": None}
|
||||
)
|
||||
@@ -105,8 +105,8 @@ class StubAgent(AgentProtocol):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[], response_id="stub-response")
|
||||
) -> AgentResponse:
|
||||
return AgentResponse(messages=[], response_id="stub-response")
|
||||
|
||||
def run_stream(
|
||||
self,
|
||||
@@ -114,8 +114,8 @@ class StubAgent(AgentProtocol):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def _stream() -> AsyncIterator[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
async def _stream() -> AsyncIterator[AgentResponseUpdate]:
|
||||
self.messages_received = [] if messages is None else list(messages) # type: ignore[arg-type]
|
||||
self.tools_received = kwargs.get("tools")
|
||||
for update in self.updates:
|
||||
|
||||
@@ -8,8 +8,8 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
AIFunction,
|
||||
ChatAgent,
|
||||
@@ -1544,7 +1544,7 @@ async def test_azure_ai_chat_client_agent_basic_run() -> None:
|
||||
response = await agent.run("Hello! Please respond with 'Hello World' exactly.")
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
assert "Hello World" in response.text
|
||||
@@ -1561,7 +1561,7 @@ async def test_azure_ai_chat_client_agent_basic_run_streaming() -> None:
|
||||
full_message: str = ""
|
||||
async for chunk in agent.run_stream("Please respond with exactly: 'This is a streaming response test.'"):
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, AgentRunResponseUpdate)
|
||||
assert isinstance(chunk, AgentResponseUpdate)
|
||||
if chunk.text:
|
||||
full_message += chunk.text
|
||||
|
||||
@@ -1585,14 +1585,14 @@ async def test_azure_ai_chat_client_agent_thread_persistence() -> None:
|
||||
first_response = await agent.run(
|
||||
"Remember this number: 42. What number did I just tell you to remember?", thread=thread
|
||||
)
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert "42" in first_response.text
|
||||
|
||||
# Second message - test conversation memory
|
||||
second_response = await agent.run(
|
||||
"What number did I tell you to remember in my previous message?", thread=thread
|
||||
)
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert "42" in second_response.text
|
||||
|
||||
|
||||
@@ -1609,7 +1609,7 @@ async def test_azure_ai_chat_client_agent_existing_thread_id() -> None:
|
||||
first_response = await first_agent.run("My name is Alice. Remember this.", thread=thread)
|
||||
|
||||
# Validate first response
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
|
||||
# The thread ID is set after the first response
|
||||
@@ -1628,7 +1628,7 @@ async def test_azure_ai_chat_client_agent_existing_thread_id() -> None:
|
||||
response2 = await second_agent.run("What is my name?", thread=thread)
|
||||
|
||||
# Validate that the agent remembers the previous conversation
|
||||
assert isinstance(response2, AgentRunResponse)
|
||||
assert isinstance(response2, AgentResponse)
|
||||
assert response2.text is not None
|
||||
# Should reference Alice from the previous conversation
|
||||
assert "alice" in response2.text.lower()
|
||||
@@ -1648,7 +1648,7 @@ async def test_azure_ai_chat_client_agent_code_interpreter():
|
||||
response = await agent.run("Write Python code to calculate the factorial of 5 and show the result.")
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
# Factorial of 5 is 120
|
||||
assert "120" in response.text or "factorial" in response.text.lower()
|
||||
@@ -1683,7 +1683,7 @@ async def test_azure_ai_chat_client_agent_file_search():
|
||||
response = await agent.run("Who is the youngest employee in the files?")
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
# Should find information about Alice Johnson (age 24) being the youngest
|
||||
assert any(term in response.text.lower() for term in ["alice", "johnson", "24"])
|
||||
@@ -1723,7 +1723,7 @@ async def test_azure_ai_chat_client_agent_hosted_mcp_tool() -> None:
|
||||
options={"max_tokens": 200},
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
@@ -1748,7 +1748,7 @@ async def test_azure_ai_chat_client_agent_level_tool_persistence():
|
||||
# First run - agent-level tool should be available
|
||||
first_response = await agent.run("What's the weather like in Chicago?")
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
# Should use the agent-level weather tool
|
||||
assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "25"])
|
||||
@@ -1756,7 +1756,7 @@ async def test_azure_ai_chat_client_agent_level_tool_persistence():
|
||||
# Second run - agent-level tool should still be available (persistence test)
|
||||
second_response = await agent.run("What's the weather in Miami?")
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
# Should use the agent-level weather tool again
|
||||
assert any(term in second_response.text.lower() for term in ["miami", "sunny", "25"])
|
||||
@@ -1781,7 +1781,7 @@ async def test_azure_ai_chat_client_agent_chat_options_run_level() -> None:
|
||||
},
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
@@ -1805,7 +1805,7 @@ async def test_azure_ai_chat_client_agent_chat_options_agent_level() -> None:
|
||||
"Provide a brief, helpful response.",
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
ChatAgent,
|
||||
ChatClientProtocol,
|
||||
ChatMessage,
|
||||
@@ -1303,7 +1303,7 @@ async def test_integration_agent_existing_thread():
|
||||
thread = first_agent.get_new_thread()
|
||||
first_response = await first_agent.run("My hobby is photography. Remember this.", thread=thread, store=True)
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
|
||||
# Preserve the thread for reuse
|
||||
@@ -1321,6 +1321,6 @@ async def test_integration_agent_existing_thread():
|
||||
# Reuse the preserved thread
|
||||
second_response = await second_agent.run("What is my hobby?", thread=preserved_thread)
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
assert "photography" in second_response.text.lower()
|
||||
|
||||
@@ -9,7 +9,7 @@ invoked during durable entity execution.
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol
|
||||
|
||||
from agent_framework import AgentRunResponse, AgentRunResponseUpdate
|
||||
from agent_framework import AgentResponse, AgentResponseUpdate
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -27,14 +27,14 @@ class AgentResponseCallbackProtocol(Protocol):
|
||||
|
||||
async def on_streaming_response_update(
|
||||
self,
|
||||
update: AgentRunResponseUpdate,
|
||||
update: AgentResponseUpdate,
|
||||
context: AgentCallbackContext,
|
||||
) -> None:
|
||||
"""Handle a streaming response update emitted by the agent."""
|
||||
|
||||
async def on_agent_response(
|
||||
self,
|
||||
response: AgentRunResponse,
|
||||
response: AgentResponse,
|
||||
context: AgentCallbackContext,
|
||||
) -> None:
|
||||
"""Handle the final agent response."""
|
||||
|
||||
+3
-3
@@ -35,7 +35,7 @@ from enum import Enum
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
BaseContent,
|
||||
ChatMessage,
|
||||
DataContent,
|
||||
@@ -693,8 +693,8 @@ class DurableAgentStateResponse(DurableAgentStateEntry):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_run_response(correlation_id: str, response: AgentRunResponse) -> DurableAgentStateResponse:
|
||||
"""Creates a DurableAgentStateResponse from an AgentRunResponse."""
|
||||
def from_run_response(correlation_id: str, response: AgentResponse) -> DurableAgentStateResponse:
|
||||
"""Creates a DurableAgentStateResponse from an AgentResponse."""
|
||||
return DurableAgentStateResponse(
|
||||
correlation_id=correlation_id,
|
||||
created_at=_parse_created_at(response.created_at),
|
||||
|
||||
@@ -15,8 +15,8 @@ from typing import Any, cast
|
||||
import azure.durable_functions as df
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
ChatMessage,
|
||||
ErrorContent,
|
||||
Role,
|
||||
@@ -95,7 +95,7 @@ class AgentEntity:
|
||||
self,
|
||||
context: df.DurableEntityContext,
|
||||
request: RunRequest | dict[str, Any] | str,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""(Deprecated) Execute the agent with a message directly in the entity.
|
||||
|
||||
Args:
|
||||
@@ -103,7 +103,7 @@ class AgentEntity:
|
||||
request: RunRequest object, dict, or string message (for backward compatibility)
|
||||
|
||||
Returns:
|
||||
AgentRunResponse enriched with execution metadata.
|
||||
AgentResponse enriched with execution metadata.
|
||||
"""
|
||||
return await self.run(context, request)
|
||||
|
||||
@@ -111,7 +111,7 @@ class AgentEntity:
|
||||
self,
|
||||
context: df.DurableEntityContext,
|
||||
request: RunRequest | dict[str, Any] | str,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Execute the agent with a message directly in the entity.
|
||||
|
||||
Args:
|
||||
@@ -119,7 +119,7 @@ class AgentEntity:
|
||||
request: RunRequest object, dict, or string message (for backward compatibility)
|
||||
|
||||
Returns:
|
||||
AgentRunResponse enriched with execution metadata.
|
||||
AgentResponse enriched with execution metadata.
|
||||
"""
|
||||
if isinstance(request, str):
|
||||
run_request = RunRequest(message=request, role=Role.USER)
|
||||
@@ -159,7 +159,7 @@ class AgentEntity:
|
||||
if response_format:
|
||||
run_kwargs["options"]["response_format"] = response_format
|
||||
|
||||
agent_run_response: AgentRunResponse = await self._invoke_agent(
|
||||
agent_response: AgentResponse = await self._invoke_agent(
|
||||
run_kwargs=run_kwargs,
|
||||
correlation_id=correlation_id,
|
||||
thread_id=thread_id,
|
||||
@@ -168,11 +168,11 @@ class AgentEntity:
|
||||
|
||||
logger.debug(
|
||||
"[AgentEntity.run] Agent invocation completed - response type: %s",
|
||||
type(agent_run_response).__name__,
|
||||
type(agent_response).__name__,
|
||||
)
|
||||
|
||||
try:
|
||||
response_text = agent_run_response.text if agent_run_response.text else "No response"
|
||||
response_text = agent_response.text if agent_response.text else "No response"
|
||||
logger.debug(f"Response: {response_text[:100]}...")
|
||||
except Exception as extraction_error:
|
||||
logger.error(
|
||||
@@ -181,12 +181,12 @@ class AgentEntity:
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
state_response = DurableAgentStateResponse.from_run_response(correlation_id, agent_run_response)
|
||||
state_response = DurableAgentStateResponse.from_run_response(correlation_id, agent_response)
|
||||
self.state.data.conversation_history.append(state_response)
|
||||
|
||||
logger.debug("[AgentEntity.run] AgentRunResponse stored in conversation history")
|
||||
logger.debug("[AgentEntity.run] AgentResponse stored in conversation history")
|
||||
|
||||
return agent_run_response
|
||||
return agent_response
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception("[AgentEntity.run] Agent execution failed.")
|
||||
@@ -196,7 +196,7 @@ class AgentEntity:
|
||||
role=Role.ASSISTANT, contents=[ErrorContent(message=str(exc), error_code=type(exc).__name__)]
|
||||
)
|
||||
|
||||
error_response = AgentRunResponse(messages=[error_message])
|
||||
error_response = AgentResponse(messages=[error_message])
|
||||
|
||||
# Create and store error response in conversation history
|
||||
error_state_response = DurableAgentStateResponse.from_run_response(correlation_id, error_response)
|
||||
@@ -211,7 +211,7 @@ class AgentEntity:
|
||||
correlation_id: str,
|
||||
thread_id: str,
|
||||
request_message: str,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Execute the agent, preferring streaming when available."""
|
||||
callback_context: AgentCallbackContext | None = None
|
||||
if self.callback is not None:
|
||||
@@ -229,7 +229,7 @@ class AgentEntity:
|
||||
stream_candidate = await stream_candidate
|
||||
|
||||
return await self._consume_stream(
|
||||
stream=cast(AsyncIterable[AgentRunResponseUpdate], stream_candidate),
|
||||
stream=cast(AsyncIterable[AgentResponseUpdate], stream_candidate),
|
||||
callback_context=callback_context,
|
||||
)
|
||||
except TypeError as type_error:
|
||||
@@ -248,32 +248,32 @@ class AgentEntity:
|
||||
else:
|
||||
logger.debug("Agent does not expose run_stream; falling back to run().")
|
||||
|
||||
agent_run_response = await self._invoke_non_stream(run_kwargs)
|
||||
await self._notify_final_response(agent_run_response, callback_context)
|
||||
return agent_run_response
|
||||
agent_response = await self._invoke_non_stream(run_kwargs)
|
||||
await self._notify_final_response(agent_response, callback_context)
|
||||
return agent_response
|
||||
|
||||
async def _consume_stream(
|
||||
self,
|
||||
stream: AsyncIterable[AgentRunResponseUpdate],
|
||||
stream: AsyncIterable[AgentResponseUpdate],
|
||||
callback_context: AgentCallbackContext | None = None,
|
||||
) -> AgentRunResponse:
|
||||
"""Consume streaming responses and build the final AgentRunResponse."""
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
) -> AgentResponse:
|
||||
"""Consume streaming responses and build the final AgentResponse."""
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
|
||||
async for update in stream:
|
||||
updates.append(update)
|
||||
await self._notify_stream_update(update, callback_context)
|
||||
|
||||
if updates:
|
||||
response = AgentRunResponse.from_agent_run_response_updates(updates)
|
||||
response = AgentResponse.from_agent_run_response_updates(updates)
|
||||
else:
|
||||
logger.debug("[AgentEntity] No streaming updates received; creating empty response")
|
||||
response = AgentRunResponse(messages=[])
|
||||
response = AgentResponse(messages=[])
|
||||
|
||||
await self._notify_final_response(response, callback_context)
|
||||
return response
|
||||
|
||||
async def _invoke_non_stream(self, run_kwargs: dict[str, Any]) -> AgentRunResponse:
|
||||
async def _invoke_non_stream(self, run_kwargs: dict[str, Any]) -> AgentResponse:
|
||||
"""Invoke the agent without streaming support."""
|
||||
run_callable = getattr(self.agent, "run", None)
|
||||
if run_callable is None or not callable(run_callable):
|
||||
@@ -283,14 +283,14 @@ class AgentEntity:
|
||||
if inspect.isawaitable(result):
|
||||
result = await result
|
||||
|
||||
if not isinstance(result, AgentRunResponse):
|
||||
raise TypeError(f"Agent run() must return an AgentRunResponse instance; received {type(result).__name__}")
|
||||
if not isinstance(result, AgentResponse):
|
||||
raise TypeError(f"Agent run() must return an AgentResponse instance; received {type(result).__name__}")
|
||||
|
||||
return result
|
||||
|
||||
async def _notify_stream_update(
|
||||
self,
|
||||
update: AgentRunResponseUpdate,
|
||||
update: AgentResponseUpdate,
|
||||
context: AgentCallbackContext | None,
|
||||
) -> None:
|
||||
"""Invoke the streaming callback if one is registered."""
|
||||
@@ -310,7 +310,7 @@ class AgentEntity:
|
||||
|
||||
async def _notify_final_response(
|
||||
self,
|
||||
response: AgentRunResponse,
|
||||
response: AgentResponse,
|
||||
context: AgentCallbackContext | None,
|
||||
) -> None:
|
||||
"""Invoke the final response callback if one is registered."""
|
||||
|
||||
@@ -11,8 +11,8 @@ from typing import TYPE_CHECKING, Any, TypeAlias, cast
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatMessage,
|
||||
get_logger,
|
||||
@@ -46,10 +46,10 @@ else:
|
||||
|
||||
|
||||
class AgentTask(_TypedCompoundTask):
|
||||
"""A custom Task that wraps entity calls and provides typed AgentRunResponse results.
|
||||
"""A custom Task that wraps entity calls and provides typed AgentResponse results.
|
||||
|
||||
This task wraps the underlying entity call task and intercepts its completion
|
||||
to convert the raw result into a typed AgentRunResponse object.
|
||||
to convert the raw result into a typed AgentResponse object.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -77,7 +77,7 @@ class AgentTask(_TypedCompoundTask):
|
||||
self.id = entity_task.id
|
||||
|
||||
def try_set_value(self, child: TaskBase) -> None:
|
||||
"""Transition the AgentTask to a terminal state and set its value to `AgentRunResponse`.
|
||||
"""Transition the AgentTask to a terminal state and set its value to `AgentResponse`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -104,7 +104,7 @@ class AgentTask(_TypedCompoundTask):
|
||||
response,
|
||||
)
|
||||
|
||||
# Set the typed AgentRunResponse as this task's result
|
||||
# Set the typed AgentResponse as this task's result
|
||||
self.set_value(is_error=False, value=response)
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
@@ -118,18 +118,18 @@ class AgentTask(_TypedCompoundTask):
|
||||
self._first_error = child.result
|
||||
self.set_value(is_error=True, value=self._first_error)
|
||||
|
||||
def _load_agent_response(self, agent_response: AgentRunResponse | dict[str, Any] | None) -> AgentRunResponse:
|
||||
"""Convert raw payloads into AgentRunResponse instance."""
|
||||
def _load_agent_response(self, agent_response: AgentResponse | dict[str, Any] | None) -> AgentResponse:
|
||||
"""Convert raw payloads into AgentResponse instance."""
|
||||
if agent_response is None:
|
||||
raise ValueError("agent_response cannot be None")
|
||||
|
||||
logger.debug("[load_agent_response] Loading agent response of type: %s", type(agent_response))
|
||||
|
||||
if isinstance(agent_response, AgentRunResponse):
|
||||
if isinstance(agent_response, AgentResponse):
|
||||
return agent_response
|
||||
if isinstance(agent_response, dict):
|
||||
logger.debug("[load_agent_response] Converting dict payload using AgentRunResponse.from_dict")
|
||||
return AgentRunResponse.from_dict(agent_response)
|
||||
logger.debug("[load_agent_response] Converting dict payload using AgentResponse.from_dict")
|
||||
return AgentResponse.from_dict(agent_response)
|
||||
|
||||
raise TypeError(f"Unsupported type for agent_response: {type(agent_response)}")
|
||||
|
||||
@@ -137,14 +137,14 @@ class AgentTask(_TypedCompoundTask):
|
||||
self,
|
||||
response_format: type[BaseModel] | None,
|
||||
correlation_id: str,
|
||||
response: AgentRunResponse,
|
||||
response: AgentResponse,
|
||||
) -> None:
|
||||
"""Ensure the AgentRunResponse value is parsed into the expected response_format."""
|
||||
"""Ensure the AgentResponse value is parsed into the expected response_format."""
|
||||
if response_format is not None and not isinstance(response.value, response_format):
|
||||
response.try_parse_value(response_format)
|
||||
|
||||
logger.debug(
|
||||
"[DurableAIAgent] Loaded AgentRunResponse.value for correlation_id %s with type: %s",
|
||||
"[DurableAIAgent] Loaded AgentResponse.value for correlation_id %s with type: %s",
|
||||
correlation_id,
|
||||
type(response.value).__name__,
|
||||
)
|
||||
@@ -190,7 +190,7 @@ class DurableAIAgent(AgentProtocol):
|
||||
# We return an AgentTask here which is a TaskBase subclass.
|
||||
# This is an intentional deviation from AgentProtocol which defines run() as async.
|
||||
# The AgentTask can be yielded in Durable Functions orchestrations and will provide
|
||||
# a typed AgentRunResponse result.
|
||||
# a typed AgentResponse result.
|
||||
def run( # type: ignore[override]
|
||||
self,
|
||||
messages: str | ChatMessage | Sequence[str | ChatMessage] | None = None,
|
||||
@@ -203,7 +203,7 @@ class DurableAIAgent(AgentProtocol):
|
||||
|
||||
This method implements AgentProtocol and returns an AgentTask (subclass of TaskBase)
|
||||
that can be yielded in Durable Functions orchestrations. The task's result will be
|
||||
a typed AgentRunResponse.
|
||||
a typed AgentResponse.
|
||||
|
||||
Args:
|
||||
messages: The message(s) to send to the agent
|
||||
@@ -212,7 +212,7 @@ class DurableAIAgent(AgentProtocol):
|
||||
**kwargs: Additional arguments (enable_tool_calls)
|
||||
|
||||
Returns:
|
||||
An AgentTask that resolves to an AgentRunResponse when yielded
|
||||
An AgentTask that resolves to an AgentResponse when yielded
|
||||
|
||||
Example:
|
||||
@app.orchestration_trigger(context_name="context")
|
||||
@@ -220,7 +220,7 @@ class DurableAIAgent(AgentProtocol):
|
||||
agent = app.get_agent(context, "MyAgent")
|
||||
thread = agent.get_new_thread()
|
||||
response = yield agent.run("Hello", thread=thread)
|
||||
# response is typed as AgentRunResponse
|
||||
# response is typed as AgentResponse
|
||||
"""
|
||||
message_str = self._normalize_messages(messages)
|
||||
|
||||
@@ -266,7 +266,7 @@ class DurableAIAgent(AgentProtocol):
|
||||
# Call the entity to get the underlying task
|
||||
entity_task = self.context.call_entity(entity_id, "run", run_request.to_dict())
|
||||
|
||||
# Wrap it in an AgentTask that will convert the result to AgentRunResponse
|
||||
# Wrap it in an AgentTask that will convert the result to AgentResponse
|
||||
agent_task = AgentTask(
|
||||
entity_task=entity_task,
|
||||
response_format=response_format,
|
||||
@@ -286,7 +286,7 @@ class DurableAIAgent(AgentProtocol):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterator[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterator[AgentResponseUpdate]:
|
||||
"""Run the agent with streaming (not supported for durable agents).
|
||||
|
||||
Raises:
|
||||
|
||||
@@ -10,7 +10,7 @@ from unittest.mock import ANY, AsyncMock, Mock, patch
|
||||
import azure.durable_functions as df
|
||||
import azure.functions as func
|
||||
import pytest
|
||||
from agent_framework import AgentRunResponse, ChatMessage, ErrorContent
|
||||
from agent_framework import AgentResponse, ChatMessage, ErrorContent
|
||||
|
||||
from agent_framework_azurefunctions import AgentFunctionApp
|
||||
from agent_framework_azurefunctions._app import WAIT_FOR_RESPONSE_FIELD, WAIT_FOR_RESPONSE_HEADER
|
||||
@@ -332,7 +332,7 @@ class TestAgentEntityOperations:
|
||||
"""Test that entity can run agent operation."""
|
||||
mock_agent = Mock()
|
||||
mock_agent.run = AsyncMock(
|
||||
return_value=AgentRunResponse(messages=[ChatMessage(role="assistant", text="Test response")])
|
||||
return_value=AgentResponse(messages=[ChatMessage(role="assistant", text="Test response")])
|
||||
)
|
||||
|
||||
entity = AgentEntity(mock_agent)
|
||||
@@ -343,7 +343,7 @@ class TestAgentEntityOperations:
|
||||
{"message": "Test message", "thread_id": "test-conv-123", "correlationId": "corr-app-entity-1"},
|
||||
)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.text == "Test response"
|
||||
assert entity.state.message_count == 2
|
||||
|
||||
@@ -351,7 +351,7 @@ class TestAgentEntityOperations:
|
||||
"""Test that the entity stores conversation history."""
|
||||
mock_agent = Mock()
|
||||
mock_agent.run = AsyncMock(
|
||||
return_value=AgentRunResponse(messages=[ChatMessage(role="assistant", text="Response 1")])
|
||||
return_value=AgentResponse(messages=[ChatMessage(role="assistant", text="Response 1")])
|
||||
)
|
||||
|
||||
entity = AgentEntity(mock_agent)
|
||||
@@ -390,7 +390,7 @@ class TestAgentEntityOperations:
|
||||
"""Test that the entity increments the message count."""
|
||||
mock_agent = Mock()
|
||||
mock_agent.run = AsyncMock(
|
||||
return_value=AgentRunResponse(messages=[ChatMessage(role="assistant", text="Response")])
|
||||
return_value=AgentResponse(messages=[ChatMessage(role="assistant", text="Response")])
|
||||
)
|
||||
|
||||
entity = AgentEntity(mock_agent)
|
||||
@@ -437,7 +437,7 @@ class TestAgentEntityFactory:
|
||||
"""Test that the entity function handles the run operation."""
|
||||
mock_agent = Mock()
|
||||
mock_agent.run = AsyncMock(
|
||||
return_value=AgentRunResponse(messages=[ChatMessage(role="assistant", text="Response")])
|
||||
return_value=AgentResponse(messages=[ChatMessage(role="assistant", text="Response")])
|
||||
)
|
||||
|
||||
entity_function = create_agent_entity(mock_agent)
|
||||
@@ -465,7 +465,7 @@ class TestAgentEntityFactory:
|
||||
"""Test that the entity function handles the deprecated run_agent operation for backward compatibility."""
|
||||
mock_agent = Mock()
|
||||
mock_agent.run = AsyncMock(
|
||||
return_value=AgentRunResponse(messages=[ChatMessage(role="assistant", text="Response")])
|
||||
return_value=AgentResponse(messages=[ChatMessage(role="assistant", text="Response")])
|
||||
)
|
||||
|
||||
entity_function = create_agent_entity(mock_agent)
|
||||
@@ -619,7 +619,7 @@ class TestErrorHandling:
|
||||
mock_context, {"message": "Test message", "thread_id": "conv-1", "correlationId": "corr-app-error-1"}
|
||||
)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) == 1
|
||||
content = result.messages[0].contents[0]
|
||||
assert isinstance(content, ErrorContent)
|
||||
|
||||
@@ -12,7 +12,7 @@ from typing import Any, TypeVar
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from agent_framework import AgentRunResponse, AgentRunResponseUpdate, ChatMessage, ErrorContent, Role
|
||||
from agent_framework import AgentResponse, AgentResponseUpdate, ChatMessage, ErrorContent, Role
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agent_framework_azurefunctions._durable_agent_state import (
|
||||
@@ -37,12 +37,12 @@ def _role_value(chat_message: DurableAgentStateMessage) -> str:
|
||||
return str(role_value)
|
||||
|
||||
|
||||
def _agent_response(text: str | None) -> AgentRunResponse:
|
||||
"""Create an AgentRunResponse with a single assistant message."""
|
||||
def _agent_response(text: str | None) -> AgentResponse:
|
||||
"""Create an AgentResponse with a single assistant message."""
|
||||
message = (
|
||||
ChatMessage(role="assistant", text=text) if text is not None else ChatMessage(role="assistant", contents=[])
|
||||
)
|
||||
return AgentRunResponse(messages=[message])
|
||||
return AgentResponse(messages=[message])
|
||||
|
||||
|
||||
class RecordingCallback:
|
||||
@@ -54,12 +54,12 @@ class RecordingCallback:
|
||||
|
||||
async def on_streaming_response_update(
|
||||
self,
|
||||
update: AgentRunResponseUpdate,
|
||||
update: AgentResponseUpdate,
|
||||
context: Any,
|
||||
) -> None:
|
||||
await self.stream_mock(update, context)
|
||||
|
||||
async def on_agent_response(self, response: AgentRunResponse, context: Any) -> None:
|
||||
async def on_agent_response(self, response: AgentResponse, context: Any) -> None:
|
||||
await self.response_mock(response, context)
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ class TestAgentEntityRunAgent:
|
||||
assert getattr(sent_message.role, "value", sent_message.role) == "user"
|
||||
|
||||
# Verify result
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.text == "Test response"
|
||||
|
||||
async def test_run_agent_executes_agent(self) -> None:
|
||||
@@ -159,18 +159,18 @@ class TestAgentEntityRunAgent:
|
||||
assert getattr(sent_message.role, "value", sent_message.role) == "user"
|
||||
|
||||
# Verify result
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.text == "Test response"
|
||||
|
||||
async def test_run_agent_streaming_callbacks_invoked(self) -> None:
|
||||
"""Ensure streaming updates trigger callbacks and run() is not used."""
|
||||
|
||||
updates = [
|
||||
AgentRunResponseUpdate(text="Hello"),
|
||||
AgentRunResponseUpdate(text=" world"),
|
||||
AgentResponseUpdate(text="Hello"),
|
||||
AgentResponseUpdate(text=" world"),
|
||||
]
|
||||
|
||||
async def update_generator() -> AsyncIterator[AgentRunResponseUpdate]:
|
||||
async def update_generator() -> AsyncIterator[AgentResponseUpdate]:
|
||||
for update in updates:
|
||||
yield update
|
||||
|
||||
@@ -192,7 +192,7 @@ class TestAgentEntityRunAgent:
|
||||
},
|
||||
)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert "Hello" in result.text
|
||||
assert callback.stream_mock.await_count == len(updates)
|
||||
assert callback.response_mock.await_count == 1
|
||||
@@ -239,7 +239,7 @@ class TestAgentEntityRunAgent:
|
||||
},
|
||||
)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.text == "Final response"
|
||||
assert callback.stream_mock.await_count == 0
|
||||
assert callback.response_mock.await_count == 1
|
||||
@@ -605,7 +605,7 @@ class TestErrorHandling:
|
||||
mock_context, {"message": "Message", "thread_id": "conv-1", "correlationId": "corr-entity-error-1"}
|
||||
)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) == 1
|
||||
content = result.messages[0].contents[0]
|
||||
assert isinstance(content, ErrorContent)
|
||||
@@ -624,7 +624,7 @@ class TestErrorHandling:
|
||||
mock_context, {"message": "Message", "thread_id": "conv-1", "correlationId": "corr-entity-error-2"}
|
||||
)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) == 1
|
||||
content = result.messages[0].contents[0]
|
||||
assert isinstance(content, ErrorContent)
|
||||
@@ -643,7 +643,7 @@ class TestErrorHandling:
|
||||
mock_context, {"message": "Message", "thread_id": "conv-1", "correlationId": "corr-entity-error-3"}
|
||||
)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) == 1
|
||||
content = result.messages[0].contents[0]
|
||||
assert isinstance(content, ErrorContent)
|
||||
@@ -682,7 +682,7 @@ class TestErrorHandling:
|
||||
)
|
||||
|
||||
# Even on error, message info should be preserved
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) == 1
|
||||
content = result.messages[0].contents[0]
|
||||
assert isinstance(content, ErrorContent)
|
||||
@@ -793,7 +793,7 @@ class TestRunRequestSupport:
|
||||
|
||||
result = await entity.run(mock_context, request)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.text == "Response"
|
||||
|
||||
async def test_run_agent_with_dict_request(self) -> None:
|
||||
@@ -814,7 +814,7 @@ class TestRunRequestSupport:
|
||||
|
||||
result = await entity.run(mock_context, request_dict)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.text == "Response"
|
||||
|
||||
async def test_run_agent_with_string_raises_without_correlation(self) -> None:
|
||||
@@ -869,7 +869,7 @@ class TestRunRequestSupport:
|
||||
|
||||
result = await entity.run(mock_context, request)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.text == '{"answer": 42}'
|
||||
assert result.value is None
|
||||
|
||||
@@ -887,7 +887,7 @@ class TestRunRequestSupport:
|
||||
|
||||
result = await entity.run(mock_context, request)
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
# Agent should have been called (tool disabling is framework-dependent)
|
||||
mock_agent.run.assert_called_once()
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from agent_framework import AgentRunResponse, AgentThread, ChatMessage
|
||||
from agent_framework import AgentResponse, AgentThread, ChatMessage
|
||||
from azure.durable_functions.models.Task import TaskBase, TaskState
|
||||
|
||||
from agent_framework_azurefunctions import AgentFunctionApp, DurableAIAgent
|
||||
@@ -39,7 +39,7 @@ def _create_entity_task(task_id: int = 1) -> TaskBase:
|
||||
|
||||
|
||||
class TestAgentResponseHelpers:
|
||||
"""Tests for helper utilities that prepare AgentRunResponse values."""
|
||||
"""Tests for helper utilities that prepare AgentResponse values."""
|
||||
|
||||
@staticmethod
|
||||
def _create_agent_task() -> AgentTask:
|
||||
@@ -48,7 +48,7 @@ class TestAgentResponseHelpers:
|
||||
|
||||
def test_load_agent_response_from_instance(self) -> None:
|
||||
task = self._create_agent_task()
|
||||
response = AgentRunResponse(messages=[ChatMessage(role="assistant", text='{"foo": "bar"}')])
|
||||
response = AgentResponse(messages=[ChatMessage(role="assistant", text='{"foo": "bar"}')])
|
||||
|
||||
loaded = task._load_agent_response(response)
|
||||
|
||||
@@ -57,7 +57,7 @@ class TestAgentResponseHelpers:
|
||||
|
||||
def test_load_agent_response_from_serialized(self) -> None:
|
||||
task = self._create_agent_task()
|
||||
serialized = AgentRunResponse(messages=[ChatMessage(role="assistant", text="structured")]).to_dict()
|
||||
serialized = AgentResponse(messages=[ChatMessage(role="assistant", text="structured")]).to_dict()
|
||||
serialized["value"] = {"answer": 42}
|
||||
|
||||
loaded = task._load_agent_response(serialized)
|
||||
@@ -65,7 +65,7 @@ class TestAgentResponseHelpers:
|
||||
assert loaded is not None
|
||||
assert loaded.value == {"answer": 42}
|
||||
loaded_dict = loaded.to_dict()
|
||||
assert loaded_dict["type"] == "agent_run_response"
|
||||
assert loaded_dict["type"] == "agent_response"
|
||||
|
||||
def test_load_agent_response_rejects_none(self) -> None:
|
||||
task = self._create_agent_task()
|
||||
@@ -86,7 +86,7 @@ class TestAgentResponseHelpers:
|
||||
|
||||
# Simulate successful entity task completion
|
||||
entity_task.state = TaskState.SUCCEEDED
|
||||
entity_task.result = AgentRunResponse(messages=[ChatMessage(role="assistant", text="Test response")]).to_dict()
|
||||
entity_task.result = AgentResponse(messages=[ChatMessage(role="assistant", text="Test response")]).to_dict()
|
||||
|
||||
# Clear pending_tasks to simulate that parent has processed the child
|
||||
task.pending_tasks.clear()
|
||||
@@ -94,9 +94,9 @@ class TestAgentResponseHelpers:
|
||||
# Call try_set_value
|
||||
task.try_set_value(entity_task)
|
||||
|
||||
# Verify task completed successfully with AgentRunResponse
|
||||
# Verify task completed successfully with AgentResponse
|
||||
assert task.state == TaskState.SUCCEEDED
|
||||
assert isinstance(task.result, AgentRunResponse)
|
||||
assert isinstance(task.result, AgentResponse)
|
||||
assert task.result.text == "Test response"
|
||||
|
||||
def test_try_set_value_failure(self) -> None:
|
||||
@@ -128,9 +128,7 @@ class TestAgentResponseHelpers:
|
||||
|
||||
# Simulate successful entity task with JSON response
|
||||
entity_task.state = TaskState.SUCCEEDED
|
||||
entity_task.result = AgentRunResponse(
|
||||
messages=[ChatMessage(role="assistant", text='{"answer": "42"}')]
|
||||
).to_dict()
|
||||
entity_task.result = AgentResponse(messages=[ChatMessage(role="assistant", text='{"answer": "42"}')]).to_dict()
|
||||
|
||||
# Clear pending_tasks to simulate that parent has processed the child
|
||||
task.pending_tasks.clear()
|
||||
@@ -140,7 +138,7 @@ class TestAgentResponseHelpers:
|
||||
|
||||
# Verify task completed and value was parsed
|
||||
assert task.state == TaskState.SUCCEEDED
|
||||
assert isinstance(task.result, AgentRunResponse)
|
||||
assert isinstance(task.result, AgentResponse)
|
||||
assert isinstance(task.result.value, TestSchema)
|
||||
assert task.result.value.answer == "42"
|
||||
|
||||
@@ -152,7 +150,7 @@ class TestAgentResponseHelpers:
|
||||
name: str
|
||||
|
||||
task = self._create_agent_task()
|
||||
response = AgentRunResponse(messages=[ChatMessage(role="assistant", text='{"name": "test"}')])
|
||||
response = AgentResponse(messages=[ChatMessage(role="assistant", text='{"name": "test"}')])
|
||||
|
||||
# Value should be None initially
|
||||
assert response.value is None
|
||||
@@ -173,7 +171,7 @@ class TestAgentResponseHelpers:
|
||||
|
||||
task = self._create_agent_task()
|
||||
existing_value = SampleSchema(name="existing")
|
||||
response = AgentRunResponse(
|
||||
response = AgentResponse(
|
||||
messages=[ChatMessage(role="assistant", text='{"name": "new"}')],
|
||||
value=existing_value,
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import logging
|
||||
from collections.abc import Sequence
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
ChatAgent,
|
||||
FunctionCallContent,
|
||||
FunctionResultContent,
|
||||
@@ -39,7 +39,7 @@ async def main() -> None:
|
||||
_log_response(response)
|
||||
|
||||
|
||||
def _log_response(response: AgentRunResponse) -> None:
|
||||
def _log_response(response: AgentResponse) -> None:
|
||||
logging.info("\nConversation transcript:")
|
||||
for idx, message in enumerate(response.messages, start=1):
|
||||
tag = f"{idx}. {message.role.value if isinstance(message.role, Role) else message.role}"
|
||||
|
||||
@@ -4,7 +4,7 @@ This package provides an integration layer between Microsoft Agent Framework
|
||||
and [OpenAI ChatKit (Python)](https://github.com/openai/chatkit-python/).
|
||||
Specifically, it mirrors the [Agent SDK integration](https://github.com/openai/chatkit-python/blob/main/docs/server.md#agents-sdk-integration), and provides the following helpers:
|
||||
|
||||
- `stream_agent_response`: A helper to convert a streamed `AgentRunResponseUpdate`
|
||||
- `stream_agent_response`: A helper to convert a streamed `AgentResponseUpdate`
|
||||
from a Microsoft Agent Framework agent that implements `AgentProtocol` to ChatKit events.
|
||||
- `ThreadItemConverter`: A extendable helper class to convert ChatKit thread items to
|
||||
`ChatMessage` objects that can be consumed by an Agent Framework agent.
|
||||
|
||||
@@ -6,7 +6,7 @@ import uuid
|
||||
from collections.abc import AsyncIterable, AsyncIterator, Callable
|
||||
from datetime import datetime
|
||||
|
||||
from agent_framework import AgentRunResponseUpdate, TextContent
|
||||
from agent_framework import AgentResponseUpdate, TextContent
|
||||
from chatkit.types import (
|
||||
AssistantMessageContent,
|
||||
AssistantMessageContentPartTextDelta,
|
||||
@@ -19,13 +19,13 @@ from chatkit.types import (
|
||||
|
||||
|
||||
async def stream_agent_response(
|
||||
response_stream: AsyncIterable[AgentRunResponseUpdate],
|
||||
response_stream: AsyncIterable[AgentResponseUpdate],
|
||||
thread_id: str,
|
||||
generate_id: Callable[[str], str] | None = None,
|
||||
) -> AsyncIterator[ThreadStreamEvent]:
|
||||
"""Convert a streamed AgentRunResponseUpdate from Agent Framework to ChatKit events.
|
||||
"""Convert a streamed AgentResponseUpdate from Agent Framework to ChatKit events.
|
||||
|
||||
This helper function takes a stream of AgentRunResponseUpdate objects from
|
||||
This helper function takes a stream of AgentResponseUpdate objects from
|
||||
a Microsoft Agent Framework agent and converts them to ChatKit ThreadStreamEvent
|
||||
objects that can be consumed by the ChatKit UI.
|
||||
|
||||
@@ -34,7 +34,7 @@ async def stream_agent_response(
|
||||
text chunk as it arrives from the agent.
|
||||
|
||||
Args:
|
||||
response_stream: An async iterable of AgentRunResponseUpdate objects
|
||||
response_stream: An async iterable of AgentResponseUpdate objects
|
||||
from an Agent Framework agent.
|
||||
thread_id: The ChatKit thread ID for the conversation.
|
||||
generate_id: Optional function to generate IDs for ChatKit items.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
from agent_framework import AgentRunResponseUpdate, Role, TextContent
|
||||
from agent_framework import AgentResponseUpdate, Role, TextContent
|
||||
from chatkit.types import (
|
||||
ThreadItemAddedEvent,
|
||||
ThreadItemDoneEvent,
|
||||
@@ -34,7 +34,7 @@ class TestStreamAgentResponse:
|
||||
"""Test streaming single text update."""
|
||||
|
||||
async def single_update_stream():
|
||||
yield AgentRunResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="Hello world")])
|
||||
yield AgentResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="Hello world")])
|
||||
|
||||
events = []
|
||||
async for event in stream_agent_response(single_update_stream(), thread_id="test_thread"):
|
||||
@@ -59,8 +59,8 @@ class TestStreamAgentResponse:
|
||||
"""Test streaming multiple text updates."""
|
||||
|
||||
async def multiple_updates_stream():
|
||||
yield AgentRunResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="Hello ")])
|
||||
yield AgentRunResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="world!")])
|
||||
yield AgentResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="Hello ")])
|
||||
yield AgentResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="world!")])
|
||||
|
||||
events = []
|
||||
async for event in stream_agent_response(multiple_updates_stream(), thread_id="test_thread"):
|
||||
@@ -91,7 +91,7 @@ class TestStreamAgentResponse:
|
||||
return f"custom_{item_type}_123"
|
||||
|
||||
async def single_update_stream():
|
||||
yield AgentRunResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="Test")])
|
||||
yield AgentResponseUpdate(role=Role.ASSISTANT, contents=[TextContent(text="Test")])
|
||||
|
||||
events = []
|
||||
async for event in stream_agent_response(
|
||||
@@ -107,8 +107,8 @@ class TestStreamAgentResponse:
|
||||
"""Test streaming updates with empty content."""
|
||||
|
||||
async def empty_content_stream():
|
||||
yield AgentRunResponseUpdate(role=Role.ASSISTANT, contents=[])
|
||||
yield AgentRunResponseUpdate(role=Role.ASSISTANT, contents=None)
|
||||
yield AgentResponseUpdate(role=Role.ASSISTANT, contents=[])
|
||||
yield AgentResponseUpdate(role=Role.ASSISTANT, contents=None)
|
||||
|
||||
events = []
|
||||
async for event in stream_agent_response(empty_content_stream(), thread_id="test_thread"):
|
||||
@@ -130,7 +130,7 @@ class TestStreamAgentResponse:
|
||||
del non_text_content.text
|
||||
|
||||
async def non_text_stream():
|
||||
yield AgentRunResponseUpdate(role=Role.ASSISTANT, contents=[non_text_content])
|
||||
yield AgentResponseUpdate(role=Role.ASSISTANT, contents=[non_text_content])
|
||||
|
||||
events = []
|
||||
async for event in stream_agent_response(non_text_stream(), thread_id="test_thread"):
|
||||
|
||||
@@ -5,8 +5,8 @@ from typing import Any, ClassVar
|
||||
|
||||
from agent_framework import (
|
||||
AgentMiddlewareTypes,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
ChatMessage,
|
||||
@@ -210,15 +210,15 @@ class CopilotStudioAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Get a response from the agent.
|
||||
|
||||
This method returns the final result of the agent's execution
|
||||
as a single AgentRunResponse object. The caller is blocked until
|
||||
as a single AgentResponse object. The caller is blocked until
|
||||
the final result is available.
|
||||
|
||||
Note: For streaming responses, use the run_stream method, which returns
|
||||
intermediate steps and the final result as a stream of AgentRunResponseUpdate
|
||||
intermediate steps and the final result as a stream of AgentResponseUpdate
|
||||
objects. Streaming only the final result is not feasible because the timing of
|
||||
the final result's availability is unknown, and blocking the caller until then
|
||||
is undesirable in streaming scenarios.
|
||||
@@ -248,7 +248,7 @@ class CopilotStudioAgent(BaseAgent):
|
||||
response_messages = [message async for message in self._process_activities(activities, streaming=False)]
|
||||
response_id = response_messages[0].message_id if response_messages else None
|
||||
|
||||
return AgentRunResponse(messages=response_messages, response_id=response_id)
|
||||
return AgentResponse(messages=response_messages, response_id=response_id)
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
@@ -256,13 +256,13 @@ class CopilotStudioAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Run the agent as a stream.
|
||||
|
||||
This method will return the intermediate steps and final results of the
|
||||
agent's execution as a stream of AgentRunResponseUpdate objects to the caller.
|
||||
agent's execution as a stream of AgentResponseUpdate objects to the caller.
|
||||
|
||||
Note: An AgentRunResponseUpdate object contains a chunk of a message.
|
||||
Note: An AgentResponseUpdate object contains a chunk of a message.
|
||||
|
||||
Args:
|
||||
messages: The message(s) to send to the agent.
|
||||
@@ -285,7 +285,7 @@ class CopilotStudioAgent(BaseAgent):
|
||||
activities = self.client.ask_question(question, thread.service_thread_id)
|
||||
|
||||
async for message in self._process_activities(activities, streaming=True):
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
role=message.role,
|
||||
contents=message.contents,
|
||||
author_name=message.author_name,
|
||||
|
||||
@@ -5,8 +5,8 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatMessage,
|
||||
Role,
|
||||
@@ -133,7 +133,7 @@ class TestCopilotStudioAgent:
|
||||
|
||||
response = await agent.run("test message")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 1
|
||||
content = response.messages[0].contents[0]
|
||||
assert isinstance(content, TextContent)
|
||||
@@ -153,7 +153,7 @@ class TestCopilotStudioAgent:
|
||||
chat_message = ChatMessage(role=Role.USER, contents=[TextContent("test message")])
|
||||
response = await agent.run(chat_message)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 1
|
||||
content = response.messages[0].contents[0]
|
||||
assert isinstance(content, TextContent)
|
||||
@@ -173,7 +173,7 @@ class TestCopilotStudioAgent:
|
||||
|
||||
response = await agent.run("test message", thread=thread)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 1
|
||||
assert thread.service_thread_id == "test-conversation-id"
|
||||
|
||||
@@ -204,7 +204,7 @@ class TestCopilotStudioAgent:
|
||||
|
||||
response_count = 0
|
||||
async for response in agent.run_stream("test message"):
|
||||
assert isinstance(response, AgentRunResponseUpdate)
|
||||
assert isinstance(response, AgentResponseUpdate)
|
||||
content = response.contents[0]
|
||||
assert isinstance(content, TextContent)
|
||||
assert content.text == "Streaming response"
|
||||
@@ -231,7 +231,7 @@ class TestCopilotStudioAgent:
|
||||
|
||||
response_count = 0
|
||||
async for response in agent.run_stream("test message", thread=thread):
|
||||
assert isinstance(response, AgentRunResponseUpdate)
|
||||
assert isinstance(response, AgentResponseUpdate)
|
||||
content = response.contents[0]
|
||||
assert isinstance(content, TextContent)
|
||||
assert content.text == "Streaming response"
|
||||
@@ -285,7 +285,7 @@ class TestCopilotStudioAgent:
|
||||
|
||||
response = await agent.run("test message")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 2
|
||||
|
||||
async def test_run_list_of_messages(self, mock_copilot_client: MagicMock, mock_activity: MagicMock) -> None:
|
||||
@@ -301,7 +301,7 @@ class TestCopilotStudioAgent:
|
||||
messages = ["Hello", "How are you?"]
|
||||
response = await agent.run(messages)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert len(response.messages) == 1
|
||||
|
||||
async def test_run_stream_start_conversation_failure(self, mock_copilot_client: MagicMock) -> None:
|
||||
|
||||
@@ -33,8 +33,8 @@ from ._serialization import SerializationMixin
|
||||
from ._threads import AgentThread, ChatMessageStoreProtocol
|
||||
from ._tools import FUNCTION_INVOKING_CHAT_CLIENT_MARKER, AIFunction, ToolProtocol
|
||||
from ._types import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
@@ -177,16 +177,16 @@ class AgentProtocol(Protocol):
|
||||
|
||||
async def run(self, messages=None, *, thread=None, **kwargs):
|
||||
# Your custom implementation
|
||||
from agent_framework import AgentRunResponse
|
||||
from agent_framework import AgentResponse
|
||||
|
||||
return AgentRunResponse(messages=[], response_id="custom-response")
|
||||
return AgentResponse(messages=[], response_id="custom-response")
|
||||
|
||||
def run_stream(self, messages=None, *, thread=None, **kwargs):
|
||||
# Your custom streaming implementation
|
||||
async def _stream():
|
||||
from agent_framework import AgentRunResponseUpdate
|
||||
from agent_framework import AgentResponseUpdate
|
||||
|
||||
yield AgentRunResponseUpdate()
|
||||
yield AgentResponseUpdate()
|
||||
|
||||
return _stream()
|
||||
|
||||
@@ -210,15 +210,15 @@ class AgentProtocol(Protocol):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Get a response from the agent.
|
||||
|
||||
This method returns the final result of the agent's execution
|
||||
as a single AgentRunResponse object. The caller is blocked until
|
||||
as a single AgentResponse object. The caller is blocked until
|
||||
the final result is available.
|
||||
|
||||
Note: For streaming responses, use the run_stream method, which returns
|
||||
intermediate steps and the final result as a stream of AgentRunResponseUpdate
|
||||
intermediate steps and the final result as a stream of AgentResponseUpdate
|
||||
objects. Streaming only the final result is not feasible because the timing of
|
||||
the final result's availability is unknown, and blocking the caller until then
|
||||
is undesirable in streaming scenarios.
|
||||
@@ -241,13 +241,13 @@ class AgentProtocol(Protocol):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Run the agent as a stream.
|
||||
|
||||
This method will return the intermediate steps and final results of the
|
||||
agent's execution as a stream of AgentRunResponseUpdate objects to the caller.
|
||||
agent's execution as a stream of AgentResponseUpdate objects to the caller.
|
||||
|
||||
Note: An AgentRunResponseUpdate object contains a chunk of a message.
|
||||
Note: An AgentResponseUpdate object contains a chunk of a message.
|
||||
|
||||
Args:
|
||||
messages: The message(s) to send to the agent.
|
||||
@@ -283,19 +283,19 @@ class BaseAgent(SerializationMixin):
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
from agent_framework import BaseAgent, AgentThread, AgentRunResponse
|
||||
from agent_framework import BaseAgent, AgentThread, AgentResponse
|
||||
|
||||
|
||||
# Create a concrete subclass that implements the protocol
|
||||
class SimpleAgent(BaseAgent):
|
||||
async def run(self, messages=None, *, thread=None, **kwargs):
|
||||
# Custom implementation
|
||||
return AgentRunResponse(messages=[], response_id="simple-response")
|
||||
return AgentResponse(messages=[], response_id="simple-response")
|
||||
|
||||
def run_stream(self, messages=None, *, thread=None, **kwargs):
|
||||
async def _stream():
|
||||
# Custom streaming implementation
|
||||
yield AgentRunResponseUpdate()
|
||||
yield AgentResponseUpdate()
|
||||
|
||||
return _stream()
|
||||
|
||||
@@ -412,8 +412,8 @@ class BaseAgent(SerializationMixin):
|
||||
description: str | None = None,
|
||||
arg_name: str = "task",
|
||||
arg_description: str | None = None,
|
||||
stream_callback: Callable[[AgentRunResponseUpdate], None]
|
||||
| Callable[[AgentRunResponseUpdate], Awaitable[None]]
|
||||
stream_callback: Callable[[AgentResponseUpdate], None]
|
||||
| Callable[[AgentResponseUpdate], Awaitable[None]]
|
||||
| None = None,
|
||||
) -> AIFunction[BaseModel, str]:
|
||||
"""Create an AIFunction tool that wraps this agent.
|
||||
@@ -478,7 +478,7 @@ class BaseAgent(SerializationMixin):
|
||||
return (await self.run(input_text, **forwarded_kwargs)).text
|
||||
|
||||
# Use streaming mode - accumulate updates and create final response
|
||||
response_updates: list[AgentRunResponseUpdate] = []
|
||||
response_updates: list[AgentResponseUpdate] = []
|
||||
async for update in self.run_stream(input_text, **forwarded_kwargs):
|
||||
response_updates.append(update)
|
||||
if is_async_callback:
|
||||
@@ -487,7 +487,7 @@ class BaseAgent(SerializationMixin):
|
||||
stream_callback(update)
|
||||
|
||||
# Create final text from accumulated updates
|
||||
return AgentRunResponse.from_agent_run_response_updates(response_updates).text
|
||||
return AgentResponse.from_agent_run_response_updates(response_updates).text
|
||||
|
||||
agent_tool: AIFunction[BaseModel, str] = AIFunction(
|
||||
name=tool_name,
|
||||
@@ -766,7 +766,7 @@ class ChatAgent(BaseAgent, Generic[TOptions_co]): # type: ignore[misc]
|
||||
| None = None,
|
||||
options: TOptions_co | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Run the agent with the given messages and options.
|
||||
|
||||
Note:
|
||||
@@ -789,7 +789,7 @@ class ChatAgent(BaseAgent, Generic[TOptions_co]): # type: ignore[misc]
|
||||
Will only be passed to functions that are called.
|
||||
|
||||
Returns:
|
||||
An AgentRunResponse containing the agent's response.
|
||||
An AgentResponse containing the agent's response.
|
||||
"""
|
||||
# Build options dict from provided options
|
||||
opts = dict(options) if options else {}
|
||||
@@ -872,7 +872,7 @@ class ChatAgent(BaseAgent, Generic[TOptions_co]): # type: ignore[misc]
|
||||
response.messages,
|
||||
**{k: v for k, v in kwargs.items() if k != "thread"},
|
||||
)
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=response.messages,
|
||||
response_id=response.response_id,
|
||||
created_at=response.created_at,
|
||||
@@ -894,7 +894,7 @@ class ChatAgent(BaseAgent, Generic[TOptions_co]): # type: ignore[misc]
|
||||
| None = None,
|
||||
options: TOptions_co | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Stream the agent with the given messages and options.
|
||||
|
||||
Note:
|
||||
@@ -917,7 +917,7 @@ class ChatAgent(BaseAgent, Generic[TOptions_co]): # type: ignore[misc]
|
||||
Will only be passed to functions that are called.
|
||||
|
||||
Yields:
|
||||
AgentRunResponseUpdate objects containing chunks of the agent's response.
|
||||
AgentResponseUpdate objects containing chunks of the agent's response.
|
||||
"""
|
||||
# Build options dict from provided options
|
||||
opts = dict(options) if options else {}
|
||||
@@ -989,7 +989,7 @@ class ChatAgent(BaseAgent, Generic[TOptions_co]): # type: ignore[misc]
|
||||
if update.author_name is None:
|
||||
update.author_name = agent_name
|
||||
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=update.contents,
|
||||
role=update.role,
|
||||
author_name=update.author_name,
|
||||
|
||||
@@ -8,7 +8,7 @@ from functools import update_wrapper
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeAlias, TypedDict, TypeVar
|
||||
|
||||
from ._serialization import SerializationMixin
|
||||
from ._types import AgentRunResponse, AgentRunResponseUpdate, ChatMessage, prepare_messages
|
||||
from ._types import AgentResponse, AgentResponseUpdate, ChatMessage, prepare_messages
|
||||
from .exceptions import MiddlewareException
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -67,8 +67,8 @@ class AgentRunContext(SerializationMixin):
|
||||
metadata: Metadata dictionary for sharing data between agent middleware.
|
||||
result: Agent execution result. Can be observed after calling ``next()``
|
||||
to see the actual execution result or can be set to override the execution result.
|
||||
For non-streaming: should be AgentRunResponse.
|
||||
For streaming: should be AsyncIterable[AgentRunResponseUpdate].
|
||||
For non-streaming: should be AgentResponse.
|
||||
For streaming: should be AsyncIterable[AgentResponseUpdate].
|
||||
terminate: A flag indicating whether to terminate execution after current middleware.
|
||||
When set to True, execution will stop as soon as control returns to framework.
|
||||
kwargs: Additional keyword arguments passed to the agent run method.
|
||||
@@ -105,7 +105,7 @@ class AgentRunContext(SerializationMixin):
|
||||
thread: "AgentThread | None" = None,
|
||||
is_streaming: bool = False,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
result: AgentRunResponse | AsyncIterable[AgentRunResponseUpdate] | None = None,
|
||||
result: AgentResponse | AsyncIterable[AgentResponseUpdate] | None = None,
|
||||
terminate: bool = False,
|
||||
kwargs: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
@@ -321,8 +321,8 @@ class AgentMiddleware(ABC):
|
||||
Use context.is_streaming to determine if this is a streaming call.
|
||||
Middleware can set context.result to override execution, or observe
|
||||
the actual execution result after calling next().
|
||||
For non-streaming: AgentRunResponse
|
||||
For streaming: AsyncIterable[AgentRunResponseUpdate]
|
||||
For non-streaming: AgentResponse
|
||||
For streaming: AsyncIterable[AgentResponseUpdate]
|
||||
next: Function to call the next middleware or final agent execution.
|
||||
Does not return anything - all data flows through the context.
|
||||
|
||||
@@ -773,8 +773,8 @@ class AgentMiddlewarePipeline(BaseMiddlewarePipeline):
|
||||
agent: "AgentProtocol",
|
||||
messages: list[ChatMessage],
|
||||
context: AgentRunContext,
|
||||
final_handler: Callable[[AgentRunContext], Awaitable[AgentRunResponse]],
|
||||
) -> AgentRunResponse | None:
|
||||
final_handler: Callable[[AgentRunContext], Awaitable[AgentResponse]],
|
||||
) -> AgentResponse | None:
|
||||
"""Execute the agent middleware pipeline for non-streaming.
|
||||
|
||||
Args:
|
||||
@@ -795,15 +795,15 @@ class AgentMiddlewarePipeline(BaseMiddlewarePipeline):
|
||||
return await final_handler(context)
|
||||
|
||||
# Store the final result
|
||||
result_container: dict[str, AgentRunResponse | None] = {"result": None}
|
||||
result_container: dict[str, AgentResponse | None] = {"result": None}
|
||||
|
||||
# Custom final handler that handles termination and result override
|
||||
async def agent_final_handler(c: AgentRunContext) -> AgentRunResponse:
|
||||
async def agent_final_handler(c: AgentRunContext) -> AgentResponse:
|
||||
# If terminate was set, return the result (which might be None)
|
||||
if c.terminate:
|
||||
if c.result is not None and isinstance(c.result, AgentRunResponse):
|
||||
if c.result is not None and isinstance(c.result, AgentResponse):
|
||||
return c.result
|
||||
return AgentRunResponse()
|
||||
return AgentResponse()
|
||||
# Execute actual handler and populate context for observability
|
||||
return await final_handler(c)
|
||||
|
||||
@@ -811,13 +811,13 @@ class AgentMiddlewarePipeline(BaseMiddlewarePipeline):
|
||||
await first_handler(context)
|
||||
|
||||
# Return the result from result container or overridden result
|
||||
if context.result is not None and isinstance(context.result, AgentRunResponse):
|
||||
if context.result is not None and isinstance(context.result, AgentResponse):
|
||||
return context.result
|
||||
|
||||
# If no result was set (next() not called), return empty AgentRunResponse
|
||||
# If no result was set (next() not called), return empty AgentResponse
|
||||
response = result_container.get("result")
|
||||
if response is None:
|
||||
return AgentRunResponse()
|
||||
return AgentResponse()
|
||||
return response
|
||||
|
||||
async def execute_stream(
|
||||
@@ -825,8 +825,8 @@ class AgentMiddlewarePipeline(BaseMiddlewarePipeline):
|
||||
agent: "AgentProtocol",
|
||||
messages: list[ChatMessage],
|
||||
context: AgentRunContext,
|
||||
final_handler: Callable[[AgentRunContext], AsyncIterable[AgentRunResponseUpdate]],
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
final_handler: Callable[[AgentRunContext], AsyncIterable[AgentResponseUpdate]],
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Execute the agent middleware pipeline for streaming.
|
||||
|
||||
Args:
|
||||
@@ -849,7 +849,7 @@ class AgentMiddlewarePipeline(BaseMiddlewarePipeline):
|
||||
return
|
||||
|
||||
# Store the final result
|
||||
result_container: dict[str, AsyncIterable[AgentRunResponseUpdate] | None] = {"result_stream": None}
|
||||
result_container: dict[str, AsyncIterable[AgentResponseUpdate] | None] = {"result_stream": None}
|
||||
|
||||
first_handler = self._create_streaming_handler_chain(final_handler, result_container, "result_stream")
|
||||
await first_handler(context)
|
||||
@@ -1210,7 +1210,7 @@ def use_agent_middleware(agent_class: type[TAgent]) -> type[TAgent]:
|
||||
thread: Any = None,
|
||||
middleware: Sequence[Middleware] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Middleware-enabled run method."""
|
||||
# Build fresh middleware pipelines from current middleware collection and run-level middleware
|
||||
agent_middleware = getattr(self, "middleware", None)
|
||||
@@ -1237,7 +1237,7 @@ def use_agent_middleware(agent_class: type[TAgent]) -> type[TAgent]:
|
||||
kwargs=kwargs,
|
||||
)
|
||||
|
||||
async def _execute_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def _execute_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
return await original_run(self, ctx.messages, thread=thread, **ctx.kwargs) # type: ignore
|
||||
|
||||
result = await agent_pipeline.execute(
|
||||
@@ -1247,7 +1247,7 @@ def use_agent_middleware(agent_class: type[TAgent]) -> type[TAgent]:
|
||||
_execute_handler,
|
||||
)
|
||||
|
||||
return result if result else AgentRunResponse()
|
||||
return result if result else AgentResponse()
|
||||
|
||||
# No middleware, execute directly
|
||||
return await original_run(self, normalized_messages, thread=thread, **kwargs) # type: ignore[return-value]
|
||||
@@ -1259,7 +1259,7 @@ def use_agent_middleware(agent_class: type[TAgent]) -> type[TAgent]:
|
||||
thread: Any = None,
|
||||
middleware: Sequence[Middleware] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Middleware-enabled run_stream method."""
|
||||
# Build fresh middleware pipelines from current middleware collection and run-level middleware
|
||||
agent_middleware = getattr(self, "middleware", None)
|
||||
@@ -1285,11 +1285,11 @@ def use_agent_middleware(agent_class: type[TAgent]) -> type[TAgent]:
|
||||
kwargs=kwargs,
|
||||
)
|
||||
|
||||
async def _execute_stream_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def _execute_stream_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
async for update in original_run_stream(self, ctx.messages, thread=thread, **ctx.kwargs): # type: ignore[misc]
|
||||
yield update
|
||||
|
||||
async def _stream_generator() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def _stream_generator() -> AsyncIterable[AgentResponseUpdate]:
|
||||
async for update in agent_pipeline.execute_stream(
|
||||
self, # type: ignore[arg-type]
|
||||
normalized_messages,
|
||||
|
||||
@@ -29,8 +29,8 @@ else:
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AgentRunResponse",
|
||||
"AgentRunResponseUpdate",
|
||||
"AgentResponse",
|
||||
"AgentResponseUpdate",
|
||||
"AnnotatedRegions",
|
||||
"Annotations",
|
||||
"BaseAnnotation",
|
||||
@@ -188,7 +188,7 @@ _T = TypeVar("_T")
|
||||
TEmbedding = TypeVar("TEmbedding")
|
||||
TChatResponse = TypeVar("TChatResponse", bound="ChatResponse")
|
||||
TToolMode = TypeVar("TToolMode", bound="ToolMode")
|
||||
TAgentRunResponse = TypeVar("TAgentRunResponse", bound="AgentRunResponse")
|
||||
TAgentRunResponse = TypeVar("TAgentRunResponse", bound="AgentResponse")
|
||||
|
||||
CreatedAtT = str # Use a datetimeoffset type? Or a more specific type like datetime.datetime?
|
||||
|
||||
@@ -2541,7 +2541,7 @@ def prepend_instructions_to_messages(
|
||||
|
||||
|
||||
def _process_update(
|
||||
response: "ChatResponse | AgentRunResponse", update: "ChatResponseUpdate | AgentRunResponseUpdate"
|
||||
response: "ChatResponse | AgentResponse", update: "ChatResponseUpdate | AgentResponseUpdate"
|
||||
) -> None:
|
||||
"""Processes a single update and modifies the response in place."""
|
||||
is_new_message = False
|
||||
@@ -2645,7 +2645,7 @@ def _coalesce_text_content(
|
||||
contents.extend(coalesced_contents)
|
||||
|
||||
|
||||
def _finalize_response(response: "ChatResponse | AgentRunResponse") -> None:
|
||||
def _finalize_response(response: "ChatResponse | AgentResponse") -> None:
|
||||
"""Finalizes the response by performing any necessary post-processing."""
|
||||
for msg in response.messages:
|
||||
_coalesce_text_content(msg.contents, TextContent)
|
||||
@@ -3067,10 +3067,10 @@ class ChatResponseUpdate(SerializationMixin):
|
||||
return self.text
|
||||
|
||||
|
||||
# region AgentRunResponse
|
||||
# region AgentResponse
|
||||
|
||||
|
||||
class AgentRunResponse(SerializationMixin):
|
||||
class AgentResponse(SerializationMixin):
|
||||
"""Represents the response to an Agent run request.
|
||||
|
||||
Provides one or more response messages and metadata about the response.
|
||||
@@ -3080,11 +3080,11 @@ class AgentRunResponse(SerializationMixin):
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
from agent_framework import AgentRunResponse, ChatMessage
|
||||
from agent_framework import AgentResponse, ChatMessage
|
||||
|
||||
# Create agent response
|
||||
msg = ChatMessage(role="assistant", text="Task completed successfully.")
|
||||
response = AgentRunResponse(messages=[msg], response_id="run_123")
|
||||
response = AgentResponse(messages=[msg], response_id="run_123")
|
||||
print(response.text) # "Task completed successfully."
|
||||
|
||||
# Access user input requests
|
||||
@@ -3092,20 +3092,20 @@ class AgentRunResponse(SerializationMixin):
|
||||
print(len(user_requests)) # 0
|
||||
|
||||
# Combine streaming updates
|
||||
updates = [...] # List of AgentRunResponseUpdate objects
|
||||
response = AgentRunResponse.from_agent_run_response_updates(updates)
|
||||
updates = [...] # List of AgentResponseUpdate objects
|
||||
response = AgentResponse.from_agent_run_response_updates(updates)
|
||||
|
||||
# Serialization - to_dict and from_dict
|
||||
response_dict = response.to_dict()
|
||||
# {'type': 'agent_run_response', 'messages': [...], 'response_id': 'run_123',
|
||||
# {'type': 'agent_response', 'messages': [...], 'response_id': 'run_123',
|
||||
# 'additional_properties': {}}
|
||||
restored_response = AgentRunResponse.from_dict(response_dict)
|
||||
restored_response = AgentResponse.from_dict(response_dict)
|
||||
print(restored_response.response_id) # "run_123"
|
||||
|
||||
# Serialization - to_json and from_json
|
||||
response_json = response.to_json()
|
||||
# '{"type": "agent_run_response", "messages": [...], "response_id": "run_123", ...}'
|
||||
restored_from_json = AgentRunResponse.from_json(response_json)
|
||||
# '{"type": "agent_response", "messages": [...], "response_id": "run_123", ...}'
|
||||
restored_from_json = AgentResponse.from_json(response_json)
|
||||
print(restored_from_json.text) # "Task completed successfully."
|
||||
"""
|
||||
|
||||
@@ -3127,7 +3127,7 @@ class AgentRunResponse(SerializationMixin):
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize an AgentRunResponse.
|
||||
"""Initialize an AgentResponse.
|
||||
|
||||
Keyword Args:
|
||||
messages: The list of chat messages in the response.
|
||||
@@ -3185,14 +3185,14 @@ class AgentRunResponse(SerializationMixin):
|
||||
@classmethod
|
||||
def from_agent_run_response_updates(
|
||||
cls: type[TAgentRunResponse],
|
||||
updates: Sequence["AgentRunResponseUpdate"],
|
||||
updates: Sequence["AgentResponseUpdate"],
|
||||
*,
|
||||
output_format_type: type[BaseModel] | None = None,
|
||||
) -> TAgentRunResponse:
|
||||
"""Joins multiple updates into a single AgentRunResponse.
|
||||
"""Joins multiple updates into a single AgentResponse.
|
||||
|
||||
Args:
|
||||
updates: A sequence of AgentRunResponseUpdate objects to combine.
|
||||
updates: A sequence of AgentResponseUpdate objects to combine.
|
||||
|
||||
Keyword Args:
|
||||
output_format_type: Optional Pydantic model type to parse the response text into structured data.
|
||||
@@ -3208,14 +3208,14 @@ class AgentRunResponse(SerializationMixin):
|
||||
@classmethod
|
||||
async def from_agent_response_generator(
|
||||
cls: type[TAgentRunResponse],
|
||||
updates: AsyncIterable["AgentRunResponseUpdate"],
|
||||
updates: AsyncIterable["AgentResponseUpdate"],
|
||||
*,
|
||||
output_format_type: type[BaseModel] | None = None,
|
||||
) -> TAgentRunResponse:
|
||||
"""Joins multiple updates into a single AgentRunResponse.
|
||||
"""Joins multiple updates into a single AgentResponse.
|
||||
|
||||
Args:
|
||||
updates: An async iterable of AgentRunResponseUpdate objects to combine.
|
||||
updates: An async iterable of AgentResponseUpdate objects to combine.
|
||||
|
||||
Keyword Args:
|
||||
output_format_type: Optional Pydantic model type to parse the response text into structured data
|
||||
@@ -3240,19 +3240,19 @@ class AgentRunResponse(SerializationMixin):
|
||||
logger.debug("Failed to parse value from agent run response text: %s", ex)
|
||||
|
||||
|
||||
# region AgentRunResponseUpdate
|
||||
# region AgentResponseUpdate
|
||||
|
||||
|
||||
class AgentRunResponseUpdate(SerializationMixin):
|
||||
class AgentResponseUpdate(SerializationMixin):
|
||||
"""Represents a single streaming response chunk from an Agent.
|
||||
|
||||
Examples:
|
||||
.. code-block:: python
|
||||
|
||||
from agent_framework import AgentRunResponseUpdate, TextContent
|
||||
from agent_framework import AgentResponseUpdate, TextContent
|
||||
|
||||
# Create an agent run update
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="Processing...")],
|
||||
role="assistant",
|
||||
response_id="run_123",
|
||||
@@ -3264,15 +3264,15 @@ class AgentRunResponseUpdate(SerializationMixin):
|
||||
|
||||
# Serialization - to_dict and from_dict
|
||||
update_dict = update.to_dict()
|
||||
# {'type': 'agent_run_response_update', 'contents': [{'type': 'text', 'text': 'Processing...'}],
|
||||
# {'type': 'agent_response_update', 'contents': [{'type': 'text', 'text': 'Processing...'}],
|
||||
# 'role': {'type': 'role', 'value': 'assistant'}, 'response_id': 'run_123'}
|
||||
restored_update = AgentRunResponseUpdate.from_dict(update_dict)
|
||||
restored_update = AgentResponseUpdate.from_dict(update_dict)
|
||||
print(restored_update.response_id) # "run_123"
|
||||
|
||||
# Serialization - to_json and from_json
|
||||
update_json = update.to_json()
|
||||
# '{"type": "agent_run_response_update", "contents": [{"type": "text", "text": "Processing..."}], ...}'
|
||||
restored_from_json = AgentRunResponseUpdate.from_json(update_json)
|
||||
# '{"type": "agent_response_update", "contents": [{"type": "text", "text": "Processing..."}], ...}'
|
||||
restored_from_json = AgentResponseUpdate.from_json(update_json)
|
||||
print(restored_from_json.text) # "Processing..."
|
||||
"""
|
||||
|
||||
@@ -3292,7 +3292,7 @@ class AgentRunResponseUpdate(SerializationMixin):
|
||||
raw_representation: Any | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize an AgentRunResponseUpdate.
|
||||
"""Initialize an AgentResponseUpdate.
|
||||
|
||||
Keyword Args:
|
||||
contents: Optional list of BaseContent items or dicts to include in the update.
|
||||
|
||||
@@ -9,8 +9,8 @@ from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, TypedDict, cast
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
BaseContent,
|
||||
@@ -126,7 +126,7 @@ class WorkflowAgent(BaseAgent):
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_storage: CheckpointStorage | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Get a response from the workflow agent (non-streaming).
|
||||
|
||||
This method collects all streaming updates and merges them into a single response.
|
||||
@@ -146,10 +146,10 @@ class WorkflowAgent(BaseAgent):
|
||||
and ai_function tools.
|
||||
|
||||
Returns:
|
||||
The final workflow response as an AgentRunResponse.
|
||||
The final workflow response as an AgentResponse.
|
||||
"""
|
||||
# Collect all streaming updates
|
||||
response_updates: list[AgentRunResponseUpdate] = []
|
||||
response_updates: list[AgentResponseUpdate] = []
|
||||
input_messages = normalize_messages_input(messages)
|
||||
thread = thread or self.get_new_thread()
|
||||
response_id = str(uuid.uuid4())
|
||||
@@ -175,7 +175,7 @@ class WorkflowAgent(BaseAgent):
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_storage: CheckpointStorage | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Stream response updates from the workflow agent.
|
||||
|
||||
Args:
|
||||
@@ -193,11 +193,11 @@ class WorkflowAgent(BaseAgent):
|
||||
and ai_function tools.
|
||||
|
||||
Yields:
|
||||
AgentRunResponseUpdate objects representing the workflow execution progress.
|
||||
AgentResponseUpdate objects representing the workflow execution progress.
|
||||
"""
|
||||
input_messages = normalize_messages_input(messages)
|
||||
thread = thread or self.get_new_thread()
|
||||
response_updates: list[AgentRunResponseUpdate] = []
|
||||
response_updates: list[AgentResponseUpdate] = []
|
||||
response_id = str(uuid.uuid4())
|
||||
|
||||
async for update in self._run_stream_impl(
|
||||
@@ -220,7 +220,7 @@ class WorkflowAgent(BaseAgent):
|
||||
checkpoint_id: str | None = None,
|
||||
checkpoint_storage: CheckpointStorage | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Internal implementation of streaming execution.
|
||||
|
||||
Args:
|
||||
@@ -233,7 +233,7 @@ class WorkflowAgent(BaseAgent):
|
||||
workflow and ai_function tools.
|
||||
|
||||
Yields:
|
||||
AgentRunResponseUpdate objects representing the workflow execution progress.
|
||||
AgentResponseUpdate objects representing the workflow execution progress.
|
||||
"""
|
||||
# Determine the event stream based on whether we have function responses
|
||||
if bool(self.pending_requests):
|
||||
@@ -289,8 +289,8 @@ class WorkflowAgent(BaseAgent):
|
||||
self,
|
||||
response_id: str,
|
||||
event: WorkflowEvent,
|
||||
) -> AgentRunResponseUpdate | None:
|
||||
"""Convert a workflow event to an AgentRunResponseUpdate.
|
||||
) -> AgentResponseUpdate | None:
|
||||
"""Convert a workflow event to an AgentResponseUpdate.
|
||||
|
||||
AgentRunUpdateEvent, RequestInfoEvent, and WorkflowOutputEvent are processed.
|
||||
Other workflow events are ignored as they are workflow-internal.
|
||||
@@ -319,17 +319,17 @@ class WorkflowAgent(BaseAgent):
|
||||
# Convert workflow output to an agent response update.
|
||||
# Handle different data types appropriately.
|
||||
|
||||
# Skip AgentRunResponse from AgentExecutor with output_response=True
|
||||
# Skip AgentResponse from AgentExecutor with output_response=True
|
||||
# since streaming events already surfaced the content.
|
||||
if isinstance(data, AgentRunResponse):
|
||||
if isinstance(data, AgentResponse):
|
||||
executor = self.workflow.executors.get(source_executor_id)
|
||||
if isinstance(executor, AgentExecutor) and executor.output_response:
|
||||
return None
|
||||
|
||||
if isinstance(data, AgentRunResponseUpdate):
|
||||
if isinstance(data, AgentResponseUpdate):
|
||||
return data
|
||||
if isinstance(data, ChatMessage):
|
||||
return AgentRunResponseUpdate(
|
||||
return AgentResponseUpdate(
|
||||
contents=list(data.contents),
|
||||
role=data.role,
|
||||
author_name=data.author_name or source_executor_id,
|
||||
@@ -341,7 +341,7 @@ class WorkflowAgent(BaseAgent):
|
||||
contents = self._extract_contents(data)
|
||||
if not contents:
|
||||
return None
|
||||
return AgentRunResponseUpdate(
|
||||
return AgentResponseUpdate(
|
||||
contents=contents,
|
||||
role=Role.ASSISTANT,
|
||||
author_name=source_executor_id,
|
||||
@@ -367,7 +367,7 @@ class WorkflowAgent(BaseAgent):
|
||||
function_call=function_call,
|
||||
additional_properties={"request_id": request_id},
|
||||
)
|
||||
return AgentRunResponseUpdate(
|
||||
return AgentResponseUpdate(
|
||||
contents=[function_call, approval_request],
|
||||
role=Role.ASSISTANT,
|
||||
author_name=self.name,
|
||||
@@ -441,30 +441,30 @@ class WorkflowAgent(BaseAgent):
|
||||
class _ResponseState(TypedDict):
|
||||
"""State for grouping response updates by message_id."""
|
||||
|
||||
by_msg: dict[str, list[AgentRunResponseUpdate]]
|
||||
dangling: list[AgentRunResponseUpdate]
|
||||
by_msg: dict[str, list[AgentResponseUpdate]]
|
||||
dangling: list[AgentResponseUpdate]
|
||||
|
||||
@staticmethod
|
||||
def merge_updates(updates: list[AgentRunResponseUpdate], response_id: str) -> AgentRunResponse:
|
||||
"""Merge streaming updates into a single AgentRunResponse.
|
||||
def merge_updates(updates: list[AgentResponseUpdate], response_id: str) -> AgentResponse:
|
||||
"""Merge streaming updates into a single AgentResponse.
|
||||
|
||||
Behavior:
|
||||
- Group updates by response_id; within each response_id, group by message_id and keep a dangling bucket for
|
||||
updates without message_id.
|
||||
- Convert each group (per message and dangling) into an intermediate AgentRunResponse via
|
||||
AgentRunResponse.from_agent_run_response_updates, then sort by created_at and merge.
|
||||
- Convert each group (per message and dangling) into an intermediate AgentResponse via
|
||||
AgentResponse.from_agent_run_response_updates, then sort by created_at and merge.
|
||||
- Append messages from updates without any response_id at the end (global dangling), while aggregating metadata.
|
||||
|
||||
Args:
|
||||
updates: The list of AgentRunResponseUpdate objects to merge.
|
||||
response_id: The response identifier to set on the returned AgentRunResponse.
|
||||
updates: The list of AgentResponseUpdate objects to merge.
|
||||
response_id: The response identifier to set on the returned AgentResponse.
|
||||
|
||||
Returns:
|
||||
An AgentRunResponse with messages in processing order and aggregated metadata.
|
||||
An AgentResponse with messages in processing order and aggregated metadata.
|
||||
"""
|
||||
# PHASE 1: GROUP UPDATES BY RESPONSE_ID AND MESSAGE_ID
|
||||
states: dict[str, WorkflowAgent._ResponseState] = {}
|
||||
global_dangling: list[AgentRunResponseUpdate] = []
|
||||
global_dangling: list[AgentResponseUpdate] = []
|
||||
|
||||
for u in updates:
|
||||
if u.response_id:
|
||||
@@ -497,7 +497,7 @@ class WorkflowAgent(BaseAgent):
|
||||
return a
|
||||
return a + b
|
||||
|
||||
def _merge_responses(current: AgentRunResponse | None, incoming: AgentRunResponse) -> AgentRunResponse:
|
||||
def _merge_responses(current: AgentResponse | None, incoming: AgentResponse) -> AgentResponse:
|
||||
if current is None:
|
||||
return incoming
|
||||
raw_list: list[object] = []
|
||||
@@ -512,7 +512,7 @@ class WorkflowAgent(BaseAgent):
|
||||
_add_raw(current.raw_representation)
|
||||
if incoming.raw_representation is not None:
|
||||
_add_raw(incoming.raw_representation)
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=(current.messages or []) + (incoming.messages or []),
|
||||
response_id=current.response_id or incoming.response_id,
|
||||
created_at=incoming.created_at or current.created_at,
|
||||
@@ -533,16 +533,16 @@ class WorkflowAgent(BaseAgent):
|
||||
by_msg = state["by_msg"]
|
||||
dangling = state["dangling"]
|
||||
|
||||
per_message_responses: list[AgentRunResponse] = []
|
||||
per_message_responses: list[AgentResponse] = []
|
||||
for _, msg_updates in by_msg.items():
|
||||
if msg_updates:
|
||||
per_message_responses.append(AgentRunResponse.from_agent_run_response_updates(msg_updates))
|
||||
per_message_responses.append(AgentResponse.from_agent_run_response_updates(msg_updates))
|
||||
if dangling:
|
||||
per_message_responses.append(AgentRunResponse.from_agent_run_response_updates(dangling))
|
||||
per_message_responses.append(AgentResponse.from_agent_run_response_updates(dangling))
|
||||
|
||||
per_message_responses.sort(key=lambda r: _parse_dt(r.created_at))
|
||||
|
||||
aggregated: AgentRunResponse | None = None
|
||||
aggregated: AgentResponse | None = None
|
||||
for resp in per_message_responses:
|
||||
if resp.response_id and grouped_response_id and resp.response_id != grouped_response_id:
|
||||
resp.response_id = grouped_response_id
|
||||
@@ -570,7 +570,7 @@ class WorkflowAgent(BaseAgent):
|
||||
|
||||
# PHASE 3: HANDLE GLOBAL DANGLING UPDATES (NO RESPONSE_ID)
|
||||
if global_dangling:
|
||||
flattened = AgentRunResponse.from_agent_run_response_updates(global_dangling)
|
||||
flattened = AgentResponse.from_agent_run_response_updates(global_dangling)
|
||||
final_messages.extend(flattened.messages)
|
||||
if flattened.usage_details:
|
||||
merged_usage = _sum_usage(merged_usage, flattened.usage_details)
|
||||
@@ -591,7 +591,7 @@ class WorkflowAgent(BaseAgent):
|
||||
raw_representations.append(cast_flat)
|
||||
|
||||
# PHASE 4: CONSTRUCT FINAL RESPONSE WITH INPUT RESPONSE_ID
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=final_messages,
|
||||
response_id=response_id,
|
||||
created_at=latest_created_at,
|
||||
|
||||
@@ -9,7 +9,7 @@ from agent_framework import FunctionApprovalRequestContent, FunctionApprovalResp
|
||||
|
||||
from .._agents import AgentProtocol, ChatAgent
|
||||
from .._threads import AgentThread
|
||||
from .._types import AgentRunResponse, AgentRunResponseUpdate, ChatMessage
|
||||
from .._types import AgentResponse, AgentResponseUpdate, ChatMessage
|
||||
from ._agent_utils import resolve_agent_id
|
||||
from ._checkpoint_encoding import decode_checkpoint_value, encode_checkpoint_value
|
||||
from ._const import WORKFLOW_RUN_KWARGS_KEY
|
||||
@@ -51,14 +51,14 @@ class AgentExecutorResponse:
|
||||
|
||||
Attributes:
|
||||
executor_id: The ID of the executor that generated the response.
|
||||
agent_run_response: The underlying agent run response (unaltered from client).
|
||||
agent_response: The underlying agent run response (unaltered from client).
|
||||
full_conversation: The full conversation context (prior inputs + all assistant/tool outputs) that
|
||||
should be used when chaining to another AgentExecutor. This prevents downstream agents losing
|
||||
user prompts while keeping the emitted AgentRunEvent text faithful to the raw agent output.
|
||||
"""
|
||||
|
||||
executor_id: str
|
||||
agent_run_response: AgentRunResponse
|
||||
agent_response: AgentResponse
|
||||
full_conversation: list[ChatMessage] | None = None
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class AgentExecutor(Executor):
|
||||
Args:
|
||||
agent: The agent to be wrapped by this executor.
|
||||
agent_thread: The thread to use for running the agent. If None, a new thread will be created.
|
||||
output_response: Whether to yield an AgentRunResponse as a workflow output when the agent completes.
|
||||
output_response: Whether to yield an AgentResponse as a workflow output when the agent completes.
|
||||
id: A unique identifier for the executor. If None, the agent's name will be used if available.
|
||||
"""
|
||||
# Prefer provided id; else use agent.name if present; else generate deterministic prefix
|
||||
@@ -106,14 +106,14 @@ class AgentExecutor(Executor):
|
||||
|
||||
@property
|
||||
def output_response(self) -> bool:
|
||||
"""Whether this executor yields AgentRunResponse as workflow output when complete."""
|
||||
"""Whether this executor yields AgentResponse as workflow output when complete."""
|
||||
return self._output_response
|
||||
|
||||
@property
|
||||
def workflow_output_types(self) -> list[type[Any]]:
|
||||
# Override to declare AgentRunResponse as a possible output type only if enabled.
|
||||
# Override to declare AgentResponse as a possible output type only if enabled.
|
||||
if self._output_response:
|
||||
return [AgentRunResponse]
|
||||
return [AgentResponse]
|
||||
return []
|
||||
|
||||
@property
|
||||
@@ -123,7 +123,7 @@ class AgentExecutor(Executor):
|
||||
|
||||
@handler
|
||||
async def run(
|
||||
self, request: AgentExecutorRequest, ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse]
|
||||
self, request: AgentExecutorRequest, ctx: WorkflowContext[AgentExecutorResponse, AgentResponse]
|
||||
) -> None:
|
||||
"""Handle an AgentExecutorRequest (canonical input).
|
||||
|
||||
@@ -136,22 +136,22 @@ class AgentExecutor(Executor):
|
||||
|
||||
@handler
|
||||
async def from_response(
|
||||
self, prior: AgentExecutorResponse, ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse]
|
||||
self, prior: AgentExecutorResponse, ctx: WorkflowContext[AgentExecutorResponse, AgentResponse]
|
||||
) -> None:
|
||||
"""Enable seamless chaining: accept a prior AgentExecutorResponse as input.
|
||||
|
||||
Strategy: treat the prior response's messages as the conversation state and
|
||||
immediately run the agent to produce a new response.
|
||||
"""
|
||||
# Replace cache with full conversation if available, else fall back to agent_run_response messages.
|
||||
# Replace cache with full conversation if available, else fall back to agent_response messages.
|
||||
if prior.full_conversation is not None:
|
||||
self._cache = list(prior.full_conversation)
|
||||
else:
|
||||
self._cache = list(prior.agent_run_response.messages)
|
||||
self._cache = list(prior.agent_response.messages)
|
||||
await self._run_agent_and_emit(ctx)
|
||||
|
||||
@handler
|
||||
async def from_str(self, text: str, ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse]) -> None:
|
||||
async def from_str(self, text: str, ctx: WorkflowContext[AgentExecutorResponse, AgentResponse]) -> None:
|
||||
"""Accept a raw user prompt string and run the agent (one-shot)."""
|
||||
self._cache = normalize_messages_input(text)
|
||||
await self._run_agent_and_emit(ctx)
|
||||
@@ -160,7 +160,7 @@ class AgentExecutor(Executor):
|
||||
async def from_message(
|
||||
self,
|
||||
message: ChatMessage,
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse],
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentResponse],
|
||||
) -> None:
|
||||
"""Accept a single ChatMessage as input."""
|
||||
self._cache = normalize_messages_input(message)
|
||||
@@ -170,7 +170,7 @@ class AgentExecutor(Executor):
|
||||
async def from_messages(
|
||||
self,
|
||||
messages: list[str | ChatMessage],
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse],
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentResponse],
|
||||
) -> None:
|
||||
"""Accept a list of chat inputs (strings or ChatMessage) as conversation context."""
|
||||
self._cache = normalize_messages_input(messages)
|
||||
@@ -181,7 +181,7 @@ class AgentExecutor(Executor):
|
||||
self,
|
||||
original_request: FunctionApprovalRequestContent,
|
||||
response: FunctionApprovalResponseContent,
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse],
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentResponse],
|
||||
) -> None:
|
||||
"""Handle user input responses for function approvals during agent execution.
|
||||
|
||||
@@ -292,7 +292,7 @@ class AgentExecutor(Executor):
|
||||
logger.debug("AgentExecutor %s: Resetting cache", self.id)
|
||||
self._cache.clear()
|
||||
|
||||
async def _run_agent_and_emit(self, ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse]) -> None:
|
||||
async def _run_agent_and_emit(self, ctx: WorkflowContext[AgentExecutorResponse, AgentResponse]) -> None:
|
||||
"""Execute the underlying agent, emit events, and enqueue response.
|
||||
|
||||
Checks ctx.is_streaming() to determine whether to emit incremental AgentRunUpdateEvent
|
||||
@@ -306,7 +306,7 @@ class AgentExecutor(Executor):
|
||||
response = await self._run_agent(cast(WorkflowContext, ctx))
|
||||
|
||||
# Always extend full conversation with cached messages plus agent outputs
|
||||
# (agent_run_response.messages) after each run. This is to avoid losing context
|
||||
# (agent_response.messages) after each run. This is to avoid losing context
|
||||
# when agent did not complete and the cache is cleared when responses come back.
|
||||
# Do not mutate response.messages so AgentRunEvent remains faithful to the raw output.
|
||||
self._full_conversation.extend(list(self._cache) + (list(response.messages) if response else []))
|
||||
@@ -323,14 +323,14 @@ class AgentExecutor(Executor):
|
||||
await ctx.send_message(agent_response)
|
||||
self._cache.clear()
|
||||
|
||||
async def _run_agent(self, ctx: WorkflowContext) -> AgentRunResponse | None:
|
||||
async def _run_agent(self, ctx: WorkflowContext) -> AgentResponse | None:
|
||||
"""Execute the underlying agent in non-streaming mode.
|
||||
|
||||
Args:
|
||||
ctx: The workflow context for emitting events.
|
||||
|
||||
Returns:
|
||||
The complete AgentRunResponse, or None if waiting for user input.
|
||||
The complete AgentResponse, or None if waiting for user input.
|
||||
"""
|
||||
run_kwargs: dict[str, Any] = await ctx.get_shared_state(WORKFLOW_RUN_KWARGS_KEY)
|
||||
|
||||
@@ -350,18 +350,18 @@ class AgentExecutor(Executor):
|
||||
|
||||
return response
|
||||
|
||||
async def _run_agent_streaming(self, ctx: WorkflowContext) -> AgentRunResponse | None:
|
||||
async def _run_agent_streaming(self, ctx: WorkflowContext) -> AgentResponse | None:
|
||||
"""Execute the underlying agent in streaming mode and collect the full response.
|
||||
|
||||
Args:
|
||||
ctx: The workflow context for emitting events.
|
||||
|
||||
Returns:
|
||||
The complete AgentRunResponse, or None if waiting for user input.
|
||||
The complete AgentResponse, or None if waiting for user input.
|
||||
"""
|
||||
run_kwargs: dict[str, Any] = await ctx.get_shared_state(WORKFLOW_RUN_KWARGS_KEY)
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
user_input_requests: list[FunctionApprovalRequestContent] = []
|
||||
async for update in self._agent.run_stream(
|
||||
self._cache,
|
||||
@@ -374,15 +374,15 @@ class AgentExecutor(Executor):
|
||||
if update.user_input_requests:
|
||||
user_input_requests.extend(update.user_input_requests)
|
||||
|
||||
# Build the final AgentRunResponse from the collected updates
|
||||
# Build the final AgentResponse from the collected updates
|
||||
if isinstance(self._agent, ChatAgent):
|
||||
response_format = self._agent.default_options.get("response_format")
|
||||
response = AgentRunResponse.from_agent_run_response_updates(
|
||||
response = AgentResponse.from_agent_run_response_updates(
|
||||
updates,
|
||||
output_format_type=response_format,
|
||||
)
|
||||
else:
|
||||
response = AgentRunResponse.from_agent_run_response_updates(updates)
|
||||
response = AgentResponse.from_agent_run_response_updates(updates)
|
||||
|
||||
# Handle any user input requests after the streaming completes
|
||||
if user_input_requests:
|
||||
|
||||
@@ -346,7 +346,7 @@ class BaseGroupChatOrchestrator(Executor, ABC):
|
||||
List of ChatMessages extracted from the response
|
||||
"""
|
||||
if isinstance(response, AgentExecutorResponse):
|
||||
return response.agent_run_response.messages
|
||||
return response.agent_response.messages
|
||||
if isinstance(response, GroupChatResponseMessage):
|
||||
return [response.message]
|
||||
raise TypeError(f"Unsupported response type: {type(response)}")
|
||||
|
||||
@@ -79,7 +79,7 @@ class _AggregateAgentConversations(Executor):
|
||||
[ single_user_prompt?, agent1_final_assistant, agent2_final_assistant, ... ]
|
||||
|
||||
- Extracts a single user prompt (first user message seen across results).
|
||||
- For each result, selects the final assistant message (prefers agent_run_response.messages).
|
||||
- For each result, selects the final assistant message (prefers agent_response.messages).
|
||||
- Avoids duplicating the same user message per agent.
|
||||
"""
|
||||
|
||||
@@ -107,7 +107,7 @@ class _AggregateAgentConversations(Executor):
|
||||
assistant_replies: list[ChatMessage] = []
|
||||
|
||||
for r in results:
|
||||
resp_messages = list(getattr(r.agent_run_response, "messages", []) or [])
|
||||
resp_messages = list(getattr(r.agent_response, "messages", []) or [])
|
||||
conv = r.full_conversation if r.full_conversation is not None else resp_messages
|
||||
|
||||
logger.debug(
|
||||
@@ -213,7 +213,7 @@ class ConcurrentBuilder:
|
||||
# Custom aggregator via callback (sync or async). The callback receives
|
||||
# list[AgentExecutorResponse] and its return value becomes the workflow's output.
|
||||
def summarize(results: list[AgentExecutorResponse]) -> str:
|
||||
return " | ".join(r.agent_run_response.messages[-1].text for r in results)
|
||||
return " | ".join(r.agent_response.messages[-1].text for r in results)
|
||||
|
||||
|
||||
workflow = ConcurrentBuilder().participants([agent1, agent2, agent3]).with_aggregator(summarize).build()
|
||||
@@ -223,7 +223,7 @@ class ConcurrentBuilder:
|
||||
class MyAggregator(Executor):
|
||||
@handler
|
||||
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext[Never, str]) -> None:
|
||||
await ctx.yield_output(" | ".join(r.agent_run_response.messages[-1].text for r in results))
|
||||
await ctx.yield_output(" | ".join(r.agent_response.messages[-1].text for r in results))
|
||||
|
||||
|
||||
workflow = (
|
||||
@@ -416,7 +416,7 @@ class ConcurrentBuilder:
|
||||
class CustomAggregator(Executor):
|
||||
@handler
|
||||
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext) -> None:
|
||||
await ctx.yield_output(" | ".join(r.agent_run_response.messages[-1].text for r in results))
|
||||
await ctx.yield_output(" | ".join(r.agent_response.messages[-1].text for r in results))
|
||||
|
||||
|
||||
wf = ConcurrentBuilder().participants([a1, a2, a3]).with_aggregator(CustomAggregator()).build()
|
||||
@@ -424,7 +424,7 @@ class ConcurrentBuilder:
|
||||
|
||||
# Callback-based aggregator (string result)
|
||||
async def summarize(results: list[AgentExecutorResponse]) -> str:
|
||||
return " | ".join(r.agent_run_response.messages[-1].text for r in results)
|
||||
return " | ".join(r.agent_response.messages[-1].text for r in results)
|
||||
|
||||
|
||||
wf = ConcurrentBuilder().participants([a1, a2, a3]).with_aggregator(summarize).build()
|
||||
@@ -432,7 +432,7 @@ class ConcurrentBuilder:
|
||||
|
||||
# Callback-based aggregator (yield result)
|
||||
async def summarize(results: list[AgentExecutorResponse], ctx: WorkflowContext[Never, str]) -> None:
|
||||
await ctx.yield_output(" | ".join(r.agent_run_response.messages[-1].text for r in results))
|
||||
await ctx.yield_output(" | ".join(r.agent_response.messages[-1].text for r in results))
|
||||
|
||||
|
||||
wf = ConcurrentBuilder().participants([a1, a2, a3]).with_aggregator(summarize).build()
|
||||
|
||||
@@ -8,7 +8,7 @@ from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Any, TypeAlias
|
||||
|
||||
from agent_framework import AgentRunResponse, AgentRunResponseUpdate
|
||||
from agent_framework import AgentResponse, AgentResponseUpdate
|
||||
|
||||
from ._checkpoint_encoding import decode_checkpoint_value, encode_checkpoint_value
|
||||
from ._typing_utils import deserialize_type, serialize_type
|
||||
@@ -367,9 +367,9 @@ class ExecutorFailedEvent(ExecutorEvent):
|
||||
class AgentRunUpdateEvent(ExecutorEvent):
|
||||
"""Event triggered when an agent is streaming messages."""
|
||||
|
||||
data: AgentRunResponseUpdate
|
||||
data: AgentResponseUpdate
|
||||
|
||||
def __init__(self, executor_id: str, data: AgentRunResponseUpdate):
|
||||
def __init__(self, executor_id: str, data: AgentResponseUpdate):
|
||||
"""Initialize the agent streaming event."""
|
||||
super().__init__(executor_id, data)
|
||||
|
||||
@@ -381,9 +381,9 @@ class AgentRunUpdateEvent(ExecutorEvent):
|
||||
class AgentRunEvent(ExecutorEvent):
|
||||
"""Event triggered when an agent run is completed."""
|
||||
|
||||
data: AgentRunResponse
|
||||
data: AgentResponse
|
||||
|
||||
def __init__(self, executor_id: str, data: AgentRunResponse):
|
||||
def __init__(self, executor_id: str, data: AgentResponse):
|
||||
"""Initialize the agent run event."""
|
||||
super().__init__(executor_id, data)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ from .._agents import AgentProtocol, ChatAgent
|
||||
from .._middleware import FunctionInvocationContext, FunctionMiddleware
|
||||
from .._threads import AgentThread
|
||||
from .._tools import AIFunction, ai_function
|
||||
from .._types import AgentRunResponse, ChatMessage, Role
|
||||
from .._types import AgentResponse, ChatMessage, Role
|
||||
from ._agent_executor import AgentExecutor, AgentExecutorRequest, AgentExecutorResponse
|
||||
from ._agent_utils import resolve_agent_id
|
||||
from ._base_group_chat_orchestrator import TerminationCondition
|
||||
@@ -155,7 +155,7 @@ class HandoffAgentUserRequest:
|
||||
agent_response: The response generated by the agent at the most recent turn
|
||||
"""
|
||||
|
||||
agent_response: AgentRunResponse
|
||||
agent_response: AgentResponse
|
||||
|
||||
@staticmethod
|
||||
def create_response(response: str | list[str] | ChatMessage | list[ChatMessage]) -> list[ChatMessage]:
|
||||
@@ -361,7 +361,7 @@ class HandoffAgentExecutor(AgentExecutor):
|
||||
return _handoff_tool
|
||||
|
||||
@override
|
||||
async def _run_agent_and_emit(self, ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse]) -> None:
|
||||
async def _run_agent_and_emit(self, ctx: WorkflowContext[AgentExecutorResponse, AgentResponse]) -> None:
|
||||
"""Override to support handoff."""
|
||||
# When the full conversation is empty, it means this is the first run.
|
||||
# Broadcast the initial cache to all other agents. Subsequent runs won't
|
||||
@@ -436,7 +436,7 @@ class HandoffAgentExecutor(AgentExecutor):
|
||||
self,
|
||||
original_request: HandoffAgentUserRequest,
|
||||
response: list[ChatMessage],
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentRunResponse],
|
||||
ctx: WorkflowContext[AgentExecutorResponse, AgentResponse],
|
||||
) -> None:
|
||||
"""Handle user response for a request that is issued after agent runs.
|
||||
|
||||
@@ -477,7 +477,7 @@ class HandoffAgentExecutor(AgentExecutor):
|
||||
# Since all agents are connected via fan-out, we can directly send the message
|
||||
await ctx.send_message(agent_executor_request)
|
||||
|
||||
def _is_handoff_requested(self, response: AgentRunResponse) -> str | None:
|
||||
def _is_handoff_requested(self, response: AgentResponse) -> str | None:
|
||||
"""Determine if the agent response includes a handoff request.
|
||||
|
||||
If a handoff tool is invoked, the middleware will short-circuit execution
|
||||
|
||||
@@ -16,7 +16,7 @@ from typing_extensions import Never
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
ChatMessage,
|
||||
Role,
|
||||
)
|
||||
@@ -594,7 +594,7 @@ class StandardMagenticManager(MagenticManagerBase):
|
||||
The agent's run method is called which applies the agent's configured options
|
||||
(temperature, seed, instructions, etc.).
|
||||
"""
|
||||
response: AgentRunResponse = await self._agent.run(messages)
|
||||
response: AgentResponse = await self._agent.run(messages)
|
||||
if not response.messages:
|
||||
raise RuntimeError("Agent returned no messages in response.")
|
||||
if len(response.messages) > 1:
|
||||
|
||||
@@ -223,7 +223,7 @@ class WorkflowBuilder:
|
||||
Args:
|
||||
candidate: The executor or agent to wrap.
|
||||
agent_thread: The thread to use for running the agent. If None, a new thread will be created.
|
||||
output_response: Whether to yield an AgentRunResponse as a workflow output when the agent completes.
|
||||
output_response: Whether to yield an AgentResponse as a workflow output when the agent completes.
|
||||
executor_id: A unique identifier for the executor. If None, the agent's name will be used if available.
|
||||
"""
|
||||
try: # Local import to avoid hard dependency at import time
|
||||
@@ -352,7 +352,7 @@ class WorkflowBuilder:
|
||||
the agent's internal name. But it must be unique within the workflow.
|
||||
agent_thread: The thread to use for running the agent. If None, a new thread will be created when
|
||||
the agent is instantiated.
|
||||
output_response: Whether to yield an AgentRunResponse as a workflow output when the agent completes.
|
||||
output_response: Whether to yield an AgentResponse as a workflow output when the agent completes.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
@@ -411,7 +411,7 @@ class WorkflowBuilder:
|
||||
Args:
|
||||
agent: The agent to add to the workflow.
|
||||
agent_thread: The thread to use for running the agent. If None, a new thread will be created.
|
||||
output_response: Whether to yield an AgentRunResponse as a workflow output when the agent completes.
|
||||
output_response: Whether to yield an AgentResponse as a workflow output when the agent completes.
|
||||
id: A unique identifier for the executor. If None, the agent's name will be used if available.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -35,8 +35,8 @@ if TYPE_CHECKING: # pragma: no cover
|
||||
from ._threads import AgentThread
|
||||
from ._tools import AIFunction
|
||||
from ._types import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
@@ -1315,10 +1315,10 @@ def use_instrumentation(
|
||||
|
||||
|
||||
def _trace_agent_run(
|
||||
run_func: Callable[..., Awaitable["AgentRunResponse"]],
|
||||
run_func: Callable[..., Awaitable["AgentResponse"]],
|
||||
provider_name: str,
|
||||
capture_usage: bool = True,
|
||||
) -> Callable[..., Awaitable["AgentRunResponse"]]:
|
||||
) -> Callable[..., Awaitable["AgentResponse"]]:
|
||||
"""Decorator to trace chat completion activities.
|
||||
|
||||
Args:
|
||||
@@ -1334,7 +1334,7 @@ def _trace_agent_run(
|
||||
*,
|
||||
thread: "AgentThread | None" = None,
|
||||
**kwargs: Any,
|
||||
) -> "AgentRunResponse":
|
||||
) -> "AgentResponse":
|
||||
global OBSERVABILITY_SETTINGS
|
||||
|
||||
if not OBSERVABILITY_SETTINGS.ENABLED:
|
||||
@@ -1384,10 +1384,10 @@ def _trace_agent_run(
|
||||
|
||||
|
||||
def _trace_agent_run_stream(
|
||||
run_streaming_func: Callable[..., AsyncIterable["AgentRunResponseUpdate"]],
|
||||
run_streaming_func: Callable[..., AsyncIterable["AgentResponseUpdate"]],
|
||||
provider_name: str,
|
||||
capture_usage: bool,
|
||||
) -> Callable[..., AsyncIterable["AgentRunResponseUpdate"]]:
|
||||
) -> Callable[..., AsyncIterable["AgentResponseUpdate"]]:
|
||||
"""Decorator to trace streaming agent run activities.
|
||||
|
||||
Args:
|
||||
@@ -1403,7 +1403,7 @@ def _trace_agent_run_stream(
|
||||
*,
|
||||
thread: "AgentThread | None" = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable["AgentRunResponseUpdate"]:
|
||||
) -> AsyncIterable["AgentResponseUpdate"]:
|
||||
global OBSERVABILITY_SETTINGS
|
||||
|
||||
if not OBSERVABILITY_SETTINGS.ENABLED:
|
||||
@@ -1412,9 +1412,9 @@ def _trace_agent_run_stream(
|
||||
yield streaming_agent_response
|
||||
return
|
||||
|
||||
from ._types import AgentRunResponse, merge_chat_options
|
||||
from ._types import AgentResponse, merge_chat_options
|
||||
|
||||
all_updates: list["AgentRunResponseUpdate"] = []
|
||||
all_updates: list["AgentResponseUpdate"] = []
|
||||
|
||||
default_options = getattr(self, "default_options", {})
|
||||
options = merge_chat_options(default_options, kwargs.get("options", {}))
|
||||
@@ -1444,7 +1444,7 @@ def _trace_agent_run_stream(
|
||||
capture_exception(span=span, exception=exception, timestamp=time_ns())
|
||||
raise
|
||||
else:
|
||||
response = AgentRunResponse.from_agent_run_response_updates(all_updates)
|
||||
response = AgentResponse.from_agent_run_response_updates(all_updates)
|
||||
attributes = _get_response_attributes(attributes, response, capture_usage=capture_usage)
|
||||
_capture_response(span=span, attributes=attributes)
|
||||
if OBSERVABILITY_SETTINGS.SENSITIVE_DATA_ENABLED and response.messages:
|
||||
@@ -1784,7 +1784,7 @@ def _to_otel_part(content: "Contents") -> dict[str, Any] | None:
|
||||
|
||||
def _get_response_attributes(
|
||||
attributes: dict[str, Any],
|
||||
response: "ChatResponse | AgentRunResponse",
|
||||
response: "ChatResponse | AgentResponse",
|
||||
duration: float | None = None,
|
||||
*,
|
||||
capture_usage: bool = True,
|
||||
|
||||
@@ -9,8 +9,8 @@ from azure.identity import AzureCliCredential
|
||||
from pydantic import Field
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatAgent,
|
||||
ChatClientProtocol,
|
||||
@@ -403,7 +403,7 @@ async def test_azure_assistants_agent_basic_run():
|
||||
response = await agent.run("Hello! Please respond with 'Hello World' exactly.")
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
assert "Hello World" in response.text
|
||||
@@ -420,7 +420,7 @@ async def test_azure_assistants_agent_basic_run_streaming():
|
||||
full_message: str = ""
|
||||
async for chunk in agent.run_stream("Please respond with exactly: 'This is a streaming response test.'"):
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, AgentRunResponseUpdate)
|
||||
assert isinstance(chunk, AgentResponseUpdate)
|
||||
if chunk.text:
|
||||
full_message += chunk.text
|
||||
|
||||
@@ -444,14 +444,14 @@ async def test_azure_assistants_agent_thread_persistence():
|
||||
first_response = await agent.run(
|
||||
"Remember this number: 42. What number did I just tell you to remember?", thread=thread
|
||||
)
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert "42" in first_response.text
|
||||
|
||||
# Second message - test conversation memory
|
||||
second_response = await agent.run(
|
||||
"What number did I tell you to remember in my previous message?", thread=thread
|
||||
)
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert "42" in second_response.text
|
||||
|
||||
# Verify thread has been populated with conversation ID
|
||||
@@ -475,7 +475,7 @@ async def test_azure_assistants_agent_existing_thread_id():
|
||||
response1 = await agent.run("What's the weather in Paris?", thread=thread)
|
||||
|
||||
# Validate first response
|
||||
assert isinstance(response1, AgentRunResponse)
|
||||
assert isinstance(response1, AgentResponse)
|
||||
assert response1.text is not None
|
||||
assert any(word in response1.text.lower() for word in ["weather", "paris"])
|
||||
|
||||
@@ -497,7 +497,7 @@ async def test_azure_assistants_agent_existing_thread_id():
|
||||
response2 = await agent.run("What was the last city I asked about?", thread=thread)
|
||||
|
||||
# Validate that the agent remembers the previous conversation
|
||||
assert isinstance(response2, AgentRunResponse)
|
||||
assert isinstance(response2, AgentResponse)
|
||||
assert response2.text is not None
|
||||
# Should reference Paris from the previous conversation
|
||||
assert "paris" in response2.text.lower()
|
||||
@@ -517,7 +517,7 @@ async def test_azure_assistants_agent_code_interpreter():
|
||||
response = await agent.run("Write Python code to calculate the factorial of 5 and show the result.")
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
# Factorial of 5 is 120
|
||||
assert "120" in response.text or "factorial" in response.text.lower()
|
||||
@@ -536,7 +536,7 @@ async def test_azure_assistants_client_agent_level_tool_persistence():
|
||||
# First run - agent-level tool should be available
|
||||
first_response = await agent.run("What's the weather like in Chicago?")
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
# Should use the agent-level weather tool
|
||||
assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"])
|
||||
@@ -544,7 +544,7 @@ async def test_azure_assistants_client_agent_level_tool_persistence():
|
||||
# Second run - agent-level tool should still be available (persistence test)
|
||||
second_response = await agent.run("What's the weather in Miami?")
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
# Should use the agent-level weather tool again
|
||||
assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"])
|
||||
|
||||
@@ -17,8 +17,8 @@ from openai.types.chat.chat_completion_chunk import ChoiceDelta as ChunkChoiceDe
|
||||
from openai.types.chat.chat_completion_message import ChatCompletionMessage
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
BaseChatClient,
|
||||
ChatAgent,
|
||||
ChatClientProtocol,
|
||||
@@ -731,7 +731,7 @@ async def test_azure_openai_chat_client_agent_basic_run():
|
||||
# Test basic run
|
||||
response = await agent.run("Please respond with exactly: 'This is a response test.'")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
assert "response test" in response.text.lower()
|
||||
@@ -747,7 +747,7 @@ async def test_azure_openai_chat_client_agent_basic_run_streaming():
|
||||
# Test streaming run
|
||||
full_text = ""
|
||||
async for chunk in agent.run_stream("Please respond with exactly: 'This is a streaming response test.'"):
|
||||
assert isinstance(chunk, AgentRunResponseUpdate)
|
||||
assert isinstance(chunk, AgentResponseUpdate)
|
||||
if chunk.text:
|
||||
full_text += chunk.text
|
||||
|
||||
@@ -769,13 +769,13 @@ async def test_azure_openai_chat_client_agent_thread_persistence():
|
||||
# First interaction
|
||||
response1 = await agent.run("My name is Alice. Remember this.", thread=thread)
|
||||
|
||||
assert isinstance(response1, AgentRunResponse)
|
||||
assert isinstance(response1, AgentResponse)
|
||||
assert response1.text is not None
|
||||
|
||||
# Second interaction - test memory
|
||||
response2 = await agent.run("What is my name?", thread=thread)
|
||||
|
||||
assert isinstance(response2, AgentRunResponse)
|
||||
assert isinstance(response2, AgentResponse)
|
||||
assert response2.text is not None
|
||||
assert "alice" in response2.text.lower()
|
||||
|
||||
@@ -795,7 +795,7 @@ async def test_azure_openai_chat_client_agent_existing_thread():
|
||||
thread = first_agent.get_new_thread()
|
||||
first_response = await first_agent.run("My name is Alice. Remember this.", thread=thread)
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
|
||||
# Preserve the thread for reuse
|
||||
@@ -810,7 +810,7 @@ async def test_azure_openai_chat_client_agent_existing_thread():
|
||||
# Reuse the preserved thread
|
||||
second_response = await second_agent.run("What is my name?", thread=preserved_thread)
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
assert "alice" in second_response.text.lower()
|
||||
|
||||
@@ -828,7 +828,7 @@ async def test_azure_chat_client_agent_level_tool_persistence():
|
||||
# First run - agent-level tool should be available
|
||||
first_response = await agent.run("What's the weather like in Chicago?")
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
# Should use the agent-level weather tool
|
||||
assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"])
|
||||
@@ -836,7 +836,7 @@ async def test_azure_chat_client_agent_level_tool_persistence():
|
||||
# Second run - agent-level tool should still be available (persistence test)
|
||||
second_response = await agent.run("What's the weather in Miami?")
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
# Should use the agent-level weather tool again
|
||||
assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"])
|
||||
|
||||
@@ -10,7 +10,7 @@ from pydantic import BaseModel
|
||||
from pytest import param
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
ChatAgent,
|
||||
ChatClientProtocol,
|
||||
ChatMessage,
|
||||
@@ -432,7 +432,7 @@ async def test_integration_client_agent_existing_thread():
|
||||
thread = first_agent.get_new_thread()
|
||||
first_response = await first_agent.run("My hobby is photography. Remember this.", thread=thread, store=True)
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
|
||||
# Preserve the thread for reuse
|
||||
@@ -447,6 +447,6 @@ async def test_integration_client_agent_existing_thread():
|
||||
# Reuse the preserved thread
|
||||
second_response = await second_agent.run("What is my hobby?", thread=preserved_thread)
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
assert "photography" in second_response.text.lower()
|
||||
|
||||
@@ -13,8 +13,8 @@ from pytest import fixture
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseChatClient,
|
||||
ChatMessage,
|
||||
@@ -231,9 +231,9 @@ class MockAgent(AgentProtocol):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
logger.debug(f"Running mock agent, with: {messages=}, {thread=}, {kwargs=}")
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("Response")])])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent("Response")])])
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
@@ -241,9 +241,9 @@ class MockAgent(AgentProtocol):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
logger.debug(f"Running mock agent stream, with: {messages=}, {thread=}, {kwargs=}")
|
||||
yield AgentRunResponseUpdate(contents=[TextContent("Response")])
|
||||
yield AgentResponseUpdate(contents=[TextContent("Response")])
|
||||
|
||||
def get_new_thread(self) -> AgentThread:
|
||||
return MockAgentThread()
|
||||
|
||||
@@ -10,8 +10,8 @@ from pytest import raises
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatAgent,
|
||||
ChatClientProtocol,
|
||||
@@ -45,7 +45,7 @@ async def test_agent_run(agent: AgentProtocol) -> None:
|
||||
|
||||
|
||||
async def test_agent_run_streaming(agent: AgentProtocol) -> None:
|
||||
async def collect_updates(updates: AsyncIterable[AgentRunResponseUpdate]) -> list[AgentRunResponseUpdate]:
|
||||
async def collect_updates(updates: AsyncIterable[AgentResponseUpdate]) -> list[AgentResponseUpdate]:
|
||||
return [u async for u in updates]
|
||||
|
||||
updates = await collect_updates(agent.run_stream(messages="test"))
|
||||
@@ -87,7 +87,7 @@ async def test_chat_client_agent_run(chat_client: ChatClientProtocol) -> None:
|
||||
async def test_chat_client_agent_run_streaming(chat_client: ChatClientProtocol) -> None:
|
||||
agent = ChatAgent(chat_client=chat_client)
|
||||
|
||||
result = await AgentRunResponse.from_agent_response_generator(agent.run_stream("Hello"))
|
||||
result = await AgentResponse.from_agent_response_generator(agent.run_stream("Hello"))
|
||||
|
||||
assert result.text == "test streaming response another update"
|
||||
|
||||
@@ -329,7 +329,7 @@ async def test_chat_agent_run_stream_context_providers(chat_client: ChatClientPr
|
||||
agent = ChatAgent(chat_client=chat_client, context_provider=mock_provider)
|
||||
|
||||
# Collect all stream updates
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Hello"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -440,9 +440,9 @@ async def test_chat_agent_as_tool_with_stream_callback(chat_client: ChatClientPr
|
||||
agent = ChatAgent(chat_client=chat_client, name="StreamingAgent")
|
||||
|
||||
# Collect streaming updates
|
||||
collected_updates: list[AgentRunResponseUpdate] = []
|
||||
collected_updates: list[AgentResponseUpdate] = []
|
||||
|
||||
def stream_callback(update: AgentRunResponseUpdate) -> None:
|
||||
def stream_callback(update: AgentResponseUpdate) -> None:
|
||||
collected_updates.append(update)
|
||||
|
||||
tool = agent.as_tool(stream_callback=stream_callback)
|
||||
@@ -474,9 +474,9 @@ async def test_chat_agent_as_tool_with_async_stream_callback(chat_client: ChatCl
|
||||
agent = ChatAgent(chat_client=chat_client, name="AsyncStreamingAgent")
|
||||
|
||||
# Collect streaming updates using an async callback
|
||||
collected_updates: list[AgentRunResponseUpdate] = []
|
||||
collected_updates: list[AgentResponseUpdate] = []
|
||||
|
||||
async def async_stream_callback(update: AgentRunResponseUpdate) -> None:
|
||||
async def async_stream_callback(update: AgentResponseUpdate) -> None:
|
||||
collected_updates.append(update)
|
||||
|
||||
tool = agent.as_tool(stream_callback=async_stream_callback)
|
||||
|
||||
@@ -8,8 +8,8 @@ import pytest
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatAgent,
|
||||
HostedCodeInterpreterTool,
|
||||
@@ -46,7 +46,7 @@ async def test_openai_responses_client_agent_basic_run_streaming():
|
||||
# Test streaming run
|
||||
full_text = ""
|
||||
async for chunk in agent.run_stream("Please respond with exactly: 'This is a streaming response test.'"):
|
||||
assert isinstance(chunk, AgentRunResponseUpdate)
|
||||
assert isinstance(chunk, AgentResponseUpdate)
|
||||
if chunk.text:
|
||||
full_text += chunk.text
|
||||
|
||||
@@ -68,13 +68,13 @@ async def test_openai_responses_client_agent_thread_persistence():
|
||||
# First interaction
|
||||
first_response = await agent.run("My favorite programming language is Python. Remember this.", thread=thread)
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
|
||||
# Second interaction - test memory
|
||||
second_response = await agent.run("What is my favorite programming language?", thread=thread)
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ async def test_openai_responses_client_agent_thread_storage_with_store_true():
|
||||
)
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
@@ -125,7 +125,7 @@ async def test_openai_responses_client_agent_existing_thread():
|
||||
thread = first_agent.get_new_thread()
|
||||
first_response = await first_agent.run("My hobby is photography. Remember this.", thread=thread)
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
|
||||
# Preserve the thread for reuse
|
||||
@@ -140,7 +140,7 @@ async def test_openai_responses_client_agent_existing_thread():
|
||||
# Reuse the preserved thread
|
||||
second_response = await second_agent.run("What is my hobby?", thread=preserved_thread)
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
assert "photography" in second_response.text.lower()
|
||||
|
||||
@@ -157,7 +157,7 @@ async def test_openai_responses_client_agent_hosted_code_interpreter_tool():
|
||||
# Test code interpreter functionality
|
||||
response = await agent.run("Calculate the sum of numbers from 1 to 10 using Python code.")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
# Should contain calculation result (sum of 1-10 = 55) or code execution content
|
||||
@@ -179,7 +179,7 @@ async def test_openai_responses_client_agent_image_generation_tool():
|
||||
# Test image generation functionality
|
||||
response = await agent.run("Generate an image of a cute red panda sitting on a tree branch in a forest.")
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.messages
|
||||
|
||||
# Verify we got image content - look for ImageGenerationToolResultContent
|
||||
@@ -209,7 +209,7 @@ async def test_openai_responses_client_agent_level_tool_persistence():
|
||||
# First run - agent-level tool should be available
|
||||
first_response = await agent.run("What's the weather like in Chicago?")
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
# Should use the agent-level weather tool
|
||||
assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"])
|
||||
@@ -217,7 +217,7 @@ async def test_openai_responses_client_agent_level_tool_persistence():
|
||||
# Second run - agent-level tool should still be available (persistence test)
|
||||
second_response = await agent.run("What's the weather in Miami?")
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
# Should use the agent-level weather tool again
|
||||
assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"])
|
||||
@@ -249,7 +249,7 @@ async def test_openai_responses_client_run_level_tool_isolation():
|
||||
tools=[get_weather_with_counter], # Run-level tool
|
||||
)
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
# Should use the run-level weather tool (call count should be 1)
|
||||
assert call_count == 1
|
||||
@@ -258,7 +258,7 @@ async def test_openai_responses_client_run_level_tool_isolation():
|
||||
# Second run - run-level tool should NOT persist (key isolation test)
|
||||
second_response = await agent.run("What's the weather like in Miami?")
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
# Should NOT use the weather tool since it was only run-level in previous call
|
||||
# Call count should still be 1 (no additional calls)
|
||||
@@ -286,7 +286,7 @@ async def test_openai_responses_client_agent_chat_options_agent_level() -> None:
|
||||
"Provide a brief, helpful response.",
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
|
||||
@@ -312,7 +312,7 @@ async def test_openai_responses_client_agent_hosted_mcp_tool() -> None:
|
||||
options={"max_tokens": 5000},
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text
|
||||
# Should contain Azure-related content since it's asking about Azure CLI
|
||||
assert any(term in response.text.lower() for term in ["azure", "storage", "account", "cli"])
|
||||
@@ -338,7 +338,7 @@ async def test_openai_responses_client_agent_local_mcp_tool() -> None:
|
||||
options={"max_tokens": 200},
|
||||
)
|
||||
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
# Should contain Azure-related content since it's asking about Azure CLI
|
||||
@@ -375,7 +375,7 @@ async def test_openai_responses_client_agent_with_response_format_pydantic() ->
|
||||
)
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.value is not None
|
||||
assert isinstance(response.value, ReleaseBrief)
|
||||
|
||||
@@ -422,7 +422,7 @@ async def test_openai_responses_client_agent_with_runtime_json_schema() -> None:
|
||||
)
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
|
||||
# Parse JSON and validate structure
|
||||
|
||||
@@ -9,8 +9,8 @@ from pydantic import BaseModel, Field
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
@@ -172,9 +172,9 @@ class TestAgentMiddlewarePipeline:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
expected_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
expected_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
return expected_response
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
@@ -200,9 +200,9 @@ class TestAgentMiddlewarePipeline:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
expected_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
expected_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
execution_order.append("handler")
|
||||
return expected_response
|
||||
|
||||
@@ -216,11 +216,11 @@ class TestAgentMiddlewarePipeline:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in pipeline.execute_stream(mock_agent, messages, context, final_handler):
|
||||
updates.append(update)
|
||||
|
||||
@@ -248,13 +248,13 @@ class TestAgentMiddlewarePipeline:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
execution_order.append("handler_start")
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
execution_order.append("handler_end")
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in pipeline.execute_stream(mock_agent, messages, context, final_handler):
|
||||
updates.append(update)
|
||||
|
||||
@@ -271,10 +271,10 @@ class TestAgentMiddlewarePipeline:
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
execution_order: list[str] = []
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
# Handler should not be executed when terminated before next()
|
||||
execution_order.append("handler")
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
response = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
assert response is not None
|
||||
@@ -291,9 +291,9 @@ class TestAgentMiddlewarePipeline:
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
execution_order: list[str] = []
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
execution_order.append("handler")
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
response = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
assert response is not None
|
||||
@@ -310,14 +310,14 @@ class TestAgentMiddlewarePipeline:
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
execution_order: list[str] = []
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
# Handler should not be executed when terminated before next()
|
||||
execution_order.append("handler_start")
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
execution_order.append("handler_end")
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in pipeline.execute_stream(mock_agent, messages, context, final_handler):
|
||||
updates.append(update)
|
||||
|
||||
@@ -334,13 +334,13 @@ class TestAgentMiddlewarePipeline:
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
execution_order: list[str] = []
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
execution_order.append("handler_start")
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
execution_order.append("handler_end")
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in pipeline.execute_stream(mock_agent, messages, context, final_handler):
|
||||
updates.append(update)
|
||||
|
||||
@@ -370,9 +370,9 @@ class TestAgentMiddlewarePipeline:
|
||||
thread = AgentThread()
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages, thread=thread)
|
||||
|
||||
expected_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
expected_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
return expected_response
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
@@ -396,9 +396,9 @@ class TestAgentMiddlewarePipeline:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages, thread=None)
|
||||
|
||||
expected_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
expected_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
return expected_response
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
@@ -767,9 +767,9 @@ class TestClassBasedMiddleware:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
metadata_updates.append("handler")
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
@@ -830,9 +830,9 @@ class TestFunctionBasedMiddleware:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
execution_order.append("handler")
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
@@ -893,9 +893,9 @@ class TestMixedMiddleware:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
execution_order.append("handler")
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
@@ -1004,9 +1004,9 @@ class TestMultipleMiddlewareOrdering:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
execution_order.append("handler")
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
@@ -1142,10 +1142,10 @@ class TestContextContentValidation:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
# Verify metadata was set by middleware
|
||||
assert ctx.metadata.get("validated") is True
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
assert result is not None
|
||||
@@ -1253,20 +1253,20 @@ class TestStreamingScenarios:
|
||||
# Test non-streaming
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
streaming_flags.append(ctx.is_streaming)
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response")])
|
||||
|
||||
await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
# Test streaming
|
||||
context_stream = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_stream_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def final_stream_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
streaming_flags.append(ctx.is_streaming)
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk")])
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in pipeline.execute_stream(mock_agent, messages, context_stream, final_stream_handler):
|
||||
updates.append(update)
|
||||
|
||||
@@ -1290,11 +1290,11 @@ class TestStreamingScenarios:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_stream_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def final_stream_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
chunks_processed.append("stream_start")
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk1")])
|
||||
chunks_processed.append("chunk1_yielded")
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="chunk2")])
|
||||
chunks_processed.append("chunk2_yielded")
|
||||
chunks_processed.append("stream_end")
|
||||
|
||||
@@ -1452,16 +1452,16 @@ class TestMiddlewareExecutionControl:
|
||||
|
||||
handler_called = False
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
nonlocal handler_called
|
||||
handler_called = True
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="should not execute")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="should not execute")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
# Verify no execution happened - should return empty AgentRunResponse
|
||||
# Verify no execution happened - should return empty AgentResponse
|
||||
assert result is not None
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.messages == [] # Empty response
|
||||
assert not handler_called
|
||||
assert context.result is None
|
||||
@@ -1483,13 +1483,13 @@ class TestMiddlewareExecutionControl:
|
||||
|
||||
handler_called = False
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
nonlocal handler_called
|
||||
handler_called = True
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="should not execute")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="should not execute")])
|
||||
|
||||
# When middleware doesn't call next(), streaming should yield no updates
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in pipeline.execute_stream(mock_agent, messages, context, final_handler):
|
||||
updates.append(update)
|
||||
|
||||
@@ -1556,17 +1556,17 @@ class TestMiddlewareExecutionControl:
|
||||
|
||||
handler_called = False
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
nonlocal handler_called
|
||||
handler_called = True
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="should not execute")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="should not execute")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
# Verify only first middleware was called and empty response returned
|
||||
assert execution_order == ["first"]
|
||||
assert result is not None
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert result.messages == [] # Empty response
|
||||
assert not handler_called
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ from pydantic import BaseModel, Field
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Role,
|
||||
@@ -40,7 +40,7 @@ class TestResultOverrideMiddleware:
|
||||
|
||||
async def test_agent_middleware_response_override_non_streaming(self, mock_agent: AgentProtocol) -> None:
|
||||
"""Test that agent middleware can override response for non-streaming execution."""
|
||||
override_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="overridden response")])
|
||||
override_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="overridden response")])
|
||||
|
||||
class ResponseOverrideMiddleware(AgentMiddleware):
|
||||
async def process(
|
||||
@@ -57,10 +57,10 @@ class TestResultOverrideMiddleware:
|
||||
|
||||
handler_called = False
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
nonlocal handler_called
|
||||
handler_called = True
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="original response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="original response")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
@@ -74,9 +74,9 @@ class TestResultOverrideMiddleware:
|
||||
async def test_agent_middleware_response_override_streaming(self, mock_agent: AgentProtocol) -> None:
|
||||
"""Test that agent middleware can override response for streaming execution."""
|
||||
|
||||
async def override_stream() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="overridden")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=" stream")])
|
||||
async def override_stream() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="overridden")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=" stream")])
|
||||
|
||||
class StreamResponseOverrideMiddleware(AgentMiddleware):
|
||||
async def process(
|
||||
@@ -91,10 +91,10 @@ class TestResultOverrideMiddleware:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="original")])
|
||||
async def final_handler(ctx: AgentRunContext) -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="original")])
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in pipeline.execute_stream(mock_agent, messages, context, final_handler):
|
||||
updates.append(update)
|
||||
|
||||
@@ -148,7 +148,7 @@ class TestResultOverrideMiddleware:
|
||||
await next(context)
|
||||
# Then conditionally override based on content
|
||||
if any("special" in msg.text for msg in context.messages if msg.text):
|
||||
context.result = AgentRunResponse(
|
||||
context.result = AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="Special response from middleware!")]
|
||||
)
|
||||
|
||||
@@ -174,10 +174,10 @@ class TestResultOverrideMiddleware:
|
||||
"""Test streaming result override functionality with ChatAgent integration."""
|
||||
mock_chat_client = MockChatClient()
|
||||
|
||||
async def custom_stream() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text="Custom")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=" streaming")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=" response!")])
|
||||
async def custom_stream() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(contents=[TextContent(text="Custom")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=" streaming")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=" response!")])
|
||||
|
||||
class ChatAgentStreamOverrideMiddleware(AgentMiddleware):
|
||||
async def process(
|
||||
@@ -195,7 +195,7 @@ class TestResultOverrideMiddleware:
|
||||
|
||||
# Test streaming override case
|
||||
override_messages = [ChatMessage(role=Role.USER, text="Give me a custom stream")]
|
||||
override_updates: list[AgentRunResponseUpdate] = []
|
||||
override_updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream(override_messages):
|
||||
override_updates.append(update)
|
||||
|
||||
@@ -206,7 +206,7 @@ class TestResultOverrideMiddleware:
|
||||
|
||||
# Test normal streaming case
|
||||
normal_messages = [ChatMessage(role=Role.USER, text="Normal streaming request")]
|
||||
normal_updates: list[AgentRunResponseUpdate] = []
|
||||
normal_updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream(normal_messages):
|
||||
normal_updates.append(update)
|
||||
|
||||
@@ -231,19 +231,19 @@ class TestResultOverrideMiddleware:
|
||||
|
||||
handler_called = False
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
nonlocal handler_called
|
||||
handler_called = True
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="executed response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="executed response")])
|
||||
|
||||
# Test case where next() is NOT called
|
||||
no_execute_messages = [ChatMessage(role=Role.USER, text="Don't run this")]
|
||||
no_execute_context = AgentRunContext(agent=mock_agent, messages=no_execute_messages)
|
||||
no_execute_result = await pipeline.execute(mock_agent, no_execute_messages, no_execute_context, final_handler)
|
||||
|
||||
# When middleware doesn't call next(), result should be empty AgentRunResponse
|
||||
# When middleware doesn't call next(), result should be empty AgentResponse
|
||||
assert no_execute_result is not None
|
||||
assert isinstance(no_execute_result, AgentRunResponse)
|
||||
assert isinstance(no_execute_result, AgentResponse)
|
||||
assert no_execute_result.messages == [] # Empty response
|
||||
assert not handler_called
|
||||
assert no_execute_context.result is None
|
||||
@@ -313,7 +313,7 @@ class TestResultObservability:
|
||||
|
||||
async def test_agent_middleware_response_observability(self, mock_agent: AgentProtocol) -> None:
|
||||
"""Test that middleware can observe response after execution."""
|
||||
observed_responses: list[AgentRunResponse] = []
|
||||
observed_responses: list[AgentResponse] = []
|
||||
|
||||
class ObservabilityMiddleware(AgentMiddleware):
|
||||
async def process(
|
||||
@@ -327,7 +327,7 @@ class TestResultObservability:
|
||||
|
||||
# Context should now contain the response for observability
|
||||
assert context.result is not None
|
||||
assert isinstance(context.result, AgentRunResponse)
|
||||
assert isinstance(context.result, AgentResponse)
|
||||
observed_responses.append(context.result)
|
||||
|
||||
middleware = ObservabilityMiddleware()
|
||||
@@ -335,8 +335,8 @@ class TestResultObservability:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="executed response")])
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="executed response")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
@@ -392,11 +392,11 @@ class TestResultObservability:
|
||||
|
||||
# Now observe and conditionally override
|
||||
assert context.result is not None
|
||||
assert isinstance(context.result, AgentRunResponse)
|
||||
assert isinstance(context.result, AgentResponse)
|
||||
|
||||
if "modify" in context.result.messages[0].text:
|
||||
# Override after observing
|
||||
context.result = AgentRunResponse(
|
||||
context.result = AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="modified after execution")]
|
||||
)
|
||||
|
||||
@@ -405,8 +405,8 @@ class TestResultObservability:
|
||||
messages = [ChatMessage(role=Role.USER, text="test")]
|
||||
context = AgentRunContext(agent=mock_agent, messages=messages)
|
||||
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response to modify")])
|
||||
async def final_handler(ctx: AgentRunContext) -> AgentResponse:
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="response to modify")])
|
||||
|
||||
result = await pipeline.execute(mock_agent, messages, context, final_handler)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Any
|
||||
import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatContext,
|
||||
ChatMessage,
|
||||
@@ -372,7 +372,7 @@ class TestChatAgentStreamingMiddleware:
|
||||
|
||||
# Execute streaming
|
||||
messages = [ChatMessage(role=Role.USER, text="test message")]
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream(messages):
|
||||
updates.append(update)
|
||||
|
||||
@@ -878,7 +878,7 @@ class TestMiddlewareDynamicRebuild:
|
||||
agent = ChatAgent(chat_client=chat_client, middleware=[middleware1])
|
||||
|
||||
# First streaming execution
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Test stream message 1"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -1085,7 +1085,7 @@ class TestRunLevelMiddleware:
|
||||
run_middleware = StreamingTrackingMiddleware("run_stream")
|
||||
|
||||
# Execute streaming with run middleware
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Test streaming", middleware=[run_middleware]):
|
||||
updates.append(update)
|
||||
|
||||
@@ -1711,7 +1711,7 @@ class TestChatAgentChatMiddleware:
|
||||
|
||||
# Execute streaming
|
||||
messages = [ChatMessage(role=Role.USER, text="test message")]
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream(messages):
|
||||
updates.append(update)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from opentelemetry.trace import StatusCode
|
||||
from agent_framework import (
|
||||
AGENT_FRAMEWORK_USER_AGENT,
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
AgentThread,
|
||||
BaseChatClient,
|
||||
ChatMessage,
|
||||
@@ -407,7 +407,7 @@ def mock_chat_agent():
|
||||
self.default_options: dict[str, Any] = {"model_id": "TestModel"}
|
||||
|
||||
async def run(self, messages=None, *, thread=None, **kwargs):
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="Agent response")],
|
||||
usage_details=UsageDetails(input_token_count=15, output_token_count=25),
|
||||
response_id="test_response_id",
|
||||
@@ -415,10 +415,10 @@ def mock_chat_agent():
|
||||
)
|
||||
|
||||
async def run_stream(self, messages=None, *, thread=None, **kwargs):
|
||||
from agent_framework import AgentRunResponseUpdate
|
||||
from agent_framework import AgentResponseUpdate
|
||||
|
||||
yield AgentRunResponseUpdate(text="Hello", role=Role.ASSISTANT)
|
||||
yield AgentRunResponseUpdate(text=" from agent", role=Role.ASSISTANT)
|
||||
yield AgentResponseUpdate(text="Hello", role=Role.ASSISTANT)
|
||||
yield AgentResponseUpdate(text=" from agent", role=Role.ASSISTANT)
|
||||
|
||||
return MockChatClientAgent
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ from pydantic import BaseModel
|
||||
from pytest import fixture, mark, raises
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
BaseContent,
|
||||
ChatMessage,
|
||||
ChatOptions,
|
||||
@@ -1026,90 +1026,90 @@ def text_content() -> TextContent:
|
||||
|
||||
|
||||
@fixture
|
||||
def agent_run_response(chat_message: ChatMessage) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=chat_message)
|
||||
def agent_response(chat_message: ChatMessage) -> AgentResponse:
|
||||
return AgentResponse(messages=chat_message)
|
||||
|
||||
|
||||
@fixture
|
||||
def agent_run_response_update(text_content: TextContent) -> AgentRunResponseUpdate:
|
||||
return AgentRunResponseUpdate(role=Role.ASSISTANT, contents=[text_content])
|
||||
def agent_response_update(text_content: TextContent) -> AgentResponseUpdate:
|
||||
return AgentResponseUpdate(role=Role.ASSISTANT, contents=[text_content])
|
||||
|
||||
|
||||
# region AgentRunResponse
|
||||
# region AgentResponse
|
||||
|
||||
|
||||
def test_agent_run_response_init_single_message(chat_message: ChatMessage) -> None:
|
||||
response = AgentRunResponse(messages=chat_message)
|
||||
response = AgentResponse(messages=chat_message)
|
||||
assert response.messages == [chat_message]
|
||||
|
||||
|
||||
def test_agent_run_response_init_list_messages(chat_message: ChatMessage) -> None:
|
||||
response = AgentRunResponse(messages=[chat_message, chat_message])
|
||||
response = AgentResponse(messages=[chat_message, chat_message])
|
||||
assert len(response.messages) == 2
|
||||
assert response.messages[0] == chat_message
|
||||
|
||||
|
||||
def test_agent_run_response_init_none_messages() -> None:
|
||||
response = AgentRunResponse()
|
||||
response = AgentResponse()
|
||||
assert response.messages == []
|
||||
|
||||
|
||||
def test_agent_run_response_text_property(chat_message: ChatMessage) -> None:
|
||||
response = AgentRunResponse(messages=[chat_message, chat_message])
|
||||
response = AgentResponse(messages=[chat_message, chat_message])
|
||||
assert response.text == "HelloHello"
|
||||
|
||||
|
||||
def test_agent_run_response_text_property_empty() -> None:
|
||||
response = AgentRunResponse()
|
||||
response = AgentResponse()
|
||||
assert response.text == ""
|
||||
|
||||
|
||||
def test_agent_run_response_from_updates(agent_run_response_update: AgentRunResponseUpdate) -> None:
|
||||
updates = [agent_run_response_update, agent_run_response_update]
|
||||
response = AgentRunResponse.from_agent_run_response_updates(updates)
|
||||
def test_agent_run_response_from_updates(agent_response_update: AgentResponseUpdate) -> None:
|
||||
updates = [agent_response_update, agent_response_update]
|
||||
response = AgentResponse.from_agent_run_response_updates(updates)
|
||||
assert len(response.messages) > 0
|
||||
assert response.text == "Test contentTest content"
|
||||
|
||||
|
||||
def test_agent_run_response_str_method(chat_message: ChatMessage) -> None:
|
||||
response = AgentRunResponse(messages=chat_message)
|
||||
response = AgentResponse(messages=chat_message)
|
||||
assert str(response) == "Hello"
|
||||
|
||||
|
||||
# region AgentRunResponseUpdate
|
||||
# region AgentResponseUpdate
|
||||
|
||||
|
||||
def test_agent_run_response_update_init_content_list(text_content: TextContent) -> None:
|
||||
update = AgentRunResponseUpdate(contents=[text_content, text_content])
|
||||
update = AgentResponseUpdate(contents=[text_content, text_content])
|
||||
assert len(update.contents) == 2
|
||||
assert update.contents[0] == text_content
|
||||
|
||||
|
||||
def test_agent_run_response_update_init_none_content() -> None:
|
||||
update = AgentRunResponseUpdate()
|
||||
update = AgentResponseUpdate()
|
||||
assert update.contents == []
|
||||
|
||||
|
||||
def test_agent_run_response_update_text_property(text_content: TextContent) -> None:
|
||||
update = AgentRunResponseUpdate(contents=[text_content, text_content])
|
||||
update = AgentResponseUpdate(contents=[text_content, text_content])
|
||||
assert update.text == "Test contentTest content"
|
||||
|
||||
|
||||
def test_agent_run_response_update_text_property_empty() -> None:
|
||||
update = AgentRunResponseUpdate()
|
||||
update = AgentResponseUpdate()
|
||||
assert update.text == ""
|
||||
|
||||
|
||||
def test_agent_run_response_update_str_method(text_content: TextContent) -> None:
|
||||
update = AgentRunResponseUpdate(contents=[text_content])
|
||||
update = AgentResponseUpdate(contents=[text_content])
|
||||
assert str(update) == "Test content"
|
||||
|
||||
|
||||
def test_agent_run_response_update_created_at() -> None:
|
||||
"""Test that AgentRunResponseUpdate properly handles created_at timestamps."""
|
||||
"""Test that AgentResponseUpdate properly handles created_at timestamps."""
|
||||
# Test with a properly formatted UTC timestamp
|
||||
utc_timestamp = "2024-12-01T00:31:30.000000Z"
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="test")],
|
||||
role=Role.ASSISTANT,
|
||||
created_at=utc_timestamp,
|
||||
@@ -1120,7 +1120,7 @@ def test_agent_run_response_update_created_at() -> None:
|
||||
# Verify that we can generate a proper UTC timestamp
|
||||
now_utc = datetime.now(tz=timezone.utc)
|
||||
formatted_utc = now_utc.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
update_with_now = AgentRunResponseUpdate(
|
||||
update_with_now = AgentResponseUpdate(
|
||||
contents=[TextContent(text="test")],
|
||||
role=Role.ASSISTANT,
|
||||
created_at=formatted_utc,
|
||||
@@ -1130,10 +1130,10 @@ def test_agent_run_response_update_created_at() -> None:
|
||||
|
||||
|
||||
def test_agent_run_response_created_at() -> None:
|
||||
"""Test that AgentRunResponse properly handles created_at timestamps."""
|
||||
"""Test that AgentResponse properly handles created_at timestamps."""
|
||||
# Test with a properly formatted UTC timestamp
|
||||
utc_timestamp = "2024-12-01T00:31:30.000000Z"
|
||||
response = AgentRunResponse(
|
||||
response = AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="Hello")],
|
||||
created_at=utc_timestamp,
|
||||
)
|
||||
@@ -1143,7 +1143,7 @@ def test_agent_run_response_created_at() -> None:
|
||||
# Verify that we can generate a proper UTC timestamp
|
||||
now_utc = datetime.now(tz=timezone.utc)
|
||||
formatted_utc = now_utc.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
response_with_now = AgentRunResponse(
|
||||
response_with_now = AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="Hello")],
|
||||
created_at=formatted_utc,
|
||||
)
|
||||
@@ -1285,20 +1285,20 @@ def test_chat_tool_mode_eq_with_string():
|
||||
assert {"mode": "auto"} == {"mode": "auto"}
|
||||
|
||||
|
||||
# region AgentRunResponse
|
||||
# region AgentResponse
|
||||
|
||||
|
||||
@fixture
|
||||
def agent_run_response_async() -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[ChatMessage(role="user", text="Hello")])
|
||||
def agent_run_response_async() -> AgentResponse:
|
||||
return AgentResponse(messages=[ChatMessage(role="user", text="Hello")])
|
||||
|
||||
|
||||
async def test_agent_run_response_from_async_generator():
|
||||
async def gen():
|
||||
yield AgentRunResponseUpdate(contents=[TextContent("A")])
|
||||
yield AgentRunResponseUpdate(contents=[TextContent("B")])
|
||||
yield AgentResponseUpdate(contents=[TextContent("A")])
|
||||
yield AgentResponseUpdate(contents=[TextContent("B")])
|
||||
|
||||
r = await AgentRunResponse.from_agent_response_generator(gen())
|
||||
r = await AgentResponse.from_agent_response_generator(gen())
|
||||
assert r.text == "AB"
|
||||
|
||||
|
||||
@@ -1668,7 +1668,7 @@ def test_chat_response_update_all_content_types():
|
||||
|
||||
|
||||
def test_agent_run_response_complex_serialization():
|
||||
"""Test AgentRunResponse from_dict and to_dict with messages and usage_details."""
|
||||
"""Test AgentResponse from_dict and to_dict with messages and usage_details."""
|
||||
|
||||
response_data = {
|
||||
"messages": [
|
||||
@@ -1683,7 +1683,7 @@ def test_agent_run_response_complex_serialization():
|
||||
},
|
||||
}
|
||||
|
||||
response = AgentRunResponse.from_dict(response_data)
|
||||
response = AgentResponse.from_dict(response_data)
|
||||
assert len(response.messages) == 2
|
||||
assert isinstance(response.messages[0], ChatMessage)
|
||||
assert isinstance(response.usage_details, UsageDetails)
|
||||
@@ -1696,7 +1696,7 @@ def test_agent_run_response_complex_serialization():
|
||||
|
||||
|
||||
def test_agent_run_response_update_all_content_types():
|
||||
"""Test AgentRunResponseUpdate from_dict with all content types and role handling."""
|
||||
"""Test AgentResponseUpdate from_dict with all content types and role handling."""
|
||||
|
||||
update_data = {
|
||||
"contents": [
|
||||
@@ -1725,7 +1725,7 @@ def test_agent_run_response_update_all_content_types():
|
||||
"role": {"value": "assistant"}, # Test role as dict
|
||||
}
|
||||
|
||||
update = AgentRunResponseUpdate.from_dict(update_data)
|
||||
update = AgentResponseUpdate.from_dict(update_data)
|
||||
assert len(update.contents) == 12 # unknown_type is logged and ignored
|
||||
assert isinstance(update.role, Role)
|
||||
assert update.role.value == "assistant"
|
||||
@@ -1738,7 +1738,7 @@ def test_agent_run_response_update_all_content_types():
|
||||
# Test role as string conversion
|
||||
update_data_str_role = update_data.copy()
|
||||
update_data_str_role["role"] = "user"
|
||||
update_str = AgentRunResponseUpdate.from_dict(update_data_str_role)
|
||||
update_str = AgentResponseUpdate.from_dict(update_data_str_role)
|
||||
assert isinstance(update_str.role, Role)
|
||||
assert update_str.role.value == "user"
|
||||
|
||||
@@ -1922,7 +1922,7 @@ def test_agent_run_response_update_all_content_types():
|
||||
id="chat_response_update",
|
||||
),
|
||||
pytest.param(
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
@@ -1942,10 +1942,10 @@ def test_agent_run_response_update_all_content_types():
|
||||
"total_token_count": 8,
|
||||
},
|
||||
},
|
||||
id="agent_run_response",
|
||||
id="agent_response",
|
||||
),
|
||||
pytest.param(
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponseUpdate,
|
||||
{
|
||||
"contents": [
|
||||
{"type": "text", "text": "Streaming"},
|
||||
@@ -1956,7 +1956,7 @@ def test_agent_run_response_update_all_content_types():
|
||||
"response_id": "run-123",
|
||||
"author_name": "Agent",
|
||||
},
|
||||
id="agent_run_response_update",
|
||||
id="agent_response_update",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -11,8 +11,8 @@ from openai.types.beta.threads.runs import RunStep
|
||||
from pydantic import Field
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatAgent,
|
||||
ChatClientProtocol,
|
||||
@@ -1118,7 +1118,7 @@ async def test_openai_assistants_agent_basic_run():
|
||||
response = await agent.run("Hello! Please respond with 'Hello World' exactly.")
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
assert len(response.text) > 0
|
||||
assert "Hello World" in response.text
|
||||
@@ -1135,7 +1135,7 @@ async def test_openai_assistants_agent_basic_run_streaming():
|
||||
full_message: str = ""
|
||||
async for chunk in agent.run_stream("Please respond with exactly: 'This is a streaming response test.'"):
|
||||
assert chunk is not None
|
||||
assert isinstance(chunk, AgentRunResponseUpdate)
|
||||
assert isinstance(chunk, AgentResponseUpdate)
|
||||
if chunk.text:
|
||||
full_message += chunk.text
|
||||
|
||||
@@ -1159,14 +1159,14 @@ async def test_openai_assistants_agent_thread_persistence():
|
||||
first_response = await agent.run(
|
||||
"Remember this number: 42. What number did I just tell you to remember?", thread=thread
|
||||
)
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert "42" in first_response.text
|
||||
|
||||
# Second message - test conversation memory
|
||||
second_response = await agent.run(
|
||||
"What number did I tell you to remember in my previous message?", thread=thread
|
||||
)
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert "42" in second_response.text
|
||||
|
||||
# Verify thread has been populated with conversation ID
|
||||
@@ -1190,7 +1190,7 @@ async def test_openai_assistants_agent_existing_thread_id():
|
||||
response1 = await agent.run("What's the weather in Paris?", thread=thread)
|
||||
|
||||
# Validate first response
|
||||
assert isinstance(response1, AgentRunResponse)
|
||||
assert isinstance(response1, AgentResponse)
|
||||
assert response1.text is not None
|
||||
assert any(word in response1.text.lower() for word in ["weather", "paris"])
|
||||
|
||||
@@ -1212,7 +1212,7 @@ async def test_openai_assistants_agent_existing_thread_id():
|
||||
response2 = await agent.run("What was the last city I asked about?", thread=thread)
|
||||
|
||||
# Validate that the agent remembers the previous conversation
|
||||
assert isinstance(response2, AgentRunResponse)
|
||||
assert isinstance(response2, AgentResponse)
|
||||
assert response2.text is not None
|
||||
# Should reference Paris from the previous conversation
|
||||
assert "paris" in response2.text.lower()
|
||||
@@ -1232,7 +1232,7 @@ async def test_openai_assistants_agent_code_interpreter():
|
||||
response = await agent.run("Write Python code to calculate the factorial of 5 and show the result.")
|
||||
|
||||
# Validate response
|
||||
assert isinstance(response, AgentRunResponse)
|
||||
assert isinstance(response, AgentResponse)
|
||||
assert response.text is not None
|
||||
# Factorial of 5 is 120
|
||||
assert "120" in response.text or "factorial" in response.text.lower()
|
||||
@@ -1251,7 +1251,7 @@ async def test_agent_level_tool_persistence():
|
||||
# First run - agent-level tool should be available
|
||||
first_response = await agent.run("What's the weather like in Chicago?")
|
||||
|
||||
assert isinstance(first_response, AgentRunResponse)
|
||||
assert isinstance(first_response, AgentResponse)
|
||||
assert first_response.text is not None
|
||||
# Should use the agent-level weather tool
|
||||
assert any(term in first_response.text.lower() for term in ["chicago", "sunny", "72"])
|
||||
@@ -1259,7 +1259,7 @@ async def test_agent_level_tool_persistence():
|
||||
# Second run - agent-level tool should still be available (persistence test)
|
||||
second_response = await agent.run("What's the weather in Miami?")
|
||||
|
||||
assert isinstance(second_response, AgentRunResponse)
|
||||
assert isinstance(second_response, AgentResponse)
|
||||
assert second_response.text is not None
|
||||
# Should use the agent-level weather tool again
|
||||
assert any(term in second_response.text.lower() for term in ["miami", "sunny", "72"])
|
||||
|
||||
@@ -5,8 +5,8 @@ from typing import Any
|
||||
|
||||
from agent_framework import (
|
||||
AgentExecutor,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
ChatMessage,
|
||||
@@ -35,9 +35,9 @@ class _CountingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
self.call_count += 1
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text=f"Response #{self.call_count}: {self.name}")]
|
||||
)
|
||||
|
||||
@@ -47,9 +47,9 @@ class _CountingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
self.call_count += 1
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=f"Response #{self.call_count}: {self.name}")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=f"Response #{self.call_count}: {self.name}")])
|
||||
|
||||
|
||||
async def test_agent_executor_checkpoint_stores_and_restores_state() -> None:
|
||||
|
||||
@@ -10,8 +10,8 @@ from typing_extensions import Never
|
||||
from agent_framework import (
|
||||
AgentExecutor,
|
||||
AgentExecutorResponse,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentRunUpdateEvent,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
@@ -46,9 +46,9 @@ class _ToolCallingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Non-streaming run - not used in this test."""
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="done")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="done")])
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
@@ -56,16 +56,16 @@ class _ToolCallingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Simulate streaming with tool calls and results."""
|
||||
# First update: some text
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text="Let me search for that...")],
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
|
||||
# Second update: tool call (no text!)
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(
|
||||
call_id="call_123",
|
||||
@@ -77,7 +77,7 @@ class _ToolCallingAgent(BaseAgent):
|
||||
)
|
||||
|
||||
# Third update: tool result (no text!)
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionResultContent(
|
||||
call_id="call_123",
|
||||
@@ -88,7 +88,7 @@ class _ToolCallingAgent(BaseAgent):
|
||||
)
|
||||
|
||||
# Fourth update: final text response
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text="The weather is sunny, 72°F.")],
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
@@ -223,7 +223,7 @@ class MockChatClient:
|
||||
|
||||
@executor(id="test_executor")
|
||||
async def test_executor(agent_executor_response: AgentExecutorResponse, ctx: WorkflowContext[Never, str]) -> None:
|
||||
await ctx.yield_output(agent_executor_response.agent_run_response.text)
|
||||
await ctx.yield_output(agent_executor_response.agent_response.text)
|
||||
|
||||
|
||||
async def test_agent_executor_tool_call_with_approval() -> None:
|
||||
|
||||
@@ -2,26 +2,26 @@
|
||||
|
||||
"""Tests for AgentRunEvent and AgentRunUpdateEvent type annotations."""
|
||||
|
||||
from agent_framework import AgentRunResponse, AgentRunResponseUpdate, ChatMessage, Role
|
||||
from agent_framework import AgentResponse, AgentResponseUpdate, ChatMessage, Role
|
||||
from agent_framework._workflows._events import AgentRunEvent, AgentRunUpdateEvent
|
||||
|
||||
|
||||
def test_agent_run_event_data_type() -> None:
|
||||
"""Verify AgentRunEvent.data is typed as AgentRunResponse | None."""
|
||||
response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Hello")])
|
||||
"""Verify AgentRunEvent.data is typed as AgentResponse | None."""
|
||||
response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Hello")])
|
||||
event = AgentRunEvent(executor_id="test", data=response)
|
||||
|
||||
# This assignment should pass type checking without a cast
|
||||
data: AgentRunResponse | None = event.data
|
||||
data: AgentResponse | None = event.data
|
||||
assert data is not None
|
||||
assert data.text == "Hello"
|
||||
|
||||
|
||||
def test_agent_run_update_event_data_type() -> None:
|
||||
"""Verify AgentRunUpdateEvent.data is typed as AgentRunResponseUpdate | None."""
|
||||
update = AgentRunResponseUpdate()
|
||||
"""Verify AgentRunUpdateEvent.data is typed as AgentResponseUpdate | None."""
|
||||
update = AgentResponseUpdate()
|
||||
event = AgentRunUpdateEvent(executor_id="test", data=update)
|
||||
|
||||
# This assignment should pass type checking without a cast
|
||||
data: AgentRunResponseUpdate | None = event.data
|
||||
data: AgentResponseUpdate | None = event.data
|
||||
assert data is not None
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from collections.abc import AsyncIterable
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import AgentRunResponse, AgentRunResponseUpdate, AgentThread, ChatMessage
|
||||
from agent_framework import AgentResponse, AgentResponseUpdate, AgentThread, ChatMessage
|
||||
from agent_framework._workflows._agent_utils import resolve_agent_id
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class MockAgent:
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse: ...
|
||||
) -> AgentResponse: ...
|
||||
|
||||
def run_stream(
|
||||
self,
|
||||
@@ -46,7 +46,7 @@ class MockAgent:
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]: ...
|
||||
) -> AsyncIterable[AgentResponseUpdate]: ...
|
||||
|
||||
def get_new_thread(self, **kwargs: Any) -> AgentThread:
|
||||
"""Creates a new conversation thread for the agent."""
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing_extensions import Never
|
||||
from agent_framework import (
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
ChatMessage,
|
||||
ConcurrentBuilder,
|
||||
Executor,
|
||||
@@ -36,7 +36,7 @@ class _FakeAgentExec(Executor):
|
||||
|
||||
@handler
|
||||
async def run(self, request: AgentExecutorRequest, ctx: WorkflowContext[AgentExecutorResponse]) -> None:
|
||||
response = AgentRunResponse(messages=ChatMessage(Role.ASSISTANT, text=self._reply_text))
|
||||
response = AgentResponse(messages=ChatMessage(Role.ASSISTANT, text=self._reply_text))
|
||||
full_conversation = list(request.messages) + list(response.messages)
|
||||
await ctx.send_message(AgentExecutorResponse(self.id, response, full_conversation=full_conversation))
|
||||
|
||||
@@ -142,7 +142,7 @@ async def test_concurrent_custom_aggregator_callback_is_used() -> None:
|
||||
async def summarize(results: list[AgentExecutorResponse]) -> str:
|
||||
texts: list[str] = []
|
||||
for r in results:
|
||||
msgs: list[ChatMessage] = r.agent_run_response.messages
|
||||
msgs: list[ChatMessage] = r.agent_response.messages
|
||||
texts.append(msgs[-1].text if msgs else "")
|
||||
return " | ".join(sorted(texts))
|
||||
|
||||
@@ -173,7 +173,7 @@ async def test_concurrent_custom_aggregator_sync_callback_is_used() -> None:
|
||||
def summarize_sync(results: list[AgentExecutorResponse], _ctx: WorkflowContext[Any]) -> str: # type: ignore[unused-argument]
|
||||
texts: list[str] = []
|
||||
for r in results:
|
||||
msgs: list[ChatMessage] = r.agent_run_response.messages
|
||||
msgs: list[ChatMessage] = r.agent_response.messages
|
||||
texts.append(msgs[-1].text if msgs else "")
|
||||
return " | ".join(sorted(texts))
|
||||
|
||||
@@ -217,7 +217,7 @@ async def test_concurrent_with_aggregator_executor_instance() -> None:
|
||||
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext[Never, str]) -> None:
|
||||
texts: list[str] = []
|
||||
for r in results:
|
||||
msgs: list[ChatMessage] = r.agent_run_response.messages
|
||||
msgs: list[ChatMessage] = r.agent_response.messages
|
||||
texts.append(msgs[-1].text if msgs else "")
|
||||
await ctx.yield_output(" & ".join(sorted(texts)))
|
||||
|
||||
@@ -251,7 +251,7 @@ async def test_concurrent_with_aggregator_executor_factory() -> None:
|
||||
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext[Never, str]) -> None:
|
||||
texts: list[str] = []
|
||||
for r in results:
|
||||
msgs: list[ChatMessage] = r.agent_run_response.messages
|
||||
msgs: list[ChatMessage] = r.agent_response.messages
|
||||
texts.append(msgs[-1].text if msgs else "")
|
||||
await ctx.yield_output(" | ".join(sorted(texts)))
|
||||
|
||||
@@ -292,7 +292,7 @@ async def test_concurrent_with_aggregator_executor_factory_with_default_id() ->
|
||||
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext[Never, str]) -> None:
|
||||
texts: list[str] = []
|
||||
for r in results:
|
||||
msgs: list[ChatMessage] = r.agent_run_response.messages
|
||||
msgs: list[ChatMessage] = r.agent_response.messages
|
||||
texts.append(msgs[-1].text if msgs else "")
|
||||
await ctx.yield_output(" | ".join(sorted(texts)))
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ from typing_extensions import Never
|
||||
from agent_framework import (
|
||||
AgentExecutor,
|
||||
AgentExecutorResponse,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
ChatMessage,
|
||||
@@ -39,8 +39,8 @@ class _SimpleAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=self._reply_text)])
|
||||
) -> AgentResponse:
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=self._reply_text)])
|
||||
|
||||
async def run_stream( # type: ignore[override]
|
||||
self,
|
||||
@@ -48,9 +48,9 @@ class _SimpleAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
# This agent does not support streaming; yield a single complete response
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=self._reply_text)])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=self._reply_text)])
|
||||
|
||||
|
||||
class _CaptureFullConversation(Executor):
|
||||
@@ -108,7 +108,7 @@ class _CaptureAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
# Normalize and record messages for verification when running non-streaming
|
||||
norm: list[ChatMessage] = []
|
||||
if messages:
|
||||
@@ -118,7 +118,7 @@ class _CaptureAgent(BaseAgent):
|
||||
elif isinstance(m, str):
|
||||
norm.append(ChatMessage(role=Role.USER, text=m))
|
||||
self._last_messages = norm
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=self._reply_text)])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=self._reply_text)])
|
||||
|
||||
async def run_stream( # type: ignore[override]
|
||||
self,
|
||||
@@ -126,7 +126,7 @@ class _CaptureAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
# Normalize and record messages for verification when running streaming
|
||||
norm: list[ChatMessage] = []
|
||||
if messages:
|
||||
@@ -136,7 +136,7 @@ class _CaptureAgent(BaseAgent):
|
||||
elif isinstance(m, str):
|
||||
norm.append(ChatMessage(role=Role.USER, text=m))
|
||||
self._last_messages = norm
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=self._reply_text)])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=self._reply_text)])
|
||||
|
||||
|
||||
async def test_sequential_adapter_uses_full_conversation() -> None:
|
||||
|
||||
@@ -8,8 +8,8 @@ import pytest
|
||||
from agent_framework import (
|
||||
AgentExecutorResponse,
|
||||
AgentRequestInfoResponse,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
BaseGroupChatOrchestrator,
|
||||
@@ -44,9 +44,9 @@ class StubAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
response = ChatMessage(role=Role.ASSISTANT, text=self._reply_text, author_name=self.name)
|
||||
return AgentRunResponse(messages=[response])
|
||||
return AgentResponse(messages=[response])
|
||||
|
||||
def run_stream( # type: ignore[override]
|
||||
self,
|
||||
@@ -54,9 +54,9 @@ class StubAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def _stream() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
async def _stream() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text=self._reply_text)], role=Role.ASSISTANT, author_name=self.name
|
||||
)
|
||||
|
||||
@@ -88,12 +88,12 @@ class StubManagerAgent(ChatAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
if self._call_count == 0:
|
||||
self._call_count += 1
|
||||
# First call: select the agent (using AgentOrchestrationOutput format)
|
||||
payload = {"terminate": False, "reason": "Selecting agent", "next_speaker": "agent", "final_message": None}
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[
|
||||
ChatMessage(
|
||||
role=Role.ASSISTANT,
|
||||
@@ -114,7 +114,7 @@ class StubManagerAgent(ChatAgent):
|
||||
"next_speaker": None,
|
||||
"final_message": "agent manager final",
|
||||
}
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[
|
||||
ChatMessage(
|
||||
role=Role.ASSISTANT,
|
||||
@@ -134,12 +134,12 @@ class StubManagerAgent(ChatAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
if self._call_count == 0:
|
||||
self._call_count += 1
|
||||
|
||||
async def _stream_initial() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(
|
||||
async def _stream_initial() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(
|
||||
contents=[
|
||||
TextContent(
|
||||
text=(
|
||||
@@ -154,8 +154,8 @@ class StubManagerAgent(ChatAgent):
|
||||
|
||||
return _stream_initial()
|
||||
|
||||
async def _stream_final() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(
|
||||
async def _stream_final() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(
|
||||
contents=[
|
||||
TextContent(
|
||||
text=(
|
||||
@@ -341,14 +341,14 @@ class TestGroupChatBuilder:
|
||||
def __init__(self) -> None:
|
||||
super().__init__(name="", description="test")
|
||||
|
||||
async def run(self, messages: Any = None, *, thread: Any = None, **kwargs: Any) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[])
|
||||
async def run(self, messages: Any = None, *, thread: Any = None, **kwargs: Any) -> AgentResponse:
|
||||
return AgentResponse(messages=[])
|
||||
|
||||
def run_stream(
|
||||
self, messages: Any = None, *, thread: Any = None, **kwargs: Any
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def _stream() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(contents=[])
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
async def _stream() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(contents=[])
|
||||
|
||||
return _stream()
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentRunUpdateEvent,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
@@ -158,9 +158,9 @@ class StubAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
response = ChatMessage(role=Role.ASSISTANT, text=self._reply_text, author_name=self.name)
|
||||
return AgentRunResponse(messages=[response])
|
||||
return AgentResponse(messages=[response])
|
||||
|
||||
def run_stream( # type: ignore[override]
|
||||
self,
|
||||
@@ -168,9 +168,9 @@ class StubAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def _stream() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
async def _stream() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text=self._reply_text)], role=Role.ASSISTANT, author_name=self.name
|
||||
)
|
||||
|
||||
@@ -424,8 +424,8 @@ class StubManagerAgent(BaseAgent):
|
||||
*,
|
||||
thread: Any = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="ok")])
|
||||
) -> AgentResponse:
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="ok")])
|
||||
|
||||
def run_stream(
|
||||
self,
|
||||
@@ -433,9 +433,9 @@ class StubManagerAgent(BaseAgent):
|
||||
*,
|
||||
thread: Any = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
async def _gen() -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(message_deltas=[ChatMessage(role=Role.ASSISTANT, text="ok")])
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
async def _gen() -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(message_deltas=[ChatMessage(role=Role.ASSISTANT, text="ok")])
|
||||
|
||||
return _gen()
|
||||
|
||||
@@ -538,14 +538,14 @@ class StubThreadAgent(BaseAgent):
|
||||
super().__init__(name=name or "agentA")
|
||||
|
||||
async def run_stream(self, messages=None, *, thread=None, **kwargs): # type: ignore[override]
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text="thread-ok")],
|
||||
author_name=self.name,
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
|
||||
async def run(self, messages=None, *, thread=None, **kwargs): # type: ignore[override]
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="thread-ok", author_name=self.name)])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="thread-ok", author_name=self.name)])
|
||||
|
||||
|
||||
class StubAssistantsClient:
|
||||
@@ -560,16 +560,14 @@ class StubAssistantsAgent(BaseAgent):
|
||||
self.chat_client = StubAssistantsClient() # type name contains 'AssistantsClient'
|
||||
|
||||
async def run_stream(self, messages=None, *, thread=None, **kwargs): # type: ignore[override]
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text="assistants-ok")],
|
||||
author_name=self.name,
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
|
||||
async def run(self, messages=None, *, thread=None, **kwargs): # type: ignore[override]
|
||||
return AgentRunResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="assistants-ok", author_name=self.name)]
|
||||
)
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="assistants-ok", author_name=self.name)])
|
||||
|
||||
|
||||
async def _collect_agent_responses_setup(participant: AgentProtocol) -> list[ChatMessage]:
|
||||
|
||||
@@ -10,8 +10,8 @@ import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatMessage,
|
||||
Role,
|
||||
@@ -114,10 +114,10 @@ class TestAgentRequestInfoExecutor:
|
||||
"""Test that request_info handler calls ctx.request_info."""
|
||||
executor = AgentRequestInfoExecutor(id="test_executor")
|
||||
|
||||
agent_run_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Agent response")])
|
||||
agent_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Agent response")])
|
||||
agent_response = AgentExecutorResponse(
|
||||
executor_id="test_agent",
|
||||
agent_run_response=agent_run_response,
|
||||
agent_response=agent_response,
|
||||
)
|
||||
|
||||
ctx = MagicMock(spec=WorkflowContext)
|
||||
@@ -132,10 +132,10 @@ class TestAgentRequestInfoExecutor:
|
||||
"""Test response handler when user provides additional messages."""
|
||||
executor = AgentRequestInfoExecutor(id="test_executor")
|
||||
|
||||
agent_run_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Original")])
|
||||
agent_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Original")])
|
||||
original_request = AgentExecutorResponse(
|
||||
executor_id="test_agent",
|
||||
agent_run_response=agent_run_response,
|
||||
agent_response=agent_response,
|
||||
)
|
||||
|
||||
response = AgentRequestInfoResponse.from_strings(["Additional input"])
|
||||
@@ -158,10 +158,10 @@ class TestAgentRequestInfoExecutor:
|
||||
"""Test response handler when user approves (no additional messages)."""
|
||||
executor = AgentRequestInfoExecutor(id="test_executor")
|
||||
|
||||
agent_run_response = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Original")])
|
||||
agent_response = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Original")])
|
||||
original_request = AgentExecutorResponse(
|
||||
executor_id="test_agent",
|
||||
agent_run_response=agent_run_response,
|
||||
agent_response=agent_response,
|
||||
)
|
||||
|
||||
response = AgentRequestInfoResponse.approve()
|
||||
@@ -205,9 +205,9 @@ class _TestAgent:
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Dummy run method."""
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Test response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Test response")])
|
||||
|
||||
def run_stream(
|
||||
self,
|
||||
@@ -215,11 +215,11 @@ class _TestAgent:
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Dummy run_stream method."""
|
||||
|
||||
async def generator():
|
||||
yield AgentRunResponseUpdate(messages=[ChatMessage(role=Role.ASSISTANT, text="Test response stream")])
|
||||
yield AgentResponseUpdate(messages=[ChatMessage(role=Role.ASSISTANT, text="Test response stream")])
|
||||
|
||||
return generator()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentExecutorResponse,
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
Executor,
|
||||
WorkflowContext,
|
||||
WorkflowEvent,
|
||||
@@ -158,7 +158,7 @@ async def test_runner_emits_runner_completion_for_agent_response_without_targets
|
||||
|
||||
await ctx.send_message(
|
||||
Message(
|
||||
data=AgentExecutorResponse("agent", AgentRunResponse()),
|
||||
data=AgentExecutorResponse("agent", AgentResponse()),
|
||||
source_id="agent",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -7,8 +7,8 @@ import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentExecutorResponse,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
ChatMessage,
|
||||
@@ -35,8 +35,8 @@ class _EchoAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=f"{self.name} reply")])
|
||||
) -> AgentResponse:
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=f"{self.name} reply")])
|
||||
|
||||
async def run_stream( # type: ignore[override]
|
||||
self,
|
||||
@@ -44,9 +44,9 @@ class _EchoAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
# Minimal async generator with one assistant update
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=f"{self.name} reply")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=f"{self.name} reply")])
|
||||
|
||||
|
||||
class _SummarizerExec(Executor):
|
||||
|
||||
@@ -11,9 +11,9 @@ import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentExecutor,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentRunEvent,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentRunUpdateEvent,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
@@ -831,9 +831,9 @@ class _StreamingTestAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
"""Non-streaming run - returns complete response."""
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=self._reply_text)])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=self._reply_text)])
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
@@ -841,11 +841,11 @@ class _StreamingTestAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
"""Streaming run - yields incremental updates."""
|
||||
# Simulate streaming by yielding character by character
|
||||
for char in self._reply_text:
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=char)])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=char)])
|
||||
|
||||
|
||||
async def test_agent_streaming_vs_non_streaming() -> None:
|
||||
|
||||
@@ -8,8 +8,8 @@ import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentRunUpdateEvent,
|
||||
AgentThread,
|
||||
ChatMessage,
|
||||
@@ -52,7 +52,7 @@ class SimpleExecutor(Executor):
|
||||
response_message = ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=response_text)])
|
||||
|
||||
# Emit update event.
|
||||
streaming_update = AgentRunResponseUpdate(
|
||||
streaming_update = AgentResponseUpdate(
|
||||
contents=[TextContent(text=response_text)], role=Role.ASSISTANT, message_id=str(uuid.uuid4())
|
||||
)
|
||||
await ctx.add_event(AgentRunUpdateEvent(executor_id=self.id, data=streaming_update))
|
||||
@@ -74,7 +74,7 @@ class RequestingExecutor(Executor):
|
||||
self, original_request: str, response: str, ctx: WorkflowContext[ChatMessage]
|
||||
) -> None:
|
||||
# Handle the response and emit completion response
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="Request completed successfully")],
|
||||
role=Role.ASSISTANT,
|
||||
message_id=str(uuid.uuid4()),
|
||||
@@ -100,7 +100,7 @@ class ConversationHistoryCapturingExecutor(Executor):
|
||||
|
||||
response_message = ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=response_text)])
|
||||
|
||||
streaming_update = AgentRunResponseUpdate(
|
||||
streaming_update = AgentResponseUpdate(
|
||||
contents=[TextContent(text=response_text)], role=Role.ASSISTANT, message_id=str(uuid.uuid4())
|
||||
)
|
||||
await ctx.add_event(AgentRunUpdateEvent(executor_id=self.id, data=streaming_update))
|
||||
@@ -124,7 +124,7 @@ class TestWorkflowAgent:
|
||||
result = await agent.run("Hello World")
|
||||
|
||||
# Verify we got responses from both executors
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) >= 2, f"Expected at least 2 messages, got {len(result.messages)}"
|
||||
|
||||
# Find messages from each executor
|
||||
@@ -162,7 +162,7 @@ class TestWorkflowAgent:
|
||||
agent = WorkflowAgent(workflow=workflow, name="Streaming Test Agent")
|
||||
|
||||
# Execute workflow streaming to capture streaming events
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Test input"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -191,13 +191,13 @@ class TestWorkflowAgent:
|
||||
agent = WorkflowAgent(workflow=workflow, name="Request Test Agent")
|
||||
|
||||
# Execute workflow streaming to get request info event
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Start request"):
|
||||
updates.append(update)
|
||||
# Should have received an approval request for the request info
|
||||
assert len(updates) > 0
|
||||
|
||||
approval_update: AgentRunResponseUpdate | None = None
|
||||
approval_update: AgentResponseUpdate | None = None
|
||||
for update in updates:
|
||||
if any(isinstance(content, FunctionApprovalRequestContent) for content in update.contents):
|
||||
approval_update = update
|
||||
@@ -248,7 +248,7 @@ class TestWorkflowAgent:
|
||||
continuation_result = await agent.run(response_message)
|
||||
|
||||
# Should complete successfully
|
||||
assert isinstance(continuation_result, AgentRunResponse)
|
||||
assert isinstance(continuation_result, AgentResponse)
|
||||
|
||||
# Verify cleanup - pending requests should be cleared after function response handling
|
||||
assert len(agent.pending_requests) == 0
|
||||
@@ -293,7 +293,7 @@ class TestWorkflowAgent:
|
||||
"""Test that ctx.yield_output() in a workflow executor surfaces as agent output when using .as_agent().
|
||||
|
||||
This validates the fix for issue #2813: WorkflowOutputEvent should be converted to
|
||||
AgentRunResponseUpdate when the workflow is wrapped via .as_agent().
|
||||
AgentResponseUpdate when the workflow is wrapped via .as_agent().
|
||||
"""
|
||||
|
||||
@executor
|
||||
@@ -314,12 +314,12 @@ class TestWorkflowAgent:
|
||||
agent = workflow.as_agent("test-agent")
|
||||
agent_result = await agent.run("hello")
|
||||
|
||||
assert isinstance(agent_result, AgentRunResponse)
|
||||
assert isinstance(agent_result, AgentResponse)
|
||||
assert len(agent_result.messages) == 1
|
||||
assert agent_result.messages[0].text == "processed: hello"
|
||||
|
||||
async def test_workflow_as_agent_yield_output_surfaces_in_run_stream(self) -> None:
|
||||
"""Test that ctx.yield_output() surfaces as AgentRunResponseUpdate when streaming."""
|
||||
"""Test that ctx.yield_output() surfaces as AgentResponseUpdate when streaming."""
|
||||
|
||||
@executor
|
||||
async def yielding_executor(messages: list[ChatMessage], ctx: WorkflowContext) -> None:
|
||||
@@ -329,7 +329,7 @@ class TestWorkflowAgent:
|
||||
workflow = WorkflowBuilder().set_start_executor(yielding_executor).build()
|
||||
agent = workflow.as_agent("test-agent")
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("hello"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -353,7 +353,7 @@ class TestWorkflowAgent:
|
||||
|
||||
result = await agent.run("test")
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) == 3
|
||||
|
||||
# Verify each content type is preserved
|
||||
@@ -410,7 +410,7 @@ class TestWorkflowAgent:
|
||||
workflow = WorkflowBuilder().set_start_executor(raw_yielding_executor).build()
|
||||
agent = workflow.as_agent("raw-test-agent")
|
||||
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("test"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -448,7 +448,7 @@ class TestWorkflowAgent:
|
||||
agent = workflow.as_agent("list-msg-agent")
|
||||
|
||||
# Verify streaming returns the update with all 4 contents before coalescing
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("test"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -460,7 +460,7 @@ class TestWorkflowAgent:
|
||||
# Verify run() coalesces text contents (expected behavior)
|
||||
result = await agent.run("test")
|
||||
|
||||
assert isinstance(result, AgentRunResponse)
|
||||
assert isinstance(result, AgentResponse)
|
||||
assert len(result.messages) == 1
|
||||
# TextContent items are coalesced into one
|
||||
assert len(result.messages[0].contents) == 1
|
||||
@@ -587,17 +587,17 @@ class TestWorkflowAgent:
|
||||
def get_new_thread(self) -> AgentThread:
|
||||
return AgentThread()
|
||||
|
||||
async def run(self, messages: Any, *, thread: AgentThread | None = None, **kwargs: Any) -> AgentRunResponse:
|
||||
return AgentRunResponse(
|
||||
async def run(self, messages: Any, *, thread: AgentThread | None = None, **kwargs: Any) -> AgentResponse:
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text=self._response_text)],
|
||||
text=self._response_text,
|
||||
)
|
||||
|
||||
async def run_stream(
|
||||
self, messages: Any, *, thread: AgentThread | None = None, **kwargs: Any
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
for word in self._response_text.split():
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text=word + " ")],
|
||||
role=Role.ASSISTANT,
|
||||
author_name=self._name,
|
||||
@@ -661,16 +661,16 @@ class TestWorkflowAgent:
|
||||
def get_new_thread(self) -> AgentThread:
|
||||
return AgentThread()
|
||||
|
||||
async def run(self, messages: Any, *, thread: AgentThread | None = None, **kwargs: Any) -> AgentRunResponse:
|
||||
return AgentRunResponse(
|
||||
async def run(self, messages: Any, *, thread: AgentThread | None = None, **kwargs: Any) -> AgentResponse:
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text=self._response_text)],
|
||||
text=self._response_text,
|
||||
)
|
||||
|
||||
async def run_stream(
|
||||
self, messages: Any, *, thread: AgentThread | None = None, **kwargs: Any
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
yield AgentRunResponseUpdate(
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text=self._response_text)],
|
||||
role=Role.ASSISTANT,
|
||||
author_name=self._name,
|
||||
@@ -717,7 +717,7 @@ class TestWorkflowAgentAuthorName:
|
||||
agent = WorkflowAgent(workflow=workflow, name="Test Agent")
|
||||
|
||||
# Collect streaming updates
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Hello"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -736,7 +736,7 @@ class TestWorkflowAgentAuthorName:
|
||||
@handler
|
||||
async def handle_message(self, message: list[ChatMessage], ctx: WorkflowContext[list[ChatMessage]]) -> None:
|
||||
# Emit update with explicit author_name
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="Response with author")],
|
||||
role=Role.ASSISTANT,
|
||||
author_name="custom_author_name", # Explicitly set
|
||||
@@ -749,7 +749,7 @@ class TestWorkflowAgentAuthorName:
|
||||
agent = WorkflowAgent(workflow=workflow, name="Test Agent")
|
||||
|
||||
# Collect streaming updates
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Hello"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -767,7 +767,7 @@ class TestWorkflowAgentAuthorName:
|
||||
agent = WorkflowAgent(workflow=workflow, name="Multi-Executor Agent")
|
||||
|
||||
# Collect streaming updates
|
||||
updates: list[AgentRunResponseUpdate] = []
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
async for update in agent.run_stream("Hello"):
|
||||
updates.append(update)
|
||||
|
||||
@@ -788,7 +788,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
# Create updates with different response_ids and message_ids in non-chronological order
|
||||
updates = [
|
||||
# Response B, Message 2 (latest in resp B)
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[TextContent(text="RespB-Msg2")],
|
||||
role=Role.ASSISTANT,
|
||||
response_id="resp-b",
|
||||
@@ -796,7 +796,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
created_at="2024-01-01T12:02:00Z",
|
||||
),
|
||||
# Response A, Message 1 (earliest overall)
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[TextContent(text="RespA-Msg1")],
|
||||
role=Role.ASSISTANT,
|
||||
response_id="resp-a",
|
||||
@@ -804,7 +804,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
created_at="2024-01-01T12:00:00Z",
|
||||
),
|
||||
# Response B, Message 1 (earlier in resp B)
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[TextContent(text="RespB-Msg1")],
|
||||
role=Role.ASSISTANT,
|
||||
response_id="resp-b",
|
||||
@@ -812,7 +812,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
created_at="2024-01-01T12:01:00Z",
|
||||
),
|
||||
# Response A, Message 2 (later in resp A)
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[TextContent(text="RespA-Msg2")],
|
||||
role=Role.ASSISTANT,
|
||||
response_id="resp-a",
|
||||
@@ -820,7 +820,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
created_at="2024-01-01T12:00:30Z",
|
||||
),
|
||||
# Global dangling update (no response_id) - should go at end
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[TextContent(text="Global-Dangling")],
|
||||
role=Role.ASSISTANT,
|
||||
response_id=None,
|
||||
@@ -891,7 +891,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
"""Test that merge_updates correctly aggregates usage details, timestamps, and additional properties."""
|
||||
# Create updates with various metadata including usage details
|
||||
updates = [
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[
|
||||
TextContent(text="First"),
|
||||
UsageContent(
|
||||
@@ -904,7 +904,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
created_at="2024-01-01T12:00:00Z",
|
||||
additional_properties={"source": "executor1", "priority": "high"},
|
||||
),
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[
|
||||
TextContent(text="Second"),
|
||||
UsageContent(
|
||||
@@ -917,7 +917,7 @@ class TestWorkflowAgentMergeUpdates:
|
||||
created_at="2024-01-01T12:01:00Z", # Later timestamp
|
||||
additional_properties={"source": "executor2", "category": "analysis"},
|
||||
),
|
||||
AgentRunResponseUpdate(
|
||||
AgentResponseUpdate(
|
||||
contents=[
|
||||
TextContent(text="Third"),
|
||||
UsageContent(details=UsageDetails(input_token_count=5, output_token_count=3, total_token_count=8)),
|
||||
|
||||
@@ -7,8 +7,8 @@ import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentExecutor,
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
ChatMessage,
|
||||
@@ -29,11 +29,11 @@ class DummyAgent(BaseAgent):
|
||||
norm.append(m)
|
||||
elif isinstance(m, str):
|
||||
norm.append(ChatMessage(role=Role.USER, text=m))
|
||||
return AgentRunResponse(messages=norm)
|
||||
return AgentResponse(messages=norm)
|
||||
|
||||
async def run_stream(self, messages=None, *, thread: AgentThread | None = None, **kwargs): # type: ignore[override]
|
||||
# Minimal async generator
|
||||
yield AgentRunResponseUpdate()
|
||||
yield AgentResponseUpdate()
|
||||
|
||||
|
||||
def test_builder_accepts_agents_directly():
|
||||
|
||||
@@ -6,8 +6,8 @@ from typing import Annotated, Any
|
||||
import pytest
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
ChatMessage,
|
||||
@@ -55,9 +55,9 @@ class _KwargsCapturingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
self.captured_kwargs.append(dict(kwargs))
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=f"{self.name} response")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text=f"{self.name} response")])
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
@@ -65,9 +65,9 @@ class _KwargsCapturingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
self.captured_kwargs.append(dict(kwargs))
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=f"{self.name} response")])
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=f"{self.name} response")])
|
||||
|
||||
|
||||
# region Sequential Builder Tests
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ from collections.abc import AsyncGenerator
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_framework import get_logger
|
||||
from agent_framework._types import AgentRunResponse, ChatMessage
|
||||
from agent_framework._types import AgentResponse, ChatMessage
|
||||
|
||||
from ._handlers import (
|
||||
ActionContext,
|
||||
@@ -348,7 +348,7 @@ async def handle_invoke_azure_agent(ctx: ActionContext) -> AsyncGenerator[Workfl
|
||||
tool_calls.extend(chunk.tool_calls)
|
||||
|
||||
# Build consolidated response from updates
|
||||
response = AgentRunResponse.from_agent_run_response_updates(updates)
|
||||
response = AgentResponse.from_agent_run_response_updates(updates)
|
||||
text = response.text
|
||||
response_messages = response.messages
|
||||
|
||||
@@ -581,7 +581,7 @@ async def handle_invoke_prompt_agent(ctx: ActionContext) -> AsyncGenerator[Workf
|
||||
)
|
||||
|
||||
# Build consolidated response from updates
|
||||
response = AgentRunResponse.from_agent_run_response_updates(updates)
|
||||
response = AgentResponse.from_agent_run_response_updates(updates)
|
||||
text = response.text
|
||||
response_messages = response.messages
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ class MessageMapper:
|
||||
"""Convert a single Agent Framework event to OpenAI events.
|
||||
|
||||
Args:
|
||||
raw_event: Agent Framework event (AgentRunResponseUpdate, WorkflowEvent, etc.)
|
||||
raw_event: Agent Framework event (AgentResponseUpdate, WorkflowEvent, etc.)
|
||||
request: Original request for context
|
||||
|
||||
Returns:
|
||||
@@ -178,26 +178,26 @@ class MessageMapper:
|
||||
|
||||
# Import Agent Framework types for proper isinstance checks
|
||||
try:
|
||||
from agent_framework import AgentRunResponse, AgentRunResponseUpdate, WorkflowEvent
|
||||
from agent_framework import AgentResponse, AgentResponseUpdate, WorkflowEvent
|
||||
from agent_framework._workflows._events import AgentRunUpdateEvent
|
||||
|
||||
# Handle AgentRunUpdateEvent - workflow event wrapping AgentRunResponseUpdate
|
||||
# Handle AgentRunUpdateEvent - workflow event wrapping AgentResponseUpdate
|
||||
# This must be checked BEFORE generic WorkflowEvent check
|
||||
if isinstance(raw_event, AgentRunUpdateEvent):
|
||||
# Extract the AgentRunResponseUpdate from the event's data attribute
|
||||
if raw_event.data and isinstance(raw_event.data, AgentRunResponseUpdate):
|
||||
# Extract the AgentResponseUpdate from the event's data attribute
|
||||
if raw_event.data and isinstance(raw_event.data, AgentResponseUpdate):
|
||||
# Preserve executor_id in context for proper output routing
|
||||
context["current_executor_id"] = raw_event.executor_id
|
||||
return await self._convert_agent_update(raw_event.data, context)
|
||||
# If no data, treat as generic workflow event
|
||||
return await self._convert_workflow_event(raw_event, context)
|
||||
|
||||
# Handle complete agent response (AgentRunResponse) - for non-streaming agent execution
|
||||
if isinstance(raw_event, AgentRunResponse):
|
||||
# Handle complete agent response (AgentResponse) - for non-streaming agent execution
|
||||
if isinstance(raw_event, AgentResponse):
|
||||
return await self._convert_agent_response(raw_event, context)
|
||||
|
||||
# Handle agent updates (AgentRunResponseUpdate) - for direct agent execution
|
||||
if isinstance(raw_event, AgentRunResponseUpdate):
|
||||
# Handle agent updates (AgentResponseUpdate) - for direct agent execution
|
||||
if isinstance(raw_event, AgentResponseUpdate):
|
||||
return await self._convert_agent_update(raw_event, context)
|
||||
|
||||
# Handle workflow events (any class that inherits from WorkflowEvent)
|
||||
@@ -686,13 +686,13 @@ class MessageMapper:
|
||||
return events
|
||||
|
||||
async def _convert_agent_response(self, response: Any, context: dict[str, Any]) -> Sequence[Any]:
|
||||
"""Convert complete AgentRunResponse to OpenAI events.
|
||||
"""Convert complete AgentResponse to OpenAI events.
|
||||
|
||||
This handles non-streaming agent execution where agent.run() returns
|
||||
a complete AgentRunResponse instead of streaming AgentRunResponseUpdate objects.
|
||||
a complete AgentResponse instead of streaming AgentResponseUpdate objects.
|
||||
|
||||
Args:
|
||||
response: Agent run response (AgentRunResponse)
|
||||
response: Agent run response (AgentResponse)
|
||||
context: Conversion context
|
||||
|
||||
Returns:
|
||||
@@ -1047,7 +1047,7 @@ class MessageMapper:
|
||||
# Create ExecutorActionItem with completed status
|
||||
# ExecutorCompletedEvent uses 'data' field, not 'result'
|
||||
# Serialize the result data to ensure it's JSON-serializable
|
||||
# (AgentExecutorResponse contains AgentRunResponse/ChatMessage which are SerializationMixin)
|
||||
# (AgentExecutorResponse contains AgentResponse/ChatMessage which are SerializationMixin)
|
||||
raw_result = getattr(event, "data", None)
|
||||
serialized_result = self._serialize_value(raw_result) if raw_result is not None else None
|
||||
executor_item = ExecutorActionItem(
|
||||
|
||||
@@ -208,7 +208,7 @@ export interface UsageDetails {
|
||||
}
|
||||
|
||||
// Agent run response update (streaming)
|
||||
export interface AgentRunResponseUpdate {
|
||||
export interface AgentResponseUpdate {
|
||||
contents: Contents[];
|
||||
role?: Role;
|
||||
author_name?: string;
|
||||
@@ -222,7 +222,7 @@ export interface AgentRunResponseUpdate {
|
||||
}
|
||||
|
||||
// Agent run response (final)
|
||||
export interface AgentRunResponse {
|
||||
export interface AgentResponse {
|
||||
messages: ChatMessage[];
|
||||
response_id?: string;
|
||||
created_at?: CreatedAtT;
|
||||
@@ -302,11 +302,11 @@ export interface ExecutorEvent extends WorkflowEvent {
|
||||
}
|
||||
|
||||
export interface AgentRunUpdateEvent extends ExecutorEvent {
|
||||
data?: AgentRunResponseUpdate;
|
||||
data?: AgentResponseUpdate;
|
||||
}
|
||||
|
||||
export interface AgentRunEvent extends ExecutorEvent {
|
||||
data?: AgentRunResponse;
|
||||
data?: AgentResponse;
|
||||
}
|
||||
|
||||
// Span event structure (from OpenTelemetry)
|
||||
|
||||
@@ -7,7 +7,7 @@ import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from agent_framework import AgentRunResponse, ChatMessage, Role, TextContent
|
||||
from agent_framework import AgentResponse, ChatMessage, Role, TextContent
|
||||
|
||||
from agent_framework_devui import register_cleanup
|
||||
from agent_framework_devui._discovery import EntityDiscovery
|
||||
@@ -35,7 +35,7 @@ class MockAgent:
|
||||
|
||||
async def run_stream(self, messages=None, *, thread=None, **kwargs):
|
||||
"""Mock streaming run method."""
|
||||
yield AgentRunResponse(
|
||||
yield AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text="Test response")])],
|
||||
)
|
||||
|
||||
@@ -259,7 +259,7 @@ async def test_cleanup_with_file_based_discovery():
|
||||
# Write agent module with cleanup registration
|
||||
agent_file = agent_dir / "__init__.py"
|
||||
agent_file.write_text("""
|
||||
from agent_framework import AgentRunResponse, ChatMessage, Role, TextContent
|
||||
from agent_framework import AgentResponse, ChatMessage, Role, TextContent
|
||||
from agent_framework_devui import register_cleanup
|
||||
|
||||
class MockCredential:
|
||||
@@ -278,7 +278,7 @@ class TestAgent:
|
||||
description = "Test agent with cleanup"
|
||||
|
||||
async def run_stream(self, messages=None, *, thread=None, **kwargs):
|
||||
yield AgentRunResponse(
|
||||
yield AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, content=[TextContent(text="Test")])],
|
||||
inner_messages=[],
|
||||
)
|
||||
|
||||
@@ -84,7 +84,7 @@ async def test_discovery_accepts_agents_with_only_run():
|
||||
|
||||
init_file = agent_dir / "__init__.py"
|
||||
init_file.write_text("""
|
||||
from agent_framework import AgentRunResponse, AgentThread, ChatMessage, Role, TextContent
|
||||
from agent_framework import AgentResponse, AgentThread, ChatMessage, Role, TextContent
|
||||
|
||||
class NonStreamingAgent:
|
||||
id = "non_streaming"
|
||||
@@ -92,7 +92,7 @@ class NonStreamingAgent:
|
||||
description = "Agent without run_stream"
|
||||
|
||||
async def run(self, messages=None, *, thread=None, **kwargs):
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(
|
||||
role=Role.ASSISTANT,
|
||||
contents=[TextContent(text="response")]
|
||||
@@ -203,13 +203,13 @@ workflow = builder.build()
|
||||
agent_dir = temp_path / "my_agent"
|
||||
agent_dir.mkdir()
|
||||
(agent_dir / "agent.py").write_text("""
|
||||
from agent_framework import AgentRunResponse, AgentThread, ChatMessage, Role, TextContent
|
||||
from agent_framework import AgentResponse, AgentThread, ChatMessage, Role, TextContent
|
||||
|
||||
class TestAgent:
|
||||
name = "Test Agent"
|
||||
|
||||
async def run(self, messages=None, *, thread=None, **kwargs):
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text="test")])],
|
||||
response_id="test"
|
||||
)
|
||||
|
||||
@@ -287,7 +287,7 @@ async def test_full_pipeline_agent_events_are_json_serializable(executor_with_re
|
||||
2. Each event is converted by the mapper
|
||||
3. Server calls model_dump_json() on each event for SSE
|
||||
|
||||
If any event contains non-serializable objects (like AgentRunResponse),
|
||||
If any event contains non-serializable objects (like AgentResponse),
|
||||
this test will fail - catching the bug before it hits production.
|
||||
"""
|
||||
executor, entity_id, mock_client = executor_with_real_agent
|
||||
@@ -327,7 +327,7 @@ async def test_full_pipeline_workflow_events_are_json_serializable():
|
||||
|
||||
This is particularly important for workflows with AgentExecutor because:
|
||||
- AgentExecutor produces ExecutorCompletedEvent with AgentExecutorResponse
|
||||
- AgentExecutorResponse contains AgentRunResponse and ChatMessage objects
|
||||
- AgentExecutorResponse contains AgentResponse and ChatMessage objects
|
||||
- These are SerializationMixin objects, not Pydantic, which caused the original bug
|
||||
|
||||
This test ensures the ENTIRE streaming pipeline works end-to-end.
|
||||
@@ -566,7 +566,7 @@ def test_extract_workflow_hil_responses_handles_stringified_json():
|
||||
|
||||
async def test_executor_handles_non_streaming_agent():
|
||||
"""Test executor can handle agents with only run() method (no run_stream)."""
|
||||
from agent_framework import AgentRunResponse, AgentThread, ChatMessage, Role, TextContent
|
||||
from agent_framework import AgentResponse, AgentThread, ChatMessage, Role, TextContent
|
||||
|
||||
class NonStreamingAgent:
|
||||
"""Agent with only run() method - does NOT satisfy full AgentProtocol."""
|
||||
@@ -576,7 +576,7 @@ async def test_executor_handles_non_streaming_agent():
|
||||
description = "Test agent without run_stream()"
|
||||
|
||||
async def run(self, messages=None, *, thread=None, **kwargs):
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=f"Processed: {messages}")])],
|
||||
response_id="test_123",
|
||||
)
|
||||
|
||||
@@ -18,8 +18,8 @@ from collections.abc import AsyncIterable, MutableSequence
|
||||
from typing import Any, Generic
|
||||
|
||||
from agent_framework import (
|
||||
AgentRunResponse,
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
BaseAgent,
|
||||
BaseChatClient,
|
||||
@@ -172,9 +172,9 @@ class MockAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
self.call_count += 1
|
||||
return AgentRunResponse(
|
||||
return AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=self.response_text)])]
|
||||
)
|
||||
|
||||
@@ -184,10 +184,10 @@ class MockAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
self.call_count += 1
|
||||
for chunk in self.streaming_chunks:
|
||||
yield AgentRunResponseUpdate(contents=[TextContent(text=chunk)], role=Role.ASSISTANT)
|
||||
yield AgentResponseUpdate(contents=[TextContent(text=chunk)], role=Role.ASSISTANT)
|
||||
|
||||
|
||||
class MockToolCallingAgent(BaseAgent):
|
||||
@@ -203,9 +203,9 @@ class MockToolCallingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AgentRunResponse:
|
||||
) -> AgentResponse:
|
||||
self.call_count += 1
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="done")])
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="done")])
|
||||
|
||||
async def run_stream(
|
||||
self,
|
||||
@@ -213,15 +213,15 @@ class MockToolCallingAgent(BaseAgent):
|
||||
*,
|
||||
thread: AgentThread | None = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterable[AgentRunResponseUpdate]:
|
||||
) -> AsyncIterable[AgentResponseUpdate]:
|
||||
self.call_count += 1
|
||||
# First: text
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text="Let me search for that...")],
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
# Second: tool call
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionCallContent(
|
||||
call_id="call_123",
|
||||
@@ -232,7 +232,7 @@ class MockToolCallingAgent(BaseAgent):
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
# Third: tool result
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[
|
||||
FunctionResultContent(
|
||||
call_id="call_123",
|
||||
@@ -242,7 +242,7 @@ class MockToolCallingAgent(BaseAgent):
|
||||
role=Role.TOOL,
|
||||
)
|
||||
# Fourth: final text
|
||||
yield AgentRunResponseUpdate(
|
||||
yield AgentResponseUpdate(
|
||||
contents=[TextContent(text="The weather is sunny, 72°F.")],
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
@@ -295,9 +295,9 @@ def create_mock_tool_agent(id: str = "tool_agent", name: str = "ToolAgent") -> M
|
||||
return MockToolCallingAgent(id=id, name=name)
|
||||
|
||||
|
||||
def create_agent_run_response(text: str = "Test response") -> AgentRunResponse:
|
||||
"""Create an AgentRunResponse with the given text."""
|
||||
return AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=text)])])
|
||||
def create_agent_run_response(text: str = "Test response") -> AgentResponse:
|
||||
"""Create an AgentResponse with the given text."""
|
||||
return AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=text)])])
|
||||
|
||||
|
||||
def create_agent_executor_response(
|
||||
@@ -308,7 +308,7 @@ def create_agent_executor_response(
|
||||
agent_response = create_agent_run_response(response_text)
|
||||
return AgentExecutorResponse(
|
||||
executor_id=executor_id,
|
||||
agent_run_response=agent_response,
|
||||
agent_response=agent_response,
|
||||
full_conversation=[
|
||||
ChatMessage(role=Role.USER, contents=[TextContent(text="User input")]),
|
||||
ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=response_text)]),
|
||||
@@ -324,7 +324,7 @@ def create_executor_completed_event(
|
||||
|
||||
This creates the exact data structure that caused the serialization bug:
|
||||
ExecutorCompletedEvent.data contains AgentExecutorResponse which contains
|
||||
AgentRunResponse and ChatMessage objects (SerializationMixin, not Pydantic).
|
||||
AgentResponse and ChatMessage objects (SerializationMixin, not Pydantic).
|
||||
"""
|
||||
data = create_agent_executor_response(executor_id) if with_agent_response else {"simple": "dict"}
|
||||
return ExecutorCompletedEvent(executor_id=executor_id, data=data)
|
||||
|
||||
@@ -13,7 +13,7 @@ import pytest
|
||||
|
||||
# Import Agent Framework types
|
||||
from agent_framework._types import (
|
||||
AgentRunResponseUpdate,
|
||||
AgentResponseUpdate,
|
||||
ErrorContent,
|
||||
FunctionCallContent,
|
||||
FunctionResultContent,
|
||||
@@ -83,11 +83,9 @@ def create_test_content(content_type: str, **kwargs: Any) -> Any:
|
||||
raise ValueError(f"Unknown content type: {content_type}")
|
||||
|
||||
|
||||
def create_test_agent_update(contents: list[Any]) -> AgentRunResponseUpdate:
|
||||
"""Create test AgentRunResponseUpdate."""
|
||||
return AgentRunResponseUpdate(
|
||||
contents=contents, role=Role.ASSISTANT, message_id="test_msg", response_id="test_resp"
|
||||
)
|
||||
def create_test_agent_update(contents: list[Any]) -> AgentResponseUpdate:
|
||||
"""Create test AgentResponseUpdate."""
|
||||
return AgentResponseUpdate(contents=contents, role=Role.ASSISTANT, message_id="test_msg", response_id="test_resp")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -105,7 +103,7 @@ async def test_critical_isinstance_bug_detection(mapper: MessageMapper, test_req
|
||||
assert not hasattr(update, "response") # Fake attribute should not exist
|
||||
|
||||
# Test isinstance works with real types
|
||||
assert isinstance(update, AgentRunResponseUpdate)
|
||||
assert isinstance(update, AgentResponseUpdate)
|
||||
|
||||
# Test mapper conversion - should NOT produce "Unknown event"
|
||||
events = await mapper.convert_event(update, test_request)
|
||||
@@ -264,7 +262,7 @@ async def test_agent_lifecycle_events(mapper: MessageMapper, test_request: Agent
|
||||
|
||||
|
||||
async def test_agent_run_response_mapping(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None:
|
||||
"""Test that mapper handles complete AgentRunResponse (non-streaming)."""
|
||||
"""Test that mapper handles complete AgentResponse (non-streaming)."""
|
||||
response = create_agent_run_response("Complete response from run()")
|
||||
|
||||
events = await mapper.convert_event(response, test_request)
|
||||
@@ -325,14 +323,14 @@ async def test_executor_completed_event_with_agent_response(
|
||||
|
||||
This is a REGRESSION TEST for the serialization bug where
|
||||
ExecutorCompletedEvent.data contained AgentExecutorResponse with nested
|
||||
AgentRunResponse and ChatMessage objects (SerializationMixin) that
|
||||
AgentResponse and ChatMessage objects (SerializationMixin) that
|
||||
Pydantic couldn't serialize.
|
||||
"""
|
||||
# Create event with realistic nested data - the exact structure that caused the bug
|
||||
event = create_executor_completed_event(executor_id="exec_agent", with_agent_response=True)
|
||||
|
||||
# Verify the data has the problematic structure
|
||||
assert hasattr(event.data, "agent_run_response")
|
||||
assert hasattr(event.data, "agent_response")
|
||||
assert hasattr(event.data, "full_conversation")
|
||||
|
||||
# First invoke the executor
|
||||
@@ -380,7 +378,7 @@ async def test_executor_completed_event_serialization_to_json(
|
||||
done_event = events[0]
|
||||
|
||||
# This is the critical test - model_dump_json() should NOT raise
|
||||
# "Unable to serialize unknown type: <class 'agent_framework._types.AgentRunResponse'>"
|
||||
# "Unable to serialize unknown type: <class 'agent_framework._types.AgentResponse'>"
|
||||
try:
|
||||
json_str = done_event.model_dump_json()
|
||||
assert json_str is not None
|
||||
@@ -453,11 +451,11 @@ async def test_magentic_agent_run_update_event_with_agent_delta_metadata(
|
||||
This tests the ACTUAL event format Magentic emits - not a fake MagenticAgentDeltaEvent class.
|
||||
Magentic uses AgentRunUpdateEvent with additional_properties containing magentic_event_type.
|
||||
"""
|
||||
from agent_framework._types import AgentRunResponseUpdate, Role, TextContent
|
||||
from agent_framework._types import AgentResponseUpdate, Role, TextContent
|
||||
from agent_framework._workflows._events import AgentRunUpdateEvent
|
||||
|
||||
# Create the REAL event format that Magentic emits
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="Hello from agent")],
|
||||
role=Role.ASSISTANT,
|
||||
author_name="Writer",
|
||||
@@ -484,11 +482,11 @@ async def test_magentic_orchestrator_message_event(mapper: MessageMapper, test_r
|
||||
Magentic emits orchestrator planning/instruction messages using AgentRunUpdateEvent
|
||||
with additional_properties containing magentic_event_type='orchestrator_message'.
|
||||
"""
|
||||
from agent_framework._types import AgentRunResponseUpdate, Role, TextContent
|
||||
from agent_framework._types import AgentResponseUpdate, Role, TextContent
|
||||
from agent_framework._workflows._events import AgentRunUpdateEvent
|
||||
|
||||
# Create orchestrator message event (REAL format from Magentic)
|
||||
update = AgentRunResponseUpdate(
|
||||
update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="Planning: First, the writer will create content...")],
|
||||
role=Role.ASSISTANT,
|
||||
author_name="Orchestrator",
|
||||
@@ -520,19 +518,19 @@ async def test_magentic_events_use_same_event_class_as_other_workflows(
|
||||
additional_properties. Any mapper code checking for 'MagenticAgentDeltaEvent'
|
||||
class names is dead code.
|
||||
"""
|
||||
from agent_framework._types import AgentRunResponseUpdate, Role, TextContent
|
||||
from agent_framework._types import AgentResponseUpdate, Role, TextContent
|
||||
from agent_framework._workflows._events import AgentRunUpdateEvent
|
||||
|
||||
# Create events the way different workflows do it
|
||||
# 1. Regular workflow (no additional_properties)
|
||||
regular_update = AgentRunResponseUpdate(
|
||||
regular_update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="Regular workflow response")],
|
||||
role=Role.ASSISTANT,
|
||||
)
|
||||
regular_event = AgentRunUpdateEvent(executor_id="regular_executor", data=regular_update)
|
||||
|
||||
# 2. Magentic workflow (with additional_properties)
|
||||
magentic_update = AgentRunResponseUpdate(
|
||||
magentic_update = AgentResponseUpdate(
|
||||
contents=[TextContent(text="Magentic workflow response")],
|
||||
role=Role.ASSISTANT,
|
||||
additional_properties={"magentic_event_type": "agent_delta"},
|
||||
|
||||
@@ -18,7 +18,7 @@ import string
|
||||
from typing import TypedDict, cast
|
||||
|
||||
import sympy # type: ignore[import-untyped,reportMissingImports]
|
||||
from agent_framework import AgentRunResponse, ChatAgent, MCPStdioTool
|
||||
from agent_framework import AgentResponse, ChatAgent, MCPStdioTool
|
||||
from agent_framework.lab.lightning import AgentFrameworkTracer
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agentlightning import LLM, Dataset, Trainer, rollout
|
||||
@@ -102,7 +102,7 @@ def _is_result_correct(prediction: str, ground_truth: str) -> float:
|
||||
return float(_scalar_are_results_same(prediction, ground_truth, 1e-2))
|
||||
|
||||
|
||||
def evaluate(result: AgentRunResponse, ground_truth: str) -> float:
|
||||
def evaluate(result: AgentResponse, ground_truth: str) -> float:
|
||||
"""Main evaluation function that extracts the agent's answer and compares with ground truth.
|
||||
|
||||
This function:
|
||||
|
||||
@@ -7,7 +7,7 @@ from agent_framework import (
|
||||
AgentExecutor,
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
AgentRunResponse,
|
||||
AgentResponse,
|
||||
ChatAgent,
|
||||
ChatClientProtocol,
|
||||
ChatMessage,
|
||||
@@ -124,7 +124,7 @@ class TaskRunner:
|
||||
f"{'<blue>assistant</blue>' if is_from_agent else '<green>user</green>'}</bold>, "
|
||||
f"routing to {'<green>user</green>' if is_from_agent else '<blue>assistant</blue>'}:"
|
||||
)
|
||||
log_messages(response.agent_run_response.messages)
|
||||
log_messages(response.agent_response.messages)
|
||||
|
||||
if self.step_count >= self.max_steps:
|
||||
logger.info(f"Max steps ({self.max_steps}) reached - terminating conversation")
|
||||
@@ -132,7 +132,7 @@ class TaskRunner:
|
||||
# Terminate the workflow
|
||||
return False
|
||||
|
||||
response_text = response.agent_run_response.text
|
||||
response_text = response.agent_response.text
|
||||
if is_from_agent and self._is_agent_stop(response_text):
|
||||
logger.info("Agent requested stop - terminating conversation")
|
||||
self.termination_reason = TerminationReason.AGENT_STOP
|
||||
@@ -144,7 +144,7 @@ class TaskRunner:
|
||||
# The final user message won't appear in the assistant's message store,
|
||||
# because it will never arrive there.
|
||||
# We need to store it because it's needed for evaluation.
|
||||
self._final_user_message = flip_messages(response.agent_run_response.messages)
|
||||
self._final_user_message = flip_messages(response.agent_response.messages)
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -255,7 +255,7 @@ class TaskRunner:
|
||||
"""
|
||||
# Flip message roles for proper conversation flow
|
||||
# Assistant messages become user messages and vice versa
|
||||
flipped = flip_messages(response.agent_run_response.messages)
|
||||
flipped = flip_messages(response.agent_response.messages)
|
||||
|
||||
# Determine source to route to correct target
|
||||
is_from_agent = response.executor_id == ASSISTANT_AGENT_ID
|
||||
@@ -342,7 +342,7 @@ class TaskRunner:
|
||||
first_message = ChatMessage(Role.ASSISTANT, text=DEFAULT_FIRST_AGENT_MESSAGE)
|
||||
initial_greeting = AgentExecutorResponse(
|
||||
executor_id=ASSISTANT_AGENT_ID,
|
||||
agent_run_response=AgentRunResponse(messages=[first_message]),
|
||||
agent_response=AgentResponse(messages=[first_message]),
|
||||
full_conversation=[ChatMessage(Role.ASSISTANT, text=DEFAULT_FIRST_AGENT_MESSAGE)],
|
||||
)
|
||||
|
||||
|
||||
@@ -57,9 +57,9 @@ class PurviewPolicyMiddleware(AgentMiddleware):
|
||||
context.messages, Activity.UPLOAD_TEXT
|
||||
)
|
||||
if should_block_prompt:
|
||||
from agent_framework import AgentRunResponse, ChatMessage, Role
|
||||
from agent_framework import AgentResponse, ChatMessage, Role
|
||||
|
||||
context.result = AgentRunResponse(
|
||||
context.result = AgentResponse(
|
||||
messages=[ChatMessage(role=Role.SYSTEM, text=self._settings.blocked_prompt_message)]
|
||||
)
|
||||
context.terminate = True
|
||||
@@ -76,7 +76,7 @@ class PurviewPolicyMiddleware(AgentMiddleware):
|
||||
await next(context)
|
||||
|
||||
try:
|
||||
# Post (response) check only if we have a normal AgentRunResponse
|
||||
# Post (response) check only if we have a normal AgentResponse
|
||||
# Use the same user_id from the request for the response evaluation
|
||||
if context.result and not context.is_streaming:
|
||||
should_block_response, _ = await self._processor.process_messages(
|
||||
@@ -85,9 +85,9 @@ class PurviewPolicyMiddleware(AgentMiddleware):
|
||||
user_id=resolved_user_id,
|
||||
)
|
||||
if should_block_response:
|
||||
from agent_framework import AgentRunResponse, ChatMessage, Role
|
||||
from agent_framework import AgentResponse, ChatMessage, Role
|
||||
|
||||
context.result = AgentRunResponse(
|
||||
context.result = AgentResponse(
|
||||
messages=[ChatMessage(role=Role.SYSTEM, text=self._settings.blocked_response_message)]
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from agent_framework import AgentRunContext, AgentRunResponse, ChatMessage, Role
|
||||
from agent_framework import AgentResponse, AgentRunContext, ChatMessage, Role
|
||||
from azure.core.credentials import AccessToken
|
||||
|
||||
from agent_framework_purview import PurviewPolicyMiddleware, PurviewSettings
|
||||
@@ -57,7 +57,7 @@ class TestPurviewPolicyMiddleware:
|
||||
async def mock_next(ctx: AgentRunContext) -> None:
|
||||
nonlocal next_called
|
||||
next_called = True
|
||||
ctx.result = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="I'm good, thanks!")])
|
||||
ctx.result = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="I'm good, thanks!")])
|
||||
|
||||
await middleware.process(context, mock_next)
|
||||
|
||||
@@ -104,7 +104,7 @@ class TestPurviewPolicyMiddleware:
|
||||
with patch.object(middleware._processor, "process_messages", side_effect=mock_process_messages):
|
||||
|
||||
async def mock_next(ctx: AgentRunContext) -> None:
|
||||
ctx.result = AgentRunResponse(
|
||||
ctx.result = AgentResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="Here's some sensitive information")]
|
||||
)
|
||||
|
||||
@@ -145,7 +145,7 @@ class TestPurviewPolicyMiddleware:
|
||||
with patch.object(middleware._processor, "process_messages", return_value=(False, "user-123")) as mock_process:
|
||||
|
||||
async def mock_next(ctx: AgentRunContext) -> None:
|
||||
ctx.result = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
ctx.result = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
|
||||
await middleware.process(context, mock_next)
|
||||
|
||||
@@ -167,7 +167,7 @@ class TestPurviewPolicyMiddleware:
|
||||
) as mock_process:
|
||||
|
||||
async def mock_next(ctx: AgentRunContext) -> None:
|
||||
ctx.result = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
ctx.result = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
|
||||
await middleware.process(context, mock_next)
|
||||
|
||||
@@ -199,7 +199,7 @@ class TestPurviewPolicyMiddleware:
|
||||
with patch.object(middleware._processor, "process_messages", side_effect=mock_process_messages):
|
||||
|
||||
async def mock_next(ctx: AgentRunContext) -> None:
|
||||
ctx.result = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
ctx.result = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
|
||||
await middleware.process(context, mock_next)
|
||||
|
||||
@@ -225,7 +225,7 @@ class TestPurviewPolicyMiddleware:
|
||||
with patch.object(middleware._processor, "process_messages", side_effect=mock_process_messages):
|
||||
|
||||
async def mock_next(ctx):
|
||||
ctx.result = AgentRunResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
ctx.result = AgentResponse(messages=[ChatMessage(role=Role.ASSISTANT, text="Response")])
|
||||
|
||||
# Should not raise, just log
|
||||
await middleware.process(context, mock_next)
|
||||
|
||||
Reference in New Issue
Block a user