data.grammar_triggers = {
{ COMMON_GRAMMAR_TRIGGER_TYPE_WORD, trigger_marker }
};
+ if (autoparser.tools.format.openai_wrapper_trigger) {
+ // model emits the OpenAI function wrapper, trigger on it
+ data.grammar_triggers.push_back({ COMMON_GRAMMAR_TRIGGER_TYPE_WORD, "{\"type\": \"function\"," });
+ }
}
}
auto single_tool_parser = p.standard_json_tools(
format.per_call_start, format.per_call_end, inputs.tools, inputs.parallel_tool_calls,
inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED, name_field, args_field, format.tools_array_wrapped,
- format.fun_name_is_key, format.id_field, format.gen_id_field, format.parameter_order);
+ format.fun_name_is_key, format.id_field, format.gen_id_field, format.parameter_order, format.openai_wrapper_trigger);
tools_parser = p.trigger_rule("tool-calls", p.one_or_more(single_tool_parser + p.space()));
} else {
tools_parser = p.standard_json_tools(
format.section_start, format.section_end, inputs.tools, inputs.parallel_tool_calls,
inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED, name_field, args_field, format.tools_array_wrapped,
- format.fun_name_is_key, format.id_field, format.gen_id_field, format.parameter_order);
+ format.fun_name_is_key, format.id_field, format.gen_id_field, format.parameter_order, format.openai_wrapper_trigger);
}
// Handle content wrappers if present
bool fun_name_is_key = false; // In JSON format function name is JSON key, i.e. { "<funname>": { ... arguments ... } }
bool tools_array_wrapped = false; // Tool calls wrapped in JSON array [...]
+ bool openai_wrapper_trigger = false; // model emits the OpenAI function wrapper, trigger on it
std::string function_field = "function";
std::string name_field = "name";
LOG_DBG(ANSI_ORANGE "[Patch: Apriel 1.6]\n" ANSI_RESET);
}
},
+ // template uses the JSON {name, parameters} tool instruction, emits the OpenAI function wrapper
+ [](const common_chat_template & tmpl, autoparser & analysis) -> void {
+ if (tmpl.src.find("Respond in the format {\"name\": function name") != std::string::npos &&
+ tmpl.src.find("Do not use variables.") != std::string::npos) {
+ analysis.tools.format.openai_wrapper_trigger = true;
+ LOG_DBG(ANSI_ORANGE "[Patch: JSON name/parameters tool instruction]\n" ANSI_RESET);
+ }
+ },
});
const std::string & effective_args_key,
const std::string & call_id_key,
const std::string & gen_call_id_key,
- const std::vector<std::string> & parameters_order) {
+ const std::vector<std::string> & parameters_order,
+ bool accept_openai_wrapper) {
auto tool_choices = choice();
auto name_key_parser = literal("\"" + effective_name_key + "\"");
return idx_a < idx_b;
});
- auto ordered_body = tool_open(literal("{")) + space();
+ // accept an optional leading "type": "function" field when the model emits the OpenAI wrapper
+ common_peg_parser type_field = eps();
+ if (accept_openai_wrapper) {
+ type_field = optional(literal("\"type\"") + space() + literal(":") + space() +
+ literal("\"function\"") + space() + literal(",") + space());
+ }
+ auto ordered_body = tool_open(literal("{")) + space() + type_field;
for (size_t i = 0; i < parser_pairs.size(); i++) {
ordered_body = ordered_body + parser_pairs[i].first;
if (i < parser_pairs.size() - 1) {
bool function_is_key,
const std::string & call_id_key,
const std::string & gen_call_id_key,
- const std::vector<std::string> & parameters_order) {
+ const std::vector<std::string> & parameters_order,
+ bool accept_openai_wrapper) {
if (!tools.is_array() || tools.empty()) {
return eps();
}
if (!name_spec.first.empty() || !args_spec.first.empty()) {
tool_choices = build_json_tools_nested_keys(tools, effective_name_key, effective_args_key, call_id_key, gen_call_id_key);
} else {
- tool_choices = build_json_tools_flat_keys(tools, effective_name_key, effective_args_key, call_id_key, gen_call_id_key, parameters_order);
+ tool_choices = build_json_tools_flat_keys(tools, effective_name_key, effective_args_key, call_id_key, gen_call_id_key, parameters_order, accept_openai_wrapper);
}
}
bool function_is_key = false,
const std::string & call_id_key = "",
const std::string & gen_call_id_key = "",
- const std::vector<std::string> & parameters_order = {});
+ const std::vector<std::string> & parameters_order = {},
+ bool accept_openai_wrapper = false);
// Legacy-compatible helper for building XML/tagged style tool calls
// Used by tests and manual parsers
const std::string & effective_args_key,
const std::string & call_id_key,
const std::string & gen_call_id_key,
- const std::vector<std::string> & parameters_order);
+ const std::vector<std::string> & parameters_order,
+ bool accept_openai_wrapper);
};
inline common_peg_arena build_chat_peg_parser(
}
return msg;
}
- throw std::runtime_error(std::string("Failed to parse input at pos ") + std::to_string(result.end) + ": " +
- effective_input.substr(result.end));
+ LOG_WRN("%s: unparsed %s output: %s\n", __func__, common_chat_format_name(params.format),
+ effective_input.substr(result.end).c_str());
+ throw std::runtime_error(std::string("The model produced output that does not match the expected ") +
+ common_chat_format_name(params.format) + " format");
}
common_chat_msg msg;