[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:
Evan Mattson
2026-02-06 16:47:20 +09:00
committed by GitHub
Unverified
parent 09f59b21ad
commit 0f3f4dbcaf
127 changed files with 1646 additions and 1703 deletions
@@ -57,9 +57,9 @@ from agent_framework.orchestrations import (
**Sequential orchestration note**: Sequential orchestration uses a few small adapter nodes for plumbing:
- `input-conversation` normalizes input to `list[ChatMessage]`
- `to-conversation:<participant>` converts agent responses into the shared conversation
- `complete` publishes the final `WorkflowOutputEvent`
- `complete` publishes the final output event (type='output')
These may appear in event streams (ExecutorInvoke/Completed). They're analogous to concurrent's dispatcher and aggregator and can be ignored if you only care about agent activity.
These may appear in event streams (executor_invoked/executor_completed). They're analogous to concurrent's dispatcher and aggregator and can be ignored if you only care about agent activity.
## Environment Variables
@@ -23,7 +23,7 @@ Demonstrates:
Prerequisites:
- Azure OpenAI access configured for AzureOpenAIChatClient (use az login + env vars)
- Familiarity with Workflow events (WorkflowOutputEvent)
- Familiarity with Workflow events (WorkflowEvent)
"""
@@ -7,7 +7,6 @@ from agent_framework import (
AgentResponseUpdate,
ChatAgent,
ChatMessage,
WorkflowOutputEvent,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import GroupChatBuilder
@@ -74,7 +73,7 @@ async def main() -> None:
# The agent orchestrator will intelligently decide when to end before this limit but just in case
.with_termination_condition(lambda messages: sum(1 for msg in messages if msg.role == "assistant") >= 4)
# Enable intermediate outputs to observe the conversation as it unfolds
# Intermediate outputs will be emitted as WorkflowOutputEvent events
# Intermediate outputs will be emitted as WorkflowEvent with type "output" events
.with_intermediate_outputs()
.build()
)
@@ -88,7 +87,7 @@ async def main() -> None:
# Keep track of the last response to format output nicely in streaming mode
last_response_id: str | None = None
async for event in workflow.run(task, stream=True):
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
data = event.data
if isinstance(data, AgentResponseUpdate):
rid = data.response_id
@@ -98,7 +97,7 @@ async def main() -> None:
print(f"{data.author_name}:", end=" ", flush=True)
last_response_id = rid
print(data.text, end="", flush=True)
else:
elif event.type == "output":
# The output of the group chat workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
print("\n" + "=" * 80)
@@ -8,7 +8,6 @@ from agent_framework import (
AgentResponseUpdate,
ChatAgent,
ChatMessage,
WorkflowOutputEvent,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import GroupChatBuilder
@@ -214,7 +213,7 @@ Share your perspective authentically. Feel free to:
.participants([farmer, developer, teacher, activist, spiritual_leader, artist, immigrant, doctor])
.with_termination_condition(lambda messages: sum(1 for msg in messages if msg.role == "assistant") >= 10)
# Enable intermediate outputs to observe the conversation as it unfolds
# Intermediate outputs will be emitted as WorkflowOutputEvent events
# Intermediate outputs will be emitted as WorkflowEvent with type "output" events
.with_intermediate_outputs()
.build()
)
@@ -241,7 +240,7 @@ Share your perspective authentically. Feel free to:
# Keep track of the last response to format output nicely in streaming mode
last_response_id: str | None = None
async for event in workflow.run(f"Please begin the discussion on: {topic}", stream=True):
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
data = event.data
if isinstance(data, AgentResponseUpdate):
rid = data.response_id
@@ -251,7 +250,7 @@ Share your perspective authentically. Feel free to:
print(f"{data.author_name}:", end=" ", flush=True)
last_response_id = rid
print(data.text, end="", flush=True)
else:
elif event.type == "output":
# The output of the group chat workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
print("\n" + "=" * 80)
@@ -7,7 +7,6 @@ from agent_framework import (
AgentResponseUpdate,
ChatAgent,
ChatMessage,
WorkflowOutputEvent,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import GroupChatBuilder, GroupChatState
@@ -92,7 +91,7 @@ async def main() -> None:
# have nothing to add, but for demo purposes we want to see at least one full round of interaction.
.with_termination_condition(lambda conversation: len(conversation) >= 6)
# Enable intermediate outputs to observe the conversation as it unfolds
# Intermediate outputs will be emitted as WorkflowOutputEvent events
# Intermediate outputs will be emitted as WorkflowEvent with type "output" events
.with_intermediate_outputs()
.build()
)
@@ -106,7 +105,7 @@ async def main() -> None:
# Keep track of the last response to format output nicely in streaming mode
last_response_id: str | None = None
async for event in workflow.run(task, stream=True):
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
data = event.data
if isinstance(data, AgentResponseUpdate):
rid = data.response_id
@@ -116,7 +115,7 @@ async def main() -> None:
print(f"{data.author_name}:", end=" ", flush=True)
last_response_id = rid
print(data.text, end="", flush=True)
else:
elif event.type == "output":
# The output of the group chat workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
print("\n" + "=" * 80)
@@ -8,8 +8,6 @@ from agent_framework import (
AgentResponseUpdate,
ChatAgent,
ChatMessage,
HandoffSentEvent,
WorkflowOutputEvent,
resolve_agent_id,
)
from agent_framework.azure import AzureOpenAIChatClient
@@ -112,9 +110,9 @@ async def main() -> None:
last_response_id: str | None = None
async for event in workflow.run(request, stream=True):
if isinstance(event, HandoffSentEvent):
print(f"\nHandoff Event: from {event.source} to {event.target}\n")
elif isinstance(event, WorkflowOutputEvent):
if event.type == "handoff_sent":
print(f"\nHandoff Event: from {event.data.source} to {event.data.target}\n")
elif event.type == "output":
data = event.data
if isinstance(data, AgentResponseUpdate):
if not data.text:
@@ -128,8 +126,8 @@ async def main() -> None:
print(f"{data.author_name}:", end=" ", flush=True)
last_response_id = rid
print(data.text, end="", flush=True)
else:
# The output of the group chat workflow is a collection of chat messages from all participants
elif event.type == "output":
# The output of the handoff workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
print("\n" + "=" * 80)
print("\nFinal Conversation Transcript:\n")
@@ -8,16 +8,13 @@ from agent_framework import (
AgentResponse,
ChatAgent,
ChatMessage,
RequestInfoEvent,
Workflow,
WorkflowEvent,
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder, HandoffSentEvent
from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder
from azure.identity import AzureCliCredential
logging.basicConfig(level=logging.ERROR)
@@ -107,35 +104,35 @@ def create_return_agent() -> ChatAgent:
)
def _handle_events(events: list[WorkflowEvent]) -> list[RequestInfoEvent]:
def _handle_events(events: list[WorkflowEvent]) -> list[WorkflowEvent[HandoffAgentUserRequest]]:
"""Process workflow events and extract any pending user input requests.
This function inspects each event type and:
- Prints workflow status changes (IDLE, IDLE_WITH_PENDING_REQUESTS, etc.)
- Displays final conversation snapshots when workflow completes
- Prints user input request prompts
- Collects all RequestInfoEvent instances for response handling
- Collects all request_info events for response handling
Args:
events: List of WorkflowEvent to process
Returns:
List of RequestInfoEvent representing pending user input requests
List of WorkflowEvent[HandoffAgentUserRequest] representing pending user input requests
"""
requests: list[RequestInfoEvent] = []
requests: list[WorkflowEvent[HandoffAgentUserRequest]] = []
for event in events:
if isinstance(event, HandoffSentEvent):
# HandoffSentEvent: Indicates a handoff has been initiated
print(f"\n[Handoff from {event.source} to {event.target} initiated.]")
elif isinstance(event, WorkflowStatusEvent) and event.state in {
if event.type == "handoff_sent":
# handoff_sent event: Indicates a handoff has been initiated
print(f"\n[Handoff from {event.data.source} to {event.data.target} initiated.]")
elif event.type == "status" and event.state in {
WorkflowRunState.IDLE,
WorkflowRunState.IDLE_WITH_PENDING_REQUESTS,
}:
# WorkflowStatusEvent: Indicates workflow state changes
# Status event: Indicates workflow state changes
print(f"\n[Workflow Status] {event.state.name}")
elif isinstance(event, WorkflowOutputEvent):
# WorkflowOutputEvent: Contains contents generated by the workflow
elif event.type == "output":
# Output event: Contains contents generated by the workflow
data = event.data
if isinstance(data, AgentResponse):
for message in data.messages:
@@ -144,7 +141,7 @@ def _handle_events(events: list[WorkflowEvent]) -> list[RequestInfoEvent]:
continue
speaker = message.author_name or message.role
print(f"- {speaker}: {message.text}")
else:
elif event.type == "output":
# The output of the handoff workflow is a collection of chat messages from all participants
conversation = cast(list[ChatMessage], event.data)
if isinstance(conversation, list):
@@ -153,11 +150,11 @@ def _handle_events(events: list[WorkflowEvent]) -> list[RequestInfoEvent]:
speaker = message.author_name or message.role
print(f"- {speaker}: {message.text or [content.type for content in message.contents]}")
print("===================================")
elif isinstance(event, RequestInfoEvent):
# RequestInfoEvent: Workflow is requesting user input
elif event.type == "request_info":
# Request info event: Workflow is requesting user input
if isinstance(event.data, HandoffAgentUserRequest):
_print_handoff_agent_user_request(event.data.agent_response)
requests.append(event)
requests.append(cast(WorkflowEvent[HandoffAgentUserRequest], event))
return requests
@@ -7,15 +7,12 @@ from agent_framework import (
AgentResponse,
ChatAgent,
ChatMessage,
RequestInfoEvent,
WorkflowEvent,
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
tool,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder, HandoffSentEvent
from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder
from azure.identity import AzureCliCredential
"""Sample: Simple handoff workflow.
@@ -102,35 +99,35 @@ def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAg
return triage_agent, refund_agent, order_agent, return_agent
def _handle_events(events: list[WorkflowEvent]) -> list[RequestInfoEvent]:
def _handle_events(events: list[WorkflowEvent]) -> list[WorkflowEvent[HandoffAgentUserRequest]]:
"""Process workflow events and extract any pending user input requests.
This function inspects each event type and:
- Prints workflow status changes (IDLE, IDLE_WITH_PENDING_REQUESTS, etc.)
- Displays final conversation snapshots when workflow completes
- Prints user input request prompts
- Collects all RequestInfoEvent instances for response handling
- Collects all request_info events for response handling
Args:
events: List of WorkflowEvent to process
Returns:
List of RequestInfoEvent representing pending user input requests
List of WorkflowEvent[HandoffAgentUserRequest] representing pending user input requests
"""
requests: list[RequestInfoEvent] = []
requests: list[WorkflowEvent[HandoffAgentUserRequest]] = []
for event in events:
if isinstance(event, HandoffSentEvent):
# HandoffSentEvent: Indicates a handoff has been initiated
print(f"\n[Handoff from {event.source} to {event.target} initiated.]")
elif isinstance(event, WorkflowStatusEvent) and event.state in {
if event.type == "handoff_sent":
# handoff_sent event: Indicates a handoff has been initiated
print(f"\n[Handoff from {event.data.source} to {event.data.target} initiated.]")
elif event.type == "status" and event.state in {
WorkflowRunState.IDLE,
WorkflowRunState.IDLE_WITH_PENDING_REQUESTS,
}:
# WorkflowStatusEvent: Indicates workflow state changes
print(f"\n[Workflow Status] {event.state.name}")
elif isinstance(event, WorkflowOutputEvent):
# WorkflowOutputEvent: Contains contents generated by the workflow
# Status event: Indicates workflow state changes
print(f"\n[Workflow Status] {event.state}")
elif event.type == "output":
# Output event: Contains contents generated by the workflow
data = event.data
if isinstance(data, AgentResponse):
for message in data.messages:
@@ -139,7 +136,7 @@ def _handle_events(events: list[WorkflowEvent]) -> list[RequestInfoEvent]:
continue
speaker = message.author_name or message.role
print(f"- {speaker}: {message.text}")
else:
elif event.type == "output":
# The output of the handoff workflow is a collection of chat messages from all participants
conversation = cast(list[ChatMessage], event.data)
if isinstance(conversation, list):
@@ -148,11 +145,9 @@ def _handle_events(events: list[WorkflowEvent]) -> list[RequestInfoEvent]:
speaker = message.author_name or message.role
print(f"- {speaker}: {message.text or [content.type for content in message.contents]}")
print("===================================")
elif isinstance(event, RequestInfoEvent):
# RequestInfoEvent: Workflow is requesting user input
if isinstance(event.data, HandoffAgentUserRequest):
_print_handoff_agent_user_request(event.data.agent_response)
requests.append(event)
elif event.type == "request_info" and isinstance(event.data, HandoffAgentUserRequest):
_print_handoff_agent_user_request(event.data.agent_response)
requests.append(cast(WorkflowEvent[HandoffAgentUserRequest], event))
return requests
@@ -6,7 +6,7 @@ Handoff Workflow with Code Interpreter File Generation Sample
This sample demonstrates retrieving file IDs from code interpreter output
in a handoff workflow context. A triage agent routes to a code specialist
that generates a text file, and we verify the file_id is captured correctly
from the streaming WorkflowOutputEvent events.
from the streaming workflow events.
Verifies GitHub issue #2718: files generated by code interpreter in
HandoffBuilder workflows can be properly retrieved.
@@ -34,13 +34,9 @@ from agent_framework import (
AgentResponseUpdate,
ChatAgent,
ChatMessage,
HandoffSentEvent,
HostedCodeInterpreterTool,
RequestInfoEvent,
WorkflowEvent,
WorkflowOutputEvent,
WorkflowRunState,
WorkflowStatusEvent,
)
from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder
from azure.identity.aio import AzureCliCredential
@@ -54,30 +50,29 @@ async def _drain(stream: AsyncIterable[WorkflowEvent]) -> list[WorkflowEvent]:
return [event async for event in stream]
def _handle_events(events: list[WorkflowEvent]) -> tuple[list[RequestInfoEvent], list[str]]:
def _handle_events(events: list[WorkflowEvent]) -> tuple[list[WorkflowEvent[HandoffAgentUserRequest]], list[str]]:
"""Process workflow events and extract file IDs and pending requests.
Returns:
Tuple of (pending_requests, file_ids_found)
"""
requests: list[RequestInfoEvent] = []
requests: list[WorkflowEvent[HandoffAgentUserRequest]] = []
file_ids: list[str] = []
for event in events:
if isinstance(event, HandoffSentEvent):
# HandoffSentEvent: Indicates a handoff has been initiated
print(f"\n[Handoff from {event.source} to {event.target} initiated.]")
elif isinstance(event, WorkflowStatusEvent) and event.state in {
if event.type == "handoff_sent":
print(f"\n[Handoff from {event.data.source} to {event.data.target} initiated.]")
elif event.type == "status" and event.state in {
WorkflowRunState.IDLE,
WorkflowRunState.IDLE_WITH_PENDING_REQUESTS,
}:
# WorkflowStatusEvent: Indicates workflow state changes
print(f"\n[Workflow Status] {event.state.name}")
elif isinstance(event, WorkflowOutputEvent):
# WorkflowOutputEvent: Contains contents generated by the workflow
print(f"[status] {event.state.name}")
elif event.type == "request_info" and isinstance(event.data, HandoffAgentUserRequest):
requests.append(cast(WorkflowEvent[HandoffAgentUserRequest], event))
elif event.type == "output":
data = event.data
if isinstance(data, AgentResponseUpdate):
# AgentResponseUpdate: Intermediate output from an agent
for content in data.contents:
if content.type == "hosted_file":
file_ids.append(content.file_id) # type: ignore
@@ -87,8 +82,7 @@ def _handle_events(events: list[WorkflowEvent]) -> tuple[list[RequestInfoEvent],
file_id = annotation["file_id"] # type: ignore
file_ids.append(file_id)
print(f"[Found file annotation: file_id={file_id}]")
else:
# The output of the handoff workflow is a collection of chat messages from all participants
elif event.type == "output":
conversation = cast(list[ChatMessage], event.data)
if isinstance(conversation, list):
print("\n=== Final Conversation Snapshot ===")
@@ -96,9 +90,6 @@ def _handle_events(events: list[WorkflowEvent]) -> tuple[list[RequestInfoEvent],
speaker = message.author_name or message.role
print(f"- {speaker}: {message.text or [content.type for content in message.contents]}")
print("===================================")
elif isinstance(event, RequestInfoEvent):
# RequestInfoEvent: Workflow is requesting user input
requests.append(event)
return requests, file_ids
@@ -9,12 +9,11 @@ from agent_framework import (
AgentResponseUpdate,
ChatAgent,
ChatMessage,
GroupChatRequestSentEvent,
HostedCodeInterpreterTool,
WorkflowOutputEvent,
WorkflowEvent,
)
from agent_framework.openai import OpenAIChatClient, OpenAIResponsesClient
from agent_framework.orchestrations import MagenticBuilder, MagenticOrchestratorEvent, MagenticProgressLedger
from agent_framework.orchestrations import GroupChatRequestSentEvent, MagenticBuilder, MagenticProgressLedger
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(__name__)
@@ -85,7 +84,7 @@ async def main() -> None:
max_reset_count=2,
)
# Enable intermediate outputs to observe the conversation as it unfolds
# Intermediate outputs will be emitted as WorkflowOutputEvent events
# Intermediate outputs will be emitted as WorkflowEvent events
.with_intermediate_outputs()
.build()
)
@@ -104,41 +103,44 @@ async def main() -> None:
# Keep track of the last executor to format output nicely in streaming mode
last_response_id: str | None = None
output_event: WorkflowEvent | None = None
async for event in workflow.run(task, stream=True):
if isinstance(event, MagenticOrchestratorEvent):
print(f"\n[Magentic Orchestrator Event] Type: {event.event_type.name}")
if isinstance(event.data, ChatMessage):
print(f"Please review the plan:\n{event.data.text}")
elif isinstance(event.data, MagenticProgressLedger):
print(f"Please review progress ledger:\n{json.dumps(event.data.to_dict(), indent=2)}")
if event.type == "output" and isinstance(event.data, AgentResponseUpdate):
response_id = event.data.response_id
if response_id != last_response_id:
if last_response_id is not None:
print("\n")
print(f"- {event.executor_id}:", end=" ", flush=True)
last_response_id = response_id
print(event.data, end="", flush=True)
elif event.type == "magentic_orchestrator":
print(f"\n[Magentic Orchestrator Event] Type: {event.data.event_type.name}")
if isinstance(event.data.content, ChatMessage):
print(f"Please review the plan:\n{event.data.content.text}")
elif isinstance(event.data.content, MagenticProgressLedger):
print(f"Please review progress ledger:\n{json.dumps(event.data.content.to_dict(), indent=2)}")
else:
print(f"Unknown data type in MagenticOrchestratorEvent: {type(event.data)}")
print(f"Unknown data type in MagenticOrchestratorEvent: {type(event.data.content)}")
# Block to allow user to read the plan/progress before continuing
# Note: this is for demonstration only and is not the recommended way to handle human interaction.
# Please refer to `with_plan_review` for proper human interaction during planning phases.
await asyncio.get_event_loop().run_in_executor(None, input, "Press Enter to continue...")
elif isinstance(event, GroupChatRequestSentEvent):
print(f"\n[REQUEST SENT ({event.round_index})] to agent: {event.participant_name}")
elif event.type == "group_chat" and isinstance(event.data, GroupChatRequestSentEvent):
print(f"\n[REQUEST SENT ({event.data.round_index})] to agent: {event.data.participant_name}")
elif isinstance(event, WorkflowOutputEvent):
data = event.data
if isinstance(data, AgentResponseUpdate):
response_id = data.response_id
if response_id != last_response_id:
if last_response_id is not None:
print("\n")
print(f"- {event.executor_id}:", end=" ", flush=True)
last_response_id = response_id
print(event.data, end="", flush=True)
else:
# The output of the magentic workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], event.data)
print("\n" + "=" * 80)
print("\nFinal Conversation Transcript:\n")
for message in outputs:
print(f"{message.author_name or message.role}: {message.text}\n")
elif event.type == "output":
output_event = event
if output_event:
# The output of the magentic workflow is a collection of chat messages from all participants
outputs = cast(list[ChatMessage], output_event.data)
print("\n" + "=" * 80)
print("\nFinal Conversation Transcript:\n")
for message in outputs:
print(f"{message.author_name or message.role}: {message.text}\n")
if __name__ == "__main__":
@@ -9,11 +9,9 @@ from agent_framework import (
ChatAgent,
ChatMessage,
FileCheckpointStorage,
RequestInfoEvent,
WorkflowCheckpoint,
WorkflowOutputEvent,
WorkflowEvent,
WorkflowRunState,
WorkflowStatusEvent,
)
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import MagenticBuilder, MagenticPlanReviewRequest
@@ -105,16 +103,16 @@ async def main() -> None:
print("\n=== Stage 1: run until plan review request (checkpointing active) ===")
workflow = build_workflow(checkpoint_storage)
# Run the workflow until the first RequestInfoEvent is surfaced. The event carries the
# Run the workflow until the first is surfaced. The event carries the
# request_id we must reuse on resume. In a real system this is where the UI would present
# the plan for human review.
plan_review_request: MagenticPlanReviewRequest | None = None
async for event in workflow.run(TASK, stream=True):
if isinstance(event, RequestInfoEvent) and event.request_type is MagenticPlanReviewRequest:
if event.type == "request_info" and event.request_type is MagenticPlanReviewRequest:
plan_review_request = event.data
print(f"Captured plan review request: {event.request_id}")
if isinstance(event, WorkflowStatusEvent) and event.state is WorkflowRunState.IDLE_WITH_PENDING_REQUESTS:
if event.type == "status" and event.state is WorkflowRunState.IDLE_WITH_PENDING_REQUESTS:
break
if plan_review_request is None:
@@ -147,9 +145,9 @@ async def main() -> None:
approval = plan_review_request.approve()
# Resume execution and capture the re-emitted plan review request.
request_info_event: RequestInfoEvent | None = None
request_info_event: WorkflowEvent | None = None
async for event in resumed_workflow.run(checkpoint_id=resume_checkpoint.checkpoint_id, stream=True):
if isinstance(event, RequestInfoEvent) and isinstance(event.data, MagenticPlanReviewRequest):
if event.type == "request_info" and isinstance(event.data, MagenticPlanReviewRequest):
request_info_event = event
if request_info_event is None:
@@ -158,9 +156,9 @@ async def main() -> None:
print(f"Resumed plan review request: {request_info_event.request_id}")
# Supply the approval and continue to run to completion.
final_event: WorkflowOutputEvent | None = None
final_event: WorkflowEvent | None = None
async for event in resumed_workflow.send_responses_streaming({request_info_event.request_id: approval}):
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
final_event = event
if final_event is None:
@@ -218,12 +216,12 @@ async def main() -> None:
if pending_messages == 0:
print("Checkpoint has no pending messages; no additional work expected on resume.")
final_event_post: WorkflowOutputEvent | None = None
final_event_post: WorkflowEvent | None = None
post_emitted_events = False
post_plan_workflow = build_workflow(checkpoint_storage)
async for event in post_plan_workflow.run(checkpoint_id=post_plan_checkpoint.checkpoint_id, stream=True):
post_emitted_events = True
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
final_event_post = event
if final_event_post is None:
@@ -9,9 +9,7 @@ from agent_framework import (
AgentResponseUpdate,
ChatAgent,
ChatMessage,
RequestInfoEvent,
WorkflowEvent,
WorkflowOutputEvent,
)
from agent_framework.openai import OpenAIChatClient
from agent_framework.orchestrations import MagenticBuilder, MagenticPlanReviewRequest, MagenticPlanReviewResponse
@@ -46,10 +44,10 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str
requests: dict[str, MagenticPlanReviewRequest] = {}
async for event in stream:
if isinstance(event, RequestInfoEvent) and event.request_type is MagenticPlanReviewRequest:
if event.type == "request_info" and event.request_type is MagenticPlanReviewRequest:
requests[event.request_id] = cast(MagenticPlanReviewRequest, event.data)
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
data = event.data
if isinstance(data, AgentResponseUpdate):
rid = data.response_id
@@ -68,7 +66,7 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str
# To make the type checker happy, we cast event.data to the expected type
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, MagenticPlanReviewResponse] = {}
@@ -129,7 +127,7 @@ async def main() -> None:
# Request human input for plan review
.with_plan_review()
# Enable intermediate outputs to observe the conversation as it unfolds
# Intermediate outputs will be emitted as WorkflowOutputEvent events
# Intermediate outputs will be emitted as WorkflowEvent with type "output"
.with_intermediate_outputs()
.build()
)
@@ -3,7 +3,7 @@
import asyncio
from typing import cast
from agent_framework import ChatMessage, WorkflowOutputEvent
from agent_framework import ChatMessage
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.orchestrations import SequentialBuilder
from azure.identity import AzureCliCredential
@@ -48,7 +48,7 @@ async def main() -> None:
# 3) Run and collect outputs
outputs: list[list[ChatMessage]] = []
async for event in workflow.run("Write a tagline for a budget-friendly eBike.", stream=True):
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
outputs.append(cast(list[ChatMessage], event.data))
if outputs: