From dcf50d82796ad214eeaad6c0992c17ea6633b8d6 Mon Sep 17 00:00:00 2001 From: Yap Sok Ann Date: Mon, 23 Feb 2026 13:54:56 +0700 Subject: [PATCH] Fix tool call for Qwen3.5 (#1300) * Fix tool call for Qwen3.5 Loosely based on mainline changes from: * https://github.com/ggml-org/llama.cpp/pull/19635 * https://github.com/ggml-org/llama.cpp/pull/19765 Also need to change the grammar to allow the model to make multiple tool calls in a row. This was likely broken for Qwen3 Coder prior to this commit. * Fix the grammar for the subsequent parameters after the first one --- common/chat.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/common/chat.cpp b/common/chat.cpp index 34a48ea5..2248c474 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -1225,6 +1225,17 @@ static common_chat_params common_chat_params_init_qwen3_coder_xml(const common_c data.prompt = apply(tmpl, params); data.format = COMMON_CHAT_FORMAT_QWEN3_CODER_XML; + // Qwen3.5 and Step-3.5-Flash use the Qwen3 Coder tool calling with thinking + bool supports_reasoning = (tmpl.source().find("") != std::string::npos); + + if (supports_reasoning && string_ends_with(data.prompt, "\n")) { + if (!params.enable_thinking) { + data.prompt += ""; + } else { + data.thinking_forced_open = true; + } + } + data.preserved_tokens = { "", "", @@ -1234,16 +1245,20 @@ static common_chat_params common_chat_params_init_qwen3_coder_xml(const common_c "", }; + if (supports_reasoning) { + data.preserved_tokens.insert(data.preserved_tokens.end(), {"", ""}); + } + // build grammar for tool call static const xml_tool_call_format form { - /* form.scope_start = */ "\n", - /* form.tool_start = */ "\n\n", /* form.key_start = */ "\n", /* form.val_end = */ "\n\n", - /* form.tool_end = */ "\n", - /* form.scope_end = */ "", + /* form.tool_end = */ "\n", + /* form.scope_end = */ "", }; build_grammar_xml_tool_call(data, params.tools, form); @@ -2064,9 +2079,7 @@ static common_chat_params common_chat_templates_apply_jinja( // Detect via explicit XML markers unique to Qwen3-Coder to avoid false positives in other templates. // Require presence of , , and blocks. if (src.find("") != std::string::npos && - src.find("") != std::string::npos && src.find("") != std::string::npos && src.find("