Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions common/chat-parser-xml-toolcall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> 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;
Expand Down
3 changes: 3 additions & 0 deletions common/chat-parser-xml-toolcall.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ struct xml_tool_call_format {
std::optional<std::string> 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.
Expand Down
23 changes: 13 additions & 10 deletions common/chat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<tool_call>\n<function=",
/* form.tool_sep = */ ">\n",
/* form.key_start = */ "<parameter=",
/* form.key_val_sep = */ ">\n",
/* form.val_end = */ "\n</parameter>\n",
/* form.tool_end = */ "</function>\n</tool_call>",
/* form.scope_end = */ "",
};
static const xml_tool_call_format form = ([]() {
xml_tool_call_format form {};
form.scope_start = "";
form.tool_start = "\n<tool_call>\n<function=";
form.tool_sep = ">\n";
form.key_start = "<parameter=";
form.key_val_sep = ">\n";
form.val_end = "\n</parameter>\n";
form.tool_end = "</function>\n</tool_call>";
form.scope_end = "";
form.relax_arg = true;
return form;
})();
build_grammar_xml_tool_call(data, params.tools, form);

return data;
Expand Down