mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Address comments
This commit is contained in:
@@ -403,8 +403,9 @@ class InProcRunnerContext:
|
||||
def reset_for_new_run(self) -> None:
|
||||
"""Reset the context for a new workflow run.
|
||||
|
||||
This clears messages, events, and resets streaming flag.
|
||||
Runtime checkpoint storage is NOT cleared here as it's managed at the workflow level.
|
||||
Clears messages, the pending event queue, the pending request_info
|
||||
correlation map, and the streaming flag. Runtime checkpoint storage is
|
||||
NOT cleared here as it's managed at the workflow level.
|
||||
"""
|
||||
self._messages.clear()
|
||||
# Clear any pending events (best-effort) by recreating the queue
|
||||
|
||||
@@ -503,11 +503,18 @@ class Workflow(DictConvertible):
|
||||
if checkpoint_storage is not None:
|
||||
self._runner.context.set_runtime_checkpoint_storage(checkpoint_storage)
|
||||
|
||||
# Capture the runner's checkpoint id before attempting to save. The runner
|
||||
# log-and-swallows storage save errors and only updates
|
||||
# ``previous_checkpoint_id`` on success, so a failed save would otherwise
|
||||
# leave the prior id in place and we'd return it as if a fresh checkpoint
|
||||
# had been created.
|
||||
previous_id_before = self._runner.previous_checkpoint_id
|
||||
try:
|
||||
await self._runner.create_checkpoint_if_enabled()
|
||||
if self._runner.previous_checkpoint_id is None:
|
||||
new_id = self._runner.previous_checkpoint_id
|
||||
if new_id is None or new_id == previous_id_before:
|
||||
raise WorkflowCheckpointException("Failed to create checkpoint.")
|
||||
return self._runner.previous_checkpoint_id
|
||||
return new_id
|
||||
finally:
|
||||
self._runner.context.clear_runtime_checkpoint_storage()
|
||||
|
||||
|
||||
@@ -1461,5 +1461,36 @@ class TestWorkflowCreateCheckpoint:
|
||||
assert second is not None
|
||||
assert second.previous_checkpoint_id == first_id
|
||||
|
||||
async def test_raises_when_save_fails_after_prior_success(self, simple_executor: Executor) -> None:
|
||||
"""A failed save after an earlier successful checkpoint must not return the stale id.
|
||||
|
||||
The runner log-and-swallows storage save errors and only updates
|
||||
``previous_checkpoint_id`` on success. Without an explicit transition check,
|
||||
``create_checkpoint`` would silently return the previously stored id as if a
|
||||
new checkpoint had been created.
|
||||
"""
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
storage = InMemoryCheckpointStorage()
|
||||
workflow = WorkflowBuilder(start_executor=simple_executor).add_edge(simple_executor, simple_executor).build()
|
||||
|
||||
# First call succeeds and seeds ``previous_checkpoint_id``.
|
||||
first_id = await workflow.create_checkpoint(storage)
|
||||
assert first_id
|
||||
|
||||
# Second call fails to save, so the runner leaves ``previous_checkpoint_id``
|
||||
# pointing at ``first_id``. The method must detect that the id did not
|
||||
# transition and raise instead of returning the stale value.
|
||||
original_save = storage.save
|
||||
storage.save = AsyncMock(side_effect=RuntimeError("boom")) # type: ignore[method-assign]
|
||||
try:
|
||||
with pytest.raises(WorkflowCheckpointException, match="Failed to create checkpoint"):
|
||||
await workflow.create_checkpoint(storage)
|
||||
finally:
|
||||
storage.save = original_save # type: ignore[method-assign]
|
||||
|
||||
# The runner's bookkeeping is unchanged after the failed call.
|
||||
assert workflow._runner.previous_checkpoint_id == first_id # type: ignore[attr-defined]
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
Reference in New Issue
Block a user