char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
147a52163
char/aliceai-t5-moe
1#ifdef NDEBUG 2# undef NDEBUG 3#endif 4 5#include "sampling.h" 6 7#include <cassert> 8#include <string> 9#include <vector> 10 11static const llama_vocab * vocab ; 12 13static bool match_string (const std::string & input ,llama_sampler * grammar ) { 14llama_sampler_reset (grammar ); 15auto tokens = common_tokenize (vocab ,input , false, false); 16 17auto n_vocab = llama_vocab_n_tokens (vocab ); 18 19 std::vector < llama_token_data > cur ; 20cur .reserve (n_vocab ); 21for (llama_token token_id = 0 ;token_id < (llama_token )n_vocab ;token_id ++ ) { 22cur .emplace_back (llama_token_data {token_id ,0.0f ,0.0f }); 23 } 24auto tok_arr = llama_token_data_array {cur .data (),cur .size (),-1 , false }; 25 26for (const auto token :tokens ) { 27for (llama_token token_id = 0 ;token_id < (llama_token )n_vocab ;token_id ++ ) { 28cur [token_id ].logit = 0.0f ; 29 } 30llama_sampler_apply (grammar ,& tok_arr ); 31if (cur [token ].logit < 0.0f ) { 32return false; 33 } 34llama_sampler_accept (grammar ,token ); 35 } 36 37// do we allow EOS at the end? if so the grammar is accepting 38 39auto tok_eos = llama_vocab_eot (vocab ); 40if (tok_eos == LLAMA_TOKEN_NULL ) { 41tok_eos = llama_vocab_eos (vocab ); 42 } 43 44cur [tok_eos ].logit = 0.0f ; 45llama_sampler_apply (grammar ,& tok_arr ); 46 47return cur [tok_eos ].logit >=0.0f ; 48} 49 50static void test (const std::string & test_desc ,const std::string & grammar_str , 51const std::vector < std::string > & passing_strings ,const std::vector < std::string > & failing_strings ) { 52fprintf (stderr ,"⚫ Testing %s\n%s\n" ,test_desc .c_str (),grammar_str .c_str ()); 53fflush (stderr ); 54 55auto * grammar = llama_sampler_init_llg (vocab ,"lark" ,grammar_str .c_str ()); 56 57fprintf (stderr ," 🔵 Valid strings:\n" ); 58 59// Passing strings 60for (const auto & test_string :passing_strings ) { 61fprintf (stderr ," \"%s\" " ,test_string .c_str ()); 62fflush (stderr ); 63 64bool matched = match_string (test_string ,grammar ); 65 66if (!matched ) { 67fprintf (stderr ,"❌ (failed to match)\n" ); 68 69// DEBUG: Write strings to files so that we can analyze more easily with gbnf-validator program to see exactly where things failed. 70// DEBUG: Write the grammar_str to test-grammar-integration.grammar.gbnf 71FILE * grammar_file = fopen ("test-grammar-integration.grammar.gbnf" ,"w" ); 72if (grammar_file ) { 73fprintf (grammar_file ,"%s" ,grammar_str .c_str ()); 74fclose (grammar_file ); 75 } 76 77// DEBUG: Write the test string to test-grammar-integration.string.txt 78FILE * string_file = fopen ("test-grammar-integration.string.txt" ,"w" ); 79if (string_file ) { 80fprintf (string_file ,"%s" ,test_string .c_str ()); 81fclose (string_file ); 82 } 83 84fprintf (stderr , 85"\n NOTE: Debug grammar file generated. To analyze this failure in detail, run the following " 86"command: ./test-gbnf-validator test-grammar-integration.grammar.gbnf " 87"test-grammar-integration.string.txt\n\n" ); 88 }else { 89fprintf (stdout ,"✅︎\n" ); 90 } 91 92assert (matched ); 93 } 94 95fprintf (stderr ," 🟠 Invalid strings:\n" ); 96 97// Failing strings 98for (const auto & test_string :failing_strings ) { 99fprintf (stderr ," \"%s\" " ,test_string .c_str ()); 100fflush (stderr ); 101 102bool matched = match_string (test_string ,grammar ); 103 104if (matched ) { 105fprintf (stderr ,"❌ (incorrectly matched)\n" ); 106 }else { 107fprintf (stdout ,"✅︎\n" ); 108 } 109assert (!matched ); 110 } 111 112llama_sampler_free (grammar ); 113} 114 115static void test_grammar (const std::string & test_desc ,const std::string & grammar_str , 116const std::vector < std::string > & passing_strings , 117const std::vector < std::string > & failing_strings ) { 118test (test_desc + ". Grammar: " + grammar_str ,grammar_str ,passing_strings ,failing_strings ); 119} 120 121static void test_schema (const std::string & test_desc ,const std::string & schema_str , 122const std::vector < std::string > & passing_strings , 123const std::vector < std::string > & failing_strings ) { 124test (test_desc + ". Schema: " + schema_str ,"%llguidance {}\nstart: %json " + schema_str ,passing_strings , 125failing_strings ); 126} 127 128static void test_simple_grammar () { 129test_schema ("min 0" , 130R"""({ 131"type": "integer", 132"minimum": 0 133})""" , 134// Passing strings 135 { 136"0" , 137"10" , 138"12" , 139"10000" , 140 }, 141// Failing strings 142 { 143"-1" , 144"-10" , 145"-10000" , 146"-100000000000000000000000000000000" , 147// "100000000000000000000000000000000", 148"00" , 149"01" , 150"-0" , 151 }); 152test_schema ("min 2" , 153// Schema 154R"""({ 155"type": "integer", 156"minimum": 2 157})""" , 158// Passing strings 159 { 160"2" , 161"3" , 162"4" , 163"10" , 164"20" , 165"1234567890000000" , 166 }, 167// Failing strings 168 { 169"0" ,"1" ,"-1" ,"-100" ,"0" ,"1" ,"01" ,"02" , 170// "12345678900000000", 171 }); 172test_schema ("min 456" , 173R"""({ 174"type": "integer", 175"minimum": 456 176})""" , 177// Passing strings 178 { 179"456" , 180"4560" , 181"457" , 182"460" , 183"500" , 184 }, 185// Failing strings 186 { 187"455" , 188"356" , 189"50" , 190"050" , 191"-1" , 192"-456" , 193 }); 194test_schema ("min -123" , 195R"""({ 196"type": "integer", 197"minimum": -123 198})""" , 199// Passing strings 200 { 201"-123" , 202"-122" , 203"-11" , 204"-1" , 205"0" , 206"1" , 207"123" , 208"1234" , 209"2345" , 210 }, 211// Failing strings 212 { 213"-1234" , 214"-124" , 215 }); 216 217test_schema ("max 9999" , 218// Schema 219R"""({ 220"type": "integer", 221"maximum": 9999 222})""" , 223// Passing strings 224 { 225"-99999" , 226"0" , 227"9999" , 228 }, 229// Failing strings 230 { 231"10000" , 232"99991" , 233 }); 234test_schema ("max -9999" , 235// Schema 236R"""({ 237"type": "integer", 238"maximum": -9999 239})""" , 240// Passing strings 241 { 242"-10000" , 243"-9999" , 244 }, 245// Failing strings 246 { 247"-9998" , 248"0" , 249"9999" , 250 }); 251test_schema ("min 5 max 30" , 252// Schema 253R"""({ 254"type": "integer", 255"minimum": 5, 256"maximum": 30 257})""" , 258// Passing strings 259 { 260"5" , 261"10" , 262"30" , 263 }, 264// Failing strings 265 { 266"05" , 267"4" , 268"-1" , 269"31" , 270"123" , 271"0123" , 272 }); 273test_schema ("min -1 max 1" , 274R"""({ 275"type": "integer", 276"minimum": -1, 277"maximum": 1 278})""" , 279// Passing strings 280 { 281"-1" , 282"0" , 283"1" , 284 }, 285// Failing strings 286 { 287"-11" , 288"-10" , 289"-2" , 290"2" , 291"10" , 292"11" , 293 }); 294test_schema ("min -123 max 42" , 295R"""({ 296"type": "integer", 297"minimum": -123, 298"maximum": 42 299})""" , 300// Passing strings 301 { 302"-123" , 303"-122" , 304"-13" , 305"-11" , 306"-2" , 307"-1" , 308"0" , 309"1" , 310"5" , 311"10" , 312"39" , 313"40" , 314"42" , 315 }, 316// Failing strings 317 { 318"-0123" , 319"-124" , 320"-1123" , 321"-200" , 322"43" , 323"123" , 324"0123" , 325 }); 326test_schema ("exclusive min / max" , 327// Schema 328R"""({ 329"type": "integer", 330"exclusiveMinimum": 0, 331"exclusiveMaximum": 10000 332})""" , 333// Passing strings 334 { 335"1" , 336"9999" , 337 }, 338// Failing strings 339 { 340"0" , 341"01" , 342"10000" , 343"99999" , 344 }); 345 346// Test case for a simple grammar 347test_grammar ("simple grammar" , 348R"""( 349start: expr 350expr: term ("+" term)* 351term: number 352number: /[0-9]+/ )""" , 353// Passing strings 354 { 355"42" , 356"1+2+3+4+5" , 357"123+456" , 358 }, 359// Failing strings 360 { 361"+" , 362"/ 3" , 363"1+2+3+4+5+" , 364"12a45" , 365 }); 366} 367 368static void test_complex_grammar () { 369// Test case for a more complex grammar, with both failure strings and success strings 370test_grammar ("medium complexity grammar" , 371// Grammar 372R"""( 373start: expression 374expression: term ws (("+"|"-") ws term)* 375term: factor ws (("*"|"/") ws factor)* 376factor: number | variable | "(" expression ")" | function-call 377number: /[0-9]+/ 378variable: /[a-zA-Z_][a-zA-Z0-9_]*/ 379function-call: variable ws "(" (expression ("," ws expression)*)? ")" 380ws: /[ \t\n\r]?/ )""" , 381// Passing strings 382 {"42" , 383"1*2*3*4*5" , 384"x" , 385"x+10" , 386"x1+y2" , 387"(a+b)*(c-d)" , 388"func()" , 389"func(x,y+2)" , 390"a*(b+c)-d/e" , 391"f(g(x),h(y,z))" , 392"x + 10" , 393"x1 + y2" , 394"(a + b) * (c - d)" , 395"func()" , 396"func(x, y + 2)" , 397"a * (b + c) - d / e" , 398"f(g(x), h(y, z))" , 399"123+456" , 400"123*456*789-123/456+789*123" , 401"123+456*789-123/456+789*123-456/789+123*456-789/123+456*789-123/456+789*123-456" }, 402// Failing strings 403 { 404"+" , 405"/ 3x" , 406"x + + y" , 407"a * / b" , 408"func(,)" , 409"func(x y)" , 410"(a + b" , 411"x + y)" , 412"a + b * (c - d" , 413"42 +" , 414"x +" , 415"x + 10 +" , 416"(a + b) * (c - d" , 417"func(" , 418"func(x, y + 2" , 419"a * (b + c) - d /" , 420"f(g(x), h(y, z)" , 421"123+456*789-123/456+789*123-456/789+123*456-789/123+456*789-123/456+789*123-456/" , 422 }); 423} 424 425static void test_special_chars () { 426// A collection of tests to exercise special characters such as "." 427test_grammar ("special characters" , 428// Grammar 429R"""( 430start: /.../ "abc" /.../ 431)""" , 432// Passing strings 433 {"abcabcabc" ,"aaaabcccc" , 434// NOTE: Also ensures that multi-byte characters still count as a single character 435"🔵🟠✅abc❌🟠🔵" }, 436// Failing strings 437 {"aaabcccc" ,"aaaaabcccc" ,"aaaabccc" ,"aaaabccccc" ,"🔵🟠✅❌abc❌✅🟠🔵" ,"🔵🟠abc🟠🔵" }); 438} 439 440static void test_quantifiers () { 441// A collection of tests to exercise * + and ? quantifiers 442 443test_grammar ( 444"* quantifier" , 445// Grammar 446R"""(start: "a"*)""" , 447// Passing strings 448 {"" ,"a" ,"aaaaa" ,"aaaaaaaaaaaaaaaaaa" ,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, 449// Failing strings 450 {"b" ,"ab" ,"aab" ,"ba" ,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" }); 451test_grammar ( 452"+ quantifier" , 453// Grammar 454R"""(start: "a"+)""" , 455// Passing strings 456 {"a" ,"aaaaa" ,"aaaaaaaaaaaaaaaaaa" ,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, 457// Failing strings 458 {"" ,"b" ,"ab" ,"aab" ,"ba" ,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" }); 459test_grammar ("? quantifier" , 460// Grammar 461R"""(start: "a"?)""" , 462// Passing strings 463 {"" ,"a" }, 464// Failing strings 465 { 466"b" , 467"ab" , 468"aa" , 469"ba" , 470 }); 471test_grammar ("mixed quantifiers" , 472// Grammar 473R"""( 474start: cons+ vowel* cons? (vowel cons)* 475vowel: /[aeiouy]/ 476cons: /[bcdfghjklmnpqrstvwxyz]/ 477)""" , 478// Passing strings 479 { 480"yes" , 481"no" , 482"noyes" , 483"crwth" , 484"four" , 485"bryyyy" , 486 }, 487// Failing strings 488 { 489"yess" , 490"yesno" , 491"forty" , 492"catyyy" , 493 }); 494test_grammar ("simple exact repetition" , 495// Grammar 496R"""( 497start: /[ab]{4}/ 498)""" , 499// Passing strings 500 { 501"aaaa" , 502"bbbb" , 503"abab" , 504 }, 505// Failing strings 506 { 507"a" , 508"b" , 509"aaaaa" , 510 }); 511test_grammar ("simple min repetition" , 512// Grammar 513R"""( 514start: /[ab]{4,}/ 515)""" , 516// Passing strings 517 { 518"aaaa" , 519"aaaaab" , 520"bbbb" , 521"ababab" , 522 }, 523// Failing strings 524 { 525"" , 526"aba" , 527 }); 528test_grammar ("simple max repetition" , 529// Grammar 530R"""( 531start: /[ab]{0,4}/ 532)""" , 533// Passing strings 534 { 535"" , 536"a" , 537"aa" , 538"aaa" , 539"aaab" , 540 }, 541// Failing strings 542 { 543"aaaaa" , 544 }); 545// test_grammar("min / max repetition", 546// // Grammar 547// R"""( 548// start: ("0x" /[A-F0-9]{2}/ " "?){3,5} 549// )""", 550// // Passing strings 551// { 552// "0xFF 0x12 0xAB", 553// "0xFF 0x12 0xAB 0x00 0x00", 554// }, 555// // Failing strings 556// { 557// "", 558// "0xFF", 559// "0xFF 0x12", 560// "0xFF 0x12 0xAB 0x00 0x00 0x00", 561// }); 562} 563 564static void test_json_schema () { 565// Note that this is similar to the regular grammar tests, 566// but we convert each json schema to a grammar before parsing. 567// Otherwise, this test structure is the same. 568 569test_schema ("empty schema (object)" , 570// Schema 571R"""( 572{"type":"object"} 573)""" , 574// Passing strings 575 { 576R"""({})""" , 577R"""({"foo": "bar"})""" , 578 }, 579// Failing strings 580 { 581"" , 582"[]" , 583"null" , 584R"""("")""" , 585"true" , 586 }); 587 588test_schema ( 589"exotic formats (list)" , 590// Schema 591R"""({ 592"items": [ 593{ "format": "date" }, 594{ "format": "uuid" }, 595{ "format": "time" }, 596{ "format": "date-time" } 597] 598})""" , 599// Passing strings 600 { 601// "{}", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it? 602// "[]", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it? 603R"""(["2012-04-23", "12345678-1234-1234-1234-1234567890ab", "18:25:43.511Z", "2012-04-23T18:25:43.511Z"])""" , 604//R"""(["2012-04-23","12345678-1234-1234-1234-1234567890ab"])""", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it? 605//R"""({"foo": "bar"})""", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it? 606 }, 607// Failing strings 608 { 609R"""(["foo", "bar"])""" , 610R"""(["12345678-1234-1234-1234-1234567890ab"])""" , 611 }); 612 613test_schema ("string" , 614// Schema 615R"""({ 616"type": "string" 617})""" , 618// Passing strings 619 { 620R"""("foo")""" , 621R"""("bar")""" , 622R"""("")""" , 623 }, 624// Failing strings 625 { 626R"""({})""" , 627R"""("foo": "bar")""" , 628 }); 629 630test_schema ("string w/ min length 1" , 631// Schema 632R"""({ 633"type": "string", 634"minLength": 1 635})""" , 636// Passing strings 637 { 638R"""("foo")""" , 639R"""("bar")""" , 640 }, 641// Failing strings 642 { 643R"""("")""" , 644R"""({})""" , 645R"""("foo": "bar")""" , 646 }); 647 648test_schema ("string w/ min length 3" , 649// Schema 650R"""({ 651"type": "string", 652"minLength": 3 653})""" , 654// Passing strings 655 { 656R"""("foo")""" , 657R"""("bar")""" , 658R"""("foobar")""" , 659 }, 660// Failing strings 661 { 662R"""("")""" , 663R"""("f")""" , 664R"""("fo")""" , 665 }); 666 667test_schema ("string w/ max length" , 668// Schema 669R"""({ 670"type": "string", 671"maxLength": 3 672})""" , 673// Passing strings 674 { 675R"""("foo")""" , 676R"""("bar")""" , 677R"""("")""" , 678R"""("f")""" , 679R"""("fo")""" , 680 }, 681// Failing strings 682 { 683R"""("foobar")""" , 684 }); 685 686test_schema ("string w/ min & max length" , 687// Schema 688R"""({ 689"type": "string", 690"minLength": 1, 691"maxLength": 4 692})""" , 693// Passing strings 694 { 695R"""("foo")""" , 696R"""("bar")""" , 697R"""("f")""" , 698R"""("barf")""" , 699 }, 700// Failing strings 701 { 702R"""("")""" , 703R"""("barfo")""" , 704R"""("foobar")""" , 705 }); 706 707test_schema ("boolean" , 708// Schema 709R"""({ 710"type": "boolean" 711})""" , 712// Passing strings 713 { 714"true" , 715"false" , 716 }, 717// Failing strings 718 { 719R"""("")""" , 720R"""("true")""" , 721R"""(True)""" , 722R"""(FALSE)""" , 723 }); 724 725test_schema ("integer" , 726// Schema 727R"""({ 728"type": "integer" 729})""" , 730// Passing strings 731 { 732R"""(0)""" , 733R"""(12345)""" , 734R"""(1234567890123456)""" , 735 }, 736// Failing strings 737 { 738R"""()""" , 739R"""(01)""" , 740R"""(007)""" , 741R"""(12345678901234567 )""" , 742 }); 743 744test_schema ("string const" , 745// Schema 746R"""({ 747"const": "foo" 748})""" , 749// Passing strings 750 { 751R"""("foo")""" , 752 }, 753// Failing strings 754 { 755R"""(foo)""" , 756R"""("bar")""" , 757 }); 758 759test_schema ("non-string const" , 760// Schema 761R"""({ 762"const": true 763})""" , 764// Passing strings 765 { 766R"""(true)""" , 767 }, 768// Failing strings 769 { 770R"""()""" , 771R"""(foo)""" , 772R"""("true")""" , 773 }); 774 775test_schema ("non-string const" , 776// Schema 777R"""({ 778"enum": ["red", "amber", "green", null, 42, ["foo"]] 779})""" , 780// Passing strings 781 { 782R"""("red")""" , 783R"""(null)""" , 784R"""(42)""" , 785R"""(["foo"])""" , 786 }, 787// Failing strings 788 { 789R"""()""" , 790R"""(420)""" , 791R"""(true)""" , 792R"""(foo)""" , 793 }); 794 795test_schema ("simple pattern" , 796// Schema 797R"""({ 798"pattern": "^[a-zA-Z0-9_-]*$" 799})""" , 800// Passing strings 801 { 802R"""("")""" , 803R"""("He_llo-12")""" , 804 }, 805// Failing strings 806 { 807R"""("!")""" , 808R"""("Hello World")""" , 809 }); 810 811test_schema ("pattern with escapes" , 812// Schema 813R"""({ 814"pattern": "^a\\^\\$\\.\\[\\]\\(\\)\\|\\{\\}\\*\\+\\?b$" 815})""" , 816// Passing strings 817 { 818R"""("a^$.[]()|{}*+?b")""" , 819 }, 820// Failing strings 821 { 822R"""("ab")""" , 823 }); 824 825test_schema ("" , 826// Schema 827R"""( 828{ 829"type": ["array", "null"], 830"items": { "type": "string" } 831} 832)""" , 833// Passing strings 834 { 835"null" , 836"[]" , 837"[\"123\"]" , 838"[\"foo\", \"bar\"]" , 839 }, 840// Failing strings 841 { 842"" , 843"[123]" , 844"\"foo\"" , 845"[\"foo\", 42]" , 846 }); 847 848test_schema ("min+max items" , 849// Schema 850R"""({ 851"items": { 852"type": ["number", "integer"] 853}, 854"minItems": 3, 855"maxItems": 5 856})""" , 857// Passing strings 858 { 859R"""([1, 2, 3])""" , 860R"""([1, 2, 3, 4])""" , 861R"""([1, 2, 3, 4, 5])""" , 862// this is in fact correct; keyword do not apply if the type is wrong 863R"""(1)""" , 864 }, 865// Failing strings 866 { 867R"""([1, 2])""" , 868R"""([1, 2, 3, 4, 5, 6])""" , 869 }); 870 871// Properties (from: https://json-schema.org/understanding-json-schema/reference/object#properties) 872test_schema ("object properties" , 873// Schema 874R"""({ 875"type": "object", 876"properties": { 877"number": { "type": "number" }, 878"street_name": { "type": "string" }, 879"street_type": { "enum": ["Street", "Avenue", "Boulevard"] } 880}, 881"additionalProperties": false 882})""" , 883// Passing strings 884 { 885R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue"})""" , 886// "By default, leaving out properties is valid" 887R"""({ "street_name": "Pennsylvania" })""" , 888R"""({ "number": 1600, "street_name": "Pennsylvania" })""" , 889// "By extension, even an empty object is valid" 890R"""({})""" , 891R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" })""" , 892 }, 893// Failing strings 894 { 895// Change datatype from number to string 896R"""({ "number": "1600", "street_name": "Pennsylvania", "street_type":"Avenue"})""" , 897// Reorder properties 898R"""({ "street_name": "Pennsylvania", "number": 1600 })""" , 899// Reorder properties 900R"""({ "number": "1600", "street_name": "Pennsylvania", "street_type":"Avenue"})""" , 901// Additional properties set to false 902R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue", "direction":"NW"})""" , 903 904 }); 905 906test_schema ("additional properties can't override other properties" , 907R"""({ 908"properties": { 909"a": {"type": "integer"}, 910"b": {"type": "integer"} 911}, 912"additionalProperties": true 913})""" , 914// Passing strings 915 { 916R"""({"a": 42})""" , 917R"""({"c": ""})""" , 918R"""({"a": 42, "c": ""})""" , 919R"""({"a_": ""})""" , 920 }, 921// Failing strings 922 { 923R"""()""" , 924R"""({"a": ""})""" , 925R"""({"a": "", "b": ""})""" , 926 }); 927 928// Properties (from: https://json-schema.org/understanding-json-schema/reference/object#properties) 929test_schema ("object properties, additionalProperties: true" , 930// Schema 931R"""({ 932"type": "object", 933"properties": { 934"number": { "type": "number" }, 935"street_name": { "type": "string" }, 936"street_type": { "enum": ["Street", "Avenue", "Boulevard"] } 937}, 938"additionalProperties": true 939})""" , 940// Passing strings 941 { 942// "By extension, even an empty object is valid" 943R"""({})""" , 944R"""({"number":1600,"street_name":"Pennsylvania","street_type":"Avenue"})""" , 945// "By default, leaving out properties is valid" 946R"""({ "street_name": "Pennsylvania" })""" , 947R"""({ "number": 1600, "street_name": "Pennsylvania" })""" , 948// "By default, providing additional properties is valid" 949R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue", "direction":"NW"})""" , 950R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" })""" , 951 }, 952// Failing strings 953 { 954// Change datatype from number to string 955R"""({ "number": "1600", "street_name": "Pennsylvania", "street_type":"Avenue"})""" , 956// Reorder properties 957R"""({ "street_name": "Pennsylvania", "number": 1600, "street_type":"Avenue"})""" , 958 }); 959 960// Additional properties: false 961test_schema ( 962"required + optional props each in original order" , 963// Schema 964R"""({ 965"type": "object", 966"properties": { 967"number": { "type": "number" }, 968"street_name": { "type": "string" }, 969"street_type": { "enum": ["Street", "Avenue", "Boulevard"] } 970}, 971"additionalProperties": false 972})""" , 973// Passing strings 974 { 975R"""({ "street_name": "Pennsylvania" })""" , 976R"""({ "number": 1600, "street_type":"Avenue"})""" , 977R"""({ "number": 1600, "street_name": "Pennsylvania" })""" , 978R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue"})""" , 979// Spaces are permitted around enum values 980R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" })""" , 981 }, 982// Failing strings 983 { 984// Reorder properties 985R"""({ "street_type": "Avenue", "number": 1600 })""" , 986// Add "direction" 987R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" })""" , 988 }); 989 990test_schema ("required + optional props each in original order" , 991// Schema 992R"""({ 993"properties": { 994"b": {"type": "string"}, 995"a": {"type": "string"}, 996"d": {"type": "string"}, 997"c": {"type": "string"} 998}, 999"required": ["a", "b"], 1000"additionalProperties": false 1001})""" , 1002// Passing strings 1003 { 1004R"""({"b": "foo", "a": "bar"})""" , 1005R"""({"b":"foo","a":"bar","d":"qux"})""" , 1006R"""({"b":"foo", "a":"bar", "d":"qux", "c":"baz"})""" , 1007 }, 1008// Failing strings 1009 { 1010R"""({"a": "foo", "b": "bar"})""" , 1011R"""({"b": "bar"})""" , 1012R"""({"a": "foo", "c": "baz"})""" , 1013R"""({"a":"foo", "b":"bar", "c":"baz", "d":"qux"})""" , 1014 }); 1015 1016// NOTE: Example from https://json-schema.org/learn/getting-started-step-by-step#define-required-properties 1017test_schema ( 1018"required props" , 1019// Schema 1020R"""({ 1021"$schema": "https://json-schema.org/draft/2020-12/schema", 1022"$id": "https://example.com/product.schema.json", 1023"title": "Product", 1024"description": "A product from Acme's catalog", 1025"type": "object", 1026"properties": { 1027"productId": { 1028"description": "The unique identifier for a product", 1029"type": "integer" 1030}, 1031"productName": { 1032"description": "Name of the product", 1033"type": "string" 1034}, 1035"price": { 1036"description": "The price of the product", 1037"type": "number", 1038"exclusiveMinimum": 0 1039}, 1040"tags": { 1041"description": "Tags for the product", 1042"type": "array", 1043"items": { 1044"type": "string" 1045}, 1046"minItems": 1, 1047"DISABLED_uniqueItems": true 1048}, 1049"dimensions": { 1050"type": "object", 1051"properties": { 1052"length": { 1053"type": "number" 1054}, 1055"width": { 1056"type": "number" 1057}, 1058"height": { 1059"type": "number" 1060} 1061}, 1062"required": [ "length", "width", "height" ] 1063} 1064}, 1065"required": [ "productId", "productName", "price" ] 1066})""" , 1067// Passing strings 1068 { 1069R"""({"productId": 1, "productName": "A green door", "price": 12.50})""" , 1070R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": ["home", "green"]})""" , 1071R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": ["home", "green"], "dimensions": {"length": 785, "width": 250.5, "height": -0.359}})""" , 1072 }, 1073// Failing strings 1074 { 1075R"""({})""" ,// Missing all required properties 1076R"""({"productName": "A green door", "price": 12.50, "productId": 1})""" ,// Out of order properties 1077// `exclusiveMinimum` is OK for llg 1078R"""({"productId": 1, "productName": "A green door", "price": -12.50})""" , 1079R"""({"productId": 1, "productName": "A green door"})""" ,// Missing required property (price) 1080R"""({"productName": "A green door", "price": 12.50})""" ,// Missing required property (productId) 1081R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": []})""" ,// tags is empty, but minItems is 1 1082R"""({"productId": 1, "productName": "A green door", "price": 12.50, "dimensions": {"length": 785, "width": 250.5, "height": -0.359}, "tags": ["home", "green"]})""" ,// Tags and dimensions are out of order 1083// TODO: The following line should fail, but currently it passes. `uniqueItems` is not supported, as it would likely be too difficult to implement. 1084// R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": ["home", "green", "home"]})""", 1085 }); 1086} 1087 1088static void one_hot (llama_token_data_array & tok_arr ,llama_token selected ) { 1089auto n_vocab = tok_arr .size ; 1090 1091tok_arr .selected = -1 ; 1092tok_arr .sorted = false; 1093for (llama_token token_id = 0 ;token_id < (llama_token )n_vocab ;token_id ++ ) { 1094tok_arr .data [token_id ].id = token_id ; 1095tok_arr .data [token_id ].logit = 0.0f ; 1096 } 1097 1098tok_arr .data [selected ].logit = 100.0f ; 1099} 1100 1101static void test_sampler_chain (void ) { 1102auto sparams = llama_sampler_chain_default_params (); 1103sparams .no_perf = false; 1104llama_sampler * sampler = llama_sampler_chain_init (sparams ); 1105 1106const auto grammar_data = R"(%llguidance {} 1107start: /[A-Z ]*/)" ; 1108 1109llama_sampler_chain_add (sampler ,llama_sampler_init_llg (vocab ,"lark" ,grammar_data )); 1110llama_sampler_chain_add (sampler ,llama_sampler_init_dist (42 )); 1111 1112auto input = "ALL YOUR BASE ARE BELONG TO US" ; 1113auto tokens = common_tokenize (vocab ,input , false, false); 1114 1115auto n_vocab = llama_vocab_n_tokens (vocab ); 1116 1117 std::vector < llama_token_data > cur ; 1118cur .reserve (n_vocab ); 1119for (llama_token token_id = 0 ;token_id < (llama_token )n_vocab ;token_id ++ ) { 1120cur .emplace_back (llama_token_data {token_id ,0.0f ,0.0f }); 1121 } 1122auto tok_arr = llama_token_data_array {cur .data (),cur .size (),-1 , false }; 1123 1124for (const auto token :tokens ) { 1125one_hot (tok_arr ,token ); 1126 1127fprintf (stderr ,"applying token: %d\n" ,token ); 1128llama_sampler_apply (sampler ,& tok_arr ); 1129 1130auto idx = tok_arr .selected ; 1131fprintf (stderr ," -> %d %f\n" ,cur [idx ].id ,cur [idx ].logit ); 1132assert (cur [tok_arr .selected ].id == token ); 1133llama_sampler_accept (sampler ,token ); 1134 } 1135 1136auto tok_eos = llama_vocab_eot (vocab ); 1137if (tok_eos == LLAMA_TOKEN_NULL ) { 1138tok_eos = llama_vocab_eos (vocab ); 1139 } 1140 1141one_hot (tok_arr ,tok_eos ); 1142 1143llama_sampler_apply (sampler ,& tok_arr ); 1144assert (cur [tok_arr .selected ].id == tok_eos ); 1145} 1146 1147int main (int argc ,const char ** argv ) { 1148fprintf (stdout ,"Running llguidance integration tests...\n" ); 1149 1150if (argc != 2 ) { 1151fprintf (stderr ,"Usage: %s <vocab-file>\n" ,argv [0 ]); 1152return 1 ; 1153 } 1154 1155const char * vocab_file = argv [1 ]; 1156 1157fprintf (stderr ,"reading vocab from: '%s'\n" ,vocab_file ); 1158 1159llama_model * model ; 1160llama_context * ctx ; 1161 1162llama_backend_init (); 1163 1164// load the vocab 1165 { 1166auto mparams = llama_model_default_params (); 1167 1168mparams .vocab_only = true; 1169 1170model = llama_model_load_from_file (vocab_file ,mparams ); 1171 1172if (model == NULL ) { 1173fprintf (stderr ,"%s: error: failed to load vocab '%s'\n" ,__func__ ,vocab_file ); 1174return 1 ; 1175 } 1176 1177// needed? 1178auto cparams = llama_context_default_params (); 1179 1180ctx = llama_init_from_model (model ,cparams ); 1181 1182if (ctx == NULL ) { 1183fprintf (stderr ,"%s: error: failed to load vocab '%s'\n" ,__func__ ,vocab_file ); 1184llama_model_free (model ); 1185return 1 ; 1186 } 1187 } 1188 1189vocab = llama_model_get_vocab (model ); 1190 1191test_simple_grammar (); 1192test_complex_grammar (); 1193test_special_chars (); 1194test_quantifiers (); 1195test_json_schema (); 1196 1197test_sampler_chain (); 1198 1199llama_free (ctx ); 1200llama_model_free (model ); 1201 1202fprintf (stdout ,"All tests passed.\n" ); 1203return 0 ; 1204}