Skip to main content

kopitiam_runtime/
lib.rs

1//! **Kopitiam Runtime**: the Qwen-family transformer forward pass, running
2//! entirely in Rust on CPU, offline.
3//!
4//! This crate is the payoff of the layers beneath it. `kopitiam-core`
5//! defines the shared vocabulary ([`kopitiam_core::DType`],
6//! [`kopitiam_core::Shape`]); `kopitiam-tensor` implements the CPU tensor
7//! and its ops (matmul, softmax, RMSNorm, SiLU, embedding gather, GGUF
8//! block-quantization decoding); `kopitiam-loader` parses GGUF/SafeTensors
9//! files into raw bytes plus dtype/shape metadata; `kopitiam-tokenizer`
10//! turns text into token ids and back. None of those crates constructs a
11//! model or runs a forward pass — this one does.
12//!
13//! # Pipeline
14//!
15//! ```text
16//! kopitiam_loader::load_model(path)
17//!   -> LoadedModel
18//!   -> QwenModel::from_loaded_model         (crate::model)
19//!        - QwenConfig::from_metadata        (crate::config)
20//!        - ModelWeights::load               (crate::weights, via crate::bridge)
21//!        - RotaryEmbedding::new             (crate::rope)
22//!   -> generate(&model, &tokenizer, prompt, ...)   (crate::generate)
23//!        per new token:
24//!          embedding lookup                  (Tensor::gather_rows)
25//!          per layer: block_forward           (crate::block)
26//!            RMSNorm -> attention_forward     (crate::attention)
27//!              RoPE (split-half)              (crate::rope)
28//!              grouped-query KV repeat        (crate::attention::repeat_kv_heads)
29//!              causal mask + softmax
30//!              KV cache read/append           (crate::kv_cache)
31//!            RMSNorm -> swiglu_mlp            (crate::mlp)
32//!          final RMSNorm -> output projection
33//!          [optional] constraint mask -> -inf  (crate::constraint)
34//!          greedy sampling                    (crate::sampling)
35//! ```
36//!
37//! # Module map
38//!
39//! * [`bridge`] — the loader/tensor glue: bytes + dtype + shape -> `Tensor`.
40//!   `kopitiam-loader` and `kopitiam-tensor` were built concurrently and
41//!   deliberately do not depend on each other; this crate is the first one
42//!   downstream of both, so this is where that gap is bridged.
43//! * [`config`] — [`config::QwenConfig`], the architecture hyperparameters
44//!   resolved (with documented fallbacks and validation) from
45//!   [`kopitiam_loader::ModelMetadata`].
46//! * [`rope`] — rotary position embeddings, split-half ("NEOX") pairing.
47//!   Read this module's docs before touching anything position-related; a
48//!   swapped pairing convention is this crate's single easiest place to
49//!   introduce silent, undetectable-by-type-system wrongness.
50//! * [`kv_cache`] — the per-layer, growable key/value cache that makes
51//!   autoregressive decoding linear instead of quadratic in sequence
52//!   length.
53//! * [`attention`] — grouped-query causal self-attention: repeating shared
54//!   KV heads across their query-head group, causal masking, and wiring
55//!   RoPE and the KV cache into one attention forward pass.
56//! * [`mlp`] — the SwiGLU feed-forward block.
57//! * [`linear`] — the single `x @ W^T + b` helper every projection in this
58//!   crate goes through.
59//! * [`block`] — one pre-norm transformer block (attention sub-layer, MLP
60//!   sub-layer, both with a residual connection).
61//! * [`weights`] — loads every named GGUF weight tensor a block/model
62//!   needs.
63//! * [`model`] — [`model::QwenModel`]: wires embedding, every block, the
64//!   final norm, and the (possibly tied) output projection into a
65//!   [`traits::Model`] implementation.
66//! * [`traits`] — [`traits::Model`] and [`traits::Backend`], the Model
67//!   Runtime boundary every layer above this crate should code against.
68//! * [`sampling`] — turning a row of logits into a token id:
69//!   [`sampling::GreedySampler`] (`argmax`) and
70//!   [`sampling::StochasticSampler`] (temperature/top-k/top-p/min-p/
71//!   repetition penalty, composed as a pipeline and driven by a seeded
72//!   PRNG — see that module's docs for the pipeline shape and why
73//!   seedability is mandatory, not optional).
74//! * [`constraint`] — grammar-constrained decoding (the keystone): mask the
75//!   tokens a [`constraint::TokenConstraint`] forbids to `-inf` at the *front*
76//!   of the sampling path, *before* temperature/top-k/top-p, so the model
77//!   physically cannot emit invalid structure. Ships a fixed allowed-token-set
78//!   ([`constraint::AllowedTokens`], e.g. a tool-name enum) and a structural
79//!   JSON constraint ([`constraint::JsonStructure`]); [`constraint::mask_logits`]
80//!   is the masking step and [`constraint::ConstrainedSampler`] the drop-in
81//!   sampler wrapper, driven end to end by [`generate::generate_constrained`].
82//!   See that module's docs for why mask-before-sample and `-inf`-not-`0.0`
83//!   (AID-0045).
84//! * [`gguf_tokenizer`] — builds a [`kopitiam_tokenizer::BpeTokenizer`]
85//!   directly from a GGUF file's embedded `tokenizer.ggml.*` vocabulary
86//!   (no companion `tokenizer.json` needed).
87//! * [`generate`] — the end-to-end entry point:
88//!   `prompt -> tokens -> forward passes -> sampled ids -> text`, with
89//!   streaming per-token output.
90//!
91//! # What is here as of Phase 2, and what is still deliberately not
92//!
93//! As of Phase 2: stochastic sampling ([`sampling::StochasticSampler`] —
94//! temperature/top-k/top-p/min-p/repetition penalty) alongside greedy, and
95//! a fused quantized matmul for `Q4_0`/`Q8_0` matmul-operand weights (see
96//! [`kopitiam_tensor::Tensor::quantized_matmul`] and
97//! [`bridge::load_matmul_weight`] — weights whose on-disk dtype is
98//! quantized now stay quantized in memory instead of being eagerly
99//! dequantized to `f32`, which is what makes a multi-gigabyte model's
100//! resident memory footprint match its on-disk size rather than balloon by
101//! 4-8x).
102//!
103//! Still not here: no batching across multiple concurrent prompts; no
104//! scheduler or execution graph (see the parent epic, `kopitiam-082`,
105//! Phase 3, and this crate's benchmark module for why a general graph
106//! engine was judged not to earn its keep yet); no SIMD. "Correct before
107//! fast" per this workspace's engineering principles remains the ordering
108//! principle — every fast path added so far (quantized matmul) ships
109//! alongside, and is tested against, the plain reference it replaces.
110
111mod attention;
112mod block;
113mod bridge;
114mod config;
115mod constraint;
116mod gguf_tokenizer;
117mod generate;
118mod kv_cache;
119mod linear;
120mod mlp;
121mod model;
122mod rope;
123mod sampling;
124mod weights;
125
126#[cfg(test)]
127mod test_support;
128
129pub mod traits;
130
131pub use bridge::{load_matmul_weight, load_matmul_weight_opt, load_tensor_f32, load_tensor_f32_opt, tensor_from_entry};
132pub use config::QwenConfig;
133pub use constraint::{
134    AllowedSet, AllowedTokens, ConstrainedSampler, ConstraintError, DecodeState, JsonStructure, SliceVocab,
135    TokenConstraint, TokenVocab, mask_logits,
136};
137pub use generate::{ConstrainedGenerateError, GenerationConfig, generate, generate_constrained, generate_with_sampler};
138pub use gguf_tokenizer::tokenizer_from_gguf;
139pub use kv_cache::KvCache;
140pub use model::QwenModel;
141pub use rope::RotaryEmbedding;
142pub use sampling::{GreedySampler, Sampler, SamplingConfig, StochasticSampler, greedy_argmax};
143pub use traits::{Backend, CpuBackend, Model};
144
145// Re-exported for ergonomics: every public signature in this crate already
146// names these types (`QwenConfig` fields, `Model::forward`'s `Result`), so
147// callers otherwise need a second `use` line from `kopitiam_core` just to
148// name what this crate hands them — the same convention
149// `kopitiam-tensor`/`kopitiam-loader` already follow at their own crate
150// boundaries.
151pub use kopitiam_core::{DType, Device, Error, Result, Shape};