Python: [Breaking] Remove WorkflowCompletedEvent, introduce workflow output and migrate to ctx.yield_output() + a huge refactoring (#845)

* Introduce input and output types for executor and workflow

* WorkflowOutputContext handles two types

* Remove can_handle_types from Executor

* Update validation

* Move workflow executor

* Move workflow executor

* Fix issues in WorkflowExecutor

* refactor executor

* update execute signature to create workflow context within Executor

* fix simple sub workflow test; fix validation

* fix output types in WorkflowExecutor

* fix issue in Executor handling of SubWorkflowRequestInfo

* update tests to use proper workflow output

* update orchestration patterns to use output

* Update sample -- not finished

* Update python/packages/main/tests/workflow/test_workflow_states.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update python/packages/main/tests/workflow/test_concurrent.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* address comments

* WorkflowOutputContext --> WorkflowContext

* remove WorkflowCompletedEvent

* update samples

* Update doc string for important classes; update WorkflowExecutor to support concurrent execution

* use Never instead of None for default type

* Update usage of WorkflowContext[None to WorkflowContext[Never

* address comments

* remove filter for None

* address comments, minor fixes

* quality of life improvement on interceptor types

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Eric Zhu
2025-09-23 13:52:53 -07:00
committed by GitHub
Unverified
parent 0f913bcdeb
commit 2133043f11
67 changed files with 2564 additions and 1648 deletions
@@ -2,7 +2,8 @@
import asyncio
from dataclasses import dataclass
from typing import Any
from typing_extensions import Never
from agent_framework import (
AgentExecutor,
@@ -13,8 +14,8 @@ from agent_framework import (
Executor,
Role,
WorkflowBuilder,
WorkflowCompletedEvent,
WorkflowContext,
WorkflowOutputEvent,
WorkflowViz,
handler,
)
@@ -71,7 +72,7 @@ class AggregateInsights(Executor):
self._expert_ids = expert_ids
@handler
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext[Any]) -> None:
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext[Never, str]) -> None:
# Map responses to text by executor id for a simple, predictable demo.
by_id: dict[str, str] = {}
for r in results:
@@ -97,7 +98,7 @@ class AggregateInsights(Executor):
f"Legal/Compliance Notes:\n{aggregated.legal}\n"
)
await ctx.add_event(WorkflowCompletedEvent(data=consolidated))
await ctx.yield_output(consolidated)
async def main() -> None:
@@ -165,17 +166,13 @@ async def main() -> None:
print("Tip: Install 'viz' extra to export workflow visualization: pip install agent-framework[viz]")
# 3) Run with a single prompt
completion: WorkflowCompletedEvent | None = None
async for event in workflow.run_stream("We are launching a new budget-friendly electric bike for urban commuters."):
if isinstance(event, AgentRunEvent):
# Show which agent ran and what step completed.
print(event)
if isinstance(event, WorkflowCompletedEvent):
completion = event
if completion:
print("===== Final Aggregated Output =====")
print(completion.data)
elif isinstance(event, WorkflowOutputEvent):
print("===== Final Aggregated Output =====")
print(event.data)
if __name__ == "__main__":