Skip to content

Commit 48b7a64

Browse files
wuliang229copybara-github
authored andcommitted
fix(live): mark all agents' Event as from other agents
This will allow agent transfer to work smoothly in live workflow. See the comment added for details. Closes issue #5238 Co-authored-by: Liang Wu <wuliang@google.com> PiperOrigin-RevId: 900993889
1 parent ae1f2e6 commit 48b7a64

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

src/google/adk/flows/llm_flows/contents.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,28 @@ def _get_current_turn_contents(
572572

573573
def _is_other_agent_reply(current_agent_name: str, event: Event) -> bool:
574574
"""Whether the event is a reply from another agent."""
575+
# In live/bidi mode, all events from any agents, including the current
576+
# agent, will be marked as other agent's reply. When agent transfers,
577+
# the conversation history will be sent to the Live API. If the current
578+
# agent previously used `transfer_to_agent` to transfer to another agent,
579+
# when the conversation is sent back to the current agent, the history will
580+
# contain a `transfer_to_agent` function call event from the current agent.
581+
# The Live API marks anything after the function response as model response.
582+
# This will confuse the model and cause the model to not respond.
583+
#
584+
# E.g. when the conversation is transferred from agent A to agent B, then
585+
# back to agent A, the history in the last transfer will be:
586+
# User: "Some message that triggers transfer to agent B"
587+
# Model: transfer_to_agent(B)
588+
# User: transfer_to_agent(B) response
589+
# User: "Some message that triggers transfer to agent A"
590+
# User: "For context: [agent B] called transfer_to_agent(A)"
591+
# User: "For context: [agent B] tool transfer_to_agent(A) returned result:"
592+
#
593+
# In this case, the last three events are marked as model response by the
594+
# Live API, instead of user input.
595+
if event.live_session_id:
596+
return event.author != 'user'
575597
return bool(
576598
current_agent_name
577599
and event.author != current_agent_name

tests/unittests/flows/llm_flows/test_contents.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,3 +1068,30 @@ async def test_adk_function_call_ids_preserved_for_interactions_model():
10681068
user_fr_part = llm_request.contents[2].parts[0]
10691069
assert user_fr_part.function_response is not None
10701070
assert user_fr_part.function_response.id == function_call_id
1071+
1072+
1073+
def test_is_other_agent_reply_live_session():
1074+
"""Test _is_other_agent_reply when live_session_id is present."""
1075+
event = Event(author="another_agent", live_session_id="session_123")
1076+
assert contents._is_other_agent_reply("current_agent", event) is True
1077+
1078+
event = Event(author="user", live_session_id="session_123")
1079+
assert contents._is_other_agent_reply("current_agent", event) is False
1080+
1081+
event = Event(author="current_agent", live_session_id="session_123")
1082+
assert contents._is_other_agent_reply("current_agent", event) is True
1083+
1084+
1085+
def test_is_other_agent_reply_non_live_session():
1086+
"""Test _is_other_agent_reply when live_session_id is not present."""
1087+
event = Event(author="another_agent")
1088+
assert contents._is_other_agent_reply("current_agent", event) is True
1089+
1090+
event = Event(author="user")
1091+
assert contents._is_other_agent_reply("current_agent", event) is False
1092+
1093+
event = Event(author="current_agent")
1094+
assert contents._is_other_agent_reply("current_agent", event) is False
1095+
1096+
event = Event(author="another_agent")
1097+
assert contents._is_other_agent_reply("", event) is False

0 commit comments

Comments
 (0)