Skip to content

fix: map Chat Completion file type to Responses API input_file#23618

Merged
Chesars merged 4 commits intoBerriAI:litellm_oss_staging_03_17_2026from
gambletan:fix/file-to-input-file-mapping
Mar 18, 2026
Merged

fix: map Chat Completion file type to Responses API input_file#23618
Chesars merged 4 commits intoBerriAI:litellm_oss_staging_03_17_2026from
gambletan:fix/file-to-input-file-mapping

Conversation

@gambletan
Copy link
Copy Markdown
Contributor

Summary

Fixes #23588

When bridging /chat/completions to the Responses API, content items with type: "file" were falling through to the default handler in _convert_content_to_responses_format and being stringified as input_text. This caused the model to receive the Python dict representation as plain text instead of the actual file bytes.

Root cause: In litellm/completion_extras/litellm_responses_transformation/transformation.py, the _convert_content_to_responses_format method handles "text", "image_url", and "image" content types explicitly, but "file" was not handled — it fell through to the else branch which calls str(item) and wraps it as input_text.

Fix: Add an explicit elif item_type == "file" branch that maps:

{"type": "file", "file": {"file_data": "data:application/pdf;base64,...", "filename": "test.pdf"}}

to:

{"type": "input_file", "file_data": "data:application/pdf;base64,...", "filename": "test.pdf"}

This is the inverse of the existing _transform_input_file_item_to_file_item in the responses-to-completion direction, which already correctly maps input_filefile.

Test plan

  • Added test_convert_chat_completion_file_type_to_input_file — verifies file_data and filename are correctly extracted
  • Added test_convert_chat_completion_file_type_with_file_id — verifies file_id variant works

🤖 Generated with Claude Code

When bridging /chat/completions to the Responses API, content items with
type 'file' were falling through to the default handler and being
stringified as input_text. This caused the model to receive the Python
dict representation as plain text instead of the actual file content.

Add explicit handling for type 'file' that correctly maps:
  {"type": "file", "file": {"file_data": "...", "filename": "..."}}
to:
  {"type": "input_file", "file_data": "...", "filename": "..."}

Fixes BerriAI#23588
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Mar 14, 2026 1:20pm

Request Review

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 14, 2026

Greptile Summary

This PR fixes a bug where Chat Completion messages containing {"type": "file", ...} content items were incorrectly stringified as input_text when bridging to the Responses API, instead of being properly converted to input_file objects. The fix adds an explicit elif item_type == "file" branch in _convert_content_to_responses_format that correctly flattens the nested file dict into a flat input_file structure expected by the Responses API.

Key changes:

  • Added elif item_type == "file" handling in LiteLLMResponsesTransformationHandler._convert_content_to_responses_format (lines 699–712) that maps file_id, file_data, and filename from the nested file dict into a flat input_file object.
  • Two new mock-only unit tests cover the file_data+filename and file_id variants of the file content item.
  • Remaining changes are cosmetic reformatting (line-wrapping dict assignments and a blank line addition).

Notable pre-existing gaps (flagged in earlier review threads, not introduced by this PR):

  • filename is still dropped on the reverse path (_transform_input_file_item_to_file_item in litellm/responses/litellm_completion_transformation/transformation.py) because its keys list only includes ["file_id", "file_data"].
  • result.append(converted) is unconditional, so a malformed {"type": "file", "file": None} input silently produces an empty {"type": "input_file"} item.

Confidence Score: 4/5

  • Safe to merge; the fix is correct, well-scoped, and tested with mock-only unit tests.
  • The core bug fix is straightforward and correct — adding the missing elif item_type == "file" branch properly flattens nested file content. The two pre-existing edge cases (silent empty input_file when file=None, and filename loss on the round-trip) were flagged in earlier review threads and are not regressions introduced by this PR. No new logic paths are touched beyond the narrow fix.
  • No files require special attention; the only file with a meaningful logic change is litellm/completion_extras/litellm_responses_transformation/transformation.py, and the change is small and well-tested.

Important Files Changed

Filename Overview
litellm/completion_extras/litellm_responses_transformation/transformation.py Adds explicit elif item_type == "file" branch in _convert_content_to_responses_format to map Chat Completion file items to Responses API input_file format, correctly flattening the nested file dict. Also contains cosmetic formatting-only changes to other parts of the file.
tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py Adds two mock-only unit tests verifying the new fileinput_file conversion: one for the file_data+filename variant and one for the file_id variant. No real network calls; tests are correctly placed in the mock-only test folder.

Sequence Diagram

sequenceDiagram
    participant Caller as Caller (/chat/completions)
    participant FwdTransform as LiteLLMResponsesTransformationHandler<br/>_convert_content_to_responses_format
    participant ResponsesAPI as Responses API
    participant RevTransform as LiteLLMCompletionResponsesConfig<br/>_transform_input_file_item_to_file_item

    Caller->>FwdTransform: {type: "file", file: {file_data: "...", filename: "test.pdf"}}
    Note over FwdTransform: NEW: elif item_type == "file"<br/>Extract file_id/file_data/filename from nested file dict
    FwdTransform->>ResponsesAPI: {type: "input_file", file_data: "...", filename: "test.pdf"}

    ResponsesAPI-->>RevTransform: {type: "input_file", file_data: "...", filename: "test.pdf"}
    Note over RevTransform: keys = ["file_id", "file_data"]<br/>⚠ "filename" is NOT in keys list (pre-existing gap)
    RevTransform-->>Caller: {type: "file", file: {file_data: "..."}}
    Note over Caller: ⚠ filename is silently dropped on round-trip
Loading

Last reviewed commit: 98890e7

@Chesars Chesars changed the base branch from main to litellm_oss_staging_03_17_2026 March 18, 2026 03:26
@Chesars Chesars merged commit 501ddb4 into BerriAI:litellm_oss_staging_03_17_2026 Mar 18, 2026
33 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Chat completions type: "file" incorrectly translated to Responses API as input_text instead of input_file

3 participants