mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(responses): preserve input_file in function_call_output during chat lowering
closes #28232
This commit is contained in:
parent
cff3e0b75e
commit
7ce0bf2c7b
2 changed files with 78 additions and 2 deletions
|
|
@ -1085,9 +1085,18 @@ class LiteLLMCompletionResponsesConfig:
|
|||
"image_url": {"url": image_url_val},
|
||||
}
|
||||
)
|
||||
elif part_type in ("input_file", "file"):
|
||||
normalized_blocks.append(
|
||||
LiteLLMCompletionResponsesConfig._transform_input_file_item_to_file_item(
|
||||
part
|
||||
)
|
||||
)
|
||||
|
||||
# Prefer structured blocks if we have images; otherwise return a string.
|
||||
if any(b.get("type") == "image_url" for b in normalized_blocks):
|
||||
# Prefer structured blocks if we have any non-text part
|
||||
# (images or files); otherwise return a string.
|
||||
if any(
|
||||
b.get("type") in ("image_url", "file") for b in normalized_blocks
|
||||
):
|
||||
# Ensure we include any accumulated text as text blocks too
|
||||
return normalized_blocks
|
||||
if text_acc:
|
||||
|
|
|
|||
|
|
@ -40,3 +40,70 @@ def test_function_call_output_string_passthrough():
|
|||
)
|
||||
assert len(out) == 1
|
||||
assert out[0]["content"] == '{"ok":true}'
|
||||
|
||||
|
||||
def test_function_call_output_input_file_is_preserved_as_file_block():
|
||||
"""A PDF returned from a tool call (input_file part inside
|
||||
function_call_output.output) must survive the lowering to a chat-completions
|
||||
tool message as a structured file content block. Previously the file part
|
||||
was silently dropped and only the input_text sibling reached the model."""
|
||||
pdf_data_url = "data:application/pdf;base64,JVBERi0xLjQKJfb=="
|
||||
|
||||
out = LiteLLMCompletionResponsesConfig._transform_responses_api_tool_call_output_to_chat_completion_message(
|
||||
tool_call_output={
|
||||
"type": "function_call_output",
|
||||
"call_id": "call_pdf",
|
||||
"output": [
|
||||
{"type": "input_text", "text": "Here is the PDF."},
|
||||
{
|
||||
"type": "input_file",
|
||||
"file_data": pdf_data_url,
|
||||
"filename": "test.pdf",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
assert len(out) == 1
|
||||
msg = out[0]
|
||||
assert msg["role"] == "tool"
|
||||
assert msg["tool_call_id"] == "call_pdf"
|
||||
|
||||
content = msg["content"]
|
||||
assert isinstance(content, list), (
|
||||
"expected structured content list when a file part is present, "
|
||||
f"got {type(content).__name__}: {content!r}"
|
||||
)
|
||||
|
||||
text_blocks = [b for b in content if b.get("type") == "text"]
|
||||
file_blocks = [b for b in content if b.get("type") == "file"]
|
||||
|
||||
assert any(b.get("text") == "Here is the PDF." for b in text_blocks)
|
||||
assert len(file_blocks) == 1
|
||||
assert file_blocks[0]["file"]["file_data"] == pdf_data_url
|
||||
|
||||
|
||||
def test_function_call_output_input_file_with_file_id_is_preserved():
|
||||
"""Same as above but for the file_id form (no inline bytes)."""
|
||||
out = LiteLLMCompletionResponsesConfig._transform_responses_api_tool_call_output_to_chat_completion_message(
|
||||
tool_call_output={
|
||||
"type": "function_call_output",
|
||||
"call_id": "call_pdf_id",
|
||||
"output": [
|
||||
{"type": "input_text", "text": "See attached."},
|
||||
{
|
||||
"type": "input_file",
|
||||
"file_id": "file-abc123",
|
||||
"filename": "report.pdf",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
assert len(out) == 1
|
||||
content = out[0]["content"]
|
||||
assert isinstance(content, list)
|
||||
|
||||
file_blocks = [b for b in content if b.get("type") == "file"]
|
||||
assert len(file_blocks) == 1
|
||||
assert file_blocks[0]["file"]["file_id"] == "file-abc123"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue