Skip to main content

llama_cpp_sys/
lib.rs

1//! Raw FFI bindings to [llama.cpp](https://github.com/ggml-org/llama.cpp),
2//! plus the first-party C++ shim (`wrapper.cpp`) that exposes the `_c`-suffixed
3//! symbols our Rust callers consume.
4//!
5//! # Activation
6//!
7//! The real bindings are hidden behind the `bindings` cargo feature. A default
8//! build (`cargo build -p llama-cpp-sys`) compiles this crate to an empty
9//! shell on every target — this keeps the workspace-wide `cargo clippy` /
10//! `cargo check` on Linux CI runners green, because llama.cpp's cmake build
11//! requires a C++ toolchain and pulls in ~180 MB of source.
12//!
13//! To get the actual FFI surface, build with `--features bindings`. The
14//! build script will use the in-tree `vendor/llama-cpp` directory if
15//! present, and otherwise clone the pinned commit
16//! `b46812de78f8fbcb6cf0154947e8633ebc78d9ac` from GitHub into `$OUT_DIR`.
17//!
18//! # Safety and stability
19//!
20//! This is a `-sys` crate: every item under [`bindings`] is `unsafe extern "C"`
21//! and mirrors the upstream C ABI (after the `_c` suffix introduced by
22//! `wrapper.cpp`) one-to-one. It is not meant to be consumed directly outside
23//! the workspace. The safe wrapper crate `xybrid-llama` owns the RAII, typed
24//! error mapping, and streaming-trampoline concerns.
25//!
26//! The generated bindings are intentionally *not* re-exported transitively:
27//! downstream crates opt in explicitly by depending on `llama-cpp-sys` with
28//! the `bindings` feature and using the items behind [`bindings`].
29//!
30//! # Phase note (epic: `llamacpp-crate-split`)
31//!
32//! The extern declarations in [`bindings`] are generated by bindgen from
33//! `wrapper.h`, which declares the first-party `_c` shim surface consumed by
34//! `xybrid-llama`.
35
36#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
37
38#[cfg(feature = "bindings")]
39pub mod bindings {
40    //! Generated FFI bindings emitted by `bindgen` against [`wrapper.h`].
41    //!
42    //! Phase 5 of the `llamacpp-crate-split` epic — replaces the
43    //! prior 26-symbol hand-written `extern "C" {}` block. Bindgen
44    //! processes `wrapper.h` (which both #includes `<llama.h>` and
45    //! declares the first-party `_c` shim functions) and emits one
46    //! generated `bindings.rs` file consumed via `include!` below.
47    //!
48    //! The allowlist in `build.rs` keeps the surface focused on
49    //! `llama_.*` (functions, types, and `LLAMA_.*` constants);
50    //! `ggml_.*` is intentionally NOT allowlisted because no
51    //! `xybrid-llama` / `xybrid-core` consumer references a `ggml_*`
52    //! symbol directly — wrapper.cpp handles all ggml interop.
53    //!
54    //! See `.notes/bindgen-symbol-diff.md` for the symbol-by-symbol
55    //! diff against the pre-Phase-5 hand-written list.
56
57    // Mirror `mlx-c-sys::bindings`'s lint allowances. Bindgen output
58    // routinely trips dead_code, improper_ctypes, and the broader
59    // clippy lints; the wide allowances keep the generated module
60    // lint-free without needing to post-process bindings.rs.
61    #![allow(
62        non_camel_case_types,
63        non_snake_case,
64        non_upper_case_globals,
65        dead_code,
66        improper_ctypes,
67        clippy::all,
68        clippy::pedantic,
69        clippy::nursery,
70        clippy::cargo
71    )]
72
73    use std::os::raw::{c_char, c_int, c_void};
74
75    // Two sources for the generated bindings:
76    //   - cargo (default): fresh bindgen output from build.rs ($OUT_DIR).
77    //     Tracks wrapper.h / the pinned llama.cpp commit automatically and
78    //     honors the `vision` feature's extra `mtmd_*` surface.
79    //   - `committed-bindings` (the Bazel path, which has no build script and
80    //     therefore no bindgen/libclang): the committed src/bindings.rs
81    //     snapshot, generated WITHOUT `vision`. Regenerate it by building
82    //     with `--features bindings` and copying $OUT_DIR/bindings.rs over
83    //     src/bindings.rs — build.rs warns when the snapshot drifts.
84    #[cfg(feature = "committed-bindings")]
85    include!("bindings.rs");
86    #[cfg(not(feature = "committed-bindings"))]
87    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
88
89    /// Convenience alias for the bindgen-generated streaming callback
90    /// typedef. Bindgen emits `llama_token_callback_c` as
91    /// `Option<unsafe extern "C" fn(...)>` (Option already wrapping the
92    /// function pointer); `TokenCallback` here is the inner `unsafe
93    /// extern "C" fn(...)` so `xybrid-llama` can construct
94    /// `Some(streaming_trampoline::<F>)` against an
95    /// `Option<TokenCallback>`-shaped FFI argument.
96    pub type TokenCallback = unsafe extern "C" fn(
97        token_id: c_int,
98        token_text: *const c_char,
99        user_data: *mut c_void,
100    ) -> c_int;
101}
102
103/// Initialize the llama.cpp backend. Idempotent across calls thanks to the
104/// internal [`std::sync::Once`] guard, so safe to invoke from every
105/// backend-construction call site — only the first invocation actually runs
106/// `llama_backend_init_c()`.
107///
108/// We intentionally never call `llama_backend_free_c()`. The `Once` guard
109/// cannot be re-armed, so if we freed the backend when the last instance
110/// drops and then created a new instance (e.g., during model swap), the
111/// backend would NOT be re-initialized — causing undefined behavior. Since
112/// `llama_backend_free_c()` only cleans up NUMA info (a no-op on most
113/// platforms), skipping it is safe. The OS reclaims all resources at
114/// process exit.
115///
116/// This `-sys` crate deliberately does not read Xybrid-specific environment
117/// variables. Higher-level crates that need policy during backend startup
118/// should call [`backend_init_with_configure`] and keep their configuration
119/// hook in the same one-time init closure.
120#[cfg(feature = "bindings")]
121pub fn backend_init() {
122    backend_init_with_configure(|| {});
123}
124
125/// Initialize the llama.cpp backend once, running `configure` immediately
126/// after `llama_backend_init_c()` during the same one-time initialization.
127///
128/// This preserves the historical timing for higher-level policies such as
129/// log-verbosity setup while keeping this `-sys` crate free of product-level
130/// environment-variable contracts.
131#[cfg(feature = "bindings")]
132pub fn backend_init_with_configure(configure: impl FnOnce()) {
133    use std::sync::Once;
134    static BACKEND_INIT: Once = Once::new();
135    BACKEND_INIT.call_once(|| {
136        unsafe { bindings::llama_backend_init_c() };
137        configure();
138    });
139}
140
141/// No-op stub when the `bindings` feature is disabled. Lets the wrapper
142/// crate's `LlamaCppBackend::new()` constructor call `backend_init()`
143/// unconditionally without a `#[cfg]` branch.
144#[cfg(not(feature = "bindings"))]
145pub fn backend_init() {}
146
147/// No-op stub when the `bindings` feature is disabled.
148#[cfg(not(feature = "bindings"))]
149pub fn backend_init_with_configure(_configure: impl FnOnce()) {}