diff --git a/common/chat-parser-xml-toolcall.cpp b/common/chat-parser-xml-toolcall.cpp index 8848c7e47..16fe26612 100644 --- a/common/chat-parser-xml-toolcall.cpp +++ b/common/chat-parser-xml-toolcall.cpp @@ -245,15 +245,28 @@ void build_grammar_xml_tool_call(common_chat_params & data, const json & tools, auto next_arg_with_sep = builder.add_rule(name + "-last-arg-end", form.last_val_end ? gbnf_format_literal(*form.last_val_end) : gbnf_format_literal(form.val_end)); decltype(next_arg_with_sep) next_arg = "\"\""; - for (auto i = arg_rules.size() - 1; /* i >= 0 && */ i < arg_rules.size(); --i) { - std::string include_this_arg = arg_rules[i].symbol_name + " " + next_arg_with_sep; - next_arg = builder.add_rule(name + "-arg-after-" + std::to_string(i), arg_rules[i].is_required ? - include_this_arg : "( " + include_this_arg + " ) | " + next_arg - ); - include_this_arg = gbnf_format_literal(form.val_end) + " " + include_this_arg; - next_arg_with_sep = builder.add_rule(name + "-arg-after-" + std::to_string(i) + "-with-sep", arg_rules[i].is_required ? - include_this_arg : "( " + include_this_arg + " ) | " + next_arg_with_sep - ); + if (form.relax_arg) { + if (!arg_rules.empty()) { + std::vector arg_symbols; + arg_symbols.reserve(arg_rules.size()); + for (const auto & rule : arg_rules) { + arg_symbols.push_back(rule.symbol_name); + } + auto any_arg = builder.add_rule(name + "-any-arg", string_join(arg_symbols, " | ")); + auto any_arg_with_end = builder.add_rule(name + "-any-arg-with-end", any_arg + " " + next_arg_with_sep); + next_arg = builder.add_rule(name + "-args-relaxed", "( " + any_arg_with_end + " )*"); + } + } else { + for (auto i = arg_rules.size() - 1; /* i >= 0 && */ i < arg_rules.size(); --i) { + std::string include_this_arg = arg_rules[i].symbol_name + " " + next_arg_with_sep; + next_arg = builder.add_rule(name + "-arg-after-" + std::to_string(i), arg_rules[i].is_required ? + include_this_arg : "( " + include_this_arg + " ) | " + next_arg + ); + include_this_arg = gbnf_format_literal(form.val_end) + " " + include_this_arg; + next_arg_with_sep = builder.add_rule(name + "-arg-after-" + std::to_string(i) + "-with-sep", arg_rules[i].is_required ? + include_this_arg : "( " + include_this_arg + " ) | " + next_arg_with_sep + ); + } } std::string quoted_name = name; diff --git a/common/chat-parser-xml-toolcall.h b/common/chat-parser-xml-toolcall.h index b309fb667..fed28d869 100644 --- a/common/chat-parser-xml-toolcall.h +++ b/common/chat-parser-xml-toolcall.h @@ -32,6 +32,9 @@ struct xml_tool_call_format { std::optional last_tool_end = std::nullopt; bool trim_raw_argval = false; bool allow_toolcall_in_think = false; + // Set true to allows function arguments in arbitrary order and without + // enforcing required field. + bool relax_arg = false; }; // make a GBNF that accept any strings except those containing any of the forbidden strings. diff --git a/common/chat.cpp b/common/chat.cpp index 2248c4740..c272d7416 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -1250,16 +1250,19 @@ static common_chat_params common_chat_params_init_qwen3_coder_xml(const common_c } // build grammar for tool call - static const xml_tool_call_format form { - /* form.scope_start = */ "", - /* form.tool_start = */ "\n\n\n", - /* form.key_start = */ "\n", - /* form.val_end = */ "\n\n", - /* form.tool_end = */ "\n", - /* form.scope_end = */ "", - }; + static const xml_tool_call_format form = ([]() { + xml_tool_call_format form {}; + form.scope_start = ""; + form.tool_start = "\n\n