Skip to content

Commit 6f2c8e8

Browse files
ochafikNeoZhangJianyu
authored andcommitted
json: better support for "type" unions (e.g. nullable arrays w/ typed items) (ggml-org#7863)
* json: better suport for "type" arrays (e.g. `{"type": ["array", "null"], "items": {"type": "string"}}`) * json: add test for type: [array, null] fix * update tests
1 parent 65c74a8 commit 6f2c8e8

5 files changed

+62
-3
lines changed

common/json-schema-to-grammar.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,9 @@ class SchemaConverter {
893893
} else if (schema_type.is_array()) {
894894
std::vector<json> schema_types;
895895
for (const auto & t : schema_type) {
896-
schema_types.push_back({{"type", t}});
896+
json schema_copy(schema);
897+
schema_copy["type"] = t;
898+
schema_types.push_back(schema_copy);
897899
}
898900
return _add_rule(rule_name, _generate_union_rule(name, schema_types));
899901
} else if (schema.contains("const")) {

examples/json_schema_to_grammar.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ def visit(self, schema, name):
565565
return self._add_rule(rule_name, self._generate_union_rule(name, schema.get('oneOf') or schema['anyOf']))
566566

567567
elif isinstance(schema_type, list):
568-
return self._add_rule(rule_name, self._generate_union_rule(name, [{'type': t} for t in schema_type]))
568+
return self._add_rule(rule_name, self._generate_union_rule(name, [{**schema, 'type': t} for t in schema_type]))
569569

570570
elif 'const' in schema:
571571
return self._add_rule(rule_name, self._generate_constant_rule(schema['const']) + ' space')

examples/server/public/json-schema-to-grammar.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ export class SchemaConverter {
616616
} else if (schema.oneOf || schema.anyOf) {
617617
return this._addRule(ruleName, this._generateUnionRule(name, schema.oneOf || schema.anyOf));
618618
} else if (Array.isArray(schemaType)) {
619-
return this._addRule(ruleName, this._generateUnionRule(name, schemaType.map(t => ({ type: t }))));
619+
return this._addRule(ruleName, this._generateUnionRule(name, schemaType.map(t => ({...schema, type: t}))));
620620
} else if ('const' in schema) {
621621
return this._addRule(ruleName, this._generateConstantRule(schema.const) + ' space');
622622
} else if ('enum' in schema) {

tests/test-grammar-integration.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,31 @@ static void test_json_schema() {
993993
}
994994
);
995995

996+
test_schema(
997+
"",
998+
// Schema
999+
R"""(
1000+
{
1001+
"type": ["array", "null"],
1002+
"items": { "type": "string" }
1003+
}
1004+
)""",
1005+
// Passing strings
1006+
{
1007+
"null",
1008+
"[]",
1009+
"[\"123\"]",
1010+
"[\"foo\", \"bar\"]",
1011+
},
1012+
// Failing strings
1013+
{
1014+
"",
1015+
"[123]",
1016+
"\"foo\"",
1017+
"[\"foo\", 42]",
1018+
}
1019+
);
1020+
9961021
test_schema(
9971022
"min+max items",
9981023
// Schema

tests/test-json-schema-to-grammar.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,38 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
502502
)"""
503503
});
504504

505+
test({
506+
SUCCESS,
507+
"string array",
508+
R"""({
509+
"type": "array",
510+
"prefixItems": { "type": "string" }
511+
})""",
512+
R"""(
513+
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
514+
root ::= "[" space (string ("," space string)*)? "]" space
515+
space ::= | " " | "\n" [ \t]{0,20}
516+
string ::= "\"" char* "\"" space
517+
)"""
518+
});
519+
520+
test({
521+
SUCCESS,
522+
"nullable string array",
523+
R"""({
524+
"type": ["array", "null"],
525+
"prefixItems": { "type": "string" }
526+
})""",
527+
R"""(
528+
alternative-0 ::= "[" space (string ("," space string)*)? "]" space
529+
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
530+
null ::= "null" space
531+
root ::= alternative-0 | null
532+
space ::= | " " | "\n" [ \t]{0,20}
533+
string ::= "\"" char* "\"" space
534+
)"""
535+
});
536+
505537
test({
506538
SUCCESS,
507539
"tuple1",

0 commit comments

Comments
 (0)