Skip to content

Python: Durable Support for Workflows#3630

Merged
dmytrostruk merged 30 commits intomainfrom
durable-workflows-v2
Feb 17, 2026
Merged

Python: Durable Support for Workflows#3630
dmytrostruk merged 30 commits intomainfrom
durable-workflows-v2

Conversation

@ahmedmuhsin
Copy link
Contributor

@ahmedmuhsin ahmedmuhsin commented Feb 2, 2026

Motivation and Context

The agent-framework-azurefunctions package currently supports single-agent and multi-agent orchestration via durable entities. However, it does not support MAF's Workflow abstraction — the graph-based orchestration model with conditional routing, parallel fan-out/fan-in, and human-in-the-loop (HITL) patterns. This PR bridges that gap by adding full workflow orchestration support to Azure Durable Functions, enabling developers to define workflows using WorkflowBuilder and run them durably with automatic HTTP endpoints.

Description

This PR adds workflow orchestration support to the agent-framework-azurefunctions package, enabling MAF Workflow graphs to execute on Azure Durable Functions. The implementation adapts MAF's async edge-routing model to Durable Functions' generator-based orchestration model (yield instead of await).

Architecture

The workflow engine distinguishes between two executor types and routes them differently:

  • Agent executors → dispatched to existing durable entity infrastructure (DurableAIAgent)
  • Non-agent executors → dispatched to new activity functions (dafx-{executor_id})

This design reuses MAF's edge group routing logic (SingleEdgeGroup, FanOutEdgeGroup, FanInEdgeGroup, SwitchCaseEdgeGroup) while adapting execution to the DF generator model.

New Modules

Module Purpose
_workflow.py (978 lines) Main orchestration engine — edge routing, agent/activity dispatch, fan-out/fan-in coordination, HITL event handling, shared state management
_context.py (168 lines) CapturingRunnerContext — captures messages/events during activity execution without durable storage
_serialization.py (217 lines) Serialization wrappers adding Pydantic model support on top of core checkpoint encoding (encode_checkpoint_value/decode_checkpoint_value)

Changes to Existing Modules

  • _app.py — Extended AgentFunctionApp to accept an optional workflow parameter. When provided, automatically:

    • Extracts agents from workflow executors and registers them as entities
    • Creates activity functions for non-agent executors
    • Sets up the workflow orchestrator and all HTTP endpoints
    • Registers HITL endpoints for status polling and response delivery
    • Changed duplicate agent registration from raising ValueError to logging a warning and skipping (needed because workflow extraction may produce overlapping agent lists)
    • Added _build_error_response() static helper for consistent error JSON responses
  • pyproject.toml — Increased test timeout from 120s to 300s (workflow integration tests with Azurite + Durable Task Scheduler need more time)

Core Package Changes (Minimal)

  • _workflows/__init__.py — Exported encode_checkpoint_value, decode_checkpoint_value, and State so the azurefunctions package can reuse the core checkpoint encoding system
  • _agent_executor.py — Added public .agent property to AgentExecutor so the azurefunctions package can extract the underlying SupportsAgentRun instance when extracting agents from workflow executors

HTTP Endpoints (Auto-registered)

Endpoint Method Description
/api/workflow/run POST Start a workflow orchestration
/api/workflow/status/{instanceId} GET Get orchestration status, outputs, and pending HITL requests
/api/workflow/respond/{instanceId}/{requestId} POST Send a response to a pending HITL request

HITL (Human-in-the-Loop) Support

The workflow engine detects RequestInfoEvent instances from executor activities, exposes them via the status endpoint as pendingHumanInputRequests, and uses wait_for_external_event to pause the orchestration. When a human response arrives via the respond endpoint, the orchestrator resumes and routes the response back through the executor's @response_handler method. Configurable timeout with automatic rejection on expiry.

Samples (4 new)

