mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
fix(deepseek): keep tool result images instead of collapsing them to text
The DeepSeek transform only kept image content lists on user messages, so a role=tool message carrying an image_url block was collapsed to its text before the shared OpenAI transform ran. That transform already moves tool message images into a user message, but by then the image was gone Keep tool message image lists so the image reaches the model Fixes #44211
This commit is contained in:
parent
aef0a53837
commit
2c6577959a
2 changed files with 18 additions and 6 deletions
|
|
@ -121,10 +121,12 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
|
|||
DeepSeek vision models accept image_url content blocks in user
|
||||
messages (https://api-docs.deepseek.com/guides/vision), so those
|
||||
content lists are forwarded as-is, with any search_results text
|
||||
appended as a trailing text block. Every other message keeps the
|
||||
historical string collapse (which also folds search_results text
|
||||
into string content); a list with no extractable text stays
|
||||
unchanged, matching what DeepSeek historically received.
|
||||
appended as a trailing text block. Tool message image lists are kept
|
||||
too, so the parent transform can move their images into a user
|
||||
message. Every other message keeps the historical string collapse
|
||||
(which also folds search_results text into string content); a list
|
||||
with no extractable text stays unchanged, matching what DeepSeek
|
||||
historically received.
|
||||
"""
|
||||
forward_images: Final = any(
|
||||
isinstance(message.get("content"), list) for message in messages
|
||||
|
|
@ -160,13 +162,13 @@ class DeepSeekChatConfig(OpenAIGPTConfig):
|
|||
|
||||
def _is_vision_forwardable_content(self, message: AllMessageValues, content: Sequence[object]) -> bool:
|
||||
"""
|
||||
True only for a user message whose content list holds well-formed
|
||||
True only for a user or tool message whose content list holds well-formed
|
||||
text and image_url blocks with at least one image; a block missing
|
||||
its payload falls back to the string collapse instead of crashing
|
||||
or reaching the wire malformed. The model capability gate lives in
|
||||
the caller.
|
||||
"""
|
||||
if message.get("role") != "user":
|
||||
if message.get("role") not in ("user", "tool"):
|
||||
return False
|
||||
if not all(self._is_forwardable_block(block) for block in content):
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -169,6 +169,16 @@ class TestDeepSeekVisionMultimodalContent:
|
|||
|
||||
assert result[0]["content"] == "what is in this image?"
|
||||
|
||||
def test_tool_result_image_reaches_vision_model(self):
|
||||
tool_message = {**self._image_message(role="tool"), "tool_call_id": "call_1"}
|
||||
image_block = tool_message["content"][1]
|
||||
|
||||
result = self.config._transform_messages([tool_message], model=self.VISION_MODEL)
|
||||
|
||||
assert result[0]["role"] == "tool"
|
||||
assert result[0]["tool_call_id"] == "call_1"
|
||||
assert any(isinstance(message["content"], list) and image_block in message["content"] for message in result)
|
||||
|
||||
def test_audio_block_collapsed_even_on_vision_model(self):
|
||||
messages = [
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue