Skip to content

Python: (core): Add functional workflow API#4238

Merged
eavanvalkenburg merged 17 commits intomicrosoft:mainfrom
moonbox3:functional-workflow-api
Apr 24, 2026
Merged

Python: (core): Add functional workflow API#4238
eavanvalkenburg merged 17 commits intomicrosoft:mainfrom
moonbox3:functional-workflow-api

Conversation

@moonbox3
Copy link
Copy Markdown
Contributor

Motivation and Context

The functional API is a stepping stone between single-agent use and the full graph API. Users write workflows as plain async functions -- no executor classes, no edges, no builder patterns.

  • Add @workflow and @step decorators for writing workflows as plain async functions
  • Native Python control flow (if/else, loops, asyncio.gather) replaces graph concepts
  • @step is opt-in: plain functions work inside @workflow without it. Use @step on expensive operations (agent calls, API requests) to save their results and skip re-execution on
    HITL resume or crash recovery
  • Streaming support via run(stream=True)
  • HITL support via ctx.request_info() with replay
  • .as_agent() wraps a functional workflow as an agent-compatible object

A very basic example of the functional workflow API:

@workflow
async def pipeline(data: str) -> str:
    upper = await to_upper(data)
    return await reverse(upper)

result = await pipeline.run("hello")
print(result.get_outputs())  # ['OLLEH']

Note: @step is opt-in for functions where per-step checkpointing matters (for example, agent calls). Without @step, workflows still support HITL and checkpointing — functions just re-execute on resume.

@step
async def call_agent(prompt: str) -> str:
    return (await agent.run(prompt)).text

@workflow
async def pipeline(data: str, ctx: RunContext) -> str:
    result = await call_agent(data)        # saved by @step
    validated = await validate(result)      # plain function, re-runs on resume
    feedback = await ctx.request_info(...)  # HITL pause
    return await finalize(result, feedback)

ctx: RunContext is only needed when you use HITL (request_info), custom events (add_event), or state (get_state/set_state). Otherwise, omit it for a cleaner signature.

Description

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.

Copilot AI review requested due to automatic review settings February 25, 2026 08:40
@markwallace-microsoft markwallace-microsoft added documentation Improvements or additions to documentation python labels Feb 25, 2026
@markwallace-microsoft
Copy link
Copy Markdown
Contributor

markwallace-microsoft commented Feb 25, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _feature_stage.py116893%98, 155, 166, 173, 180, 187, 206, 234
packages/core/agent_framework/_workflows
   _events.py1522186%90–91, 226, 230, 232, 264, 344, 359, 374, 389, 401–403, 415–417, 419–420, 422–423, 427
   _functional.py4893193%397–398, 464, 487, 496–498, 501, 531, 712–713, 720, 968, 999, 1002–1003, 1011–1013, 1088–1089, 1095, 1478–1479, 1481, 1488–1491, 1512, 1537
   _workflow.py2801993%91, 270–272, 274–275, 293, 297, 444, 639, 660, 716, 728, 734, 739, 759–761, 812
TOTAL29824348388% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
5998 30 💤 0 ❌ 0 🔥 1m 35s ⏱️

Copy link
Copy Markdown
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 introduces a functional workflow API as an alternative to the existing graph-based workflow API. The functional approach allows users to write workflows as plain async functions decorated with @workflow, using native Python control flow (if/else, loops, asyncio.gather) instead of explicit graph construction with executors and edges. The @step decorator is optional and provides per-step checkpointing, caching, and observability.

