A wrapper around the llama-cpp library for rust, including new Sampler API from llama-cpp.
See the codeSafe Rust bindings to llama.cpp, tracking upstream closely.
| Crate | Description | crates.io |
|---|---|---|
llama-cpp-4 | Safe high-level API | |
llama-cpp-sys-4 | Raw bindgen bindings |
llama.cpp version: 22397c31a0 (b10881, incl. v0.4.0) (Sep 2026) β includes
TurboQuant (PR #21038),
MTP / multi-token-prediction speculative decoding (PR #22673),
DeepSeek V4 MTP + DSpark (PR #25784) β surfaced through LlamaModelParams::with_load_mtp, and
upstream next-n embedding hooks used by MTP (llama_set_embeddings_nextn).
[dependencies]
llama-cpp-4 = "0.7.0"
Import the common types with the prelude:
use llama_cpp_4::prelude::*;
Core types are also at the crate root (llama_cpp_4::LlamaModel, β¦). See
llama-cpp-4/README.md for the full API guide and
prelude on docs.rs
for runnable examples.
| Package name | Directory | Description |
|---|---|---|
simple | examples/simple/ | Single-turn text completion from CLI or Hugging Face |
chat | examples/chat/ | Interactive multi-turn chat REPL |
embeddings | examples/embeddings/ | Batch embedding with cosine similarity |
split-model-example | examples/split_model/ | Load sharded / split GGUF files |
openai-server | examples/server/ | OpenAI-compatible HTTP server β chat, completions, embeddings, tools, files (mtmd), tokenize |
mtmd | examples/mtmd/ | Multimodal (vision / audio) inference (requires --features mtmd) |
quantize | examples/quantize/ | Quantize a GGUF model with full typed API |
turbo-quant | examples/turbo-quant/ | TurboQuant demo β compare attn rotation on/off |
incremental-chat | examples/incremental-chat/ | Chat with incremental prefill β processes tokens while you type |
mtp | examples/mtp/ | MTP speculative decoding via MtpSession (--predict, --p-min, draft loop) |
ngram | examples/ngram/ | Speculative decoding with no draft model β n-gram lookup + --verify that output is byte-identical to plain greedy |
git clone --recursive https://github.com/eugenehp/llama-cpp-rs
cd llama-cpp-rs
cargo run -p chat -- \
hf-model bartowski/Llama-3.2-3B-Instruct-GGUF Llama-3.2-3B-Instruct-Q4_K_M.gguf
# Starts on http://127.0.0.1:8080
cargo run -p openai-server -- \
hf-model bartowski/Llama-3.2-3B-Instruct-GGUF Llama-3.2-3B-Instruct-Q4_K_M.gguf
Full REST API reference: examples/server/README.md.
| Method | Path | Description |
|---|---|---|
| GET | /health, /v1/health | Liveness (no auth) |
| GET | /v1/models | Loaded model metadata |
| POST | /v1/chat/completions, /chat/completions | Chat Β· streaming Β· tools |
| POST | /v1/completions, /completions | Raw completion Β· streaming |
| POST | /v1/embeddings, /embeddings | L2-normalised embeddings |
| POST | /tokenize, /detokenize | llama.cpp-compatible token helpers |
| POST/GET/DELETE | /v1/files/... | File store for multimodal (--features mtmd, --mmproj) |
Legacy paths without /v1 mirror upstream llama-server.
Not implemented here (use upstream server instead): /v1/responses, /v1/messages, /rerank, /slots, /props.
llama-cpp-sys-4 can consume precompiled llama/ggml libraries via env vars.
This is useful for CI pipelines that publish native artifacts once and reuse
them in downstream repos (for example, speeding up a separate app build).
# Directory containing prebuilt libs in one of:
# <dir>, <dir>/lib, <dir>/lib64, <dir>/bin
export LLAMA_PREBUILT_DIR=/path/to/prebuilt
# Optional: force dynamic linking mode for prebuilt artifacts.
# Defaults to the crate's normal link mode for the active feature set.
# export LLAMA_PREBUILT_SHARED=1
cargo build -p your-app --features "q1,vulkan"
Notes:
q1 compatibility is determined by the prebuilt artifact itself β publish
separate artifacts per feature/backend tuple (q1+vulkan, q1+metal, ...).build.rs still generates Rust bindings, but skips the expensive CMake
compile when LLAMA_PREBUILT_DIR is set.Backend feature coverage (practical targets):
metal β macOS (Apple Silicon and Intel Macs)vulkan β Linux/Windows (cross-vendor desktop GPUs)webgpu β Linux/Windows (experimental; requires Dawn/WebGPU-native stack)cuda β Linux/Windows with NVIDIA CUDA toolkit (experimental in CI)hip β Linux ROCm/HIP environments (experimental in CI)The prebuilt feature flag provides automatic prebuilt artifact management. Benchmark results (Apple Silicon M2, macOS 14.4):
| Configuration | Build Type | Time | Improvement |
|---|---|---|---|
| Base (Static) | Debug | 11.99s | Baseline |
Base + prebuilt | Debug | 11.01s | 8% faster |
| Dynamic Linking | Debug | 26.80s | -123% (slower) |
Dynamic + prebuilt | Debug | 27.47s | -129% (slower) |
| Base (Static) | Release | 26.01s | Baseline |
| Dynamic Linking | Release | 26.79s | -3% (slower) |
Key Insights:
Usage:
# Enable prebuilt feature for faster development
cargo build --features prebuilt
# Combine with other features
cargo build --features "prebuilt,vulkan"
# Release builds (prebuilt provides minimal benefit)
cargo build --release --features prebuilt
Implementation Status:
target/llama-prebuilt-cache/When the prebuilt feature is enabled, build.rs will:
cpu, vulkan, blas, metal)v{CARGO_PKG_VERSION})target/llama-prebuilt-cache/Environment overrides:
| Variable | Description |
|---|---|
LLAMA_PREBUILT_DIR | Use a local directory (skips download) |
LLAMA_PREBUILT_TAG | Release tag to download (default: crate version, e.g. v0.7.0) |
LLAMA_PREBUILT_REPO | GitHub owner/repo (default: eugenehp/llama-cpp-rs) |
LLAMA_PREBUILT_URL | Full URL override for the tarball |
LLAMA_PREBUILT_OFF | Set to 1 to disable auto-download |
LLAMA_PREBUILT_SHARED | Force shared/dynamic linking when using LLAMA_PREBUILT_DIR |
Manual prefetch:
./scripts/fetch-prebuilt.sh
cargo build --features prebuilt
opencl β Linux/Windows with OpenCL SDK/runtime (experimental in CI)blas β CPU acceleration (Linux/macOS/Windows)# Chat completion (max_completion_tokens is also accepted)
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello!"}], "max_tokens":128}'
# Streaming
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Count to 5"}], "stream":true}'
# Embeddings
curl http://127.0.0.1:8080/v1/embeddings \
-H "Content-Type: application/json" \
-d '{"input": ["Hello world", "Bonjour le monde"]}'
# Tokenize / detokenize (llama.cpp server-compatible)
curl http://127.0.0.1:8080/tokenize \
-H "Content-Type: application/json" \
-d '{"content":"Hello","add_special":false}'
With --api-key, pass Authorization: Bearer <key> on every route except /health and /v1/health.
use llama_cpp_4::prelude::*;
use std::num::NonZeroU32;
fn main() -> anyhow::Result<()> {
let backend = LlamaBackend::init()?;
let model = LlamaModel::load_from_file(
&backend,
"model.gguf",
&LlamaModelParams::default(),
)?;
let mut ctx = model.new_context(
&backend,
LlamaContextParams::default().with_n_ctx(NonZeroU32::new(2048)),
)?;
let tokens = model.str_to_token("Hello, world!", AddBos::Always)?;
let mut batch = LlamaBatch::new(512, 1);
for (i, &tok) in tokens.iter().enumerate() {
batch.add(tok, i as i32, &[0], i == tokens.len() - 1)?;
}
ctx.decode(&mut batch)?;
let sampler = LlamaSampler::chain_simple([LlamaSampler::greedy()]);
let token = sampler.sample(&ctx, 0);
let piece = model.token_to_bytes(token, Special::Plaintext)?;
println!("{}", String::from_utf8_lossy(&piece));
Ok(())
}
Byte-fallback tokenizers split a single UTF-8 character (emoji, CJK, accents) across several
tokens, so decoding each token on its own can produce invalid UTF-8. StreamDetokenizer
(new in 0.4.1) buffers the raw piece bytes and emits only complete text β ideal for a
token-by-token generation loop:
use llama_cpp_4::prelude::*;
fn stream(model: &LlamaModel, tokens: &[LlamaToken]) -> Result<String, DetokenizeError> {
let mut detok = StreamDetokenizer::new(model, Special::Plaintext);
let mut text = String::new();
for &token in tokens {
text.push_str(&detok.push(token)?); // returns only completed UTF-8
}
text.push_str(&detok.finish()?); // flush any trailing text
Ok(text)
}
For lossless, non-streaming conversion use model.tokens_to_raw_bytes(&tokens, special) (or
token_to_raw_bytes for one token) β these preserve control/byte pieces that token_to_bytes
filters away. Runnable demo: cargo run --example detokenize -- model.gguf.
The llama_cpp_4::quantize module provides a fully typed Rust API for all
quantization options.
use llama_cpp_4::prelude::*;
use llama_cpp_4::quantize::TensorTypeOverride;
// Basic β quantize to Q4_K_M
let params = QuantizeParams::new(LlamaFtype::MostlyQ4KM)
.with_nthread(8)
.with_quantize_output_tensor(true);
llama_cpp_4::model_quantize("model-f16.gguf", "model-q4km.gguf", ¶ms).unwrap();
// Advanced β keep output tensor in F16, prune layers 28-31
let params = QuantizeParams::new(LlamaFtype::MostlyQ5KM)
.with_tensor_type_override(TensorTypeOverride::new("output", GgmlType::F16).unwrap())
.with_pruned_layers(28..=31);
llama_cpp_4::model_quantize("model-f16.gguf", "model-q5km-pruned.gguf", ¶ms).unwrap();
From the CLI:
# List all available quantization types
cargo run -p quantize -- --list-types
# Quantize with auto output name
cargo run -p quantize -- model-f16.gguf Q4_K_M
# Override a specific tensor type
cargo run -p quantize -- --tensor-type output=F16 model-f16.gguf Q5_K_M
# Dry-run: show size without writing
cargo run -p quantize -- --dry-run model-f16.gguf Q4_K_M
TurboQuant (llama.cpp PR #21038) applies a Hadamard rotation to the Q, K, and V tensors before they are stored in the KV cache.
Attention activations have large outlier values on some dimensions that make quantization hard. The rotation spreads these outliers evenly so the KV cache can be stored in aggressive formats (Q4_0, Q5_0) with drastically less quality loss:
| KV cache type | Without TurboQuant | With TurboQuant | VRAM vs F16 |
|---|---|---|---|
| F16 (baseline) | β | β | 100% |
| Q8_0 | +0.003 PPL | +0.003 PPL | 53% |
| Q5_1 | +61.70 PPL | +0.44 PPL | 37% |
| Q5_0 | +17.28 PPL | +0.55 PPL | 34% |
| Q4_1 | +212.5 PPL | +8.65 PPL | 31% |
| Q4_0 | +62.02 PPL | +32.6 PPL | 28% |
PPL delta vs F16 baseline on Qwen3 0.6B BF16 β source: llama.cpp PR #21038.
Numbers below come from a benchmark run against Qwen2.5-0.5B-Instruct
(24 layers, 2 KV heads, 64 head-dim), obtained by calling ggml_row_size()
directly against the compiled GGML library in this repo's build tree.
Model : Qwen2.5-0.5B-Instruct (24 layers, 2 KV heads, 64 head-dim)
Config B/row B/elem KV @2K KV @32K Saved@32K Ratio
-------------------- ------ ------ --------- ---------- --------- -----
F16 (baseline) 128 2.0000 24.00 MB 384.00 MB β 1.00x
Q8_0 + TurboQuant 68 1.0625 12.75 MB 204.00 MB 180.0 MB 1.88x
Q5_1 + TurboQuant 48 0.7500 9.00 MB 144.00 MB 240.0 MB 2.67x
Q5_0 + TurboQuant 44 0.6875 8.25 MB 132.00 MB 252.0 MB 2.91x β sweet spot
Q4_1 + TurboQuant 40 0.6250 7.50 MB 120.00 MB 264.0 MB 3.20x
Q4_0 + TurboQuant 36 0.5625 6.75 MB 108.00 MB 276.0 MB 3.56x
The ratios are pure GGML block geometry and scale identically to larger models β for a 7B model (32 layers, 8 KV heads, 128 head-dim) multiply every MB figure by ~85Γ; the ratios and % savings are the same.
LLAMA_ATTN_ROT_DISABLE env var β set to 1 to opt out.use llama_cpp_4::prelude::*;
// TurboQuant is ON by default β just set a quantized KV cache type:
let ctx_params = LlamaContextParams::default()
.with_cache_type_k(GgmlType::Q5_0)
.with_cache_type_v(GgmlType::Q5_0);
let ctx = model.new_context(&backend, ctx_params)?;
use llama_cpp_4::prelude::*;
let ctx_params = LlamaContextParams::default()
.with_cache_type_k(GgmlType::Q5_0)
.with_attn_rot_disabled(true);
let ctx = model.new_context(&backend, ctx_params)?;
// Global process-level toggle (call before creating any context):
use llama_cpp_4::quantize::{attn_rot_disabled, set_attn_rot_disabled};
set_attn_rot_disabled(true);
assert!(attn_rot_disabled());
set_attn_rot_disabled(false); // restore
# API reference + PPL table (no model required)
cargo run -p turbo-quant -- --show-api
# Run both passes and compare outputs directly
cargo run -p turbo-quant -- \
--model model.gguf \
--kv-type q5_0 \
--prompt "The capital of France is" \
--n-predict 16
Upstream PR #22673 added
MTP draft heads to llama.cpp. The Rust API lives in
llama_cpp_4::mtp: build a target + draft context
pair, wrap them in MtpSession, and drive the
verify/accept loop from Rust.
Both contexts come from the same MTP-capable GGUF. The draft context must
use LlamaContextType::Mtp and
n_rs_seq >= n_draft_max (rollback snapshots for speculative verification):
use llama_cpp_4::prelude::*;
let n_draft_max = 3;
let target = model.new_context(&backend, LlamaContextParams::default())?;
let draft = model.new_context(
&backend,
LlamaContextParams::default()
.with_ctx_type(LlamaContextType::Mtp)
.with_n_rs_seq(n_draft_max.max(4)),
)?;
MtpSessionConfig maps to upstream
common_params_speculative_draft:
| Field | Meaning | Typical value |
|---|---|---|
n_seq | Parallel sequences | 1 |
n_draft_max | Max tokens drafted per round | 1β3 (model-dependent) |
p_min | Drop draft tokens below this probability | 0.0 (upstream default since #23269) |
n_min | Minimum drafts to propose | 0 |
use llama_cpp_4::prelude::*;
let mut session = MtpSession::new(&target, &draft, 1, n_draft_max)?;
let config = MtpSessionConfig::new(1, n_draft_max)
.with_p_min(0.0)
.with_n_min(0);
let mut session = MtpSession::new_with_config(&target, &draft, config)?;
assert!(session.need_embd_pre_norm());
assert!(!session.need_embd());
The Rust API still uses *_pre_norm names; upstream renamed the C API to
llama_set_embeddings_nextn / common_speculative_need_embd_nextn.
Upstream configures next-n extraction on both contexts during session init;
you normally do not need to call
LlamaContext::set_embeddings_pre_norm yourself.
After every target.decode(batch):
session.process(&batch)? β sync MTP with the target batchsession.draft(seq_id, n_past, last_token)? β propose draft tokenssession.accept(seq_id, n_accepted)? β update draft recurrent statesession.print_stats() β log upstream draft/accept counters (optional)See examples/mtp/src/main.rs for a complete
working loop with timing and acceptance reporting.
Smoke test (build contexts only):
cargo run --release -p mtp --features metal -- \
hf-model froggeric/Qwen3.6-27B-MTP-GGUF Qwen3.6-27B-IQ2_M-mtp.gguf
Full generation with draft tuning:
cargo run --release -p mtp --features metal -- \
--predict 64 \
--n-draft-max 1 \
--p-min 0.0 \
--prompt "The capital of France is" \
hf-model froggeric/Qwen3.6-27B-MTP-GGUF Qwen3.6-27B-IQ2_M-mtp.gguf
Use --features cuda or --features vulkan on other platforms instead of
metal.
Draft depth is quant- and model-sensitive. See MTP.md for measured throughput on Apple Silicon and notes on upstream #23269 sampling changes.
For comparison against upstream llama-server --spec-type draft-mtp, use
scripts/bench-mtp.sh.
The incremental-chat example demonstrates incremental prefill β decoding
prompt tokens into the KV cache while the user is still typing, so that
generation starts almost instantly when they press Enter.
apply_chat_templateMeasured on Qwen2.5-0.5B-Instruct Q4_K_M (Apple Silicon, CPU-only).
Run the full benchmark: cargo run --release -p incremental-chat --bin incremental-bench -- model.gguf
Generate charts: cargo run -p incremental-chat --bin incremental-charts


2. Speed β 167 tok/s generation throughput (32 tokens in 191ms)

4. Precision β incremental prefill produces identical first token to normal prefill (β ALL MATCH)

6. DX β 3-method API (new/prefill_speculative/flush), pure userspace pattern, ~130 lines of shared code

Generated 64 tokens and compared output to F16 baseline:
| Config | Diverges at | Quality | Output sample |
|---|---|---|---|
| F16 (baseline) | β | β | "Rust and C++ are both popular programming languages..." |
| Q8_0 + TurboQuant | char 195 | near-identical | Same as F16 for ~195 chars |
| Q5_0 + TurboQuant | char 24 | coherent | "...both high-level programming languages..." |
| Q4_0 + TurboQuant | char 24 | coherent | "...different approaches to memory management..." |
| Q5_0 no TurboQuant | char 2 | β degraded | "The following of the following of the of the..." |
| Q4_0 no TurboQuant | char 2 | β degraded | "The term 'in terms of memory safety...is a programming language..." |
TurboQuant makes quantized KV cache usable. Without it, Q5_0/Q4_0 produce degenerate output (diverges at char 2). With it, Q5_0 produces coherent text that diverges only in wording, while Q8_0 is near-identical to F16. See also the TurboQuant section for PPL and VRAM numbers.

9 sampler configurations tested with seed=42 for reproducibility:
| Sampler | Gen (48t) | tok/s | Output style |
|---|---|---|---|
| greedy (t=0) | 306 ms | 157 | Deterministic, factual |
| temp=0.1 top_k=40 | 306 ms | 157 | Nearly identical to greedy |
| temp=0.4 top_p=0.9 | 330 ms | 145 | Slight variation, still focused |
| temp=0.7 top_p=0.9 | 335 ms | 143 | Creative, writes actual haiku |
| temp=1.0 top_p=0.95 | 413 ms | 116 | More diverse, poetic |
| temp=1.5 top_k=50 | 307 ms | 156 | Wild β mixes English and Chinese |
| min_p=0.05 t=0.7 | 310 ms | 155 | Focused, similar to top_p |
| top_n_sigma=1.0 | 643 ms | 75 | Slower (large candidate set) |
| mirostat_v2 Ο=5 | 565 ms | 85 | Adaptive, poetic output |
Key findings:
temp=0.7 + top_p=0.9 is the sweet spot for creative tasksmin_p is a fast alternative to top_p with similar quality# Interactive chat with live prefill
cargo run --release -p incremental-chat -- local model.gguf
# Quantized KV cache with TurboQuant (saves VRAM, near-zero quality loss)
cargo run --release -p incremental-chat -- --kv-type q5_0 local model.gguf
# Without TurboQuant (for comparison)
cargo run --release -p incremental-chat -- --kv-type q5_0 --no-turbo-quant local model.gguf
# Cache the system prompt session to disk (instant restart)
cargo run --release -p incremental-chat -- --session-cache sys.session local model.gguf
# Custom system prompt, debounce, and sliding window
cargo run --release -p incremental-chat -- \
--system-prompt "You are a pirate. Respond only in pirate speak." \
--debounce-ms 100 --keep-turns 4 \
local model.gguf
# Run the comprehensive benchmark (7 dimensions)
cargo run --release -p incremental-chat --bin incremental-bench -- model.gguf
The key building blocks from llama-cpp-4:
use llama_cpp_4::llama_batch::LlamaBatch;
use llama_cpp_4::token::LlamaToken;
// Decode only new tokens (the delta) into the KV cache.
// Withhold the last 2 tokens β BPE can retroactively change them
// when the next character is typed.
let new_tokens = model.str_to_token(&user_text, AddBos::Always)?;
let stable_end = new_tokens.len().saturating_sub(2); // BPE margin
let common = find_common_prefix(&cached_tokens, &new_tokens[..stable_end]);
// Trim cache if the user edited earlier text
if common < cached_tokens.len() {
ctx.clear_kv_cache_seq(Some(0), Some(common as u32), None)?;
}
// Decode only the genuinely new stable tokens
let mut batch = LlamaBatch::new(512, 1);
for (i, &token) in new_tokens[common..stable_end].iter().enumerate() {
let pos = (common + i) as i32;
batch.add(token, pos, &[0], i == stable_end - common - 1)?;
}
ctx.decode(&mut batch)?;
// When user presses Enter: flush ALL tokens (including the tail)
| Feature | Hardware | Flag |
|---|---|---|
cuda | NVIDIA (CUDA) | --features cuda |
metal | Apple Silicon | --features metal |
vulkan | AMD / Intel / cross-platform | --features vulkan |
native | CPU with AVX2/NEON auto-detect | --features native |
openmp | Multi-core CPU (default on) | --features openmp |
rpc | Remote compute backend | --features rpc |
prebuilt | All (build optimization) | --features prebuilt |
# Metal (macOS)
cargo run -p openai-server --features metal -- --n-gpu-layers 99 \
local model.gguf
# CUDA (Linux/Windows)
cargo run -p openai-server --features cuda -- --n-gpu-layers 99 \
local model.gguf
# Vulkan (cross-platform)
cargo run -p openai-server --features vulkan -- --n-gpu-layers 99 \
hf-model bartowski/Llama-3.2-3B-Instruct-GGUF Llama-3.2-3B-Instruct-Q4_K_M.gguf
The default dynamic-link feature builds llama.cpp as shared libraries, and
llama-cpp-sys-4 places them next to the binaries Cargo produces. Locating them
at runtime is platform-specific:
| Platform | How the binary finds the libraries | Action needed |
|---|---|---|
| macOS | Install names are rewritten to @loader_path/β¦ by the build script | none |
| Windows | The loader searches the directory of the .exe | none |
| Linux / BSD | ELF requires an rpath on the final executable | see below |
Cargo does not add an rpath to the binaries it builds, and a dependency's build
script cannot inject link arguments into a dependent crate's binary β so on
Linux the consuming project has to supply it. This repository does so in
.cargo/config.toml; in your own project add:
# .cargo/config.toml
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-Wl,-rpath,$ORIGIN"]
Note that setting the RUSTFLAGS environment variable replaces these flags
rather than appending to them, so a CI job that sets RUSTFLAGS must repeat the
rpath itself.
Without this, cargo run and cargo test still work β they set
LD_LIBRARY_PATH / DYLD_FALLBACK_LIBRARY_PATH to the target directory β but
running the binary directly fails with libggml-base.so.0: cannot open shared object file. To sidestep runtime lookup entirely, link statically instead:
llama-cpp-4 = { version = "0.7.0", default-features = false }
All examples and the server accept a hf-model <repo> [quant] subcommand
that downloads models from the Hub (cached in ~/.cache/huggingface/).
# Interactive quant picker for repos with many options
cargo run -p openai-server -- hf-model unsloth/Qwen3.5-397B-A17B-GGUF
# Select by quant name (downloads all shards automatically)
cargo run -p openai-server -- hf-model unsloth/Qwen3.5-397B-A17B-GGUF Q4_K_M
# Exact filename
cargo run -p openai-server -- \
hf-model TheBloke/Llama-2-7B-Chat-GGUF llama-2-7b-chat.Q4_K_M.gguf
Set HUGGING_FACE_HUB_TOKEN for gated models.
# Clone with submodules (llama.cpp is a submodule of llama-cpp-sys-4)
git clone --recursive https://github.com/eugenehp/llama-cpp-rs
# Or after cloning without --recursive
git submodule update --init --recursive
# Build everything (with optimizations)
cargo build
# Build with prebuilt artifacts for faster compilation
cargo build --features prebuilt
# Run all unit tests (no model required)
cargo test
# Run server unit tests specifically
cargo test -p openai-server
The build system includes several optimizations for faster compilation:
--features prebuilt) (8% faster debug builds, 50-80% expected for CI/CD)For best performance, install the recommended tools:
# macOS
brew install ninja sccache
# Linux (Ubuntu/Debian)
sudo apt-get install ninja-build mold
cargo install sccache
# Enable detailed build logging
BUILD_DEBUG=1 cargo build
See BUILD_OPTIMIZATIONS.md for more details.
cd llama-cpp-sys-4/llama.cpp
git fetch origin master
git checkout origin/master # or a specific commit/tag
cd ../..
cargo build # build.rs regenerates bindings automatically
Build with --features mtmd. The server auto-detects mmproj-*.gguf next to the
model, or accept --mmproj PATH. Upload images via POST /v1/files, then reference
them in chat messages (image_url / image_file parts β see
examples/server/README.md).
cargo run -p openai-server --features mtmd --release -- \
hf-model unsloth/Qwen3.5-27B-GGUF Qwen3.5-27B-Q4_0
Or with an explicit mmproj path:
cargo run -p openai-server --features mtmd -- \
--mmproj mmproj-BF16.gguf \
hf-model unsloth/Qwen3.5-27B-GGUF Qwen3.5-27B-Q4_0
cargo run --features mtmd -p mtmd -- \
--model /path/to/model.gguf \
--mmproj /path/to/mmproj.gguf \
--image /path/to/image.jpg \
--prompt "Describe this image."
Originally derived from llama-cpp-2 β thanks to those contributors.
See also bitnet-cpp-rs for highly-quantized BitNet model support.
@software{hauptmann2025llamacpprs,
author = {Hauptmann, Eugene},
title = {{llama-cpp-4}: llama-cpp {Rust} wrapper},
year = {2025},
version = {0.7.0},
url = {https://github.com/eugenehp/llama-cpp-rs},
}
This project is licensed under the MIT License.
Β© 2025-2026, Eugene Hauptmann
(top 30 of 37)
Rust
88.5%
C++
6.7%
C
2.6%
Shell
1.9%
A wrapper around the llama-cpp library for rust, including new Sampler API from llama-cpp.
See the codeSafe Rust bindings to llama.cpp, tracking upstream closely.
| Crate | Description | crates.io |
|---|---|---|
llama-cpp-4 | Safe high-level API | |
llama-cpp-sys-4 | Raw bindgen bindings |
llama.cpp version: 22397c31a0 (b10881, incl. v0.4.0) (Sep 2026) β includes
TurboQuant (PR #21038),
MTP / multi-token-prediction speculative decoding (PR #22673),
DeepSeek V4 MTP + DSpark (PR #25784) β surfaced through LlamaModelParams::with_load_mtp, and
upstream next-n embedding hooks used by MTP (llama_set_embeddings_nextn).
[dependencies]
llama-cpp-4 = "0.7.0"
Import the common types with the prelude:
use llama_cpp_4::prelude::*;
Core types are also at the crate root (llama_cpp_4::LlamaModel, β¦). See
llama-cpp-4/README.md for the full API guide and
prelude on docs.rs
for runnable examples.
| Package name | Directory | Description |
|---|---|---|
simple | examples/simple/ | Single-turn text completion from CLI or Hugging Face |
chat | examples/chat/ | Interactive multi-turn chat REPL |
embeddings | examples/embeddings/ | Batch embedding with cosine similarity |
split-model-example | examples/split_model/ | Load sharded / split GGUF files |
openai-server | examples/server/ | OpenAI-compatible HTTP server β chat, completions, embeddings, tools, files (mtmd), tokenize |
mtmd | examples/mtmd/ | Multimodal (vision / audio) inference (requires --features mtmd) |
quantize | examples/quantize/ | Quantize a GGUF model with full typed API |
turbo-quant | examples/turbo-quant/ | TurboQuant demo β compare attn rotation on/off |
incremental-chat | examples/incremental-chat/ | Chat with incremental prefill β processes tokens while you type |
mtp | examples/mtp/ | MTP speculative decoding via MtpSession (--predict, --p-min, draft loop) |
ngram | examples/ngram/ | Speculative decoding with no draft model β n-gram lookup + --verify that output is byte-identical to plain greedy |
git clone --recursive https://github.com/eugenehp/llama-cpp-rs
cd llama-cpp-rs
cargo run -p chat -- \
hf-model bartowski/Llama-3.2-3B-Instruct-GGUF Llama-3.2-3B-Instruct-Q4_K_M.gguf
# Starts on http://127.0.0.1:8080
cargo run -p openai-server -- \
hf-model bartowski/Llama-3.2-3B-Instruct-GGUF Llama-3.2-3B-Instruct-Q4_K_M.gguf
Full REST API reference: examples/server/README.md.
| Method | Path | Description |
|---|---|---|
| GET | /health, /v1/health | Liveness (no auth) |
| GET | /v1/models | Loaded model metadata |
| POST | /v1/chat/completions, /chat/completions | Chat Β· streaming Β· tools |
| POST | /v1/completions, /completions | Raw completion Β· streaming |
| POST | /v1/embeddings, /embeddings | L2-normalised embeddings |
| POST | /tokenize, /detokenize | llama.cpp-compatible token helpers |
| POST/GET/DELETE | /v1/files/... | File store for multimodal (--features mtmd, --mmproj) |
Legacy paths without /v1 mirror upstream llama-server.
Not implemented here (use upstream server instead): /v1/responses, /v1/messages, /rerank, /slots, /props.
llama-cpp-sys-4 can consume precompiled llama/ggml libraries via env vars.
This is useful for CI pipelines that publish native artifacts once and reuse
them in downstream repos (for example, speeding up a separate app build).
# Directory containing prebuilt libs in one of:
# <dir>, <dir>/lib, <dir>/lib64, <dir>/bin
export LLAMA_PREBUILT_DIR=/path/to/prebuilt
# Optional: force dynamic linking mode for prebuilt artifacts.
# Defaults to the crate's normal link mode for the active feature set.
# export LLAMA_PREBUILT_SHARED=1
cargo build -p your-app --features "q1,vulkan"
Notes:
q1 compatibility is determined by the prebuilt artifact itself β publish
separate artifacts per feature/backend tuple (q1+vulkan, q1+metal, ...).build.rs still generates Rust bindings, but skips the expensive CMake
compile when LLAMA_PREBUILT_DIR is set.Backend feature coverage (practical targets):
metal β macOS (Apple Silicon and Intel Macs)vulkan β Linux/Windows (cross-vendor desktop GPUs)webgpu β Linux/Windows (experimental; requires Dawn/WebGPU-native stack)cuda β Linux/Windows with NVIDIA CUDA toolkit (experimental in CI)hip β Linux ROCm/HIP environments (experimental in CI)The prebuilt feature flag provides automatic prebuilt artifact management. Benchmark results (Apple Silicon M2, macOS 14.4):
| Configuration | Build Type | Time | Improvement |
|---|---|---|---|
| Base (Static) | Debug | 11.99s | Baseline |
Base + prebuilt | Debug | 11.01s | 8% faster |
| Dynamic Linking | Debug | 26.80s | -123% (slower) |
Dynamic + prebuilt | Debug | 27.47s | -129% (slower) |
| Base (Static) | Release | 26.01s | Baseline |
| Dynamic Linking | Release | 26.79s | -3% (slower) |
Key Insights:
Usage:
# Enable prebuilt feature for faster development
cargo build --features prebuilt
# Combine with other features
cargo build --features "prebuilt,vulkan"
# Release builds (prebuilt provides minimal benefit)
cargo build --release --features prebuilt
Implementation Status:
target/llama-prebuilt-cache/When the prebuilt feature is enabled, build.rs will:
cpu, vulkan, blas, metal)v{CARGO_PKG_VERSION})target/llama-prebuilt-cache/Environment overrides:
| Variable | Description |
|---|---|
LLAMA_PREBUILT_DIR | Use a local directory (skips download) |
LLAMA_PREBUILT_TAG | Release tag to download (default: crate version, e.g. v0.7.0) |
LLAMA_PREBUILT_REPO | GitHub owner/repo (default: eugenehp/llama-cpp-rs) |
LLAMA_PREBUILT_URL | Full URL override for the tarball |
LLAMA_PREBUILT_OFF | Set to 1 to disable auto-download |
LLAMA_PREBUILT_SHARED | Force shared/dynamic linking when using LLAMA_PREBUILT_DIR |
Manual prefetch:
./scripts/fetch-prebuilt.sh
cargo build --features prebuilt
opencl β Linux/Windows with OpenCL SDK/runtime (experimental in CI)blas β CPU acceleration (Linux/macOS/Windows)# Chat completion (max_completion_tokens is also accepted)
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello!"}], "max_tokens":128}'
# Streaming
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Count to 5"}], "stream":true}'
# Embeddings
curl http://127.0.0.1:8080/v1/embeddings \
-H "Content-Type: application/json" \
-d '{"input": ["Hello world", "Bonjour le monde"]}'
# Tokenize / detokenize (llama.cpp server-compatible)
curl http://127.0.0.1:8080/tokenize \
-H "Content-Type: application/json" \
-d '{"content":"Hello","add_special":false}'
With --api-key, pass Authorization: Bearer <key> on every route except /health and /v1/health.
use llama_cpp_4::prelude::*;
use std::num::NonZeroU32;
fn main() -> anyhow::Result<()> {
let backend = LlamaBackend::init()?;
let model = LlamaModel::load_from_file(
&backend,
"model.gguf",
&LlamaModelParams::default(),
)?;
let mut ctx = model.new_context(
&backend,
LlamaContextParams::default().with_n_ctx(NonZeroU32::new(2048)),
)?;
let tokens = model.str_to_token("Hello, world!", AddBos::Always)?;
let mut batch = LlamaBatch::new(512, 1);
for (i, &tok) in tokens.iter().enumerate() {
batch.add(tok, i as i32, &[0], i == tokens.len() - 1)?;
}
ctx.decode(&mut batch)?;
let sampler = LlamaSampler::chain_simple([LlamaSampler::greedy()]);
let token = sampler.sample(&ctx, 0);
let piece = model.token_to_bytes(token, Special::Plaintext)?;
println!("{}", String::from_utf8_lossy(&piece));
Ok(())
}
Byte-fallback tokenizers split a single UTF-8 character (emoji, CJK, accents) across several
tokens, so decoding each token on its own can produce invalid UTF-8. StreamDetokenizer
(new in 0.4.1) buffers the raw piece bytes and emits only complete text β ideal for a
token-by-token generation loop:
use llama_cpp_4::prelude::*;
fn stream(model: &LlamaModel, tokens: &[LlamaToken]) -> Result<String, DetokenizeError> {
let mut detok = StreamDetokenizer::new(model, Special::Plaintext);
let mut text = String::new();
for &token in tokens {
text.push_str(&detok.push(token)?); // returns only completed UTF-8
}
text.push_str(&detok.finish()?); // flush any trailing text
Ok(text)
}
For lossless, non-streaming conversion use model.tokens_to_raw_bytes(&tokens, special) (or
token_to_raw_bytes for one token) β these preserve control/byte pieces that token_to_bytes
filters away. Runnable demo: cargo run --example detokenize -- model.gguf.
The llama_cpp_4::quantize module provides a fully typed Rust API for all
quantization options.
use llama_cpp_4::prelude::*;
use llama_cpp_4::quantize::TensorTypeOverride;
// Basic β quantize to Q4_K_M
let params = QuantizeParams::new(LlamaFtype::MostlyQ4KM)
.with_nthread(8)
.with_quantize_output_tensor(true);
llama_cpp_4::model_quantize("model-f16.gguf", "model-q4km.gguf", ¶ms).unwrap();
// Advanced β keep output tensor in F16, prune layers 28-31
let params = QuantizeParams::new(LlamaFtype::MostlyQ5KM)
.with_tensor_type_override(TensorTypeOverride::new("output", GgmlType::F16).unwrap())
.with_pruned_layers(28..=31);
llama_cpp_4::model_quantize("model-f16.gguf", "model-q5km-pruned.gguf", ¶ms).unwrap();
From the CLI:
# List all available quantization types
cargo run -p quantize -- --list-types
# Quantize with auto output name
cargo run -p quantize -- model-f16.gguf Q4_K_M
# Override a specific tensor type
cargo run -p quantize -- --tensor-type output=F16 model-f16.gguf Q5_K_M
# Dry-run: show size without writing
cargo run -p quantize -- --dry-run model-f16.gguf Q4_K_M
TurboQuant (llama.cpp PR #21038) applies a Hadamard rotation to the Q, K, and V tensors before they are stored in the KV cache.
Attention activations have large outlier values on some dimensions that make quantization hard. The rotation spreads these outliers evenly so the KV cache can be stored in aggressive formats (Q4_0, Q5_0) with drastically less quality loss:
| KV cache type | Without TurboQuant | With TurboQuant | VRAM vs F16 |
|---|---|---|---|
| F16 (baseline) | β | β | 100% |
| Q8_0 | +0.003 PPL | +0.003 PPL | 53% |
| Q5_1 | +61.70 PPL | +0.44 PPL | 37% |
| Q5_0 | +17.28 PPL | +0.55 PPL | 34% |
| Q4_1 | +212.5 PPL | +8.65 PPL | 31% |
| Q4_0 | +62.02 PPL | +32.6 PPL | 28% |
PPL delta vs F16 baseline on Qwen3 0.6B BF16 β source: llama.cpp PR #21038.
Numbers below come from a benchmark run against Qwen2.5-0.5B-Instruct
(24 layers, 2 KV heads, 64 head-dim), obtained by calling ggml_row_size()
directly against the compiled GGML library in this repo's build tree.
Model : Qwen2.5-0.5B-Instruct (24 layers, 2 KV heads, 64 head-dim)
Config B/row B/elem KV @2K KV @32K Saved@32K Ratio
-------------------- ------ ------ --------- ---------- --------- -----
F16 (baseline) 128 2.0000 24.00 MB 384.00 MB β 1.00x
Q8_0 + TurboQuant 68 1.0625 12.75 MB 204.00 MB 180.0 MB 1.88x
Q5_1 + TurboQuant 48 0.7500 9.00 MB 144.00 MB 240.0 MB 2.67x
Q5_0 + TurboQuant 44 0.6875 8.25 MB 132.00 MB 252.0 MB 2.91x β sweet spot
Q4_1 + TurboQuant 40 0.6250 7.50 MB 120.00 MB 264.0 MB 3.20x
Q4_0 + TurboQuant 36 0.5625 6.75 MB 108.00 MB 276.0 MB 3.56x
The ratios are pure GGML block geometry and scale identically to larger models β for a 7B model (32 layers, 8 KV heads, 128 head-dim) multiply every MB figure by ~85Γ; the ratios and % savings are the same.
LLAMA_ATTN_ROT_DISABLE env var β set to 1 to opt out.use llama_cpp_4::prelude::*;
// TurboQuant is ON by default β just set a quantized KV cache type:
let ctx_params = LlamaContextParams::default()
.with_cache_type_k(GgmlType::Q5_0)
.with_cache_type_v(GgmlType::Q5_0);
let ctx = model.new_context(&backend, ctx_params)?;
use llama_cpp_4::prelude::*;
let ctx_params = LlamaContextParams::default()
.with_cache_type_k(GgmlType::Q5_0)
.with_attn_rot_disabled(true);
let ctx = model.new_context(&backend, ctx_params)?;
// Global process-level toggle (call before creating any context):
use llama_cpp_4::quantize::{attn_rot_disabled, set_attn_rot_disabled};
set_attn_rot_disabled(true);
assert!(attn_rot_disabled());
set_attn_rot_disabled(false); // restore
# API reference + PPL table (no model required)
cargo run -p turbo-quant -- --show-api
# Run both passes and compare outputs directly
cargo run -p turbo-quant -- \
--model model.gguf \
--kv-type q5_0 \
--prompt "The capital of France is" \
--n-predict 16
Upstream PR #22673 added
MTP draft heads to llama.cpp. The Rust API lives in
llama_cpp_4::mtp: build a target + draft context
pair, wrap them in MtpSession, and drive the
verify/accept loop from Rust.
Both contexts come from the same MTP-capable GGUF. The draft context must
use LlamaContextType::Mtp and
n_rs_seq >= n_draft_max (rollback snapshots for speculative verification):
use llama_cpp_4::prelude::*;
let n_draft_max = 3;
let target = model.new_context(&backend, LlamaContextParams::default())?;
let draft = model.new_context(
&backend,
LlamaContextParams::default()
.with_ctx_type(LlamaContextType::Mtp)
.with_n_rs_seq(n_draft_max.max(4)),
)?;
MtpSessionConfig maps to upstream
common_params_speculative_draft:
| Field | Meaning | Typical value |
|---|---|---|
n_seq | Parallel sequences | 1 |
n_draft_max | Max tokens drafted per round | 1β3 (model-dependent) |
p_min | Drop draft tokens below this probability | 0.0 (upstream default since #23269) |
n_min | Minimum drafts to propose | 0 |
use llama_cpp_4::prelude::*;
let mut session = MtpSession::new(&target, &draft, 1, n_draft_max)?;
let config = MtpSessionConfig::new(1, n_draft_max)
.with_p_min(0.0)
.with_n_min(0);
let mut session = MtpSession::new_with_config(&target, &draft, config)?;
assert!(session.need_embd_pre_norm());
assert!(!session.need_embd());
The Rust API still uses *_pre_norm names; upstream renamed the C API to
llama_set_embeddings_nextn / common_speculative_need_embd_nextn.
Upstream configures next-n extraction on both contexts during session init;
you normally do not need to call
LlamaContext::set_embeddings_pre_norm yourself.
After every target.decode(batch):
session.process(&batch)? β sync MTP with the target batchsession.draft(seq_id, n_past, last_token)? β propose draft tokenssession.accept(seq_id, n_accepted)? β update draft recurrent statesession.print_stats() β log upstream draft/accept counters (optional)See examples/mtp/src/main.rs for a complete
working loop with timing and acceptance reporting.
Smoke test (build contexts only):
cargo run --release -p mtp --features metal -- \
hf-model froggeric/Qwen3.6-27B-MTP-GGUF Qwen3.6-27B-IQ2_M-mtp.gguf
Full generation with draft tuning:
cargo run --release -p mtp --features metal -- \
--predict 64 \
--n-draft-max 1 \
--p-min 0.0 \
--prompt "The capital of France is" \
hf-model froggeric/Qwen3.6-27B-MTP-GGUF Qwen3.6-27B-IQ2_M-mtp.gguf
Use --features cuda or --features vulkan on other platforms instead of
metal.
Draft depth is quant- and model-sensitive. See MTP.md for measured throughput on Apple Silicon and notes on upstream #23269 sampling changes.
For comparison against upstream llama-server --spec-type draft-mtp, use
scripts/bench-mtp.sh.
The incremental-chat example demonstrates incremental prefill β decoding
prompt tokens into the KV cache while the user is still typing, so that
generation starts almost instantly when they press Enter.
apply_chat_templateMeasured on Qwen2.5-0.5B-Instruct Q4_K_M (Apple Silicon, CPU-only).
Run the full benchmark: cargo run --release -p incremental-chat --bin incremental-bench -- model.gguf
Generate charts: cargo run -p incremental-chat --bin incremental-charts


2. Speed β 167 tok/s generation throughput (32 tokens in 191ms)

4. Precision β incremental prefill produces identical first token to normal prefill (β ALL MATCH)

6. DX β 3-method API (new/prefill_speculative/flush), pure userspace pattern, ~130 lines of shared code

Generated 64 tokens and compared output to F16 baseline:
| Config | Diverges at | Quality | Output sample |
|---|---|---|---|
| F16 (baseline) | β | β | "Rust and C++ are both popular programming languages..." |
| Q8_0 + TurboQuant | char 195 | near-identical | Same as F16 for ~195 chars |
| Q5_0 + TurboQuant | char 24 | coherent | "...both high-level programming languages..." |
| Q4_0 + TurboQuant | char 24 | coherent | "...different approaches to memory management..." |
| Q5_0 no TurboQuant | char 2 | β degraded | "The following of the following of the of the..." |
| Q4_0 no TurboQuant | char 2 | β degraded | "The term 'in terms of memory safety...is a programming language..." |
TurboQuant makes quantized KV cache usable. Without it, Q5_0/Q4_0 produce degenerate output (diverges at char 2). With it, Q5_0 produces coherent text that diverges only in wording, while Q8_0 is near-identical to F16. See also the TurboQuant section for PPL and VRAM numbers.

9 sampler configurations tested with seed=42 for reproducibility:
| Sampler | Gen (48t) | tok/s | Output style |
|---|---|---|---|
| greedy (t=0) | 306 ms | 157 | Deterministic, factual |
| temp=0.1 top_k=40 | 306 ms | 157 | Nearly identical to greedy |
| temp=0.4 top_p=0.9 | 330 ms | 145 | Slight variation, still focused |
| temp=0.7 top_p=0.9 | 335 ms | 143 | Creative, writes actual haiku |
| temp=1.0 top_p=0.95 | 413 ms | 116 | More diverse, poetic |
| temp=1.5 top_k=50 | 307 ms | 156 | Wild β mixes English and Chinese |
| min_p=0.05 t=0.7 | 310 ms | 155 | Focused, similar to top_p |
| top_n_sigma=1.0 | 643 ms | 75 | Slower (large candidate set) |
| mirostat_v2 Ο=5 | 565 ms | 85 | Adaptive, poetic output |
Key findings:
temp=0.7 + top_p=0.9 is the sweet spot for creative tasksmin_p is a fast alternative to top_p with similar quality# Interactive chat with live prefill
cargo run --release -p incremental-chat -- local model.gguf
# Quantized KV cache with TurboQuant (saves VRAM, near-zero quality loss)
cargo run --release -p incremental-chat -- --kv-type q5_0 local model.gguf
# Without TurboQuant (for comparison)
cargo run --release -p incremental-chat -- --kv-type q5_0 --no-turbo-quant local model.gguf
# Cache the system prompt session to disk (instant restart)
cargo run --release -p incremental-chat -- --session-cache sys.session local model.gguf
# Custom system prompt, debounce, and sliding window
cargo run --release -p incremental-chat -- \
--system-prompt "You are a pirate. Respond only in pirate speak." \
--debounce-ms 100 --keep-turns 4 \
local model.gguf
# Run the comprehensive benchmark (7 dimensions)
cargo run --release -p incremental-chat --bin incremental-bench -- model.gguf
The key building blocks from llama-cpp-4:
use llama_cpp_4::llama_batch::LlamaBatch;
use llama_cpp_4::token::LlamaToken;
// Decode only new tokens (the delta) into the KV cache.
// Withhold the last 2 tokens β BPE can retroactively change them
// when the next character is typed.
let new_tokens = model.str_to_token(&user_text, AddBos::Always)?;
let stable_end = new_tokens.len().saturating_sub(2); // BPE margin
let common = find_common_prefix(&cached_tokens, &new_tokens[..stable_end]);
// Trim cache if the user edited earlier text
if common < cached_tokens.len() {
ctx.clear_kv_cache_seq(Some(0), Some(common as u32), None)?;
}
// Decode only the genuinely new stable tokens
let mut batch = LlamaBatch::new(512, 1);
for (i, &token) in new_tokens[common..stable_end].iter().enumerate() {
let pos = (common + i) as i32;
batch.add(token, pos, &[0], i == stable_end - common - 1)?;
}
ctx.decode(&mut batch)?;
// When user presses Enter: flush ALL tokens (including the tail)
| Feature | Hardware | Flag |
|---|---|---|
cuda | NVIDIA (CUDA) | --features cuda |
metal | Apple Silicon | --features metal |
vulkan | AMD / Intel / cross-platform | --features vulkan |
native | CPU with AVX2/NEON auto-detect | --features native |
openmp | Multi-core CPU (default on) | --features openmp |
rpc | Remote compute backend | --features rpc |
prebuilt | All (build optimization) | --features prebuilt |
# Metal (macOS)
cargo run -p openai-server --features metal -- --n-gpu-layers 99 \
local model.gguf
# CUDA (Linux/Windows)
cargo run -p openai-server --features cuda -- --n-gpu-layers 99 \
local model.gguf
# Vulkan (cross-platform)
cargo run -p openai-server --features vulkan -- --n-gpu-layers 99 \
hf-model bartowski/Llama-3.2-3B-Instruct-GGUF Llama-3.2-3B-Instruct-Q4_K_M.gguf
The default dynamic-link feature builds llama.cpp as shared libraries, and
llama-cpp-sys-4 places them next to the binaries Cargo produces. Locating them
at runtime is platform-specific:
| Platform | How the binary finds the libraries | Action needed |
|---|---|---|
| macOS | Install names are rewritten to @loader_path/β¦ by the build script | none |
| Windows | The loader searches the directory of the .exe | none |
| Linux / BSD | ELF requires an rpath on the final executable | see below |
Cargo does not add an rpath to the binaries it builds, and a dependency's build
script cannot inject link arguments into a dependent crate's binary β so on
Linux the consuming project has to supply it. This repository does so in
.cargo/config.toml; in your own project add:
# .cargo/config.toml
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-Wl,-rpath,$ORIGIN"]
Note that setting the RUSTFLAGS environment variable replaces these flags
rather than appending to them, so a CI job that sets RUSTFLAGS must repeat the
rpath itself.
Without this, cargo run and cargo test still work β they set
LD_LIBRARY_PATH / DYLD_FALLBACK_LIBRARY_PATH to the target directory β but
running the binary directly fails with libggml-base.so.0: cannot open shared object file. To sidestep runtime lookup entirely, link statically instead:
llama-cpp-4 = { version = "0.7.0", default-features = false }
All examples and the server accept a hf-model <repo> [quant] subcommand
that downloads models from the Hub (cached in ~/.cache/huggingface/).
# Interactive quant picker for repos with many options
cargo run -p openai-server -- hf-model unsloth/Qwen3.5-397B-A17B-GGUF
# Select by quant name (downloads all shards automatically)
cargo run -p openai-server -- hf-model unsloth/Qwen3.5-397B-A17B-GGUF Q4_K_M
# Exact filename
cargo run -p openai-server -- \
hf-model TheBloke/Llama-2-7B-Chat-GGUF llama-2-7b-chat.Q4_K_M.gguf
Set HUGGING_FACE_HUB_TOKEN for gated models.
# Clone with submodules (llama.cpp is a submodule of llama-cpp-sys-4)
git clone --recursive https://github.com/eugenehp/llama-cpp-rs
# Or after cloning without --recursive
git submodule update --init --recursive
# Build everything (with optimizations)
cargo build
# Build with prebuilt artifacts for faster compilation
cargo build --features prebuilt
# Run all unit tests (no model required)
cargo test
# Run server unit tests specifically
cargo test -p openai-server
The build system includes several optimizations for faster compilation:
--features prebuilt) (8% faster debug builds, 50-80% expected for CI/CD)For best performance, install the recommended tools:
# macOS
brew install ninja sccache
# Linux (Ubuntu/Debian)
sudo apt-get install ninja-build mold
cargo install sccache
# Enable detailed build logging
BUILD_DEBUG=1 cargo build
See BUILD_OPTIMIZATIONS.md for more details.
cd llama-cpp-sys-4/llama.cpp
git fetch origin master
git checkout origin/master # or a specific commit/tag
cd ../..
cargo build # build.rs regenerates bindings automatically
Build with --features mtmd. The server auto-detects mmproj-*.gguf next to the
model, or accept --mmproj PATH. Upload images via POST /v1/files, then reference
them in chat messages (image_url / image_file parts β see
examples/server/README.md).
cargo run -p openai-server --features mtmd --release -- \
hf-model unsloth/Qwen3.5-27B-GGUF Qwen3.5-27B-Q4_0
Or with an explicit mmproj path:
cargo run -p openai-server --features mtmd -- \
--mmproj mmproj-BF16.gguf \
hf-model unsloth/Qwen3.5-27B-GGUF Qwen3.5-27B-Q4_0
cargo run --features mtmd -p mtmd -- \
--model /path/to/model.gguf \
--mmproj /path/to/mmproj.gguf \
--image /path/to/image.jpg \
--prompt "Describe this image."
Originally derived from llama-cpp-2 β thanks to those contributors.
See also bitnet-cpp-rs for highly-quantized BitNet model support.
@software{hauptmann2025llamacpprs,
author = {Hauptmann, Eugene},
title = {{llama-cpp-4}: llama-cpp {Rust} wrapper},
year = {2025},
version = {0.7.0},
url = {https://github.com/eugenehp/llama-cpp-rs},
}
This project is licensed under the MIT License.
Β© 2025-2026, Eugene Hauptmann
(top 30 of 37)
Rust
88.5%
C++
6.7%
C
2.6%
Shell
1.9%