fix(agentchat): respect allow_repeated_speaker=False in selector fallback#7568
fix(agentchat): respect allow_repeated_speaker=False in selector fallback#7568extrasmall0 wants to merge 1 commit intomicrosoft:mainfrom
Conversation
…back When the model fails to select a valid speaker within max_selector_attempts and allow_repeated_speaker=False, the fallback unconditionally returned self._previous_speaker — the exact speaker that should be excluded — causing a livelock where the same agent kept being selected indefinitely. The _select_speaker method already receives a participants list that has the previous speaker filtered out (done by the caller at line 196-197), so the fix is to use random.choice(participants) for the fallback when repeated speakers are not allowed, instead of falling through to the self._previous_speaker branch.
|
Quick follow-up: this is ready for review, and I wanted to add one concrete bit of context in case it helps triage. The regression happens in the selector fallback path when |
|
Quick follow-up after another week in queue: this is still ready for review, and the scope remains intentionally narrow. The change only fixes the selector fallback path when |
|
Quick follow-up after rechecking this against current main: the patch is still a small, isolated fallback fix in the selector path. It only changes the exhausted selector fallback when allow_repeated_speaker=False, so instead of returning the previously excluded speaker it chooses from the already-filtered participant list. If it helps review, I can also add a focused regression test around the exhausted-selector path. |
Fixes #7471
Problem
When
allow_repeated_speaker=Falseand the model exhaustsmax_selector_attemptswithout picking a valid (non-previous) speaker, the fallback in_select_speakerreturnsself._previous_speaker:This creates a livelock: agent A speaks → selector tries to pick someone else → exhausts attempts → falls back to A → A speaks again → repeat.
Root Cause
The
_select_speakermethod receives aparticipantslist that already has the previous speaker filtered out (done by the caller whenallow_repeated_speaker=False). The fallback logic ignores this filtered list and reads directly fromself._previous_speaker.Fix
Add an early-exit branch before the existing fallback: when
allow_repeated_speaker=Falseand there is a previous speaker, userandom.choice(participants)(the already-filtered list) instead of returning the excluded speaker.The existing fallback for
allow_repeated_speaker=True(return previous speaker) is unchanged.