char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
acecd5603
char/aliceai-t5-moe
1#include "parsers.h" 2 3common_chat_params common_chat_params_init_gpt_oss (const common_chat_template & tmpl , 4const autoparser::generation_params & inputs ) { 5common_chat_params data ; 6 7// Copy reasoning to the "thinking" field as expected by the gpt-oss template 8auto adjusted_messages = json::array (); 9for (auto msg :inputs .messages ) { 10if (msg .contains ("reasoning_content" )&& msg .at ("reasoning_content" ).is_string ()) { 11msg ["thinking" ]= msg .at ("reasoning_content" ); 12if (msg .contains ("tool_calls" )&& msg .at ("tool_calls" ).is_array ()&& !msg .at ("tool_calls" ).empty ()) { 13msg .erase ("content" ); 14 } 15 } 16adjusted_messages .push_back (msg ); 17 } 18 19auto 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 during 22// inference and without generation prompt. For more details see: 23// https://github.com/ggml-org/llama.cpp/issues/15417 24if (inputs .is_inference && !inputs .add_generation_prompt ) { 25static constexpr std::string_view return_token = "<|return|>" ; 26static constexpr std::string_view end_token = "<|end|>" ; 27if (size_t pos = prompt .rfind (return_token );pos != std::string::npos ) { 28prompt .replace (pos ,return_token .length (),end_token ); 29 } 30 } 31 32data .prompt = prompt ; 33data .generation_prompt = common_chat_template_generation_prompt_impl (tmpl ,inputs ,/* messages_override= */ adjusted_messages ); 34data .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 42data .format = COMMON_CHAT_FORMAT_PEG_NATIVE ; 43data .supports_thinking = true; 44 45data .thinking_start_tag = "<|channel|>analysis<|message|>" ; 46data .thinking_end_tags = {"<|end|>" }; 47 48// These special tokens are required to parse properly, so we include them 49// even if parse_tool_calls is false. 50data .preserved_tokens = { 51"<|channel|>" ,"<|constrain|>" ,"<|message|>" ,"<|start|>" ,"<|end|>" , 52 }; 53 54// Adjust prompt for continuation 55if (inputs .has_continuation ()) { 56const auto & msg = inputs .continue_msg ; 57 58data .generation_prompt = "<|start|>assistant<|channel|>analysis<|message|>" + msg .reasoning_content ; 59if (inputs .continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT ) { 60data .generation_prompt += "<|end|><|start|>assistant<|channel|>final<|message|>" + msg .render_content (); 61 } 62 63data .prompt += data .generation_prompt ; 64 } 65 66auto has_tools = inputs .tools .is_array ()&& !inputs .tools .empty (); 67auto has_response_format = !inputs .json_schema .is_null ()&& inputs .json_schema .is_object (); 68auto include_grammar = has_response_format || (has_tools && inputs .tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE ); 69auto extract_reasoning = inputs .reasoning_format != COMMON_REASONING_FORMAT_NONE ; 70 71auto parser = build_chat_peg_parser ([& ](common_chat_peg_builder & p ) { 72auto start = p .rule ("start" ,p .literal ("<|start|>assistant" )); 73auto end = p .rule ("end" ,p .literal ("<|end|>" )); 74auto content = p .rule ("message-content" ,p .until ("<|end|>" )); 75auto channel = p .literal ("<|channel|>" )+ (p .literal ("commentary" ) |p .literal ("analysis" )); 76auto constrain_type = p .chars ("[A-Za-z0-9_-]" ,1 ,-1 ); 77 78// Occasionally, gpt-oss-20b will prefix channels with this commentary 79auto stray_commentary = p .optional (p .literal ("<|channel|>commentary" )+ p .optional (p .literal (" to=assistant" ))); 80auto start_analysis = stray_commentary + p .literal ("<|channel|>analysis<|message|>" ); 81 82if (extract_reasoning ) { 83p .rule ("analysis" ,start_analysis + p .reasoning (content )+ end ); 84 }else { 85p .rule ("analysis" ,p .content (start_analysis + content + end )); 86 } 87 88auto analysis = p .ref ("analysis" ); 89auto preamble = p .rule ("preamble" ,p .literal ("<|channel|>commentary<|message|>" )+ p .content (content )+ end ); 90auto 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 functions 93auto unsolicited = p .rule ("unsolicited" ,p .atomic (p .optional (channel )+ p .literal (" to=" )+ content + end )); 94 95auto any = p .rule ("any" ,preamble |analysis ); 96 97if (has_response_format ) { 98auto constraint = p .optional (p .space ()+ p .optional (p .literal ("<|constrain|>" ))+ constrain_type ); 99auto response_format = p .rule ("response-format" , 100p .literal ("<|channel|>final" )+ constraint + p .literal ("<|message|>" )+ 101p .content (p .schema (p .json (),"response-format-schema" ,inputs .json_schema ))); 102 103return p .zero_or_more (start + analysis )+ start + response_format ; 104 } 105 106if (has_tools && inputs .tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE ) { 107auto tool_choice = p .choice (); 108 109foreach_function (inputs .tools , [& ](const json & tool ) { 110const auto & function = tool .at ("function" ); 111 std::string name = function .at ("name" ); 112const auto params = common_chat_tool_parameters (function ); 113 114auto func_name = p .literal (" to=functions." )+ p .tool_name (p .literal (name )); 115auto constraint = p .optional (p .space ()+ p .optional (p .literal ("<|constrain|>" ))+ constrain_type ); 116auto args = p .tool_args (p .schema (p .json (),"tool-" + name + "-schema" ,params )); 117 118// recipient in role header 119// <|start|>assistant to=functions.NAME<|channel|>(commentary|analysis)[constraint]<|message|>ARGS 120auto tool_in_role = p .tool (p .tool_open (func_name + channel + constraint + p .literal ("<|message|>" ))+ args ); 121 122// recipient in channel header 123// <|channel|>(commentary|analysis) to=functions.NAME[constraint]<|message|>ARGS 124auto tool_in_channel = p .tool (p .tool_open (channel + func_name + constraint + p .literal ("<|message|>" ))+ args ); 125 126tool_choice |=p .rule ("tool-" + name ,tool_in_role |tool_in_channel ); 127 }); 128 129auto tool_call = p .trigger_rule ("tool-call" ,tool_choice ); 130 131if (inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED ) { 132return p .zero_or_more (start + any )+ start + tool_call ; 133 } 134 135return p .zero_or_more (start + any )+ start + (tool_call |final_msg ); 136 } 137 138return p .zero_or_more (start + any )+ start + (final_msg |unsolicited ); 139 }); 140 141data .parser = parser .save (); 142 143if (include_grammar ) { 144data .grammar_lazy = !(has_response_format || (has_tools && inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED )); 145data .grammar = build_grammar ([& ](const common_grammar_builder & builder ) { 146parser .build_grammar (builder ,data .grammar_lazy ); 147 }); 148 149data .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 157return data ; 158}