From 5c73f30583ca24aad0e5533b0ecfc77982dc4779 Mon Sep 17 00:00:00 2001 From: Bill Date: Thu, 6 Nov 2025 17:58:41 -0500 Subject: [PATCH] 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. --- agent/chat_model_wrapper.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/agent/chat_model_wrapper.py b/agent/chat_model_wrapper.py index 45d3808..2acdb6a 100644 --- a/agent/chat_model_wrapper.py +++ b/agent/chat_model_wrapper.py @@ -37,13 +37,21 @@ class ToolCallArgsParsingWrapper: original_parse_tool_call = langchain_base.parse_tool_call 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) if result: args_type = type(result.get('args', None)).__name__ print(f"[DIAGNOSTIC] parse_tool_call returned: args type = {args_type}") 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 # Replace in base.py's namespace (where _convert_dict_to_message uses it)