From c751a1950293cc48e18a942942e55874252c1fcf Mon Sep 17 00:00:00 2001 From: tuanzirwar <1281227988@qq.com> Date: Thu, 1 Oct 2026 21:55:37 +0800 Subject: [PATCH 1/2] fix: support unconstrained array item schemas --- CHANGELOG.md | 2 ++ llama_cpp/llama_grammar.py | 9 +++++-- tests/test_llama_grammar.py | 53 +++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c34e2729e..fb83871499 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- fix: support unconstrained array item schemas by @tuanzirwar (PR pending) + ## [0.3.36] - feat: update llama.cpp to ggml-org/llama.cpp@0c1e57098 diff --git a/llama_cpp/llama_grammar.py b/llama_cpp/llama_grammar.py index ba34dda831..3efcfc2c9d 100644 --- a/llama_cpp/llama_grammar.py +++ b/llama_cpp/llama_grammar.py @@ -771,7 +771,9 @@ def add_component(comp_schema, is_required): elif schema_type in (None, "array") and ( "items" in schema or "prefixItems" in schema ): - items = schema.get("items") or schema["prefixItems"] + items = schema.get("items") or schema.get("prefixItems") + if items is None: + items = schema["items"] if isinstance(items, list): return self._add_rule( rule_name, @@ -830,7 +832,10 @@ def add_component(comp_schema, is_required): + r' "\"" space', ) - elif (schema_type == "object") or (len(schema) == 0): + elif len(schema) == 0: + return self._add_primitive(rule_name, PRIMITIVE_RULES["value"]) + + elif schema_type == "object": return self._add_rule( rule_name, self._add_primitive("object", PRIMITIVE_RULES["object"]) ) diff --git a/tests/test_llama_grammar.py b/tests/test_llama_grammar.py index 34ef2874df..7ec1b7b94f 100644 --- a/tests/test_llama_grammar.py +++ b/tests/test_llama_grammar.py @@ -76,3 +76,56 @@ def test_grammar_anyof(): grammar = llama_cpp.LlamaGrammar.from_json_schema(json.dumps(sch)) # assert grammar.grammar is not None + + +def test_grammar_unconstrained_array_items(): + schema = {"type": "array", "items": {}} + grammar = llama_cpp.LlamaGrammar.from_json_schema(json.dumps(schema)) + rules = dict(line.split(" ::= ", 1) for line in grammar._grammar.splitlines()) + assert rules["item"] == "object | array | string | number | boolean | null" + assert "item" in rules["root"] + + +def test_grammar_unconstrained_tuple_item(): + schema = {"type": "array", "prefixItems": [{}, {"type": "integer"}]} + grammar = llama_cpp.LlamaGrammar.from_json_schema(json.dumps(schema)) + rules = dict(line.split(" ::= ", 1) for line in grammar._grammar.splitlines()) + assert rules["tuple-0"] == "object | array | string | number | boolean | null" + assert "integer" in rules["root"] + + +def test_grammar_empty_schema_allows_any_json_value(): + grammar = llama_cpp.LlamaGrammar.from_json_schema("{}") + rules = dict(line.split(" ::= ", 1) for line in grammar._grammar.splitlines()) + assert rules["root"] == "object | array | string | number | boolean | null" + + +def test_grammar_typed_array_preserves_item_constraints(): + grammar = llama_cpp.LlamaGrammar.from_json_schema( + json.dumps( + { + "type": "array", + "items": {"type": "integer"}, + "minItems": 1, + "maxItems": 2, + } + ) + ) + assert ( + "integer" + in dict(line.split(" ::= ", 1) for line in grammar._grammar.splitlines())[ + "root" + ] + ) + + +def test_grammar_closed_tuple_preserves_prefix_items(): + schema = { + "type": "array", + "prefixItems": [{"type": "integer"}, {"type": "string"}], + "items": False, + } + grammar = llama_cpp.LlamaGrammar.from_json_schema(json.dumps(schema)) + rules = dict(line.split(" ::= ", 1) for line in grammar._grammar.splitlines()) + assert "integer" in rules["root"] + assert "string" in rules["root"] From 59e611cd3ce645f6fb62a08c2e7583143700fe07 Mon Sep 17 00:00:00 2001 From: tuanzirwar <1281227988@qq.com> Date: Thu, 1 Oct 2026 22:05:32 +0800 Subject: [PATCH 2/2] docs: reference grammar fix PR in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb83871499..dc5aac1d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- fix: support unconstrained array item schemas by @tuanzirwar (PR pending) +- fix: support unconstrained array item schemas by @tuanzirwar (#2378) ## [0.3.36]