From 0883d5f243a3840f36f901f5cb1008ec69be2c09 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 18:30:34 +0530 Subject: [PATCH 01/19] feat: allow thinking_config in generate_content_config (#4108) Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 20 ++++++++++++++++++-- src/google/adk/planners/built_in_planner.py | 7 +++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 8d85e48a84..28a4ecd6e3 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -14,6 +14,7 @@ from __future__ import annotations +import warnings import importlib import inspect import logging @@ -849,8 +850,6 @@ def validate_generate_content_config( ) -> types.GenerateContentConfig: if not generate_content_config: return types.GenerateContentConfig() - if generate_content_config.thinking_config: - raise ValueError('Thinking config should be set via LlmAgent.planner.') if generate_content_config.tools: raise ValueError('All tools must be set via LlmAgent.tools.') if generate_content_config.system_instruction: @@ -862,6 +861,23 @@ def validate_generate_content_config( 'Response schema must be set via LlmAgent.output_schema.' ) return generate_content_config + + def model_post_init(self, __context: Any) -> None: + """Provides a warning if multiple thinking configurations are found.""" + super().model_post_init(__context) + + # Check if thinking_config is set in both the model config and the planner + if (self.generate_content_config and + self.generate_content_config.thinking_config and + self.planner and + getattr(self.planner, 'thinking_config', None)): + warnings.warn( + 'Both `thinking_config` in `generate_content_config` and an ' + 'agent `planner` with thinking enabled are provided. The ' + 'planner\'s configuration will take precedence.', + UserWarning, + stacklevel=2, + ) @classmethod @experimental diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index 7429837ced..7abc7d2491 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -14,6 +14,7 @@ from typing import List from typing import Optional +from venv import logger from google.genai import types from typing_extensions import override @@ -57,6 +58,12 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: """ if self.thinking_config: llm_request.config = llm_request.config or types.GenerateContentConfig() + # Log if we are about to overwrite a config set directly by the user + if llm_request.config.thinking_config: + logger.info( + 'BuiltInPlanner is overwriting the existing thinking_config ' + 'on the LlmRequest.' + ) llm_request.config.thinking_config = self.thinking_config @override From 2d485cbe728777fd90337c45af9e9a202fe21c3b Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 19:02:39 +0530 Subject: [PATCH 02/19] fix: address review comments regarding imports and logging Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 1 - src/google/adk/planners/built_in_planner.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 28a4ecd6e3..25215d93b3 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -14,7 +14,6 @@ from __future__ import annotations -import warnings import importlib import inspect import logging diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index 7abc7d2491..e95c6938ee 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -14,7 +14,7 @@ from typing import List from typing import Optional -from venv import logger +import logging from google.genai import types from typing_extensions import override @@ -60,7 +60,7 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: llm_request.config = llm_request.config or types.GenerateContentConfig() # Log if we are about to overwrite a config set directly by the user if llm_request.config.thinking_config: - logger.info( + logging.info( 'BuiltInPlanner is overwriting the existing thinking_config ' 'on the LlmRequest.' ) From 25a9186e891d45980177d2f74d63ca92e9d67034 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 19:19:56 +0530 Subject: [PATCH 03/19] style: improve readability of precedence check in LlmAgent Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 25215d93b3..16cb85a17f 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -864,12 +864,18 @@ def validate_generate_content_config( def model_post_init(self, __context: Any) -> None: """Provides a warning if multiple thinking configurations are found.""" super().model_post_init(__context) - + # Check if thinking_config is set in both the model config and the planner - if (self.generate_content_config and - self.generate_content_config.thinking_config and + has_manual_thinking_config = ( + self.generate_content_config and + self.generate_content_config.thinking_config + ) + planner_has_thinking_config = ( self.planner and - getattr(self.planner, 'thinking_config', None)): + getattr(self.planner, 'thinking_config', None) + ) + + if has_manual_thinking_config and planner_has_thinking_config: warnings.warn( 'Both `thinking_config` in `generate_content_config` and an ' 'agent `planner` with thinking enabled are provided. The ' From c5aa50e2bb0b606357cc4e072eb0831cc788e9c5 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 19:52:30 +0530 Subject: [PATCH 04/19] docs: update LlmAgent docstring and use explicit None checks Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 6 +++--- src/google/adk/planners/built_in_planner.py | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 16cb85a17f..f571acb3d3 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -285,7 +285,7 @@ class LlmAgent(BaseAgent): """The additional content generation configurations. NOTE: not all fields are usable, e.g. tools must be configured via `tools`, - thinking_config must be configured via `planner` in LlmAgent. + thinking_config can be configured via generate_content_config or planner in LlmAgent. For example: use this config to adjust model temperature, configure safety settings, etc. @@ -868,11 +868,11 @@ def model_post_init(self, __context: Any) -> None: # Check if thinking_config is set in both the model config and the planner has_manual_thinking_config = ( self.generate_content_config and - self.generate_content_config.thinking_config + self.generate_content_config.thinking_config is not None ) planner_has_thinking_config = ( self.planner and - getattr(self.planner, 'thinking_config', None) + getattr(self.planner, 'thinking_config', None) is not None ) if has_manual_thinking_config and planner_has_thinking_config: diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index e95c6938ee..f500ba1e43 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -56,10 +56,9 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: Args: llm_request: The LLM request to apply the thinking config to. """ - if self.thinking_config: - llm_request.config = llm_request.config or types.GenerateContentConfig() - # Log if we are about to overwrite a config set directly by the user - if llm_request.config.thinking_config: + if self.thinking_config is not None: + # Log info if we are overwriting an existing config from generate_content_config + if llm_request.config.thinking_config is not None: logging.info( 'BuiltInPlanner is overwriting the existing thinking_config ' 'on the LlmRequest.' From abce5883997c2447399eeeb63f3f844d984e1a37 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 19:59:47 +0530 Subject: [PATCH 05/19] docs: update doc Signed-off-by: Akshat Kumar --- src/google/adk/planners/built_in_planner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index f500ba1e43..a66ad9f8d0 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -60,8 +60,8 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: # Log info if we are overwriting an existing config from generate_content_config if llm_request.config.thinking_config is not None: logging.info( - 'BuiltInPlanner is overwriting the existing thinking_config ' - 'on the LlmRequest.' + 'Overwriting `thinking_config` from `generate_content_config` with ' + 'the one provided by the `BuiltInPlanner`.' ) llm_request.config.thinking_config = self.thinking_config From 3a492dab725d59731f04757d68fdf56d6c6dc63c Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 20:06:45 +0530 Subject: [PATCH 06/19] docs: update doc Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index f571acb3d3..79ec184d74 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -285,7 +285,7 @@ class LlmAgent(BaseAgent): """The additional content generation configurations. NOTE: not all fields are usable, e.g. tools must be configured via `tools`, - thinking_config can be configured via generate_content_config or planner in LlmAgent. + thinking_config can be configured here or via the `planner`. If both are set, the planner's configuration takes precedence. For example: use this config to adjust model temperature, configure safety settings, etc. From d8dd6c922b0c49a79a77f9b1857ad4b04861e494 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 20:22:34 +0530 Subject: [PATCH 07/19] test: update unit tests to allow thinking_config and verify precedence warnings Signed-off-by: Akshat Kumar --- .../unittests/agents/test_llm_agent_fields.py | 79 ++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index ad70adc6b2..9adf26a41f 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -166,9 +166,32 @@ def _global_instruction_provider(ctx: ReadonlyContext) -> str: assert bypass_state_injection -async def test_async_canonical_global_instruction(): - async def _global_instruction_provider(ctx: ReadonlyContext) -> str: - return f'global instruction: {ctx.state["state_var"]}' +def test_validate_generate_content_config_thinking_config_allow(): + """Tests that thinking_config is now allowed directly in the agent init.""" + agent = LlmAgent( + name='test_agent', + generate_content_config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ), + ) + assert agent.generate_content_config.thinking_config.include_thoughts is True + + +def test_thinking_config_precedence_warning(): + """Tests that a UserWarning is issued when both manual config and planner exist.""" + from google.adk.planners.built_in_planner import BuiltInPlanner + + config = types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) + planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + + with pytest.warns(UserWarning, match="planner's configuration will take precedence"): + LlmAgent( + name='test_agent', + generate_content_config=config, + planner=planner + ) agent = LlmAgent( name='test_agent', global_instruction=_global_instruction_provider @@ -313,6 +336,56 @@ async def test_handle_google_search_with_other_tools(self): assert tools[1].name == 'google_search_agent' assert tools[1].__class__.__name__ == 'GoogleSearchAgentTool' + def test_validate_generate_content_config_thinking_config_allow(): + """Tests that thinking_config is now allowed in generate_content_config.""" + # This should NOT throw a ValueError + agent = LlmAgent( + name='test_agent', + generate_content_config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ), + ) + assert agent.generate_content_config.thinking_config.include_thoughts is True + + +def test_thinking_config_precedence_warning(): + """Tests that a UserWarning is issued when both manual config and planner exist.""" + from google.adk.planners.built_in_planner import BuiltInPlanner + + config = types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) + planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + + with pytest.warns(UserWarning, match="planner's configuration will take precedence"): + LlmAgent( + model='gemini-1.5-flash', + name='test_agent', + generate_content_config=config, + planner=planner + ) + + +async def test_builtin_planner_overwrite_logging(caplog): + """Tests that the planner logs an INFO message when overwriting a config.""" + import logging + from google.adk.planners.built_in_planner import BuiltInPlanner + + planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + + # Create a request that already has a thinking_config + req = LlmRequest( + contents=[], + config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) + ) + + with caplog.at_level(logging.INFO): + planner.apply_thinking_config(req) + + assert "Overwriting `thinking_config` from `generate_content_config`" in caplog.text + async def test_handle_google_search_with_other_tools_no_bypass(self): """Test that google_search is not wrapped into an agent.""" agent = LlmAgent( From bec52bb4e6d3d33291bb717a6934cc6d217cd684 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 20:28:21 +0530 Subject: [PATCH 08/19] test: remove obsolete failing test and add new verification tests Signed-off-by: Akshat Kumar --- tests/unittests/agents/test_llm_agent_fields.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index 9adf26a41f..e9c9c8425b 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -257,14 +257,15 @@ def _before_model_callback( assert agent.before_model_callback is not None -def test_validate_generate_content_config_thinking_config_throw(): - with pytest.raises(ValueError): - _ = LlmAgent( - name='test_agent', - generate_content_config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig() - ), - ) +def test_validate_generate_content_config_thinking_config_allow(): + """Tests that thinking_config is now allowed in generate_content_config.""" + agent = LlmAgent( + name='test_agent', + generate_content_config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ), + ) + assert agent.generate_content_config.thinking_config.include_thoughts is True def test_validate_generate_content_config_tools_throw(): From a631439c1284e60cb2c80156689a907c0106c61d Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 20:38:44 +0530 Subject: [PATCH 09/19] test: cleanup duplicate test definitions and fix imports Signed-off-by: Akshat Kumar --- .../unittests/agents/test_llm_agent_fields.py | 157 ++++++------------ 1 file changed, 53 insertions(+), 104 deletions(-) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index e9c9c8425b..2caa8dc508 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -165,110 +165,8 @@ def _global_instruction_provider(ctx: ReadonlyContext) -> str: assert canonical_global_instruction == 'global instruction: state_value' assert bypass_state_injection - -def test_validate_generate_content_config_thinking_config_allow(): - """Tests that thinking_config is now allowed directly in the agent init.""" - agent = LlmAgent( - name='test_agent', - generate_content_config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ), - ) - assert agent.generate_content_config.thinking_config.include_thoughts is True - - -def test_thinking_config_precedence_warning(): - """Tests that a UserWarning is issued when both manual config and planner exist.""" - from google.adk.planners.built_in_planner import BuiltInPlanner - - config = types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ) - planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) - - with pytest.warns(UserWarning, match="planner's configuration will take precedence"): - LlmAgent( - name='test_agent', - generate_content_config=config, - planner=planner - ) - - agent = LlmAgent( - name='test_agent', global_instruction=_global_instruction_provider - ) - ctx = await _create_readonly_context( - agent, state={'state_var': 'state_value'} - ) - canonical_global_instruction, bypass_state_injection = ( - await agent.canonical_global_instruction(ctx) - ) - assert canonical_global_instruction == 'global instruction: state_value' - assert bypass_state_injection - - -def test_output_schema_with_sub_agents_will_not_throw(): - class Schema(BaseModel): - pass - - sub_agent = LlmAgent( - name='sub_agent', - ) - - agent = LlmAgent( - name='test_agent', - output_schema=Schema, - sub_agents=[sub_agent], - ) - - # Transfer is not disabled - assert not agent.disallow_transfer_to_parent - assert not agent.disallow_transfer_to_peers - - assert agent.output_schema == Schema - assert agent.sub_agents == [sub_agent] - - -def test_output_schema_with_tools_will_not_throw(): - class Schema(BaseModel): - pass - - def _a_tool(): - pass - - LlmAgent( - name='test_agent', - output_schema=Schema, - tools=[_a_tool], - ) - - -def test_before_model_callback(): - def _before_model_callback( - callback_context: CallbackContext, - llm_request: LlmRequest, - ) -> None: - return None - - agent = LlmAgent( - name='test_agent', before_model_callback=_before_model_callback - ) - - # TODO: add more logic assertions later. - assert agent.before_model_callback is not None - - -def test_validate_generate_content_config_thinking_config_allow(): - """Tests that thinking_config is now allowed in generate_content_config.""" - agent = LlmAgent( - name='test_agent', - generate_content_config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ), - ) - assert agent.generate_content_config.thinking_config.include_thoughts is True - - def test_validate_generate_content_config_tools_throw(): + """Tests that tools cannot be set directly in config.""" with pytest.raises(ValueError): _ = LlmAgent( name='test_agent', @@ -279,6 +177,7 @@ def test_validate_generate_content_config_tools_throw(): def test_validate_generate_content_config_system_instruction_throw(): + """Tests that system instructions cannot be set directly in config.""" with pytest.raises(ValueError): _ = LlmAgent( name='test_agent', @@ -289,6 +188,7 @@ def test_validate_generate_content_config_system_instruction_throw(): def test_validate_generate_content_config_response_schema_throw(): + """Tests that response schema cannot be set directly in config.""" class Schema(BaseModel): pass @@ -301,6 +201,55 @@ class Schema(BaseModel): ) +def test_validate_generate_content_config_thinking_config_allow(): + """Tests that thinking_config is now allowed directly in the agent init.""" + agent = LlmAgent( + name='test_agent', + generate_content_config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ), + ) + assert agent.generate_content_config.thinking_config.include_thoughts is True + + +def test_thinking_config_precedence_warning(): + """Tests that a UserWarning is issued when both manual config and planner exist.""" + from google.adk.planners.built_in_planner import BuiltInPlanner + + config = types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) + planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + + with pytest.warns(UserWarning, match="planner's configuration will take precedence"): + LlmAgent( + name='test_agent', + generate_content_config=config, + planner=planner + ) + + +async def test_builtin_planner_overwrite_logging(caplog): + """Tests that the planner logs an INFO message when overwriting a config.""" + import logging + from google.adk.planners.built_in_planner import BuiltInPlanner + + planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + + # Create a request that already has a thinking_config + req = LlmRequest( + contents=[], + config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) + ) + + with caplog.at_level(logging.INFO): + planner.apply_thinking_config(req) + + assert "Overwriting `thinking_config` from `generate_content_config`" in caplog.text + + def test_allow_transfer_by_default(): sub_agent = LlmAgent(name='sub_agent') agent = LlmAgent(name='test_agent', sub_agents=[sub_agent]) @@ -337,7 +286,7 @@ async def test_handle_google_search_with_other_tools(self): assert tools[1].name == 'google_search_agent' assert tools[1].__class__.__name__ == 'GoogleSearchAgentTool' - def test_validate_generate_content_config_thinking_config_allow(): +def test_validate_generate_content_config_thinking_config_allow(): """Tests that thinking_config is now allowed in generate_content_config.""" # This should NOT throw a ValueError agent = LlmAgent( From 412447a591006707dde9ec778b478dee8b4734ab Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 20:51:54 +0530 Subject: [PATCH 10/19] refactor: simplify precedence check and remove duplicate tests Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 5 +- .../unittests/agents/test_llm_agent_fields.py | 127 +++++++++++------- 2 files changed, 81 insertions(+), 51 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 79ec184d74..34543e989c 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -866,12 +866,11 @@ def model_post_init(self, __context: Any) -> None: super().model_post_init(__context) # Check if thinking_config is set in both the model config and the planner + # Using getattr with a default value is cleaner for optional attributes. has_manual_thinking_config = ( - self.generate_content_config and - self.generate_content_config.thinking_config is not None + getattr(self.generate_content_config, 'thinking_config', None) is not None ) planner_has_thinking_config = ( - self.planner and getattr(self.planner, 'thinking_config', None) is not None ) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index 2caa8dc508..ebee191c7b 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -165,6 +165,85 @@ def _global_instruction_provider(ctx: ReadonlyContext) -> str: assert canonical_global_instruction == 'global instruction: state_value' assert bypass_state_injection + +async def test_async_canonical_global_instruction(): + async def _global_instruction_provider(ctx: ReadonlyContext) -> str: + return f'global instruction: {ctx.state["state_var"]}' + + agent = LlmAgent( + name='test_agent', global_instruction=_global_instruction_provider + ) + ctx = await _create_readonly_context( + agent, state={'state_var': 'state_value'} + ) + canonical_global_instruction, bypass_state_injection = ( + await agent.canonical_global_instruction(ctx) + ) + assert canonical_global_instruction == 'global instruction: state_value' + assert bypass_state_injection + + +def test_output_schema_with_sub_agents_will_not_throw(): + class Schema(BaseModel): + pass + + sub_agent = LlmAgent( + name='sub_agent', + ) + + agent = LlmAgent( + name='test_agent', + output_schema=Schema, + sub_agents=[sub_agent], + ) + + # Transfer is not disabled + assert not agent.disallow_transfer_to_parent + assert not agent.disallow_transfer_to_peers + + assert agent.output_schema == Schema + assert agent.sub_agents == [sub_agent] + + +def test_output_schema_with_tools_will_not_throw(): + class Schema(BaseModel): + pass + + def _a_tool(): + pass + + LlmAgent( + name='test_agent', + output_schema=Schema, + tools=[_a_tool], + ) + + +def test_before_model_callback(): + def _before_model_callback( + callback_context: CallbackContext, + llm_request: LlmRequest, + ) -> None: + return None + + agent = LlmAgent( + name='test_agent', before_model_callback=_before_model_callback + ) + + # TODO: add more logic assertions later. + assert agent.before_model_callback is not None + + +def test_validate_generate_content_config_thinking_config_throw(): + with pytest.raises(ValueError): + _ = LlmAgent( + name='test_agent', + generate_content_config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig() + ), + ) + + def test_validate_generate_content_config_tools_throw(): """Tests that tools cannot be set directly in config.""" with pytest.raises(ValueError): @@ -229,26 +308,6 @@ def test_thinking_config_precedence_warning(): ) -async def test_builtin_planner_overwrite_logging(caplog): - """Tests that the planner logs an INFO message when overwriting a config.""" - import logging - from google.adk.planners.built_in_planner import BuiltInPlanner - - planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) - - # Create a request that already has a thinking_config - req = LlmRequest( - contents=[], - config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ) - ) - - with caplog.at_level(logging.INFO): - planner.apply_thinking_config(req) - - assert "Overwriting `thinking_config` from `generate_content_config`" in caplog.text - def test_allow_transfer_by_default(): sub_agent = LlmAgent(name='sub_agent') @@ -286,34 +345,6 @@ async def test_handle_google_search_with_other_tools(self): assert tools[1].name == 'google_search_agent' assert tools[1].__class__.__name__ == 'GoogleSearchAgentTool' -def test_validate_generate_content_config_thinking_config_allow(): - """Tests that thinking_config is now allowed in generate_content_config.""" - # This should NOT throw a ValueError - agent = LlmAgent( - name='test_agent', - generate_content_config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ), - ) - assert agent.generate_content_config.thinking_config.include_thoughts is True - - -def test_thinking_config_precedence_warning(): - """Tests that a UserWarning is issued when both manual config and planner exist.""" - from google.adk.planners.built_in_planner import BuiltInPlanner - - config = types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ) - planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) - - with pytest.warns(UserWarning, match="planner's configuration will take precedence"): - LlmAgent( - model='gemini-1.5-flash', - name='test_agent', - generate_content_config=config, - planner=planner - ) async def test_builtin_planner_overwrite_logging(caplog): From 8438ed6327cd9fe3abd11913c2fa28fbfaf41f64 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 21:02:38 +0530 Subject: [PATCH 11/19] test: cleanup unit tests and fix logging test placement Signed-off-by: Akshat Kumar --- .../unittests/agents/test_llm_agent_fields.py | 102 ++++++++---------- 1 file changed, 45 insertions(+), 57 deletions(-) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index ebee191c7b..b05fb6c255 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -234,13 +234,31 @@ def _before_model_callback( assert agent.before_model_callback is not None -def test_validate_generate_content_config_thinking_config_throw(): - with pytest.raises(ValueError): - _ = LlmAgent( +def test_validate_generate_content_config_thinking_config_allow(): + """Tests that thinking_config is now allowed directly in the agent init.""" + agent = LlmAgent( + name='test_agent', + generate_content_config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ), + ) + assert agent.generate_content_config.thinking_config.include_thoughts is True + + +def test_thinking_config_precedence_warning(): + """Tests that a UserWarning is issued when both manual config and planner exist.""" + from google.adk.planners.built_in_planner import BuiltInPlanner + + config = types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) + planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + + with pytest.warns(UserWarning, match="planner's configuration will take precedence"): + LlmAgent( name='test_agent', - generate_content_config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig() - ), + generate_content_config=config, + planner=planner ) @@ -280,35 +298,6 @@ class Schema(BaseModel): ) -def test_validate_generate_content_config_thinking_config_allow(): - """Tests that thinking_config is now allowed directly in the agent init.""" - agent = LlmAgent( - name='test_agent', - generate_content_config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ), - ) - assert agent.generate_content_config.thinking_config.include_thoughts is True - - -def test_thinking_config_precedence_warning(): - """Tests that a UserWarning is issued when both manual config and planner exist.""" - from google.adk.planners.built_in_planner import BuiltInPlanner - - config = types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ) - planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) - - with pytest.warns(UserWarning, match="planner's configuration will take precedence"): - LlmAgent( - name='test_agent', - generate_content_config=config, - planner=planner - ) - - - def test_allow_transfer_by_default(): sub_agent = LlmAgent(name='sub_agent') agent = LlmAgent(name='test_agent', sub_agents=[sub_agent]) @@ -345,28 +334,6 @@ async def test_handle_google_search_with_other_tools(self): assert tools[1].name == 'google_search_agent' assert tools[1].__class__.__name__ == 'GoogleSearchAgentTool' - - -async def test_builtin_planner_overwrite_logging(caplog): - """Tests that the planner logs an INFO message when overwriting a config.""" - import logging - from google.adk.planners.built_in_planner import BuiltInPlanner - - planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) - - # Create a request that already has a thinking_config - req = LlmRequest( - contents=[], - config=types.GenerateContentConfig( - thinking_config=types.ThinkingConfig(include_thoughts=True) - ) - ) - - with caplog.at_level(logging.INFO): - planner.apply_thinking_config(req) - - assert "Overwriting `thinking_config` from `generate_content_config`" in caplog.text - async def test_handle_google_search_with_other_tools_no_bypass(self): """Test that google_search is not wrapped into an agent.""" agent = LlmAgent( @@ -525,3 +492,24 @@ def test_agent_with_litellm_string_model(model_name): agent = LlmAgent(name='test_agent', model=model_name) assert isinstance(agent.canonical_model, LiteLlm) assert agent.canonical_model.model == model_name + + +async def test_builtin_planner_overwrite_logging(caplog): + """Tests that the planner logs an INFO message when overwriting a config.""" + import logging + from google.adk.planners.built_in_planner import BuiltInPlanner + + planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + + # Create a request that already has a thinking_config + req = LlmRequest( + contents=[], + config=types.GenerateContentConfig( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) + ) + + with caplog.at_level(logging.INFO): + planner.apply_thinking_config(req) + + assert "Overwriting `thinking_config` from `generate_content_config`" in caplog.text \ No newline at end of file From 4b23903dd27e203d3892f9e5fb9025b864a55d3c Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 21:08:52 +0530 Subject: [PATCH 12/19] test:clean up Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 6 ++++-- tests/unittests/agents/test_llm_agent_fields.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 34543e989c..7bc56fb293 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -868,10 +868,12 @@ def model_post_init(self, __context: Any) -> None: # Check if thinking_config is set in both the model config and the planner # Using getattr with a default value is cleaner for optional attributes. has_manual_thinking_config = ( - getattr(self.generate_content_config, 'thinking_config', None) is not None + self.generate_content_config is not None + and self.generate_content_config.thinking_config is not None ) planner_has_thinking_config = ( - getattr(self.planner, 'thinking_config', None) is not None + self.planner is not None + and getattr(self.planner, 'thinking_config', None) is not None ) if has_manual_thinking_config and planner_has_thinking_config: diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index b05fb6c255..01d1740e67 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -494,7 +494,7 @@ def test_agent_with_litellm_string_model(model_name): assert agent.canonical_model.model == model_name -async def test_builtin_planner_overwrite_logging(caplog): +def test_builtin_planner_overwrite_logging(caplog): """Tests that the planner logs an INFO message when overwriting a config.""" import logging from google.adk.planners.built_in_planner import BuiltInPlanner From 072f8ad9ceda0232139c96b8363c8ec48bfdd0c8 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 21:31:52 +0530 Subject: [PATCH 13/19] style: move test imports to top level per PEP 8 Signed-off-by: Akshat Kumar --- tests/unittests/agents/test_llm_agent_fields.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index 01d1740e67..e1538cbd92 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -17,7 +17,9 @@ from typing import Any from typing import Optional from unittest import mock +import logging +from google.adk.planners.built_in_planner import BuiltInPlanner from google.adk.agents.callback_context import CallbackContext from google.adk.agents.invocation_context import InvocationContext from google.adk.agents.llm_agent import LlmAgent @@ -247,7 +249,6 @@ def test_validate_generate_content_config_thinking_config_allow(): def test_thinking_config_precedence_warning(): """Tests that a UserWarning is issued when both manual config and planner exist.""" - from google.adk.planners.built_in_planner import BuiltInPlanner config = types.GenerateContentConfig( thinking_config=types.ThinkingConfig(include_thoughts=True) @@ -496,8 +497,6 @@ def test_agent_with_litellm_string_model(model_name): def test_builtin_planner_overwrite_logging(caplog): """Tests that the planner logs an INFO message when overwriting a config.""" - import logging - from google.adk.planners.built_in_planner import BuiltInPlanner planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) From 74600d8b0dd52fb1647ef16a592f40f70f4f62b3 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Sun, 11 Jan 2026 21:37:17 +0530 Subject: [PATCH 14/19] refactor: simplify apply_thinking_config and cleanup test imports Signed-off-by: Akshat Kumar --- src/google/adk/planners/built_in_planner.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index a66ad9f8d0..a44e7560e9 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -56,14 +56,15 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: Args: llm_request: The LLM request to apply the thinking config to. """ - if self.thinking_config is not None: - # Log info if we are overwriting an existing config from generate_content_config - if llm_request.config.thinking_config is not None: - logging.info( - 'Overwriting `thinking_config` from `generate_content_config` with ' - 'the one provided by the `BuiltInPlanner`.' - ) - llm_request.config.thinking_config = self.thinking_config + llm_request.config = llm_request.config or types.GenerateContentConfig() + + # Log info if we are overwriting an existing config from generate_content_config + if llm_request.config.thinking_config is not None: + logging.info( + 'Overwriting `thinking_config` from `generate_content_config` with ' + 'the one provided by the `BuiltInPlanner`.' + ) + llm_request.config.thinking_config = self.thinking_config @override def build_planning_instruction( From f137045cbd920888f55f60d4e6c9ae68a8651f86 Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Tue, 13 Jan 2026 19:51:46 +0530 Subject: [PATCH 15/19] fix: address maintainer feedback on logic bug and redundant comments Signed-off-by: Akshat Kumar --- src/google/adk/planners/built_in_planner.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index a44e7560e9..26a5724428 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -56,15 +56,14 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: Args: llm_request: The LLM request to apply the thinking config to. """ - llm_request.config = llm_request.config or types.GenerateContentConfig() - - # Log info if we are overwriting an existing config from generate_content_config - if llm_request.config.thinking_config is not None: - logging.info( - 'Overwriting `thinking_config` from `generate_content_config` with ' - 'the one provided by the `BuiltInPlanner`.' - ) - llm_request.config.thinking_config = self.thinking_config + if self.thinking_config: + llm_request.config = llm_request.config or types.GenerateContentConfig() + if llm_request.config.thinking_config: + logging.info( + 'Overwriting `thinking_config` from `generate_content_config` with ' + 'the one provided by the `BuiltInPlanner`.' + ) + llm_request.config.thinking_config = self.thinking_config @override def build_planning_instruction( From 978d7d12a29b7c115952686d4c139f7748ad06ec Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Tue, 13 Jan 2026 20:00:43 +0530 Subject: [PATCH 16/19] fix: resolve logic bug in planner and refactor for consistency Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 10 ++++------ tests/unittests/agents/test_llm_agent_fields.py | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 7bc56fb293..2458a853b4 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -865,15 +865,13 @@ def model_post_init(self, __context: Any) -> None: """Provides a warning if multiple thinking configurations are found.""" super().model_post_init(__context) - # Check if thinking_config is set in both the model config and the planner - # Using getattr with a default value is cleaner for optional attributes. + # Check if thinking_config is set in both the model config and the planner. + # Using getattr for consistency in checking optional attributes. has_manual_thinking_config = ( - self.generate_content_config is not None - and self.generate_content_config.thinking_config is not None + getattr(self.generate_content_config, 'thinking_config', None) is not None ) planner_has_thinking_config = ( - self.planner is not None - and getattr(self.planner, 'thinking_config', None) is not None + getattr(self.planner, 'thinking_config', None) is not None ) if has_manual_thinking_config and planner_has_thinking_config: diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index e1538cbd92..242ab8e29d 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -14,10 +14,11 @@ """Unit tests for canonical_xxx fields in LlmAgent.""" +import logging from typing import Any from typing import Optional from unittest import mock -import logging + from google.adk.planners.built_in_planner import BuiltInPlanner from google.adk.agents.callback_context import CallbackContext From cd2c510460bc46ab1c4dd747d13e7ec54fe36b0f Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Thu, 15 Jan 2026 10:24:13 +0530 Subject: [PATCH 17/19] style: automatic cleanup of imports and formatting via isort and pyink Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 25 +++++++--------- src/google/adk/planners/built_in_planner.py | 8 +++-- .../unittests/agents/test_llm_agent_fields.py | 29 +++++++++++-------- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index 2458a853b4..a71c7d2680 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -860,27 +860,22 @@ def validate_generate_content_config( 'Response schema must be set via LlmAgent.output_schema.' ) return generate_content_config - + + @override def model_post_init(self, __context: Any) -> None: """Provides a warning if multiple thinking configurations are found.""" super().model_post_init(__context) - # Check if thinking_config is set in both the model config and the planner. - # Using getattr for consistency in checking optional attributes. - has_manual_thinking_config = ( - getattr(self.generate_content_config, 'thinking_config', None) is not None - ) - planner_has_thinking_config = ( - getattr(self.planner, 'thinking_config', None) is not None - ) - - if has_manual_thinking_config and planner_has_thinking_config: + # Note: Using getattr to check both locations for thinking_config + if getattr( + self.generate_content_config, 'thinking_config', None + ) and getattr(self.planner, 'thinking_config', None): warnings.warn( - 'Both `thinking_config` in `generate_content_config` and an ' - 'agent `planner` with thinking enabled are provided. The ' - 'planner\'s configuration will take precedence.', + 'Both `thinking_config` in `generate_content_config` and a ' + 'planner with `thinking_config` are provided. The ' + "planner's configuration will take precedence.", UserWarning, - stacklevel=2, + stacklevel=3, ) @classmethod diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index 26a5724428..5b2867851a 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + +import logging from typing import List from typing import Optional -import logging from google.genai import types from typing_extensions import override @@ -24,6 +26,8 @@ from ..models.llm_request import LlmRequest from .base_planner import BasePlanner +_LOGGER = logging.getLogger(__name__) + class BuiltInPlanner(BasePlanner): """The built-in planner that uses model's built-in thinking features. @@ -59,7 +63,7 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: if self.thinking_config: llm_request.config = llm_request.config or types.GenerateContentConfig() if llm_request.config.thinking_config: - logging.info( + _LOGGER.info( 'Overwriting `thinking_config` from `generate_content_config` with ' 'the one provided by the `BuiltInPlanner`.' ) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index 242ab8e29d..2cfc55609a 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -19,8 +19,6 @@ from typing import Optional from unittest import mock - -from google.adk.planners.built_in_planner import BuiltInPlanner from google.adk.agents.callback_context import CallbackContext from google.adk.agents.invocation_context import InvocationContext from google.adk.agents.llm_agent import LlmAgent @@ -30,6 +28,7 @@ from google.adk.models.lite_llm import LiteLlm from google.adk.models.llm_request import LlmRequest from google.adk.models.registry import LLMRegistry +from google.adk.planners.built_in_planner import BuiltInPlanner from google.adk.sessions.in_memory_session_service import InMemorySessionService from google.adk.tools.google_search_tool import google_search from google.adk.tools.google_search_tool import GoogleSearchTool @@ -254,14 +253,14 @@ def test_thinking_config_precedence_warning(): config = types.GenerateContentConfig( thinking_config=types.ThinkingConfig(include_thoughts=True) ) - planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + planner = BuiltInPlanner( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) - with pytest.warns(UserWarning, match="planner's configuration will take precedence"): - LlmAgent( - name='test_agent', - generate_content_config=config, - planner=planner - ) + with pytest.warns( + UserWarning, match="planner's configuration will take precedence" + ): + LlmAgent(name='test_agent', generate_content_config=config, planner=planner) def test_validate_generate_content_config_tools_throw(): @@ -288,6 +287,7 @@ def test_validate_generate_content_config_system_instruction_throw(): def test_validate_generate_content_config_response_schema_throw(): """Tests that response schema cannot be set directly in config.""" + class Schema(BaseModel): pass @@ -499,17 +499,22 @@ def test_agent_with_litellm_string_model(model_name): def test_builtin_planner_overwrite_logging(caplog): """Tests that the planner logs an INFO message when overwriting a config.""" - planner = BuiltInPlanner(thinking_config=types.ThinkingConfig(include_thoughts=True)) + planner = BuiltInPlanner( + thinking_config=types.ThinkingConfig(include_thoughts=True) + ) # Create a request that already has a thinking_config req = LlmRequest( contents=[], config=types.GenerateContentConfig( thinking_config=types.ThinkingConfig(include_thoughts=True) - ) + ), ) with caplog.at_level(logging.INFO): planner.apply_thinking_config(req) - assert "Overwriting `thinking_config` from `generate_content_config`" in caplog.text \ No newline at end of file + assert ( + 'Overwriting `thinking_config` from `generate_content_config`' + in caplog.text + ) From 7d3c06f8b051e9bfdecae39bd3cd7d7a001267db Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Thu, 15 Jan 2026 11:20:37 +0530 Subject: [PATCH 18/19] style: final manual fix for import grouping and hierarchical logging Signed-off-by: Akshat Kumar --- tests/unittests/agents/test_llm_agent_fields.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index 2cfc55609a..2594fb9fcd 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -19,6 +19,9 @@ from typing import Optional from unittest import mock +from pydantic import BaseModel +import pytest + from google.adk.agents.callback_context import CallbackContext from google.adk.agents.invocation_context import InvocationContext from google.adk.agents.llm_agent import LlmAgent @@ -30,12 +33,10 @@ from google.adk.models.registry import LLMRegistry from google.adk.planners.built_in_planner import BuiltInPlanner from google.adk.sessions.in_memory_session_service import InMemorySessionService -from google.adk.tools.google_search_tool import google_search from google.adk.tools.google_search_tool import GoogleSearchTool +from google.adk.tools.google_search_tool import google_search from google.adk.tools.vertex_ai_search_tool import VertexAiSearchTool from google.genai import types -from pydantic import BaseModel -import pytest async def _create_readonly_context( From 39b55ecdb2fe9b34da5782e43b4b0bdf3e8c75cd Mon Sep 17 00:00:00 2001 From: Akshat Kumar Date: Thu, 15 Jan 2026 11:57:49 +0530 Subject: [PATCH 19/19] fix: address bot suggestions for stacklevel and redundant config initialization Signed-off-by: Akshat Kumar --- src/google/adk/agents/llm_agent.py | 2 +- src/google/adk/planners/built_in_planner.py | 1 - tests/unittests/agents/test_llm_agent_fields.py | 7 +++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index a71c7d2680..771cd0e190 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -875,7 +875,7 @@ def model_post_init(self, __context: Any) -> None: 'planner with `thinking_config` are provided. The ' "planner's configuration will take precedence.", UserWarning, - stacklevel=3, + stacklevel=5, ) @classmethod diff --git a/src/google/adk/planners/built_in_planner.py b/src/google/adk/planners/built_in_planner.py index 5b2867851a..fd6f10cfc2 100644 --- a/src/google/adk/planners/built_in_planner.py +++ b/src/google/adk/planners/built_in_planner.py @@ -61,7 +61,6 @@ def apply_thinking_config(self, llm_request: LlmRequest) -> None: llm_request: The LLM request to apply the thinking config to. """ if self.thinking_config: - llm_request.config = llm_request.config or types.GenerateContentConfig() if llm_request.config.thinking_config: _LOGGER.info( 'Overwriting `thinking_config` from `generate_content_config` with ' diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index 2594fb9fcd..2cfc55609a 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -19,9 +19,6 @@ from typing import Optional from unittest import mock -from pydantic import BaseModel -import pytest - from google.adk.agents.callback_context import CallbackContext from google.adk.agents.invocation_context import InvocationContext from google.adk.agents.llm_agent import LlmAgent @@ -33,10 +30,12 @@ from google.adk.models.registry import LLMRegistry from google.adk.planners.built_in_planner import BuiltInPlanner from google.adk.sessions.in_memory_session_service import InMemorySessionService -from google.adk.tools.google_search_tool import GoogleSearchTool from google.adk.tools.google_search_tool import google_search +from google.adk.tools.google_search_tool import GoogleSearchTool from google.adk.tools.vertex_ai_search_tool import VertexAiSearchTool from google.genai import types +from pydantic import BaseModel +import pytest async def _create_readonly_context(