char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
acecd5603
char/aliceai-t5-moe
1#include "parsers.h" 2 3// Cohere2 MoE (a.k.a. "North Code") parser. 4// 5// The assistant turn is fully marker-wrapped: 6// <|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|> 7// <|START_THINKING|>{reasoning}<|END_THINKING|> 8// then EITHER content: <|START_TEXT|>{content}<|END_TEXT|> 9// OR tool calls: <|START_ACTION|>[ 10// {"tool_call_id": "0", "tool_name": "f", "parameters": {...}}, ... 11// ]<|END_ACTION|> 12// <|END_OF_TURN_TOKEN|> 13// 14// The generation prompt forces a leading <|START_THINKING|> (when reasoning is enabled, which is 15// the template default), so the model's output continues from *inside* the thinking block. The 16// parser literal therefore only covers the stable <|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|> prefix 17// and the reasoning rule consumes the <|START_THINKING|> ... <|END_THINKING|> markers itself, 18// regardless of whether they came from the generation prompt or the generated text. 19common_chat_params common_chat_params_init_cohere2moe (const common_chat_template & tmpl , 20const autoparser::generation_params & inputs ) { 21common_chat_params data ; 22 23const std::string TURN_START = "<|START_OF_TURN_TOKEN|>" ; 24const std::string TURN_END = "<|END_OF_TURN_TOKEN|>" ; 25const std::string CHATBOT = "<|CHATBOT_TOKEN|>" ; 26const std::string USER = "<|USER_TOKEN|>" ; 27const std::string SYSTEM = "<|SYSTEM_TOKEN|>" ; 28const std::string THINK_START = "<|START_THINKING|>" ; 29const std::string THINK_END = "<|END_THINKING|>" ; 30const std::string TEXT_START = "<|START_TEXT|>" ; 31const std::string TEXT_END = "<|END_TEXT|>" ; 32const std::string ACTION_START = "<|START_ACTION|>" ; 33const std::string ACTION_END = "<|END_ACTION|>" ; 34const std::string RESULT_START = "<|START_TOOL_RESULT|>" ; 35const std::string RESULT_END = "<|END_TOOL_RESULT|>" ; 36 37// Stable prefix of the generation prompt that precedes the (forced) <|START_THINKING|> marker. 38const std::string GEN_PREFIX = TURN_START + CHATBOT ; 39 40data .prompt = common_chat_template_direct_apply_impl (tmpl ,inputs ); 41data .generation_prompt = common_chat_template_generation_prompt_impl (tmpl ,inputs ); 42data .format = COMMON_CHAT_FORMAT_PEG_NATIVE ; 43data .supports_thinking = true; 44data .thinking_start_tag = THINK_START ; 45data .thinking_end_tags = {THINK_END }; 46data .preserved_tokens = { 47TURN_START ,TURN_END ,CHATBOT ,USER ,SYSTEM , 48THINK_START ,THINK_END , 49TEXT_START ,TEXT_END , 50ACTION_START ,ACTION_END , 51RESULT_START ,RESULT_END , 52 }; 53 54// Declare per-role message delimiters. Tool results are rendered with the 55// system token followed by <|START_TOOL_RESULT|>, so the "tool" delimiter must be listed before 56// the plain "system" one (it is a strict superset, and the role split tries delimiters in order). 57data .message_delimiters = { 58 {COMMON_CHAT_ROLE_ASSISTANT ,GEN_PREFIX }, 59 {COMMON_CHAT_ROLE_USER ,TURN_START + USER }, 60 {COMMON_CHAT_ROLE_TOOL ,TURN_START + SYSTEM + RESULT_START }, 61 {COMMON_CHAT_ROLE_SYSTEM ,TURN_START + SYSTEM }, 62 }; 63 64auto has_tools = inputs .tools .is_array ()&& !inputs .tools .empty (); 65auto has_response_format = inputs .json_schema .is_object ()&& !inputs .json_schema .empty (); 66auto extract_reasoning = inputs .reasoning_format != COMMON_REASONING_FORMAT_NONE ; 67auto include_grammar = has_response_format || (has_tools && inputs .tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE ); 68 69if (inputs .has_continuation ()) { 70const auto & msg = inputs .continue_msg ; 71 72data .generation_prompt = GEN_PREFIX + THINK_START + msg .reasoning_content ; 73if (inputs .continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT ) { 74data .generation_prompt += THINK_END + TEXT_START + msg .render_content (); 75 } 76 77data .prompt += data .generation_prompt ; 78 } 79 80auto parser = build_chat_peg_parser ([& ](common_chat_peg_builder & p ) { 81auto generation_prompt = p .literal (GEN_PREFIX ); 82auto end = p .end (); 83 84// The thinking block is always present (the generation prompt forces <|START_THINKING|>). 85// When extracting reasoning, capture its body; otherwise keep the whole block (markers 86// included) inline as content, matching reasoning_format=NONE conventions. 87common_peg_parser reasoning = p .eps (); 88if (extract_reasoning ) { 89reasoning = p .optional (p .literal (THINK_START )+ 90p .reasoning (p .until_one_of ({THINK_END ,TEXT_START ,ACTION_START }))+ 91p .optional (p .literal (THINK_END ))); 92 }else { 93reasoning = p .optional (p .content (p .literal (THINK_START )+ 94p .until_one_of ({THINK_END ,TEXT_START ,ACTION_START })+ 95p .optional (p .literal (THINK_END )))); 96 } 97 98auto text_content = has_response_format 99 ?p .literal (TEXT_START )+ 100p .content (p .schema (p .json (),"response-format-schema" ,inputs .json_schema ))+ 101p .optional (p .literal (TEXT_END )) 102 :p .literal (TEXT_START )+ p .content (p .until (TEXT_END ))+ p .optional (p .literal (TEXT_END )); 103 104if (!has_tools || inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_NONE ) { 105return generation_prompt + reasoning + text_content + p .optional (p .literal (TURN_END ))+ end ; 106 } 107 108auto require_tools = inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED ; 109 110// <|START_ACTION|>[ {"tool_call_id": "0", "tool_name": "f", "parameters": {...}}, ... ]<|END_ACTION|> 111auto tool_calls = p .standard_json_tools (ACTION_START ,ACTION_END ,inputs .tools ,inputs .parallel_tool_calls , 112/* force_tool_calls = */ true, 113/* name_key = */ "tool_name" , 114/* args_key = */ "parameters" , 115/* array_wrapped = */ true, 116/* function_is_key = */ false, 117/* call_id_key = */ "" , 118/* gen_call_id_key = */ "tool_call_id" , 119/* parameters_order = */ {"tool_call_id" ,"tool_name" ,"parameters" }); 120 121// Content and tool calls are mutually exclusive in this format. 122common_peg_parser body = require_tools ?tool_calls :p .choice ({tool_calls ,text_content }); 123 124return generation_prompt + reasoning + body + p .optional (p .literal (TURN_END ))+ end ; 125 }); 126 127data .parser = parser .save (); 128 129if (include_grammar ) { 130data .grammar_lazy = !has_response_format && inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO ; 131data .grammar = build_grammar ([& ](const common_grammar_builder & builder ) { 132parser .build_grammar (builder ,data .grammar_lazy ); 133 }); 134 135data .grammar_triggers = { 136 {COMMON_GRAMMAR_TRIGGER_TYPE_WORD ,ACTION_START } 137 }; 138 } 139 140return data ; 141}