diff --git a/src/google/adk/models/lite_llm.py b/src/google/adk/models/lite_llm.py index 7d13696c96..e3803b8da9 100644 --- a/src/google/adk/models/lite_llm.py +++ b/src/google/adk/models/lite_llm.py @@ -811,6 +811,7 @@ async def _content_to_message_param( follow_up = await _content_to_message_param( types.Content(role=content.role, parts=non_tool_parts), provider=provider, + model=model, ) follow_up_messages = ( follow_up if isinstance(follow_up, list) else [follow_up] @@ -1091,7 +1092,7 @@ async def _get_content( if not mime_type: # LiteLLM's Vertex AI backend requires format for GCS URIs. mime_type = _DEFAULT_MIME_TYPE - logger.debug( + logger.warning( "Could not determine MIME type for file_uri %s, using default: %s", part.file_data.file_uri, mime_type, diff --git a/tests/unittests/models/test_litellm.py b/tests/unittests/models/test_litellm.py index ace08ad997..e92c5d1da3 100644 --- a/tests/unittests/models/test_litellm.py +++ b/tests/unittests/models/test_litellm.py @@ -4894,3 +4894,56 @@ async def test_content_to_message_param_anthropic_no_signature_falls_back(): # Falls back to reasoning_content when no signatures present assert result.get("reasoning_content") == "thinking without sig" assert "thinking_blocks" not in result + + +@pytest.mark.asyncio +async def test_content_to_message_param_file_uri_mime_fallback_logs_warning( + caplog, +) -> None: + """Test that falling back to application/octet-stream logs a warning.""" + file_part = types.Part( + file_data=types.FileData( + file_uri="gs://bucket/artifact/0" + ) + ) + content = types.Content( + role="user", + parts=[file_part], + ) + + with caplog.at_level(logging.WARNING, logger="google.adk.models.lite_llm"): + await _content_to_message_param(content) + + assert any( + "Could not determine MIME type" in record.message + for record in caplog.records + ), "Expected a warning about MIME type fallback" + + +@pytest.mark.asyncio +async def test_content_to_message_param_function_response_with_extra_parts_propagates_model() -> None: + """Test that model parameter is propagated in recursive calls for mixed parts.""" + tool_part = types.Part.from_function_response( + name="load_image", + response={"status": "success"}, + ) + tool_part.function_response.id = "tool_call_1" + + text_part = types.Part.from_text(text="Here is the result") + + content = types.Content( + role="user", + parts=[tool_part, text_part], + ) + + # Call with model parameter — should not raise and should produce valid output + messages = await _content_to_message_param( + content, provider="anthropic", model="anthropic/claude-4-sonnet" + ) + assert isinstance(messages, list) + assert len(messages) == 2 + # First message is tool response + assert messages[0]["role"] == "tool" + assert messages[0]["tool_call_id"] == "tool_call_1" + # Second message is the text follow-up + assert messages[1]["role"] == "user"