Skip to content

feat: add support for GBNF grammar - #20

Merged
daavoo merged 3 commits into
mozilla-ai:mainfrom
oglego:feature/support-gbnf-grammar
Aug 25, 2026
Merged

feat: add support for GBNF grammar#20
daavoo merged 3 commits into
mozilla-ai:mainfrom
oglego:feature/support-gbnf-grammar

Conversation

@oglego

@oglego oglego commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR partially addresses #18 - adding support for GBNF grammar. I believe that I have everything for this wired in correctly, I also tried to follow the current coding style as best as I could, but if anything needs to be changed please let me know.

Changes

The main items that I have added on this PR are:

  • ModelConfig gains grammar (GBNF string) and grammar_root (rule name, defaults to "root").

  • load_grammar_file() reads a .gbnf file into ModelConfig::grammar.

  • initialize_context() applies the grammar to the sampler chain when set.

  • examples/grammar/ — minimal sentiment classifier demonstrating grammar-constrained output.

  • tests/test_grammar.cpp — config defaults, file loading, missing-file error handling.

For testing this locally and interacting with it I just used the recommended model (granite-4.0-micro-Q8_0.gguf).

Thanks in advance for the review!

oglego and others added 3 commits August 19, 2026 17:23
fix: update comment on GBNF grammar

fix: update comments in model.h for GBNF grammar

test: update test suite for GBNF grammar support

refactor: clean up grammar test formatting

feat: update GBNF grammar support with example and reset fix

Update GBNF grammar support to ModelConfig via a `grammar` string and
`grammar_root` rule name, applied to the sampler chain in
initialize_context(). load_grammar_file() reads a .gbnf file into
ModelConfig::grammar.

The grammar sampler retains its parse position across calls to
generate_from_tokens(), so a completed grammar from one turn forces
EOS immediately on the next. Reset only the grammar sampler (not the
whole chain) at the start of each turn, via a non-owning pointer
kept on Model, so the dist sampler's RNG and other stateful samplers
are left untouched when an explicit seed is configured.

Add examples/grammar, a minimal sentiment classifier constrained to
{"sentiment": "positive"|"negative"|"neutral"} via sentiment.gbnf,
demonstrating the feature end-to-end. Document grammar-constrained
output in the README and register the example in CMakeLists.txt.

fix: allow custom grammar roots in grammar example

Add a -r flag to the grammar demo, update the README usage examples,
and polish the grammar documentation wording.

fix: comment formatting in model.cpp

fix: update formatting for comments in model.h
An empty grammar file (or a path that opens but reads nothing) made
load_grammar_file return an empty string, which initialize_context then
silently treated as "no grammar" - unconstrained output with no error.
Throw instead.

Also document that a grammar constrains every token, so it blocks tool
calls unless the grammar allows the tool-call format.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@daavoo

daavoo commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Thanks @oglego 🚀 !

@daavoo
daavoo merged commit becf32d into mozilla-ai:main Aug 25, 2026
5 checks passed
@oglego

oglego commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Awesome! Thanks again for the review and also for the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants