llama-cpp-sys-v3 0.1.6

Raw FFI bindings for llama.cpp with runtime DLL loading support
Documentation
use libloading::Library;
use std::path::{Path, PathBuf};

pub mod types;
pub use types::*;

#[derive(Debug, thiserror::Error)]
pub enum LoadError {
    #[error("DLL not found: {0}")]
    NotFound(PathBuf),
    #[error("Failed to load DLL: {0}")]
    LoadFailed(#[from] libloading::Error),
    #[error("Symbol not found: {0}")]
    SymbolMissing(&'static str),
}

/// A loaded instance of the llama.cpp dynamic library.
/// This struct holds the library handle and all resolved function pointers.
pub struct LlamaLib {
    // We must keep the libraries alive as long as the functions are used.
    _libs: Vec<Library>,
    pub symbols: LlamaSymbols,
}

macro_rules! resolve_symbols {
    ($libs:expr, { $( $name:ident : $type:ty ),* $(,)? }) => {
        LlamaSymbols {
            $(
                $name: {
                    let mut found = None;
                    for lib in $libs.iter() {
                        if let Ok(sym) = unsafe { lib.get::<$type>(stringify!($name).as_bytes()) } {
                            found = Some(*sym);
                            break;
                        }
                    }
                    found.ok_or(LoadError::SymbolMissing(stringify!($name)))?
                },
            )*
        }
    };
}

impl LlamaLib {
    /// Attempt to load the llama.cpp library from the given path.
    pub fn open(path: &Path) -> Result<Self, LoadError> {
        if !path.exists() {
            return Err(LoadError::NotFound(path.to_path_buf()));
        }

        let mut libs = Vec::new();

        // Try to load ggml.dll first if it's in the same directory
        if let Some(parent) = path.parent() {
            let ggml_path = parent.join("ggml.dll");
            if ggml_path.exists() {
                #[cfg(target_os = "windows")]
                let lib = unsafe {
                    libloading::os::windows::Library::load_with_flags(
                        &ggml_path,
                        libloading::os::windows::LOAD_WITH_ALTERED_SEARCH_PATH,
                    )?
                };
                #[cfg(not(target_os = "windows"))]
                let lib = unsafe { libloading::Library::new(&ggml_path)? };

                libs.push(libloading::Library::from(lib));
            }
        }

        #[cfg(target_os = "windows")]
        let main_lib = unsafe {
            libloading::os::windows::Library::load_with_flags(
                path,
                libloading::os::windows::LOAD_WITH_ALTERED_SEARCH_PATH,
            )?
        };
        #[cfg(not(target_os = "windows"))]
        let main_lib = unsafe { Library::new(path)? };

        libs.push(libloading::Library::from(main_lib));

        // Resolve all required symbols here
        let symbols = resolve_symbols!(libs, {
            llama_backend_init: unsafe extern "C" fn(),
            llama_backend_free: unsafe extern "C" fn(),
            ggml_backend_load_all: unsafe extern "C" fn(),
            ggml_backend_load_all_from_path: unsafe extern "C" fn(*const std::ffi::c_char),

            llama_model_default_params: unsafe extern "C" fn() -> llama_model_params,
            llama_model_load_from_file: unsafe extern "C" fn(*const std::ffi::c_char, llama_model_params) -> *mut llama_model,
            llama_model_free: unsafe extern "C" fn(*mut llama_model),

            llama_context_default_params: unsafe extern "C" fn() -> llama_context_params,
            llama_init_from_model: unsafe extern "C" fn(*mut llama_model, llama_context_params) -> *mut llama_context,
            llama_free: unsafe extern "C" fn(*mut llama_context),

            llama_batch_get_one: unsafe extern "C" fn(*mut llama_token, i32) -> llama_batch,
            llama_batch_init: unsafe extern "C" fn(i32, i32, i32) -> llama_batch,
            llama_batch_free: unsafe extern "C" fn(llama_batch),

            llama_decode: unsafe extern "C" fn(*mut llama_context, llama_batch) -> i32,
            llama_get_memory: unsafe extern "C" fn(*const llama_context) -> *mut llama_memory,
            llama_memory_clear: unsafe extern "C" fn(*mut llama_memory, bool),

            llama_set_n_threads: unsafe extern "C" fn(*mut llama_context, u32, u32),
            llama_model_get_vocab: unsafe extern "C" fn(*const llama_model) -> *const llama_vocab,
            llama_vocab_n_tokens: unsafe extern "C" fn(*const llama_vocab) -> i32,
            llama_n_vocab: unsafe extern "C" fn(*const llama_vocab) -> i32,
            llama_n_ctx: unsafe extern "C" fn(*const llama_context) -> u32,

            llama_get_logits: unsafe extern "C" fn(*mut llama_context) -> *mut f32,
            llama_get_logits_ith: unsafe extern "C" fn(*mut llama_context, i32) -> *mut f32,

            llama_token_get_text: unsafe extern "C" fn(*const llama_vocab, llama_token) -> *const std::ffi::c_char,
            llama_tokenize: unsafe extern "C" fn(*const llama_vocab, *const std::ffi::c_char, i32, *mut llama_token, i32, bool, bool) -> i32,
            llama_token_to_piece: unsafe extern "C" fn(*const llama_vocab, llama_token, *mut std::ffi::c_char, i32, i32, bool) -> i32,

            llama_vocab_bos: unsafe extern "C" fn(*const llama_vocab) -> llama_token,
            llama_vocab_eos: unsafe extern "C" fn(*const llama_vocab) -> llama_token,
            llama_vocab_nl: unsafe extern "C" fn(*const llama_vocab) -> llama_token,
            llama_vocab_is_eog: unsafe extern "C" fn(*const llama_vocab, llama_token) -> bool,

            llama_print_system_info: unsafe extern "C" fn() -> *const std::ffi::c_char,

            // Sampler API
            llama_sampler_chain_init: unsafe extern "C" fn(llama_sampler_chain_params) -> *mut llama_sampler,
            llama_sampler_init_greedy: unsafe extern "C" fn() -> *mut llama_sampler,
            llama_sampler_free: unsafe extern "C" fn(*mut llama_sampler),
            llama_sampler_init_temp: unsafe extern "C" fn(f32) -> *mut llama_sampler,
            llama_sampler_init_top_k: unsafe extern "C" fn(i32) -> *mut llama_sampler,
            llama_sampler_init_top_p: unsafe extern "C" fn(f32, usize) -> *mut llama_sampler,
            llama_sampler_init_dist: unsafe extern "C" fn(u32) -> *mut llama_sampler,
            llama_sampler_init_min_p: unsafe extern "C" fn(f32, usize) -> *mut llama_sampler,
            llama_sampler_init_typical: unsafe extern "C" fn(f32, usize) -> *mut llama_sampler,
            llama_sampler_init_mirostat_v2: unsafe extern "C" fn(u32, f32, f32) -> *mut llama_sampler,
            llama_sampler_init_penalties: unsafe extern "C" fn(i32, f32, f32, f32) -> *mut llama_sampler,
            llama_sampler_chain_add: unsafe extern "C" fn(*mut llama_sampler, *mut llama_sampler),
            llama_sampler_sample: unsafe extern "C" fn(*mut llama_sampler, *mut llama_context, i32) -> llama_token,
            llama_sampler_accept: unsafe extern "C" fn(*mut llama_sampler, llama_token),
            llama_chat_apply_template: unsafe extern "C" fn(*const std::ffi::c_char, *const llama_chat_message, usize, bool, *mut std::ffi::c_char, i32) -> i32,
            llama_model_chat_template: unsafe extern "C" fn(*const llama_model, *const std::ffi::c_char, *mut std::ffi::c_char, usize) -> i32,
        });

        Ok(Self {
            _libs: libs,
            symbols,
        })
    }
}

pub struct LlamaSymbols {
    pub llama_backend_init: unsafe extern "C" fn(),
    pub llama_backend_free: unsafe extern "C" fn(),
    pub ggml_backend_load_all: unsafe extern "C" fn(),
    pub ggml_backend_load_all_from_path: unsafe extern "C" fn(*const std::ffi::c_char),

    pub llama_model_default_params: unsafe extern "C" fn() -> llama_model_params,
    pub llama_model_load_from_file:
        unsafe extern "C" fn(*const std::ffi::c_char, llama_model_params) -> *mut llama_model,
    pub llama_model_free: unsafe extern "C" fn(*mut llama_model),

    pub llama_context_default_params: unsafe extern "C" fn() -> llama_context_params,
    pub llama_init_from_model:
        unsafe extern "C" fn(*mut llama_model, llama_context_params) -> *mut llama_context,
    pub llama_free: unsafe extern "C" fn(*mut llama_context),

    pub llama_batch_get_one: unsafe extern "C" fn(*mut llama_token, i32) -> llama_batch,
    pub llama_batch_init: unsafe extern "C" fn(i32, i32, i32) -> llama_batch,
    pub llama_batch_free: unsafe extern "C" fn(llama_batch),

    pub llama_decode: unsafe extern "C" fn(*mut llama_context, llama_batch) -> i32,
    pub llama_get_memory: unsafe extern "C" fn(*const llama_context) -> *mut llama_memory,
    pub llama_memory_clear: unsafe extern "C" fn(*mut llama_memory, bool),

    pub llama_set_n_threads: unsafe extern "C" fn(*mut llama_context, u32, u32),
    pub llama_model_get_vocab: unsafe extern "C" fn(*const llama_model) -> *const llama_vocab,
    pub llama_vocab_n_tokens: unsafe extern "C" fn(*const llama_vocab) -> i32,
    pub llama_n_vocab: unsafe extern "C" fn(*const llama_vocab) -> i32,
    pub llama_n_ctx: unsafe extern "C" fn(*const llama_context) -> u32,

    pub llama_get_logits: unsafe extern "C" fn(*mut llama_context) -> *mut f32,
    pub llama_get_logits_ith: unsafe extern "C" fn(*mut llama_context, i32) -> *mut f32,

    pub llama_token_get_text:
        unsafe extern "C" fn(*const llama_vocab, llama_token) -> *const std::ffi::c_char,
    pub llama_tokenize: unsafe extern "C" fn(
        *const llama_vocab,
        *const std::ffi::c_char,
        i32,
        *mut llama_token,
        i32,
        bool,
        bool,
    ) -> i32,
    pub llama_token_to_piece: unsafe extern "C" fn(
        *const llama_vocab,
        llama_token,
        *mut std::ffi::c_char,
        i32,
        i32,
        bool,
    ) -> i32,

    pub llama_vocab_bos: unsafe extern "C" fn(*const llama_vocab) -> llama_token,
    pub llama_vocab_eos: unsafe extern "C" fn(*const llama_vocab) -> llama_token,
    pub llama_vocab_nl: unsafe extern "C" fn(*const llama_vocab) -> llama_token,
    pub llama_vocab_is_eog: unsafe extern "C" fn(*const llama_vocab, llama_token) -> bool,

    pub llama_print_system_info: unsafe extern "C" fn() -> *const std::ffi::c_char,

    pub llama_sampler_chain_init:
        unsafe extern "C" fn(llama_sampler_chain_params) -> *mut llama_sampler,
    pub llama_sampler_init_greedy: unsafe extern "C" fn() -> *mut llama_sampler,
    pub llama_sampler_free: unsafe extern "C" fn(*mut llama_sampler),
    pub llama_sampler_init_temp: unsafe extern "C" fn(f32) -> *mut llama_sampler,
    pub llama_sampler_init_top_k: unsafe extern "C" fn(i32) -> *mut llama_sampler,
    pub llama_sampler_init_top_p: unsafe extern "C" fn(f32, usize) -> *mut llama_sampler,
    pub llama_sampler_init_dist: unsafe extern "C" fn(u32) -> *mut llama_sampler,
    pub llama_sampler_init_min_p: unsafe extern "C" fn(f32, usize) -> *mut llama_sampler,
    pub llama_sampler_init_typical: unsafe extern "C" fn(f32, usize) -> *mut llama_sampler,
    pub llama_sampler_init_mirostat_v2: unsafe extern "C" fn(u32, f32, f32) -> *mut llama_sampler,
    pub llama_sampler_init_penalties:
        unsafe extern "C" fn(i32, f32, f32, f32) -> *mut llama_sampler,
    pub llama_sampler_chain_add: unsafe extern "C" fn(*mut llama_sampler, *mut llama_sampler),
    pub llama_sampler_sample:
        unsafe extern "C" fn(*mut llama_sampler, *mut llama_context, i32) -> llama_token,
    pub llama_sampler_accept: unsafe extern "C" fn(*mut llama_sampler, llama_token),
    pub llama_chat_apply_template: unsafe extern "C" fn(
        *const std::ffi::c_char,
        *const llama_chat_message,
        usize,
        bool,
        *mut std::ffi::c_char,
        i32,
    ) -> i32,
    pub llama_model_chat_template: unsafe extern "C" fn(
        *const llama_model,
        *const std::ffi::c_char,
        *mut std::ffi::c_char,
        usize,
    ) -> i32,
}