mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
[BREAKING] Python: Refactor workflow events to unified discriminated union pattern (#3690)
* Refactor events * Merge main * Fixes * Cleanup * Update samples and tests * Remove unused imports * PR feedback * Merge main. Add properties for events to help typing * Formatting * Cleanup * use builtins.type to avoid shadowing by WorkflowEvent.type attribute * Final improvements
This commit is contained in:
committed by
GitHub
Unverified
parent
09f59b21ad
commit
0f3f4dbcaf
+7
-9
@@ -6,14 +6,12 @@ from typing import Annotated
|
||||
|
||||
from agent_framework import (
|
||||
ChatMessage,
|
||||
ConcurrentBuilder,
|
||||
Content,
|
||||
RequestInfoEvent,
|
||||
WorkflowEvent,
|
||||
WorkflowOutputEvent,
|
||||
tool,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework.orchestrations import ConcurrentBuilder
|
||||
|
||||
"""
|
||||
Sample: Concurrent Workflow with Tool Approval Requests
|
||||
@@ -36,7 +34,7 @@ agents may independently trigger approval requests.
|
||||
|
||||
Demonstrate:
|
||||
- Handling multiple approval requests from different agents in concurrent workflows.
|
||||
- Handling RequestInfoEvent during concurrent agent execution.
|
||||
- Handling during concurrent agent execution.
|
||||
- Understanding that approval pauses only the agent that triggered it, not all agents.
|
||||
|
||||
Prerequisites:
|
||||
@@ -89,12 +87,12 @@ def get_portfolio_balance() -> str:
|
||||
return "Portfolio: $50,000 invested, $10,000 cash available. Holdings: AAPL, GOOGL, MSFT."
|
||||
|
||||
|
||||
def _print_output(event: WorkflowOutputEvent) -> None:
|
||||
def _print_output(event: WorkflowEvent) -> None:
|
||||
if not event.data:
|
||||
raise ValueError("WorkflowOutputEvent has no data")
|
||||
raise ValueError("WorkflowEvent has no data")
|
||||
|
||||
if not isinstance(event.data, list) and not all(isinstance(msg, ChatMessage) for msg in event.data):
|
||||
raise ValueError("WorkflowOutputEvent data is not a list of ChatMessage")
|
||||
raise ValueError("WorkflowEvent data is not a list of ChatMessage")
|
||||
|
||||
messages: list[ChatMessage] = event.data # type: ignore
|
||||
|
||||
@@ -109,10 +107,10 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str
|
||||
"""Process events from the workflow stream to capture human feedback requests."""
|
||||
requests: dict[str, Content] = {}
|
||||
async for event in stream:
|
||||
if isinstance(event, RequestInfoEvent) and isinstance(event.data, Content):
|
||||
if event.type == "request_info" and isinstance(event.data, Content):
|
||||
# We are only expecting tool approval requests in this sample
|
||||
requests[event.request_id] = event.data
|
||||
elif isinstance(event, WorkflowOutputEvent):
|
||||
elif event.type == "output":
|
||||
_print_output(event)
|
||||
|
||||
responses: dict[str, Content] = {}
|
||||
|
||||
+5
-8
@@ -7,14 +7,11 @@ from typing import Annotated, cast
|
||||
from agent_framework import (
|
||||
ChatMessage,
|
||||
Content,
|
||||
GroupChatBuilder,
|
||||
GroupChatState,
|
||||
RequestInfoEvent,
|
||||
WorkflowEvent,
|
||||
WorkflowOutputEvent,
|
||||
tool,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework.orchestrations import GroupChatBuilder, GroupChatState
|
||||
|
||||
"""
|
||||
Sample: Group Chat Workflow with Tool Approval Requests
|
||||
@@ -36,7 +33,7 @@ different agents have different levels of tool access.
|
||||
|
||||
Demonstrate:
|
||||
- Using set_select_speakers_func with agents that have approval-required tools.
|
||||
- Handling RequestInfoEvent in group chat scenarios.
|
||||
- Handling request_info events (type='request_info') in group chat scenarios.
|
||||
- Multi-round group chat with tool approval interruption and resumption.
|
||||
|
||||
Prerequisites:
|
||||
@@ -99,16 +96,16 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str
|
||||
"""Process events from the workflow stream to capture human feedback requests."""
|
||||
requests: dict[str, Content] = {}
|
||||
async for event in stream:
|
||||
if isinstance(event, RequestInfoEvent) and isinstance(event.data, Content):
|
||||
if event.type == "request_info" and isinstance(event.data, Content):
|
||||
# We are only expecting tool approval requests in this sample
|
||||
requests[event.request_id] = event.data
|
||||
elif isinstance(event, WorkflowOutputEvent):
|
||||
elif event.type == "output":
|
||||
# The output of the workflow comes from the orchestrator and it's a list of messages
|
||||
print("\n" + "=" * 60)
|
||||
print("Workflow summary:")
|
||||
outputs = cast(list[ChatMessage], event.data)
|
||||
for msg in outputs:
|
||||
speaker = msg.author_name or msg.role.value
|
||||
speaker = msg.author_name or msg.role
|
||||
print(f"[{speaker}]: {msg.text}")
|
||||
|
||||
responses: dict[str, Content] = {}
|
||||
|
||||
+8
-8
@@ -7,13 +7,11 @@ from typing import Annotated, cast
|
||||
from agent_framework import (
|
||||
ChatMessage,
|
||||
Content,
|
||||
RequestInfoEvent,
|
||||
SequentialBuilder,
|
||||
WorkflowEvent,
|
||||
WorkflowOutputEvent,
|
||||
tool,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework.orchestrations import SequentialBuilder
|
||||
|
||||
"""
|
||||
Sample: Sequential Workflow with Tool Approval Requests
|
||||
@@ -26,7 +24,7 @@ This sample works as follows:
|
||||
1. A SequentialBuilder workflow is created with a single agent that has tools requiring approval.
|
||||
2. The agent receives a user task and determines it needs to call a sensitive tool.
|
||||
3. The tool call triggers a function_approval_request Content, pausing the workflow.
|
||||
4. The sample simulates human approval by responding to the RequestInfoEvent.
|
||||
4. The sample simulates human approval by responding to the .
|
||||
5. Once approved, the tool executes and the agent completes its response.
|
||||
6. The workflow outputs the final conversation with all messages.
|
||||
|
||||
@@ -36,7 +34,7 @@ requiring any additional builder configuration.
|
||||
|
||||
Demonstrate:
|
||||
- Using @tool(approval_mode="always_require") for sensitive operations.
|
||||
- Handling RequestInfoEvent with function_approval_request Content in sequential workflows.
|
||||
- Handling with function_approval_request Content in sequential workflows.
|
||||
- Resuming workflow execution after approval via send_responses_streaming.
|
||||
|
||||
Prerequisites:
|
||||
@@ -55,7 +53,9 @@ def execute_database_query(
|
||||
return f"Query executed successfully. Results: 3 rows affected by '{query}'"
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/getting_started/tools/function_tool_with_approval.py and samples/getting_started/tools/function_tool_with_approval_and_threads.py.
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production;
|
||||
# see samples/getting_started/tools/function_tool_with_approval.py and
|
||||
# samples/getting_started/tools/function_tool_with_approval_and_threads.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_database_schema() -> str:
|
||||
"""Get the current database schema. Does not require approval."""
|
||||
@@ -71,10 +71,10 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str
|
||||
"""Process events from the workflow stream to capture human feedback requests."""
|
||||
requests: dict[str, Content] = {}
|
||||
async for event in stream:
|
||||
if isinstance(event, RequestInfoEvent) and isinstance(event.data, Content):
|
||||
if event.type == "request_info" and isinstance(event.data, Content):
|
||||
# We are only expecting tool approval requests in this sample
|
||||
requests[event.request_id] = event.data
|
||||
elif isinstance(event, WorkflowOutputEvent):
|
||||
elif event.type == "output":
|
||||
# The output of the workflow comes from the orchestrator and it's a list of messages
|
||||
print("\n" + "=" * 60)
|
||||
print("Workflow summary:")
|
||||
|
||||
Reference in New Issue
Block a user