@@ -931,7 +931,12 @@ static void common_chat_parse_qwen3_coder_xml(common_chat_msg_parser & builder)
931931 // For now, use empty tools vector - we'll need to pass tools differently
932932 std::vector<common_chat_tool> empty_tools;
933933 if (builder.parse_qwen3_xml_tool_call (content, empty_tools)) {
934- // Successfully parsed XML tool call
934+ // Only treat as parsed if at least one tool call was actually added.
935+ // On malformed or incomplete XML, fall back to plain content.
936+ const auto & parsed = builder.result ();
937+ if (parsed.tool_calls .empty () && parsed.content .empty ()) {
938+ builder.add_content (content);
939+ }
935940 return ;
936941 }
937942
@@ -2237,9 +2242,11 @@ static common_chat_params common_chat_templates_apply_jinja(
22372242 }
22382243
22392244 // Qwen3-Coder XML format detection (must come before Hermes 2 Pro)
2240- // Look for unique patterns that distinguish Qwen3-Coder from other formats
2241- if (src.find (" Function calls MUST follow the specified format" ) != std::string::npos ||
2242- src.find (" <function=...></function> block must be nested within <tool_call></tool_call>" ) != std::string::npos) {
2245+ // Detect via explicit XML markers unique to Qwen3-Coder to avoid false positives in other templates.
2246+ // Require presence of <tool_call>, <function=...>, and <parameter=...> blocks.
2247+ if (src.find (" <tool_call>" ) != std::string::npos &&
2248+ src.find (" <function=" ) != std::string::npos &&
2249+ src.find (" <parameter=" ) != std::string::npos) {
22432250 return common_chat_params_init_qwen3_coder_xml (tmpl, params);
22442251 }
22452252
0 commit comments