Changes:

  • Added core implementation (_functional.py) with @workflow, @step decorators, RunContext, FunctionalWorkflow, and FunctionalWorkflowAgent classes
  • Added comprehensive test suite (40+ test cases covering basic execution, HITL, checkpointing, streaming, error handling, edge cases)
  • Added 6 sample files demonstrating functional workflows (basic pipeline, streaming, parallel execution, checkpointing, HITL, agent integration)
  • Restructured getting-started samples to introduce functional workflows before graph workflows
  • Updated exports in __init__.py to expose new functional API symbols

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
python/packages/core/agent_framework/_workflows/_functional.py Core implementation of functional workflow API with RunContext, StepWrapper, FunctionalWorkflow, and FunctionalWorkflowAgent classes (1105 lines)
python/packages/core/agent_framework/__init__.py Added exports for FunctionalWorkflow, FunctionalWorkflowAgent, RunContext, StepWrapper, step, and workflow
python/packages/core/tests/workflow/test_functional_workflow.py Comprehensive test suite covering basic execution, events, parallelism, HITL, errors, streaming, state, checkpointing, control flow, and edge cases (1031 lines)
python/samples/01-get-started/05_first_functional_workflow.py Getting started sample demonstrating basic functional workflow with plain async functions
python/samples/01-get-started/06_first_graph_workflow.py Renamed and updated graph workflow sample (previously 05_first_workflow.py)
python/samples/01-get-started/07_host_your_agent.py Renamed agent hosting sample (previously 06_host_your_agent.py)
python/samples/01-get-started/README.md Updated sample listing to include both functional and graph workflow samples
python/samples/03-workflows/functional/basic_pipeline.py Sample showing simplest sequential pipeline with @workflow decorator
python/samples/03-workflows/functional/basic_streaming_pipeline.py Sample demonstrating streaming workflow events with run(stream=True)
python/samples/03-workflows/functional/parallel_pipeline.py Sample showing fan-out/fan-in with asyncio.gather
python/samples/03-workflows/functional/steps_and_checkpointing.py Sample explaining @step decorator for per-step checkpointing and observability
python/samples/03-workflows/functional/hitl_review.py Sample demonstrating HITL with ctx.request_info() and resume
python/samples/03-workflows/functional/agent_integration.py Sample showing agent calls inside workflows and .as_agent() wrapper
python/samples/03-workflows/README.md Added functional workflow section to samples overview

Comment thread python/samples/01-get-started/README.md Outdated
Comment thread python/packages/core/agent_framework/_workflows/_functional.py Outdated
Comment thread python/samples/03-workflows/functional/basic_pipeline.py
Comment thread python/samples/03-workflows/functional/hitl_review.py
Comment thread python/samples/03-workflows/functional/hitl_review.py
Comment thread python/samples/03-workflows/functional/agent_integration.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_functional.py Outdated
Comment thread python/samples/01-get-started/06_functional_workflow_basics.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_functional.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/samples/01-get-started/06_functional_workflow_basics.py
@moonbox3
Copy link
Copy Markdown
Contributor Author

Btw, @eavanvalkenburg and @TaoChenOSU I think it would be best to stick this functional API in to its own package. We want to get some more signal around the APIs and use of it before we deem it "GA worthy," IMO.

Comment thread python/samples/01-get-started/05_first_functional_workflow.py Outdated
Comment thread python/samples/01-get-started/06_functional_workflow_with_agents.py Outdated
Comment thread python/samples/01-get-started/06_functional_workflow_with_agents.py
Comment thread python/samples/01-get-started/06_functional_workflow_with_agents.py
Comment thread python/samples/03-workflows/functional/agent_integration.py Outdated
Comment thread python/samples/03-workflows/functional/hitl_review.py
Comment thread python/samples/03-workflows/README.md
Comment thread python/samples/03-workflows/functional/agent_integration.py Outdated
@TaoChenOSU
Copy link
Copy Markdown
Contributor

TaoChenOSU commented Mar 4, 2026

Btw, @eavanvalkenburg and @TaoChenOSU I think it would be best to stick this functional API in to its own package. We want to get some more signal around the APIs and use of it before we deem it "GA worthy," IMO.

We can use the experimental flag now :)

moonbox3 pushed a commit to moonbox3/agent-framework that referenced this pull request Mar 25, 2026
Comment 1 (request_info in @step): Already supported. Added comment in
StepWrapper.__call__ explaining why WorkflowInterrupted (BaseException)
safely bypasses the except Exception handler.

Comment 2 (None response): Added docstring to _get_response clarifying
the (found, value) return tuple semantics and None handling.

Comment 3 (bypass event type): executor_bypassed is already a dedicated
event type in WorkflowEventType. Updated comment at the bypass site to
make the deliberate event type choice explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@eavanvalkenburg eavanvalkenburg left a comment

