llm
Running Llama and Qwen language models from a GGUF file.
import llm
This package runs large language models of the Llama architecture, such as Llama, Qwen and Mistral, on the processor with no outside dependency. Download the quantised model in GGUF form, read it with LoadGgufModel and make text with Generate.
For more control, Forward gives the logits and a Sampler picks the next token with temperature, top-k, top-p and a repetition penalty. The runnable example builds a small random model so it works without a download.
Types
Tokenizer
struct Tokenizer| Name | Type | Description |
|---|---|---|
kind | int | The kind. |
tokens | Vector | The text of the tokens. |
scores | Vector | The scores, for SentencePiece. |
index | HashMap | The index of each token. |
merges | HashMap | The ranks of the BPE merges. |
merge_keys | Vector | The merge keys. |
bos | int = -1 | The index of the beginning token; -1 means there is none. |
eos | int = -1 | The index of the end token. |
unk | int = 0 | The index of the unknown token. |
add_bos | bool = true | Add the beginning token when encoding. |
byte_ids | Vector | The token index of each byte. |
b2u | Vector | The byte to character map of byte level BPE. |
import llm
import tensor
func main:
mut tokenizer := llm.NewTokenizer(llm.TOK_BPE)
each text in ["a", "b", "c", "ab"]:
_ := tokenizer.add_token(text, 0.0)
end
tokenizer.add_merge("a", "b")
tokenizer.finish()
tokenizer.add_bos = false
mut ids := tokenizer.encode("abcab")
println tokenizer.size(), ids.len(), tokenizer.token_text(ids.get(0)), tokenizer.decode(ids)
println llm.TOK_SPM, llm.ROPE_NORMAL, llm.ROPE_NEOX
mut config := llm.Config { vocab = tokenizer.size(), dim = 8, layers = 2, heads = 2, kv_heads = 1, ff = 16, ctx = 32 }
println config.head_dim()
mut model := llm.RandomModel(config, 7)
mut cache := llm.NewKVCache(config)
println model.ok, model.layers.len(), cache.ctx
mut logits := llm.Forward(model, cache, ids)
println tensor.ShapeString(logits), cache.len
mut last := llm.LogitsRow(logits, ids.len() - 1)
greedy := llm.Greedy(logits, ids.len() - 1)
println tensor.ShapeString(last), greedy == tensor.ArgMaxAll(last)
mut sampler := llm.NewSampler(1)
sampler.temperature = 0.8
sampler.top_k = 3
sampler.top_p = 0.9
sampler.repeat_penalty = 1.1
mut recent := Vector {} as Vector<int>
next := sampler.sample(logits, ids.len() - 1, recent)
println next >= 0 and next < tokenizer.size()
cache.reset()
generated := llm.GenerateIds(model, cache, ids, 4, sampler, -1)
println generated.len()
cache.reset()
text := llm.Generate(model, cache, tokenizer, "ab", 3, sampler)
println text.len() > 0
tensor.Free(logits)
tensor.Free(last)
cache.free()
model.free()
tokenizer.free()
endadd_token
func add_token(text: str, score: f64): intThe example for this item is shown with the ones above: llm-Generate.salam
add_merge
func add_merge(a: str, b: str)The example for this item is shown with the ones above: llm-Generate.salam
finish
func finish()The example for this item is shown with the ones above: llm-Generate.salam
size
func size(): intThe example for this item is shown with the ones above: llm-Generate.salam
token_text
func token_text(id: int): strThe example for this item is shown with the ones above: llm-Generate.salam
encode
func encode(text: str): Vector<int>The example for this item is shown with the ones above: llm-Generate.salam
decode
func decode(ids &: Vector<int>): strThe example for this item is shown with the ones above: llm-Generate.salam
free
func free()The example for this item is shown with the ones above: llm-Generate.salam
Sampler
struct Sampler| Name | Type | Description |
|---|---|---|
temperature | f64 = 1 | The temperature; lower means a more certain answer. |
top_k | int = 0 | Out of the k likeliest tokens only; zero means no limit. |
top_p | f64 = 1 | Out of the tokens whose probabilities add up to p only. |
repeat_penalty | f64 = 1 | The penalty for repeating a recent token. |
rng | tensor.Rng | The random generator. |
The example for this item is shown with the ones above: llm-Generate.salam
sample
func sample(logits &: tensor.Tensor, row: int, recent &: Vector<int>): intThe example for this item is shown with the ones above: llm-Generate.salam
Config
struct Config| Name | Type | Description |
|---|---|---|
vocab | int = 0 | The vocabulary size. |
dim | int = 0 | The embedding size. |
layers | int = 0 | The number of layers. |
heads | int = 1 | The number of attention heads. |
kv_heads | int = 1 | The number of key and value heads; fewer than heads means grouped attention. |
ff | int = 0 | The size of the feed forward layer. |
ctx | int = 512 | The greatest text length. |
eps | f64 = 1e-05 | The small number for normalisation. |
rope_theta | f64 = 10000 | The base of the rotary embedding. |
rope_kind | int | The kind of rotary embedding, one of the ROPE_ constants. |
The example for this item is shown with the ones above: llm-Generate.salam
head_dim
func head_dim(): intThe example for this item is shown with the ones above: llm-Generate.salam
Layer
struct Layer| Name | Type | Description |
|---|---|---|
attn_norm | tensor.Tensor | The normalisation before the attention. |
wq | tensor.Tensor | The query weight. |
wk | tensor.Tensor | The key weight. |
wv | tensor.Tensor | The value weight. |
wo | tensor.Tensor | The attention output weight. |
ffn_norm | tensor.Tensor | The normalisation before the feed forward layer. |
w_gate | tensor.Tensor | The SwiGLU gate weight. |
w_up | tensor.Tensor | The up weight. |
w_down | tensor.Tensor | The down weight. |
The example for this item is shown with the ones above: llm-Generate.salam
free
func free()The example for this item is shown with the ones above: llm-Generate.salam
Model
struct Model| Name | Type | Description |
|---|---|---|
cfg | Config | The configuration. |
tok_embed | tensor.Tensor | The token embedding table. |
layers | Vector | The layers. |
out_norm | tensor.Tensor | The final normalisation. |
out_head | tensor.Tensor | The output weight back to the vocabulary. |
ok | bool = false | It was built or loaded correctly. |
The example for this item is shown with the ones above: llm-Generate.salam
free
func free()The example for this item is shown with the ones above: llm-Generate.salam
KVCache
struct KVCache| Name | Type | Description |
|---|---|---|
k | Vector | The keys, one set per layer. |
v | Vector | The values. |
len | int = 0 | The number of tokens processed. |
ctx | int = 0 | The capacity. |
The example for this item is shown with the ones above: llm-Generate.salam
reset
func reset()The example for this item is shown with the ones above: llm-Generate.salam
free
func free()The example for this item is shown with the ones above: llm-Generate.salam
Constants and variables
TOK_SPM
const TOK_SPM: i32 = 0The example for this item is shown with the ones above: llm-Generate.salam
TOK_BPE
const TOK_BPE: i32 = 1The example for this item is shown with the ones above: llm-Generate.salam
ROPE_NORMAL
const ROPE_NORMAL: i32 = 0The example for this item is shown with the ones above: llm-Generate.salam
ROPE_NEOX
const ROPE_NEOX: i32 = 1The example for this item is shown with the ones above: llm-Generate.salam
Functions
NewTokenizer
func NewTokenizer(kind: int): TokenizerThe example for this item is shown with the ones above: llm-Generate.salam
NewSampler
func NewSampler(seed: i64): SamplerThe example for this item is shown with the ones above: llm-Generate.salam
Greedy
func Greedy(logits &: tensor.Tensor, row: int): intThe example for this item is shown with the ones above: llm-Generate.salam
GenerateIds
func GenerateIds(m &: Model, cache &: KVCache, prompt_ids &: Vector<int>, max_new: int, sp &: Sampler, eos: int): Vector<int>The example for this item is shown with the ones above: llm-Generate.salam
Generate
func Generate(m &: Model, cache &: KVCache, tok &: Tokenizer, prompt: str, max_new: int, sp &: Sampler): strThe example for this item is shown with the ones above: llm-Generate.salam
Forward
func Forward(m &: Model, cache &: KVCache, tokens &: Vector<int>): tensor.TensorThe example for this item is shown with the ones above: llm-Generate.salam
LogitsRow
func LogitsRow(logits &: tensor.Tensor, row: int): tensor.TensorThe example for this item is shown with the ones above: llm-Generate.salam
ConfigFromGguf
func ConfigFromGguf(g &: gguf.Gguf): Configimport llm
import gguf
func main:
path := "models/qwen2.5-0.5b-instruct-q8_0.gguf"
mut model := llm.Model {}
mut tokenizer := llm.NewTokenizer(llm.TOK_BPE)
if not llm.LoadGgufModel(path, model, tokenizer):
println "the model was not loaded:", path
ret
end
mut cache := llm.NewKVCache(model.cfg)
mut sampler := llm.NewSampler(42)
sampler.temperature = 0.7
println llm.Generate(model, cache, tokenizer, "The capital of France", 20, sampler)
mut good := false
mut file := gguf.LoadGguf(path, good)
config := llm.ConfigFromGguf(file)
println config.vocab, config.dim, config.layers, config.heads, config.kv_heads, config.ff, config.ctx, config.eps, config.rope_theta, config.rope_kind
mut model2 := llm.ModelFromGguf(file)
mut tokenizer2 := llm.TokenizerFromGguf(file)
println tokenizer2.bos, tokenizer2.eos, tokenizer2.unk
model2.free()
tokenizer2.free()
file.free()
cache.free()
model.free()
tokenizer.free()
endModelFromGguf
func ModelFromGguf(g &: gguf.Gguf): ModelThe example for this item is shown with the ones above: llm-Load.salam
TokenizerFromGguf
func TokenizerFromGguf(g &: gguf.Gguf): TokenizerThe example for this item is shown with the ones above: llm-Load.salam
LoadGgufModel
func LoadGgufModel(path: str, m &: Model, t &: Tokenizer): bool.gguf file.The example for this item is shown with the ones above: llm-Load.salam
NewKVCache
func NewKVCache(cfg &: Config): KVCacheThe example for this item is shown with the ones above: llm-Generate.salam
RandomModel
func RandomModel(cfg &: Config, seed: i64): ModelThe example for this item is shown with the ones above: llm-Generate.salam