Skip to content

Python: Add workflow reset#6407

Draft
TaoChenOSU wants to merge 8 commits into
mainfrom
feature/python-add-workflow-reset
Draft

Python: Add workflow reset#6407
TaoChenOSU wants to merge 8 commits into
mainfrom
feature/python-add-workflow-reset

Conversation

@TaoChenOSU

@TaoChenOSU TaoChenOSU commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Motivation and Context

When a workflow is hosted as an agent in Foundry Hosted Agent, a new message comes in without a conversation ID or a previous response ID should trigger a new workflow run, without any residual states in the workflow from previous interactions.

This is currently not possible unless a new workflow instance is built from the builder, which is impossible in hosted agent since the workflow instance is only created once when a session starts. This PR addresses the limitation by introducing reset_for_new_run.

Description

  1. Refactor _workflow.py, _runner.py and _runner_context.py such that a workflow contains only the minimal amount of state of a run. Responsibilities are also made to be more clearly distributed across workflow, runner and runner_context.
  2. Add reset to workflow, runner, and executors.
  3. Add tests and samples.

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.

@TaoChenOSU TaoChenOSU self-assigned this Jun 8, 2026
Copilot AI review requested due to automatic review settings June 8, 2026 23:08
@TaoChenOSU TaoChenOSU added python workflows Related to Workflows in agent-framework labels Jun 8, 2026
@github-actions github-actions Bot changed the title Add workflow reset Python: Add workflow reset Jun 8, 2026
@moonbox3 moonbox3 added the documentation Improvements or additions to documentation label Jun 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 an explicit “reset between independent runs” capability for Python workflows, so a single Workflow instance (and its executors/orchestrators) can be safely reused without leaking in-memory state across unrelated jobs/conversations. It also tightens concurrent-run protection by reserving the runner synchronously, and wires reset behavior into orchestrations and Foundry hosting.

Changes:

  • Add Workflow.reset_for_new_run() / Runner.reset_for_new_run() plus per-executor reset() hooks, and switch concurrency guarding to a synchronous Runner.reserve() lifecycle.
  • Implement reset behavior for key executors/orchestrators (e.g., AgentExecutor, WorkflowExecutor, group chat + Magentic orchestrators) and validate via new unit tests.
  • Add a new workflow sample (workflow_reset.py) and index it in the workflows samples README; update Foundry hosting to reset workflows when there’s no prior conversation context to restore.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
python/samples/03-workflows/state-management/workflow_reset.py New sample demonstrating Workflow.reset_for_new_run() for per-job state isolation.
python/samples/03-workflows/README.md Adds the new reset sample to the workflows samples table.
python/packages/orchestrations/tests/test_group_chat.py Adds reset-focused tests for group chat orchestrators and end-to-end workflow reset.
python/packages/orchestrations/agent_framework_orchestrations/_magentic.py Adds Magentic-specific _reset_pattern_state() implementation.
python/packages/orchestrations/agent_framework_orchestrations/_group_chat.py Clears per-run cache and conditionally rotates/preserves sessions on reset.
python/packages/orchestrations/agent_framework_orchestrations/_base_group_chat_orchestrator.py Adds an async reset() that clears shared conversation state and calls a pattern hook.
python/packages/foundry_hosting/tests/test_responses.py Adds tests ensuring hosting resets workflows for independent requests (and doesn’t reset when restoring checkpoints).
python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py Resets the workflow when there is no prior checkpoint context to restore.
python/packages/core/tests/workflow/test_workflow.py Updates concurrency exception expectations; adds Workflow.reset_for_new_run() coverage.
python/packages/core/tests/workflow/test_sub_workflow.py Adds reset coverage for WorkflowExecutor (sub-workflows).
python/packages/core/tests/workflow/test_runner.py Adds reservation lifecycle + runner reset tests; updates existing tests to call reserve().
python/packages/core/tests/workflow/test_executor.py Adds tests for default and overridden Executor.reset().
python/packages/core/tests/workflow/test_agent_executor.py Adds reset tests for AgentExecutor, including session ownership behavior.
python/packages/core/tests/core/test_mcp_skills.py Formatting-only refactors for test readability (no behavior changes).
python/packages/core/tests/core/test_mcp_observability.py Formatting-only refactors for test readability (no behavior changes).
python/packages/core/agent_framework/_workflows/_workflow.py Switches to runner reservation for concurrency; stores run kwargs via Runner.state; adds reset_for_new_run().
python/packages/core/agent_framework/_workflows/_workflow_executor.py Adds WorkflowExecutor.reset() to clear in-flight execution mappings and reset wrapped workflow.
python/packages/core/agent_framework/_workflows/_runner.py Introduces runner lifecycle enum, synchronous reserve()/release(), and reset_for_new_run() that clears state/context and calls executor.reset().
python/packages/core/agent_framework/_workflows/_executor.py Adds async Executor.reset() hook (default no-op) for workflow resets.
python/packages/core/agent_framework/_workflows/_agent_executor.py Makes reset async; clears per-run buffers and rotates session only when auto-created (warns when caller-supplied).
python/packages/core/agent_framework/_skills.py Small formatting simplifications.
python/packages/core/agent_framework/_mcp.py Small formatting simplifications.

