char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
acecd5603
char/aliceai-t5-moe
1// Chat support (incl. tool call grammar constraining & output parsing) w/ generic & custom template handlers. 2 3#pragma once 4 5#include "common.h" 6#include "peg-parser.h" 7#include "jinja/parser.h" 8#include "jinja/runtime.h" 9#include "jinja/caps.h" 10 11#include "json.h" 12 13#include <chrono> 14#include <functional> 15#include <map> 16#include <string> 17#include <vector> 18 19using chat_template_caps = jinja ::caps ; 20 21struct common_chat_templates ; 22 23namespace autoparser { 24struct generation_params ; 25}// namespace autoparser 26 27struct common_chat_tool_call { 28std ::string name ; 29std ::string arguments ; 30std ::string id ; 31 32bool operator == (const common_chat_tool_call & other )const { 33return name == other .name && arguments == other .arguments && id == other .id ; 34 } 35}; 36 37struct common_chat_msg_content_part { 38std ::string type ; 39std ::string text ; 40 41// TODO @ngxson : no known chat templates support reasoning_content in content parts yet 42// this can be useful for models with interleaved thinking (like Kimi-K2) 43// if you see any templates explicitly support this, please ping me 44// std::string reasoning_content; 45 46bool operator == (const common_chat_msg_content_part & other )const { 47return type == other .type && text == other .text ; 48 } 49}; 50 51struct common_chat_template { 52jinja ::program prog ; 53std ::string bos_tok ; 54std ::string eos_tok ; 55std ::string src ; 56chat_template_caps caps ; 57 58common_chat_template (const std ::string & src ,const std ::string & bos_token ,const std ::string & eos_token ) { 59jinja ::lexer lexer ; 60 autolexer_res = lexer .tokenize (src ); 61this -> prog = jinja ::parse_from_tokens (lexer_res ); 62 63this -> src = lexer_res .source ; 64this -> bos_tok = bos_token ; 65this -> eos_tok = eos_token ; 66 67this -> caps = jinja ::caps_get (prog ); 68// LOG_INF("%s: caps:\n%s\n", __func__, this->caps.to_string().c_str()); 69 } 70 71const std ::string & source ()const {return src ; } 72const std ::string & bos_token ()const {return bos_tok ; } 73const std ::string & eos_token ()const {return eos_tok ; } 74 75chat_template_caps original_caps ()const { 76return caps ; 77 } 78}; 79 80struct common_chat_msg { 81std ::string role ; 82std ::string content ; 83std ::vector < common_chat_msg_content_part > content_parts ; 84std ::vector < common_chat_tool_call > tool_calls ; 85std ::string reasoning_content ; 86std ::string tool_name ; 87std ::string tool_call_id ; 88 89common_json to_json_oaicompat (bool concat_typed_text = false)const ; 90 91std ::string render_content (const std ::string & delimiter = "\n\n" )const ; 92 93bool empty ()const { 94return content .empty ()&& content_parts .empty ()&& tool_calls .empty ()&& reasoning_content .empty ()&& 95tool_name .empty ()&& tool_call_id .empty (); 96 } 97 98bool contains_media ()const { 99for (const auto & part :content_parts ) { 100if (part .type == "media_marker" ) { 101return true; 102 } 103 } 104return false; 105 } 106 107void set_tool_call_ids (std ::vector < std ::string > & ids_cache , 108const std ::function < std ::string ()> & gen_tool_call_id ) { 109for (autoi = 0u ; i < tool_calls. size (); i ++ ) { 110if (ids_cache. size () <= i) { 111auto id = tool_calls[i]. id ; 112if (id. empty ()) { 113id = gen_tool_call_id (); 114} 115ids_cache. push_back (id); 116} 117tool_calls[i]. id = ids_cache[i]; 118} 119} 120 121bool operator == ( const common_chat_msg & other) const { 122return role == other. role && content == other. content && content_parts == other. content_parts && 123tool_calls == other. tool_calls && reasoning_content == other. reasoning_content && 124tool_name == other. tool_name && tool_call_id == other. tool_call_id ; 125} 126 127bool operator != ( const common_chat_msg & other) const { return !( * this == other); } 128}; 129 130struct common_chat_msg_diff { 131std :: string reasoning_content_delta ; 132std :: string content_delta ; 133size_t tool_call_index = std::string::npos; 134common_chat_tool_call tool_call_delta ; 135 136static std :: vector < common_chat_msg_diff > compute_diffs ( const common_chat_msg & msg_prv, 137const common_chat_msg & msg_new); 138 139bool operator == ( const common_chat_msg_diff & other ) const { 140return content_delta == other . content_delta && tool_call_index == other . tool_call_index && 141tool_call_delta == other . tool_call_delta ; 142} 143}; 144 145enum common_chat_role { 146COMMON_CHAT_ROLE_UNKNOWN , 147COMMON_CHAT_ROLE_SYSTEM , 148COMMON_CHAT_ROLE_ASSISTANT , 149COMMON_CHAT_ROLE_USER , 150COMMON_CHAT_ROLE_TOOL 151}; 152 153common_chat_role common_chat_role_from_string ( const std ::string & role); 154const char * common_chat_role_to_string ( common_chat_role role); 155 156struct common_chat_msg_span { 157common_chat_role role = COMMON_CHAT_ROLE_UNKNOWN ; 158std :: size_t pos = 0 ; 159std :: size_t len = 0 ; 160 161bool valid () const { 162return role != COMMON_CHAT_ROLE_UNKNOWN ; 163} 164}; 165 166struct common_chat_msg_spans { 167std :: vector < common_chat_msg_span > spans ; 168 169void add ( common_chat_role role, size_t pos, size_t len) { 170spans. push_back ({ role, pos, len }); 171} 172 173bool is_user_start ( int32_t pos) const { 174for (auto it = spans. begin (); it != spans. end (); ++ it) { 175if (it -> role == COMMON_CHAT_ROLE_USER && pos == ( int32_t ) it -> pos ) { 176return true; 177} 178} 179return false; 180} 181 182int32_t last_user_message_pos () const { 183for (auto it = spans. rbegin (); it != spans. rend (); ++ it) { 184if (it -> role == COMMON_CHAT_ROLE_USER ) { 185return ( int32_t ) it -> pos ; 186} 187} 188return -1 ; 189} 190}; 191 192struct common_chat_msg_delimiter { 193common_chat_role role = COMMON_CHAT_ROLE_UNKNOWN ; 194std :: string delimiter ; 195llama_tokens tokens = {}; 196}; 197 198struct common_chat_msg_delimiters { 199std :: vector < common_chat_msg_delimiter > delimiters ; 200 201common_chat_msg_delimiters () = default ; 202common_chat_msg_delimiters( std :: initializer_list < common_chat_msg_delimiter > delims) : delimiters ( delims ) {} 203 204void add ( common_chat_role role, const std ::string & delimiter) { 205delimiters. push_back ({ role, delimiter }); 206} 207 208void tokenize ( const llama_vocab * vocab); 209 210// split tokens into message spans. skips maps a start index to a length of a region to jump over without matching 211common_chat_msg_spans split ( const llama_tokens & tokens, const std ::map < size_t , size_t > & skips = {}) const ; 212 213common_json to_json () const ; 214}; 215 216struct common_chat_tool { 217std :: string name ; 218std :: string description ; 219std :: string parameters ; 220}; 221 222enum common_chat_tool_choice { 223COMMON_CHAT_TOOL_CHOICE_AUTO , 224COMMON_CHAT_TOOL_CHOICE_REQUIRED , 225COMMON_CHAT_TOOL_CHOICE_NONE , 226}; 227 228enum common_chat_format { 229COMMON_CHAT_FORMAT_CONTENT_ONLY , 230 231// These are intended to be parsed by the PEG parser 232COMMON_CHAT_FORMAT_PEG_SIMPLE , 233COMMON_CHAT_FORMAT_PEG_NATIVE , 234COMMON_CHAT_FORMAT_PEG_GEMMA4 , 235COMMON_CHAT_FORMAT_PEG_MINIMAX_M3 , 236 237COMMON_CHAT_FORMAT_COUNT , // Not a format, just the # formats 238}; 239 240 241// Continuation method provided via `continue_final_message` 242enum common_chat_continuation { 243COMMON_CHAT_CONTINUATION_NONE , 244COMMON_CHAT_CONTINUATION_AUTO , 245COMMON_CHAT_CONTINUATION_REASONING , 246COMMON_CHAT_CONTINUATION_CONTENT , 247}; 248 249struct common_chat_templates_inputs { 250std :: vector < common_chat_msg > messages ; 251std :: string grammar ; 252std :: string json_schema ; 253bool add_generation_prompt = true; 254common_chat_continuation continue_final_message = COMMON_CHAT_CONTINUATION_NONE ; 255bool use_jinja = true; 256// Parameters below only supported when use_jinja is true 257std :: vector < common_chat_tool > tools ; 258common_chat_tool_choice tool_choice = COMMON_CHAT_TOOL_CHOICE_AUTO ; 259bool parallel_tool_calls = false; 260common_reasoning_format reasoning_format = COMMON_REASONING_FORMAT_NONE ; // TODO: refactor this to "bool enable_thinking" 261bool enable_thinking = true; 262std :: chrono ::system_clock::time_point now = std::chrono::system_clock:: now (); 263std :: map < std ::string, std ::string > chat_template_kwargs; 264bool add_bos = false; 265bool add_eos = false; 266bool force_pure_content = false; 267}; 268 269struct common_chat_params { 270common_chat_format format = COMMON_CHAT_FORMAT_CONTENT_ONLY ; 271std :: string prompt ; 272std :: string grammar ; 273bool grammar_lazy = false; 274std :: string generation_prompt ; 275bool supports_thinking = false; 276std :: string thinking_start_tag ; // e.g., "<think>" 277std :: vector < std ::string > thinking_end_tags; // e.g., "</think>" 278std :: vector < common_grammar_trigger > grammar_triggers ; 279std :: vector < std ::string > preserved_tokens; 280std :: vector < std ::string > additional_stops; 281std :: string parser ; 282common_chat_msg_delimiters message_delimiters ; 283}; 284 285// per-message parsing syntax 286// should be derived from common_chat_params 287struct common_chat_parser_params { 288common_chat_format format = COMMON_CHAT_FORMAT_CONTENT_ONLY ; 289common_reasoning_format reasoning_format = COMMON_REASONING_FORMAT_NONE ; // TODO: refactor this to "bool parse_reasoning" 290// Whether reasoning_content should be inlined in the content (e.g. for reasoning_format=deepseek in stream mode) 291bool reasoning_in_content = false; 292std :: string generation_prompt ; 293bool parse_tool_calls = true; 294bool is_continuation = false; 295bool echo = false; // Include assistant prefilled msg in output 296bool debug = false; // Enable debug output for PEG parser 297common_peg_arena parser = {}; 298common_chat_parser_params() = default ; 299common_chat_parser_params ( const common_chat_params & chat_params) { 300format = chat_params.format; 301generation_prompt = chat_params.generation_prompt; 302} 303}; 304 305// Check if the template supplied via "--chat-template" is supported or not. Returns true if it's valid 306bool common_chat_verify_template( const std ::string & tmpl, bool use_jinja ); 307 308void common_chat_templates_free ( struct common_chat_templates * tmpls); 309 310struct common_chat_templates_deleter { 311void operator ()( common_chat_templates * tmpls) { common_chat_templates_free ( tmpls ); } 312}; 313 314typedef std ::unique_ptr < struct common_chat_templates, common_chat_templates_deleter > common_chat_templates_ptr; 315 316common_chat_templates_ptr common_chat_templates_init ( const struct llama_model * model, 317const std ::string & chat_template_override, 318const std::string & bos_token_override = "" , 319const std::string & eos_token_override = "" ); 320 321bool common_chat_templates_was_explicit ( const struct common_chat_templates * tmpls); 322std :: string common_chat_templates_source ( const struct common_chat_templates * tmpls, const std ::string & variant = "" ); 323 324struct common_chat_params common_chat_templates_apply ( const struct common_chat_templates * tmpls, 325const struct common_chat_templates_inputs & inputs); 326 327// Format single message, while taking into account the position of that message in chat history 328std :: string common_chat_format_single ( const struct common_chat_templates * tmpls, 329const std ::vector < common_chat_msg > & past_msg, 330const common_chat_msg & new_msg , 331bool add_ass , 332bool use_jinja); 333 334// Returns an example of formatted chat 335std :: string common_chat_format_example ( const struct common_chat_templates * tmpls, 336bool use_jinja, 337const std ::map < std::string, std ::string > & chat_template_kwargs); 338 339const char * common_chat_format_name ( common_chat_format format); 340common_chat_msg common_chat_parse ( const std ::string & input, bool is_partial , const common_chat_parser_params & params ); 341common_chat_msg common_chat_peg_parse ( const common_peg_arena & src_parser, const std ::string & input, bool is_partial , const common_chat_parser_params & params ); 342 343// used by arg and server 344const char * common_reasoning_format_name ( common_reasoning_format format); 345common_reasoning_format common_reasoning_format_from_name ( const std ::string & format); 346 347common_chat_tool_choice common_chat_tool_choice_parse_oaicompat ( const std ::string & tool_choice); 348 349bool common_chat_templates_support_enable_thinking ( const common_chat_templates * chat_templates); 350 351// Parses a JSON array of messages in OpenAI's chat completion API format. 352std :: vector < common_chat_msg > common_chat_msgs_parse_oaicompat ( const common_json & messages); 353 354std :: vector < common_chat_tool > common_chat_tools_parse_oaicompat ( const common_json & tools); 355 356common_chat_continuation common_chat_continuation_parse ( const common_json & value); 357 358// DEPRECATED: only used in tests 359common_json common_chat_msgs_to_json_oaicompat ( const std ::vector < common_chat_msg > & msgs, bool concat_typed_text = false); 360 361common_json common_chat_tools_to_json_oaicompat ( const std ::vector < common_chat_tool > & tools); 362 363// The parameters schema of a function tool. A tool without parameters, or with an empty {}, takes zero arguments. 364common_json common_chat_tool_parameters ( const common_json & function); 365 366// get template caps, useful for reporting to server /props endpoint 367std :: map < std ::string, bool > common_chat_templates_get_caps ( const common_chat_templates * chat_templates); 368 369std :: string common_chat_template_direct_apply ( 370const common_chat_template & tmpl, 371const autoparser ::generation_params & inputs); 372 373std :: string common_chat_template_generation_prompt ( 374const common_chat_template & tmpl, 375const autoparser ::generation_params & inputs); 376 377std :: optional < common_chat_params > common_chat_try_specialized_template ( 378const common_chat_template & tmpl, 379const std ::string & src, 380autoparser ::generation_params & params); 381 382 383// specialized per-task preset 384struct common_chat_prompt_preset { 385std :: string system ; 386std :: string user ; 387}; 388 389common_chat_prompt_preset common_chat_get_asr_prompt ( const common_chat_templates * chat_templates); 390 391common_chat_msg_delimiters common_chat_msg_delimiters_parse ( const common_json & delimiters );