Skip to content

Commit b1c7de9

Browse files
nikk0o046mdrxy
andauthored
fix(deepseek): convert tool output arrays to strings (#31913)
## Description When ChatDeepSeek invokes a tool that returns a list, it results in an openai.UnprocessableEntityError due to a failure in deserializing the JSON body. The root of the problem is that ChatDeepSeek uses BaseChatOpenAI internally, but the APIs are not identical: OpenAI v1/chat/completions accepts arrays as tool results, but Deepseek API does not. As a solution added `_get_request_payload` method to ChatDeepSeek, which inherits the behavior from BaseChatOpenAI but adds a step to stringify tool message content in case the content is an array. I also add a unit test for this. From the linked issue you can find the full reproducible example the reporter of the issue provided. After the changes it works as expected. Source: [Deepseek docs](https://api-docs.deepseek.com/api/create-chat-completion/) ![image](https://github.com/user-attachments/assets/a59ed3e7-6444-46d1-9dcf-97e40e4e8952) Source: [OpenAI docs](https://platform.openai.com/docs/api-reference/chat/create) ![image](https://github.com/user-attachments/assets/728f4fc6-e1a3-4897-b39f-6f1ade07d3dc) ## Issue Fixes #31394 ## Dependencies: No new dependencies. ## Twitter handle: Don't have one. --------- Co-authored-by: Mason Daugherty <[email protected]> Co-authored-by: Mason Daugherty <[email protected]>
1 parent 96bf826 commit b1c7de9

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

libs/partners/deepseek/langchain_deepseek/chat_models.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import json
56
from collections.abc import Iterator
67
from json import JSONDecodeError
78
from typing import Any, Literal, Optional, TypeVar, Union
@@ -218,6 +219,19 @@ def validate_environment(self) -> Self:
218219
self.async_client = self.root_async_client.chat.completions
219220
return self
220221

222+
def _get_request_payload(
223+
self,
224+
input_: LanguageModelInput,
225+
*,
226+
stop: Optional[list[str]] = None,
227+
**kwargs: Any,
228+
) -> dict:
229+
payload = super()._get_request_payload(input_, stop=stop, **kwargs)
230+
for message in payload["messages"]:
231+
if message["role"] == "tool" and isinstance(message["content"], list):
232+
message["content"] = json.dumps(message["content"])
233+
return payload
234+
221235
def _create_chat_result(
222236
self,
223237
response: Union[dict, openai.BaseModel],

libs/partners/deepseek/tests/unit_tests/test_chat_models.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import Any, Literal, Union
66
from unittest.mock import MagicMock
77

8-
from langchain_core.messages import AIMessageChunk
8+
from langchain_core.messages import AIMessageChunk, ToolMessage
99
from langchain_tests.unit_tests import ChatModelUnitTests
1010
from openai import BaseModel
1111
from openai.types.chat import ChatCompletionMessage
@@ -217,3 +217,19 @@ def test_convert_chunk_with_empty_delta(self) -> None:
217217
msg = "Expected chunk_result not to be None"
218218
raise AssertionError(msg)
219219
assert chunk_result.message.additional_kwargs.get("reasoning_content") is None
220+
221+
def test_get_request_payload(self) -> None:
222+
"""Test that tool message content is converted from list to string."""
223+
chat_model = ChatDeepSeek(model="deepseek-chat", api_key=SecretStr("api_key"))
224+
225+
tool_message = ToolMessage(content=[], tool_call_id="test_id")
226+
payload = chat_model._get_request_payload([tool_message])
227+
assert payload["messages"][0]["content"] == "[]"
228+
229+
tool_message = ToolMessage(content=["item1", "item2"], tool_call_id="test_id")
230+
payload = chat_model._get_request_payload([tool_message])
231+
assert payload["messages"][0]["content"] == '["item1", "item2"]'
232+
233+
tool_message = ToolMessage(content="test string", tool_call_id="test_id")
234+
payload = chat_model._get_request_payload([tool_message])
235+
assert payload["messages"][0]["content"] == "test string"

0 commit comments

Comments
 (0)