Skip to content

[Bug] LiteLlm uploads PDFs to Azure without filename or MIME metadata #6539

Description

@ross-p

🔴 Required Information

Describe the Bug:

google.adk.models.lite_llm.LiteLlm uploads inline PDF attachments to Azure
using raw bytes without a filename or content type.

This occurs with a direct Azure model identifier:

azure/<deployment>

ADK correctly recognizes Azure and enters its OpenAI/Azure file-upload path.
However, it calls litellm.acreate_file() like this:

await litellm.acreate_file(
    file=part.inline_data.data,
    purpose="assistants",
    custom_llm_provider="azure",
)

Because file is only a bytes object, the upload carries neither a filename
such as document.pdf nor the application/pdf content type. Azure stores or
interprets the uploaded file as having no recognized file type and rejects it
when the file is used as model input.

Steps to Reproduce:

  1. Install the dependencies:

    pip install google-adk litellm google-genai
  2. Configure credentials and endpoint information for an Azure OpenAI
    deployment that supports PDF input.

  3. Construct an ADK model using a direct Azure identifier:

    from google.adk.models.lite_llm import LiteLlm
    
    model = LiteLlm(model="azure/<deployment>")
  4. Send a user message containing an inline PDF:

    from google.genai import types
    
    message = types.Content(
        role="user",
        parts=[
            types.Part(text="Describe this PDF"),
            types.Part(
                inline_data=types.Blob(
                    mime_type="application/pdf",
                    data=pdf_bytes,
                )
            ),
        ],
    )
  5. Observe that Azure rejects the request before inference:

    Invalid input: Expected context stuffing file type to be a supported
    format: ... .pdf ... but got none.
    

A network-free reproduction of the upload arguments is included below.

Expected Behavior:

When uploading an inline_data part whose MIME type is application/pdf, ADK
should preserve sufficient file metadata for Azure to recognize the uploaded
file as a PDF.

The file upload should include both:

  • a filename with a supported extension, such as .pdf; and
  • the application/pdf content type.

The uploaded file should then be usable as model input.

Observed Behavior:

ADK passes only the raw PDF bytes to litellm.acreate_file().

Azure later rejects the uploaded file with:

Invalid input: Expected context stuffing file type to be a supported format:
... .pdf ... but got none.

The error response identifies input as the invalid parameter:

{
  "error": {
    "message": "Invalid input: Expected context stuffing file type to be a supported format: ... .pdf ... but got none.",
    "type": "invalid_request_error",
    "param": "input",
    "code": null
  }
}

The request is rejected before any model inference occurs.

Environment Details:

  • ADK Library Version: google-adk 2.6.0
  • Desktop OS: macOS
  • Python Version: 3.11
  • LiteLLM Version: 1.94.1
  • google-genai Version: 2.16.0

Model Information:

  • Are you using LiteLLM: Yes
  • Which model is being used: an Azure OpenAI deployment through the direct
    azure/<deployment> route

🟡 Optional Information

Regression:

Not known to have worked previously.

This appears to be an incomplete resolution of #4174 rather than a new
regression. The behavior remains present in ADK 2.6.0 and current main.

Logs:

BadRequestError: litellm.BadRequestError: AzureException BadRequestError - {
  "error": {
    "message": "Invalid input: Expected context stuffing file type to be a supported format: ... .pdf ... but got none.",
    "type": "invalid_request_error",
    "param": "input",
    "code": null
  }
}

Additional Context:

Issue #4174 reported that PDF attachments sent through LiteLlm to OpenAI/Azure
were seen with a missing file type.

It was closed by commit
065234e27973d4af0084cbac1db3d21561ba6e66, which added the MIME type to the
file reference created after upload:

{
    "type": "file",
    "file": {
        "file_id": file_response.id,
        "format": mime_type,
    },
}

However, that change did not add metadata to the file upload itself. Current
code still performs the upload with:

file_response = await litellm.acreate_file(
    file=part.inline_data.data,
    purpose="assistants",
    custom_llm_provider=provider,
)

Adding "format": "application/pdf" to the later file reference cannot restore
the filename or MIME metadata that was absent when the file was uploaded.

A previous proposed fix in #4603 passed a (filename, bytes, mime_type) tuple
to acreate_file(), but that PR was closed without merging.

Minimal Reproduction Code:

The following reproduction makes no provider request. It intercepts
acreate_file() and shows the value ADK supplies for file:

import asyncio

from google.adk.models import lite_llm as adk_lite_llm
from google.genai import types


async def main():
    adk_lite_llm._ensure_litellm_imported()

    captured = {}
    original_acreate_file = adk_lite_llm.litellm.acreate_file

    async def capture_acreate_file(**kwargs):
        captured.update(kwargs)

        class FileResponse:
            id = "assistant-demo"

        return FileResponse()

    adk_lite_llm.litellm.acreate_file = capture_acreate_file

    try:
        content = await adk_lite_llm._get_content(
            [
                types.Part(text="Describe this PDF"),
                types.Part(
                    inline_data=types.Blob(
                        mime_type="application/pdf",
                        data=b"%PDF-1.4\n",
                    )
                ),
            ],
            provider="azure",
            model="azure/example-deployment",
        )
    finally:
        adk_lite_llm.litellm.acreate_file = original_acreate_file

    print("file type:", type(captured["file"]).__name__)
    print("file value:", repr(captured["file"]))
    print("purpose:", captured["purpose"])
    print("provider:", captured["custom_llm_provider"])
    print("content:", content)


asyncio.run(main())

Observed output:

file type: bytes
file value: b'%PDF-1.4\n'
purpose: assistants
provider: azure
content: [
  {'type': 'text', 'text': 'Describe this PDF'},
  {
    'type': 'file',
    'file': {
      'file_id': 'assistant-demo',
      'format': 'application/pdf'
    }
  }
]

This demonstrates that the subsequent content block carries the MIME type, but
the upload itself receives only anonymous raw bytes.

How often has this issue occurred?:

  • Always (100%)

Relationship to #6538

This issue is related to #6538 because both affect PDF input through LiteLlm, but they are not duplicates:

Fixing #6538 may cause that proxy route to reach the upload path described here, where it could still fail until this separate metadata defect is resolved. Fixing this issue alone would not correct the nested-provider detection in #6538.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions