fix: patch parse_tool_call bug that returns string args instead of dict

Root cause identified: langchain_core's parse_tool_call() sometimes returns
tool_calls with 'args' as a JSON string instead of parsed dict object.

This violates AIMessage's Pydantic schema which expects args to be dict.

Solution: Wrapper now detects when parse_tool_call returns string args
and immediately converts them to dict using json.loads().

This is a workaround for what appears to be a LangChain bug where
parse_tool_call's json.loads() call either:
1. Fails silently without raising exception, or
2. Succeeds but result is not being assigned to args field

The fix ensures AIMessage always receives properly parsed dict args,
resolving Pydantic validation errors for all DeepSeek tool calls.
This commit is contained in:
2025-11-06 17:58:41 -05:00
parent b73d88ca8f
commit 5c73f30583

View File

@@ -37,13 +37,21 @@ class ToolCallArgsParsingWrapper:
original_parse_tool_call = langchain_base.parse_tool_call original_parse_tool_call = langchain_base.parse_tool_call
def patched_parse_tool_call(raw_tool_call, *, partial=False, strict=False, return_id=True): def patched_parse_tool_call(raw_tool_call, *, partial=False, strict=False, return_id=True):
"""Patched parse_tool_call to log what it returns""" """Patched parse_tool_call to fix string args bug and add logging"""
result = original_parse_tool_call(raw_tool_call, partial=partial, strict=strict, return_id=return_id) result = original_parse_tool_call(raw_tool_call, partial=partial, strict=strict, return_id=return_id)
if result: if result:
args_type = type(result.get('args', None)).__name__ args_type = type(result.get('args', None)).__name__
print(f"[DIAGNOSTIC] parse_tool_call returned: args type = {args_type}") print(f"[DIAGNOSTIC] parse_tool_call returned: args type = {args_type}")
if args_type == 'str': if args_type == 'str':
print(f"[DIAGNOSTIC] ⚠️ BUG FOUND! parse_tool_call returned STRING args: {result['args']}") print(f"[DIAGNOSTIC] ⚠️ BUG FOUND! parse_tool_call returned STRING args, fixing...")
# FIX: parse_tool_call sometimes returns string args instead of dict
# This happens when it fails to parse but doesn't raise an exception
try:
result['args'] = json.loads(result['args'])
print(f"[DIAGNOSTIC] ✓ Fixed! Converted string args to dict")
except (json.JSONDecodeError, TypeError) as e:
print(f"[DIAGNOSTIC] ❌ Failed to parse args: {e}")
# Leave as string if we can't parse it
return result return result
# Replace in base.py's namespace (where _convert_dict_to_message uses it) # Replace in base.py's namespace (where _convert_dict_to_message uses it)