From 119c85756acd34cf689a8dfb8652812f7efa8c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Hawrylak?= Date: Sat, 20 Sep 2025 11:25:13 +0200 Subject: [PATCH 1/3] Tool calling arguments JSON fix --- chatmock/utils.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/chatmock/utils.py b/chatmock/utils.py index 6c344be..cc9fd49 100644 --- a/chatmock/utils.py +++ b/chatmock/utils.py @@ -323,7 +323,16 @@ def _merge_from(src): if isinstance(eff_params, (dict, list)): args_str = json.dumps(eff_params) elif isinstance(eff_params, str): - args_str = json.dumps({"query": eff_params}) + # Try to parse as JSON first + try: + parsed = json.loads(eff_params) + if isinstance(parsed, (dict, list)): + args_str = json.dumps(parsed) # Use parsed object directly + else: + args_str = json.dumps({"query": eff_params}) # Primitive value, wrap in query + except (json.JSONDecodeError, ValueError): + # Not valid JSON, treat as plain string + args_str = json.dumps({"query": eff_params}) else: args_str = "{}" if call_id not in ws_index: @@ -405,7 +414,16 @@ def _merge_from(src): if isinstance(eff_args, (dict, list)): args = json.dumps(eff_args) elif isinstance(eff_args, str): - args = json.dumps({"query": eff_args}) + # Try to parse as JSON first + try: + parsed = json.loads(eff_args) + if isinstance(parsed, (dict, list)): + args = json.dumps(parsed) # Use parsed object directly + else: + args = json.dumps({"query": eff_args}) # Primitive value, wrap in query + except (json.JSONDecodeError, ValueError): + # Not valid JSON, treat as plain string + args = json.dumps({"query": eff_args}) else: args = "{}" except Exception: From dcc60a79dba2c7994d79d81328182bdeafab637e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Hawrylak?= Date: Sat, 20 Sep 2025 11:33:32 +0200 Subject: [PATCH 2/3] Extracted duplicated code to helper function --- chatmock/utils.py | 58 +++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/chatmock/utils.py b/chatmock/utils.py index cc9fd49..690653b 100644 --- a/chatmock/utils.py +++ b/chatmock/utils.py @@ -254,6 +254,32 @@ def sse_translate_chat( ws_index: dict[str, int] = {} ws_next_index: int = 0 + def _serialize_tool_args(eff_args: Any) -> str: + """ + Serialize tool call arguments with proper JSON handling. + + Args: + eff_args: Arguments to serialize (dict, list, str, or other) + + Returns: + JSON string representation of the arguments + """ + if isinstance(eff_args, (dict, list)): + return json.dumps(eff_args) + elif isinstance(eff_args, str): + # Try to parse as JSON first + try: + parsed = json.loads(eff_args) + if isinstance(parsed, (dict, list)): + return json.dumps(parsed) # Use parsed object directly + else: + return json.dumps({"query": eff_args}) # Primitive value, wrap in query + except (json.JSONDecodeError, ValueError): + # Not valid JSON, treat as plain string + return json.dumps({"query": eff_args}) + else: + return "{}" + def _extract_usage(evt: Dict[str, Any]) -> Dict[str, int] | None: try: usage = (evt.get("response") or {}).get("usage") @@ -320,21 +346,7 @@ def _merge_from(src): except Exception: pass eff_params = ws_state.get(call_id, params if isinstance(params, (dict, list, str)) else {}) - if isinstance(eff_params, (dict, list)): - args_str = json.dumps(eff_params) - elif isinstance(eff_params, str): - # Try to parse as JSON first - try: - parsed = json.loads(eff_params) - if isinstance(parsed, (dict, list)): - args_str = json.dumps(parsed) # Use parsed object directly - else: - args_str = json.dumps({"query": eff_params}) # Primitive value, wrap in query - except (json.JSONDecodeError, ValueError): - # Not valid JSON, treat as plain string - args_str = json.dumps({"query": eff_params}) - else: - args_str = "{}" + args_str = _serialize_tool_args(eff_params) if call_id not in ws_index: ws_index[call_id] = ws_next_index ws_next_index += 1 @@ -411,21 +423,7 @@ def _merge_from(src): pass eff_args = ws_state.get(call_id, raw_args if isinstance(raw_args, (dict, list, str)) else {}) try: - if isinstance(eff_args, (dict, list)): - args = json.dumps(eff_args) - elif isinstance(eff_args, str): - # Try to parse as JSON first - try: - parsed = json.loads(eff_args) - if isinstance(parsed, (dict, list)): - args = json.dumps(parsed) # Use parsed object directly - else: - args = json.dumps({"query": eff_args}) # Primitive value, wrap in query - except (json.JSONDecodeError, ValueError): - # Not valid JSON, treat as plain string - args = json.dumps({"query": eff_args}) - else: - args = "{}" + args = _serialize_tool_args(eff_args) except Exception: args = "{}" if item.get("type") == "web_search_call" and verbose and vlog: From 4f7b87fe47c3e5ffb6509c73682743d1c9cd4627 Mon Sep 17 00:00:00 2001 From: Game_Time <108236317+RayBytes@users.noreply.github.com> Date: Sat, 20 Sep 2025 18:06:04 +0500 Subject: [PATCH 3/3] Update utils.py --- chatmock/utils.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/chatmock/utils.py b/chatmock/utils.py index 690653b..a4ada3f 100644 --- a/chatmock/utils.py +++ b/chatmock/utils.py @@ -267,15 +267,13 @@ def _serialize_tool_args(eff_args: Any) -> str: if isinstance(eff_args, (dict, list)): return json.dumps(eff_args) elif isinstance(eff_args, str): - # Try to parse as JSON first try: parsed = json.loads(eff_args) if isinstance(parsed, (dict, list)): - return json.dumps(parsed) # Use parsed object directly + return json.dumps(parsed) else: - return json.dumps({"query": eff_args}) # Primitive value, wrap in query + return json.dumps({"query": eff_args}) except (json.JSONDecodeError, ValueError): - # Not valid JSON, treat as plain string return json.dumps({"query": eff_args}) else: return "{}"