[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
@@ -3,7 +3,7 @@
import asyncio
import random
from agent_framework import Executor, WorkflowBuilder, WorkflowContext, WorkflowOutputEvent, handler
from agent_framework import Executor, WorkflowBuilder, WorkflowContext, handler
from typing_extensions import Never
"""
@@ -87,7 +87,7 @@ async def main() -> None:
# 2) Run the workflow
output: list[int | float] | None = None
async for event in workflow.run([random.randint(1, 100) for _ in range(10)], stream=True):
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
output = event.data
if output is not None:
@@ -3,18 +3,14 @@
import asyncio
from dataclasses import dataclass
from agent_framework import ( # Core chat primitives to build LLM requests
from agent_framework import (
AgentExecutorRequest, # The message bundle sent to an AgentExecutor
AgentExecutorResponse, # The structured result returned by an AgentExecutor
ChatAgent, # Tracing event for agent execution steps
ChatMessage, # Chat message structure
Executor, # Base class for custom Python executors
ExecutorCompletedEvent,
ExecutorInvokedEvent,
Role, # Enum of chat roles (user, assistant, system)
WorkflowBuilder, # Fluent builder for wiring the workflow graph
WorkflowContext, # Per run context and event bus
WorkflowOutputEvent, # Event emitted when workflow yields output
handler, # Decorator to mark an Executor method as invokable
)
from agent_framework.azure import AzureOpenAIChatClient
@@ -45,7 +41,7 @@ class DispatchToExperts(Executor):
@handler
async def dispatch(self, prompt: str, ctx: WorkflowContext[AgentExecutorRequest]) -> None:
# Wrap the incoming prompt as a user message for each expert and request a response.
initial_message = ChatMessage(Role.USER, text=prompt)
initial_message = ChatMessage("user", text=prompt)
await ctx.send_message(AgentExecutorRequest(messages=[initial_message], should_respond=True))
@@ -143,12 +139,12 @@ async def main() -> None:
async for event in workflow.run(
"We are launching a new budget-friendly electric bike for urban commuters.", stream=True
):
if isinstance(event, ExecutorInvokedEvent):
if event.type == "executor_invoked":
# Show when executors are invoked and completed for lightweight observability.
print(f"{event.executor_id} invoked")
elif isinstance(event, ExecutorCompletedEvent):
elif event.type == "executor_completed":
print(f"{event.executor_id} completed")
elif isinstance(event, WorkflowOutputEvent):
elif event.type == "output":
print("===== Final Aggregated Output =====")
print(event.data)
@@ -10,8 +10,7 @@ import aiofiles
from agent_framework import (
Executor, # Base class for custom workflow steps
WorkflowBuilder, # Fluent builder for executors and edges
WorkflowContext, # Per run context with workflow state and messaging
WorkflowOutputEvent, # Event emitted when workflow yields output
WorkflowContext, # Per run context with shared state and messaging
WorkflowViz, # Utility to visualize a workflow graph
handler, # Decorator to expose an Executor method as a step
)
@@ -332,7 +331,7 @@ async def main():
# Step 4: Run the workflow with the raw text as input.
async for event in workflow.run(raw_text, stream=True):
print(f"Event: {event}")
if isinstance(event, WorkflowOutputEvent):
if event.type == "output":
print(f"Final Output: {event.data}")