Skip to main content

llama_cpp_4/
lib.rs

1//! Bindings to the llama.cpp library.
2//!
3//! As llama.cpp is a very fast moving target, this crate does not attempt to create a stable API
4//! with all the rust idioms. Instead it provides safe wrappers around nearly direct bindings to
5//! llama.cpp. This makes it easier to keep up with the changes in llama.cpp, but does mean that
6//! the API is not as nice as it could be.
7//!
8//! # Quick start
9//!
10//! ```no_run
11//! use llama_cpp_4::prelude::*;
12//! use std::num::NonZeroU32;
13//!
14//! fn main() {
15//!     let backend = LlamaBackend::init().unwrap();
16//!     let model = LlamaModel::load_from_file(
17//!         &backend,
18//!         "model.gguf",
19//!         &LlamaModelParams::default(),
20//!     )
21//!     .unwrap();
22//!     let mut ctx = model
23//!         .new_context(
24//!             &backend,
25//!             LlamaContextParams::default().with_n_ctx(NonZeroU32::new(2048)),
26//!         )
27//!         .unwrap();
28//!
29//!     let tokens = model.str_to_token("Hello, world!", AddBos::Always).unwrap();
30//!     let mut batch = LlamaBatch::new(512, 1);
31//!     for (i, &tok) in tokens.iter().enumerate() {
32//!         batch
33//!             .add(tok, i as i32, &[0], i == tokens.len() - 1)
34//!             .unwrap();
35//!     }
36//!     ctx.decode(&mut batch).unwrap();
37//!
38//!     let token = LlamaSampler::greedy().sample(&ctx, 0);
39//!     let _piece = model.token_to_bytes(token, Special::Plaintext).unwrap();
40//! }
41//! ```
42//!
43//! # Examples in this repository
44//!
45//! - [simple](https://github.com/eugenehp/llama-cpp-rs/tree/main/examples/simple)
46//! - [chat](https://github.com/eugenehp/llama-cpp-rs/tree/main/examples/chat)
47//! - [embeddings](https://github.com/eugenehp/llama-cpp-rs/tree/main/examples/embeddings)
48//! - [server](https://github.com/eugenehp/llama-cpp-rs/tree/main/examples/server)
49//! - [mtp](https://github.com/eugenehp/llama-cpp-rs/tree/main/examples/mtp) — MTP speculative decoding via [`crate::mtp::MtpSession`]
50//! - [eagle](https://github.com/eugenehp/llama-cpp-rs/tree/main/examples/eagle) — EAGLE-3 speculative decoding via [`crate::eagle::Eagle3Session`]
51//!
52//! # Advanced: tensor capture
53//!
54//! Use [`TensorCapture`] with [`LlamaContextParams::with_tensor_capture`] to read
55//! per-layer hidden states (or other named graph nodes) during
56//! [`LlamaContext::decode`]. See [`context::tensor_capture`] for a full example.
57//!
58//! # Prelude
59//!
60//! For the types used in most inference programs, import [`prelude`]:
61//!
62//! ```
63//! use llama_cpp_4::prelude::*;
64//! ```
65//!
66//! The same core types are also re-exported at the crate root (e.g.
67//! [`LlamaModel`], [`LlamaBackend`]) so you can pick whichever import style
68//! you prefer. See [`prelude`] for a full list and additional examples (chat,
69//! embeddings, memory estimation).
70//!
71//! # Feature Flags
72//!
73//! - `cuda` enables CUDA GPU support.
74//! - `metal` enables Apple Metal GPU support.
75//! - `vulkan` enables Vulkan GPU support (AMD / Intel / cross-platform).
76//! - `native` enables host-CPU optimisations (`-march=native`).
77//! - `openmp` enables OpenMP multi-core CPU parallelism (on by default).
78//! - `rpc` enables RPC backend support for distributed inference across multiple machines.
79//! - `mtmd` enables multimodal (image + audio) support via `libmtmd`.
80use std::ffi::NulError;
81use std::fmt::Debug;
82use std::num::NonZeroI32;
83
84use crate::llama_batch::BatchAddError;
85use std::os::raw::c_int;
86use std::path::PathBuf;
87use std::string::FromUtf8Error;
88
89mod cfile;
90
91pub mod chat;
92pub mod common;
93pub mod common_sampler;
94pub mod context;
95pub mod eagle;
96pub mod fit;
97#[cfg(feature = "ggml")]
98pub mod ggml;
99pub mod llama_backend;
100pub mod llama_batch;
101pub mod llama_batch_ext;
102
103/// The C `FILE` type, re-exported so callers can name it without taking a
104/// direct dependency on `llama-cpp-sys-4`.
105///
106/// Needed by the `*_from_file_ptr` constructors —
107/// [`LlamaModel::load_from_file_ptr`](model::LlamaModel::load_from_file_ptr)
108/// and
109/// [`LlamaModel::lora_adapter_init_from_file_ptr`](model::LlamaModel::lora_adapter_init_from_file_ptr)
110/// — which read a GGUF from an already-open handle at its current position.
111/// For the common case of an adapter embedded at a byte offset, prefer the safe
112/// [`LlamaModel::lora_adapter_init_at_offset`](model::LlamaModel::lora_adapter_init_at_offset),
113/// which needs none of this.
114pub use llama_cpp_sys_4::FILE;
115pub mod model;
116pub mod mtp;
117pub mod ngram;
118pub mod prelude;
119pub mod quantize;
120pub mod runtime;
121pub mod sampling;
122// Plumbing shared by the shim-backed modules; not part of the public surface.
123mod shim;
124
125/// The error type every shim-backed module shares.
126///
127/// [`chat::ChatError`], [`ngram::NgramError`], [`common_sampler::CommonSamplerError`]
128/// and [`runtime::RuntimeError`] are all aliases of this one type: the C shims
129/// share a status enum and a thread-local error buffer, so they share an error.
130/// Re-exported here because those aliases are public API and their variants
131/// should be documented somewhere reachable.
132pub use crate::shim::ShimError;
133pub mod speculative;
134pub mod token;
135pub mod token_type;
136
137#[cfg(feature = "rpc")]
138pub mod rpc;
139
140#[cfg(feature = "mtmd")]
141pub mod mtmd;
142
143/// A failable result from a llama.cpp function.
144pub type Result<T> = std::result::Result<T, LLamaCppError>;
145
146/// All errors that can occur in the llama-cpp crate.
147#[derive(Debug, Eq, PartialEq, thiserror::Error)]
148pub enum LLamaCppError {
149    /// The backend was already initialized. This can generally be ignored as initializing the backend
150    /// is idempotent.
151    #[error("BackendAlreadyInitialized")]
152    BackendAlreadyInitialized,
153    /// There was an error while get the chat template from model.
154    #[error("{0}")]
155    ChatTemplateError(#[from] ChatTemplateError),
156    /// There was an error while decoding a batch.
157    #[error("{0}")]
158    DecodeError(#[from] DecodeError),
159    /// There was an error while encoding a batch.
160    #[error("{0}")]
161    EncodeError(#[from] EncodeError),
162    /// There was an error loading a model.
163    #[error("{0}")]
164    LlamaModelLoadError(#[from] LlamaModelLoadError),
165    /// There was an error creating a new model context.
166    #[error("{0}")]
167    LlamaContextLoadError(#[from] LlamaContextLoadError),
168    /// There was an error adding a token to a batch.
169    #[error["{0}"]]
170    BatchAddError(#[from] BatchAddError),
171    /// see [`EmbeddingsError`]
172    #[error(transparent)]
173    EmbeddingError(#[from] EmbeddingsError),
174}
175
176/// There was an error while getting the chat template from a model.
177#[derive(Debug, Eq, PartialEq, thiserror::Error)]
178pub enum ChatTemplateError {
179    /// the buffer was too small.
180    #[error("The buffer was too small. However, a buffer size of {0} would be just large enough.")]
181    BuffSizeError(usize),
182    /// gguf has no chat template
183    #[error("the model has no meta val - returned code {0}")]
184    MissingTemplate(i32),
185    /// The chat template was not valid utf8.
186    #[error(transparent)]
187    Utf8Error(#[from] std::str::Utf8Error),
188}
189
190/// Error retrieving a string from the model (e.g. description, metadata key/value).
191#[derive(Debug, Eq, PartialEq, thiserror::Error)]
192pub enum StringFromModelError {
193    /// The C function returned a negative error code.
194    #[error("llama.cpp returned error code {0}")]
195    ReturnedError(i32),
196    /// The returned bytes were not valid UTF-8.
197    #[error(transparent)]
198    Utf8Error(#[from] std::str::Utf8Error),
199}
200
201/// Failed to Load context
202#[derive(Debug, Eq, PartialEq, thiserror::Error)]
203pub enum LlamaContextLoadError {
204    /// llama.cpp returned null
205    #[error("null reference from llama.cpp")]
206    NullReturn,
207}
208
209/// Failed to decode a batch.
210#[derive(Debug, Eq, PartialEq, thiserror::Error)]
211pub enum DecodeError {
212    /// A Rust tensor callback failed or unwound after native execution began.
213    #[error(transparent)]
214    TensorCallback(#[from] context::TensorCallbackFailure),
215    /// No kv cache slot was available.
216    #[error("Decode Error 1: NoKvCacheSlot")]
217    NoKvCacheSlot,
218    /// The number of tokens in the batch was 0.
219    #[error("Decode Error -1: n_tokens == 0")]
220    NTokensZero,
221    /// A decode lifecycle hook vetoed the decode (native returned `-4`). The
222    /// specific cause is normally surfaced via [`DecodeError::TensorCallback`];
223    /// this is the fallback when no Rust-side failure was recorded.
224    #[error("Decode Error -4: vetoed by a decode lifecycle hook")]
225    VetoedByDecodeHook,
226    /// An unknown error occurred.
227    #[error("Decode Error {0}: unknown")]
228    Unknown(c_int),
229}
230
231/// Failed to decode a batch.
232#[derive(Debug, Eq, PartialEq, thiserror::Error)]
233pub enum EncodeError {
234    /// No kv cache slot was available.
235    #[error("Encode Error 1: NoKvCacheSlot")]
236    NoKvCacheSlot,
237    /// The number of tokens in the batch was 0.
238    #[error("Encode Error -1: n_tokens == 0")]
239    NTokensZero,
240    /// An unknown error occurred.
241    #[error("Encode Error {0}: unknown")]
242    Unknown(c_int),
243}
244
245/// Failed to run a [`LlamaBatchExt`](llama_batch_ext::LlamaBatchExt) through a
246/// context.
247///
248/// `llama_process` returns the same codes as `llama_decode`; the first two
249/// variants are checks this crate makes before calling it.
250#[derive(Debug, Eq, PartialEq, thiserror::Error)]
251pub enum ProcessError {
252    /// The batch was built for a different context. A
253    /// [`LlamaBatchExt`](llama_batch_ext::LlamaBatchExt) captures its
254    /// context's batch size, sequence count and memory module, so it cannot be
255    /// handed to another one.
256    #[error("this batch was built for a different llama context")]
257    ContextMismatch,
258    /// The context owns tensor transactions, which are driven by hooks patched
259    /// into `llama_decode`. `llama_process` does not pass through them, so
260    /// running the batch would leave the transactions silently inert — use
261    /// [`LlamaContext::decode`](context::LlamaContext::decode) with a
262    /// [`llama_batch::LlamaBatch`] instead.
263    #[error("llama_process() does not run the decode lifecycle hooks that this context's tensor transactions need; use decode() with a LlamaBatch")]
264    TensorTransactionsUnsupported,
265    /// An entry in the batch carries no input — neither a token id nor an
266    /// input embedding. See
267    /// [`LlamaBatchExt::has_incomplete_entry`](llama_batch_ext::LlamaBatchExt::has_incomplete_entry).
268    #[error("batch entry {0} carries no input; clear the batch and rebuild it")]
269    IncompleteEntry(i32),
270    /// No kv cache slot was available.
271    #[error("Process Error 1: NoKvCacheSlot")]
272    NoKvCacheSlot,
273    /// The batch held no entries.
274    #[error("Process Error -1: n_tokens == 0")]
275    NTokensZero,
276    /// An unknown error occurred.
277    #[error("Process Error {0}: unknown")]
278    Unknown(c_int),
279}
280
281/// When embedding related functions fail
282#[derive(Debug, Eq, PartialEq, thiserror::Error)]
283pub enum EmbeddingsError {
284    /// Embeddings weren't enabled in the context options
285    #[error("Embeddings weren't enabled in the context options")]
286    NotEnabled,
287    /// Logits weren't enabled for the given token
288    #[error("Logits were not enabled for the given token")]
289    LogitsNotEnabled,
290    /// The given sequence index exceeds the max sequence id
291    #[error("Can't use sequence embeddings with a model supporting only LLAMA_POOLING_TYPE_NONE")]
292    NonePoolType,
293}
294
295/// Decode a error from llama.cpp into a [`DecodeError`].
296impl From<NonZeroI32> for DecodeError {
297    fn from(value: NonZeroI32) -> Self {
298        match value.get() {
299            1 => DecodeError::NoKvCacheSlot,
300            -1 => DecodeError::NTokensZero,
301            -4 => DecodeError::VetoedByDecodeHook,
302            i => DecodeError::Unknown(i),
303        }
304    }
305}
306
307/// Encode a error from llama.cpp into a [`EncodeError`].
308impl From<NonZeroI32> for EncodeError {
309    fn from(value: NonZeroI32) -> Self {
310        match value.get() {
311            1 => EncodeError::NoKvCacheSlot,
312            -1 => EncodeError::NTokensZero,
313            i => EncodeError::Unknown(i),
314        }
315    }
316}
317
318/// Decode an error from `llama_process` into a [`ProcessError`].
319impl From<NonZeroI32> for ProcessError {
320    fn from(value: NonZeroI32) -> Self {
321        match value.get() {
322            1 => ProcessError::NoKvCacheSlot,
323            -1 => ProcessError::NTokensZero,
324            i => ProcessError::Unknown(i),
325        }
326    }
327}
328
329/// An error that can occur when loading a model.
330#[derive(Debug, Eq, PartialEq, thiserror::Error)]
331pub enum LlamaModelLoadError {
332    /// There was a null byte in a provided string and thus it could not be converted to a C string.
333    #[error("null byte in string {0}")]
334    NullError(#[from] NulError),
335    /// llama.cpp returned a nullptr - this could be many different causes.
336    #[error("null result from llama cpp")]
337    NullResult,
338    /// Failed to convert the path to a rust str. This means the path was not valid unicode
339    #[error("failed to convert path {0} to str")]
340    PathToStrError(PathBuf),
341}
342
343/// An error that can occur when loading a model.
344#[derive(Debug, Eq, PartialEq, thiserror::Error)]
345pub enum LlamaLoraAdapterInitError {
346    /// There was a null byte in a provided string and thus it could not be converted to a C string.
347    #[error("null byte in string {0}")]
348    NullError(#[from] NulError),
349    /// llama.cpp returned a nullptr - this could be many different causes.
350    #[error("null result from llama cpp")]
351    NullResult,
352    /// Failed to convert the path to a rust str. This means the path was not valid unicode
353    #[error("failed to convert path {0} to str")]
354    PathToStrError(PathBuf),
355    /// The adapter file could not be opened.
356    #[error("failed to open {path}: {reason}")]
357    FileOpen {
358        /// Path that could not be opened.
359        path: PathBuf,
360        /// What the OS reported.
361        reason: String,
362    },
363    /// The adapter file could not be positioned at the requested offset.
364    #[error("failed to seek {path} to offset {offset}: {reason}")]
365    FileSeek {
366        /// Path being read.
367        path: PathBuf,
368        /// Offset that was requested.
369        offset: u64,
370        /// What the OS reported.
371        reason: String,
372    },
373}
374
375/// An error that can occur when loading a model.
376#[derive(Debug, Eq, PartialEq, thiserror::Error)]
377pub enum LlamaLoraAdapterSetError {
378    /// llama.cpp returned a non-zero error code.
379    #[error("error code from llama cpp")]
380    ErrorResult(i32),
381}
382
383/// An error that can occur when loading a model.
384#[derive(Debug, Eq, PartialEq, thiserror::Error)]
385pub enum LlamaLoraAdapterRemoveError {
386    /// llama.cpp returned a non-zero error code.
387    #[error("error code from llama cpp")]
388    ErrorResult(i32),
389}
390
391/// get the time (in microseconds) according to llama.cpp
392/// ```
393/// # use llama_cpp_4::llama_time_us;
394/// let time = llama_time_us();
395/// assert!(time > 0);
396/// ```
397#[must_use]
398pub fn llama_time_us() -> i64 {
399    unsafe { llama_cpp_sys_4::llama_time_us() }
400}
401
402/// get the max number of devices according to llama.cpp (this is generally cuda devices)
403/// ```
404/// # use llama_cpp_4::max_devices;
405/// let max_devices = max_devices();
406/// assert!(max_devices >= 0);
407/// ```
408#[must_use]
409pub fn max_devices() -> usize {
410    unsafe { llama_cpp_sys_4::llama_max_devices() }
411}
412
413/// is memory mapping supported according to llama.cpp
414/// ```
415/// # use llama_cpp_4::mmap_supported;
416/// let mmap_supported = mmap_supported();
417/// if mmap_supported {
418///   println!("mmap_supported!");
419/// }
420/// ```
421#[must_use]
422pub fn mmap_supported() -> bool {
423    unsafe { llama_cpp_sys_4::llama_supports_mmap() }
424}
425
426/// is memory locking supported according to llama.cpp
427/// ```
428/// # use llama_cpp_4::mlock_supported;
429/// let mlock_supported = mlock_supported();
430/// if mlock_supported {
431///    println!("mlock_supported!");
432/// }
433/// ```
434#[must_use]
435pub fn mlock_supported() -> bool {
436    unsafe { llama_cpp_sys_4::llama_supports_mlock() }
437}
438
439/// An error that can occur when converting a token to a string.
440#[derive(Debug, thiserror::Error, Clone)]
441#[non_exhaustive]
442pub enum TokenToStringError {
443    /// the token type was unknown
444    #[error("Unknown Token Type")]
445    UnknownTokenType,
446    /// There was insufficient buffer space to convert the token to a string.
447    #[error("Insufficient Buffer Space {0}")]
448    InsufficientBufferSpace(c_int),
449    /// Caller-owned storage exceeds llama.cpp's signed buffer-length type.
450    #[error("piece buffer capacity {0} exceeds the native c_int bound")]
451    BufferCapacityExceeded(usize),
452    /// llama.cpp reported a positive piece length outside the supplied buffer.
453    #[error("native piece length {returned} exceeds buffer capacity {capacity}")]
454    NativePieceLength {
455        /// Positive length returned by llama.cpp.
456        returned: c_int,
457        /// Supplied caller-owned capacity.
458        capacity: usize,
459    },
460    /// The token was not valid utf8.
461    #[error("FromUtf8Error {0}")]
462    FromUtf8Error(#[from] FromUtf8Error),
463}
464
465/// Failed to convert a string to a token sequence.
466#[derive(Debug, thiserror::Error)]
467pub enum StringToTokenError {
468    /// the string contained a null byte and thus could not be converted to a c string.
469    #[error("{0}")]
470    NulError(#[from] NulError),
471    /// The string contained an interior NUL at the reported byte.
472    #[error("input contains an interior NUL at byte {0}")]
473    InteriorNul(usize),
474    #[error("{0}")]
475    /// Failed to convert a provided integer to a [`c_int`].
476    CIntConversionError(#[from] std::num::TryFromIntError),
477    /// llama.cpp reported a positive token count outside the supplied buffer.
478    #[error("native token count {returned} exceeds buffer capacity {capacity}")]
479    NativeTokenCount {
480        /// Positive count returned by llama.cpp.
481        returned: c_int,
482        /// Supplied caller-owned capacity.
483        capacity: usize,
484    },
485}
486
487/// Failed to apply model chat template.
488#[derive(Debug, thiserror::Error)]
489pub enum NewLlamaChatMessageError {
490    /// the string contained a null byte and thus could not be converted to a c string.
491    #[error("{0}")]
492    NulError(#[from] NulError),
493}
494
495/// Failed to apply model chat template.
496#[derive(Debug, thiserror::Error)]
497pub enum ApplyChatTemplateError {
498    /// the buffer was too small.
499    #[error("The buffer was too small. Please contact a maintainer and we will update it.")]
500    BuffSizeError,
501    /// the string contained a null byte and thus could not be converted to a c string.
502    #[error("{0}")]
503    NulError(#[from] NulError),
504    /// the string could not be converted to utf8.
505    #[error("{0}")]
506    FromUtf8Error(#[from] FromUtf8Error),
507}
508
509/// Get the time in microseconds according to ggml
510///
511/// ```
512/// # use std::time::Duration;
513/// use llama_cpp_4::ggml_time_us;
514///
515/// let start = ggml_time_us();
516///
517/// std::thread::sleep(Duration::from_micros(10));
518///
519/// let end = ggml_time_us();
520///
521/// let elapsed = end - start;
522///
523/// assert!(elapsed >= 10)
524#[must_use]
525pub fn ggml_time_us() -> i64 {
526    unsafe { llama_cpp_sys_4::ggml_time_us() }
527}
528
529/// Checks if mlock is supported.
530///
531/// ```
532/// # use llama_cpp_4::llama_supports_mlock;
533///
534/// if llama_supports_mlock() {
535///   println!("mlock is supported!");
536/// } else {
537///   println!("mlock is not supported!");
538/// }
539/// ```
540#[must_use]
541pub fn llama_supports_mlock() -> bool {
542    unsafe { llama_cpp_sys_4::llama_supports_mlock() }
543}
544
545/// Checks if GPU offload is supported.
546///
547/// Returns `true` if the library was compiled with GPU support (CUDA, Metal, Vulkan, etc.).
548#[must_use]
549pub fn supports_gpu_offload() -> bool {
550    unsafe { llama_cpp_sys_4::llama_supports_gpu_offload() }
551}
552
553/// Checks if RPC backend is supported.
554///
555/// Returns `true` if the library was compiled with RPC support.
556#[must_use]
557pub fn supports_rpc() -> bool {
558    unsafe { llama_cpp_sys_4::llama_supports_rpc() }
559}
560
561/// Version of the vendored llama.cpp this crate is linked against.
562///
563/// llama.cpp adopted semantic versioning in `b10470` / `v0.1.1`, so this is a
564/// `MAJOR.MINOR.PATCH` string (with a `-dev` suffix for builds off a
565/// non-release commit) rather than a `bNNNNN` build number. Useful for
566/// reporting the exact upstream a binary carries, since the crate version and
567/// the llama.cpp version move independently.
568///
569/// ```
570/// # use llama_cpp_4::llama_version;
571/// let version = llama_version();
572/// assert!(!version.is_empty());
573/// // e.g. "0.1.1"
574/// assert!(version.starts_with(char::is_numeric));
575/// ```
576///
577/// # Panics
578///
579/// Panics if the returned string is not valid UTF-8.
580#[must_use]
581pub fn llama_version() -> &'static str {
582    // SAFETY: llama.cpp returns a pointer to a string literal baked in at
583    // compile time, so it is non-null and lives for the life of the process.
584    let c_str = unsafe { std::ffi::CStr::from_ptr(llama_cpp_sys_4::llama_version()) };
585    c_str.to_str().expect("llama version is not valid UTF-8")
586}
587
588/// Get system information string.
589///
590/// Returns a string containing CPU features, build info, and other system details.
591///
592/// # Panics
593///
594/// Panics if the returned string is not valid UTF-8.
595#[must_use]
596pub fn print_system_info() -> String {
597    let c_str = unsafe { llama_cpp_sys_4::llama_print_system_info() };
598    let c_str = unsafe { std::ffi::CStr::from_ptr(c_str) };
599    c_str
600        .to_str()
601        .expect("system info is not valid UTF-8")
602        .to_owned()
603}
604
605/// Get the maximum number of parallel sequences supported.
606#[must_use]
607pub fn max_parallel_sequences() -> usize {
608    unsafe { llama_cpp_sys_4::llama_max_parallel_sequences() }
609}
610
611/// Get the maximum number of tensor buffer type overrides.
612#[must_use]
613pub fn max_tensor_buft_overrides() -> usize {
614    unsafe { llama_cpp_sys_4::llama_max_tensor_buft_overrides() }
615}
616
617/// Get the name of a flash attention type.
618///
619/// # Panics
620///
621/// Panics if the returned string is not valid UTF-8.
622#[must_use]
623pub fn flash_attn_type_name(flash_attn_type: i32) -> String {
624    let c_str = unsafe { llama_cpp_sys_4::llama_flash_attn_type_name(flash_attn_type) };
625    let c_str = unsafe { std::ffi::CStr::from_ptr(c_str) };
626    c_str
627        .to_str()
628        .expect("flash_attn_type_name is not valid UTF-8")
629        .to_owned()
630}
631
632/// Get the string representation of a model metadata key.
633///
634/// # Panics
635///
636/// Panics if the returned string is not valid UTF-8.
637#[must_use]
638pub fn model_meta_key_str(key: u32) -> String {
639    let c_str = unsafe { llama_cpp_sys_4::llama_model_meta_key_str(key as _) };
640    let c_str = unsafe { std::ffi::CStr::from_ptr(c_str) };
641    c_str
642        .to_str()
643        .expect("meta_key_str is not valid UTF-8")
644        .to_owned()
645}
646
647/// Quantize a model file using typed [`crate::quantize::QuantizeParams`].
648///
649/// Returns `Ok(())` on success, or `Err(code)` with the non-zero error code
650/// returned by `llama_model_quantize`.
651///
652/// # Panics
653///
654/// Panics if either path contains an interior null byte.
655///
656/// # Errors
657///
658/// Returns `Err(code)` with the non-zero status code from `llama_model_quantize`
659/// when quantization fails.
660///
661/// # Example
662///
663/// ```no_run
664/// use llama_cpp_4::quantize::{LlamaFtype, QuantizeParams};
665///
666/// let params = QuantizeParams::new(LlamaFtype::MostlyQ4KM)
667///     .with_nthread(8)
668///     .with_quantize_output_tensor(true);
669///
670/// llama_cpp_4::model_quantize("model-f16.gguf", "model-q4km.gguf", &params).unwrap();
671/// ```
672pub fn model_quantize(
673    fname_inp: &str,
674    fname_out: &str,
675    params: &quantize::QuantizeParams,
676) -> std::result::Result<(), u32> {
677    let c_inp = std::ffi::CString::new(fname_inp).expect("input path contains null bytes");
678    let c_out = std::ffi::CString::new(fname_out).expect("output path contains null bytes");
679    let guard = params.to_raw();
680    let rc = unsafe {
681        llama_cpp_sys_4::llama_model_quantize(c_inp.as_ptr(), c_out.as_ptr(), &raw const guard.raw)
682    };
683    if rc == 0 {
684        Ok(())
685    } else {
686        Err(rc)
687    }
688}
689
690/// Set the log callback.
691///
692/// # Safety
693///
694/// The callback and user data must remain valid for the lifetime of the application
695/// or until the callback is replaced.
696pub unsafe fn log_set(
697    callback: llama_cpp_sys_4::ggml_log_callback,
698    user_data: *mut std::ffi::c_void,
699) {
700    llama_cpp_sys_4::llama_log_set(callback, user_data);
701}
702
703/// Get the current log callback and user data.
704///
705/// # Safety
706///
707/// The caller must ensure the pointers are valid.
708pub unsafe fn log_get(
709    log_callback: *mut llama_cpp_sys_4::ggml_log_callback,
710    user_data: *mut *mut std::ffi::c_void,
711) {
712    llama_cpp_sys_4::llama_log_get(log_callback, user_data);
713}
714
715/// Initialize optimizer state for fine-tuning.
716///
717/// # Safety
718///
719/// The context and model must be valid and compatible.
720pub unsafe fn opt_init(
721    ctx: *mut llama_cpp_sys_4::llama_context,
722    model: *mut llama_cpp_sys_4::llama_model,
723    params: llama_cpp_sys_4::llama_opt_params,
724) {
725    llama_cpp_sys_4::llama_opt_init(ctx, model, params);
726}
727
728/// Run one training epoch.
729///
730/// # Safety
731///
732/// All pointers and handles must be valid.
733#[allow(clippy::too_many_arguments)]
734pub unsafe fn opt_epoch(
735    ctx: *mut llama_cpp_sys_4::llama_context,
736    dataset: llama_cpp_sys_4::ggml_opt_dataset_t,
737    result_train: llama_cpp_sys_4::ggml_opt_result_t,
738    result_eval: llama_cpp_sys_4::ggml_opt_result_t,
739    idata_split: i64,
740    callback_train: llama_cpp_sys_4::ggml_opt_epoch_callback,
741    callback_eval: llama_cpp_sys_4::ggml_opt_epoch_callback,
742) {
743    llama_cpp_sys_4::llama_opt_epoch(
744        ctx,
745        dataset,
746        result_train,
747        result_eval,
748        idata_split,
749        callback_train,
750        callback_eval,
751    );
752}
753
754/// Parameter filter that accepts all tensors (for use with [`opt_init`]).
755///
756/// # Safety
757///
758/// The tensor pointer must be valid.
759pub unsafe fn opt_param_filter_all(
760    tensor: *const llama_cpp_sys_4::ggml_tensor,
761    userdata: *mut std::ffi::c_void,
762) -> bool {
763    llama_cpp_sys_4::llama_opt_param_filter_all(tensor, userdata)
764}
765
766// ── Crate-root re-exports (see also [`prelude`]) ────────────────────────────
767//
768// These mirror the most common [`prelude`] exports so callers can write
769// `llama_cpp_4::LlamaModel` without a glob import.
770
771/// Parameters used when creating a context.
772pub use context::params::LlamaContextParams;
773/// One captured intermediate tensor from [`TensorCapture`].
774pub use context::CapturedTensor;
775/// Typed retained storage from an owned tensor transaction.
776pub use context::CapturedTensorData;
777/// An inference context tied to a model.
778pub use context::LlamaContext;
779/// Per-buffer memory usage entry from [`LlamaContext::memory_breakdown`].
780pub use context::MemoryBreakdownEntry;
781/// Access granted to an exact tensor selector.
782pub use context::TensorAccess;
783/// Exact sequence and causal-position metadata for one tensor row.
784pub use context::TensorBatchRow;
785/// A contained tensor callback failure.
786pub use context::TensorCallbackFailure;
787/// Hook `cb_eval` during decode to copy named graph tensors (layer hidden states, …).
788pub use context::TensorCapture;
789/// Typed Rust-owned tensor storage supplied to a transaction handler.
790pub use context::TensorDataMut;
791/// Element representation required by an exact tensor selector.
792pub use context::TensorElementType;
793pub use context::TensorFiniteValidation;
794/// Mapping between selected tensor rows and the decode batch.
795pub use context::TensorRowMapping;
796/// Exact bounded graph-node contract.
797pub use context::TensorSelector;
798/// Validated tensor dimensions.
799pub use context::TensorShape;
800/// One synchronous owned tensor transaction.
801pub use context::TensorTransaction;
802/// Error returned by a transaction handler or selector validator.
803pub use context::TensorTransactionError;
804/// Safe synchronous tensor transaction handler.
805pub use context::TensorTransactionHandler;
806/// Owned pinned tensor callback program.
807pub use context::TensorTransactions;
808/// Native write-back decision returned by a transaction handler.
809pub use context::TensorWriteback;
810/// Complete retained tensor from one transaction.
811pub use context::TransactionalTensorCapture;
812/// Initialise the llama.cpp backend and hardware drivers.
813pub use llama_backend::LlamaBackend;
814/// Micro-batch submitted to [`LlamaContext::decode`].
815pub use llama_batch::LlamaBatch;
816/// Parameters used when loading a model.
817pub use model::params::LlamaModelParams;
818/// Controls whether tokenisation prepends a BOS token.
819pub use model::AddBos;
820/// A loaded GGUF model.
821pub use model::LlamaModel;
822/// Controls how special tokens are rendered as text.
823pub use model::Special;
824/// Sampler chain for token selection.
825pub use sampling::LlamaSampler;
826/// Failure while capturing or restoring versioned speculative state.
827pub use speculative::SpeculativeStateError;
828/// A single vocabulary token id.
829pub use token::LlamaToken;