Choose a reason for hiding this comment

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

I focused mostly on the samples to understand the API shape and it looks good, with some questions about how certain things work!

Comment thread python/packages/core/agent_framework/_workflows/_functional.py
Comment thread python/samples/01-get-started/05_functional_workflow_with_agents.py Outdated
Comment thread python/samples/01-get-started/05_functional_workflow_with_agents.py
Comment thread python/samples/01-get-started/06_functional_workflow_basics.py
Comment thread python/samples/03-workflows/functional/basic_streaming_pipeline.py
Comment thread python/samples/03-workflows/functional/basic_streaming_pipeline.py
Comment thread python/samples/03-workflows/functional/hitl_review.py
Comment thread python/samples/03-workflows/functional/hitl_review.py Outdated
Comment thread python/samples/03-workflows/functional/naive_group_chat.py Outdated
Comment thread python/samples/03-workflows/functional/parallel_pipeline.py
moonbox3 pushed a commit to moonbox3/agent-framework that referenced this pull request Apr 21, 2026
Comment 1 (request_info in @step): Already supported. Added comment in
StepWrapper.__call__ explaining why WorkflowInterrupted (BaseException)
safely bypasses the except Exception handler.

Comment 2 (None response): Added docstring to _get_response clarifying
the (found, value) return tuple semantics and None handling.

