Skip to content

Revert "studio: tool calling for Llama-3, Mistral, Gemma 4 on safetensors + MLX (#5615)"#5619

Merged
danielhanchen merged 1 commit into
mainfrom
revert-5615-studio-tools-multi-format
May 19, 2026
Merged

Revert "studio: tool calling for Llama-3, Mistral, Gemma 4 on safetensors + MLX (#5615)"#5619
danielhanchen merged 1 commit into
mainfrom
revert-5615-studio-tools-multi-format

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Reverts #5615. Holding the multi-format parser back until we validate the full safetensors / MLX tool-call healing parity package end-to-end with real models.

@danielhanchen danielhanchen merged commit 735d26b into main May 19, 2026
22 of 31 checks passed
@danielhanchen danielhanchen deleted the revert-5615-studio-tools-multi-format branch May 19, 2026 14:26

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request simplifies the tool-call parser by removing support for several model-specific formats, including Llama-3, Mistral, and Gemma 4, to focus on a unified XML-based approach. Review feedback highlights two regressions: the regex for function and parameter names is now too restrictive, and the JSON parsing logic no longer handles non-dictionary arguments or validates for empty tool names as robustly as the previous implementation.

Comment on lines +63 to +66
_TC_FUNC_START_RE = re.compile(r"<function=(\w+)>\s*")
_TC_END_TAG_RE = re.compile(r"</tool_call>")
_TC_FUNC_CLOSE_RE = re.compile(r"\s*</function>\s*$")
_TC_PARAM_START_RE = re.compile(r"<parameter=([\w\.\-]+)>\s*")
_TC_PARAM_START_RE = re.compile(r"<parameter=(\w+)>\s*")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The regular expressions for parsing function and parameter names have been restricted from [\w\.\-]+ to \w+. This is a regression, as it no longer supports names containing dots or hyphens, which was possible before.

Suggested change
_TC_FUNC_START_RE = re.compile(r"<function=(\w+)>\s*")
_TC_END_TAG_RE = re.compile(r"</tool_call>")
_TC_FUNC_CLOSE_RE = re.compile(r"\s*</function>\s*$")
_TC_PARAM_START_RE = re.compile(r"<parameter=([\w\.\-]+)>\s*")
_TC_PARAM_START_RE = re.compile(r"<parameter=(\w+)>\s*")
_TC_FUNC_START_RE = re.compile(r"<function=([\w\.\-]+)>\s*")
_TC_END_TAG_RE = re.compile(r"</tool_call>")
_TC_FUNC_CLOSE_RE = re.compile(r"\s*</function>\s*$")
_TC_PARAM_START_RE = re.compile(r"<parameter=([\w\.\-]+)>\s*")

Comment on lines +128 to +141
obj = json.loads(json_str)
tc = {
"id": f"call_{id_offset + len(tool_calls)}",
"type": "function",
"function": {
"name": obj.get("name", ""),
"arguments": obj.get("arguments", {}),
},
}
if isinstance(tc["function"]["arguments"], dict):
tc["function"]["arguments"] = json.dumps(
tc["function"]["arguments"]
)
tool_calls.append(tc)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This implementation for parsing JSON tool calls has a couple of regressions compared to the original _parse_tool_call_json function that was removed:

  1. Incorrect handling of arguments: It no longer correctly handles arguments that are not a dictionary or a string. The previous logic would wrap other types (like lists or numbers) in a {"value": ...} object and serialize it to a JSON string. The current code passes them through as-is, violating the function's contract that arguments is always a JSON string.
  2. Missing name validation: It doesn't check if the tool name is empty. The previous implementation would correctly skip tool calls with no name.

Here's a suggestion to restore the more robust, original behavior.

Suggested change
obj = json.loads(json_str)
tc = {
"id": f"call_{id_offset + len(tool_calls)}",
"type": "function",
"function": {
"name": obj.get("name", ""),
"arguments": obj.get("arguments", {}),
},
}
if isinstance(tc["function"]["arguments"], dict):
tc["function"]["arguments"] = json.dumps(
tc["function"]["arguments"]
)
tool_calls.append(tc)
obj = json.loads(json_str)
name = obj.get("name", "")
if not name:
continue
args = obj.get("arguments", {})
if isinstance(args, dict):
args_str = json.dumps(args)
elif isinstance(args, str):
args_str = args
else:
args_str = json.dumps({"value": args})
tool_calls.append(
{
"id": f"call_{id_offset + len(tool_calls)}",
"type": "function",
"function": {"name": name, "arguments": args_str},
}
)

@danielhanchen

Copy link
Copy Markdown
Member Author

Confirming the revert was correct. #5615 shipped five concrete parser regressions (Mistral nested-JSON truncation, <|python_tag|> stop-on-< markup leak, Llama-3 sentinel single-pass dropping calls, UTF-8 corruption in the Llama-3 KV decoder, and GGUF heal-key always-query breaking python/terminal tools). Details on each are in #5615. Replacement is being validated in #5620pytest studio/backend/tests/test_safetensors_tool_loop.py studio/backend/tests/test_safetensors_capability_advertise.py -q on pr-5620 is 110 passed in 3.86s and each of the five regressions has a dedicated unit test (TestLoopRePrompt, TestLoopCanonicalHealKey).

rsd-darshan pushed a commit to rsd-darshan/unsloth that referenced this pull request Jun 3, 2026
…sors + MLX (unslothai#5615)" (unslothai#5619)

Reverts PR unslothai#5615 to give the safetensors + MLX healing parity work more time to bake before re-merging. The reverted feature branch `studio-tools-multi-format` remains untouched, and the follow-up PR will layer the healing-parity commits on top.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant