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