Comment 3 (bypass event type): executor_bypassed is already a dedicated
event type in WorkflowEventType. Updated comment at the bypass site to
make the deliberate event type choice explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
moonbox3 added a commit to moonbox3/agent-framework that referenced this pull request Apr 21, 2026
- RunContext docstring leads with purpose (opt-in handle for HITL,
  custom events, state) so readers importing it from the public surface
  understand its role before the mechanics (#2993513452).
- Rename `06_first_functional_workflow.py` to
  `06_functional_workflow_basics.py`; the previous filename was
  confusing since it followed `05_functional_workflow_with_agents.py`
  (#2993531979).
- Simplify `05_functional_workflow_with_agents.py` to call agents
  directly without a @step wrapper; the step-vs-no-step contrast lives
  in `03-workflows/functional/agent_integration.py`, keeping the
  get-started sample minimal (#2993525532).
- Switch functional samples to `FoundryChatClient` for consistency with
  the rest of 01-get-started and 03-workflows (follow-up on #2876988570).
- Use walrus in `hitl_review.py` final-state assertion (#2993572182).
- Add expected-output block to `basic_streaming_pipeline.py` (#2993557609).
- Clarify in `parallel_pipeline.py` that `@step` composes with
  `asyncio.gather` (#2993597282).
- `naive_group_chat.py` threads `list[Message]` between turns instead
  of stringifying the transcript, preserving role/authorship (#2993583231).

Drive-by: pre-commit hook sorts an unrelated import block in
`samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/main.py`.
@moonbox3 moonbox3 force-pushed the functional-workflow-api branch from f7c767a to c0093ac Compare April 21, 2026 07:37
@moonbox3
Copy link
Copy Markdown
Contributor Author

Revived this PR:

  • Rebased onto upstream/main (c0093ac, linear history — dropped the in-branch merge commits, resolved conflicts in agent_framework/__init__.py, samples/README.md, and samples/01-get-started/README.md without stomping either side)
  • Addressed the still-open review threads from @eavanvalkenburg in a single commit on top (c0093ac):
    • RunContext docstring rewritten to lead with purpose
    • 06_first_functional_workflow.py06_functional_workflow_basics.py
    • 05_functional_workflow_with_agents.py simplified (step-vs-no-step contrast stays in agent_integration.py)
    • All functional samples moved to FoundryChatClient for consistency
    • hitl_review.py uses the walrus as suggested
    • basic_streaming_pipeline.py gets an expected-output block
    • parallel_pipeline.py clarifies @step composes with asyncio.gather
    • naive_group_chat.py threads list[Message] between turns
    • Reply posted on each thread with the resolution

Verified green: 2319 core tests pass (workflow suite: 748 pass, 1 skip, 2 xfail — no regressions).

moonbox3 pushed a commit to moonbox3/agent-framework that referenced this pull request Apr 21, 2026
Comment 1 (request_info in @step): Already supported. Added comment in
StepWrapper.__call__ explaining why WorkflowInterrupted (BaseException)
safely bypasses the except Exception handler.

Comment 2 (None response): Added docstring to _get_response clarifying
the (found, value) return tuple semantics and None handling.

Comment 3 (bypass event type): executor_bypassed is already a dedicated
event type in WorkflowEventType. Updated comment at the bypass site to
make the deliberate event type choice explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
moonbox3 added a commit to moonbox3/agent-framework that referenced this pull request Apr 21, 2026
- RunContext docstring leads with purpose (opt-in handle for HITL,
  custom events, state) so readers importing it from the public surface
  understand its role before the mechanics (#2993513452).
- Rename `06_first_functional_workflow.py` to
  `06_functional_workflow_basics.py`; the previous filename was
  confusing since it followed `05_functional_workflow_with_agents.py`
  (#2993531979).
- Simplify `05_functional_workflow_with_agents.py` to call agents
  directly without a @step wrapper; the step-vs-no-step contrast lives
  in `03-workflows/functional/agent_integration.py`, keeping the
  get-started sample minimal (#2993525532).
- Switch functional samples to `FoundryChatClient` for consistency with
  the rest of 01-get-started and 03-workflows (follow-up on #2876988570).
- Use walrus in `hitl_review.py` final-state assertion (#2993572182).
- Add expected-output block to `basic_streaming_pipeline.py` (#2993557609).
- Clarify in `parallel_pipeline.py` that `@step` composes with
  `asyncio.gather` (#2993597282).
- `naive_group_chat.py` threads `list[Message]` between turns instead
  of stringifying the transcript, preserving role/authorship (#2993583231).

Drive-by: pre-commit hook sorts an unrelated import block in
`samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/main.py`.
@moonbox3 moonbox3 force-pushed the functional-workflow-api branch from c0093ac to bf7d586 Compare April 21, 2026 07:39
moonbox3 pushed a commit to moonbox3/agent-framework that referenced this pull request Apr 21, 2026
Comment 1 (request_info in @step): Already supported. Added comment in
StepWrapper.__call__ explaining why WorkflowInterrupted (BaseException)
safely bypasses the except Exception handler.

Comment 2 (None response): Added docstring to _get_response clarifying
the (found, value) return tuple semantics and None handling.

Comment 3 (bypass event type): executor_bypassed is already a dedicated
event type in WorkflowEventType. Updated comment at the bypass site to
make the deliberate event type choice explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
moonbox3 added a commit to moonbox3/agent-framework that referenced this pull request Apr 21, 2026
- RunContext docstring leads with purpose (opt-in handle for HITL,
  custom events, state) so readers importing it from the public surface
  understand its role before the mechanics (#2993513452).
- Rename `06_first_functional_workflow.py` to
  `06_functional_workflow_basics.py`; the previous filename was
  confusing since it followed `05_functional_workflow_with_agents.py`
  (#2993531979).
- Simplify `05_functional_workflow_with_agents.py` to call agents
  directly without a @step wrapper; the step-vs-no-step contrast lives
  in `03-workflows/functional/agent_integration.py`, keeping the
  get-started sample minimal (#2993525532).
- Switch functional samples to `FoundryChatClient` for consistency with
  the rest of 01-get-started and 03-workflows (follow-up on #2876988570).
- Use walrus in `hitl_review.py` final-state assertion (#2993572182).
- Add expected-output block to `basic_streaming_pipeline.py` (#2993557609).
- Clarify in `parallel_pipeline.py` that `@step` composes with
  `asyncio.gather` (#2993597282).
- `naive_group_chat.py` threads `list[Message]` between turns instead
  of stringifying the transcript, preserving role/authorship (#2993583231).

Drive-by: pre-commit hook sorts an unrelated import block in
`samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/main.py`.
@moonbox3 moonbox3 force-pushed the functional-workflow-api branch from c32a18e to 647019e Compare April 21, 2026 08:49
@moonbox3 moonbox3 self-assigned this Apr 22, 2026
@moonbox3 moonbox3 added the workflows Related to Workflows in agent-framework label Apr 22, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 24, 2026
@moonbox3 moonbox3 added this pull request to the merge queue Apr 24, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 24, 2026
moonbox3 and others added 17 commits April 24, 2026 11:35
- Swap 05/06 get-started samples: agent workflow first (motivates
  why workflows exist), simple text workflow second
- Rename text_pipeline → text_workflow, poem_pipeline → poem_workflow
- Add @step to agent workflow sample (05) to demonstrate caching
- Switch agent samples to AzureOpenAIResponsesClient with Foundry
- Remove .as_agent() from agent_integration.py to focus on the key
  difference between inline agent calls vs @step-cached calls
- Add commented-out Agent.run example in hitl_review.py
- Add clarifying comment in _functional.py that event streaming is
  buffered (not true per-token streaming)
- Add naive_group_chat.py functional sample: round-robin group chat
  as a plain Python loop
- Update READMEs to reflect new file names and group chat sample

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Allow request_info inside @step: Auto-inject RunContext into step
   functions that declare a RunContext parameter (by type or name 'ctx'),
   and expose get_run_context() for programmatic access.

2. Handle None responses: Log a warning when a response value is None,
   and document the behavior in request_info docstring.

3. Add executor_bypassed event type: Replace executor_invoked +
   executor_completed with a single executor_bypassed event when a step
   replays from cache, making cached vs live execution explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The three review comments (request_info in @step, None response handling,
executor_bypassed event type) were already addressed in 7da7db4. This
commit adds cross-cutting regression tests that exercise the interactions
between these features:

- HITL in step with caching: preceding step bypassed on resume
- Full checkpoint lifecycle with HITL step (interrupt -> resume -> restore)
- None response inside step-level request_info logs warning
- WorkflowInterrupted from step does not emit executor_failed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment 1 (request_info in @step): Already supported. Added comment in
StepWrapper.__call__ explaining why WorkflowInterrupted (BaseException)
safely bypasses the except Exception handler.

Comment 2 (None response): Added docstring to _get_response clarifying
the (found, value) return tuple semantics and None handling.

Comment 3 (bypass event type): executor_bypassed is already a dedicated
event type in WorkflowEventType. Updated comment at the bypass site to
make the deliberate event type choice explicit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mark all public classes and decorators (workflow, step, RunContext,
FunctionalWorkflow, StepWrapper, FunctionalWorkflowAgent) as
experimental and subject to change or removal.
- RunContext docstring leads with purpose (opt-in handle for HITL,
  custom events, state) so readers importing it from the public surface
  understand its role before the mechanics (#2993513452).
- Rename `06_first_functional_workflow.py` to
  `06_functional_workflow_basics.py`; the previous filename was
  confusing since it followed `05_functional_workflow_with_agents.py`
  (#2993531979).
- Simplify `05_functional_workflow_with_agents.py` to call agents
  directly without a @step wrapper; the step-vs-no-step contrast lives
  in `03-workflows/functional/agent_integration.py`, keeping the
  get-started sample minimal (#2993525532).
- Switch functional samples to `FoundryChatClient` for consistency with
  the rest of 01-get-started and 03-workflows (follow-up on #2876988570).
- Use walrus in `hitl_review.py` final-state assertion (#2993572182).
- Add expected-output block to `basic_streaming_pipeline.py` (#2993557609).
- Clarify in `parallel_pipeline.py` that `@step` composes with
  `asyncio.gather` (#2993597282).
- `naive_group_chat.py` threads `list[Message]` between turns instead
  of stringifying the transcript, preserving role/authorship (#2993583231).

Drive-by: pre-commit hook sorts an unrelated import block in
`samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/main.py`.
- bug_001: `ctx.request_info()` without an explicit `request_id` now derives
  a deterministic `auto::<index>` id from the call-counter, so HITL resume
  works correctly on the documented default path.  A uuid was regenerated on
  every replay, making resume impossible.

- bug_002: `StepWrapper.__call__` no longer deepcopies arguments on the
  cache-hit replay branch.  The copy is only performed on the live-execution
  path (for the event log) and falls back to the original mapping if deepcopy
  fails, so steps whose args aren't deepcopyable (locks, sockets, sessions)
  can still resume from checkpoint.

- bug_007: `_set_responses` now prunes each resolved `request_id` from
  `_pending_requests`, and the cache-hit branch in `request_info` does the
  same.  Previously, answered requests were re-serialized into every
  subsequent checkpoint and the final checkpoint falsely claimed pending
  requests even after the workflow completed.

- bug_008: `_compute_signature_hash` now mixes the function's `co_code` and
  `co_names` into the checkpoint signature, so changes to the workflow body
  invalidate older checkpoints even when steps are accessed via module /
  class attributes (which `_discover_step_names` can't see statically).
  `RunContext._record_observed_step` records observed step names for
  diagnostics.

- bug_010: `FunctionalWorkflow.run()` docstring corrected — says "at least
  one of message/responses/checkpoint_id" and explicitly notes `responses`
  may be combined with `checkpoint_id` (the validator already allowed this).

- bug_013: `FunctionalWorkflowAgent` now surfaces `request_info` events as
  `FunctionApprovalRequestContent` items (mirroring graph `WorkflowAgent`),
  threads `responses=` and `checkpoint_id=` through to the underlying
  workflow, and exposes `pending_requests`.  Previously `.as_agent()`
  returned empty `AgentResponse` for HITL workflows — effectively unusable.

- bug_014: `FunctionalWorkflow` now clears `_last_message`,
  `_last_step_cache`, and `_last_pending_request_ids` on clean completion.
  `run()` validates that `responses=` keys intersect the currently-pending
  request set (or raises with a clear error) instead of silently replaying
  against stale singleton state from a prior run.

- bug_015: `FunctionalWorkflow.as_agent` signature now matches graph
  `Workflow.as_agent`: accepts `name`, `description`, `context_providers`,
  and `**kwargs`.  `FunctionalWorkflowAgent` stores the overrides.

- bug_017: `RunContext.set_state` raises `ValueError` for underscore-
  prefixed keys (the framework's `_step_cache` / `_original_message` keys
  would silently clobber user state on checkpoint save and user
  underscore-prefixed state was dropped on restore).  Docstring documents
  the reserved prefix.

- merged_bug_003: Workflow function arity is validated at decoration time.
  Multiple non-ctx parameters raise `ValueError` immediately (previously
  every arg past the first was silently dropped at call time).  Passing a
  non-None `message` to a ctx-only workflow raises `ValueError` instead of
  silently discarding the message.

Test coverage: +18 regression tests covering every fix.  Full workflow
suite now 766 passed, 1 skipped, 2 xfailed; full core suite 2338 passed.
- Remove dead instrumentation added in the prior commit that was never
  consumed: `RunContext._observed_step_names`,
  `RunContext._record_observed_step`, `FunctionalWorkflow._runtime_step_names`,
  and `FunctionalWorkflowAgent._extra_kwargs`.  The signature hash relies on
  `co_code` alone, which covers the attribute-access case without the
  collection-scaffolding.
- Trim over-explanatory comments that restated what the code does or what
  it no longer does.  Keep only the comments that answer "why" for the
  non-obvious bits (deterministic id contract, defensive deepcopy, stale
  replay guard).
- Compress the `_compute_signature_hash` and FunctionalWorkflow `__init__`
  block docstrings without losing the user-facing reasoning.

Net -49 lines.  Regression lock preserved (766 passed, 1 skipped, 2 xfailed).
@eavanvalkenburg eavanvalkenburg force-pushed the functional-workflow-api branch from 9dad777 to 7c790b0 Compare April 24, 2026 09:35
@eavanvalkenburg eavanvalkenburg added this pull request to the merge queue Apr 24, 2026
Merged via the queue into microsoft:main with commit da32e8c Apr 24, 2026
33 checks passed
moonbox3 added a commit to moonbox3/agent-framework that referenced this pull request Apr 24, 2026
Previous commit incorrectly renamed the [1.1.1] header to [1.2.0], which
wiped the historical 1.1.1 entries and wrongly attributed them to 1.2.0.
This restores [1.1.1] to its origin/main content and adds a new [1.2.0]
section above containing only the commits in python-1.1.1..HEAD:

- microsoft#4238 functional workflow API
- microsoft#5142 GitHub Copilot OpenTelemetry
- microsoft#2403 A2A bridge support
- microsoft#5070 oauth_consent_request events in Foundry clients
- microsoft#5447 FoundryAgent hosted agent sessions
- microsoft#5459 hosting server dependency upgrade + types
- microsoft#5389 AG-UI reasoning/multimodal parsing fix
- microsoft#5440 stop [TOOLBOXES] warning spam
- microsoft#5455 user agent prefix fix

Also corrects the [1.2.0] compare base to python-1.1.1 (not 1.1.0) and
adds the missing [1.1.1] reference link.
moonbox3 added a commit that referenced this pull request Apr 24, 2026
* Bump Python package versions for 1.2.0 release

Released tier bumps 1.1.1 -> 1.2.0 (core, openai, foundry, root) to
reflect additive public APIs landed since 1.1.0: functional workflow API
(#4238) and FunctionTool SKIP_PARSING sentinel (#5424). All beta packages
stamped 1.0.0b260424, alpha packages 1.0.0a260424. All 26 non-core
agent-framework-core floors raised to >=1.2.0,<2. CHANGELOG consolidates
the never-tagged 1.1.1 entries with the post-merge additions into [1.2.0].

* Update CHANGELOG footer links for 1.2.0

Advance [Unreleased] comparison base from python-1.1.0 to python-1.2.0
and add a [1.2.0] reference link comparing python-1.1.0...python-1.2.0
so the heading links resolve correctly.

* Fix CHANGELOG: restore [1.1.1] section and add proper [1.2.0]

Previous commit incorrectly renamed the [1.1.1] header to [1.2.0], which
wiped the historical 1.1.1 entries and wrongly attributed them to 1.2.0.
This restores [1.1.1] to its origin/main content and adds a new [1.2.0]
section above containing only the commits in python-1.1.1..HEAD:

- #4238 functional workflow API
- #5142 GitHub Copilot OpenTelemetry
- #2403 A2A bridge support
- #5070 oauth_consent_request events in Foundry clients
- #5447 FoundryAgent hosted agent sessions
- #5459 hosting server dependency upgrade + types
- #5389 AG-UI reasoning/multimodal parsing fix
- #5440 stop [TOOLBOXES] warning spam
- #5455 user agent prefix fix

Also corrects the [1.2.0] compare base to python-1.1.1 (not 1.1.0) and
adds the missing [1.1.1] reference link.
learn-build-service-prod Bot pushed a commit to MicrosoftDocs/semantic-kernel-docs that referenced this pull request Apr 27, 2026
* Sync Agent Framework Python docs for PRs since 2026-04-22

Updates docs to reflect changes in microsoft/agent-framework Python PRs
merged after MicrosoftDocs/semantic-kernel-pr#982 (2026-04-22).

- get-started/workflows.md, AGENTS.md: rename
  01-get-started/05_first_workflow.py -> 07_first_graph_workflow.py
  (microsoft/agent-framework#4238).
- integrations/a2a.md: add Python section 'Exposing an Agent Framework
  agent over A2A' documenting A2AExecutor and the new
  agent_framework_to_a2a.py sample (microsoft/agent-framework#2403).
- agents/providers/github-copilot.md: add Observability subsection in
  Python zone showing configure_otel_providers() and
  RawGitHubCopilotAgent (microsoft/agent-framework#5142).
- agents/providers/microsoft-foundry.md: add 'Connecting to a deployed
  (hosted) Foundry agent' subsection covering allow_preview=True / v2
  and explicit AIProjectClient session creation
  (microsoft/agent-framework#5447).
- integrations/hyperlight.md: note that result_parser does not run on
  the sandbox path; tools must format in-sandbox themselves
  (microsoft/agent-framework#5424).

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

* Defer #4238 (functional workflow API) doc updates to PR author

Revert the get-started/workflows.md sample rename and the AGENTS.md
mapping update. The author of microsoft/agent-framework#4238 will
handle the docs (rename + new functional workflow page) in a
dedicated follow-up.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 workflows Related to Workflows in agent-framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: Add functional workflow API

6 participants