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)