Quaynor's engine is written in Rust, and the quaynor crate gives you that engine directly — no FFI layer, no bridge, just the same API that powers every other binding.

Install

cargo add quaynor

Building compiles llama.cpp from source, so you need CMake and a C/C++ toolchain installed. GPU acceleration is selected per platform automatically: Metal on macOS, Vulkan on x86/x86_64/aarch64 Linux and Windows.

Chat

Models load from a local path, a URL, or a Hugging Face path (hf://owner/repo/file.gguf), downloaded and cached automatically:

use quaynor::chat::ChatBuilder;
use quaynor::llm::get_model;
use std::sync::Arc;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let model = Arc::new(get_model(
        "hf://bartowski/Qwen_Qwen3-0.6B-GGUF/Qwen_Qwen3-0.6B-Q4_K_M.gguf",
        true, // use GPU if available
        None, // no multimodal projector
    )?);

    let chat = ChatBuilder::new(model)
        .with_context_size(4096)
        .with_system_prompt(Some("You are a helpful assistant."))
        .build();

    let mut stream = chat.ask("Is a zebra black or white?");
    while let Some(token) = stream.next_token() {
        print!("{token}");
    }
    Ok(())
}

Wait for the whole reply instead of streaming with .completed():

let full = chat.ask("Why is the sky blue?").completed()?;

Async

ChatBuilder::build_async() returns a ChatHandleAsync for use inside a Tokio runtime:

let chat = ChatBuilder::new(model).build_async();
let mut stream = chat.ask("Tell me a joke");
while let Some(token) = stream.next_token().await {
    print!("{token}");
}

Tool calling

Tools are grammar-constrained with GBNF, so the model can only produce valid calls:

use quaynor::tool_calling::Tool;
use std::sync::Arc;

let circle_area = Tool::new(
    "circle_area",
    "Area of a circle from radius",
    serde_json::json!({
        "type": "object",
        "properties": { "radius": { "type": "number" } },
        "required": ["radius"]
    }),
    Arc::new(|args| {
        let r = args["radius"].as_f64().unwrap_or(0.0);
        format!("{:.2}", std::f64::consts::PI * r * r)
    }),
);

let chat = ChatBuilder::new(model).with_tool(circle_area).build();

Two sandboxed tools ship built in: Tool::python(..) runs snippets in the monty interpreter and Tool::bash(..) in the bashkit virtual shell — both isolated from the host filesystem and network.

Beyond chat

Module What it does
quaynor::encoder Embedding generation
quaynor::crossencoder Cross-encoder reranking
quaynor::tokenizer Tokenize / detokenize helpers
quaynor::template Minijinja chat-template rendering
quaynor::sampler_config Sampler presets and full sampler chains

Logging

Quaynor uses the tracing ecosystem. Forward llama.cpp's own logs into it with:

quaynor::send_llamacpp_logs_to_tracing();

Full API reference lives on docs.rs/quaynor.