diff --git a/common/json-schema-to-grammar.cpp b/common/json-schema-to-grammar.cpp index b18607c..16ad010 100644 --- a/common/json-schema-to-grammar.cpp +++ b/common/json-schema-to-grammar.cpp @@ -677,6 +677,13 @@ private: optional_props.push_back("*"); } + if (required_props.empty() && optional_props.empty()) { + // No properties and no additionalProperties: avoid falling through to two + // adjacent `space` tokens with nothing between them, which is invalid GBNF + // (e.g. a no-arg tool schema like {"type":"object","properties":{}}). + return "\"{\" space \"}\""; + } + std::string rule = "\"{\" space "; for (size_t i = 0; i < required_props.size(); i++) { if (i > 0) { @@ -972,6 +979,14 @@ public: std::string char_rule = _add_primitive("char", PRIMITIVE_RULES.at("char")); int min_len = schema.contains("minLength") ? schema["minLength"].get() : 0; int max_len = schema.contains("maxLength") ? schema["maxLength"].get() : std::numeric_limits::max(); + // Clamp to the grammar engine's own repetition cap (MAX_REPETITION_THRESHOLD in + // src/llama-grammar.cpp, currently 2000) so a large-but-valid maxLength doesn't + // blow up grammar init. A bound this large isn't meaningfully enforceable via a + // literal GBNF repetition anyway, so treat "too large to represent" as unbounded. + constexpr int GRAMMAR_MAX_REPETITION_THRESHOLD = 2000; + if (max_len > GRAMMAR_MAX_REPETITION_THRESHOLD) { + max_len = std::numeric_limits::max(); + } return _add_rule(rule_name, "\"\\\"\" " + build_repetition(char_rule, min_len, max_len) + " \"\\\"\""); } if (schema_type == "integer" && (schema.contains("minimum") || schema.contains("exclusiveMinimum") || schema.contains("maximum") || schema.contains("exclusiveMaximum"))) {