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:
committed by
GitHub
Unverified
parent
1ae0b09e42
commit
d8cf8361bd
@@ -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"},
|
||||
|
||||
Reference in New Issue
Block a user