@fugood/llama.node 0.3.8 → 0.3.10

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