Felipe97/llama-cpp-compiled
01.2k
1#include "parsers.h"2 3common_chat_params common_chat_params_init_gpt_oss(const common_chat_template & tmpl,4 const autoparser::generation_params & inputs) {5 common_chat_params data;6 7 // Copy reasoning to the "thinking" field as expected by the gpt-oss template8 auto adjusted_messages = json::array();9 for (auto msg : inputs.messages) {10 if (msg.contains("reasoning_content") && msg.at("reasoning_content").is_string()) {11 msg["thinking"] = msg.at("reasoning_content");12 if (msg.contains("tool_calls") && msg.at("tool_calls").is_array() && !msg.at("tool_calls").empty()) {13 msg.erase("content");14 }15 }16 adjusted_messages.push_back(msg);17 }18 19 auto prompt = common_chat_template_direct_apply_impl(tmpl, inputs, /* messages_override= */ adjusted_messages);20 21 // Check if we need to replace the return token with end token during22 // inference and without generation prompt. For more details see:23 // https://github.com/ggml-org/llama.cpp/issues/1541724 if (inputs.is_inference && !inputs.add_generation_prompt) {25 static constexpr std::string_view return_token = "<|return|>";26 static constexpr std::string_view end_token = "<|end|>";27 if (size_t pos = prompt.rfind(return_token); pos != std::string::npos) {28 prompt.replace(pos, return_token.length(), end_token);29 }30 }31 32 data.prompt = prompt;33 data.generation_prompt = common_chat_template_generation_prompt_impl(tmpl, inputs, /* messages_override= */ adjusted_messages);34 data.message_delimiters = {35 { COMMON_CHAT_ROLE_ASSISTANT, "<|start|>assistant" },36 { COMMON_CHAT_ROLE_USER, "<|start|>user" },37 { COMMON_CHAT_ROLE_SYSTEM, "<|start|>developer" },38 { COMMON_CHAT_ROLE_SYSTEM, "<|start|>system" },39 { COMMON_CHAT_ROLE_TOOL, "<|start|>functions" },40 };41 42 data.format = COMMON_CHAT_FORMAT_PEG_NATIVE;43 data.supports_thinking = true;44 45 data.thinking_start_tag = "<|channel|>analysis<|message|>";46 data.thinking_end_tags = {"<|end|>"};47 48 // These special tokens are required to parse properly, so we include them49 // even if parse_tool_calls is false.50 data.preserved_tokens = {51 "<|channel|>", "<|constrain|>", "<|message|>", "<|start|>", "<|end|>",52 };53 54 // Adjust prompt for continuation55 if (inputs.has_continuation()) {56 const auto & msg = inputs.continue_msg;57 58 data.generation_prompt = "<|start|>assistant<|channel|>analysis<|message|>" + msg.reasoning_content;59 if (inputs.continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT) {60 data.generation_prompt += "<|end|><|start|>assistant<|channel|>final<|message|>" + msg.render_content();61 }62 63 data.prompt += data.generation_prompt;64 }65 66 auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();67 auto has_response_format = !inputs.json_schema.is_null() && inputs.json_schema.is_object();68 auto include_grammar = has_response_format || (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE);69 auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;70 71 auto parser = build_chat_peg_parser([&](common_chat_peg_builder & p) {72 auto start = p.rule("start", p.literal("<|start|>assistant"));73 auto end = p.rule("end", p.literal("<|end|>"));74 auto content = p.rule("message-content", p.until("<|end|>"));75 auto channel = p.literal("<|channel|>") + (p.literal("commentary") | p.literal("analysis"));76 auto constrain_type = p.chars("[A-Za-z0-9_-]", 1, -1);77 78 // Occasionally, gpt-oss-20b will prefix channels with this commentary79 auto stray_commentary = p.optional(p.literal("<|channel|>commentary") + p.optional(p.literal(" to=assistant")));80 auto start_analysis = stray_commentary + p.literal("<|channel|>analysis<|message|>");81 82 if (extract_reasoning) {83 p.rule("analysis", start_analysis + p.reasoning(content) + end);84 } else {85 p.rule("analysis", p.content(start_analysis + content + end));86 }87 88 auto analysis = p.ref("analysis");89 auto preamble = p.rule("preamble", p.literal("<|channel|>commentary<|message|>") + p.content(content) + end);90 auto final_msg = p.rule("final", stray_commentary + p.literal("<|channel|>final<|message|>") + p.content(content));91 92 // Consume any unsolicited tool calls, e.g. builtin functions93 auto unsolicited = p.rule("unsolicited", p.atomic(p.optional(channel) + p.literal(" to=") + content + end));94 95 auto any = p.rule("any", preamble | analysis);96 97 if (has_response_format) {98 auto constraint = p.optional(p.space() + p.optional(p.literal("<|constrain|>")) + constrain_type);99 auto response_format = p.rule("response-format",100 p.literal("<|channel|>final") + constraint + p.literal("<|message|>") +101 p.content(p.schema(p.json(), "response-format-schema", inputs.json_schema)));102 103 return p.zero_or_more(start + analysis) + start + response_format;104 }105 106 if (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE) {107 auto tool_choice = p.choice();108 109 foreach_function(inputs.tools, [&](const json & tool) {110 const auto & function = tool.at("function");111 std::string name = function.at("name");112 const auto params = common_chat_tool_parameters(function);113 114 auto func_name = p.literal(" to=functions.") + p.tool_name(p.literal(name));115 auto constraint = p.optional(p.space() + p.optional(p.literal("<|constrain|>")) + constrain_type);116 auto args = p.tool_args(p.schema(p.json(), "tool-" + name + "-schema", params));117 118 // recipient in role header119 // <|start|>assistant to=functions.NAME<|channel|>(commentary|analysis)[constraint]<|message|>ARGS120 auto tool_in_role = p.tool(p.tool_open(func_name + channel + constraint + p.literal("<|message|>")) + args);121 122 // recipient in channel header123 // <|channel|>(commentary|analysis) to=functions.NAME[constraint]<|message|>ARGS124 auto tool_in_channel = p.tool(p.tool_open(channel + func_name + constraint + p.literal("<|message|>")) + args);125 126 tool_choice |= p.rule("tool-" + name, tool_in_role | tool_in_channel);127 });128 129 auto tool_call = p.trigger_rule("tool-call", tool_choice);130 131 if (inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED) {132 return p.zero_or_more(start + any) + start + tool_call;133 }134 135 return p.zero_or_more(start + any) + start + (tool_call | final_msg);136 }137 138 return p.zero_or_more(start + any) + start + (final_msg | unsolicited);139 });140 141 data.parser = parser.save();142 143 if (include_grammar) {144 data.grammar_lazy = !(has_response_format || (has_tools && inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED));145 data.grammar = build_grammar([&](const common_grammar_builder & builder) {146 parser.build_grammar(builder, data.grammar_lazy);147 });148 149 data.grammar_triggers = {150 { COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN, "^\\s+to$" },151 { COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN, "^<\\|channel\\|>(?:commentary|analysis)\\s+to=functions$" },152 { COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN, "<\\|start\\|>assistant(\\s+to)" },153 { COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN, "<\\|start\\|>assistant(<\\|channel\\|>(?:commentary|analysis)\\s+to)" }154 };155 }156 157 return data;158}159 