@@ -1882,15 +1882,7 @@ async def test_content_to_message_param_user_message_file_uri_only(
18821882
18831883@pytest .mark .asyncio
18841884async def test_content_to_message_param_user_message_file_uri_without_mime_type ():
1885- """Test handling of file_data without mime_type (GcsArtifactService scenario).
1886-
1887- When using GcsArtifactService, artifacts may have file_uri (gs://...) but
1888- without mime_type set. LiteLLM's Vertex AI backend requires the format
1889- field to be present, so we infer MIME type from the URI extension or use
1890- a default fallback to ensure compatibility.
1891-
1892- See: https://github.com/google/adk-python/issues/3787
1893- """
1885+ """Test file_data without mime_type fails fast when inference is impossible."""
18941886 file_part = types .Part (
18951887 file_data = types .FileData (
18961888 file_uri = "gs://agent-artifact-bucket/app/user/session/artifact/0"
@@ -1904,22 +1896,11 @@ async def test_content_to_message_param_user_message_file_uri_without_mime_type(
19041896 ],
19051897 )
19061898
1907- message = await _content_to_message_param (content )
1908- assert message == {
1909- "role" : "user" ,
1910- "content" : [
1911- {"type" : "text" , "text" : "Analyze this file." },
1912- {
1913- "type" : "file" ,
1914- "file" : {
1915- "file_id" : (
1916- "gs://agent-artifact-bucket/app/user/session/artifact/0"
1917- ),
1918- "format" : "application/octet-stream" ,
1919- },
1920- },
1921- ],
1922- }
1899+ with pytest .raises (
1900+ ValueError ,
1901+ match = "Cannot determine MIME type for file_uri" ,
1902+ ):
1903+ await _content_to_message_param (content )
19231904
19241905
19251906@pytest .mark .asyncio
@@ -2027,6 +2008,50 @@ async def test_content_to_message_param_function_response_with_extra_parts():
20272008 ]
20282009
20292010
2011+ @pytest .mark .asyncio
2012+ async def test_content_to_message_param_function_response_preserves_model_for_file_uri ():
2013+ tool_part = types .Part (
2014+ function_response = types .FunctionResponse (
2015+ name = "load_document" ,
2016+ response = {"status" : "success" },
2017+ )
2018+ )
2019+ tool_part .function_response .id = "tool_call_1"
2020+
2021+ file_part = types .Part (
2022+ file_data = types .FileData (
2023+ file_uri = "gs://bucket/path/to/document.pdf" ,
2024+ mime_type = "application/pdf" ,
2025+ )
2026+ )
2027+ content = types .Content (role = "user" , parts = [tool_part , file_part ])
2028+
2029+ messages = await _content_to_message_param (
2030+ content ,
2031+ provider = "vertex_ai" ,
2032+ model = "vertex_ai/gemini-2.5-flash" ,
2033+ )
2034+
2035+ assert isinstance (messages , list )
2036+ assert messages == [
2037+ {
2038+ "role" : "tool" ,
2039+ "tool_call_id" : "tool_call_1" ,
2040+ "content" : '{"status": "success"}' ,
2041+ },
2042+ {
2043+ "role" : "user" ,
2044+ "content" : [{
2045+ "type" : "file" ,
2046+ "file" : {
2047+ "file_id" : "gs://bucket/path/to/document.pdf" ,
2048+ "format" : "application/pdf" ,
2049+ },
2050+ }],
2051+ },
2052+ ]
2053+
2054+
20302055@pytest .mark .asyncio
20312056async def test_content_to_message_param_function_response_preserves_string ():
20322057 """Tests that string responses are used directly without double-serialization.
@@ -2999,6 +3024,26 @@ async def test_get_content_file_uri_anthropic_openai_file_id_falls_back_to_text(
29993024 ]
30003025
30013026
3027+ @pytest .mark .asyncio
3028+ @pytest .mark .parametrize (
3029+ "provider,model" ,
3030+ [
3031+ ("openai" , "openai/gpt-4o" ),
3032+ ("azure" , "azure/gpt-4" ),
3033+ ],
3034+ )
3035+ async def test_get_content_file_uri_file_id_required_missing_mime_falls_back_to_text (
3036+ provider , model
3037+ ):
3038+ parts = [
3039+ types .Part (file_data = types .FileData (file_uri = "gs://bucket/artifact/0" ))
3040+ ]
3041+ content = await _get_content (parts , provider = provider , model = model )
3042+ assert content == [
3043+ {"type" : "text" , "text" : '[File reference: "gs://bucket/artifact/0"]' }
3044+ ]
3045+
3046+
30023047@pytest .mark .asyncio
30033048async def test_get_content_file_uri_vertex_ai_non_gemini_falls_back_to_text ():
30043049 parts = [
@@ -3097,27 +3142,17 @@ async def test_get_content_file_uri_infers_from_display_name():
30973142
30983143@pytest .mark .asyncio
30993144async def test_get_content_file_uri_default_mime_type ():
3100- """Test that file_uri without extension uses default MIME type.
3101-
3102- When file_data has a file_uri without a recognizable extension and no explicit
3103- mime_type, a default MIME type should be used to ensure compatibility with
3104- LiteLLM backends.
3105-
3106- See: https://github.com/google/adk-python/issues/3787
3107- """
3145+ """Test that file_uri without extension raises a helpful ValueError."""
31083146 # Use Part constructor directly to create file_data without mime_type
31093147 # (types.Part.from_uri requires a valid mime_type when it can't infer)
31103148 parts = [
31113149 types .Part (file_data = types .FileData (file_uri = "gs://bucket/artifact/0" ))
31123150 ]
3113- content = await _get_content (parts )
3114- assert content [0 ] == {
3115- "type" : "file" ,
3116- "file" : {
3117- "file_id" : "gs://bucket/artifact/0" ,
3118- "format" : "application/octet-stream" ,
3119- },
3120- }
3151+ with pytest .raises (
3152+ ValueError ,
3153+ match = "Cannot determine MIME type for file_uri" ,
3154+ ):
3155+ await _get_content (parts )
31213156
31223157
31233158@pytest .mark .asyncio
0 commit comments