Commit Graph
89 Commits
Author SHA1 Message Date
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
Josh SorefandCopybara-Service aa1233608a chore: Fix spelling
Merge https://github.com/google/adk-python/pull/2447

This PR corrects misspellings identified by the [check-spelling action](https://github.com/marketplace/actions/check-spelling)

The misspellings have been reported at https://github.com/jsoref/adk-python/actions/runs/16840838898/attempts/1#summary-47711379253

The action reports that the changes in this PR would make it happy: https://github.com/jsoref/adk-python/actions/runs/16840839269/attempts/1#summary-47711380479

Note: while I use tooling to identify errors, the tooling doesn't _actually_ provide the corrections, I'm picking them on my own. I'm a human, and I may make mistakes.

I've included a couple of changes to make CI happy. Personally, I object to CI being in a state of "random drive by person who adds a blank line in the middle of a file must fix all the preexisting bugs in the file", but that appears to be the state for this repository.

COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2447 from jsoref:spelling d85398e7fd154d124d477c6af6181481a01f34e0
PiperOrigin-RevId: 827629615
2025-11-03 13:33:53 -08:00
Eliza HuangandCopybara-Service a2c6a8a85c feat: Expose the Python code run by the code interpreter in the logs
Co-authored-by: Eliza Huang <heliza@google.com>
PiperOrigin-RevId: 826521391
2025-10-31 09:38:53 -07:00
Kevin QianandCopybara-Service b8a2b6c570 fix: include delimiter when matching events from parent nodes in content processor
Previously we only do a simple prefix string matching, thus `agent_00` will match with `agent_0`

With this new change, we either check directly equality, or must expect seeing `agent_0.`. See added test for branches we now match / skip.

TBF `.` is also not a perfect delimiter (I would imagine users might put dot in agent names). We might consider a follow up that bans such agent names.

Tested with script in the linked issue (I updated prompt so we see which agent they see from):

Before:

```
[agent_8]: 73
[agent_0]: 97
[agent_1]: 73
[agent_5]: 97
[agent_4]: 73
[agent_2]: 73
[agent_3]: 73
[agent_9]: 93
[agent_6]: 73
[agent_7]: 1
[agent_70]: 1 (agent_7)
[agent_20]: 73 (agent_2)
[agent_30]: 73 (agent_3)
[agent_00]: 97 (agent_0)
[agent_40]: 73 (agent_4)
[agent_80]: 73 (agent_8)
[agent_50]: 97 (agent_5)
[agent_90]: 93 (agent_9)
[agent_10]: 73 (agent_1)
[agent_60]: 73 (agent_6)
```

After:
```
[agent_9]: 73
[agent_6]: 73
[agent_2]: 73
[agent_7]: 93
[agent_4]: 73
[agent_1]: 73
[agent_3]: 73
[agent_5]: 97
[agent_0]: 73
[agent_8]: 87
[agent_50]: 0
[agent_80]: 0
[agent_10]: 0
[agent_90]: 0
[agent_30]: 0
[agent_20]: 0
[agent_60]: 0
[agent_00]: 0
[agent_40]: 0
[agent_70]: 0
```

Closes #2948

Co-authored-by: Kevin Qian <kqian@google.com>
PiperOrigin-RevId: 826187198
2025-10-30 14:26:39 -07:00
Jaroslav PantsjohaandCopybara-Service 34d9b53f37 feat: Enhance error messages for tool and agent not found errors
Merge https://github.com/google/adk-python/pull/3219

## Summary

Enhance error messages for tool and agent not found errors to provide actionable guidance and reduce developer debugging time from hours to minutes.

Fixes #3217

## Changes

### Modified Files

1. **`src/google/adk/flows/llm_flows/functions.py`**
   - Enhanced `_get_tool()` error message with:
     - Available tools list (formatted, truncated to 20 for readability)
     - Possible causes
     - Suggested fixes
     - Fuzzy matching suggestions

2. **`src/google/adk/agents/llm_agent.py`**
   - Enhanced `__get_agent_to_run()` error message with:
     - Available agents list (formatted, truncated to 20 for readability)
     - Timing/ordering issue explanation
     - Fuzzy matching for agent names
   - Added `_get_available_agent_names()` helper method

### New Test Files

3. **`tests/unittests/flows/llm_flows/test_functions_error_messages.py`**
   - Tests for enhanced tool not found error messages
   - Fuzzy matching validation
   - Edge cases (no close matches, empty tools dict, 100+ tools)

4. **`tests/unittests/agents/test_llm_agent_error_messages.py`**
   - Tests for enhanced agent not found error messages
   - Agent tree traversal validation
   - Fuzzy matching for agents
   - Long list truncation

## Testing Plan

### Unit Tests

```bash
pytest tests/unittests/flows/llm_flows/test_functions_error_messages.py -v
pytest tests/unittests/agents/test_llm_agent_error_messages.py -v
```

**Results**:  8/8 tests passing

```
tests/unittests/flows/llm_flows/test_functions_error_messages.py::test_tool_not_found_enhanced_error PASSED
tests/unittests/flows/llm_flows/test_functions_error_messages.py::test_tool_not_found_fuzzy_matching PASSED
tests/unittests/flows/llm_flows/test_functions_error_messages.py::test_tool_not_found_no_fuzzy_match PASSED
tests/unittests/flows/llm_flows/test_functions_error_messages.py::test_tool_not_found_truncates_long_list PASSED
tests/unittests/agents/test_llm_agent_error_messages.py::test_agent_not_found_enhanced_error PASSED
tests/unittests/agents/test_llm_agent_error_messages.py::test_agent_not_found_fuzzy_matching PASSED
tests/unittests/agents/test_llm_agent_error_messages.py::test_agent_tree_traversal PASSED
tests/unittests/agents/test_llm_agent_error_messages.py::test_agent_not_found_truncates_long_list PASSED

8 passed, 1 warning in 4.38s
```

### Example Enhanced Error Messages

#### Before (Current Error)

```
ValueError: Function get_equipment_specs is not found in the tools_dict: dict_keys(['get_equipment_details', 'query_vendor_catalog', 'score_proposals'])
```

#### After (Enhanced Error)

```
Function 'get_equipment_specs' is not found in available tools.

Available tools: get_equipment_details, query_vendor_catalog, score_proposals

Possible causes:
  1. LLM hallucinated the function name - review agent instruction clarity
  2. Tool not registered - verify agent.tools list
  3. Name mismatch - check for typos

Suggested fixes:
  - Review agent instruction to ensure tool usage is clear
  - Verify tool is included in agent.tools list
  - Check for typos in function name

Did you mean one of these?
  - get_equipment_details
```

## Community Impact

- **Addresses 3 active issues**: #2050, #2933 (12 comments), #2164
- **Reduces debugging time** from 3+ hours to < 5 minutes (validated in production multi-agent RFQ solution for recent partner nanothon initiative)
- **Improves developer experience** for new ADK users

## Implementation Details

- Uses standard library `difflib` for fuzzy matching (no new dependencies)
- Error path only (no performance impact on happy path)
- Measured performance: < 0.03ms per error
- Truncates long lists to first 20 items to prevent log overflow
- Fully backward compatible (same exception types)

## Checklist

- [x] Unit tests added and passing (8/8 tests)
- [x] Code formatted with `./autoformat.sh` (isort + pyink)
- [x] No new dependencies (uses standard library `difflib`)
- [x] Docstrings updated
- [x] Tested with Python 3.11
- [x] Issue #3217 created and linked

## Related Issues

- Fixes #3217
- Addresses #2050 - Tool verification callback request
- Addresses #2933 - How to handle "Function is not found in the tools_dict" Error
- Addresses #2164 - ValueError: {agent} not found in agent tree

---

**Note**: For production scenarios where LLM tool hallucinations occur, ADK's built-in [`ReflectAndRetryToolPlugin`](https://github.com/google/adk-python/blob/main/src/google/adk/plugins/reflect_retry_tool_plugin.py) can automatically retry failed tool calls (available since v1.16.0). This PR's enhanced error messages complement that by helping developers quickly identify and fix configuration issues during development.

Cheers, JP

Co-authored-by: Yvonne Yu <yyyu@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3219 from jpantsjoha:feat/better-error-messages a4df8bfb031685dce9e528d8eb7006f53447b75b
PiperOrigin-RevId: 826132579
2025-10-30 12:08:13 -07:00
George WealeandCopybara-Service 1e6a9daa63 fix: Change instruction insertion to respect tool call/response pairs
Make sure _add_instructions_to_user_content skips over user messages that carry function_response parts so tool_use/tool_result blocks stay together

Close #3229

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 826076141
2025-10-30 09:54:49 -07:00
Ieva GrublyteandCopybara-Service c0892c725c chore: remove legacy validation that forbids co-exist of agent transfer and output_schema. Closes #3318
Remove validation for output_schema and agent transfer flags.

The check that prevented `output_schema` from co-existing with agent transfer capabilities (`disallow_transfer_to_parent` or `disallow_transfer_to_peers` being False) has been removed. The agent will no longer automatically set these transfer flags to True when `output_schema` is present.

Co-authored-by: Ieva Grublyte <ievagrublyte@google.com>
PiperOrigin-RevId: 825998224
2025-10-30 06:06:26 -07:00