* Fix orchestration outputs so as_agent() returns the final answer only. Align other orchestration outputs * Fix orchestration output issues from review comments 1. Sample cleanup: Remove commented-out FoundryChatClient block and update prerequisites to reference OPENAI_CHAT_MODEL_ID instead of FOUNDRY_* vars. 2. Sequential approval output: Change _EndWithConversation.end_with_agent_executor_response from a no-op sink to yield response.agent_response. When the last participant is AgentApprovalExecutor (via with_request_info), _EndWithConversation is the output executor so the yield produces the terminal answer. When the last participant is a regular AgentExecutor, _EndWithConversation is not in output_executors so the yield is silently filtered out. 3. Forward data events through WorkflowExecutor: _process_workflow_result now also forwards 'data' events from sub-workflows so that emit_intermediate_data=True on AgentExecutor works correctly when wrapped in AgentApprovalExecutor. 4. Concurrent docstring: Update _AggregateAgentConversations docstring to say 'deterministic participant order' instead of 'completion order'. 5. Add test_concurrent_intermediate_outputs_emits_data_events verifying that ConcurrentBuilder(intermediate_outputs=True) emits per-participant data events alongside the single aggregated output event. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add tests for sequential workflow with_request_info and intermediate_outputs (#5301) Address PR review comments 2, 3, and 5: - Add test_sequential_request_info_last_participant_emits_output: Verifies that when the last participant is wrapped via with_request_info() (AgentApprovalExecutor), the workflow still emits a terminal output after approval, exercising the _EndWithConversation.end_with_agent_executor_response fallback path. - Add test_sequential_request_info_with_intermediate_outputs_emits_data_events: Verifies that emit_intermediate_data=True works correctly through AgentApprovalExecutor wrapping—WorkflowExecutor._process_result already forwards data events from sub-workflows, so intermediate agent responses surface as data events in the parent workflow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix pyright type errors from AgentResponse output refactor (#5301) Update cast() calls in _group_chat.py and _magentic.py to use WorkflowContext[Never, AgentResponse] instead of the old WorkflowContext[Never, list[Message]], matching the updated method signatures in _base_group_chat_orchestrator.py. Fix _sequential.py _EndWithConversation.end_with_agent_executor_response to declare WorkflowContext[Any, AgentResponse] so yield_output accepts AgentResponse[None]. Fix _workflow_executor.py data event forwarding to handle nullable executor_id. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix pyright reportUnknownVariableType in _agent.py (#5301) Extract event.data into a typed local variable before the isinstance check to avoid pyright narrowing it to AgentResponse[Unknown]. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix pyright reportMissingImports for orjson in file history samples (#5301) Add pyright: ignore[reportMissingImports] to orjson imports that are already guarded by try/except ImportError, matching the existing pattern used elsewhere in the samples. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5301: review comment fixes * Address review feedback for #5301: review comment fixes * Revert sequential_workflow_as_agent sample to FoundryChatClient Reverts the mistaken switch from FoundryChatClient to OpenAIChatClient in the sequential workflow as agent sample. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address ultrareview feedback: emit_data_events rename + WorkflowAgent reasoning conversion Layered on top of the prior review-feedback work in this branch. Renames: - AgentExecutor.emit_intermediate_data -> emit_data_events (mechanical rename; orchestration semantics live at the orchestration layer, not the general-purpose executor). Forwarded through MagenticAgentExecutor, AgentApprovalExecutor, and all orchestration call sites. - HandoffAgentExecutor._check_terminate_and_yield -> _should_terminate (pure predicate; no longer yields anything). HandoffBuilder docstring rewritten to describe the new per-agent AgentResponse output contract. WorkflowAgent reasoning-content conversion: - Add _rewrite_text_to_reasoning(contents) and _msg_as_reasoning(msg) helpers; the as_agent() path now reframes text content from data events as text_reasoning Content blocks before merging into the AgentResponse. - Consumers iterate msg.contents and branch on content.type — same path they already use for Claude thinking and OpenAI reasoning. No new field on Message/AgentResponse/WorkflowEvent. - Streaming branch constructs fresh AgentResponseUpdate instances instead of mutating shared payloads (regression test added). - Helper _msg_maybe_reasoning consolidates the conditional rewrite at three call sites in the non-streaming conversion. Tests: - TestWorkflowAgentReasoningHelpers + TestWorkflowAgentDataEventReasoningConversion add 9 new tests covering helpers, non-streaming, streaming, mixed content, already-reasoning passthrough, and mutation-safety regression. - Updated test_sequential_as_agent_with_intermediate_outputs_includes_chain to assert text_reasoning content for intermediate agents. * Fix pyright: widen event.data to Any to avoid partial-unknown narrowing The streaming conversion path narrowed event.data via isinstance against generic AgentResponse, producing AgentResponse[Unknown] and tripping reportUnknownVariableType/reportUnknownMemberType. Binding data: Any before the check keeps runtime behavior identical while restoring a fully known type for downstream access. * Clean up design * Scope to agent output semantics only * yield AgentResponseUpdate streaming, AgentResponse non-streaming * Fix mypy/pyright: widen cast types at GroupChat callsites Eight callsites in _group_chat.py still cast to WorkflowContext[Never, AgentResponse] but the base orchestrator methods now accept the wider WorkflowContext[Never, AgentResponse | AgentResponseUpdate] (mode-aware yields). W_OutT is invariant, so the narrower cast is not assignable. Magentic was widened in the same commit; this catches the GroupChat callsites that were missed. * Python: skip flaky Foundry / Foundry Hosting integration tests (#5553) These two integration tests have been failing in the merge queue across multiple unrelated PRs (5301, 5531). Both are marked `@pytest.mark.flaky` with 3 retries, but all attempts fail back-to-back. Skipping both with a reason pointing to #5553 so they can be fixed properly without continuing to block unrelated merges. - packages/foundry_hosting/tests/test_responses_int.py::TestOptions::test_temperature_and_max_tokens - packages/foundry/tests/foundry/test_foundry_embedding_client.py::TestFoundryEmbeddingIntegration::test_text_embedding_live Also includes a one-line uv.lock specifier-ordering normalization auto-applied by the poe-check pre-commit hook. --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Workflows Getting Started Samples
Installation
Microsoft Agent Framework Workflows support ships with the core agent-framework or agent-framework-core package, so no extra installation step is required.
To install with visualization support:
pip install agent-framework[viz] --pre
To export visualization images you also need to install GraphViz.
Samples Overview
Foundational Concepts - Start Here
Begin with the _start-here folder in order. These three samples introduce the core ideas of executors, edges, agents in workflows, and streaming.
| Sample | File | Concepts |
|---|---|---|
| Executors and Edges | _start-here/step1_executors_and_edges.py | Minimal workflow with basic executors and edges |
| Agents in a Workflow | _start-here/step2_agents_in_a_workflow.py | Introduces adding Agents as nodes; calling agents inside a workflow |
| Streaming (Basics) | _start-here/step3_streaming.py | Extends workflows with event streaming |
Once comfortable with these, explore the rest of the samples below.
Samples Overview (by directory)
functional
Write workflows as plain Python async functions — no graph concepts, no executor classes, no edges. Use native control flow (if/else, loops, asyncio.gather) for branching and parallelism.
| Sample | File | Concepts |
|---|---|---|
| Basic Pipeline | functional/basic_pipeline.py | Sequential steps as plain async functions |
| Basic Streaming Pipeline | functional/basic_streaming_pipeline.py | Stream workflow events in real time with run(stream=True) |
| Parallel Pipeline | functional/parallel_pipeline.py | Fan-out/fan-in with asyncio.gather |
| Steps and Checkpointing | functional/steps_and_checkpointing.py | @step decorator for per-step checkpointing and observability |
| Human-in-the-Loop Review | functional/hitl_review.py | HITL with ctx.request_info() and replay |
| Agent Integration | functional/agent_integration.py | Calling agents inside workflow steps |
| Naive Group Chat | functional/naive_group_chat.py | Simple round-robin group chat as a plain loop |
agents
| Sample | File | Concepts |
|---|---|---|
| Azure Chat Agents (Streaming) | agents/azure_chat_agents_streaming.py | Add Azure Chat agents as edges and handle streaming events |
| Azure AI Agents (Streaming) | agents/azure_ai_agents_streaming.py | Add Azure AI agents as edges and handle streaming events |
| Azure AI Agents (Shared Thread) | agents/azure_ai_agents_with_shared_session.py | Share a common message session between multiple Azure AI agents in a workflow |
| Custom Agent Executors | agents/custom_agent_executors.py | Create executors to handle agent run methods |
| Workflow as Agent (Reflection Pattern) | agents/workflow_as_agent_reflection_pattern.py | Wrap a workflow so it can behave like an agent (reflection pattern) |
| Workflow as Agent + HITL | agents/workflow_as_agent_human_in_the_loop.py | Extend workflow-as-agent with human-in-the-loop capability |
| Workflow as Agent with Session | agents/workflow_as_agent_with_session.py | Use AgentSession to maintain conversation history across workflow-as-agent invocations |
| Workflow as Agent kwargs | agents/workflow_as_agent_kwargs.py | Pass custom context (data, user tokens) via kwargs through workflow.as_agent() to @ai_function tools |
checkpoint
| Sample | File | Concepts |
|---|---|---|
| Checkpoint & Resume | checkpoint/checkpoint_with_resume.py | Create checkpoints, inspect them, and resume execution |
| Checkpoint & HITL Resume | checkpoint/checkpoint_with_human_in_the_loop.py | Combine checkpointing with human approvals and resume pending HITL requests |
| Checkpointed Sub-Workflow | checkpoint/sub_workflow_checkpoint.py | Save and resume a sub-workflow that pauses for human approval |
| Handoff + Tool Approval Resume | orchestrations/handoff_with_tool_approval_checkpoint_resume.py | Handoff workflow that captures tool-call approvals in checkpoints and resumes with human decisions |
| Workflow as Agent Checkpoint | checkpoint/workflow_as_agent_checkpoint.py | Enable checkpointing when using workflow.as_agent() with checkpoint_storage parameter |
| Cosmos DB Checkpoint Storage | checkpoint/cosmos_workflow_checkpointing.py | Use CosmosCheckpointStorage for durable workflow checkpointing backed by Azure Cosmos DB NoSQL |
| Cosmos DB + Foundry Checkpoint | checkpoint/cosmos_workflow_checkpointing_foundry.py | Multi-agent workflow using FoundryChatClient with CosmosCheckpointStorage for durable pause/resume |
composition
| Sample | File | Concepts |
|---|---|---|
| Sub-Workflow (Basics) | composition/sub_workflow_basics.py | Wrap a workflow as an executor and orchestrate sub-workflows |
| Sub-Workflow: Request Interception | composition/sub_workflow_request_interception.py | Intercept and forward sub-workflow requests using @handler for SubWorkflowRequestMessage |
| Sub-Workflow: Parallel Requests | composition/sub_workflow_parallel_requests.py | Multiple specialized interceptors handling different request types from same sub-workflow |
| Sub-Workflow: kwargs Propagation | composition/sub_workflow_kwargs.py | Pass custom context (user tokens, config) from parent workflow through to sub-workflow agents |
control-flow
| Sample | File | Concepts |
|---|---|---|
| Sequential Executors | control-flow/sequential_executors.py | Sequential workflow with explicit executor setup |
| Sequential (Streaming) | control-flow/sequential_streaming.py | Stream events from a simple sequential run |
| Edge Condition | control-flow/edge_condition.py | Conditional routing based on agent classification |
| Switch-Case Edge Group | control-flow/switch_case_edge_group.py | Switch-case branching using classifier outputs |
| Multi-Selection Edge Group | control-flow/multi_selection_edge_group.py | Select one or many targets dynamically (subset fan-out) |
| Simple Loop | control-flow/simple_loop.py | Feedback loop where an agent judges ABOVE/BELOW/MATCHED |
| Workflow Cancellation | control-flow/workflow_cancellation.py | Cancel a running workflow using asyncio tasks |
human-in-the-loop
| Sample | File | Concepts |
|---|---|---|
| Human-In-The-Loop (Guessing Game) | human-in-the-loop/guessing_game_with_human_input.py | Interactive request/response prompts with a human via ctx.request_info() |
| Agents with Approval Requests in Workflows | human-in-the-loop/agents_with_approval_requests.py | Agents that create approval requests during workflow execution and wait for human approval to proceed |
| Agents with Declaration-Only Tools | human-in-the-loop/agents_with_declaration_only_tools.py | Workflow pauses when agent calls a client-side tool (func=None), caller supplies the result |
Builder-oriented request-info samples are maintained in the orchestration sample set (sequential, concurrent, and group-chat builder variants).
tool-approval
Builder-based tool approval samples are maintained in the orchestration sample set.
observability
| Sample | File | Concepts |
|---|---|---|
| Executor I/O Observation | observability/executor_io_observation.py | Observe executor input/output data via executor_invoked events (type='executor_invoked') and executor_completed events (type='executor_completed') without modifying executor code |
For additional observability samples in Agent Framework, see the observability concept samples. The workflow observability sample demonstrates integrating observability into workflows.
orchestration
Orchestration-focused samples (Sequential, Concurrent, Handoff, GroupChat, Magentic), including builder-based
workflow.as_agent(...) variants, are documented in the orchestrations directory.
parallelism
| Sample | File | Concepts |
|---|---|---|
| Concurrent (Fan-out/Fan-in) | parallelism/fan_out_fan_in_edges.py | Dispatch to multiple executors and aggregate results |
| Aggregate Results of Different Types | parallelism/aggregate_results_of_different_types.py | Handle results of different types from multiple concurrent executors |
| Map-Reduce with Visualization | parallelism/map_reduce_and_visualization.py | Fan-out/fan-in pattern with diagram export |
state-management
| Sample | File | Concepts |
|---|---|---|
| State with Agents | state-management/state_with_agents.py | Store in state once and later reuse across agents |
| Workflow Kwargs - Global Context | state-management/workflow_kwargs_global.py | Pass custom context (data, user tokens) via kwargs to @tool tools in all agents |
| Workflow Kwargs - Per Agent | state-management/workflow_kwargs_per_agent.py | Pass custom context (data, user tokens) via kwargs to @tool tools in individual agents |
visualization
| Sample | File | Concepts |
|---|---|---|
| Concurrent with Visualization | visualization/concurrent_with_visualization.py | Fan-out/fan-in workflow with diagram export |
declarative
YAML-based declarative workflows allow you to define multi-agent orchestration patterns without writing Python code. See the declarative workflows README for more details on YAML workflow syntax and available actions.
| Sample | File | Concepts |
|---|---|---|
| Agent to Function Tool | declarative/agent_to_function_tool/ | Chain agent output to InvokeFunctionTool actions |
| Conditional Workflow | declarative/conditional_workflow/ | Nested conditional branching based on user input |
| Customer Support | declarative/customer_support/ | Multi-agent customer support with routing |
| Deep Research | declarative/deep_research/ | Research workflow with planning, searching, and synthesis |
| Function Tools | declarative/function_tools/ | Invoking Python functions from declarative workflows |
| Human-in-Loop | declarative/human_in_loop/ | Interactive workflows that request user input |
| Invoke Function Tool | declarative/invoke_function_tool/ | Call registered Python functions with InvokeFunctionTool |
| Marketing | declarative/marketing/ | Marketing content generation workflow |
| Simple Workflow | declarative/simple_workflow/ | Basic workflow with variable setting, conditionals, and loops |
| Student Teacher | declarative/student_teacher/ | Student-teacher interaction pattern |
resources
- Sample text inputs used by certain workflows:
Notes
- Agent-based samples use provider SDKs (Azure/OpenAI, etc.). Ensure credentials are configured, or adapt agents accordingly.
Sequential orchestration uses a few small adapter nodes for plumbing:
- "input-conversation" normalizes input to
list[Message] - "to-conversation:" converts agent responses into the shared conversation
- "complete" publishes the final output event (type='output') 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.
Why FoundryChatClient?
Workflow and orchestration samples use FoundryChatClient because they create agents locally and do not need
server-managed agent resources. This lightweight, project-backed chat client is a good fit for orchestration
patterns such as Sequential, Concurrent, Handoff, GroupChat, and Magentic.
If you need persistent server-side agent resources, use the hosted-agent flows rather than these workflow samples.
Environment Variables
Workflow samples that use FoundryChatClient expect:
FOUNDRY_PROJECT_ENDPOINT(Azure AI Foundry Agent Service (V2) project endpoint)FOUNDRY_MODEL(model deployment name)
These values are passed directly into the client constructor via os.getenv() in sample code.