Comment thread python/packages/core/agent_framework/_workflows/_workflow.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_runner.py Outdated
Comment thread python/packages/core/tests/workflow/test_runner.py Outdated
Comment thread python/packages/core/tests/workflow/test_runner.py Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 4 | Confidence: 63%

✓ Security Reliability

No actionable issues found in this dimension.

✓ Test Coverage

The PR adds extensive test coverage for the new workflow reset functionality across Runner, AgentExecutor, WorkflowExecutor, Workflow, base orchestrator, and the foundry hosting layer. The one notable gap is that MagenticOrchestrator._reset_pattern_state() — which clears four state fields — has no dedicated unit test, unlike the analogous AgentBasedGroupChatOrchestrator._reset_pattern_state() which is tested in test_group_chat.py.

✓ Failure Modes

The PR adds a well-designed workflow reset mechanism with proper lifecycle state management (IDLE/RESERVED/RUNNING enum replacing a boolean flag) and comprehensive test coverage. The main concern is that Runner.reset_for_new_run() mutates shared state (iteration counter, context, State) before iterating over executors to call reset(). If any executor's reset() raises (e.g., AgentExecutor.create_session() fails for a remote agent), the runner is left in an inconsistent half-reset state with no rollback path. The formatting changes are purely cosmetic and correct.

✓ Design Approach

I found one design issue in the workflow run-lock changes. The new synchronous Runner.reserve() call correctly blocks races before the first await, but this code path no longer attaches a ResponseStream cleanup hook. That means a caller can obtain workflow.run(..., stream=True) and drop the returned stream before iteration starts, leaving the runner permanently reserved and causing later runs on the same workflow instance to fail as if one were still in progress. That leaves users with two contradictory lifecycle contracts for the same type.


Automated review by TaoChenOSU's agents

Comment thread python/packages/core/agent_framework/_workflows/_workflow.py
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   _skills.py10213796%294, 541, 1007, 1022, 1024–1025, 1381–1382, 1394–1395, 1625, 1654, 2117, 2573–2574, 2709, 2714, 2717, 2722, 2749, 2754, 2808, 2817, 2822, 2825, 2830, 2854, 2859, 3101–3102, 3451, 3678–3679, 3706–3707, 3714–3715
packages/core/agent_framework/_workflows
   _agent_executor.py2161593%166, 194, 235, 259, 279–280, 360–362, 364, 525, 552–553, 625, 631
   _executor.py1861094%211, 335, 337, 346, 366, 369, 476, 481, 491, 658
   _runner.py178298%330–331
   _runner_context.py1591193%66, 80–81, 83–84, 86, 381, 400, 454, 467, 471
   _workflow.py3512493%61, 63, 68, 92, 97, 158, 194, 412–414, 416–417, 441, 475, 643, 872, 893, 941, 953, 959, 964, 984–986
   _workflow_executor.py2003085%95, 405, 465, 488, 490, 498–499, 504, 506, 511, 513, 622–628, 632–634, 642, 647, 658, 668, 672, 678, 682, 692, 696
packages/foundry_hosting/agent_framework_foundry_hosting
   _responses.py7718588%186–189, 254, 314–315, 329, 332–333, 381–382, 392, 429, 484, 498, 549, 552–556, 575, 578, 584, 586, 645, 1023, 1036, 1505–1507, 1509, 1556–1557, 1559–1560, 1562–1563, 1565–1566, 1571, 1580, 1583–1585, 1587, 1601, 1614, 1659–1660, 1662, 1667–1671, 1673, 1680–1681, 1683–1684, 1690, 1692–1696, 1703, 1709, 1731, 1737, 1743, 1745, 1747–1750, 1758, 1760, 1811–1813, 1823–1824
packages/orchestrations/agent_framework_orchestrations
   _base_group_chat_orchestrator.py1801293%109, 277, 292, 326–328, 332, 351, 439, 485–487
   _group_chat.py3397079%183, 372, 379, 412, 423–424, 430, 435, 456, 460, 473–474, 487, 502–503, 505, 521, 548–553, 555, 589–592, 594, 599–603, 697, 700, 739, 742, 745, 748, 756, 768–769, 771–772, 774–775, 777, 782, 785, 794, 800, 844–845, 849–850, 864–865, 867–868, 899–900, 966, 985, 993, 998–1000, 1007, 1017
   _magentic.py6099185%73–82, 87, 91–102, 267, 278, 282, 302, 363, 372, 374, 416, 433, 442–443, 445–447, 449, 460, 611, 613, 653, 703, 739–741, 743, 753, 761–762, 835–838, 929, 935, 941, 983, 1021, 1053, 1070, 1081, 1138–1139, 1143–1145, 1169, 1193–1194, 1207, 1227, 1250, 1304–1305, 1343–1344, 1513, 1516, 1525, 1528, 1533, 1584–1585, 1626–1627, 1675, 1706, 1764, 1778, 1789
