Skip to content

Commit 1bed6c0

Browse files
committed
Python: Reset Magentic participant sessions on replan
MagenticAgentExecutor.handle_magentic_reset assigned the new session to _agent_thread, an attribute nothing reads since the AgentExecutor session attribute was renamed to _session, so participants kept their whole conversation across every reset. Assign _session instead and also clear _pending_request_order with the other pending request state.
1 parent 279d97f commit 1bed6c0

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

‎python/packages/orchestrations/agent_framework_orchestrations/_magentic.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1374,8 +1374,9 @@ async def handle_magentic_reset(self, signal: MagenticResetSignal, ctx: Workflow
13741374
# Request into related
13751375
self._pending_agent_requests.clear()
13761376
self._pending_responses_to_agent.clear()
1377+
self._pending_request_order.clear()
13771378
# Reset sessions
1378-
self._agent_thread = self._agent.create_session()
1379+
self._session = self._agent.create_session()
13791380

13801381

13811382
# endregion Magentic Agent Executor

‎python/packages/orchestrations/tests/test_magentic.py‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,56 @@ async def test_magentic_stall_and_reset_reach_limits():
857857
assert output_event.data.text == "Workflow terminated due to reaching maximum reset count."
858858

859859

860+
async def test_magentic_reset_gives_participants_a_fresh_session():
861+
"""After a stall forces a reset and replan, participants must not see their earlier history."""
862+
863+
class RecordingChatClient(BaseChatClient):
864+
"""Captures the message texts handed to the model on each call."""
865+
866+
def __init__(self) -> None:
867+
super().__init__()
868+
self.calls: list[list[str]] = []
869+
870+
@override
871+
def _inner_get_response(self, *, messages, stream, options, **kwargs): # type: ignore[override]
872+
self.calls.append([m.text for m in messages])
873+
reply = f"reply-{len(self.calls)}"
874+
875+
async def _get() -> ChatResponse:
876+
return ChatResponse(messages=Message(role="assistant", contents=[reply]))
877+
878+
return _get()
879+
880+
class StallOnceManager(NotProgressingManager):
881+
"""Asks agentA, stalls once (forcing a reset and replan), asks agentA again, then finishes."""
882+
883+
def __init__(self) -> None:
884+
super().__init__(max_round_count=10, max_stall_count=0)
885+
self.rounds = 0
886+
887+
async def create_progress_ledger(self, magentic_context: MagenticContext) -> MagenticProgressLedger:
888+
self.rounds += 1
889+
instruction = "before reset" if self.rounds == 1 else "after reset"
890+
progress = self.rounds != 2
891+
return MagenticProgressLedger(
892+
is_request_satisfied=MagenticProgressLedgerItem(reason="r", answer=self.rounds >= 4),
893+
is_in_loop=MagenticProgressLedgerItem(reason="r", answer=not progress),
894+
is_progress_being_made=MagenticProgressLedgerItem(reason="r", answer=progress),
895+
next_speaker=MagenticProgressLedgerItem(reason="r", answer="agentA"),
896+
instruction_or_question=MagenticProgressLedgerItem(reason="r", answer=instruction),
897+
)
898+
899+
client = RecordingChatClient()
900+
manager = StallOnceManager()
901+
wf = MagenticBuilder(participants=[Agent(name="agentA", client=client)], manager=manager).build()
902+
903+
await wf.run("test reset")
904+
905+
assert manager.rounds == 4
906+
assert client.calls[0] == ["before reset"]
907+
assert client.calls[1] == ["after reset"], "agentA kept its pre-reset session history"
908+
909+
860910
async def test_magentic_checkpoint_runtime_only() -> None:
861911
"""Test checkpointing configured ONLY at runtime, not at build time."""
862912
storage = InMemoryCheckpointStorage()

0 commit comments

Comments
 (0)