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// Kimi K2 Thinking - uses unique tool call ID format: functions.<name>:<index> 4// The ID contains both the function name and an incrementing counter 5common_chat_params common_chat_params_init_kimi_k2 (const common_chat_template & tmpl , 6const autoparser::generation_params & inputs ) { 7common_chat_params data ; 8 9data .prompt = common_chat_template_direct_apply_impl (tmpl ,inputs ); 10data .generation_prompt = common_chat_template_generation_prompt_impl (tmpl ,inputs ); 11data .format = COMMON_CHAT_FORMAT_PEG_NATIVE ; 12data .supports_thinking = true; 13data .preserved_tokens = { 14"<|tool_calls_section_begin|>" , 15"<|tool_calls_section_end|>" , 16"<|tool_call_begin|>" , 17"<|tool_call_argument_begin|>" , 18"<|tool_call_end|>" , 19"<think>" , 20"</think>" , 21 }; 22 23auto has_tools = inputs .tools .is_array ()&& !inputs .tools .empty (); 24auto extract_reasoning = inputs .reasoning_format != COMMON_REASONING_FORMAT_NONE ; 25auto include_grammar = has_tools && inputs .tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE ; 26 27const std::string SECTION_BEGIN = "<|tool_calls_section_begin|>" ; 28const std::string SECTION_END = "<|tool_calls_section_end|>" ; 29const std::string CALL_BEGIN = "<|tool_call_begin|>" ; 30const std::string ARGS_BEGIN = "<|tool_call_argument_begin|>" ; 31const std::string CALL_END = "<|tool_call_end|>" ; 32 33const std::string THINK_START = "<think>" ; 34const std::string THINK_END = "</think>" ; 35const std::string GEN_PROMPT = "<|im_assistant|>assistant<|im_middle|>" ; 36 37data .thinking_start_tag = THINK_START ; 38data .thinking_end_tags = {THINK_END }; 39 40if (inputs .has_continuation ()) { 41const auto & msg = inputs .continue_msg ; 42 43data .generation_prompt = GEN_PROMPT + THINK_START + msg .reasoning_content ; 44if (inputs .continue_final_message == COMMON_CHAT_CONTINUATION_CONTENT ) { 45data .generation_prompt += THINK_END + msg .render_content (); 46 } 47 48data .prompt += data .generation_prompt ; 49 } 50 51auto parser = build_chat_peg_parser ([& ](common_chat_peg_builder & p ) { 52// Kimi K2 Thinking format: 53// - Reasoning: <think>{reasoning}</think> 54// - Content: text after reasoning 55// - Tool calls section: 56// <|tool_calls_section_begin|> 57// <|tool_call_begin|>functions.<name>:<index><|tool_call_argument_begin|>{json_args}<|tool_call_end|> 58// ... 59// <|tool_calls_section_end|> 60// The ID format is: functions.<function_name>:<counter> where counter is 0, 1, 2, ... 61 62// Tool call markers 63auto end = p .end (); 64 65// Note: this model is CRAZY. It can diverge from its supposed tool calling pattern in so many ways it's not funny. 66// For example, it can call tools at the end of reasoning without closing reasoning... 67auto reasoning = extract_reasoning ?p .optional (THINK_START + p .reasoning ( 68p .until_one_of ({THINK_END ,"<|tool_calls_section_begin|>" ,"<|tool_call_begin|>" }))+ 69p .optional (p .literal (THINK_END ))) :p .eps (); 70auto generation_prompt = p .literal (GEN_PROMPT ); 71 72 73// Content only parser (no tools) 74if (!has_tools || inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_NONE ) { 75return generation_prompt + reasoning + p .content (p .rest ())+ end ; 76 } 77 78// Build tool call parsers for each available function 79// The ID format is: functions.<name>:<index> 80// We need to match: functions.<name>:<digits> 81auto tool_choice = p .choice (); 82foreach_function (inputs .tools , [& ](const json & tool ) { 83const auto & function = tool .at ("function" ); 84 std::string name = function .at ("name" ); 85const auto schema = common_chat_tool_parameters (function ); 86 87// Match: functions.<name>:<digits> 88// Capture the full call id (functions.<name>:<digits>) using tool_id tag 89auto tool_id = p .tool_id (p .literal ("functions." )+ p .tool_name (p .literal (name ))+ p .literal (":" )+ p .chars ("[0-9]" ,1 ,-1 )); 90auto tool_parser = p .tool ( 91p .tool_open (tool_id + p .literal (ARGS_BEGIN ))+ 92p .tool_args (p .schema (p .json (),"tool-" + name + "-schema" ,schema ))+ 93p .tool_close (p .optional ((p .literal (CALL_END )))) 94 ); 95 96tool_choice |=p .rule ("tool-" + name ,tool_parser ); 97 }); 98 99// Tool calls section: <|tool_calls_section_begin|> tool_calls <|tool_calls_section_end|> 100auto min_calls = inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED ?1 :0 ; 101auto max_calls = inputs .parallel_tool_calls ?-1 :1 ; 102// Use trigger_rule so grammar generator knows where to start generating rules 103auto tool_calls = p .rule ("tool-calls" , 104p .optional (p .literal (SECTION_BEGIN ))+ 105p .trigger_rule ("tool-call" ,p .repeat (CALL_BEGIN + tool_choice ,min_calls ,max_calls )+ 106p .optional (p .literal (SECTION_END ))) 107 ); 108 109auto content_before_tools = p .content (p .until_one_of ({SECTION_BEGIN ,CALL_BEGIN })); 110 111return generation_prompt + reasoning + content_before_tools + tool_calls + end ; 112 }); 113 114data .parser = parser .save (); 115 116if (include_grammar ) { 117data .grammar_lazy = inputs .tool_choice == COMMON_CHAT_TOOL_CHOICE_AUTO ; 118data .grammar = build_grammar ([& ](const common_grammar_builder & builder ) { 119parser .build_grammar (builder ,data .grammar_lazy ); 120 }); 121 122data .grammar_triggers = { 123 {COMMON_GRAMMAR_TRIGGER_TYPE_WORD ,"<|tool_call_begin|>" } 124 }; 125 } 126 127return data ; 128}