TOTAL38615441088% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
7778 34 💤 0 ❌ 0 🔥 1m 57s ⏱️

@TaoChenOSU TaoChenOSU marked this pull request as ready for review June 9, 2026 16:40

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated Code Review

Reviewers: 5 | Confidence: 89%

✓ Correctness

No actionable issues found in this dimension.

✓ Security Reliability

The InProcRunnerContext.reset_for_new_run() implementation does not clear _pending_request_info_events, violating the documented 'clean slate' contract of Workflow.reset_for_new_run() which states it resets EVERYTHING 'including pending requests/messages'. In the foundry-hosting use case (the motivating scenario), stale request_info events from a prior workflow run would persist after reset, constituting a state leak.

✓ Test Coverage

The PR adds comprehensive test coverage for the new reset_for_new_run feature across Workflow, Runner, AgentExecutor, WorkflowExecutor, and orchestrators. However, two test coverage gaps stand out: (1) InProcRunnerContext.reset_for_new_run() does not clear _pending_request_info_events, and the tests don't verify this behavior—meaning pending approval requests from a prior run could leak into a fresh run after reset, contradicting the documented guarantee; (2) MagenticOrchestrator._reset_pattern_state() is new production code with no dedicated test. The group chat orchestrator reset tests are well-structured and cover the base class, agent-based subclass, session management, and end-to-end workflow reset. However, there is a notable test coverage gap: the MagenticOrchestrator._reset_pattern_state() override (which resets four critical fields including _terminated) has no dedicated test, despite _terminated being the guard that prevents reuse (line 914 raises RuntimeError if True).

✓ Failure Modes

The InProcRunnerContext.reset_for_new_run() method (line 403-412 of _runner_context.py) clears messages, events, and the streaming flag, but does NOT clear _pending_request_info_events. When Runner.reset_for_new_run() calls self._ctx.reset_for_new_run() at line 99 of _runner.py, stale request_info events from a prior run survive the reset. This contradicts the PR's goal of ensuring 'no residual states' and can cause stale events to be baked into the next run's checkpoints (line 389 of _runner_context.py). The MagenticOrchestrator._reset_pattern_state() resets the orchestrator's own fields but does not rotate or clear the StandardMagenticManager._session, which accumulates LM conversation history across every _complete() call. After reset_for_new_run(), subsequent agent calls reuse a session carrying stale context from the prior run. The checkpoint logic explicitly saves/restores this session (line 746), confirming it holds run-significant state.

✗ Design Approach

The new reset API leaves one class of per-run state behind: pending request_info bookkeeping in the runner context. That means reset_for_new_run() does not actually provide the clean-slate semantics this PR is introducing for hosted-agent scenarios, and stale request IDs can still be consumed after a reset. The new group-chat reset hook mostly covers orchestrator-local state, but Magentic still leaves its manager-owned agent session alive across reset_for_new_run(). That means one important path can keep prior-run context even after a reset, which undercuts the PR's goal of reusing a workflow instance for independent runs without residual state.

Flagged Issues

  • _magentic.py:1267: _reset_pattern_state() resets only orchestrator fields, but StandardMagenticManager creates a long-lived session once (_magentic.py:571-572) and reuses it for every planner call (_magentic.py:601). The code path workflow.run(...); workflow.reset_for_new_run(); workflow.run(...) still sends the second run through the original manager session carrying stale conversation history. The existing agent-backed group chat reset path shows the intended pattern by rotating auto-created sessions on reset (_group_chat.py:349-358).

Suggestions

  • Add a test verifying that InProcRunnerContext.reset_for_new_run() clears _pending_request_info_events. The current implementation at _runner_context.py:403-412 does NOT clear them, yet Workflow.reset_for_new_run() documents: 'This will reset EVERYTHING... including pending requests/messages.' This is the exact scenario from the PR motivation (Foundry hosted agent receiving a new message without prior context).

Automated review by TaoChenOSU's agents

Comment thread python/packages/core/agent_framework/_workflows/_runner.py
Comment thread python/packages/core/agent_framework/_workflows/_workflow.py
@TaoChenOSU TaoChenOSU marked this pull request as draft June 9, 2026 23:27
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.

3 participants