char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
acecd5603
char/aliceai-t5-moe
1#include "json-schema-to-grammar.h" 2#include "common.h" 3#include "trie.h" 4#include "unicode.h" 5 6#include <algorithm> 7#include <limits> 8#include <map> 9#include <regex> 10#include <sstream> 11#include <string> 12#include <unordered_map> 13#include <unordered_set> 14#include <vector> 15 16using json = common_json ; 17 18static std::string build_repetition (const std::string & item_rule ,int min_items ,int max_items ,const std::string & separator_rule = "" ) { 19auto has_max = max_items != std::numeric_limits < int > ::max (); 20 21if (max_items == 0 ) { 22return "" ; 23 } 24if (min_items == 0 && max_items == 1 ) { 25return item_rule + "?" ; 26 } 27 28if (separator_rule .empty ()) { 29if (min_items == 1 && !has_max ) { 30return item_rule + "+" ; 31 } 32if (min_items == 0 && !has_max ) { 33return item_rule + "*" ; 34 } 35return item_rule + "{" + std::to_string (min_items )+ "," + (has_max ? std::to_string (max_items ) :"" )+ "}" ; 36 } 37 38auto result = item_rule + " " + build_repetition ("(" + separator_rule + " " + item_rule + ")" ,min_items == 0 ?0 :min_items - 1 ,has_max ?max_items - 1 :max_items ); 39if (min_items == 0 ) { 40result = "(" + result + ")?" ; 41 } 42return result ; 43} 44 45static void build_min_max_int (int64_t min_value ,int64_t max_value , std::stringstream & out ,int decimals_left = 16 ,bool top_level = true) { 46auto has_min = min_value != std::numeric_limits < int64_t > ::min (); 47auto has_max = max_value != std::numeric_limits < int64_t > ::max (); 48 49auto digit_range = [& ](char from ,char to ) { 50out <<"[" ; 51if (from == to ) { 52out <<from ; 53 }else { 54out <<from <<"-" <<to ; 55 } 56out <<"]" ; 57 }; 58auto more_digits = [& ](int min_digits ,int max_digits ) { 59out <<"[0-9]" ; 60if (min_digits == max_digits && min_digits == 1 ) { 61return ; 62 } 63out <<"{" ; 64out <<min_digits ; 65if (max_digits != min_digits ) { 66out <<"," ; 67if (max_digits != std::numeric_limits < int > ::max ()) { 68out <<max_digits ; 69 } 70 } 71out <<"}" ; 72 }; 73 std::function < void (const std::string_view & ,const std::string_view & )> uniform_range = 74 [& ](const std::string_view & from ,const std::string_view & to ) { 75size_t i = 0 ; 76while (i < from .length ()&& i < to .length ()&& from [i ]== to [i ]) { 77i ++ ; 78 } 79if (i > 0 ) { 80out <<"\"" <<from .substr (0 ,i ) <<"\"" ; 81 } 82if (i < from .length ()&& i < to .length ()) { 83if (i > 0 ) { 84out <<" " ; 85 } 86auto sub_len = from .length ()- i - 1 ; 87if (sub_len > 0 ) { 88auto from_sub = from .substr (i + 1 ); 89auto to_sub = to .substr (i + 1 ); 90auto sub_zeros = string_repeat ("0" ,sub_len ); 91auto sub_nines = string_repeat ("9" ,sub_len ); 92 93auto to_reached = false; 94out <<"(" ; 95if (from_sub == sub_zeros ) { 96digit_range (from [i ],to [i ]- 1 ); 97out <<" " ; 98more_digits (sub_len ,sub_len ); 99 }else { 100out <<"[" <<from [i ] <<"] " ; 101out <<"(" ; 102uniform_range (from_sub ,sub_nines ); 103out <<")" ; 104if (from [i ]< to [i ]- 1 ) { 105out <<" | " ; 106if (to_sub == sub_nines ) { 107digit_range (from [i ]+ 1 ,to [i ]); 108to_reached = true; 109 }else { 110digit_range (from [i ]+ 1 ,to [i ]- 1 ); 111 } 112out <<" " ; 113more_digits (sub_len ,sub_len ); 114 } 115 } 116if (!to_reached ) { 117out <<" | " ; 118digit_range (to [i ],to [i ]); 119out <<" " ; 120uniform_range (sub_zeros ,to_sub ); 121 } 122out <<")" ; 123 }else { 124out <<"[" <<from [i ] <<"-" <<to [i ] <<"]" ; 125 } 126 } 127 }; 128 129if (has_min && has_max ) { 130if (min_value < 0 && max_value < 0 ) { 131out <<"\"-\" (" ; 132build_min_max_int (- max_value ,- min_value ,out ,decimals_left ,/* top_level= */ true); 133out <<")" ; 134return ; 135 } 136 137if (min_value < 0 ) { 138out <<"\"-\" (" ; 139build_min_max_int (0 ,- min_value ,out ,decimals_left ,/* top_level= */ true); 140out <<") | " ; 141min_value = 0 ; 142 } 143 144auto min_s = std::to_string (min_value ); 145auto max_s = std::to_string (max_value ); 146auto min_digits = min_s .length (); 147auto max_digits = max_s .length (); 148 149for (auto digits = min_digits ;digits < max_digits ;digits ++ ) { 150uniform_range (min_s ,string_repeat ("9" ,digits )); 151min_s = "1" + string_repeat ("0" ,digits ); 152out <<" | " ; 153 } 154uniform_range (min_s ,max_s ); 155return ; 156 } 157 158auto less_decimals = std::max (decimals_left - 1 ,1 ); 159 160if (has_min ) { 161if (min_value < 0 ) { 162out <<"\"-\" (" ; 163build_min_max_int (std::numeric_limits < int64_t > ::min (),- min_value ,out ,decimals_left ,/* top_level= */ false); 164out <<") | [0] | [1-9] " ; 165more_digits (0 ,decimals_left - 1 ); 166 }else if (min_value == 0 ) { 167if (top_level ) { 168out <<"[0] | [1-9] " ; 169more_digits (0 ,less_decimals ); 170 }else { 171more_digits (1 ,decimals_left ); 172 } 173 }else if (min_value <=9 ) { 174char c = '0' + min_value ; 175auto range_start = top_level ?'1' :'0' ; 176if (c > range_start ) { 177digit_range (range_start ,c - 1 ); 178out <<" " ; 179more_digits (1 ,less_decimals ); 180out <<" | " ; 181 } 182digit_range (c ,'9' ); 183out <<" " ; 184more_digits (0 ,less_decimals ); 185 }else { 186auto min_s = std::to_string (min_value ); 187auto len = min_s .length (); 188auto c = min_s [0 ]; 189 190if (c > '1' ) { 191digit_range (top_level ?'1' :'0' ,c - 1 ); 192out <<" " ; 193more_digits (len ,less_decimals ); 194out <<" | " ; 195 } 196digit_range (c ,c ); 197out <<" (" ; 198build_min_max_int (std::stoll (min_s .substr (1 )), std::numeric_limits < int64_t > ::max (),out ,less_decimals ,/* top_level= */ false); 199out <<")" ; 200if (c < '9' ) { 201out <<" | " ; 202digit_range (c + 1 ,'9' ); 203out <<" " ; 204more_digits (len - 1 ,less_decimals ); 205 } 206 } 207return ; 208 } 209 210if (has_max ) { 211if (max_value >=0 ) { 212if (top_level ) { 213out <<"\"-\" [1-9] " ; 214more_digits (0 ,less_decimals ); 215out <<" | " ; 216 } 217build_min_max_int (0 ,max_value ,out ,decimals_left ,/* top_level= */ true); 218 }else { 219out <<"\"-\" (" ; 220build_min_max_int (- max_value , std::numeric_limits < int64_t > ::max (),out ,decimals_left ,/* top_level= */ false); 221out <<")" ; 222 } 223return ; 224 } 225 226throw std::runtime_error ("At least one of min_value or max_value must be set" ); 227} 228 229const std::string SPACE_RULE = "| \" \" | \"\\n\"{1,2} [ \\t]{0,20}" ; 230 231struct BuiltinRule { 232 std::string content ; 233 std::vector < std::string > deps ; 234}; 235 236static std::unordered_map < std::string ,BuiltinRule > PRIMITIVE_RULES = { 237 {"boolean" , {"(\"true\" | \"false\")" , {}}}, 238 {"decimal-part" , {"[0-9]{1,16}" , {}}}, 239 {"integral-part" , {"[0] | [1-9] [0-9]{0,15}" , {}}}, 240 {"number" , {"(\"-\"? integral-part) (\".\" decimal-part)? ([eE] [-+]? integral-part)?" , {"integral-part" ,"decimal-part" }}}, 241 {"integer" , {"(\"-\"? integral-part)" , {"integral-part" }}}, 242 {"value" , {"object | array | string | number | boolean | null" , {"object" ,"array" ,"string" ,"number" ,"boolean" ,"null" }}}, 243 {"object" , {"\"{\" space ( string \":\" space value (\",\" space string \":\" space value)* )? space \"}\"" , {"string" ,"value" }}}, 244 {"array" , {"\"[\" space ( value (\",\" space value)* )? space \"]\"" , {"value" }}}, 245 {"uuid" , {"\"\\\"\" [0-9a-fA-F]{8} \"-\" [0-9a-fA-F]{4} \"-\" [0-9a-fA-F]{4} \"-\" [0-9a-fA-F]{4} \"-\" [0-9a-fA-F]{12} \"\\\"\"" , {}}}, 246 {"char" , {"[^\"\\\\\\x7F\\x00-\\x1F] | [\\\\] ([\"\\\\bfnrt] | \"u\" [0-9a-fA-F]{4})" , {}}}, 247 {"string" , {"\"\\\"\" char* \"\\\"\"" , {"char" }}}, 248 {"null" , {"\"null\"" , {}}}, 249}; 250 251static std::unordered_map < std::string ,BuiltinRule > STRING_FORMAT_RULES = { 252 {"date" , {"[0-9]{4} \"-\" ( \"0\" [1-9] | \"1\" [0-2] ) \"-\" ( \"0\" [1-9] | [1-2] [0-9] | \"3\" [0-1] )" , {}}}, 253 {"time" , {"([01] [0-9] | \"2\" [0-3]) \":\" [0-5] [0-9] \":\" [0-5] [0-9] ( \".\" [0-9]{3} )? ( \"Z\" | ( \"+\" | \"-\" ) ( [01] [0-9] | \"2\" [0-3] ) \":\" [0-5] [0-9] )" , {}}}, 254 {"date-time" , {"date \"T\" time" , {"date" ,"time" }}}, 255 {"date-string" , {"\"\\\"\" date \"\\\"\"" , {"date" }}}, 256 {"time-string" , {"\"\\\"\" time \"\\\"\"" , {"time" }}}, 257 {"date-time-string" , {"\"\\\"\" date-time \"\\\"\"" , {"date-time" }}} 258}; 259 260static bool is_reserved_name (const std::string & name ) { 261static const std::unordered_set < std::string > RESERVED_NAMES = [] { 262 std::unordered_set < std::string > s ; 263s .insert ("root" ); 264for (const auto & p :PRIMITIVE_RULES ) { 265s .insert (p .first ); 266 } 267for (const auto & p :STRING_FORMAT_RULES ) { 268s .insert (p .first ); 269 } 270return s ; 271 }(); 272return RESERVED_NAMES .find (name )!= RESERVED_NAMES .end (); 273} 274 275static std::regex INVALID_RULE_CHARS_RE ("[^a-zA-Z0-9-]+" ); 276static std::regex GRAMMAR_LITERAL_ESCAPE_RE ("[\r\n\"\\\\]" ); 277static std::regex GRAMMAR_RANGE_LITERAL_ESCAPE_RE ("[\r\n\"\\]\\-\\\\]" ); 278static std::unordered_map < char , std::string > GRAMMAR_LITERAL_ESCAPES = { 279 {'\r' ,"\\r" }, {'\n' ,"\\n" }, {'"' ,"\\\"" }, {'-' ,"\\-" }, {']' ,"\\]" }, {'\\' ,"\\\\" } 280}; 281 282static const int MAX_PATTERN_DEPTH = 100 ; 283 284static std::unordered_set < char > NON_LITERAL_SET = {'|' ,'.' ,'(' ,')' ,'[' ,']' ,'{' ,'}' ,'*' ,'+' ,'?' ,'^' ,'$' }; 285static std::unordered_set < char > ESCAPED_IN_REGEXPS_BUT_NOT_IN_LITERALS = {'^' ,'$' ,'.' ,'[' ,']' ,'(' ,')' ,'|' ,'{' ,'}' ,'*' ,'+' ,'?' }; 286 287static std::string replacePattern (const std::string & input ,const std::regex & regex ,const std::function < std::string (const std::smatch & )> & replacement ) { 288 std::smatch match ; 289 std::string result ; 290 291 std::string::const_iterator searchStart (input .cbegin ()); 292 std::string::const_iterator searchEnd (input .cend ()); 293 294while (std::regex_search (searchStart ,searchEnd ,match ,regex )) { 295result .append (searchStart ,searchStart + match .position ()); 296result .append (replacement (match )); 297searchStart = match .suffix ().first ; 298 } 299 300result .append (searchStart ,searchEnd ); 301 302return result ; 303} 304 305static std::string format_literal (const std::string & literal ) { 306 std::string escaped = replacePattern (literal ,GRAMMAR_LITERAL_ESCAPE_RE , [& ](const std::smatch & match ) { 307char c = match .str ()[0 ]; 308return GRAMMAR_LITERAL_ESCAPES .at (c ); 309 }); 310return "\"" + escaped + "\"" ; 311} 312 313std::string gbnf_format_literal (const std::string & literal ) {return format_literal (literal ); } 314 315static size_t gbnf_escape_length (const std::string & pattern ,size_t pos ) { 316if (pos + 1 >=pattern .length ()|| pattern [pos ]!= '\\' ) { 317return 0 ; 318 } 319size_t n_hex = 0 ; 320switch (pattern [pos + 1 ]) { 321case 'x' :n_hex = 2 ;break ; 322case 'u' :n_hex = 4 ;break ; 323case 'U' :n_hex = 8 ;break ; 324case 't' :case 'r' :case 'n' :case '\\' :case '"' :case '[' :case ']' : 325return 2 ; 326default : 327return 0 ; 328 } 329if (pos + 2 + n_hex > pattern .length ()) { 330return 0 ; 331 } 332for (size_t i = pos + 2 ;i < pos + 2 + n_hex ;i ++ ) { 333char h = pattern [i ]; 334if (!((h >='0' && h <='9' )|| (h >='a' && h <='f' )|| (h >='A' && h <='F' ))) { 335return 0 ; 336 } 337 } 338return 2 + n_hex ; 339} 340 341class common_chat_schema_converter { 342private : 343friend std::string build_grammar (const std::function < void (const common_grammar_builder & )> & cb ,const common_grammar_options & options ); 344bool _dotall ; 345 std::map < std::string , std::string > _rules ; 346 std::unordered_set < std::string > _refs_being_resolved ; 347 std::vector < std::string > _errors ; 348 std::vector < std::string > _warnings ; 349 350template < typename T > 351static const T & as (const common_chat_schema & node ) { 352return static_cast < const T &> (node ); 353 } 354 355 std::string _add_rule (const std::string & name ,const std::string & rule ) { 356 std::string esc_name = regex_replace (name ,INVALID_RULE_CHARS_RE ,"-" ); 357if (_rules .find (esc_name )== _rules .end ()|| _rules [esc_name ]== rule ) { 358_rules [esc_name ]= rule ; 359return esc_name ; 360 } 361int i = 0 ; 362while (_rules .find (esc_name + std::to_string (i ))!= _rules .end ()&& _rules [esc_name + std::to_string (i )]!= rule ) { 363i ++ ; 364 } 365 std::string key = esc_name + std::to_string (i ); 366_rules [key ]= rule ; 367return key ; 368 } 369 370 std::string _generate_union_rule (const std::string & name ,const std::vector < common_chat_schema_ptr > & alt_schemas ) { 371 std::vector < std::string > rules ; 372rules .reserve (alt_schemas .size ()); 373for (size_t i = 0 ;i < alt_schemas .size ();i ++ ) { 374rules .push_back (visit (* alt_schemas [i ],name + (name .empty () ?"alternative-" :"-" )+ std::to_string (i ))); 375 } 376return string_join (rules ," | " ); 377 } 378 379// thrown when the pattern is a valid regex with no grammar equivalent 380struct unsupported_pattern :public std::runtime_error { 381using std::runtime_error::runtime_error ; 382 }; 383 384// thrown when the pattern is not a valid regex 385struct invalid_pattern :public std::runtime_error { 386using std::runtime_error::runtime_error ; 387 }; 388 389 std::string _visit_pattern (const std::string & pattern ,const std::string & name ) { 390auto rules_snapshot = _rules ; 391try { 392return _pattern_to_rule (pattern ,name ); 393 }catch (const unsupported_pattern & err ) { 394// revert rules 395_rules = std::move (rules_snapshot ); 396_warnings .push_back ("pattern " + pattern + " is not supported (" + err .what ()+ "), accepting any string" ); 397return _add_rule (name ,_add_primitive ("string" ,PRIMITIVE_RULES .at ("string" ))); 398 }catch (const invalid_pattern & err ) { 399_rules = std::move (rules_snapshot ); 400_errors .push_back ("Invalid pattern " + pattern + ": " + err .what ()); 401return "" ; 402 } 403 } 404 405 std::string _pattern_to_rule (const std::string & pattern ,const std::string & name ) { 406if (pattern .length ()< 2 || pattern .front ()!= '^' || pattern .back ()!= '$' ) { 407throw unsupported_pattern ("not anchored with '^' and '$'" ); 408 } 409 std::string sub_pattern = pattern .substr (1 ,pattern .length ()- 2 ); 410 std::unordered_map < std::string , std::string > sub_rule_ids ; 411 412size_t i = 0 ; 413size_t length = sub_pattern .length (); 414int paren_depth = 0 ; 415 416using literal_or_rule = std::pair < std::string ,bool > ; 417auto to_rule = [& ](const literal_or_rule & ls ) { 418auto is_literal = ls .second ; 419auto s = ls .first ; 420return is_literal ?"\"" + s + "\"" :s ; 421 }; 422 std::function < literal_or_rule ()> transform = [& ]()-> literal_or_rule { 423 std::vector < literal_or_rule > seq ; 424 425auto get_dot = [& ]() { 426 std::string rule ; 427if (_dotall ) { 428rule = "[\\U00000000-\\U0010FFFF]" ; 429 }else { 430rule = "[^\\x0A\\x0D]" ; 431 } 432return _add_rule ("dot" ,rule ); 433 }; 434 435// Joins the sequence, merging consecutive literals together. 436auto join_seq = [& ]() { 437 std::vector < literal_or_rule > ret ; 438 439 std::string literal ; 440auto flush_literal = [& ]() { 441if (literal .empty ()) { 442return false; 443 } 444ret .emplace_back (literal , true); 445literal .clear (); 446return true; 447 }; 448 449for (const auto & item :seq ) { 450auto is_literal = item .second ; 451if (is_literal ) { 452literal += item .first ; 453 }else { 454flush_literal (); 455ret .push_back (item ); 456 } 457 } 458flush_literal (); 459 460 std::vector < std::string > results ; 461results .reserve (ret .size ()); 462for (const auto & item :ret ) { 463results .push_back (to_rule (item )); 464 } 465return std::make_pair (string_join (results ," " ), false); 466 }; 467 468while (i < length ) { 469char c = sub_pattern [i ]; 470if (c == '.' ) { 471seq .emplace_back (get_dot (), false); 472i ++ ; 473 }else if (c == '(' ) { 474i ++ ; 475if (i < length && sub_pattern [i ]== '?' ) { 476if (i + 1 < length && sub_pattern [i + 1 ]== ':' ) { 477i += 2 ;// skip "?:" for non-capturing group, treat as regular group 478 }else { 479// lookaround, named group, inline flags, ... 480throw unsupported_pattern ("unsupported group syntax" ); 481 } 482 } 483paren_depth ++ ; 484if (paren_depth > MAX_PATTERN_DEPTH ) { 485throw unsupported_pattern ("pattern nesting too deep" ); 486 } 487seq .emplace_back ("(" + to_rule (transform ())+ ")" , false); 488 }else if (c == ')' ) { 489i ++ ; 490if (paren_depth == 0 ) { 491throw invalid_pattern ("unbalanced parentheses" ); 492 } 493paren_depth -- ; 494return join_seq (); 495 }else if (c == '^' || c == '$' ) { 496throw unsupported_pattern ("anchor inside the pattern" ); 497 }else if (c == '[' ) { 498 std::string square_brackets = std::string (1 ,c ); 499i ++ ; 500while (i < length && sub_pattern [i ]!= ']' ) { 501if (sub_pattern [i ]== '\\' ) { 502auto escape_length = gbnf_escape_length (sub_pattern ,i ); 503if (escape_length == 0 ) { 504throw unsupported_pattern ("unsupported escape in character class: " + sub_pattern .substr (i ,2 )); 505 } 506square_brackets += sub_pattern .substr (i ,escape_length ); 507i += escape_length ; 508 }else { 509square_brackets += sub_pattern [i ]; 510i ++ ; 511 } 512 } 513if (i >=length ) { 514throw invalid_pattern ("unterminated character class" ); 515 } 516square_brackets += ']' ; 517i ++ ; 518seq .emplace_back (square_brackets , false); 519 }else if (c == '|' ) { 520seq .emplace_back ("|" , false); 521i ++ ; 522 }else if (c == '*' || c == '+' || c == '?' ) { 523if (seq .empty ()) { 524throw invalid_pattern ("nothing to repeat" ); 525 } 526seq .back ()= std::make_pair (to_rule (seq .back ())+ c , false); 527i ++ ; 528 }else if (c == '{' ) { 529 std::string curly_brackets = std::string (1 ,c ); 530i ++ ; 531while (i < length && sub_pattern [i ]!= '}' ) { 532curly_brackets += sub_pattern [i ]; 533i ++ ; 534 } 535if (i >=length ) { 536throw unsupported_pattern ("unterminated curly brackets" ); 537 } 538curly_brackets += '}' ; 539i ++ ; 540auto nums = string_split (curly_brackets .substr (1 ,curly_brackets .length ()- 2 ),"," ); 541int min_times = 0 ; 542int max_times = std::numeric_limits < int > ::max (); 543if (nums .size ()!= 1 && nums .size ()!= 2 ) { 544throw unsupported_pattern ("wrong number of values in curly brackets" ); 545 } 546try { 547if (nums .size ()== 1 ) { 548min_times = max_times = std::stoi (nums [0 ]); 549 }else { 550if (!nums [0 ].empty ()) { 551min_times = std::stoi (nums [0 ]); 552 } 553if (!nums [1 ].empty ()) { 554max_times = std::stoi (nums [1 ]); 555 } 556 } 557 }catch (const std::logic_error & ) { 558throw unsupported_pattern ("invalid number in curly brackets" ); 559 } 560if (seq .empty ()) { 561throw invalid_pattern ("nothing to repeat" ); 562 } 563auto & last = seq .back (); 564auto & sub = last .first ; 565auto sub_is_literal = last .second ; 566 567if (!sub_is_literal ) { 568 std::string & sub_id = sub_rule_ids [sub ]; 569if (sub_id .empty ()) { 570sub_id = _add_rule (name + "-" + std::to_string (sub_rule_ids .size ()),sub ); 571 } 572sub = sub_id ; 573 } 574seq .back ().first = build_repetition ( 575sub_is_literal ?"\"" + sub + "\"" :sub , 576min_times , 577max_times , 578"" 579 ); 580seq .back ().second = false; 581 }else { 582 std::string literal ; 583auto is_non_literal = [& ](char c ) { 584return NON_LITERAL_SET .find (c )!= NON_LITERAL_SET .end (); 585 }; 586while (i < length ) { 587if (sub_pattern [i ]== '\\' ) { 588if (i == length - 1 ) { 589throw invalid_pattern ("trailing backslash" ); 590 } 591char next = sub_pattern [i + 1 ]; 592if (ESCAPED_IN_REGEXPS_BUT_NOT_IN_LITERALS .find (next )!= ESCAPED_IN_REGEXPS_BUT_NOT_IN_LITERALS .end ()) { 593i ++ ; 594literal += sub_pattern [i ]; 595i ++ ; 596 }else { 597auto escape_length = gbnf_escape_length (sub_pattern ,i ); 598if (escape_length == 0 ) { 599throw unsupported_pattern ("unsupported escape: " + sub_pattern .substr (i ,2 )); 600 } 601literal += sub_pattern .substr (i ,escape_length ); 602i += escape_length ; 603 } 604 }else if (sub_pattern [i ]== '"' ) { 605literal += "\\\"" ; 606i ++ ; 607 }else if (!is_non_literal (sub_pattern [i ])&& 608 (i == length - 1 || literal .empty ()|| sub_pattern [i + 1 ]== '.' || !is_non_literal (sub_pattern [i + 1 ]))) { 609literal += sub_pattern [i ]; 610i ++ ; 611 }else { 612break ; 613 } 614 } 615if (literal .empty ()) {// nothing was consumed, ex. a stray ']' or '}' 616throw unsupported_pattern (std::string ("unsupported character: " )+ c ); 617 } 618seq .emplace_back (literal , true); 619 } 620 } 621return join_seq (); 622 }; 623 624auto rule = to_rule (transform ()); 625if (paren_depth != 0 ) { 626throw invalid_pattern ("unbalanced parentheses" ); 627 } 628 629return _add_rule (name ,"\"\\\"\" (" + rule + ") \"\\\"\"" ); 630 } 631 632/* 633Returns a rule that matches a JSON string that is none of the provided strings 634 635not_strings({"a"}) 636-> ["] ( [a] char+ | [^"a] char* )? ["] 637not_strings({"and", "also"}) 638-> ["] ( [a] ([l] ([s] ([o] char+ | [^"o] char*) | [^"s] char*) | [n] ([d] char+ | [^"d] char*) | [^"ln] char*) | [^"a] char* )? ["] 639*/ 640 std::string _not_strings (const std::vector < std::string > & strings ) { 641common_trie trie (strings ); 642 643 std::string char_rule = _add_primitive ("char" ,PRIMITIVE_RULES .at ("char" )); 644 std::ostringstream out ; 645out <<"[\"] ( " ; 646 std::function < void (size_t )> visit = [& ](size_t idx ) { 647const auto & node = trie .nodes [idx ]; 648 std::string rejects ; 649auto first = true; 650for (const auto & [cpt ,child ] :node .children ) { 651 std::string c = common_unicode_cpt_to_utf8 (cpt ); 652rejects += c ; 653if (first ) { 654first = false; 655 }else { 656out <<" | " ; 657 } 658out <<"[" <<c <<"]" ; 659if (!trie .nodes [child ].children .empty ()) { 660out <<" (" ; 661visit (child ); 662out <<")" ; 663 }else { 664out <<" " <<char_rule <<"+" ; 665 } 666 } 667if (!node .children .empty ()) { 668out <<" | [^\"" <<rejects <<"] " <<char_rule <<"*" ; 669 } 670 }; 671visit (0 ); 672 673out <<" )" ; 674if (trie .nodes [0 ].pattern < 0 ) { 675out <<"?" ; 676 } 677out <<" [\"]" ; 678return out .str (); 679 } 680 681 std::string _resolve_ref (const common_chat_schema_ref & schema ) { 682auto it = schema .ref .find ('#' ); 683 std::string ref_fragment = it != std::string::npos ?schema .ref .substr (it + 1 ) :schema .ref ; 684static const std::regex nonalphanumeric_regex (R"([^a-zA-Z0-9-]+)" ); 685 std::string ref_name = "ref" + std::regex_replace (ref_fragment ,nonalphanumeric_regex ,"-" ); 686if (_rules .find (ref_name )== _rules .end ()&& _refs_being_resolved .find (schema .ref )== _refs_being_resolved .end ()) { 687if (!schema .target ) { 688_errors .push_back ("Unresolved $ref " + schema .ref ); 689return "" ; 690 } 691_refs_being_resolved .insert (schema .ref ); 692ref_name = visit (* schema .target ,ref_name ); 693_refs_being_resolved .erase (schema .ref ); 694 } 695return ref_name ; 696 } 697 698 std::string _build_object_rule ( 699const std::vector < std::pair < std::string ,const common_chat_schema *>> & properties , 700const std::unordered_set < std::string > & required , 701const std::string & name , 702const common_chat_schema * additional_properties ) 703 { 704 std::vector < std::string > required_props ; 705 std::vector < std::string > optional_props ; 706 std::unordered_map < std::string , std::string > prop_kv_rule_names ; 707 std::vector < std::string > prop_names ; 708for (const auto & kv :properties ) { 709const auto & prop_name = kv .first ; 710const auto & prop_schema = kv .second ; 711 712 std::string prop_rule_name = visit (* prop_schema ,name + (name .empty () ?"" :"-" )+ prop_name ); 713prop_kv_rule_names [prop_name ]= _add_rule ( 714name + (name .empty () ?"" :"-" )+ prop_name + "-kv" , 715format_literal (json (prop_name ).dump ())+ " space \":\" space " + prop_rule_name 716 ); 717if (required .find (prop_name )!= required .end ()) { 718required_props .push_back (prop_name ); 719 }else { 720optional_props .push_back (prop_name ); 721 } 722prop_names .push_back (prop_name ); 723 } 724if (additional_properties ) { 725 std::string sub_name = name + (name .empty () ?"" :"-" )+ "additional" ; 726 std::string value_rule = 727additional_properties -> kind ()!= common_chat_schema::KIND_ANY ?visit (* additional_properties ,sub_name + "-value" ) 728 :_add_primitive ("value" ,PRIMITIVE_RULES .at ("value" )); 729 730auto key_rule = 731prop_names .empty () ?_add_primitive ("string" ,PRIMITIVE_RULES .at ("string" )) 732 :_add_rule (sub_name + "-k" ,_not_strings (prop_names )); 733 std::string kv_rule = _add_rule (sub_name + "-kv" ,key_rule + " \":\" space " + value_rule ); 734prop_kv_rule_names ["*" ]= kv_rule ; 735optional_props .push_back ("*" ); 736 } 737 738if (required_props .empty ()&& optional_props .empty ()) { 739return "\"{\" space \"}\"" ; 740 } 741 742 std::string rule = "\"{\" space " ; 743for (size_t i = 0 ;i < required_props .size ();i ++ ) { 744if (i > 0 ) { 745rule += " \",\" space " ; 746 } 747rule += prop_kv_rule_names [required_props [i ]]; 748 } 749 750if (!optional_props .empty ()) { 751rule += " (" ; 752if (!required_props .empty ()) { 753rule += " \",\" space ( " ; 754 } 755 756 std::function < std::string (const std::vector < std::string > & ,bool )> get_recursive_refs = [& ](const std::vector < std::string > & ks ,bool first_is_optional ) { 757 std::string res ; 758if (ks .empty ()) { 759return res ; 760 } 761const std::string & k = ks [0 ]; 762 std::string kv_rule_name = prop_kv_rule_names [k ]; 763 std::string comma_ref = "( \",\" space " + kv_rule_name + " )" ; 764if (first_is_optional ) { 765res = comma_ref + (k == "*" ?"*" :"?" ); 766 }else { 767res = kv_rule_name + (k == "*" ?" " + comma_ref + "*" :"" ); 768 } 769if (ks .size ()> 1 ) { 770res += " " + _add_rule ( 771name + (name .empty () ?"" :"-" )+ k + "-rest" , 772get_recursive_refs (std::vector < std::string > (ks .begin ()+ 1 ,ks .end ()), true) 773 ); 774 } 775return res ; 776 }; 777 778for (size_t i = 0 ;i < optional_props .size ();i ++ ) { 779if (i > 0 ) { 780rule += " | " ; 781 } 782rule += get_recursive_refs (std::vector < std::string > (optional_props .begin ()+ i ,optional_props .end ()), false); 783 } 784if (!required_props .empty ()) { 785rule += " )" ; 786 } 787rule += " )?" ; 788 } 789 790rule += " space \"}\"" ; 791 792return rule ; 793 } 794 795 std::string _add_primitive (const std::string & name ,const BuiltinRule & rule ) { 796auto n = _add_rule (name ,rule .content ); 797for (const auto & dep :rule .deps ) { 798BuiltinRule dep_rule ; 799auto it = PRIMITIVE_RULES .find (dep ); 800if (it == PRIMITIVE_RULES .end ()) { 801it = STRING_FORMAT_RULES .find (dep ); 802if (it == STRING_FORMAT_RULES .end ()) { 803_errors .push_back ("Rule " + dep + " not known" ); 804continue ; 805 } 806 } 807if (_rules .find (dep )== _rules .end ()) { 808_add_primitive (dep ,it -> second ); 809 } 810 } 811return n ; 812 } 813 814public : 815explicit common_chat_schema_converter (bool dotall ) :_dotall (dotall ) { 816_rules ["space" ]= SPACE_RULE ; 817 } 818 819 std::string add_schema (const std::string & name ,const common_chat_schema & schema ) { 820return visit (schema ,name ); 821 } 822 823static std::string _generate_constant_rule (const json & value ) { 824return format_literal (value .dump ()); 825 } 826 827 std::string _visit_primitive (const std::string & rule_name ,const std::string & type ) { 828return _add_primitive (rule_name == "root" ?"root" :type ,PRIMITIVE_RULES .at (type )); 829 } 830 831 std::string _visit_all_of (const common_chat_schema_all_of & schema ,const std::string & name ,const std::string & rule_name ) { 832 std::unordered_set < std::string > required ; 833 std::vector < std::pair < std::string ,const common_chat_schema *>> properties ; 834 std::map < std::string ,size_t > enum_values ; 835 std::function < void (const common_chat_schema & ,bool )> add_component = [& ](const common_chat_schema & comp ,bool is_required ) { 836if (comp .kind ()== common_chat_schema::KIND_REF ) { 837if (const auto * target = as < common_chat_schema_ref > (comp ).target ) { 838add_component (* target ,is_required ); 839 } 840 }else if (comp .kind ()== common_chat_schema::KIND_OBJECT ) { 841for (const auto & prop :as < common_chat_schema_object > (comp ).properties ) { 842properties .emplace_back (prop .name ,prop .schema .get ()); 843if (is_required ) { 844required .insert (prop .name ); 845 } 846 } 847 }else if (comp .kind ()== common_chat_schema::KIND_ENUM ) { 848for (const auto & v :as < common_chat_schema_enum > (comp ).values ) { 849enum_values [_generate_constant_rule (v )]+= 1 ; 850 } 851 } 852 }; 853for (const auto & child :schema .children ) { 854if (child -> kind ()== common_chat_schema::KIND_ANY_OF ) { 855for (const auto & alt :as < common_chat_schema_any_of > (* child ).children ) { 856add_component (* alt , false); 857 } 858 }else { 859add_component (* child , true); 860 } 861 } 862if (!enum_values .empty ()) { 863 std::vector < std::string > enum_intersection ; 864for (const auto & p :enum_values ) { 865if (p .second == schema .children .size ()) { 866enum_intersection .push_back (p .first ); 867 } 868 } 869if (!enum_intersection .empty ()) { 870return _add_rule (rule_name ,"(" + string_join (enum_intersection ," | " )+ ")" ); 871 } 872 } 873return _add_rule (rule_name ,_build_object_rule (properties ,required ,name ,nullptr )); 874 } 875 876 std::string visit (const common_chat_schema & schema ,const std::string & name ) { 877 std::string rule_name = is_reserved_name (name ) ?name + "-" :name .empty () ?"root" :name ; 878 std::string sub_name = name + (name .empty () ?"" :"-" ); 879 880switch (schema .kind ()) { 881case common_chat_schema::KIND_REF : 882return _add_rule (rule_name ,_resolve_ref (as < common_chat_schema_ref > (schema ))); 883case common_chat_schema::KIND_ANY_OF : 884return _add_rule (rule_name ,_generate_union_rule (name ,as < common_chat_schema_any_of > (schema ).children )); 885case common_chat_schema::KIND_ALL_OF : 886return _visit_all_of (as < common_chat_schema_all_of > (schema ),name ,rule_name ); 887case common_chat_schema::KIND_CONST : 888return _add_rule (rule_name ,_generate_constant_rule (as < common_chat_schema_const > (schema ).value )); 889case common_chat_schema::KIND_ENUM : { 890 std::vector < std::string > enum_values ; 891for (const auto & v :as < common_chat_schema_enum > (schema ).values ) { 892enum_values .push_back (_generate_constant_rule (v )); 893 } 894return _add_rule (rule_name ,"(" + string_join (enum_values ," | " )+ ")" ); 895 } 896case common_chat_schema::KIND_OBJECT : { 897const auto & obj = as < common_chat_schema_object > (schema ); 898if (obj .properties .empty ()&& obj .additional_properties && obj .additional_properties -> kind ()== common_chat_schema::KIND_ANY ) { 899return _add_rule (rule_name ,_add_primitive ("object" ,PRIMITIVE_RULES .at ("object" ))); 900 } 901 std::vector < std::pair < std::string ,const common_chat_schema *>> properties ; 902 std::unordered_set < std::string > required ; 903for (const auto & prop :obj .properties ) { 904properties .emplace_back (prop .name ,prop .schema .get ()); 905if (prop .required ) { 906required .insert (prop .name ); 907 } 908 } 909return _add_rule (rule_name ,_build_object_rule (properties ,required ,name ,obj .additional_properties .get ())); 910 } 911case common_chat_schema::KIND_TUPLE : { 912const auto & items = as < common_chat_schema_tuple > (schema ).items ; 913 std::string rule = "\"[\" space " ; 914for (size_t i = 0 ;i < items .size ();i ++ ) { 915if (i > 0 ) { 916rule += " \",\" space " ; 917 } 918rule += visit (* items [i ],sub_name + "tuple-" + std::to_string (i )); 919 } 920rule += " space \"]\"" ; 921return _add_rule (rule_name ,rule ); 922 } 923case common_chat_schema::KIND_ARRAY : { 924const auto & arr = as < common_chat_schema_array > (schema ); 925if (arr .items -> kind ()== common_chat_schema::KIND_ANY && arr .min_items == 0 && arr .max_items < 0 ) { 926return _visit_primitive (rule_name ,"array" ); 927 } 928 std::string item_rule_name = visit (* arr .items ,sub_name + "item" ); 929int max_items = arr .max_items < 0 ? std::numeric_limits < int > ::max () :arr .max_items ; 930return _add_rule (rule_name ,"\"[\" space " + build_repetition (item_rule_name ,arr .min_items ,max_items ,"\",\" space" )+ " space \"]\"" ); 931 } 932case common_chat_schema::KIND_STRING : { 933const auto & str = as < common_chat_schema_string > (schema ); 934if (!str .pattern .empty ()) { 935return _visit_pattern (str .pattern ,rule_name ); 936 } 937if (str .format == common_chat_schema::FORMAT_UUID ) { 938return _visit_primitive (rule_name ,"uuid" ); 939 } 940if (str .format != common_chat_schema::FORMAT_NONE ) { 941 std::string prim_name = std::string (str .format == common_chat_schema::FORMAT_DATE ?"date" :str .format == common_chat_schema::FORMAT_TIME ?"time" :"date-time" )+ "-string" ; 942return _add_rule (rule_name ,_add_primitive (prim_name ,STRING_FORMAT_RULES .at (prim_name ))); 943 } 944if (str .min_length > 0 || str .max_length >=0 ) { 945 std::string char_rule = _add_primitive ("char" ,PRIMITIVE_RULES .at ("char" )); 946int max_len = str .max_length < 0 ? std::numeric_limits < int > ::max () :str .max_length ; 947return _add_rule (rule_name ,"\"\\\"\" " + build_repetition (char_rule ,str .min_length ,max_len )+ " \"\\\"\"" ); 948 } 949return _visit_primitive (rule_name ,"string" ); 950 } 951case common_chat_schema::KIND_INTEGER : { 952const auto & i = as < common_chat_schema_integer > (schema ); 953if (i .minimum == std::numeric_limits < int64_t > ::min ()&& i .maximum == std::numeric_limits < int64_t > ::max ()) { 954return _visit_primitive (rule_name ,"integer" ); 955 } 956 std::stringstream out ; 957out <<"(" ; 958build_min_max_int (i .minimum ,i .maximum ,out ); 959out <<")" ; 960return _add_rule (rule_name ,out .str ()); 961 } 962case common_chat_schema::KIND_NUMBER : 963return _visit_primitive (rule_name ,"number" ); 964case common_chat_schema::KIND_BOOLEAN : 965return _visit_primitive (rule_name ,"boolean" ); 966case common_chat_schema::KIND_NULL : 967return _visit_primitive (rule_name ,"null" ); 968case common_chat_schema::KIND_ANY : 969return _add_rule (rule_name ,_add_primitive ("value" ,PRIMITIVE_RULES .at ("value" ))); 970 } 971return "" ; 972 } 973 974void check_errors () { 975if (!_errors .empty ()) { 976throw std::invalid_argument ("JSON schema conversion failed:\n" + string_join (_errors ,"\n" )); 977 } 978if (!_warnings .empty ()) { 979fprintf (stderr ,"WARNING: JSON schema conversion was incomplete: %s\n" ,string_join (_warnings ,"; " ).c_str ()); 980 } 981 } 982 983 std::string format_grammar () { 984 std::stringstream ss ; 985for (const auto & kv :_rules ) { 986ss <<kv .first <<" ::= " <<kv .second <<'\n' ; 987 } 988return ss .str (); 989 } 990}; 991 992std::string json_schema_to_grammar (const common_json & schema ,bool force_gbnf ) { 993#ifdef LLAMA_USE_LLGUIDANCE 994if (!force_gbnf ) { 995return "%llguidance {}\nstart: %json " + schema .dump (); 996 } 997#else 998 (void )force_gbnf ; 999#endif // LLAMA_USE_LLGUIDANCE 1000try { 1001return json_schema_to_grammar (common_chat_schema_from_json (schema )); 1002 }catch (const std::runtime_error & e ) { 1003throw std::invalid_argument (std::string ("JSON schema conversion failed:\n" )+ e .what ()); 1004 } 1005} 1006 1007std::string json_schema_to_grammar (const common_chat_schema_document & schema ) { 1008common_chat_schema_converter converter (false); 1009converter .visit (* schema .root ,"" ); 1010converter .check_errors (); 1011return converter .format_grammar (); 1012} 1013 1014std::string build_grammar (const std::function < void (const common_grammar_builder & )> & cb ,const common_grammar_options & options ) { 1015common_chat_schema_converter converter (options .dotall ); 1016common_grammar_builder builder { 1017/* .add_rule = */ [& ](const std::string & name ,const std::string & rule ) { 1018return converter ._add_rule (name ,rule ); 1019 }, 1020/* .add_schema = */ [& ](const std::string & name ,const common_chat_schema & schema ) { 1021return converter .add_schema (name == "root" ?"" :name ,schema ); 1022 }, 1023 }; 1024cb (builder ); 1025converter .check_errors (); 1026return converter .format_grammar (); 1027}