-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Python: Fix group chat invoking a re-selected participant with no messages #7549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
66ef1cf
cefce97
da6305a
57bc2b6
cc7c43b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,6 +69,13 @@ | |
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| # Sent when the agent participant that just spoke is selected again. It is excluded from the | ||
| # broadcast (its own reply is already in its session) and AgentExecutor clears its cache after | ||
| # each run, so the request would otherwise carry no messages and some agents (for example | ||
| # A2AAgent) reject empty input. Custom executors are excluded: they have no such cache and | ||
| # receive full context in the request envelope. | ||
| _CONTINUATION_DEFAULT_INSTRUCTION = "Continue the conversation." | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class GroupChatState: | ||
|
|
@@ -233,6 +240,11 @@ async def _handle_response( | |
| await self._send_request_to_participant( | ||
| next_speaker, | ||
| cast(WorkflowContext[AgentExecutorRequest | GroupChatRequestMessage], ctx), | ||
| additional_instruction=( | ||
| _CONTINUATION_DEFAULT_INSTRUCTION | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When a consecutive A2A turn ended in |
||
| if next_speaker == participant and self._participant_registry.is_agent(participant) | ||
| else None | ||
| ), | ||
| ) | ||
| self._increment_round() | ||
|
|
||
|
|
@@ -409,10 +421,16 @@ async def _handle_response( | |
| participants=[p for p in self._participant_registry.participants if p != participant], | ||
| ) | ||
| # Send request to selected participant | ||
| next_speaker = agent_orchestration_output.next_speaker | ||
| await self._send_request_to_participant( | ||
| # If not terminating, next_speaker must be provided thus will not be None | ||
| agent_orchestration_output.next_speaker, # type: ignore[arg-type] | ||
| next_speaker, # type: ignore[arg-type] | ||
| cast(WorkflowContext[AgentExecutorRequest | GroupChatRequestMessage], ctx), | ||
| additional_instruction=( | ||
| _CONTINUATION_DEFAULT_INSTRUCTION | ||
| if next_speaker == participant and self._participant_registry.is_agent(participant) | ||
| else None | ||
| ), | ||
| ) | ||
| self._increment_round() | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure the empty-cache case is fully addressed yet. What happens when an earlier speaker is selected after a tool-only turn? If
Aspeaks and clears its cache,Bthen returns only tool content soclean_conversation_for_handoffbroadcasts[], and the selector choosesA, this condition is false even thoughAis empty. AnA2AAgentwould then raise the originalValueError.Could the continuation be carried as a fallback on
AgentExecutorRequestthatAgentExecutor.runapplies only when_cacheis empty? That keeps the decision with the component that owns_cache, avoids displacing valid cached context, and covers both consecutive and non-consecutive empty-cache cases. Could we also add the regressionAtext →Btool-only →A? The agent-based handler at_group_chat.py:429has the same condition.