private String generateObjectRuleGetRecursiveRefs(
            String name, Map<String, String> propertyRules, List<String> properties, boolean firstIsOptional) {
        var headProperty = properties.get(0);
        var tailProperties = properties.subList(1, properties.size());
        var headPropertyRule = propertyRules.get(headProperty);
        var commaRef = "( \",\" space " + headPropertyRule + " )";

        var content = new StringBuilder();

        if (firstIsOptional) {
            content.append(commaRef);

            if (headProperty.equals("*")) {
                content.append("*");
            } else {
                content.append("?");
            }
        } else {
            content.append(headPropertyRule);

            if (headProperty.equals("*")) {
                content.append(" ").append(commaRef).append("*");
            }
        }

        if (!tailProperties.isEmpty()) {
            content.append(" ");
            var subruleName = (name == null ? "" : name + "-") + headProperty + "-rest";
            var subruleContent = generateObjectRuleGetRecursiveRefs(name, propertyRules, tailProperties, true);
            var subrule = addRule(subruleName, subruleContent);
            content.append(subrule);
        }

        return content.toString();
    }
Converts a JSON Schema to a GBNF grammar to be used for structured output with llama.cpp.
It is a port of Python implementation from llama.cpp repository.
Current implementation doesn't support regex patterns, resolving $ref or fetching schemas from URLs.
The plan is to replace this with C++ implementation from llama.cpp though JNI.
model-integration/src/main/java/ai/vespa/llm/clients/JsonSchemaToGrammar.java