Entropic 2.11.1
Local-first agentic inference engine
Loading...
Searching...
No Matches
llama_cpp_sampler.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
21#include "llama_cpp_sampler.h"
23
24#include <llama.h>
25
26#include <cstdint>
27#include <memory>
28#include <unordered_map>
29#include <vector>
30
31static auto logger = entropic::log::get("inference.sampler");
32
33namespace entropic {
34
35namespace {
36
53void add_grammar_sampler(llama_sampler* chain,
54 const llama_vocab* vocab,
55 const std::string& grammar) {
56 if (grammar.empty()) { return; }
57 llama_sampler* g = llama_sampler_init_grammar(
58 vocab, grammar.c_str(), "root");
59 if (g) {
60 llama_sampler_chain_add(chain, g);
61 // gh#95: surface grammar attachment — the issue (and the
62 // tool-staged enforcement gap) had no log to confirm the sampler
63 // actually engaged the constraint.
64 logger->info("Grammar sampler attached ({} bytes)", grammar.size());
65 } else {
66 logger->error("Grammar sampler init FAILED for root rule — output "
67 "will be UNCONSTRAINED. Grammar ({} bytes): {}",
68 grammar.size(), grammar);
69 }
70}
71
82void add_logit_bias_sampler(llama_sampler* chain,
83 const llama_vocab* vocab,
84 const std::unordered_map<int32_t, float>& biases) {
85 if (biases.empty()) { return; }
86 std::vector<llama_logit_bias> entries;
87 entries.reserve(biases.size());
88 for (auto& [tok, val] : biases) {
89 entries.push_back({tok, val});
90 }
91 llama_sampler_chain_add(chain,
92 llama_sampler_init_logit_bias(
93 llama_vocab_n_tokens(vocab),
94 static_cast<int32_t>(entries.size()),
95 entries.data()));
96}
97
108uint32_t resolve_dist_seed(int caller_seed) {
109 return caller_seed < 0
110 ? LLAMA_DEFAULT_SEED
111 : static_cast<uint32_t>(caller_seed);
112}
113
114} // anonymous namespace
115
116// ── LlamaCppSampler ────────────────────────────────────────
117
123LlamaCppSampler::LlamaCppSampler(llama_sampler* chain, llama_context* ctx)
124 : chain_(chain), ctx_(ctx) {}
125
132 if (chain_) {
133 llama_sampler_free(chain_);
134 chain_ = nullptr;
135 }
136}
137
145 if (chain_ == nullptr || ctx_ == nullptr) { return -1; }
146 return llama_sampler_sample(chain_, ctx_, -1);
147}
148
155 if (chain_ != nullptr) {
156 llama_sampler_reset(chain_);
157 }
158}
159
160// ── LlamaCppSamplerFactory ─────────────────────────────────
161
168 llama_context* ctx, const llama_vocab* vocab)
169 : ctx_(ctx), vocab_(vocab) {}
170
184std::unique_ptr<Sampler> LlamaCppSamplerFactory::create(
185 const GenerationParams& params)
186{
187 llama_sampler_chain_params chain_params =
188 llama_sampler_chain_default_params();
189 llama_sampler* chain = llama_sampler_chain_init(chain_params);
190
191 add_grammar_sampler(chain, vocab_, params.grammar);
192 add_logit_bias_sampler(chain, vocab_, params.logit_bias);
193
194 // gh#23 MVP items 2 + 3 (v2.3.14 + v2.3.15): the penalties sampler
195 // now also carries presence_penalty (4th arg) and frequency_penalty
196 // (3rd arg). Gate fires when ANY of repeat / presence / frequency
197 // is non-default, so any single knob is sufficient to activate
198 // the stage.
199 if (params.repeat_penalty != 1.0f
200 || params.presence_penalty > 0.0f
201 || params.frequency_penalty > 0.0f) {
202 llama_sampler_chain_add(chain,
203 llama_sampler_init_penalties(
204 64, params.repeat_penalty,
205 params.frequency_penalty,
206 params.presence_penalty));
207 }
208 if (params.temperature > 0.0f) {
209 llama_sampler_chain_add(chain,
210 llama_sampler_init_temp(params.temperature));
211 }
212 if (params.top_k > 0) {
213 llama_sampler_chain_add(chain,
214 llama_sampler_init_top_k(params.top_k));
215 }
216 if (params.top_p < 1.0f) {
217 llama_sampler_chain_add(chain,
218 llama_sampler_init_top_p(params.top_p, 1));
219 }
220 // Min-P (gh#23 MVP item 1, v2.3.10) — gated so default 0.0f is no-op
221 if (params.min_p > 0.0f) {
222 llama_sampler_chain_add(chain,
223 llama_sampler_init_min_p(params.min_p, 1));
224 }
225
226 llama_sampler_chain_add(chain,
227 llama_sampler_init_dist(resolve_dist_seed(params.seed)));
228
229 return std::make_unique<LlamaCppSampler>(chain, ctx_);
230}
231
232} // namespace entropic
LlamaCppSamplerFactory(llama_context *ctx, const llama_vocab *vocab)
Construct with borrowed context + vocab pointers.
std::unique_ptr< Sampler > create(const GenerationParams &params) override
Build the v2.3.10 sampler chain from GenerationParams.
~LlamaCppSampler() override
Free the underlying llama.cpp sampler chain.
LlamaCppSampler(llama_sampler *chain, llama_context *ctx)
Construct with an already-built llama_sampler chain.
int32_t sample() override
Sample one token from the current logits via the wrapped chain.
void reset() override
Reset llama_sampler internal state.
Concrete llama.cpp Sampler + SamplerFactory (v2.3.10 seam impl).
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Definition logging.cpp:211
Activate model on GPU (WARM → ACTIVE).
Generation parameters for a single inference call.
Definition config.h:313
std::string grammar
GBNF grammar string (empty = unconstrained)
Definition config.h:370
int top_k
Top-K sampling.
Definition config.h:316
std::unordered_map< int32_t, float > logit_bias
Per-token logit bias map (gh#23 MVP item 4).
Definition config.h:347
float repeat_penalty
Repetition penalty.
Definition config.h:317
float temperature
Sampling temperature.
Definition config.h:314
float frequency_penalty
Frequency-penalty term in llama.cpp's penalties sampler (gh#23 MVP item 3).
Definition config.h:360
float presence_penalty
Presence-penalty term in llama.cpp's penalties sampler (gh#23 MVP item 2).
Definition config.h:333
float min_p
Min-p nucleus sampling threshold (gh#23 MVP item 1).
Definition config.h:326
float top_p
Nucleus sampling threshold.
Definition config.h:315
int seed
RNG seed for reproducible sampling.
Definition config.h:367