rig_llama_cpp/types.rs
1use rig_core::completion::{CompletionError, GetTokenUsage, Usage};
2use rig_core::message::AssistantContent;
3use rig_core::one_or_many::OneOrMany;
4use rig_core::streaming::RawStreamingChoice;
5use serde::{Deserialize, Serialize};
6use tokio::sync::{mpsc, oneshot};
7
8/// Raw completion response returned by the model.
9///
10/// Marked `#[non_exhaustive]` because new fields may be added in future
11/// minor releases.
12#[derive(Clone, Debug, Serialize, Deserialize)]
13#[non_exhaustive]
14pub struct RawResponse {
15 /// The full generated text.
16 pub text: String,
17}
18
19/// A single chunk emitted during streaming inference.
20///
21/// The final chunk in a stream includes token usage counts. Marked
22/// `#[non_exhaustive]` because new fields may be added in future minor
23/// releases.
24#[derive(Clone, Debug, Serialize, Deserialize)]
25#[non_exhaustive]
26pub struct StreamChunk {
27 /// The text fragment for this chunk.
28 pub text: String,
29 /// Number of prompt tokens (only set on the final chunk).
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub prompt_tokens: Option<u64>,
32 /// Number of completion tokens (only set on the final chunk).
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub completion_tokens: Option<u64>,
35 /// Number of prompt tokens that were served from the persistent KV-cache prefix
36 /// (only set on the final chunk).
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub cached_input_tokens: Option<u64>,
39}
40
41impl GetTokenUsage for StreamChunk {
42 fn token_usage(&self) -> Usage {
43 let Some((input, output)) = self.prompt_tokens.zip(self.completion_tokens) else {
44 return Usage::new();
45 };
46 Usage {
47 input_tokens: input,
48 output_tokens: output,
49 total_tokens: input + output,
50 cached_input_tokens: self.cached_input_tokens.unwrap_or(0),
51 cache_creation_input_tokens: 0,
52 tool_use_prompt_tokens: 0,
53 reasoning_tokens: 0,
54 }
55 }
56}
57
58pub(crate) type StreamSender =
59 mpsc::UnboundedSender<Result<RawStreamingChoice<StreamChunk>, CompletionError>>;
60
61pub(crate) enum ResponseChannel {
62 Completion(oneshot::Sender<Result<InferenceResult, String>>),
63 Streaming(StreamSender),
64}
65
66pub(crate) enum InferenceCommand {
67 Request(InferenceRequest),
68 Reload(ReloadRequest),
69 Shutdown,
70}
71
72pub(crate) struct ReloadRequest {
73 pub model_path: String,
74 pub mmproj_path: Option<String>,
75 pub n_ctx: u32,
76 pub fit_params: FitParams,
77 pub kv_cache_params: KvCacheParams,
78 pub checkpoint_params: CheckpointParams,
79 pub result_tx: std::sync::mpsc::Sender<Result<(), crate::error::LoadError>>,
80}
81
82pub(crate) struct InferenceRequest {
83 pub params: InferenceParams,
84 pub response_channel: ResponseChannel,
85}
86
87pub(crate) struct InferenceParams {
88 pub prepared_request: PreparedRequest,
89 pub max_tokens: u32,
90 pub temperature: f32,
91 pub top_p: f32,
92 pub top_k: i32,
93 pub min_p: f32,
94 pub presence_penalty: f32,
95 pub repetition_penalty: f32,
96}
97
98pub(crate) struct InferenceResult {
99 pub text: String,
100 pub choice: OneOrMany<AssistantContent>,
101 pub prompt_tokens: u64,
102 pub completion_tokens: u64,
103 /// Tokens of the prompt that were already present in the persistent KV cache
104 /// (i.e. the longest common prefix shared with the previous request).
105 pub cached_input_tokens: u64,
106}
107
108pub(crate) struct PreparedRequest {
109 pub messages_json: String,
110 pub tools_json: Option<String>,
111 pub tool_choice: Option<String>,
112 pub json_schema: Option<String>,
113 /// Parsed from the request's `additional_params` (`{ "thinking": bool }`).
114 ///
115 /// `None` means the caller expressed no preference, and is distinct from
116 /// `Some(false)`: templates commonly gate reasoning on
117 /// `enable_thinking is defined` or `| default(true)`, so passing an
118 /// explicit `false` for an unasked question turns thinking *off* for a
119 /// model that reasons by default. `None` reaches the template as undefined,
120 /// leaving its own default in charge.
121 ///
122 /// Forwarded as the `enable_thinking` variable, but only on the minijinja
123 /// paths (`src/jinja.rs`). llama.cpp's own applier takes just
124 /// `(role, content)` pairs — `llama-cpp-2` 0.1.147 dropped the
125 /// `chat_template_kwargs` plumbing the old oaicompat path used — so for a
126 /// model llama.cpp can template natively this flag stays advisory and the
127 /// template's own default decides.
128 pub enable_thinking: Option<bool>,
129 #[cfg(feature = "mtmd")]
130 pub images: Vec<PreparedImage>,
131}
132
133/// One image extracted from the chat history with its FNV-1a hash precomputed.
134/// The hash is propagated into the underlying `MtmdBitmap` via `set_id` so
135/// that `MtmdInputChunk::id()` round-trips it for the prefix-cache diff.
136#[cfg(feature = "mtmd")]
137#[derive(Clone, Debug)]
138pub(crate) struct PreparedImage {
139 pub bytes: Vec<u8>,
140 pub hash: u64,
141}
142
143pub(crate) struct PromptBuildResult {
144 pub prompt: String,
145}
146
147/// Sampling parameters that control token generation.
148///
149/// Marked `#[non_exhaustive]` so future sampling knobs can be added without
150/// a breaking release. Start from [`SamplingParams::default`] and chain
151/// `with_*` setters:
152///
153/// ```
154/// let params = rig_llama_cpp::SamplingParams::default()
155/// .with_top_k(40)
156/// .with_presence_penalty(1.5);
157/// ```
158#[derive(Clone, Copy, Debug)]
159#[non_exhaustive]
160pub struct SamplingParams {
161 /// Nucleus sampling threshold (default: `0.95`).
162 pub top_p: f32,
163 /// Top-k sampling parameter (default: `40`).
164 pub top_k: i32,
165 /// Minimum probability threshold (default: `0.0`).
166 pub min_p: f32,
167 /// Penalty for token presence (default: `0.0`).
168 pub presence_penalty: f32,
169 /// Penalty for token repetition (default: `1.0`).
170 pub repetition_penalty: f32,
171}
172
173impl Default for SamplingParams {
174 fn default() -> Self {
175 Self {
176 top_p: 0.95,
177 top_k: 40,
178 min_p: 0.0,
179 presence_penalty: 0.0,
180 repetition_penalty: 1.0,
181 }
182 }
183}
184
185impl SamplingParams {
186 /// Set the nucleus sampling threshold.
187 #[must_use]
188 pub fn with_top_p(mut self, top_p: f32) -> Self {
189 self.top_p = top_p;
190 self
191 }
192
193 /// Set the top-k sampling parameter.
194 #[must_use]
195 pub fn with_top_k(mut self, top_k: i32) -> Self {
196 self.top_k = top_k;
197 self
198 }
199
200 /// Set the minimum probability threshold.
201 #[must_use]
202 pub fn with_min_p(mut self, min_p: f32) -> Self {
203 self.min_p = min_p;
204 self
205 }
206
207 /// Set the presence penalty.
208 #[must_use]
209 pub fn with_presence_penalty(mut self, presence_penalty: f32) -> Self {
210 self.presence_penalty = presence_penalty;
211 self
212 }
213
214 /// Set the repetition penalty.
215 #[must_use]
216 pub fn with_repetition_penalty(mut self, repetition_penalty: f32) -> Self {
217 self.repetition_penalty = repetition_penalty;
218 self
219 }
220}
221
222/// Configuration for automatic GPU/CPU layer fitting.
223///
224/// Passed to [`crate::Client::builder`] (or [`crate::Client::from_gguf`]) so
225/// llama.cpp can probe available device memory and pick the optimal number
226/// of layers to offload to GPU automatically, instead of requiring a manual
227/// `n_gpu_layers` value.
228///
229/// Marked `#[non_exhaustive]`; build via `Default::default()` and chain the
230/// `with_*` setters.
231#[derive(Clone, Debug)]
232#[non_exhaustive]
233pub struct FitParams {
234 /// Memory margin per device in bytes. If `None`, defaults to 1 GiB per device.
235 pub margins: Option<Vec<usize>>,
236 /// Minimum context size to preserve during fitting (default: `4096`).
237 pub n_ctx_min: u32,
238}
239
240impl Default for FitParams {
241 fn default() -> Self {
242 Self {
243 margins: None,
244 n_ctx_min: 4096,
245 }
246 }
247}
248
249impl FitParams {
250 /// Override the per-device memory margin in bytes.
251 #[must_use]
252 pub fn with_margins(mut self, margins: Option<Vec<usize>>) -> Self {
253 self.margins = margins;
254 self
255 }
256
257 /// Override the minimum context size to preserve during fitting.
258 #[must_use]
259 pub fn with_n_ctx_min(mut self, n_ctx_min: u32) -> Self {
260 self.n_ctx_min = n_ctx_min;
261 self
262 }
263}
264
265/// Tunable parameters for the in-memory state-checkpoint cache used to
266/// preserve KV/recurrent state across chat turns for hybrid models.
267///
268/// Hybrid architectures (Qwen 3.5, Jamba, etc.) interleave Mamba-style
269/// recurrent layers with transformer layers. The recurrent state can't be
270/// rolled back to an arbitrary earlier position, so a partial KV trim
271/// fails whenever the next prompt diverges deep into the conversation.
272/// To work around this, we periodically snapshot the partial seq state
273/// (recurrent + SWA, via `LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY`) during
274/// prompt prefill and restore the closest snapshot when the next prompt
275/// arrives. Mirrors the mechanism used by upstream `llama-server`.
276///
277/// For non-hybrid models (Qwen 2.5, Llama 3, Gemma, ...) checkpoints are
278/// created but never used because the cheaper partial-trim path
279/// succeeds.
280///
281/// Marked `#[non_exhaustive]`; build via `Default::default()` and chain the
282/// `with_*` setters.
283#[derive(Clone, Copy, Debug)]
284#[non_exhaustive]
285pub struct CheckpointParams {
286 /// Maximum number of checkpoints retained per persistent context.
287 /// `0` disables checkpointing entirely. Each checkpoint is a few MB
288 /// for typical hybrid models.
289 pub max_checkpoints: u32,
290 /// Approximate spacing between checkpoints during prompt prefill, in
291 /// tokens. The last `4..=4 + n_ubatch` tokens always get a
292 /// checkpoint regardless. `<= 0` means "only checkpoint near the end
293 /// of the prompt".
294 pub every_n_tokens: i32,
295 /// Don't checkpoint the very start of a prompt — saves space for
296 /// no benefit because we'd have to re-decode that prefix anyway if
297 /// it's the entire reuse window.
298 pub min_tokens: u32,
299 /// Don't take two checkpoints closer than this many tokens apart.
300 pub min_gap: u32,
301}
302
303impl Default for CheckpointParams {
304 fn default() -> Self {
305 Self {
306 // llama-server uses 32; cap lower because each checkpoint is
307 // a few MB and we'd rather not balloon RSS.
308 max_checkpoints: 8,
309 every_n_tokens: 8192,
310 min_tokens: 64,
311 min_gap: 64,
312 }
313 }
314}
315
316impl CheckpointParams {
317 /// Override the maximum number of checkpoints retained per context.
318 #[must_use]
319 pub fn with_max_checkpoints(mut self, max_checkpoints: u32) -> Self {
320 self.max_checkpoints = max_checkpoints;
321 self
322 }
323
324 /// Override the approximate spacing between checkpoints (in tokens).
325 #[must_use]
326 pub fn with_every_n_tokens(mut self, every_n_tokens: i32) -> Self {
327 self.every_n_tokens = every_n_tokens;
328 self
329 }
330
331 /// Override the minimum prompt length before checkpoints are taken.
332 #[must_use]
333 pub fn with_min_tokens(mut self, min_tokens: u32) -> Self {
334 self.min_tokens = min_tokens;
335 self
336 }
337
338 /// Override the minimum spacing between two consecutive checkpoints.
339 #[must_use]
340 pub fn with_min_gap(mut self, min_gap: u32) -> Self {
341 self.min_gap = min_gap;
342 self
343 }
344}
345
346/// Data type used for an entry in the attention KV cache.
347///
348/// Mirrors the subset of `ggml_type` values that `llama.cpp` accepts as KV
349/// cache element types. The `F16` default preserves full attention quality;
350/// quantizing (e.g. `Q8_0` ≈ ½ size, `Q4_0` ≈ ¼ size) trades a small amount
351/// of accuracy for a large VRAM reduction at long `n_ctx`.
352///
353/// This is a local shim around `llama_cpp_2::context::params::KvCacheType`
354/// so a future `llama-cpp-2` update doesn't force a breaking release of
355/// `rig-llama-cpp`. Marked `#[non_exhaustive]`: when llama.cpp adds a new
356/// `ggml_type`, we add a corresponding variant in a minor (`0.1.x`) release.
357#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
358#[allow(non_camel_case_types)]
359#[non_exhaustive]
360pub enum KvCacheType {
361 /// IEEE 754 single precision.
362 F32,
363 /// IEEE 754 half precision (llama.cpp's default for both K and V).
364 F16,
365 /// Brain floating-point 16, common on newer NVIDIA / AMD GPUs.
366 BF16,
367 /// IEEE 754 double precision.
368 F64,
369 /// 4-bit block quantization, type 0.
370 Q4_0,
371 /// 4-bit block quantization, type 1.
372 Q4_1,
373 /// 5-bit block quantization, type 0.
374 Q5_0,
375 /// 5-bit block quantization, type 1.
376 Q5_1,
377 /// 8-bit block quantization, type 0.
378 Q8_0,
379 /// 8-bit block quantization, type 1.
380 Q8_1,
381 /// 2-bit K-quant.
382 Q2_K,
383 /// 3-bit K-quant.
384 Q3_K,
385 /// 4-bit K-quant.
386 Q4_K,
387 /// 5-bit K-quant.
388 Q5_K,
389 /// 6-bit K-quant.
390 Q6_K,
391 /// 8-bit K-quant.
392 Q8_K,
393 /// Importance-weighted 2-bit, extra-extra-small.
394 IQ2_XXS,
395 /// Importance-weighted 2-bit, extra-small.
396 IQ2_XS,
397 /// Importance-weighted 2-bit, small.
398 IQ2_S,
399 /// Importance-weighted 3-bit, extra-extra-small.
400 IQ3_XXS,
401 /// Importance-weighted 3-bit, small.
402 IQ3_S,
403 /// Importance-weighted 1-bit, small.
404 IQ1_S,
405 /// Importance-weighted 1-bit, medium.
406 IQ1_M,
407 /// Importance-weighted 4-bit, extra-small.
408 IQ4_XS,
409 /// Importance-weighted 4-bit, non-linear.
410 IQ4_NL,
411 /// Signed 8-bit integer.
412 I8,
413 /// Signed 16-bit integer.
414 I16,
415 /// Signed 32-bit integer.
416 I32,
417 /// Signed 64-bit integer.
418 I64,
419 /// Ternary 1-bit, type 0.
420 TQ1_0,
421 /// Ternary 2-bit, type 0.
422 TQ2_0,
423 /// Microscaling FP4.
424 MXFP4,
425}
426
427impl From<KvCacheType> for llama_cpp_2::context::params::KvCacheType {
428 fn from(value: KvCacheType) -> Self {
429 use llama_cpp_2::context::params::KvCacheType as Upstream;
430 match value {
431 KvCacheType::F32 => Upstream::F32,
432 KvCacheType::F16 => Upstream::F16,
433 KvCacheType::BF16 => Upstream::BF16,
434 KvCacheType::F64 => Upstream::F64,
435 KvCacheType::Q4_0 => Upstream::Q4_0,
436 KvCacheType::Q4_1 => Upstream::Q4_1,
437 KvCacheType::Q5_0 => Upstream::Q5_0,
438 KvCacheType::Q5_1 => Upstream::Q5_1,
439 KvCacheType::Q8_0 => Upstream::Q8_0,
440 KvCacheType::Q8_1 => Upstream::Q8_1,
441 KvCacheType::Q2_K => Upstream::Q2_K,
442 KvCacheType::Q3_K => Upstream::Q3_K,
443 KvCacheType::Q4_K => Upstream::Q4_K,
444 KvCacheType::Q5_K => Upstream::Q5_K,
445 KvCacheType::Q6_K => Upstream::Q6_K,
446 KvCacheType::Q8_K => Upstream::Q8_K,
447 KvCacheType::IQ2_XXS => Upstream::IQ2_XXS,
448 KvCacheType::IQ2_XS => Upstream::IQ2_XS,
449 KvCacheType::IQ2_S => Upstream::IQ2_S,
450 KvCacheType::IQ3_XXS => Upstream::IQ3_XXS,
451 KvCacheType::IQ3_S => Upstream::IQ3_S,
452 KvCacheType::IQ1_S => Upstream::IQ1_S,
453 KvCacheType::IQ1_M => Upstream::IQ1_M,
454 KvCacheType::IQ4_XS => Upstream::IQ4_XS,
455 KvCacheType::IQ4_NL => Upstream::IQ4_NL,
456 KvCacheType::I8 => Upstream::I8,
457 KvCacheType::I16 => Upstream::I16,
458 KvCacheType::I32 => Upstream::I32,
459 KvCacheType::I64 => Upstream::I64,
460 KvCacheType::TQ1_0 => Upstream::TQ1_0,
461 KvCacheType::TQ2_0 => Upstream::TQ2_0,
462 KvCacheType::MXFP4 => Upstream::MXFP4,
463 }
464 }
465}
466
467/// KV cache quantization configuration.
468///
469/// Controls the data type used for the attention K and V caches. llama.cpp defaults
470/// both to `F16` (`GGML_TYPE_F16`), which is what `KvCacheParams::default()` preserves.
471/// Quantizing the KV cache (e.g. `Q8_0` → ~½ size, `Q4_0` → ~¼ size) trades a small
472/// amount of accuracy for a large reduction in VRAM usage, which is often the dominant
473/// cost at long `n_ctx`.
474///
475/// Marked `#[non_exhaustive]`; build via `Default::default()` and chain the
476/// `with_*` setters:
477///
478/// ```
479/// use rig_llama_cpp::{KvCacheParams, KvCacheType};
480///
481/// let kv = KvCacheParams::default()
482/// .with_type_k(KvCacheType::Q8_0)
483/// .with_type_v(KvCacheType::Q8_0);
484/// ```
485#[derive(Clone, Copy, Debug)]
486#[non_exhaustive]
487pub struct KvCacheParams {
488 /// Data type for the K cache (default: [`KvCacheType::F16`]).
489 pub type_k: KvCacheType,
490 /// Data type for the V cache (default: [`KvCacheType::F16`]).
491 pub type_v: KvCacheType,
492}
493
494impl Default for KvCacheParams {
495 fn default() -> Self {
496 Self {
497 type_k: KvCacheType::F16,
498 type_v: KvCacheType::F16,
499 }
500 }
501}
502
503impl KvCacheParams {
504 /// Override the K cache data type.
505 #[must_use]
506 pub fn with_type_k(mut self, type_k: KvCacheType) -> Self {
507 self.type_k = type_k;
508 self
509 }
510
511 /// Override the V cache data type.
512 #[must_use]
513 pub fn with_type_v(mut self, type_v: KvCacheType) -> Self {
514 self.type_v = type_v;
515 self
516 }
517}