mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Merge branch 'main' into feature/hosted-dwf
This commit is contained in:
@@ -535,6 +535,7 @@ class WorkflowAgent(BaseAgent):
|
||||
raw_representations.append(output_event)
|
||||
else:
|
||||
data = output_event.data
|
||||
|
||||
if isinstance(data, AgentResponseUpdate):
|
||||
# We cannot support AgentResponseUpdate in non-streaming mode. This is because the message
|
||||
# sequence cannot be guaranteed when there are streaming updates in between non-streaming
|
||||
@@ -635,16 +636,23 @@ class WorkflowAgent(BaseAgent):
|
||||
A list of AgentResponseUpdate objects. Empty list if the event is not relevant.
|
||||
"""
|
||||
if event.type == "output":
|
||||
# Convert workflow output to agent response updates.
|
||||
# Handle different data types appropriately.
|
||||
data = event.data
|
||||
executor_id = event.executor_id
|
||||
|
||||
if isinstance(data, AgentResponseUpdate):
|
||||
# Pass through AgentResponseUpdate directly (streaming from AgentExecutor)
|
||||
if not data.author_name:
|
||||
data.author_name = executor_id
|
||||
return [data]
|
||||
# Construct a fresh AgentResponseUpdate so we don't mutate a payload
|
||||
# that AgentExecutor still holds a reference to in its `updates` list.
|
||||
return [
|
||||
AgentResponseUpdate(
|
||||
contents=list(data.contents),
|
||||
role=data.role,
|
||||
author_name=data.author_name or executor_id,
|
||||
response_id=data.response_id,
|
||||
message_id=data.message_id,
|
||||
created_at=data.created_at,
|
||||
raw_representation=data.raw_representation,
|
||||
)
|
||||
]
|
||||
if isinstance(data, AgentResponse):
|
||||
# Convert each message in AgentResponse to an AgentResponseUpdate
|
||||
updates: list[AgentResponseUpdate] = []
|
||||
|
||||
@@ -156,8 +156,9 @@ class AgentExecutor(Executor):
|
||||
the agent run.
|
||||
- "custom": use the provided context_filter function to determine which messages to include
|
||||
as context for the agent run.
|
||||
context_filter: An optional function for filtering conversation context when context_mode is set
|
||||
to "custom".
|
||||
context_filter: A function that takes the full conversation (list of Messages) as input and returns
|
||||
a filtered list of Messages to be used as context for the agent run. This is required
|
||||
if context_mode is set to "custom".
|
||||
"""
|
||||
# Prefer provided id; else use agent.name if present; else generate deterministic prefix
|
||||
exec_id = id or resolve_agent_id(agent)
|
||||
|
||||
@@ -361,7 +361,7 @@ class WorkflowExecutor(Executor):
|
||||
return any(is_instance_of(message.data, input_type) for input_type in self.workflow.input_types)
|
||||
|
||||
@handler
|
||||
async def process_workflow(self, input_data: object, ctx: WorkflowContext[Any]) -> None:
|
||||
async def process_workflow(self, input_data: object, ctx: WorkflowContext[Any, Any]) -> None:
|
||||
"""Execute the sub-workflow with raw input data.
|
||||
|
||||
This handler starts a new sub-workflow execution. When the sub-workflow
|
||||
@@ -428,7 +428,7 @@ class WorkflowExecutor(Executor):
|
||||
async def handle_message_wrapped_request_response(
|
||||
self,
|
||||
response: SubWorkflowResponseMessage,
|
||||
ctx: WorkflowContext[Any],
|
||||
ctx: WorkflowContext[Any, Any],
|
||||
) -> None:
|
||||
"""Handle response from parent for a forwarded request.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
This module lazily re-exports objects from:
|
||||
- ``agent-framework-anthropic``
|
||||
- ``agent-framework-azure-contentunderstanding``
|
||||
- ``agent-framework-foundry``
|
||||
- ``agent-framework-foundry-local``
|
||||
"""
|
||||
@@ -12,7 +13,12 @@ import importlib
|
||||
from typing import Any
|
||||
|
||||
_IMPORTS: dict[str, tuple[str, str]] = {
|
||||
"AnalysisSection": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
||||
"AnthropicFoundryClient": ("agent_framework_anthropic", "agent-framework-anthropic"),
|
||||
"ContentUnderstandingContextProvider": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
||||
"DocumentStatus": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
||||
"FileSearchBackend": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
||||
"FileSearchConfig": ("agent_framework_azure_contentunderstanding", "agent-framework-azure-contentunderstanding"),
|
||||
"FoundryAgent": ("agent_framework_foundry", "agent-framework-foundry"),
|
||||
"FoundryAgentOptions": ("agent_framework_foundry", "agent-framework-foundry"),
|
||||
"FoundryChatClient": ("agent_framework_foundry", "agent-framework-foundry"),
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
# Install the relevant packages for full type support.
|
||||
|
||||
from agent_framework_anthropic import AnthropicFoundryClient, RawAnthropicFoundryClient
|
||||
from agent_framework_azure_contentunderstanding import (
|
||||
AnalysisSection,
|
||||
ContentUnderstandingContextProvider,
|
||||
DocumentStatus,
|
||||
FileSearchBackend,
|
||||
FileSearchConfig,
|
||||
)
|
||||
from agent_framework_foundry import (
|
||||
FoundryAgent,
|
||||
FoundryChatClient,
|
||||
@@ -31,7 +38,12 @@ from agent_framework_foundry_local import (
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AnalysisSection",
|
||||
"AnthropicFoundryClient",
|
||||
"ContentUnderstandingContextProvider",
|
||||
"DocumentStatus",
|
||||
"FileSearchBackend",
|
||||
"FileSearchConfig",
|
||||
"FoundryAgent",
|
||||
"FoundryChatClient",
|
||||
"FoundryChatOptions",
|
||||
|
||||
@@ -232,16 +232,18 @@ async def test_groupchat_kwargs_flow_to_agents() -> None:
|
||||
|
||||
async def test_kwargs_stored_in_state() -> None:
|
||||
"""Test that function_invocation_kwargs are stored in State with the correct key."""
|
||||
from agent_framework import Executor, WorkflowContext, handler
|
||||
from typing_extensions import Never
|
||||
|
||||
from agent_framework import AgentResponse, Executor, WorkflowContext, handler
|
||||
|
||||
stored_kwargs: dict[str, Any] | None = None
|
||||
|
||||
class _StateInspector(Executor):
|
||||
@handler
|
||||
async def inspect(self, msgs: list[Message], ctx: WorkflowContext[list[Message]]) -> None:
|
||||
async def inspect(self, msgs: list[Message], ctx: WorkflowContext[Never, AgentResponse]) -> None:
|
||||
nonlocal stored_kwargs
|
||||
stored_kwargs = ctx.get_state(WORKFLOW_RUN_KWARGS_KEY)
|
||||
await ctx.send_message(msgs)
|
||||
await ctx.yield_output(AgentResponse(messages=msgs))
|
||||
|
||||
inspector = _StateInspector(id="inspector")
|
||||
workflow = SequentialBuilder(participants=[inspector]).build()
|
||||
@@ -256,16 +258,18 @@ async def test_kwargs_stored_in_state() -> None:
|
||||
|
||||
async def test_empty_kwargs_stored_as_empty_dict() -> None:
|
||||
"""Test that empty kwargs are stored as empty dict in State."""
|
||||
from agent_framework import Executor, WorkflowContext, handler
|
||||
from typing_extensions import Never
|
||||
|
||||
from agent_framework import AgentResponse, Executor, WorkflowContext, handler
|
||||
|
||||
stored_kwargs: Any = "NOT_CHECKED"
|
||||
|
||||
class _StateChecker(Executor):
|
||||
@handler
|
||||
async def check(self, msgs: list[Message], ctx: WorkflowContext[list[Message]]) -> None:
|
||||
async def check(self, msgs: list[Message], ctx: WorkflowContext[Never, AgentResponse]) -> None:
|
||||
nonlocal stored_kwargs
|
||||
stored_kwargs = ctx.get_state(WORKFLOW_RUN_KWARGS_KEY)
|
||||
await ctx.send_message(msgs)
|
||||
await ctx.yield_output(AgentResponse(messages=msgs))
|
||||
|
||||
checker = _StateChecker(id="checker")
|
||||
workflow = SequentialBuilder(participants=[checker]).build()
|
||||
@@ -695,7 +699,9 @@ async def test_subworkflow_kwargs_accessible_via_state() -> None:
|
||||
Verifies that WORKFLOW_RUN_KWARGS_KEY is populated in the subworkflow's State
|
||||
with kwargs from the parent workflow.
|
||||
"""
|
||||
from agent_framework import Executor, WorkflowContext, handler
|
||||
from typing_extensions import Never
|
||||
|
||||
from agent_framework import AgentResponse, Executor, WorkflowContext, handler
|
||||
from agent_framework._workflows._workflow_executor import WorkflowExecutor
|
||||
|
||||
captured_kwargs_from_state: list[dict[str, Any]] = []
|
||||
@@ -704,10 +710,10 @@ async def test_subworkflow_kwargs_accessible_via_state() -> None:
|
||||
"""Executor that reads kwargs from State for verification."""
|
||||
|
||||
@handler
|
||||
async def read_kwargs(self, msgs: list[Message], ctx: WorkflowContext[list[Message]]) -> None:
|
||||
async def read_kwargs(self, msgs: list[Message], ctx: WorkflowContext[Never, AgentResponse]) -> None:
|
||||
kwargs_from_state = ctx.get_state(WORKFLOW_RUN_KWARGS_KEY)
|
||||
captured_kwargs_from_state.append(kwargs_from_state or {})
|
||||
await ctx.send_message(msgs)
|
||||
await ctx.yield_output(AgentResponse(messages=msgs))
|
||||
|
||||
# Build inner workflow with State reader
|
||||
state_reader = _StateReader(id="state_reader")
|
||||
|
||||
Reference in New Issue
Block a user