Skip to main content

llama_cpp_4/
context.rs

1//! Safe wrapper around `llama_context`.
2//!
3//! Submodules:
4//!
5//! - [`tensor_capture`] — hook `cb_eval` during [`LlamaContext::decode`] to copy
6//!   intermediate tensors (per-layer hidden states, norms, …).
7//! - [`memory_breakdown`] — per-buffer memory usage after load/decode.
8//! - [`kv_cache`] — sequence copy, shift, and clear helpers.
9
10use std::fmt::{Debug, Formatter};
11use std::num::NonZeroI32;
12use std::pin::Pin;
13use std::ptr::NonNull;
14use std::slice;
15use std::sync::atomic::{AtomicU64, Ordering};
16
17use llama_cpp_sys_4::llama_pooling_type;
18use params::{LlamaContextType, LlamaPoolingType};
19use perf::PerfContextData;
20
21use crate::llama_batch::LlamaBatch;
22use crate::llama_batch_ext::{LlamaBatchExt, LlamaProcessType};
23use crate::model::{LlamaLoraAdapter, LlamaModel};
24use crate::token::data::LlamaTokenData;
25use crate::token::data_array::LlamaTokenDataArray;
26use crate::token::LlamaToken;
27use crate::{
28    DecodeError, EmbeddingsError, EncodeError, LlamaLoraAdapterRemoveError,
29    LlamaLoraAdapterSetError, ProcessError,
30};
31
32/// Source of [`LlamaContext::id`].
33///
34/// A [`LlamaBatchExt`] is bound to the context that built it, and comparing raw
35/// `llama_context` pointers is not enough to detect a stale binding: the
36/// allocator can hand a new context the address a dropped one had. A counter
37/// that only ever goes up cannot be reused that way.
38static NEXT_CONTEXT_ID: AtomicU64 = AtomicU64::new(0);
39
40pub mod kv_cache;
41pub mod memory_breakdown;
42pub mod params;
43pub mod perf;
44pub mod session;
45pub mod tensor_capture;
46pub mod tensor_transaction;
47
48pub use memory_breakdown::MemoryBreakdownEntry;
49pub use tensor_capture::{CapturedTensor, TensorCapture};
50pub use tensor_transaction::{
51    CapturedTensorData, TensorAccess, TensorBatchRow, TensorCallbackFailure, TensorDataMut,
52    TensorElementType, TensorFiniteValidation, TensorRowMapping, TensorSelector, TensorShape,
53    TensorTransaction, TensorTransactionError, TensorTransactionHandler, TensorTransactions,
54    TensorWriteback, TransactionalTensorCapture,
55};
56
57/// A safe wrapper around the `llama_context` C++ context.
58///
59/// This struct provides a safe interface to interact with the `llama_context` used by the `LlamaModel`.
60/// It encapsulates the raw C++ context pointer and provides additional fields for managing the model and
61/// context-specific settings like embeddings and logits.
62///
63/// The `LlamaContext` struct ensures that the C++ context is always valid by using the `NonNull` type for
64/// the context pointer, preventing it from being null. The struct also holds a reference to the model
65/// (`LlamaModel`) that the context is tied to, along with some internal state like whether embeddings are enabled
66/// and the initialized logits for the context.
67///
68/// # Fields
69///
70/// - `context`: A non-null pointer to the raw C++ `llama_context`. This is the main context used for interacting with the model.
71/// - `model`: A reference to the `LlamaModel` associated with this context. This model provides the data and parameters
72///   that the context interacts with.
73/// - `initialized_logits`: A vector used to store the initialized logits. These are used in the model's processing and
74///   are kept separate from the context data.
75/// - `embeddings_enabled`: A boolean flag indicating whether embeddings are enabled in the context. This is useful for
76///   controlling whether embedding data is generated during the interaction with the model.
77#[allow(clippy::module_name_repetitions)]
78pub struct LlamaContext<'a> {
79    pub(crate) context: NonNull<llama_cpp_sys_4::llama_context>,
80    /// a reference to the contexts model.
81    pub model: &'a LlamaModel,
82    initialized_logits: Vec<i32>,
83    embeddings_enabled: bool,
84    context_type: LlamaContextType,
85    tensor_transactions: Option<Pin<Box<TensorTransactions>>>,
86    /// Identity of this context, unique for the life of the process. See
87    /// [`NEXT_CONTEXT_ID`].
88    id: u64,
89}
90
91impl Debug for LlamaContext<'_> {
92    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93        f.debug_struct("LlamaContext")
94            .field("context", &self.context)
95            .finish()
96    }
97}
98
99impl<'model> LlamaContext<'model> {
100    /// Creates a new instance of `LlamaContext` with the provided model, context, and embeddings flag.
101    ///
102    /// This function initializes a new `LlamaContext` object, which is used to interact with the
103    /// `LlamaModel`. The context is created from a pointer to a C++ context and the embeddings flag
104    /// determines whether embeddings are enabled in the context.
105    ///
106    /// # Parameters
107    ///
108    /// - `llama_model`: A reference to an existing `LlamaModel` that will be used with the new context.
109    /// - `llama_context`: A non-null pointer to an existing `llama_cpp_sys_4::llama_context` representing
110    ///   the context created in previous steps. This context is necessary for interacting with the model.
111    /// - `embeddings_enabled`: A boolean flag indicating whether embeddings are enabled in this context.
112    /// - `context_type`: The exact graph/context mode selected at allocation.
113    ///
114    /// # Returns
115    ///
116    /// This function returns a new instance of `LlamaContext` initialized with the given parameters:
117    /// - The model reference (`llama_model`) is stored in the context.
118    /// - The raw context pointer (`llama_context`) is wrapped in a `NonNull` to ensure safety.
119    /// - The `embeddings_enabled` flag is used to determine if embeddings are enabled for the context.
120    ///
121    /// # Example
122    /// ```ignore
123    /// let llama_model = LlamaModel::load_from_file(&backend, "path/to/model", &params).unwrap();
124    /// let context_ptr = NonNull::new(some_llama_context_ptr).unwrap();
125    /// let context = LlamaContext::new(
126    ///     &llama_model,
127    ///     context_ptr,
128    ///     true,
129    ///     LlamaContextType::Default,
130    ///     None,
131    /// );
132    /// // Now you can use the context
133    /// ```
134    pub(crate) fn new(
135        llama_model: &'model LlamaModel,
136        llama_context: NonNull<llama_cpp_sys_4::llama_context>,
137        embeddings_enabled: bool,
138        context_type: LlamaContextType,
139        tensor_transactions: Option<Pin<Box<TensorTransactions>>>,
140    ) -> Self {
141        Self {
142            context: llama_context,
143            model: llama_model,
144            initialized_logits: Vec::new(),
145            embeddings_enabled,
146            context_type,
147            tensor_transactions,
148            id: NEXT_CONTEXT_ID.fetch_add(1, Ordering::Relaxed),
149        }
150    }
151
152    /// Identity of this context, unique among every context this process has
153    /// created. Used to tie a [`LlamaBatchExt`] to the context that built it.
154    pub(crate) fn id(&self) -> u64 {
155        self.id
156    }
157
158    /// Returns the context type selected at native allocation.
159    #[must_use]
160    pub fn context_type(&self) -> LlamaContextType {
161        self.context_type
162    }
163
164    /// Gets the max number of logical tokens that can be submitted to decode. Must be greater than or equal to `n_ubatch`.
165    #[must_use]
166    pub fn n_batch(&self) -> u32 {
167        unsafe { llama_cpp_sys_4::llama_n_batch(self.context.as_ptr()) }
168    }
169
170    /// Gets the max number of physical tokens (hardware level) to decode in batch. Must be less than or equal to `n_batch`.
171    #[must_use]
172    pub fn n_ubatch(&self) -> u32 {
173        unsafe { llama_cpp_sys_4::llama_n_ubatch(self.context.as_ptr()) }
174    }
175
176    /// Gets the size of the context.
177    #[must_use]
178    pub fn n_ctx(&self) -> u32 {
179        unsafe { llama_cpp_sys_4::llama_n_ctx(self.context.as_ptr()) }
180    }
181
182    /// Decodes the batch.
183    ///
184    /// # Errors
185    ///
186    /// - `DecodeError` if the decoding failed.
187    ///
188    /// # Panics
189    ///
190    /// - the returned [`std::ffi::c_int`] from llama-cpp does not fit into a i32 (this should never happen on most systems)
191    pub fn decode(&mut self, batch: &mut LlamaBatch) -> Result<(), DecodeError> {
192        let result =
193            unsafe { llama_cpp_sys_4::llama_decode(self.context.as_ptr(), batch.llama_batch) };
194        if let Some(failure) = self
195            .tensor_transactions
196            .as_ref()
197            .and_then(|transactions| transactions.failure())
198            .cloned()
199        {
200            return Err(DecodeError::TensorCallback(failure));
201        }
202
203        match NonZeroI32::new(result) {
204            None => {
205                self.initialized_logits
206                    .clone_from(&batch.initialized_logits);
207                Ok(())
208            }
209            Some(error) => Err(DecodeError::from(error)),
210        }
211    }
212
213    /// Run an entry-wise [`LlamaBatchExt`] through this context's encoder or
214    /// decoder.
215    ///
216    /// This is `llama_process`, upstream's entry point for the
217    /// `llama_batch_ext` API. Use it when the batch needs something
218    /// [`LlamaBatch`] cannot express — per-entry embeddings, M-RoPE positions,
219    /// or an MTP state embedding. For a plain token batch,
220    /// [`Self::decode`] and [`Self::encode`] are the same work with less
221    /// ceremony.
222    ///
223    /// # Decode lifecycle hooks do not apply
224    ///
225    /// This crate's tensor transactions
226    /// ([`LlamaContextParams::with_tensor_transactions`](crate::context::params::LlamaContextParams))
227    /// are driven by hooks this repo patches into `llama_decode`.
228    /// `llama_process` reaches the graph without passing through it, and the
229    /// hook signature is built around `llama_batch`, so it cannot describe an
230    /// entry-wise batch at all. Rather than run the batch with the transactions
231    /// silently inert, this returns
232    /// [`ProcessError::TensorTransactionsUnsupported`].
233    ///
234    /// # Errors
235    ///
236    /// - [`ProcessError::ContextMismatch`] if `batch` was built for a
237    ///   different context — it borrows its originating context's batch size,
238    ///   sequence count and memory module, so it is not transferable.
239    /// - [`ProcessError::TensorTransactionsUnsupported`] as described above.
240    /// - [`ProcessError::IncompleteEntry`] if an entry carries no input; see
241    ///   [`LlamaBatchExt::has_incomplete_entry`].
242    /// - Otherwise whatever `llama_decode` would report; the return codes are
243    ///   the same.
244    pub fn process(
245        &mut self,
246        batch: &mut LlamaBatchExt,
247        kind: LlamaProcessType,
248    ) -> Result<(), ProcessError> {
249        if batch.ctx_id != self.id {
250            return Err(ProcessError::ContextMismatch);
251        }
252        if self.tensor_transactions.is_some() {
253            return Err(ProcessError::TensorTransactionsUnsupported);
254        }
255        if let Some(entry) = batch.first_incomplete_entry() {
256            return Err(ProcessError::IncompleteEntry(entry));
257        }
258
259        let result = unsafe {
260            llama_cpp_sys_4::llama_process(self.context.as_ptr(), kind.into(), batch.as_ptr())
261        };
262
263        match NonZeroI32::new(result) {
264            None => {
265                self.initialized_logits
266                    .clone_from(&batch.initialized_logits);
267                Ok(())
268            }
269            Some(error) => Err(ProcessError::from(error)),
270        }
271    }
272
273    /// Returns the owned tensor transaction program attached to this context.
274    #[must_use]
275    pub fn tensor_transactions(&self) -> Option<&TensorTransactions> {
276        self.tensor_transactions.as_deref()
277    }
278
279    /// Returns the owned tensor transaction program attached to this context.
280    ///
281    /// Retained captures can be removed with [`TensorTransactions::take_captures`].
282    pub fn tensor_transactions_mut(&mut self) -> Option<&mut TensorTransactions> {
283        self.tensor_transactions
284            .as_mut()
285            .map(|transactions| transactions.as_mut().get_mut())
286    }
287
288    /// Encodes the batch.
289    ///
290    /// # Errors
291    ///
292    /// - `EncodeError` if the decoding failed.
293    ///
294    /// # Panics
295    ///
296    /// - the returned [`std::ffi::c_int`] from llama-cpp does not fit into a i32 (this should never happen on most systems)
297    pub fn encode(&mut self, batch: &mut LlamaBatch) -> Result<(), EncodeError> {
298        let result =
299            unsafe { llama_cpp_sys_4::llama_encode(self.context.as_ptr(), batch.llama_batch) };
300
301        match NonZeroI32::new(result) {
302            None => {
303                self.initialized_logits
304                    .clone_from(&batch.initialized_logits);
305                Ok(())
306            }
307            Some(error) => Err(EncodeError::from(error)),
308        }
309    }
310
311    /// Return Pooling type for Llama's Context
312    #[must_use]
313    pub fn pooling_type(&self) -> LlamaPoolingType {
314        let pooling_type = unsafe { llama_pooling_type(self.context.as_ptr()) };
315
316        LlamaPoolingType::from(pooling_type)
317    }
318
319    /// Get the embeddings for the `i`th sequence in the current context.
320    ///
321    /// # Returns
322    ///
323    /// A slice containing the embeddings for the last decoded batch.
324    /// The size corresponds to the `n_embd` parameter of the context's model.
325    ///
326    /// # Errors
327    ///
328    /// - When the current context was constructed without enabling embeddings.
329    /// - If the current model had a pooling type of [`llama_cpp_sys_4::LLAMA_POOLING_TYPE_NONE`]
330    /// - If the given sequence index exceeds the max sequence id.
331    ///
332    /// # Panics
333    ///
334    /// * `n_embd` does not fit into a usize
335    pub fn embeddings_seq_ith(&self, i: i32) -> Result<&[f32], EmbeddingsError> {
336        if !self.embeddings_enabled {
337            return Err(EmbeddingsError::NotEnabled);
338        }
339
340        let n_embd =
341            usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
342
343        unsafe {
344            let embedding = llama_cpp_sys_4::llama_get_embeddings_seq(self.context.as_ptr(), i);
345
346            // Technically also possible whenever `i >= max(batch.n_seq)`, but can't check that here.
347            if embedding.is_null() {
348                Err(EmbeddingsError::NonePoolType)
349            } else {
350                Ok(slice::from_raw_parts(embedding, n_embd))
351            }
352        }
353    }
354
355    /// Get the embeddings for the `i`th token in the current context.
356    ///
357    /// # Returns
358    ///
359    /// A slice containing the embeddings for the last decoded batch of the given token.
360    /// The size corresponds to the `n_embd` parameter of the context's model.
361    ///
362    /// # Errors
363    ///
364    /// - When the current context was constructed without enabling embeddings.
365    /// - When the given token didn't have logits enabled when it was passed.
366    /// - If the given token index exceeds the max token id.
367    ///
368    /// # Panics
369    ///
370    /// * `n_embd` does not fit into a usize
371    pub fn embeddings_ith(&self, i: i32) -> Result<&[f32], EmbeddingsError> {
372        if !self.embeddings_enabled {
373            return Err(EmbeddingsError::NotEnabled);
374        }
375
376        let n_embd =
377            usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
378
379        unsafe {
380            let embedding = llama_cpp_sys_4::llama_get_embeddings_ith(self.context.as_ptr(), i);
381            // Technically also possible whenever `i >= batch.n_tokens`, but no good way of checking `n_tokens` here.
382            if embedding.is_null() {
383                Err(EmbeddingsError::LogitsNotEnabled)
384            } else {
385                Ok(slice::from_raw_parts(embedding, n_embd))
386            }
387        }
388    }
389
390    /// Get the logits for the last token in the context.
391    ///
392    /// # Returns
393    /// An iterator over unsorted `LlamaTokenData` containing the
394    /// logits for the last token in the context.
395    ///
396    /// # Panics
397    ///
398    /// - underlying logits data is null
399    pub fn candidates(&self) -> impl Iterator<Item = LlamaTokenData> + '_ {
400        (0_i32..).zip(self.get_logits()).map(|(i, logit)| {
401            let token = LlamaToken::new(i);
402            LlamaTokenData::new(token, *logit, 0_f32)
403        })
404    }
405
406    /// Get the token data array for the last token in the context.
407    ///
408    /// This is a convience method that implements:
409    /// ```ignore
410    /// LlamaTokenDataArray::from_iter(ctx.candidates(), false)
411    /// ```
412    ///
413    /// # Panics
414    ///
415    /// - underlying logits data is null
416    #[must_use]
417    pub fn token_data_array(&self) -> LlamaTokenDataArray {
418        LlamaTokenDataArray::from_iter(self.candidates(), false)
419    }
420
421    /// Token logits obtained from the last call to `decode()`.
422    /// The logits for which `batch.logits[i] != 0` are stored contiguously
423    /// in the order they have appeared in the batch.
424    /// Rows: number of tokens for which `batch.logits[i] != 0`
425    /// Cols: `n_vocab`
426    ///
427    /// # Returns
428    ///
429    /// A slice containing the logits for the last decoded token.
430    /// The size corresponds to the `n_vocab` parameter of the context's model.
431    ///
432    /// # Panics
433    ///
434    /// - `n_vocab` does not fit into a usize
435    /// - token data returned is null
436    #[must_use]
437    pub fn get_logits(&self) -> &[f32] {
438        let data = unsafe { llama_cpp_sys_4::llama_get_logits(self.context.as_ptr()) };
439        assert!(!data.is_null(), "logits data for last token is null");
440        let len = usize::try_from(self.model.n_vocab()).expect("n_vocab does not fit into a usize");
441
442        unsafe { slice::from_raw_parts(data, len) }
443    }
444
445    /// Get the logits for the ith token in the context.
446    ///
447    /// # Panics
448    ///
449    /// - logit `i` is not initialized.
450    pub fn candidates_ith(&self, i: i32) -> impl Iterator<Item = LlamaTokenData> + '_ {
451        (0_i32..).zip(self.get_logits_ith(i)).map(|(i, logit)| {
452            let token = LlamaToken::new(i);
453            LlamaTokenData::new(token, *logit, 0_f32)
454        })
455    }
456
457    /// Get the logits for the ith token in the context.
458    ///
459    /// # Panics
460    ///
461    /// - `i` is greater than `n_ctx`
462    /// - `n_vocab` does not fit into a usize
463    /// - logit `i` is not initialized.
464    #[must_use]
465    pub fn get_logits_ith(&self, i: i32) -> &[f32] {
466        assert!(
467            self.initialized_logits.contains(&i),
468            "logit {i} is not initialized. only {:?} is",
469            self.initialized_logits
470        );
471        assert!(
472            self.n_ctx() > u32::try_from(i).expect("i does not fit into a u32"),
473            "n_ctx ({}) must be greater than i ({})",
474            self.n_ctx(),
475            i
476        );
477
478        let data = unsafe { llama_cpp_sys_4::llama_get_logits_ith(self.context.as_ptr(), i) };
479        let len = usize::try_from(self.model.n_vocab()).expect("n_vocab does not fit into a usize");
480
481        unsafe { slice::from_raw_parts(data, len) }
482    }
483
484    /// Get the number of context tokens per sequence.
485    #[must_use]
486    pub fn n_ctx_seq(&self) -> u32 {
487        unsafe { llama_cpp_sys_4::llama_n_ctx_seq(self.context.as_ptr()) }
488    }
489
490    /// Get the maximum number of sequences.
491    #[must_use]
492    pub fn n_seq_max(&self) -> u32 {
493        unsafe { llama_cpp_sys_4::llama_n_seq_max(self.context.as_ptr()) }
494    }
495
496    /// Get the number of recurrent-state snapshots per sequence.
497    #[must_use]
498    pub fn n_rs_seq(&self) -> u32 {
499        unsafe { llama_cpp_sys_4::llama_n_rs_seq(self.context.as_ptr()) }
500    }
501
502    /// Get the number of threads used for generation.
503    #[must_use]
504    pub fn n_threads(&self) -> i32 {
505        unsafe { llama_cpp_sys_4::llama_n_threads(self.context.as_ptr()) }
506    }
507
508    /// Get the number of threads used for batch processing.
509    #[must_use]
510    pub fn n_threads_batch(&self) -> i32 {
511        unsafe { llama_cpp_sys_4::llama_n_threads_batch(self.context.as_ptr()) }
512    }
513
514    /// Set the number of threads used for generation and batch processing.
515    pub fn set_n_threads(&mut self, n_threads: i32, n_threads_batch: i32) {
516        unsafe {
517            llama_cpp_sys_4::llama_set_n_threads(self.context.as_ptr(), n_threads, n_threads_batch);
518        }
519    }
520
521    /// Set whether to use causal attention.
522    ///
523    /// If set to `false`, the model will use non-causal attention, which is
524    /// needed for embedding models.
525    pub fn set_causal_attn(&mut self, causal_attn: bool) {
526        unsafe {
527            llama_cpp_sys_4::llama_set_causal_attn(self.context.as_ptr(), causal_attn);
528        }
529    }
530
531    /// Whether the context is currently using causal attention.
532    ///
533    /// The counterpart to [`Self::set_causal_attn`]. Worth reading back rather
534    /// than tracking yourself: the initial value comes from
535    /// [`LlamaContextParams::with_attention_type`](crate::context::params::LlamaContextParams::with_attention_type)
536    /// via the model's own default, not from anything the caller set.
537    #[must_use]
538    pub fn causal_attn(&self) -> bool {
539        unsafe { llama_cpp_sys_4::llama_get_causal_attn(self.context.as_ptr()) }
540    }
541
542    /// Set whether to compute embeddings.
543    ///
544    /// This allows toggling embedding mode at runtime (as opposed to only at
545    /// context creation time).
546    pub fn set_embeddings(&mut self, embeddings: bool) {
547        self.embeddings_enabled = embeddings;
548        unsafe {
549            llama_cpp_sys_4::llama_set_embeddings(self.context.as_ptr(), embeddings);
550        }
551    }
552
553    /// Mark the next computation as a warmup run.
554    ///
555    /// Warmup runs are useful for GPU backends to compile kernels before
556    /// actual inference begins.
557    pub fn set_warmup(&mut self, warmup: bool) {
558        unsafe {
559            llama_cpp_sys_4::llama_set_warmup(self.context.as_ptr(), warmup);
560        }
561    }
562
563    /// Wait for all pending async computations to finish.
564    pub fn synchronize(&mut self) {
565        unsafe {
566            llama_cpp_sys_4::llama_synchronize(self.context.as_ptr());
567        }
568    }
569
570    /// Get all embeddings for the current context.
571    ///
572    /// Returns a slice of all embeddings from the last decoded batch.
573    /// For pooled embeddings use [`embeddings_seq_ith`](Self::embeddings_seq_ith) instead.
574    ///
575    /// # Errors
576    ///
577    /// - When the current context was constructed without enabling embeddings.
578    /// - If the embeddings pointer is null.
579    ///
580    /// # Panics
581    ///
582    /// * `n_embd` does not fit into a usize
583    pub fn get_embeddings(&self) -> Result<&[f32], EmbeddingsError> {
584        if !self.embeddings_enabled {
585            return Err(EmbeddingsError::NotEnabled);
586        }
587
588        let n_embd =
589            usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
590
591        unsafe {
592            let embedding = llama_cpp_sys_4::llama_get_embeddings(self.context.as_ptr());
593            if embedding.is_null() {
594                Err(EmbeddingsError::NonePoolType)
595            } else {
596                Ok(slice::from_raw_parts(embedding, n_embd))
597            }
598        }
599    }
600
601    /// Toggle extraction of next-n embeddings (Rust name: pre-norm) — hidden
602    /// states used by MTP draft heads. Upstream C API: `llama_set_embeddings_nextn`
603    /// (llama.cpp PR #23198 and later renames).
604    ///
605    /// If `masked` is `true`, pre-norm rows are extracted only for tokens
606    /// whose `batch.logits[i]` is non-zero. If `masked` is `false`, rows are
607    /// extracted for every token in the batch regardless of `batch.logits` —
608    /// callers can then leave `batch.logits[i] = false` on prompt-fill
609    /// positions and avoid copying the full logits row for each one.
610    ///
611    /// Upstream's MTP session init configures pre-norm extraction on the target
612    /// and draft contexts automatically. Call this manually only for custom
613    /// speculative setups.
614    pub fn set_embeddings_pre_norm(&mut self, value: bool, masked: bool) {
615        unsafe {
616            llama_cpp_sys_4::llama_set_embeddings_nextn(self.context.as_ptr(), value, masked);
617        }
618    }
619
620    /// Get the full pre-norm embeddings buffer for the last decoded batch.
621    ///
622    /// Returns `None` when pre-norm embeddings are disabled or the buffer
623    /// hasn't been populated. The length of the returned slice is
624    /// `n_embd * <number of pre-norm rows>` — interpretation of the row
625    /// count depends on whether the setter was called with `masked=true`
626    /// (one row per sampled token) or `masked=false` (one row per batch
627    /// token). Use [`get_embeddings_pre_norm_ith`](Self::get_embeddings_pre_norm_ith)
628    /// when you only need a single row.
629    ///
630    /// # Panics
631    ///
632    /// Panics if `n_embd` does not fit in `usize`.
633    #[must_use]
634    pub fn get_embeddings_pre_norm(&self) -> Option<&[f32]> {
635        let n_embd =
636            usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
637        unsafe {
638            let p = llama_cpp_sys_4::llama_get_embeddings_nextn(self.context.as_ptr());
639            if p.is_null() {
640                None
641            } else {
642                Some(slice::from_raw_parts(p, n_embd))
643            }
644        }
645    }
646
647    /// Get the pre-norm embedding row for the `i`th output position of the
648    /// last decoded batch. Returns `None` if upstream rejects the index
649    /// (e.g. masked mode with `batch.logits[i] == 0`, or out of range).
650    ///
651    /// # Panics
652    ///
653    /// Panics if `n_embd` does not fit in `usize`.
654    #[must_use]
655    pub fn get_embeddings_pre_norm_ith(&self, i: i32) -> Option<&[f32]> {
656        let n_embd =
657            usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
658        unsafe {
659            let p = llama_cpp_sys_4::llama_get_embeddings_nextn_ith(self.context.as_ptr(), i);
660            if p.is_null() {
661                None
662            } else {
663                Some(slice::from_raw_parts(p, n_embd))
664            }
665        }
666    }
667
668    /// Select which `NextN` block the MTP draft graph runs.
669    ///
670    /// `offset` indexes past the trunk transformer layers (`0` = first `NextN`
671    /// head). Required for multi-head MTP models such as Step3.5; restore to
672    /// `0` after drafting. See [`crate::mtp`] for the full speculative loop.
673    ///
674    /// # Examples
675    ///
676    /// ```no_run
677    /// # use llama_cpp_4::model::LlamaModel;
678    /// # use llama_cpp_4::mtp::MtpSession;
679    /// # use llama_cpp_4::token::LlamaToken;
680    /// # fn f(model: &LlamaModel, session: &mut MtpSession<'_, '_>, n_past: i32,
681    /// #      last_token: LlamaToken) -> Result<(), Box<dyn std::error::Error>> {
682    /// for head in 0..model.n_layer_nextn() {
683    ///     // Via the session: it borrows the draft context exclusively.
684    ///     session.draft_context_mut().set_nextn_layer_offset(head);
685    ///     let drafts = session.draft(0, n_past, last_token)?;
686    /// }
687    /// session.draft_context_mut().set_nextn_layer_offset(0);
688    /// # Ok(())
689    /// # }
690    /// ```
691    pub fn set_nextn_layer_offset(&mut self, offset: i32) {
692        unsafe {
693            llama_cpp_sys_4::llama_set_nextn_layer_offset(self.context.as_ptr(), offset);
694        }
695    }
696
697    /// Return the paired context set via
698    /// [`crate::context::params::LlamaContextParams::with_ctx_other`].
699    ///
700    /// The pointer refers to the other live context created during
701    /// [`crate::model::LlamaModel::new_context`]; it is `None` when no pairing
702    /// was configured.
703    #[must_use]
704    pub fn ctx_other(&self) -> Option<NonNull<llama_cpp_sys_4::llama_context>> {
705        NonNull::new(unsafe { llama_cpp_sys_4::llama_get_ctx_other(self.context.as_ptr()) })
706    }
707
708    /// Reset the timings for the context.
709    pub fn reset_timings(&mut self) {
710        unsafe { llama_cpp_sys_4::ggml_time_init() }
711    }
712
713    /// Returns the timings for the context.
714    pub fn timings(&mut self) -> PerfContextData {
715        let perf_context_data =
716            unsafe { llama_cpp_sys_4::llama_perf_context(self.context.as_ptr()) };
717        PerfContextData { perf_context_data }
718    }
719
720    /// Reset the performance counters for the context.
721    pub fn perf_context_reset(&mut self) {
722        unsafe { llama_cpp_sys_4::llama_perf_context_reset(self.context.as_ptr()) }
723    }
724
725    /// Check if the KV cache memory supports shifting.
726    #[must_use]
727    pub fn memory_can_shift(&self) -> bool {
728        unsafe {
729            let mem = llama_cpp_sys_4::llama_get_memory(self.context.as_ptr());
730            llama_cpp_sys_4::llama_memory_can_shift(mem)
731        }
732    }
733
734    /// Get the minimum position in a sequence's KV cache.
735    #[must_use]
736    pub fn memory_seq_pos_min(&self, seq_id: i32) -> i32 {
737        unsafe {
738            let mem = llama_cpp_sys_4::llama_get_memory(self.context.as_ptr());
739            llama_cpp_sys_4::llama_memory_seq_pos_min(mem, seq_id)
740        }
741    }
742
743    /// Print a human-readable memory breakdown to stderr via llama.cpp.
744    ///
745    /// For structured access use [`Self::memory_breakdown`].
746    pub fn memory_breakdown_print(&self) {
747        unsafe {
748            llama_cpp_sys_4::common_memory_breakdown_print(self.context.as_ptr());
749        }
750    }
751
752    /// Return structured per-buffer memory usage for this context.
753    ///
754    /// Each [`memory_breakdown::MemoryBreakdownEntry`] reports model weights,
755    /// KV / recurrent cache, and compute scratch bytes for one ggml buffer
756    /// type. Returns an empty vector when no buffers are registered.
757    ///
758    /// # Examples
759    ///
760    /// ```no_run
761    /// use llama_cpp_4::prelude::*;
762    ///
763    /// fn main() {
764    ///     let backend = LlamaBackend::init().unwrap();
765    ///     let model = LlamaModel::load_from_file(&backend, "model.gguf", &LlamaModelParams::default()).unwrap();
766    ///     let ctx = model.new_context(&backend, LlamaContextParams::default()).unwrap();
767    ///     let total: usize = ctx.memory_breakdown().iter().map(|e| e.total()).sum();
768    ///     println!("context uses {total} bytes across all buffer types");
769    /// }
770    /// ```
771    #[must_use]
772    pub fn memory_breakdown(&self) -> Vec<memory_breakdown::MemoryBreakdownEntry> {
773        memory_breakdown::collect_memory_breakdown(self.context.as_ptr())
774    }
775
776    /// Enable or disable extraction of input embeddings for a transformer layer.
777    ///
778    /// Maps to `llama_set_embeddings_layer_inp`. After a successful
779    /// [`Self::decode`], read the vector with [`Self::get_embeddings_layer_inp`].
780    pub fn set_embeddings_layer_inp(&mut self, layer_id: u32, value: bool) {
781        unsafe {
782            llama_cpp_sys_4::llama_set_embeddings_layer_inp(self.context.as_ptr(), layer_id, value);
783        }
784    }
785
786    /// Get input embeddings for `layer_id` from the last decoded batch.
787    ///
788    /// Returns `None` when the layer was not enabled via
789    /// [`Self::set_embeddings_layer_inp`] or when upstream has no data for
790    /// `layer_id`. The slice length is [`LlamaModel::n_embd`].
791    ///
792    /// # Panics
793    ///
794    /// Panics if `n_embd` does not fit in `usize`.
795    #[must_use]
796    pub fn get_embeddings_layer_inp(&self, layer_id: u32) -> Option<&[f32]> {
797        let n_embd =
798            usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
799        unsafe {
800            let p =
801                llama_cpp_sys_4::llama_get_embeddings_layer_inp(self.context.as_ptr(), layer_id);
802            if p.is_null() {
803                None
804            } else {
805                Some(slice::from_raw_parts(p, n_embd))
806            }
807        }
808    }
809
810    /// Get the size of the full context state in bytes.
811    ///
812    /// This is the size needed for [`state_get_data`](Self::state_get_data) and
813    /// [`state_set_data`](Self::state_set_data).
814    #[must_use]
815    pub fn state_get_size(&mut self) -> usize {
816        unsafe { llama_cpp_sys_4::llama_state_get_size(self.context.as_ptr()) }
817    }
818
819    /// Copy the full context state into a byte buffer.
820    ///
821    /// The buffer must be at least [`state_get_size`](Self::state_get_size) bytes.
822    ///
823    /// Returns the number of bytes written.
824    pub fn state_get_data(&mut self, dst: &mut [u8]) -> usize {
825        unsafe {
826            llama_cpp_sys_4::llama_state_get_data(
827                self.context.as_ptr(),
828                dst.as_mut_ptr(),
829                dst.len(),
830            )
831        }
832    }
833
834    /// Restore the full context state from a byte buffer.
835    ///
836    /// Returns the number of bytes read.
837    pub fn state_set_data(&mut self, src: &[u8]) -> usize {
838        unsafe {
839            llama_cpp_sys_4::llama_state_set_data(self.context.as_ptr(), src.as_ptr(), src.len())
840        }
841    }
842
843    /// Save the context state to a file along with the given tokens.
844    ///
845    /// Returns `true` on success.
846    ///
847    /// # Panics
848    ///
849    /// Panics if the path contains null bytes.
850    pub fn state_save_file(
851        &mut self,
852        path: impl AsRef<std::path::Path>,
853        tokens: &[LlamaToken],
854    ) -> bool {
855        let path_str = path.as_ref().to_str().expect("path is not valid UTF-8");
856        let c_path = std::ffi::CString::new(path_str).expect("path contains null bytes");
857        unsafe {
858            llama_cpp_sys_4::llama_state_save_file(
859                self.context.as_ptr(),
860                c_path.as_ptr(),
861                tokens.as_ptr().cast(),
862                tokens.len(),
863            )
864        }
865    }
866
867    /// Load a context state from a file.
868    ///
869    /// Returns `true` on success and fills `tokens_out` with the saved tokens.
870    ///
871    /// # Panics
872    ///
873    /// Panics if the path contains null bytes.
874    pub fn state_load_file(
875        &mut self,
876        path: impl AsRef<std::path::Path>,
877        tokens_out: &mut Vec<LlamaToken>,
878        n_token_capacity: usize,
879    ) -> bool {
880        tokens_out.resize(n_token_capacity, LlamaToken(0));
881        let mut n_token_count: usize = 0;
882        let path_str = path.as_ref().to_str().expect("path is not valid UTF-8");
883        let c_path = std::ffi::CString::new(path_str).expect("path contains null bytes");
884        let ok = unsafe {
885            llama_cpp_sys_4::llama_state_load_file(
886                self.context.as_ptr(),
887                c_path.as_ptr(),
888                tokens_out.as_mut_ptr().cast(),
889                n_token_capacity,
890                std::ptr::addr_of_mut!(n_token_count),
891            )
892        };
893        if ok {
894            tokens_out.truncate(n_token_count);
895        }
896        ok
897    }
898
899    /// Get the size of a single sequence's state in bytes.
900    #[must_use]
901    pub fn state_seq_get_size(&mut self, seq_id: i32) -> usize {
902        unsafe { llama_cpp_sys_4::llama_state_seq_get_size(self.context.as_ptr(), seq_id) }
903    }
904
905    /// Copy a single sequence's state into a byte buffer.
906    ///
907    /// Returns the number of bytes written.
908    pub fn state_seq_get_data(&mut self, dst: &mut [u8], seq_id: i32) -> usize {
909        unsafe {
910            llama_cpp_sys_4::llama_state_seq_get_data(
911                self.context.as_ptr(),
912                dst.as_mut_ptr(),
913                dst.len(),
914                seq_id,
915            )
916        }
917    }
918
919    /// Restore a single sequence's state from a byte buffer.
920    ///
921    /// Returns the number of bytes read.
922    pub fn state_seq_set_data(&mut self, src: &[u8], dest_seq_id: i32) -> usize {
923        unsafe {
924            llama_cpp_sys_4::llama_state_seq_set_data(
925                self.context.as_ptr(),
926                src.as_ptr(),
927                src.len(),
928                dest_seq_id,
929            )
930        }
931    }
932
933    /// Save a single sequence's state to a file.
934    ///
935    /// Returns the number of bytes written (0 on failure).
936    ///
937    /// # Panics
938    ///
939    /// Panics if the path contains null bytes.
940    pub fn state_seq_save_file(
941        &mut self,
942        path: impl AsRef<std::path::Path>,
943        seq_id: i32,
944        tokens: &[LlamaToken],
945    ) -> usize {
946        let path_str = path.as_ref().to_str().expect("path is not valid UTF-8");
947        let c_path = std::ffi::CString::new(path_str).expect("path contains null bytes");
948        unsafe {
949            llama_cpp_sys_4::llama_state_seq_save_file(
950                self.context.as_ptr(),
951                c_path.as_ptr(),
952                seq_id,
953                tokens.as_ptr().cast(),
954                tokens.len(),
955            )
956        }
957    }
958
959    /// Load a single sequence's state from a file.
960    ///
961    /// Returns the number of bytes read (0 on failure).
962    ///
963    /// # Panics
964    ///
965    /// Panics if the path contains null bytes.
966    pub fn state_seq_load_file(
967        &mut self,
968        path: impl AsRef<std::path::Path>,
969        dest_seq_id: i32,
970        tokens_out: &mut Vec<LlamaToken>,
971        n_token_capacity: usize,
972    ) -> usize {
973        tokens_out.resize(n_token_capacity, LlamaToken(0));
974        let mut n_token_count: usize = 0;
975        let path_str = path.as_ref().to_str().expect("path is not valid UTF-8");
976        let c_path = std::ffi::CString::new(path_str).expect("path contains null bytes");
977        let ret = unsafe {
978            llama_cpp_sys_4::llama_state_seq_load_file(
979                self.context.as_ptr(),
980                c_path.as_ptr(),
981                dest_seq_id,
982                tokens_out.as_mut_ptr().cast(),
983                n_token_capacity,
984                std::ptr::addr_of_mut!(n_token_count),
985            )
986        };
987        if ret > 0 {
988            tokens_out.truncate(n_token_count);
989        }
990        ret
991    }
992
993    /// Set a control vector on the context.
994    ///
995    /// # Parameters
996    ///
997    /// - `data`: The control vector data (embedding values). Pass an empty slice to clear.
998    /// - `n_embd`: The embedding dimension.
999    /// - `il_start`: The starting layer index (inclusive).
1000    /// - `il_end`: The ending layer index (exclusive).
1001    ///
1002    /// # Errors
1003    ///
1004    /// Returns `Err` with the error code if the operation fails.
1005    pub fn set_adapter_cvec(
1006        &mut self,
1007        data: &[f32],
1008        n_embd: i32,
1009        il_start: i32,
1010        il_end: i32,
1011    ) -> Result<(), i32> {
1012        let ret = unsafe {
1013            llama_cpp_sys_4::llama_set_adapter_cvec(
1014                self.context.as_ptr(),
1015                data.as_ptr(),
1016                data.len(),
1017                n_embd,
1018                il_start,
1019                il_end,
1020            )
1021        };
1022        if ret != 0 {
1023            Err(ret)
1024        } else {
1025            Ok(())
1026        }
1027    }
1028
1029    /// Get sampled token debug info for the `i`th position.
1030    ///
1031    /// Returns the sampled token at position `i` from the last decode call.
1032    #[must_use]
1033    pub fn get_sampled_token_ith(&self, i: i32) -> LlamaToken {
1034        let token =
1035            unsafe { llama_cpp_sys_4::llama_get_sampled_token_ith(self.context.as_ptr(), i) };
1036        LlamaToken(token)
1037    }
1038
1039    /// Get sampled candidate tokens for the `i`th position.
1040    ///
1041    /// Returns a slice of candidate tokens from the last decode call.
1042    #[must_use]
1043    pub fn get_sampled_candidates_ith(&self, i: i32) -> &[LlamaToken] {
1044        let count = unsafe {
1045            llama_cpp_sys_4::llama_get_sampled_candidates_count_ith(self.context.as_ptr(), i)
1046        } as usize;
1047        if count == 0 {
1048            return &[];
1049        }
1050        let ptr =
1051            unsafe { llama_cpp_sys_4::llama_get_sampled_candidates_ith(self.context.as_ptr(), i) };
1052        if ptr.is_null() {
1053            return &[];
1054        }
1055        unsafe { slice::from_raw_parts(ptr.cast::<LlamaToken>(), count) }
1056    }
1057
1058    /// Get the number of sampled logits for the `i`th position.
1059    #[must_use]
1060    pub fn get_sampled_logits_count_ith(&self, i: i32) -> u32 {
1061        unsafe { llama_cpp_sys_4::llama_get_sampled_logits_count_ith(self.context.as_ptr(), i) }
1062    }
1063
1064    /// Get sampled logits for the `i`th position.
1065    ///
1066    /// Returns a slice of logit values from the last decode call.
1067    #[must_use]
1068    pub fn get_sampled_logits_ith(&self, i: i32) -> &[f32] {
1069        let count = self.get_sampled_logits_count_ith(i) as usize;
1070        if count == 0 {
1071            return &[];
1072        }
1073        let ptr =
1074            unsafe { llama_cpp_sys_4::llama_get_sampled_logits_ith(self.context.as_ptr(), i) };
1075        if ptr.is_null() {
1076            return &[];
1077        }
1078        unsafe { slice::from_raw_parts(ptr, count) }
1079    }
1080
1081    /// Get the number of sampled probabilities for the `i`th position.
1082    #[must_use]
1083    pub fn get_sampled_probs_count_ith(&self, i: i32) -> u32 {
1084        unsafe { llama_cpp_sys_4::llama_get_sampled_probs_count_ith(self.context.as_ptr(), i) }
1085    }
1086
1087    /// Get sampled probabilities for the `i`th position.
1088    ///
1089    /// Returns a slice of probability values from the last decode call.
1090    #[must_use]
1091    pub fn get_sampled_probs_ith(&self, i: i32) -> &[f32] {
1092        let count = self.get_sampled_probs_count_ith(i) as usize;
1093        if count == 0 {
1094            return &[];
1095        }
1096        let ptr = unsafe { llama_cpp_sys_4::llama_get_sampled_probs_ith(self.context.as_ptr(), i) };
1097        if ptr.is_null() {
1098            return &[];
1099        }
1100        unsafe { slice::from_raw_parts(ptr, count) }
1101    }
1102
1103    /// Get the size of a single sequence's state with flags.
1104    #[must_use]
1105    pub fn state_seq_get_size_ext(&mut self, seq_id: i32, flags: u32) -> usize {
1106        unsafe {
1107            llama_cpp_sys_4::llama_state_seq_get_size_ext(self.context.as_ptr(), seq_id, flags)
1108        }
1109    }
1110
1111    /// Copy a single sequence's state into a byte buffer with flags.
1112    ///
1113    /// Returns the number of bytes written.
1114    pub fn state_seq_get_data_ext(&mut self, dst: &mut [u8], seq_id: i32, flags: u32) -> usize {
1115        unsafe {
1116            llama_cpp_sys_4::llama_state_seq_get_data_ext(
1117                self.context.as_ptr(),
1118                dst.as_mut_ptr(),
1119                dst.len(),
1120                seq_id,
1121                flags,
1122            )
1123        }
1124    }
1125
1126    /// Restore a single sequence's state from a byte buffer with flags.
1127    ///
1128    /// Returns the number of bytes read.
1129    pub fn state_seq_set_data_ext(&mut self, src: &[u8], dest_seq_id: i32, flags: u32) -> usize {
1130        unsafe {
1131            llama_cpp_sys_4::llama_state_seq_set_data_ext(
1132                self.context.as_ptr(),
1133                src.as_ptr(),
1134                src.len(),
1135                dest_seq_id,
1136                flags,
1137            )
1138        }
1139    }
1140
1141    /// Set an abort callback for the context.
1142    ///
1143    /// The callback is called periodically during computation. If it returns `true`,
1144    /// the computation is aborted.
1145    ///
1146    /// # Safety
1147    ///
1148    /// The callback data must remain valid for the lifetime of the context or until
1149    /// the callback is replaced.
1150    pub unsafe fn set_abort_callback(
1151        &mut self,
1152        callback: llama_cpp_sys_4::ggml_abort_callback,
1153        data: *mut std::ffi::c_void,
1154    ) {
1155        llama_cpp_sys_4::llama_set_abort_callback(self.context.as_ptr(), callback, data);
1156    }
1157
1158    /// Attach a thread pool to the context.
1159    ///
1160    /// # Safety
1161    ///
1162    /// The thread pools must remain valid for the lifetime of the context or until
1163    /// they are detached.
1164    pub unsafe fn attach_threadpool(
1165        &mut self,
1166        threadpool: llama_cpp_sys_4::ggml_threadpool_t,
1167        threadpool_batch: llama_cpp_sys_4::ggml_threadpool_t,
1168    ) {
1169        llama_cpp_sys_4::llama_attach_threadpool(
1170            self.context.as_ptr(),
1171            threadpool,
1172            threadpool_batch,
1173        );
1174    }
1175
1176    /// Detach the thread pool from the context.
1177    pub fn detach_threadpool(&mut self) {
1178        unsafe {
1179            llama_cpp_sys_4::llama_detach_threadpool(self.context.as_ptr());
1180        }
1181    }
1182
1183    /// Set a sampler for a specific sequence.
1184    ///
1185    /// Returns `true` on success.
1186    pub fn set_sampler(
1187        &mut self,
1188        seq_id: i32,
1189        sampler: &mut crate::sampling::LlamaSampler,
1190    ) -> bool {
1191        unsafe {
1192            llama_cpp_sys_4::llama_set_sampler(
1193                self.context.as_ptr(),
1194                seq_id,
1195                sampler.sampler.as_ptr(),
1196            )
1197        }
1198    }
1199
1200    /// Get the raw model pointer from this context.
1201    ///
1202    /// This is mainly useful for FFI interop. In normal usage, access
1203    /// the model via the `model` field instead.
1204    #[must_use]
1205    pub fn get_model_ptr(&self) -> *const llama_cpp_sys_4::llama_model {
1206        unsafe { llama_cpp_sys_4::llama_get_model(self.context.as_ptr()) }
1207    }
1208
1209    /// Sets a lora adapter.
1210    ///
1211    /// # Errors
1212    ///
1213    /// See [`LlamaLoraAdapterSetError`] for more information.
1214    pub fn lora_adapter_set(
1215        &self,
1216        adapter: &mut LlamaLoraAdapter,
1217        scale: f32,
1218    ) -> Result<(), LlamaLoraAdapterSetError> {
1219        let err_code = unsafe {
1220            // llama_set_adapter_lora / llama_rm_adapter_lora were replaced by llama_set_adapters_lora
1221            // which takes a full list of adapters + scales at once (b8249+)
1222            let mut adapter_ptr = adapter.lora_adapter.as_ptr();
1223            let mut scale_val = scale;
1224            llama_cpp_sys_4::llama_set_adapters_lora(
1225                self.context.as_ptr(),
1226                &raw mut adapter_ptr,
1227                1,
1228                &raw mut scale_val,
1229            )
1230        };
1231        if err_code != 0 {
1232            return Err(LlamaLoraAdapterSetError::ErrorResult(err_code));
1233        }
1234
1235        tracing::debug!("Set lora adapter");
1236        Ok(())
1237    }
1238
1239    /// Remove all lora adapters from the context.
1240    ///
1241    /// Note: as of llama.cpp b8249 the per-adapter remove API was replaced by
1242    /// `llama_set_adapters_lora` which operates on the full adapter list at once.
1243    /// Calling this function clears **all** adapters currently set on the context.
1244    ///
1245    /// # Errors
1246    ///
1247    /// See [`LlamaLoraAdapterRemoveError`] for more information.
1248    pub fn lora_adapter_remove(
1249        &self,
1250        _adapter: &mut LlamaLoraAdapter,
1251    ) -> Result<(), LlamaLoraAdapterRemoveError> {
1252        let err_code = unsafe {
1253            llama_cpp_sys_4::llama_set_adapters_lora(
1254                self.context.as_ptr(),
1255                std::ptr::null_mut(),
1256                0,
1257                std::ptr::null_mut(),
1258            )
1259        };
1260        if err_code != 0 {
1261            return Err(LlamaLoraAdapterRemoveError::ErrorResult(err_code));
1262        }
1263
1264        tracing::debug!("Remove lora adapter");
1265        Ok(())
1266    }
1267}
1268
1269impl Drop for LlamaContext<'_> {
1270    fn drop(&mut self) {
1271        unsafe { llama_cpp_sys_4::llama_free(self.context.as_ptr()) }
1272    }
1273}