From 850235a559963df6f7d94a0298a8850961454fcb Mon Sep 17 00:00:00 2001 From: chelsealong Date: Mon, 24 Aug 2026 17:37:41 +0000 Subject: [PATCH] fix(tools): use full query text and own logger in PreloadMemoryTool PreloadMemoryTool only read the search query from the first content part, so a leading non-text part (a file/image, or a placeholder text part some providers require) either skipped the search entirely or searched memory using the placeholder instead of the user's actual question. Join text across all parts, matching the same idiom already used on the memory-rendering side in _memory_entry_utils.extract_text. Also log retrieval failures through the module's own google_adk logger instead of the root logger, so operators who configure logging by the google_adk namespace can observe and alert on a failing memory backend instead of it looking identical to "no memories matched". Fixes #6884 --- src/google/adk/tools/preload_memory_tool.py | 13 +++-- .../tools/test_preload_memory_tool.py | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/google/adk/tools/preload_memory_tool.py b/src/google/adk/tools/preload_memory_tool.py index a69421f0b2..e3bfc59483 100644 --- a/src/google/adk/tools/preload_memory_tool.py +++ b/src/google/adk/tools/preload_memory_tool.py @@ -52,18 +52,17 @@ async def process_llm_request( llm_request: LlmRequest, ) -> None: user_content = tool_context.user_content - if ( - not user_content - or not user_content.parts - or not user_content.parts[0].text - ): + if not user_content or not user_content.parts: + return + + user_query = ' '.join(part.text for part in user_content.parts if part.text) + if not user_query: return - user_query: str = user_content.parts[0].text try: response = await tool_context.search_memory(user_query) except Exception: - logging.warning('Failed to preload memory for query: %s', user_query) + logger.warning('Failed to preload memory for query: %s', user_query) return if not response.memories: diff --git a/tests/unittests/tools/test_preload_memory_tool.py b/tests/unittests/tools/test_preload_memory_tool.py index 53173e0e7a..ded3228828 100644 --- a/tests/unittests/tools/test_preload_memory_tool.py +++ b/tests/unittests/tools/test_preload_memory_tool.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging from unittest import mock from google.adk.memory.base_memory_service import SearchMemoryResponse @@ -149,3 +150,52 @@ async def test_preload_memory_search_failure_is_noop(): ) assert request == original + + +@pytest.mark.asyncio +async def test_preload_memory_uses_text_from_every_part(): + """The search query is not limited to the first content part. + + A leading non-text part (e.g. an inline file or a provider-required + placeholder) must not blank out or replace the user's actual question. + """ + request = LlmRequest(contents=[types.UserContent('current query')]) + tool_context = mock.Mock() + tool_context.user_content = types.Content( + role='user', + parts=[ + types.Part(text=''), + types.Part.from_text(text='what tea do I like'), + ], + ) + tool_context.search_memory = mock.AsyncMock( + return_value=SearchMemoryResponse(memories=[]) + ) + + await PreloadMemoryTool().process_llm_request( + tool_context=tool_context, + llm_request=request, + ) + + tool_context.search_memory.assert_awaited_once_with('what tea do I like') + + +@pytest.mark.asyncio +async def test_preload_memory_logs_search_failure_on_own_logger(caplog): + """Retrieval failures must be observable via the module's own logger. + + Applications that configure logging by the `google_adk` namespace would + otherwise never see a memory backend outage, since a fail-open retrieval + error is indistinguishable from "no memories matched". + """ + request = LlmRequest(contents=[types.UserContent('current query')]) + tool_context = _tool_context() + tool_context.search_memory.side_effect = RuntimeError('unavailable') + + with caplog.at_level(logging.WARNING, logger='google_adk'): + await PreloadMemoryTool().process_llm_request( + tool_context=tool_context, + llm_request=request, + ) + + assert any(record.name.startswith('google_adk') for record in caplog.records)