fix: Set role='model' for request_input event Content
Set role='model' in the Content object generated by create_request_input_event() to ensure proper role attribution when returning function call events to the client. Co-authored-by: Shangjie Chen <deanchen@google.com> PiperOrigin-RevId: 929538818
This commit is contained in:
committed by
Copybara-Service
parent
048deeaeb7
commit
0c6974cbc4
+1
-1
@@ -94,7 +94,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-9",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"role": "user"
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-9",
|
||||
"invocationId": "i-3",
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
"name": "adk_request_credential"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-2",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
"name": "adk_request_input"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-4",
|
||||
"invocationId": "i-1",
|
||||
@@ -162,7 +163,8 @@
|
||||
"name": "adk_request_input"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-8",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -72,7 +72,8 @@
|
||||
"name": "adk_request_input"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-4",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -92,7 +92,8 @@
|
||||
"name": "adk_request_input"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-3",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -73,7 +73,8 @@
|
||||
"name": "adk_request_input"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-4",
|
||||
"invocationId": "i-1",
|
||||
@@ -162,7 +163,8 @@
|
||||
"name": "adk_request_input"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"role": "model"
|
||||
},
|
||||
"id": "e-8",
|
||||
"invocationId": "i-1",
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import AsyncGenerator
|
||||
from typing import Optional
|
||||
from unittest import mock
|
||||
|
||||
from google.adk.agents.base_agent import BaseAgent
|
||||
from google.adk.apps.app import App
|
||||
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
|
||||
from google.adk.cli.utils.agent_loader import AgentLoader
|
||||
@@ -249,6 +250,33 @@ def _make_nodes_sequential(obj, visited=None):
|
||||
_make_nodes_sequential(obj._node, visited)
|
||||
|
||||
|
||||
def _get_all_agent_names(obj, visited=None):
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if id(obj) in visited:
|
||||
return set()
|
||||
visited.add(id(obj))
|
||||
|
||||
from google.adk.workflow._parallel_worker import _ParallelWorker
|
||||
from google.adk.workflow._workflow import Workflow
|
||||
|
||||
names = set()
|
||||
if isinstance(obj, BaseAgent) and hasattr(obj, "name"):
|
||||
names.add(obj.name)
|
||||
if hasattr(obj, "sub_agents") and obj.sub_agents:
|
||||
for sub in obj.sub_agents:
|
||||
names.update(_get_all_agent_names(sub, visited))
|
||||
elif isinstance(obj, Workflow):
|
||||
if obj.graph and obj.graph.nodes:
|
||||
for node in obj.graph.nodes:
|
||||
names.update(_get_all_agent_names(node, visited))
|
||||
elif isinstance(obj, _ParallelWorker):
|
||||
if hasattr(obj, "_node"):
|
||||
names.update(_get_all_agent_names(obj._node, visited))
|
||||
return names
|
||||
|
||||
|
||||
def _extract_user_content(event: dict) -> Optional[types.Content]:
|
||||
"""Extracts user content from an event dict and returns a types.Content object.
|
||||
|
||||
@@ -444,6 +472,26 @@ def test_agent_replay(agent_dir, test_file, monkeypatch):
|
||||
else agent_or_app
|
||||
)
|
||||
_make_nodes_sequential(root_agent)
|
||||
agent_names = _get_all_agent_names(root_agent)
|
||||
|
||||
import inspect
|
||||
|
||||
# Dynamically locate the loaded agent module from sys.modules
|
||||
mod = sys.modules.get(f"{agent_dir.name}.agent") or sys.modules.get(
|
||||
agent_dir.name
|
||||
)
|
||||
if not mod:
|
||||
# Fallback for namespace packages or nested imports
|
||||
for k, v in sys.modules.items():
|
||||
if k.endswith(f"{agent_dir.name}.agent") or k.endswith(agent_dir.name):
|
||||
mod = v
|
||||
break
|
||||
|
||||
# Reflectively find all Agent instances defined in the module (e.g. dynamic agents)
|
||||
if mod:
|
||||
for _, obj in inspect.getmembers(mod):
|
||||
if isinstance(obj, BaseAgent) and hasattr(obj, "name"):
|
||||
agent_names.add(obj.name)
|
||||
|
||||
with open(test_file, "r") as f:
|
||||
session_data = json.load(f)
|
||||
@@ -488,6 +536,23 @@ def test_agent_replay(agent_dir, test_file, monkeypatch):
|
||||
last_was_set_model_response = False
|
||||
continue
|
||||
|
||||
if ev.get("author", "") not in agent_names:
|
||||
continue
|
||||
|
||||
parts = content_dict.get("parts", [])
|
||||
is_sys_hitl = False
|
||||
for part in parts:
|
||||
if "functionCall" in part:
|
||||
fc_name = part["functionCall"].get("name")
|
||||
if fc_name in (
|
||||
"adk_request_confirmation",
|
||||
"adk_request_credential",
|
||||
):
|
||||
is_sys_hitl = True
|
||||
break
|
||||
if is_sys_hitl:
|
||||
continue
|
||||
|
||||
try:
|
||||
content_obj = types.Content.model_validate(content_dict)
|
||||
all_responses.append(
|
||||
|
||||
@@ -381,9 +381,7 @@ def generate_request_confirmation_event(
|
||||
invocation_id=invocation_context.invocation_id,
|
||||
author=invocation_context.agent.name,
|
||||
branch=invocation_context.branch,
|
||||
content=types.Content(
|
||||
parts=parts, role=function_response_event.content.role
|
||||
),
|
||||
content=types.Content(parts=parts, role='model'),
|
||||
long_running_tool_ids=long_running_tool_ids,
|
||||
)
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ def create_request_input_event(request_input: RequestInput) -> Event:
|
||||
)
|
||||
return Event(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[
|
||||
types.Part(
|
||||
function_call=types.FunctionCall(
|
||||
@@ -63,7 +64,7 @@ def create_request_input_event(request_input: RequestInput) -> Event:
|
||||
id=request_input.interrupt_id,
|
||||
)
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
long_running_tool_ids=[request_input.interrupt_id],
|
||||
)
|
||||
@@ -179,6 +180,7 @@ def create_auth_request_event(
|
||||
|
||||
return Event(
|
||||
content=types.Content(
|
||||
role='model',
|
||||
parts=[
|
||||
types.Part(
|
||||
function_call=types.FunctionCall(
|
||||
@@ -187,7 +189,7 @@ def create_auth_request_event(
|
||||
args=args,
|
||||
)
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
long_running_tool_ids=[interrupt_id],
|
||||
)
|
||||
|
||||
@@ -40,6 +40,7 @@ class TestCreateRequestInputEvent:
|
||||
|
||||
assert event.long_running_tool_ids == {"test-id"}
|
||||
assert event.content is not None
|
||||
assert event.content.role == "model"
|
||||
fc = event.content.parts[0].function_call
|
||||
assert fc.name == "adk_request_input"
|
||||
assert fc.id == "test-id"
|
||||
|
||||
Reference in New Issue
Block a user