Commit Graph
95 Commits
Author SHA1 Message Date
Xiang (Sean) ZhouandCopybara-Service 976a238544 refactor: Extract reusable private methods
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 871359399
2026-02-17 09:36:25 -08:00
Google Team MemberandCopybara-Service 42eeaef2b3 refactor: Extract reusable private methods
PiperOrigin-RevId: 871151624
2026-02-17 00:05:25 -08:00
Xiang (Sean) ZhouandCopybara-Service 706f9fe74d refactor: Extract reusable private methods
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 871128393
2026-02-16 22:52:47 -08:00
Liang WuandCopybara-Service 61c329f8ce ci: match the environment with internal ones for pyink/isort/unittest
Previously submitted change is causing [Pyink error](https://github.com/google/adk-python/actions/runs/21884351396/job/63176122118) in repo because Pyink recently updated to 25.12.0.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 868806575
2026-02-11 13:01:32 -08:00
George WealeandCopybara-Service 663cb75b32 fix: Conditionally preserve function call IDs in LLM requests
Function call and response IDs generated by ADK are now preserved in the LLM request contents when the agent is using a Gemini model with `use_interactions_api` enabled

Close #4381

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 867675945
2026-02-09 10:46:55 -08:00
Xuan YangandCopybara-Service 2220d885cd fix: Check will_continue for streaming function calls
Related: https://github.com/google/adk-python/issues/4311

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 865074872
2026-02-03 15:33:44 -08:00
Google Team MemberandCopybara-Service 00aba2d884 feat: Improve asyncio loop handling and test cleanup
This CL enhances asyncio event loop management and test isolation.

-   **BigQuery Analytics Plugin:** Ensure the asyncio event loop is consistently closed within the BigQuery analytics plugin. This prevents potential resource leaks. Add checks to handle potential deadlocks in Python 3.13+ when creating loops during interpreter shutdown.
-   **Test Thread Pool Cleanup:** Introduce a pytest fixture (`cleanup_thread_pools`) to automatically shut down and clear all tool-related thread pools after each test run in `test_functions_thread_pool.py`. This improves test isolation and prevents order-dependent test failures.
-   **Streaming Test Loop Restoration:** Refactor event loop handling in `test_streaming.py`. A new `_run_with_loop` method is introduced in the custom test runners to create a temporary event loop for each test execution, run the coroutine, and crucially, restore the original event loop afterwards. This prevents tests from interfering with each other's loop state.
-   **Resource Closure:** Ensure services are closed properly in tests by adding `await service.close()` in `test_service_factory.py` and using `async with session_service` in `test_session_service.py`.

PiperOrigin-RevId: 863305565
2026-01-30 10:47:09 -08:00
Xuan YangandCopybara-Service 853a3b0e14 fix: Do not treat FCs and FRs as invisible when marked as thoughts
Related: https://github.com/google/adk-python/issues/4159

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 860254198
2026-01-23 14:25:42 -08:00
Xiang (Sean) ZhouandCopybara-Service 714c3ad047 feat: Support run tools in separate thread for live mode
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 860242861
2026-01-23 13:55:31 -08:00
Xuan YangandCopybara-Service d62f9c896c chore: Always skip executing partial function calls
Related: https://github.com/google/adk-python/issues/4159

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 859184844
2026-01-21 11:20:16 -08:00
George WealeandCopybara-Service 2367901ec5 chore: Upgrade to headers to 2026
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 858763407
2026-01-20 14:50:09 -08:00
Xiang (Sean) ZhouandCopybara-Service ab62b1bffd fix: Use the agent name as the author of the audio event
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 855789317
2026-01-13 10:38:53 -08:00
George WealeandCopybara-Service b28721508a feat: make LlmAgent.model optional with a default fallback
LlmAgent now resolves model from ancestors or a system default (gemini-2.5-flash) when unset. Added LlmAgent.set_default_model() to override the default globally

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 853006116
2026-01-06 17:56:14 -08:00
George WealeandCopybara-Service 688f48fffb fix: Update empty event check to include executable code and execution results
Close #3859
Close #3921

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 852375447
2026-01-05 11:04:34 -08:00
Xuan YangandCopybara-Service 0b1cff2976 feat: Enable PROGRESSIVE_SSE_STREAMING feature by default
Related: https://github.com/google/adk-python/issues/3705

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 846793027
2025-12-19 10:50:49 -08:00
Google Team MemberandCopybara-Service f35d129b4c feat(fix): Check all content parts for emptiness in _contains_empty_content
PiperOrigin-RevId: 845954226
2025-12-17 16:03:12 -08:00
Xiang (Sean) ZhouandCopybara-Service c6320caaa5 feat: Support interactions API for calling models
Co-authored-by: Xiang (Sean) Zhou <seanzhougoogle@google.com>
PiperOrigin-RevId: 843032402
2025-12-10 21:21:14 -08:00
Xuan YangandCopybara-Service 786aaed335 feat: Support streaming function call arguments in progressive SSE streaming feature
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 837172244
2025-11-26 10:15:31 -08:00
qieqieplusandCopybara-Service 4a42d0d9d8 feat: Add enum constraint to agent_name for transfer_to_agent
Merge https://github.com/google/adk-python/pull/2437

Current implementation of `transfer_to_agent` doesn't enforce strict constraints on agent names, we could use JSON Schema's enum definition to implement stricter constraints.

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2437 from qieqieplus:main 052e8e73b9d61c0998573a2077f15864873d0dd7
PiperOrigin-RevId: 836410397
2025-11-24 16:51:36 -08:00
Google Team MemberandCopybara-Service 631b58336d fix: Content is marked non empty if its first part contains text or inline_data or file_data or func call/response
PiperOrigin-RevId: 835063599
2025-11-20 22:19:28 -08:00
Xuan YangandCopybara-Service a5ac1d5e14 feat: Add progressive SSE streaming feature
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 833483804
2025-11-17 13:55:38 -08:00
Henrique CadioliandCopybara-Service 8f3c3bfda5 fix: cache canonical tools to avoid multiple calls when streaming
Merge https://github.com/google/adk-python/pull/3299
Fixes https://github.com/google/adk-python/issues/3237

Co-authored-by: Xuan Yang <xygoogle@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3299 from hcadioli:fix/cache-tools de02bd3e4533c3741edf05788a5e8b2d3d38bae4
PiperOrigin-RevId: 829499299
2025-11-07 10:51:32 -08:00
ananyablonkoandCopybara-Service 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
2025-11-05 17:41:35 -08:00
Xuan YangandCopybara-Service 9ec38c0d89 feat: Add on_model_error_callback in LlmAgent
Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 828560608
2025-11-05 11:37:08 -08:00
Xuan YangandCopybara-Service 6a94af24bf chore: Disable SetModelResponseTool workaround for Vertex AI Gemini 2+ models
Gemini models now [support Function calling being used together with structured output on Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#structured-output-bp).

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 827709903
2025-11-03 17:12:01 -08:00