Sample Pattern
09_workflow_shared_state Linear workflow with State for cross-executor data sharing
10_workflow_no_shared_state Linear workflow using message-only data passing (no shared state)
11_workflow_parallel Fan-out/fan-in with three patterns: executor-only, agent-only, and mixed agent+executor parallel execution
12_workflow_hitl Content moderation pipeline with AI analysis → human approval → publish/reject using request_info/@response_handler

Each sample includes function_app.py, README.md, demo.http, host.json, local.settings.json.sample, and requirements.txt. All samples support dual-mode execution: Azure Functions (default) or pure MAF with DevUI (--maf flag).

Tests

Test File Type Count Description
test_workflow.py Unit 14 Edge routing logic, message extraction, response construction
test_utils.py Unit 23 Serialization roundtrip (dataclass, Pydantic, nested), CapturingRunnerContext, reconstruct_to_type
test_app.py Unit 5 AgentFunctionApp workflow initialization, agent extraction, executor activity registration
test_09_workflow_shared_state.py Integration 3 End-to-end shared state workflow via Azurite + Durable Task Scheduler
test_10_workflow_no_shared_state.py Integration 3 End-to-end message-only workflow
test_11_workflow_parallel.py Integration 4 End-to-end parallel fan-out/fan-in patterns
test_12_workflow_hitl.py Integration 4 End-to-end HITL with approval, rejection, and timeout scenarios

All 152 unit tests and 14 integration tests pass.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

@markwallace-microsoft markwallace-microsoft added documentation Improvements or additions to documentation python labels Feb 3, 2026
@github-actions github-actions bot changed the title Durable Support for Workflows in Python Python: Durable Support for Workflows in Python Feb 3, 2026
@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Feb 3, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/azurefunctions/agent_framework_azurefunctions
   _app.py50017265%261–262, 265, 267–269, 275, 277–280, 282–283, 285–287, 291, 294, 296, 298–299, 302–304, 306, 308, 316, 324–327, 330, 333, 338–339, 342–345, 348, 351–353, 364–367, 373, 375, 383–384, 387, 392–393, 395–396, 398, 401, 404, 406, 408, 410–412, 416–419, 421, 423–424, 426, 437–439, 443–444, 446–447, 449, 460–465, 473, 475, 481–483, 489–490, 492–493, 495–498, 502, 508, 527, 626–627, 735, 743–744, 764–766, 772–774, 780–782, 815–816, 876–877, 926–927, 932, 1014, 1017, 1026–1028, 1030–1032, 1034, 1036, 1047, 1049–1052, 1054, 1056–1057, 1059, 1066–1067, 1069–1070, 1072–1073, 1075, 1079, 1089–1091, 1093–1094, 1096–1098, 1105, 1107–1108, 1110, 1131, 1136, 1148, 1220, 1310, 1325–1328, 1353
   _context.py66690%98, 102, 153–154, 162, 169
   _serialization.py431174%40–46, 114–115, 129–130
   _workflow.py31821432%153, 158, 160, 167, 216–218, 288–290, 292–294, 316, 322, 324–325, 348–349, 351–355, 357, 364, 389, 392–401, 404–405, 407, 434–435, 438–439, 442–448, 451–452, 455–460, 463–466, 469, 471–475, 490–492, 494, 497–498, 501–506, 508–509, 511–512, 514–515, 518, 536–540, 547, 572, 579–581, 584–585, 587, 635, 638–639, 642, 647, 649–651, 654, 659–663, 666–669, 671–672, 675–679, 681–682, 685–686, 689–690, 693, 695, 698–699, 702, 718–719, 722–723, 725, 727, 729, 732–733, 741–746, 749, 752, 759–761, 766, 768, 771, 798–800, 803, 806–808, 810–812, 815–818, 828–830, 832–835, 845, 847, 861–862, 899, 902–904, 907, 910, 913, 915–916, 922, 926, 935, 939, 952, 958–959, 962–964, 967–974, 977–978
packages/core/agent_framework/_workflows
   _agent_executor.py2012786%97, 113, 150, 168–169, 221–222, 224–225, 255–257, 265–267, 275–277, 279, 283, 287, 291–292, 391–392, 438, 456
