liblloyal 1.0.0
Branched Inference for llama.cpp
Loading...
Searching...
No Matches
grammar.hpp
Go to the documentation of this file.
1#pragma once
2
3// SPDX-License-Identifier: LicenseRef-FSL-1.1-Apache-2.0
4// Copyright 2026 Lloyal Labs
5
6
7#include "common.hpp"
8#include "tokenizer.hpp"
9#include <llama/llama.h>
10#include <json-schema-to-grammar.h> // llama.cpp common library
11#include <json.h>
12#include <nlohmann/json.hpp>
13#include <stdexcept>
14#include <string>
15#include <vector>
16
34namespace lloyal::grammar {
35
48inline std::string from_json_schema(const std::string &schema_json) {
50 "[grammar::from_json_schema] Converting JSON schema (%zu bytes)",
51 schema_json.size());
52
53 try {
54 // Parse JSON schema
55 common_json schema = common_json::parse(schema_json);
56
57 LLOYAL_LOG_DEBUG("[grammar::from_json_schema] Schema parsed, calling "
58 "json_schema_to_grammar");
59
60 // Call json_schema_to_grammar from llama.cpp common library
61 // Parameters: (schema, force_gbnf)
62 // force_gbnf=false allows EBNF optimization when possible
63 std::string grammar = json_schema_to_grammar(schema, false);
64
65 if (grammar.empty()) {
66 LLOYAL_LOG_DEBUG("[grammar::from_json_schema] ERROR: Conversion produced "
67 "empty grammar");
68 throw std::runtime_error("Grammar conversion produced empty result");
69 }
70
72 "[grammar::from_json_schema] Generated GBNF grammar (%zu bytes)",
73 grammar.size());
74 return grammar;
75
76 } catch (const common_json_error &e) {
77 std::string errorMsg = std::string("JSON parse error: ") + e.what();
78 LLOYAL_LOG_DEBUG("[grammar::from_json_schema] ERROR: %s", errorMsg.c_str());
79 throw std::runtime_error(errorMsg);
80 } catch (const std::exception &e) {
81 std::string errorMsg =
82 std::string("Grammar conversion failed: ") + e.what();
83 LLOYAL_LOG_DEBUG("[grammar::from_json_schema] ERROR: %s", errorMsg.c_str());
84 throw std::runtime_error(errorMsg);
85 }
86}
87
88// ===== SAMPLER INITIALIZATION =====
89
108inline llama_sampler *init_sampler(const llama_model *model,
109 const std::string &grammar_str,
110 const std::string &root_rule = "root") {
111 if (!model) {
112 LLOYAL_LOG_DEBUG("[grammar::init_sampler] ERROR: model is null");
113 return nullptr;
114 }
115
116 if (grammar_str.empty()) {
117 LLOYAL_LOG_DEBUG("[grammar::init_sampler] ERROR: grammar_str is empty");
118 return nullptr;
119 }
120
121 const llama_vocab *vocab = tokenizer::get_vocab(model);
122 if (!vocab) {
123 LLOYAL_LOG_DEBUG("[grammar::init_sampler] ERROR: get_vocab returned null");
124 return nullptr;
125 }
126
127 LLOYAL_LOG_DEBUG("[grammar::init_sampler] Initializing grammar sampler "
128 "(grammar: %zu bytes, root: %s)",
129 grammar_str.size(), root_rule.c_str());
130
131 llama_sampler *sampler =
132 llama_sampler_init_grammar(vocab, grammar_str.c_str(), root_rule.c_str());
133
134 if (!sampler) {
135 LLOYAL_LOG_DEBUG("[grammar::init_sampler] ERROR: "
136 "llama_sampler_init_grammar returned null");
137 }
138
139 return sampler;
140}
141
159inline llama_sampler *init_lazy_sampler(
160 const llama_model *model,
161 const std::string &grammar_str,
162 const std::vector<std::string> &trigger_patterns,
163 const std::vector<llama_token> &trigger_tokens,
164 const std::string &root_rule = "root") {
165 if (!model) {
166 LLOYAL_LOG_DEBUG("[grammar::init_lazy_sampler] ERROR: model is null");
167 return nullptr;
168 }
169
170 if (grammar_str.empty()) {
171 LLOYAL_LOG_DEBUG("[grammar::init_lazy_sampler] ERROR: grammar_str is empty");
172 return nullptr;
173 }
174
175 const llama_vocab *vocab = tokenizer::get_vocab(model);
176 if (!vocab) {
177 LLOYAL_LOG_DEBUG("[grammar::init_lazy_sampler] ERROR: get_vocab returned null");
178 return nullptr;
179 }
180
181 std::vector<const char *> patterns_c;
182 patterns_c.reserve(trigger_patterns.size());
183 for (const auto &p : trigger_patterns) patterns_c.push_back(p.c_str());
184
185 LLOYAL_LOG_DEBUG("[grammar::init_lazy_sampler] Initializing lazy grammar "
186 "(grammar: %zu bytes, %zu patterns, %zu tokens, root: %s)",
187 grammar_str.size(), trigger_patterns.size(),
188 trigger_tokens.size(), root_rule.c_str());
189
190 llama_sampler *sampler = llama_sampler_init_grammar_lazy_patterns(
191 vocab, grammar_str.c_str(), root_rule.c_str(),
192 patterns_c.data(), patterns_c.size(),
193 trigger_tokens.data(), trigger_tokens.size());
194
195 if (!sampler) {
196 LLOYAL_LOG_DEBUG("[grammar::init_lazy_sampler] ERROR: "
197 "llama_sampler_init_grammar_lazy_patterns returned null");
198 }
199
200 return sampler;
201}
202
214inline llama_sampler *clone_sampler(llama_sampler *smpl) {
215 if (!smpl) {
216 LLOYAL_LOG_DEBUG("[grammar::clone_sampler] Input is null, returning null");
217 return nullptr;
218 }
219
220 llama_sampler *cloned = llama_sampler_clone(smpl);
221
222 if (!cloned) {
223 LLOYAL_LOG_DEBUG("[grammar::clone_sampler] ERROR: llama_sampler_clone failed");
224 } else {
225 LLOYAL_LOG_DEBUG("[grammar::clone_sampler] Cloned sampler successfully");
226 }
227
228 return cloned;
229}
230
236inline void free_sampler(llama_sampler* smpl) {
237 if (smpl) {
238 llama_sampler_free(smpl);
239 }
240}
241
250inline void apply(llama_sampler* smpl, llama_token_data_array* cur_p) {
251 if (smpl && cur_p) {
252 llama_sampler_apply(smpl, cur_p);
253 }
254}
255
264inline void accept(llama_sampler* smpl, llama_token token) {
265 if (smpl) {
266 llama_sampler_accept(smpl, token);
267 }
268}
269
270} // namespace lloyal::grammar
#define LLOYAL_LOG_DEBUG(...)
liblloyal - Common definitions and logging
Definition common.hpp:48
void free_sampler(llama_sampler *smpl)
Free a grammar sampler.
Definition grammar.hpp:236
llama_sampler * clone_sampler(llama_sampler *smpl)
Clone a grammar sampler (for fork/branching).
Definition grammar.hpp:214
llama_sampler * init_sampler(const llama_model *model, const std::string &grammar_str, const std::string &root_rule="root")
Initialize a grammar sampler from GBNF grammar string.
Definition grammar.hpp:108
llama_sampler * init_lazy_sampler(const llama_model *model, const std::string &grammar_str, const std::vector< std::string > &trigger_patterns, const std::vector< llama_token > &trigger_tokens, const std::string &root_rule="root")
Initialize a lazy grammar sampler from GBNF grammar string.
Definition grammar.hpp:159
std::string from_json_schema(const std::string &schema_json)
Convert JSON schema to GBNF (Grammar BNF) format.
Definition grammar.hpp:48
void accept(llama_sampler *smpl, llama_token token)
Accept a token into grammar state.
Definition grammar.hpp:264
void apply(llama_sampler *smpl, llama_token_data_array *cur_p)
Apply grammar constraint to candidates.
Definition grammar.hpp:250
const llama_vocab * get_vocab(const llama_model *model)
Get vocabulary from model.
Text Tokenization Operations.