Python: Preserve workflow run kwargs when continuing with run(responses=...)#4296
Python: Preserve workflow run kwargs when continuing with run(responses=...)#4296moonbox3 merged 2 commits intomicrosoft:mainfrom
run(responses=...)#4296Conversation
…icrosoft#4293) When continuing a paused workflow with run(responses=...), the existing run kwargs stored in state were unconditionally overwritten with an empty dict. This caused subsequent agent invocations to lose the original run context (e.g., custom_data, user tokens). Now kwargs are only overwritten when: - New kwargs are explicitly provided (override), or - State was just cleared for a fresh run (initialize to {}) On continuation without new kwargs, existing kwargs are preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
moonbox3
left a comment
There was a problem hiding this comment.
Automated Code Review
Reviewers: 3 | Confidence: 85%
✓ Correctness
The change correctly fixes the kwargs-overwrite-on-continuation bug (#4293) by guarding the state write: kwargs are only written when explicitly provided or when the context is freshly reset. The logic is sound for the intended workflow lifecycle (initial run → pause → continue). The tests cover preservation and override scenarios well. One minor edge-case worth noting: if
run(responses=..., reset_context=False)is ever called without a preceding run,WORKFLOW_RUN_KWARGS_KEYwill never be set, whereas the old code always ensured the key existed. This is unlikely in practice sincereset_context=Falseimplies a continuation, but the old comment explicitly called out deterministic retrieval.
✓ Security Reliability
This diff fixes a bug where workflow run kwargs were unintentionally overwritten to an empty dict on continuation calls (reset_context=False). The new conditional logic correctly preserves kwargs when no new ones are provided during continuation, and only resets them on a fresh run. The change is minimal, well-scoped, and accompanied by thorough regression tests. No security or reliability issues are introduced.
✓ Test Coverage
The two new tests directly cover the primary behavioral change (preserving kwargs on continuation and overriding them when explicitly provided). Assertions are meaningful and include a diagnostic message on the most important check. However, an edge case is missing: passing an explicit empty dict (
run_kwargs={}) on continuation should overwrite the preserved kwargs with{}(since{} is not None), which is a subtle but important distinction from passingNone. There is also no dedicated test for thereset_context=Truewithrun_kwargs=Nonebranch (line 355), though existing tests for fresh runs likely exercise it indirectly. Overall the coverage is solid for the primary scenarios.
Suggestions
- Consider whether downstream consumers of WORKFLOW_RUN_KWARGS_KEY use a safe access pattern (e.g., state.get with a default) rather than assuming the key always exists. The old code guaranteed the key was always set; the new code may leave it unset on the (unlikely) path where reset_context=False and run_kwargs=None with no prior run.
- Consider whether the state should be defensively initialized with an empty dict if WORKFLOW_RUN_KWARGS_KEY is missing during a continuation (reset_context=False, run_kwargs=None) — e.g., if a workflow is resumed from persisted state that predates this key. A missing key at retrieval time could surface as an unexpected KeyError downstream.
- Add a test for the continuation case where
run_kwargsis explicitly an empty dict{}(notNone). This exercises a subtle boundary:{}passes theis not Nonecheck and should clear previously-stored kwargs, whereas omitting kwargs (None) should preserve them. - Consider adding a targeted unit test that calls
workflow.run(...)withreset_context=Trueand no kwargs to explicitly verify theelif reset_contextbranch (line 355) stores{}.
Automated review by moonbox3's agents
run(responses=...)run(responses=...)
Python Test Coverage Report •
Python Unit Test Overview
|
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Pull request overview
This PR fixes a regression where workflow run kwargs were incorrectly cleared when continuing a paused workflow via run(responses=...). The issue affected executors/agents that rely on custom data (e.g., auth tokens, configuration) passed at runtime.
Changes:
- Modified kwargs storage logic to preserve existing kwargs on continuation calls unless explicitly overridden
- Added two comprehensive regression tests validating both preservation and override behaviors
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| python/packages/core/agent_framework/_workflows/_workflow.py | Replaced unconditional kwargs overwrite with conditional logic: preserve on continuation (reset_context=False) unless new kwargs explicitly provided |
| python/packages/core/tests/workflow/test_workflow_kwargs.py | Added test_kwargs_preserved_on_response_continuation and test_kwargs_overridden_on_response_continuation to verify fix |
- Use consistent get_state(key, {}) default pattern in _agent_executor.py
and _workflow_executor.py instead of get_state(key) or {} to safely
handle missing WORKFLOW_RUN_KWARGS_KEY
- Add test for empty-value kwargs on continuation (custom_data={}) to
verify the is-not-None boundary between overwrite and preserve
- Add test for reset_context=True with no kwargs to exercise the elif
branch that initializes WORKFLOW_RUN_KWARGS_KEY to {}
- Add len assertion to override test for consistency
- Document kwargs-collapsing behavior at the public API call site
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Motivation and Context
When a workflow pauses (e.g., for human approval) and is resumed via
run(responses=...), the originalrun_kwargspassed in the initial call were silently dropped and replaced with an empty dict. This caused executors/agents to lose access to custom data like auth tokens or configuration passed at run time.Fixes #4293
Description
The root cause was that
_state.set(WORKFLOW_RUN_KWARGS_KEY, run_kwargs or {})unconditionally overwrote the stored kwargs on everyrun()call, including continuation calls whererun_kwargsisNone. The fix conditionally updates stored kwargs: they are only overwritten when new kwargs are explicitly provided, or when the context is being reset (fresh run). On continuation calls with no new kwargs (reset_context=False), the previously stored kwargs are preserved. Two regression tests verify both preservation and explicit override behavior.Contribution Checklist