mirror of
https://github.com/encounter/adk-python.git
synced 2026-07-09 18:19:28 -07:00
42d4c4ed5ff292d54bc5382f9fb274ec98becaba
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0fccc7933a |
fix(core): ParallelAgent's branching with include_contents='none' may cause index out of range error
Merge https://github.com/google/adk-python/pull/2961 Fixes #2404 Consider code where a sub_agent somewhere under a ParallelAgent has include_contents='none'. ```python import asyncio import os from typing import TypedDict from dotenv import load_dotenv from google.adk.agents import LlmAgent, ParallelAgent, SequentialAgent from google.adk.runners import Runner from google.adk.sessions import DatabaseSessionService, InMemorySessionService from google.genai import types import logging USE_DB=False load_dotenv() logging.basicConfig( filename='log.log', level=logging.DEBUG ) for logger_name in ["httpcore"]: logging.getLogger(logger_name).setLevel(logging.ERROR) class AgentArgs(TypedDict): model: str instruction: str class SessionArgs(TypedDict): user_id: str session_id: str agent_args = AgentArgs( model="gemini-2.0-flash", instruction="Answer 'Ack' and nothing else." ) session_args = SessionArgs( user_id="0", session_id="0" ) app_name = "Test" def create_agent_in_branch(i: int): agent_1 = LlmAgent( name=f"subagent_{i}_1", **agent_args ) agent_2 = LlmAgent( name=f"subagent_{i}_2", include_contents='none', **agent_args ) return SequentialAgent( name=f"agent_{i}", sub_agents=[agent_1, agent_2] ) root = ParallelAgent( name="root", sub_agents=[create_agent_in_branch(i) for i in range(1, 5)] ) runner = Runner( agent=root, app_name=app_name, session_service=DatabaseSessionService(db_url=os.getenv("DB_URL", "")) if USE_DB else InMemorySessionService(), ) async def main() -> None: try: await runner.session_service.delete_session(app_name=app_name, **session_args) except: pass await runner.session_service.create_session(app_name=app_name, **session_args) message = types.Content(role="user", parts=[types.Part(text=" ")]) async for event in runner.run_async(**session_args, new_message=message): if event.is_final_response(): print(((event.content or types.Content()).parts or [types.Part()])[0].text or "") if __name__ == '__main__': asyncio.run(main()) ``` The log here will often have one or more ```subagent_{i}_2``` not receive their prompts from ```subagent_{i}_1```. This inconsistency is caused by the way ```include_contents='none'``` is implemented in flows/llm_flows/contents.py:328-356: ```python for i in range(len(events) - 1, -1, -1): event = events[i] if event.author == 'user' or _is_other_agent_reply(agent_name, event): return _get_contents(current_branch, events[i:], agent_name) return [] ``` That is, we first find the most recent (other-agent / user) event, **even if it's not on our branch**, and then filter the remainder. Thus in the above example, we may sometimes filter out all events, when we expect to have the event of a previous agent in a ```SequentialAgent```. The solution is to first filter events by branch, and only then search for the latest. ### TEST SUMMARY: ``` =================================================== short test summary info =================================================== FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-LoopAgent-LoopAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-google.adk.agents.LoopAgent-LoopAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-google.adk.agents.loop_agent.LoopAgent-LoopAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-ParallelAgent-ParallelAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-google.adk.agents.ParallelAgent-ParallelAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-google.adk.agents.parallel_agent.ParallelAgent-ParallelAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-SequentialAgent-SequentialAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-google.adk.agents.SequentialAgent-SequentialAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[GOOGLE_AI-google.adk.agents.sequential_agent.SequentialAgent-SequentialAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-LoopAgent-LoopAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-google.adk.agents.LoopAgent-LoopAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-google.adk.agents.loop_agent.LoopAgent-LoopAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-ParallelAgent-ParallelAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-google.adk.agents.ParallelAgent-ParallelAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-google.adk.agents.parallel_agent.ParallelAgent-ParallelAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-SequentialAgent-SequentialAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-google.adk.agents.SequentialAgent-SequentialAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_with_sub_agents[VERTEX-google.adk.agents.sequential_agent.SequentialAgent-SequentialAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_llm_agent_with_sub_agents[GOOGLE_AI-LlmAgent-LlmAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_llm_agent_with_sub_agents[GOOGLE_AI-google.adk.agents.LlmAgent-LlmAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_llm_agent_with_sub_agents[GOOGLE_AI-google.adk.agents.llm_agent.LlmAgent-LlmAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_llm_agent_with_sub_agents[VERTEX-LlmAgent-LlmAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_llm_agent_with_sub_agents[VERTEX-google.adk.agents.LlmAgent-LlmAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/agents/test_agent_config.py::test_agent_config_discriminator_llm_agent_with_sub_agents[VERTEX-google.adk.agents.llm_agent.LlmAgent-LlmAgent] - FileNotFoundError: Config file not found: C:\Users\AnanYablonko\AppData\Local\Temp\pytest-of-AnanYablonko\pytest-1\test_age... FAILED tests/unittests/cli/utils/test_cli_tools_click.py::test_cli_run_invokes_run_cli[GOOGLE_AI] - AssertionError: assert 1 == 0 FAILED tests/unittests/cli/utils/test_cli_tools_click.py::test_cli_run_invokes_run_cli[VERTEX] - AssertionError: assert 1 == 0 FAILED tests/unittests/cli/utils/test_cli_tools_click.py::test_cli_eval_with_eval_set_file_path[GOOGLE_AI] - assert 0 == 1 FAILED tests/unittests/cli/utils/test_cli_tools_click.py::test_cli_eval_with_eval_set_file_path[VERTEX] - assert 0 == 1 FAILED tests/unittests/evaluation/test_local_eval_sets_manager.py::TestLocalEvalSetsManager::test_local_eval_sets_manager_update_eval_case_eval_set_not_found[GOOGLE_AI] - OSError: [Errno 22] Invalid argument: '<tests.unittests.evaluation.test_local_eval_sets_manager.TestLocalEvalSetsManager ob... FAILED tests/unittests/evaluation/test_local_eval_sets_manager.py::TestLocalEvalSetsManager::test_local_eval_sets_manager_update_eval_case_eval_set_not_found[VERTEX] - OSError: [Errno 22] Invalid argument: '<tests.unittests.evaluation.test_local_eval_sets_manager.TestLocalEvalSetsManager ob... FAILED tests/unittests/evaluation/test_local_eval_sets_manager.py::TestLocalEvalSetsManager::test_local_eval_sets_manager_delete_eval_case_eval_set_not_found[GOOGLE_AI] - OSError: [Errno 22] Invalid argument: '<tests.unittests.evaluation.test_local_eval_sets_manager.TestLocalEvalSetsManager ob... FAILED tests/unittests/evaluation/test_local_eval_sets_manager.py::TestLocalEvalSetsManager::test_local_eval_sets_manager_delete_eval_case_eval_set_not_found[VERTEX] - OSError: [Errno 22] Invalid argument: '<tests.unittests.evaluation.test_local_eval_sets_manager.TestLocalEvalSetsManager ob... FAILED tests/unittests/sessions/test_session_service.py::test_get_session_with_config[GOOGLE_AI-SessionServiceType.DATABASE] - OSError: [Errno 22] Invalid argument FAILED tests/unittests/sessions/test_session_service.py::test_get_session_with_config[VERTEX-SessionServiceType.DATABASE] - OSError: [Errno 22] Invalid argument ================================= 34 failed, 4540 passed, 3044 warnings in 187.68s (0:03:07) ================================== ``` Co-authored-by: Wei Sun (Jack) <weisun@google.com> COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2961 from ananyablonko:main b4a21adcd4231efeffd552a96734b2161b317e0a PiperOrigin-RevId: 828700099 |
||
|
|
9dce06f9b0 |
feat: Add rewind_async to support rewinding the session to before a previous invocation
PiperOrigin-RevId: 820552460 |
||
|
|
a30851ee16 |
fix: Fixes thought handling in contents.py and refactors its unit tests
Before this change, other agent's reply with thought will still be inserted in the outgoing LlmRequest due to the wrong `else` statement for calling all other type of part. This commit also refactors test_contents.py to be behavior-oriented tests, instead of implementation-oriented, and add more test cases to cover expected scenarios. The tests are divided into the following files with different focus: - test_contents.py: covers the basic logic of event filter; - test_contents_branch.py: covers the behavior related to branch, which takes effect when ParallelAgent is used. - test_contents_other_agent.py: covers the retelling behavior to include other agents' reply as context for the current agent. - test_contents_function.py: covers the function_call/function_response rearrangement logic mainly for `LongRunningFunctionTool`. PiperOrigin-RevId: 802759821 |
||
|
|
a17bcbb2aa |
feat: Add a tool confirmation flow that can guard tool execution with explicit confirmation and custom input
The existing `LongRunningTool` does not define a programmatic way to provide & validate structured input, also it relies on LLM to reason and parse the user's response. For a quick start, annotate the function with `FunctionTool(my_function, require_confirmation=True)`. A more advanced flow is shown in the `human_tool_confirmation` sample. The new flow is similar to the existing Auth flow: - User request a tool confirmation by calling `tool_context.request_confirmation()` in the tool or `before_tool_callback`, or just using the `require_confirmation` shortcut in FunctionTool. - User can provide custom validation logic before tool call proceeds. - ADK creates corresponding RequestConfirmation FunctionCall Event to ask user for confirmation - User needs to provide the expected tool confirmation to a RequestConfirmation FunctionResponse Event. - ADK then checks the response and continues the tool call. PiperOrigin-RevId: 801019917 |
||
|
|
927c75f0ee |
chore: Replace imports by importing from actual module instead of from package (__init__.py)
PiperOrigin-RevId: 786342250 |
||
|
|
134ec0d71e |
fix: Fix the long running function response event merge logic
1) raise explicit error if the response event contains responses against multiple function call events 2) merge all function responses for the corresponding function call event PiperOrigin-RevId: 782154577 |
||
|
|
f54b9b6ad1 |
chore: Add unit tests for contents.py
PiperOrigin-RevId: 775713101 |