Heuristic:HKUDS AI Trader DeepSeek Tool Args Workaround
| Knowledge Sources | |
|---|---|
| Domains | LLM_Agents, Debugging |
| Last Updated | 2026-02-09 14:00 GMT |
Overview
DeepSeek API compatibility workaround: use a custom DeepSeekChatOpenAI wrapper class to handle tool_calls.args being returned as JSON strings instead of parsed dicts.
Description
When using DeepSeek models through the OpenAI-compatible API, the tool_calls response format differs from standard OpenAI behavior. DeepSeek returns tool_calls.args as raw JSON strings rather than pre-parsed Python dicts. The AI-Trader codebase addresses this by subclassing ChatOpenAI into DeepSeekChatOpenAI, which overrides the message parsing methods to handle this format difference transparently.
Usage
This workaround is automatically activated when the model name contains "deepseek" (case-insensitive check). No manual configuration needed. Be aware of this pattern when adding support for new LLM providers that may have similar API format differences.
The Insight (Rule of Thumb)
- Action: When
"deepseek" in self.basemodel.lower(), useDeepSeekChatOpenAIinstead ofChatOpenAI. - Value: Transparent compatibility; the rest of the agent code works identically.
- Trade-off: Adds a provider-specific code path. If DeepSeek fixes their API format, this class becomes dead code.
Reasoning
Different LLM providers implement the OpenAI-compatible API with subtle differences. DeepSeek's tool_calls format returns arguments as serialized JSON strings, while LangChain's ChatOpenAI expects parsed dicts. Rather than post-processing every response, subclassing the model class encapsulates the fix at the appropriate abstraction layer.
Code Evidence
DeepSeek wrapper class from agent/base_agent/base_agent.py:39-48:
class DeepSeekChatOpenAI(ChatOpenAI):
"""
Custom ChatOpenAI wrapper for DeepSeek API compatibility.
Handles the case where DeepSeek returns tool_calls.args as JSON strings instead of dicts.
"""
def _create_message_dicts(self, messages: list, stop: Optional[list] = None) -> list:
"""Override to handle response parsing"""
message_dicts = super()._create_message_dicts(messages, stop)
return message_dicts
Conditional model selection from agent/base_agent/base_agent.py:382-397:
# Create AI model - use custom DeepSeekChatOpenAI for DeepSeek models
# to handle tool_calls.args format differences (JSON string vs dict)
if "deepseek" in self.basemodel.lower():
self.model = DeepSeekChatOpenAI(
model=self.basemodel,
base_url=self.openai_base_url,
api_key=self.openai_api_key,
max_retries=3,
timeout=30,
)
else:
self.model = ChatOpenAI(
model=self.basemodel,
base_url=self.openai_base_url,
api_key=self.openai_api_key,
max_retries=3,
timeout=30,
)