Skip to main content

llama_cpp_4/context/params/
mod.rs

1//! A safe wrapper around `llama_context_params`.
2//!
3//! Use [`LlamaContextParams`] to configure context size, batching, KV layout,
4//! `RoPE` / `YaRN` scaling, flash attention, per-sequence samplers, and pairing
5//! with another context (`ctx_other`).
6mod advanced;
7mod types;
8
9pub use types::*;
10
11use std::num::NonZeroU32;
12use std::pin::Pin;
13
14use thiserror::Error;
15
16use super::tensor_transaction::{
17    tensor_transaction_callback, tensor_transaction_decode_begin, tensor_transaction_decode_end,
18    TensorTransactions,
19};
20use crate::sampling::LlamaSampler;
21
22/// Error returned when [`LlamaContextParams::try_clone`] cannot duplicate state.
23#[derive(Debug, Error, PartialEq, Eq)]
24pub enum ParamsCloneError {
25    /// Per-sequence sampler chains cannot be duplicated.
26    #[error("cannot clone params that own per-sequence sampler chains")]
27    SamplerChains,
28    /// Owned tensor transaction handlers cannot be duplicated.
29    #[error("cannot clone params that own tensor transactions")]
30    TensorTransactions,
31}
32
33/// Builder for [`llama_context_params`](llama_cpp_sys_4::llama_context_params).
34///
35/// Construct with [`Default::default()`], chain `with_*` setters, then pass the
36/// value to [`crate::model::LlamaModel::new_context`]. Getter methods mirror
37/// the fields that exist on the underlying C struct.
38///
39/// # Sampler ownership
40///
41/// [`Self::with_sampler_seq_configs`] stores owned [`LlamaSampler`] chains inside
42/// this struct until the context is created. [`Clone`] clears sampler configs
43/// because the underlying chains cannot be duplicated safely.
44///
45/// # Examples
46///
47/// ```rust
48/// # use std::num::NonZeroU32;
49/// use llama_cpp_4::context::params::LlamaContextParams;
50///
51/// let ctx_params = LlamaContextParams::default()
52///     .with_n_ctx(NonZeroU32::new(2048));
53///
54/// assert_eq!(ctx_params.n_ctx(), NonZeroU32::new(2048));
55/// ```
56#[derive(Debug)]
57#[allow(
58    missing_docs,
59    clippy::struct_excessive_bools,
60    clippy::module_name_repetitions
61)]
62pub struct LlamaContextParams {
63    pub(crate) context_params: llama_cpp_sys_4::llama_context_params,
64    /// When `true`, the `TurboQuant` attention rotation (PR #21038) will be
65    /// disabled for any context created from these params.
66    pub(crate) attn_rot_disabled: bool,
67    /// Keeps sampler chains alive while `context_params.samplers` points at them.
68    owned_samplers: Vec<LlamaSampler>,
69    sampler_configs: Vec<llama_cpp_sys_4::llama_sampler_seq_config>,
70    pub(crate) tensor_transactions: Option<Pin<Box<TensorTransactions>>>,
71}
72
73impl LlamaContextParams {
74    /// Set the side of the context
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// # use std::num::NonZeroU32;
80    /// use llama_cpp_4::context::params::LlamaContextParams;
81    /// let params = LlamaContextParams::default();
82    /// let params = params.with_n_ctx(NonZeroU32::new(2048));
83    /// assert_eq!(params.n_ctx(), NonZeroU32::new(2048));
84    /// ```
85    #[must_use]
86    pub fn with_n_ctx(mut self, n_ctx: Option<NonZeroU32>) -> Self {
87        self.context_params.n_ctx = n_ctx.map_or(0, std::num::NonZeroU32::get);
88        self
89    }
90
91    /// Get the size of the context.
92    ///
93    /// [`None`] if the context size is specified by the model and not the context.
94    ///
95    /// # Examples
96    ///
97    /// ```rust
98    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
99    /// assert_eq!(params.n_ctx(), std::num::NonZeroU32::new(512));
100    #[must_use]
101    pub fn n_ctx(&self) -> Option<NonZeroU32> {
102        NonZeroU32::new(self.context_params.n_ctx)
103    }
104
105    /// Set the maximum number of independent sequence states in the context.
106    ///
107    /// This maps to llama.cpp's `llama_context_params.n_seq_max` and must match
108    /// the highest sequence id used by batched decoding.
109    ///
110    /// # Examples
111    ///
112    /// ```rust
113    /// use llama_cpp_4::context::params::LlamaContextParams;
114    /// let params = LlamaContextParams::default()
115    ///     .with_n_seq_max(16);
116    /// assert_eq!(params.n_seq_max(), 16);
117    /// ```
118    #[must_use]
119    pub fn with_n_seq_max(mut self, n_seq_max: u32) -> Self {
120        self.context_params.n_seq_max = n_seq_max.max(1);
121        self
122    }
123
124    /// Get the configured maximum number of independent sequence states.
125    #[must_use]
126    pub fn n_seq_max(&self) -> u32 {
127        self.context_params.n_seq_max
128    }
129
130    /// Set the `n_batch`
131    ///
132    /// # Examples
133    ///
134    /// ```rust
135    /// # use std::num::NonZeroU32;
136    /// use llama_cpp_4::context::params::LlamaContextParams;
137    /// let params = LlamaContextParams::default()
138    ///     .with_n_batch(2048);
139    /// assert_eq!(params.n_batch(), 2048);
140    /// ```
141    #[must_use]
142    pub fn with_n_batch(mut self, n_batch: u32) -> Self {
143        self.context_params.n_batch = n_batch;
144        self
145    }
146
147    /// Get the `n_batch`
148    ///
149    /// # Examples
150    ///
151    /// ```rust
152    /// use llama_cpp_4::context::params::LlamaContextParams;
153    /// let params = LlamaContextParams::default();
154    /// assert_eq!(params.n_batch(), 2048);
155    /// ```
156    #[must_use]
157    pub fn n_batch(&self) -> u32 {
158        self.context_params.n_batch
159    }
160
161    /// Set the `n_ubatch`
162    ///
163    /// # Examples
164    ///
165    /// ```rust
166    /// # use std::num::NonZeroU32;
167    /// use llama_cpp_4::context::params::LlamaContextParams;
168    /// let params = LlamaContextParams::default()
169    ///     .with_n_ubatch(512);
170    /// assert_eq!(params.n_ubatch(), 512);
171    /// ```
172    #[must_use]
173    pub fn with_n_ubatch(mut self, n_ubatch: u32) -> Self {
174        self.context_params.n_ubatch = n_ubatch;
175        self
176    }
177
178    /// Get the `n_ubatch`
179    ///
180    /// # Examples
181    ///
182    /// ```rust
183    /// use llama_cpp_4::context::params::LlamaContextParams;
184    /// let params = LlamaContextParams::default();
185    /// assert_eq!(params.n_ubatch(), 512);
186    /// ```
187    #[must_use]
188    pub fn n_ubatch(&self) -> u32 {
189        self.context_params.n_ubatch
190    }
191
192    /// Set the context type (e.g. [`LlamaContextType::Mtp`] for the draft context in
193    /// [`crate::mtp::MtpSession`]).
194    #[must_use]
195    pub fn with_ctx_type(mut self, ctx_type: LlamaContextType) -> Self {
196        self.context_params.ctx_type = ctx_type.into();
197        self
198    }
199
200    /// Get the configured context type.
201    #[must_use]
202    pub fn ctx_type(&self) -> LlamaContextType {
203        self.context_params.ctx_type.into()
204    }
205
206    /// Set the number of recurrent-state snapshots per sequence (MTP rollback).
207    ///
208    /// Must be `>=` [`MtpSessionConfig::n_draft_max`](crate::mtp::MtpSessionConfig::n_draft_max)
209    /// on the draft context. See [`crate::mtp`].
210    #[must_use]
211    pub fn with_n_rs_seq(mut self, n_rs_seq: u32) -> Self {
212        self.context_params.n_rs_seq = n_rs_seq;
213        self
214    }
215
216    /// Get the number of recurrent-state snapshots per sequence used for MTP rollback.
217    #[must_use]
218    pub fn n_rs_seq(&self) -> u32 {
219        self.context_params.n_rs_seq
220    }
221
222    /// Set the `offload_kqv` parameter to control offloading KV cache & KQV ops to GPU
223    ///
224    /// # Examples
225    ///
226    /// ```rust
227    /// use llama_cpp_4::context::params::LlamaContextParams;
228    /// let params = LlamaContextParams::default()
229    ///     .with_offload_kqv(false);
230    /// assert_eq!(params.offload_kqv(), false);
231    /// ```
232    #[must_use]
233    pub fn with_offload_kqv(mut self, enabled: bool) -> Self {
234        self.context_params.offload_kqv = enabled;
235        self
236    }
237
238    /// Get the `offload_kqv` parameter
239    ///
240    /// # Examples
241    ///
242    /// ```rust
243    /// use llama_cpp_4::context::params::LlamaContextParams;
244    /// let params = LlamaContextParams::default();
245    /// assert_eq!(params.offload_kqv(), true);
246    /// ```
247    #[must_use]
248    pub fn offload_kqv(&self) -> bool {
249        self.context_params.offload_kqv
250    }
251
252    /// Set the type of rope scaling.
253    ///
254    /// # Examples
255    ///
256    /// ```rust
257    /// use llama_cpp_4::context::params::{LlamaContextParams, RopeScalingType};
258    /// let params = LlamaContextParams::default()
259    ///     .with_rope_scaling_type(RopeScalingType::Linear);
260    /// assert_eq!(params.rope_scaling_type(), RopeScalingType::Linear);
261    /// ```
262    #[must_use]
263    pub fn with_rope_scaling_type(mut self, rope_scaling_type: RopeScalingType) -> Self {
264        self.context_params.rope_scaling_type = i32::from(rope_scaling_type);
265        self
266    }
267
268    /// Get the type of rope scaling.
269    ///
270    /// # Examples
271    ///
272    /// ```rust
273    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
274    /// assert_eq!(params.rope_scaling_type(), llama_cpp_4::context::params::RopeScalingType::Unspecified);
275    /// ```
276    #[must_use]
277    pub fn rope_scaling_type(&self) -> RopeScalingType {
278        RopeScalingType::from(self.context_params.rope_scaling_type)
279    }
280
281    /// Set the rope frequency base.
282    ///
283    /// # Examples
284    ///
285    /// ```rust
286    /// use llama_cpp_4::context::params::LlamaContextParams;
287    /// let params = LlamaContextParams::default()
288    ///    .with_rope_freq_base(0.5);
289    /// assert_eq!(params.rope_freq_base(), 0.5);
290    /// ```
291    #[must_use]
292    pub fn with_rope_freq_base(mut self, rope_freq_base: f32) -> Self {
293        self.context_params.rope_freq_base = rope_freq_base;
294        self
295    }
296
297    /// Get the rope frequency base.
298    ///
299    /// # Examples
300    ///
301    /// ```rust
302    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
303    /// assert_eq!(params.rope_freq_base(), 0.0);
304    /// ```
305    #[must_use]
306    pub fn rope_freq_base(&self) -> f32 {
307        self.context_params.rope_freq_base
308    }
309
310    /// Set the rope frequency scale.
311    ///
312    /// # Examples
313    ///
314    /// ```rust
315    /// use llama_cpp_4::context::params::LlamaContextParams;
316    /// let params = LlamaContextParams::default()
317    ///   .with_rope_freq_scale(0.5);
318    /// assert_eq!(params.rope_freq_scale(), 0.5);
319    /// ```
320    #[must_use]
321    pub fn with_rope_freq_scale(mut self, rope_freq_scale: f32) -> Self {
322        self.context_params.rope_freq_scale = rope_freq_scale;
323        self
324    }
325
326    /// Get the rope frequency scale.
327    ///
328    /// # Examples
329    ///
330    /// ```rust
331    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
332    /// assert_eq!(params.rope_freq_scale(), 0.0);
333    /// ```
334    #[must_use]
335    pub fn rope_freq_scale(&self) -> f32 {
336        self.context_params.rope_freq_scale
337    }
338
339    /// Get the number of threads.
340    ///
341    /// # Examples
342    ///
343    /// ```rust
344    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
345    /// assert_eq!(params.n_threads(), 4);
346    /// ```
347    #[must_use]
348    pub fn n_threads(&self) -> i32 {
349        self.context_params.n_threads
350    }
351
352    /// Get the number of threads allocated for batches.
353    ///
354    /// # Examples
355    ///
356    /// ```rust
357    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
358    /// assert_eq!(params.n_threads_batch(), 4);
359    /// ```
360    #[must_use]
361    pub fn n_threads_batch(&self) -> i32 {
362        self.context_params.n_threads_batch
363    }
364
365    /// Set the number of threads.
366    ///
367    /// # Examples
368    ///
369    /// ```rust
370    /// use llama_cpp_4::context::params::LlamaContextParams;
371    /// let params = LlamaContextParams::default()
372    ///    .with_n_threads(8);
373    /// assert_eq!(params.n_threads(), 8);
374    /// ```
375    #[must_use]
376    pub fn with_n_threads(mut self, n_threads: i32) -> Self {
377        self.context_params.n_threads = n_threads;
378        self
379    }
380
381    /// Set the number of threads allocated for batches.
382    ///
383    /// # Examples
384    ///
385    /// ```rust
386    /// use llama_cpp_4::context::params::LlamaContextParams;
387    /// let params = LlamaContextParams::default()
388    ///    .with_n_threads_batch(8);
389    /// assert_eq!(params.n_threads_batch(), 8);
390    /// ```
391    #[must_use]
392    pub fn with_n_threads_batch(mut self, n_threads: i32) -> Self {
393        self.context_params.n_threads_batch = n_threads;
394        self
395    }
396
397    /// Check whether embeddings are enabled
398    ///
399    /// # Examples
400    ///
401    /// ```rust
402    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
403    /// assert!(!params.embeddings());
404    /// ```
405    #[must_use]
406    pub fn embeddings(&self) -> bool {
407        self.context_params.embeddings
408    }
409
410    /// Enable the use of embeddings
411    ///
412    /// # Examples
413    ///
414    /// ```rust
415    /// use llama_cpp_4::context::params::LlamaContextParams;
416    /// let params = LlamaContextParams::default()
417    ///    .with_embeddings(true);
418    /// assert!(params.embeddings());
419    /// ```
420    #[must_use]
421    pub fn with_embeddings(mut self, embedding: bool) -> Self {
422        self.context_params.embeddings = embedding;
423        self
424    }
425
426    /// Set the evaluation callback.
427    ///
428    /// # Examples
429    ///
430    /// ```no_run
431    /// extern "C" fn cb_eval_fn(
432    ///     t: *mut llama_cpp_sys_4::ggml_tensor,
433    ///     ask: bool,
434    ///     user_data: *mut std::ffi::c_void,
435    /// ) -> bool {
436    ///     false
437    /// }
438    ///
439    /// use llama_cpp_4::context::params::LlamaContextParams;
440    /// let params = LlamaContextParams::default().with_cb_eval(Some(cb_eval_fn));
441    /// ```
442    #[must_use]
443    pub fn with_cb_eval(
444        mut self,
445        cb_eval: llama_cpp_sys_4::ggml_backend_sched_eval_callback,
446    ) -> Self {
447        self.context_params.cb_eval = cb_eval;
448        self
449    }
450
451    /// Set the evaluation callback user data.
452    ///
453    /// # Examples
454    ///
455    /// ```no_run
456    /// use llama_cpp_4::context::params::LlamaContextParams;
457    /// let params = LlamaContextParams::default();
458    /// let user_data = std::ptr::null_mut();
459    /// let params = params.with_cb_eval_user_data(user_data);
460    /// ```
461    #[must_use]
462    pub fn with_cb_eval_user_data(mut self, cb_eval_user_data: *mut std::ffi::c_void) -> Self {
463        self.context_params.cb_eval_user_data = cb_eval_user_data;
464        self
465    }
466
467    /// Attach a [`TensorCapture`](super::tensor_capture::TensorCapture) to
468    /// intercept intermediate tensor outputs during [`crate::LlamaContext::decode`].
469    ///
470    /// Sets `cb_eval` to copy tensors matching the capture filter (layer outputs,
471    /// named nodes, prefix, or all). After `decode()`, read results from the
472    /// capture — see [`crate::TensorCapture`] and [`crate::context::tensor_capture`].
473    ///
474    /// The capture must outlive the context. Call
475    /// [`TensorCapture::clear`](crate::TensorCapture::clear) before reusing it
476    /// on another batch. Prefer [`Self::with_tensor_transactions`], which
477    /// transfers owned pinned callback state into the context.
478    ///
479    /// # Safety
480    ///
481    /// The caller must keep `capture` at a stable address until after the
482    /// resulting context is dropped. It must not be accessed concurrently with
483    /// any context operation that can invoke the callback. The reference
484    /// accepted here does not remain borrowed by the returned params.
485    ///
486    /// # Example
487    ///
488    /// ```no_run
489    /// use llama_cpp_4::prelude::*;
490    ///
491    /// fn main() {
492    ///     let backend = LlamaBackend::init().unwrap();
493    ///     let model = LlamaModel::load_from_file(
494    ///         &backend,
495    ///         "model.gguf",
496    ///         &LlamaModelParams::default(),
497    ///     )
498    ///     .unwrap();
499    ///
500    ///     let mut capture = TensorCapture::for_layers(&[13, 20, 27]);
501    ///     let ctx_params = unsafe {
502    ///         LlamaContextParams::default().with_tensor_capture(&mut capture)
503    ///     };
504    ///     let _ctx = model.new_context(&backend, ctx_params).unwrap();
505    /// }
506    /// ```
507    #[must_use]
508    pub unsafe fn with_tensor_capture(
509        self,
510        capture: &mut super::tensor_capture::TensorCapture,
511    ) -> Self {
512        self.with_cb_eval(Some(super::tensor_capture::tensor_capture_callback))
513            .with_cb_eval_user_data(
514                std::ptr::from_mut::<super::tensor_capture::TensorCapture>(capture)
515                    .cast::<std::ffi::c_void>(),
516            )
517    }
518
519    /// Attaches bounded owned tensor transactions to the context.
520    ///
521    /// The transaction state is pinned before its address is installed in the
522    /// native callback parameters. On successful context creation ownership
523    /// moves into [`crate::LlamaContext`] and remains there until after
524    /// `llama_free`.
525    ///
526    /// # Panics
527    ///
528    /// Panics if the linked `libllama` was not built with the decode-lifecycle
529    /// hooks patch — an ABI mismatch that would otherwise silently disable the
530    /// hooks or misread the context-params struct.
531    #[must_use]
532    pub fn with_tensor_transactions(mut self, transactions: TensorTransactions) -> Self {
533        // ABI guard: this symbol is defined only in a libllama built with the
534        // decode-lifecycle-hooks patch (0004). Referencing it makes an
535        // unpatched or ABI-mismatched prebuilt fail at link time — and, if it
536        // somehow linked, this assert fails — rather than silently dropping the
537        // hooks or reading a struct with a different layout.
538        assert_eq!(
539            // SAFETY: no pointer is involved; the shim returns a constant ABI
540            // version compiled into libllama.
541            unsafe { llama_cpp_sys_4::llama_cpp_rs_decode_hooks_abi_v1() },
542            1,
543            "libllama is missing the decode-hook ABI patch (0004)",
544        );
545
546        let mut transactions = Box::pin(transactions);
547        let user_data = std::ptr::from_mut(transactions.as_mut().get_mut()).cast();
548        self.context_params.cb_eval = Some(tensor_transaction_callback);
549        self.context_params.cb_eval_user_data = user_data;
550        self.context_params.cb_decode_begin = Some(tensor_transaction_decode_begin);
551        self.context_params.cb_decode_end = Some(tensor_transaction_decode_end);
552        self.tensor_transactions = Some(transactions);
553        self
554    }
555
556    /// Set the storage type for the **K** (key) KV cache tensors.
557    ///
558    /// The default is `GgmlType::F16`.  Quantized types like `GgmlType::Q5_0`
559    /// or `GgmlType::Q4_0` reduce VRAM usage significantly; combining them with
560    /// `TurboQuant` attention rotation (the default) keeps quality high.
561    ///
562    /// # Examples
563    ///
564    /// ```rust
565    /// use llama_cpp_4::context::params::LlamaContextParams;
566    /// use llama_cpp_4::quantize::GgmlType;
567    /// let params = LlamaContextParams::default()
568    ///     .with_cache_type_k(GgmlType::Q5_0);
569    /// ```
570    #[must_use]
571    pub fn with_cache_type_k(mut self, ty: crate::quantize::GgmlType) -> Self {
572        self.context_params.type_k = ty as llama_cpp_sys_4::ggml_type;
573        self
574    }
575
576    /// Get the K-cache storage type.
577    #[must_use]
578    pub fn cache_type_k(&self) -> llama_cpp_sys_4::ggml_type {
579        self.context_params.type_k
580    }
581
582    /// Set the storage type for the **V** (value) KV cache tensors.
583    ///
584    /// See [`with_cache_type_k`](Self::with_cache_type_k) for details.
585    ///
586    /// # Examples
587    ///
588    /// ```rust
589    /// use llama_cpp_4::context::params::LlamaContextParams;
590    /// use llama_cpp_4::quantize::GgmlType;
591    /// let params = LlamaContextParams::default()
592    ///     .with_cache_type_v(GgmlType::Q5_0);
593    /// ```
594    #[must_use]
595    pub fn with_cache_type_v(mut self, ty: crate::quantize::GgmlType) -> Self {
596        self.context_params.type_v = ty as llama_cpp_sys_4::ggml_type;
597        self
598    }
599
600    /// Get the V-cache storage type.
601    #[must_use]
602    pub fn cache_type_v(&self) -> llama_cpp_sys_4::ggml_type {
603        self.context_params.type_v
604    }
605
606    /// Control the `TurboQuant` attention-rotation feature (llama.cpp PR #21038).
607    ///
608    /// By default, llama.cpp applies a Hadamard rotation to Q/K/V tensors
609    /// before writing them into the KV cache.  This significantly improves
610    /// quantized KV-cache quality at near-zero overhead, and is enabled
611    /// automatically for models whose head dimension is a power of two.
612    ///
613    /// Set `disabled = true` to opt out (equivalent to `LLAMA_ATTN_ROT_DISABLE=1`).
614    /// The env-var is applied just before the context is created and restored
615    /// afterwards, so this is safe to call from a single thread.
616    ///
617    /// # Examples
618    ///
619    /// ```rust
620    /// use llama_cpp_4::context::params::LlamaContextParams;
621    /// // Disable rotation for this context only:
622    /// let params = LlamaContextParams::default().with_attn_rot_disabled(true);
623    /// assert!(params.attn_rot_disabled());
624    /// ```
625    #[must_use]
626    pub fn with_attn_rot_disabled(mut self, disabled: bool) -> Self {
627        self.attn_rot_disabled = disabled;
628        self
629    }
630
631    /// Returns `true` if `TurboQuant` attention rotation is disabled for this context.
632    ///
633    /// ```rust
634    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
635    /// assert!(!params.attn_rot_disabled());
636    /// ```
637    #[must_use]
638    pub fn attn_rot_disabled(&self) -> bool {
639        self.attn_rot_disabled
640    }
641
642    /// Set the type of pooling.
643    ///
644    /// # Examples
645    ///
646    /// ```rust
647    /// use llama_cpp_4::context::params::{LlamaContextParams, LlamaPoolingType};
648    /// let params = LlamaContextParams::default()
649    ///     .with_pooling_type(LlamaPoolingType::Last);
650    /// assert_eq!(params.pooling_type(), LlamaPoolingType::Last);
651    /// ```
652    #[must_use]
653    pub fn with_pooling_type(mut self, pooling_type: LlamaPoolingType) -> Self {
654        self.context_params.pooling_type = i32::from(pooling_type);
655        self
656    }
657
658    /// Get the type of pooling.
659    ///
660    /// # Examples
661    ///
662    /// ```rust
663    /// let params = llama_cpp_4::context::params::LlamaContextParams::default();
664    /// assert_eq!(params.pooling_type(), llama_cpp_4::context::params::LlamaPoolingType::Unspecified);
665    /// ```
666    #[must_use]
667    pub fn pooling_type(&self) -> LlamaPoolingType {
668        LlamaPoolingType::from(self.context_params.pooling_type)
669    }
670
671    /// Clone these params, failing when sampler chains are attached.
672    ///
673    /// Prefer this over [`Clone::clone`] when you need to detect dropped sampler
674    /// configuration.
675    ///
676    /// # Errors
677    ///
678    /// Returns [`ParamsCloneError::SamplerChains`] when per-sequence sampler
679    /// chains are attached and cannot be duplicated, or
680    /// [`ParamsCloneError::TensorTransactions`] when an owned callback program
681    /// is attached.
682    pub fn try_clone(&self) -> Result<Self, ParamsCloneError> {
683        if !self.sampler_configs.is_empty() {
684            return Err(ParamsCloneError::SamplerChains);
685        }
686        if self.tensor_transactions.is_some() {
687            return Err(ParamsCloneError::TensorTransactions);
688        }
689        Ok(self.clone())
690    }
691}
692
693/// Default parameters for `LlamaContext`. (as defined in llama.cpp by `llama_context_default_params`)
694/// ```
695/// # use std::num::NonZeroU32;
696/// use llama_cpp_4::context::params::{LlamaContextParams, RopeScalingType};
697/// let params = LlamaContextParams::default();
698/// assert_eq!(params.n_ctx(), NonZeroU32::new(512), "n_ctx should be 512");
699/// assert_eq!(params.rope_scaling_type(), RopeScalingType::Unspecified);
700/// ```
701impl Default for LlamaContextParams {
702    fn default() -> Self {
703        // SAFETY: no pointer is involved; llama.cpp returns a fully
704        // initialised params struct by value.
705        let context_params = unsafe { llama_cpp_sys_4::llama_context_default_params() };
706        Self {
707            context_params,
708            attn_rot_disabled: false,
709            owned_samplers: Vec::new(),
710            sampler_configs: Vec::new(),
711            tensor_transactions: None,
712        }
713    }
714}
715
716/// Duplicate context params for reuse.
717///
718/// Sampler chains attached via [`LlamaContextParams::with_sampler_seq_configs`]
719/// are **not** cloned — the copy clears `samplers` / `n_samplers` because the
720/// underlying C chains cannot be duplicated safely.
721impl Clone for LlamaContextParams {
722    fn clone(&self) -> Self {
723        let mut context_params = self.context_params;
724        // Sampler chains cannot be duplicated here; cloned params omit them.
725        context_params.samplers = std::ptr::null_mut();
726        context_params.n_samplers = 0;
727        if self.tensor_transactions.is_some() {
728            context_params.cb_eval = None;
729            context_params.cb_eval_user_data = std::ptr::null_mut();
730            context_params.cb_decode_begin = None;
731            context_params.cb_decode_end = None;
732        }
733        Self {
734            context_params,
735            attn_rot_disabled: self.attn_rot_disabled,
736            owned_samplers: Vec::new(),
737            sampler_configs: Vec::new(),
738            tensor_transactions: None,
739        }
740    }
741}