TOTAL21166345083% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
4086 239 💤 0 ❌ 0 🔥 1m 16s ⏱️

@ahmedmuhsin ahmedmuhsin changed the title Python: Durable Support for Workflows in Python Python: Durable Support for Workflows Feb 3, 2026
@ahmedmuhsin ahmedmuhsin marked this pull request as ready for review February 3, 2026 16:55
@ahmedmuhsin ahmedmuhsin requested a review from a team as a code owner February 3, 2026 16:55
Copilot AI review requested due to automatic review settings February 3, 2026 16:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive workflow orchestration support to Azure Functions, enabling multi-agent workflows with conditional routing, parallel execution, and human-in-the-loop (HITL) patterns using Azure Durable Functions.

Changes:

  • New workflow orchestration engine in _workflow.py that executes MAF Workflows using Durable Functions' generator-based model
  • Serialization utilities in _serialization.py for cross-activity message passing
  • Capturing runner context in _context.py for activity execution
  • Extended AgentFunctionApp with workflow parameter and auto-registration of agents
  • Four new samples (09-12) demonstrating shared state, stateless, parallel, and HITL workflow patterns
  • Comprehensive unit and integration tests (152 unit tests, 14 integration tests)
  • Bug fix: Updated chat_client.as_agent() usage with default_options parameter

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
_workflow.py Main orchestration engine with HITL support, parallel execution, and edge group routing
_serialization.py Serialization/deserialization for dataclasses, Pydantic models, and MAF types
_context.py CapturingRunnerContext for activity execution without durable storage
_app.py Extended AgentFunctionApp with workflow support and HTTP endpoints
_agent_executor.py Added agent property to expose underlying agent
Samples 09-12 Four new workflow samples with requirements, configs, and README files
Test files Unit tests for workflow utilities and integration tests for all samples
pyproject.toml Increased test timeout from 120s to 300s for workflow tests
Sample 07 Fixed type annotations for Azure Functions worker compatibility
Comments suppressed due to low confidence (1)

python/packages/azurefunctions/agent_framework_azurefunctions/_app.py:1056

  • This import of module asyncio is redundant, as it was previously imported on line 9.
        import asyncio

@ahmedmuhsin ahmedmuhsin force-pushed the durable-workflows-v2 branch 2 times, most recently from 5edfd45 to 3c3c325 Compare February 4, 2026 19:30
@ahmedmuhsin ahmedmuhsin force-pushed the durable-workflows-v2 branch 2 times, most recently from 2530661 to 3d7d917 Compare February 10, 2026 17:16
@ahmedmuhsin ahmedmuhsin force-pushed the durable-workflows-v2 branch 2 times, most recently from c3843db to 42b6f97 Compare February 12, 2026 20:02
ahmedmuhsin and others added 24 commits February 17, 2026 13:18
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…_message_content_from_dict since it is not needed
- State.import_state/export_state are now sync (removed await)
- Add State.commit() before export_state() in activity execution
- Rename executor parameter shared_state -> state
- Rename ctx.set_shared_state/get_shared_state -> set_state/get_state (sync)
- WorkflowBuilder now takes start_executor as constructor kwarg
- Update WorkflowOutputEvent -> WorkflowEvent with type='output'
- Update RequestInfoEvent -> WorkflowEvent[Any]
- Update SharedState -> State in test imports
- Update duplicate agent name tests to match new warning behavior
- Update sample README API references
- Add workflow samples 09-12 to 04-hosting/azure_functions/
- Adapt to ChatMessage -> Message rename from main
- Adapt to pickle-based checkpoint encoding from main
- Simplify _serialization.py to delegate to core encode/decode
- Fix Message -> WorkflowMessage disambiguation in _context.py
- Remove non-existent _checkpoint_summary import
@dmytrostruk dmytrostruk added this pull request to the merge queue Feb 17, 2026
Merged via the queue into main with commit bb3d3c2 Feb 17, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

Comments