Language models¶
iron.lm is what every language model shares; each model is a package
under iron/applications/ over it. The agent instructions
describe how the modules fit together.
config
¶
A model's shape.
Config, read from a checkpoint's own config.json, and a decoder's,
DecoderConfig, with its RoPE table (rope_angles).
Config
dataclass
¶
A model's shape, built from a checkpoint's config.json (from_checkpoint).
precision is every projection's, chosen per application on its
measured accuracy, with no default here.
from_checkpoint
classmethod
¶
cls's fields read from path's own config.json.
Each at its metadata["json"] path (under json_prefix),
converted by metadata["convert"] if it names one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
A |
required |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
No |
ValueError
|
The file lacks a field that has no default. |
Source code in iron/lm/config.py
from_json
classmethod
¶
from_checkpoint on data, a config.json already parsed.
source names it in an error.
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/config.py
json_key
classmethod
¶
Where config.json holds field f; None: nowhere.
Source code in iron/lm/config.py
to_json
¶
to_json() -> dict
Return the fields as config.json names them, from_checkpoint's inverse.
A nested Config gives its own to_json(), another dataclass
its fields. A not_json field, or one that is None or not JSON,
is left out.
Source code in iron/lm/config.py
DecoderConfig
dataclass
¶
DecoderConfig(vocab_size: int, hidden_size: int, num_hidden_layers: int, num_attention_heads: int, num_key_value_heads: int, intermediate_size: int, rope_theta: float | None, max_seq_len: int, rope_scaling: RopeScaling | None = None, rotary_dim: int | None = not_json(None), rope_attention_scaling: float = not_json(1.0), prefill_chunk: int = not_json(2048), hidden_act: str = 'silu', tie_word_embeddings: bool = True, *, eps: float = 1e-06, precision: Precision, head_dim: int | None = None, attention_bias: bool = False, mlp_bias: bool = False, sliding_window: int | None = None, use_sliding_window: bool | None = None)
Bases: Config
A decoder's shape, named as a Hugging Face checkpoint's own config.json does where it has one.
max_seq_len is the checkpoint's native context unless a caller
lowers it.
window
property
¶
window: int | None
The positions before a query each layer attends to (KVCache's window), sliding_window less the query's own.
None is all.
rope_width
property
¶
rope_width: int
The angle table's own width: head_dim, or rotary_dim under partial rotary.
angles
¶
angles() -> ndarray | None
Return the RoPE table, a row per position; None with no rope_theta.
Source code in iron/lm/config.py
rope_angles
¶
rope_angles(head_dim: int, context_length: int, rope_theta: float = 500000.0, scaling: RopeScaling | None = None) -> ndarray
Return the RoPE table, (context_length, head_dim) float32, cos and sin interleaved.
Each transcendental is evaluated in float64 and rounded once.
Source code in iron/lm/config.py
unscaled
¶
unscaled(rope_scaling: dict) -> None
Return None for a checkpoint's rope_scaling that leaves RoPE as it is.
Raises:
| Type | Description |
|---|---|
ValueError
|
It scales RoPE, which this config builds no table for. |
Source code in iron/lm/config.py
checkpoint
¶
A model's weights: Checkpoint.tree(), the tensors by their own names.
model.layers.3.self_attn.q_proj.weight is
weights.model.layers[3].self_attn.q_proj.weight.
Checkpoint
¶
A .safetensors file or a PyTorch checkpoint.
Or, given its .safetensors.index.json, every shard a sharded
checkpoint splits across, its tensors merged into one mapping,
tensors.
Source code in iron/lm/checkpoint.py
release
¶
release(array: ndarray) -> None
Drop the process's pages of array if it is a view of one of the files.
Anything else is left alone.
fingerprint
¶
fingerprint() -> str
Return a digest of each tensor's name, type, shape and first/last 4 KiB.
What tells a fine-tune from its base without reading either whole.
Source code in iron/lm/checkpoint.py
tree
¶
tree(fill: Literal['read', 'random', 'unread'] = 'read', seed: int = 0) -> SimpleNamespace
Return the checkpoint's tensors as a tree of param, split at the dots.
model.layers.3.weight becomes tree.model.layers[3].weight.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fill
|
Literal['read', 'random', 'unread']
|
What the arrays hold: |
'read'
|
seed
|
int
|
The seed |
0
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/checkpoint.py
weights_file
¶
weights_file(directory) -> Path
Return a checkpoint directory's weights file.
A sharded checkpoint's model.safetensors.index.json, else
model.safetensors.
Source code in iron/lm/checkpoint.py
leaves
¶
Return each leaf of tree, a Checkpoint.tree()'s namespaces and lists.
By its dotted name: nest's inverse.
Source code in iron/lm/checkpoint.py
nest
¶
Nest values by dotted name, as Checkpoint.tree() nests them.
Under path (a dotted prefix) of file, which a missing name
names.
Source code in iron/lm/checkpoint.py
subtree
¶
Build a tree of modules, each a module of tree, at their names.
A checkpoint's part as a model loads it (a text decoder of a
vision-language checkpoint), a missing name naming tree's file.
Source code in iron/lm/checkpoint.py
renamed
¶
Copy tree with each leaf under a run of whole dotted names moved.
A leaf among renames' keys moves to the run its value names, the
rest stay where they are; its namespaces are new, so a caller may
change them and leave tree as it was.
Source code in iron/lm/checkpoint.py
blocks
¶
The blocks a model builds from operators, for a graph's body.
A graph here is called in a model's body as it is on its own (Graph):
its steps record into the model's. Weights are a checkpoint's (out, in),
projected by F.linear.
LogMel
¶
LogMel(frame: int = 320, hop: int = 160, fft: int = 512, mels: int = 128, sample_rate: int = 16000, max_hz: float = 8000.0, mel_floor: float = 0.001)
Bases: Graph
The log-mel spectrogram of float32 samples, as Hugging Face computes it.
Gemma 4's feature extractor, via bf16-limb GEMMs (Limbs) for exact
float32 rfft and filterbank products.
Source code in iron/lm/blocks.py
ImagePatches
¶
ImagePatches(patches: int, image: tuple[int, int], pool: int = 3, positions: int = 10240, patch_columns: int = 8, chunk: int = 4096)
Bases: Graph
A decoded image resized as torchvision's antialiased bicubic resize does it.
Rescaled to [0, 1] and cut row-major into a vision tower's
patches rows of 16x16-pixel patches.
Source code in iron/lm/blocks.py
pack
¶
Build a call's image buffer and per-call sizes for a decoded image.
Resized to out_height by out_width.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
image
|
ndarray
|
|
required |
out_height
|
int
|
The height to resize to. |
required |
out_width
|
int
|
The width to resize to. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
The image or its patches do not fit a call, or
|
Source code in iron/lm/blocks.py
Weights
¶
Linear
¶
Bases: Graph
torch.nn.Linear: x @ weight.T, plus bias where it holds one.
At precision (F.linear's, the caller's choice).
Source code in iron/lm/blocks.py
split
¶
Project x through each run of rows of the weight in turn.
Each a fused projection's output; splitting the output columns instead would need a strided view no bounded-row descriptor fits.
Source code in iron/lm/blocks.py
Embedding
¶
RMSNorm
¶
Bases: Graph
torch.nn.RMSNorm over the last axis, times weight.
eps None is the input dtype's machine epsilon.
Source code in iron/lm/blocks.py
plus_one
staticmethod
¶
Make every RMSNorm of module, loaded, scale by 1 + weight.
As Gemma's and Qwen3.5's do, the sum in float32 rounded to bf16 (Hugging Face keeps it float32).
Source code in iron/lm/blocks.py
LayerNorm
¶
Bases: Graph
torch.nn.LayerNorm over the last axis, times weight plus bias.
Computed by F.layer_norm.
Source code in iron/lm/blocks.py
Rope
¶
Rope(c, q_scale: ndarray | None = None)
Bases: Graph
A decoder's RoPE table, one row per position (DecoderConfig.angles).
q and k share it unless q_scale gives q a scaled table of its own. A
model copies each call's rows once (rows) for every layer to read.
Source code in iron/lm/blocks.py
rows
¶
rows(call) -> tuple
Return call's rows of q's table and of k's.
The same rows where they share one: a prompt chunk's from its
start, else the decode step's at its position.
Source code in iron/lm/blocks.py
table_start
¶
KVCache
¶
KVCache(c, window: int | None = None)
Bases: Graph
A layer's keys and values and causal attention over them (attend).
A prompt chunk writes a run of rows; a decode step attends with MHA
over one query, which costs the context, not the cache.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
window
|
int | None
|
The positions before a query it attends to besides its own
(None: all). Hugging Face's |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
A ring with a chunk of other than a power of two. |
Source code in iron/lm/blocks.py
restart
¶
Return where a call may start to change the positions from held on.
After ran ran: the chunk boundary before it while the ring
still holds the window before that, else the first.
Source code in iron/lm/blocks.py
attend
¶
attend(call, q, k, v, scale: float | None = None)
Write k and v (rows, groups, head_dim) at the call's positions.
None for a layer reading another's cache. Attends q (rows,
heads, head_dim) over them; (rows, heads * head_dim).
Source code in iron/lm/blocks.py
NoCache
¶
Bases: Graph
No memory: attention over the call's own rows, both ways, or back if causal.
Within window positions either side (None: all), what an encoder's
layer holds in a KVCache's place.
Source code in iron/lm/blocks.py
attend
¶
attend(call, q, k, v, scale: float | None = None)
Attend q (rows, heads, head_dim) over k and v.
k and v are (rows, groups, head_dim), the logits scaled
by scale (MHA's 1 / sqrt(head_dim) if None); (rows, heads
* head_dim).
Source code in iron/lm/blocks.py
Attention
¶
Bases: Graph
Self-attention for layer i of a decoder with config c.
q, k and v project, RoPE rotates the first rotary columns when set,
and attention runs over the layer's KVCache.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
norms
|
str
|
Which of q, k and v each head of is RMS-normed before RoPE
( |
''
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/blocks.py
projections
¶
q_proj, k_proj and v_proj.
Source code in iron/lm/blocks.py
project
¶
project(x, **gemv) -> list
Project q, k and v of x, each given the GEMV's gemv.
None for a projection the layer does not hold.
Source code in iron/lm/blocks.py
rotate
¶
Turn x (rows, head_dim) by RoPE.
Only its first rotary columns where given.
Source code in iron/lm/blocks.py
output
¶
Return the block's output from the heads o (rows, heads * head_dim).
Of the input x.
MLP
¶
Bases: Graph
A feed-forward on c's shape: fc2(act(fc1(x))), act is hidden_act.
Each projection has a bias where the config's mlp_bias says, at
precision.
Source code in iron/lm/blocks.py
GatedMLP
¶
DecoderLayer
¶
DecoderLayer(c, i: int, self_attn=None, mlp=None, cache=None)
Bases: Graph
Layer i of a pre-norm decoder with config c.
input_layernorm then self_attn over the layer's cache,
post_attention_layernorm then mlp, each scaled by
residual_multiplier before its residual.
Source code in iron/lm/blocks.py
mix
¶
Attend the normed rows h over the layer's cache.
EncoderLayer
¶
EncoderLayer(c, i: int, self_attn=None)
Bases: DecoderLayer
Layer i of a pre-norm encoder, as in SigLIP and Whisper.
DecoderLayer's two blocks after a LayerNorm, bidirectional
attention with no cache (NoCache) and a plain (ungated) MLP.
Source code in iron/lm/blocks.py
SandwichLayer
¶
SandwichLayer(c, i: int, self_attn=None, mlp=None, cache=None)
Bases: DecoderLayer
DecoderLayer with each block's output normed again before its residual.
(post_attention_layernorm, post_feedforward_layernorm) and
mlp's input normed by pre_feedforward_layernorm (Gemma's).
Source code in iron/lm/blocks.py
PleLayer
¶
PleLayer(c, i: int, self_attn=None, mlp=None, cache=None)
Bases: SandwichLayer
SandwichLayer, then its per-layer input ple (rows, ple_dim) gated in.
(per_layer_input_gate, per_layer_projection,
post_per_layer_input_norm) and scaled by layer_scalar (Gemma 4's).
Source code in iron/lm/blocks.py
filterbank
¶
Compute the (fft // 2 + 1, mels) unnormalized mel triangles, HTK scale.
Over the rfft's bins, from 0 Hz to max_hz, float64, as transformers'
mel_filter_bank.
Source code in iron/lm/blocks.py
activation
¶
activation(name: str)
Return the function hidden_act name is.
Raises:
| Type | Description |
|---|---|
ValueError
|
No operator computes it: only |
Source code in iron/lm/blocks.py
feature_map
¶
Conv2d, a convolution over the rows of a channels-last feature map.
The map is (rows, width * channels).
Conv2d
¶
Conv2d(weight: ndarray, width: int, stride: int = 1, padding: int = 0, border: int = 0, groups: int = 1)
Bases: Graph
A bias-free 2-D convolution of channels-last rows (rows, width * channels).
Any row padding is held in the input. It computes the per-call rows
of (out_rows, (out_width + 2 * border) * out_channels) summed in
float32. Its output is written to a given y only when it fills whole
GEMM blocks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weight
|
ndarray
|
|
required |
width
|
int
|
The input's pixels a row, its held padding among them. |
required |
stride
|
int
|
The step between output pixels, and rows. |
1
|
padding
|
int
|
The zero columns either side of the width; one channel or depthwise only. |
0
|
border
|
int
|
The zero pixels either side of each output row; one channel or depthwise only. |
0
|
groups
|
int
|
|
1
|
Source code in iron/common/feature_map.py
causal
¶
A decoder-only language model on the NPU, as one graph.
A call with no tensor is a decode step; prefill_chunk token ids
are a prompt chunk from start, of which a call runs the first
rows. Both draw the next token on the device and carry it and
position + 1 into the next call.
Call
dataclass
¶
Call(*, prompt: bool, start: Any, rows: Any, position: Any, rope_start: Any = None, ropes: dict = dict(), ids: Any = None, token: Any = None, embeddings: Any = None)
One call's values, built once in body and read by every memory module.
A memory module is one keeping state from call to call (KVCache,
Checkpoints, Rope): a prompt chunk's rows from start, or
the decode step's one at position.
Checkpoints
dataclass
¶
Checkpoints(slots: State, chunk: int)
A state a decoder carries from position to position, a slot per prompt-chunk boundary.
Slot k holds it after the positions up to k * chunk run so far,
slot 0 (never written) before the sequence.
of
classmethod
¶
of(shape: tuple[int, ...], max_seq_len: int, chunk: int, dtype=bfloat16) -> Checkpoints
Return a state of shape.
A slot for every boundary of chunk-position prompt chunks up to
max_seq_len positions.
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/causal.py
at
¶
Return the slot call reads and the slot it writes, in a graph's body.
The one read holds the state before its first position, the one
written the state after its last. A call ends within the chunk it
starts in (CausalLM splits them).
Source code in iron/lm/causal.py
restart
¶
Where a call may start to change the positions from held on, after ran positions ran.
There where the last call ended, else at the boundary before it.
Source code in iron/lm/causal.py
slot
¶
CausalConv1d
¶
Bases: Graph
A depth-wise causal convolution of kernel taps over a call's rows.
Each of channels its own taps (weight, torch's
(channels, 1, kernel)), plus bias where it holds one. The
kernel - 1 rows before a call are its state, a slot per
prompt-chunk boundary; CausalLM gives every one of a model the
scratch F.causal_conv1d stages a call in (window, tail).
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/causal.py
Decoder
¶
Decoder(c: DecoderConfig, layer: type[DecoderLayer])
Bases: Graph
A decoder's modules on c's shape, as a checkpoint's model names them.
embed_tokens, layers, layer i a layer(c, i), and the
final norm.
Source code in iron/lm/causal.py
CausalLM
¶
CausalLM(config: DecoderConfig, weights)
Bases: Model
A decoder on config's shape: model, lm_head and rotary_emb.
model is a decoder_class of layer_class layers each holding
its own KVCache; rotary_emb is the RoPE table, None with no
rope_theta.
Source code in iron/lm/causal.py
recurrent
property
¶
recurrent: list[Checkpoints | KVCache]
Each state a layer carries from position to position, which restarts only at some (restart).
Checkpoints, and a KVCache keeping a ring.
device_loop
property
¶
device_loop: bool
Whether generate can draw on the device: a full-ELF decode step.
draws_at_once
property
¶
draws_at_once: bool
A device loop's: generate reads its draws once the last is drawn.
layer
¶
head
¶
lookup
¶
from_rows
¶
Run a prompt chunk's call on its embedding rows x, bounded to rows.
In place of the lookup of its ids; token is the decode
step's, unread here.
Source code in iron/lm/causal.py
forward
¶
Run the layers, the head and the draw over x, the rows call runs.
Each memory module reads call.
Source code in iron/lm/causal.py
draw
¶
draw(logits, call: Call)
Return what a call returns: logits and the token drawn from them.
logits is head's output; the token is drawn from it on the
device, carried with the next position.
Source code in iron/lm/causal.py
rotary
¶
rotary(config: DecoderConfig) -> Rope
rope_row
¶
Return the RoPE table row a chunk from begin reads first.
Of an n-token prompt (Rope.table_start).
Source code in iron/lm/causal.py
tune_arguments
classmethod
¶
Add the flags tune_calls follows from (tune.arguments).
tune_calls
classmethod
¶
tune_calls(parser, args) -> list
tune.calls on args, built on weights it never reads.
Source code in iron/lm/causal.py
shapes
¶
Return the input shapes of each version.
The decode step's (none), then the prompt chunk's token ids.
use
¶
use(decode: CompiledGraph, prompt: CompiledGraph, placed: CompiledGraph | None = None, placeholders: tuple[int, ...] = ()) -> None
Run logits and generate on these versions, in one arena.
The versions are this model's own, or those of a graph recording it
in their bodies (a multimodal model's); each prompt version feeds
decode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
decode
|
CompiledGraph
|
The decode step. |
required |
prompt
|
CompiledGraph
|
A prompt chunk, on |
required |
placed
|
CompiledGraph | None
|
A prompt chunk holding any of |
None
|
placeholders
|
tuple[int, ...]
|
The placeholder tokens, in |
()
|
Source code in iron/lm/causal.py
call_values
¶
call_values() -> dict
Return the per-call values every call gives beyond the standard ones.
The standard ones are the token, position, start, rows and rope_start; none beyond them by default.
release
¶
loop
¶
loop(version: CompiledGraph) -> CarriedLoop
Return the device loop of decode steps version starts, made once.
Its runs are bound to the versions, which every session shares.
Source code in iron/lm/causal.py
logits
¶
logits(tokens) -> ndarray
Return the logits after the last of tokens, the whole history.
Run in session (Session.logits).
encoder
¶
An encoder on the NPU as one graph: a sequence of at most max_tokens rows in.
Attended bidirectionally, compiled once for every length.
Encoder looks up token ids and runs forward over them; a Tower
is an encoder whose rows come from a front end and whose output is soft
tokens in a text model's space.
Encoder
¶
Bases: Model
An encoder over max_tokens rows at most, one compile for every length.
layers, a layer_class(config, i) each, which a subclass loads
from its checkpoint; it gives lookup and head.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
Any
|
The model's shape: |
required |
Source code in iron/lm/encoder.py
load_layers
¶
Load layer i from tree, renamed by renames, at {layers}{i}..
Then loads final_norm, where there is one, at norm.
Source code in iron/lm/encoder.py
lookup
¶
angles
¶
angles(rows) -> dict | None
Return the RoPE angles of the first rows positions, by the rope each layer names.
Call.ropes; None: no RoPE.
positions
¶
positions(x, call: Call)
Return x, the rows call runs, with the model's absolute positions placed.
Unchanged by default.
layer
¶
pool
¶
forward
¶
forward(x: Handle, rows, ropes, ids=None)
Run the layers, the final norm, pool and head over the rows x.
Of ids (None for a tower's), bounded to their first rows,
rotated by ropes (Call.ropes).
Source code in iron/lm/encoder.py
from_rows
¶
Run a call on its embedding rows x, bounded to rows.
In place of the lookup of its ids.
shapes
¶
soft
¶
inputs
¶
Return a call's ids, tokens padded to max_tokens, and its per-call values.
Raises:
| Type | Description |
|---|---|
ValueError
|
|
IndexError
|
A token is outside |
Source code in iron/lm/encoder.py
Tower
¶
Bases: Encoder
An encoder whose rows come from a front end rather than token ids.
Its output is soft tokens in a text model's space, text_dim wide,
which take a placeholder's places.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
soft_rows
|
int
|
The rows of soft tokens a call returns, the first
|
required |
Source code in iron/lm/encoder.py
inputs
¶
Return a call's input buffer and per-call values for raw.
calls
¶
Return each call raw takes, in turn, its input buffer and per-call values.
Each call's soft tokens after the last's: one, inputs's, by
default.
Source code in iron/lm/encoder.py
soft_tokens
¶
soft_tokens(*raw) -> ndarray
Return the soft tokens of raw, as calls takes it, (soft, text_dim) float32.
Source code in iron/lm/encoder.py
multimodal
¶
A text model whose placeholder tokens take its towers' soft tokens.
As Hugging Face's masked_scatter places them: the j-th of a token's
placeholders takes its tower's j-th soft token.
Multimodal
¶
Bases: Model
A text model and its towers, keyed by placeholder token, as one graph.
A tower writes its soft tokens into merged, and placed scatters
them into the text model's rows by placeholder position.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
towers
|
dict[int, Tower]
|
Each tower, by the token its placeholders are. |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
Two inputs or values share a name, or a tower takes other than one input. |
Source code in iron/lm/multimodal.py
shapes
¶
Return the input shapes of each version.
The text model's, first so its decode step leads (Model.shapes),
each tower's, and the ids with their placeholders (and a causal
model's counts).
Source code in iron/lm/multimodal.py
release
¶
loaded
¶
loaded(versions: list) -> None
Hand a causal text model its versions (use).
Source code in iron/lm/multimodal.py
longest
¶
longest() -> dict
Return the per-call values of the longest call.
The text model's and each tower's.
Source code in iron/lm/multimodal.py
tower_calls
¶
Each tower version raw runs, in turn.
Its inputs and every tower's per-call values, zero for a tower not running.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tokens
|
Sequence[int] | ndarray
|
The token ids, each placeholder run as long as its tower's soft tokens. |
required |
raw
|
dict[int, tuple]
|
Each tower's raw input, as its |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
A placeholder's count is not its tower's soft tokens. |
Source code in iron/lm/multimodal.py
calls
¶
Build an encoder text model's call: each version it runs, in turn.
Its inputs and per-call values (tower_calls).
Raises:
| Type | Description |
|---|---|
TypeError
|
The text model is causal: |
Source code in iron/lm/multimodal.py
run
¶
Return an encoder text model's output for tokens.
raw at their placeholders (as calls takes them), each
version run in turn.
Source code in iron/lm/multimodal.py
place
¶
Run each tower raw names.
So a causal text model's prompt chunks find its soft tokens in
merged, unless each ran on the same last time, else forgetting
its caches from the first stale placeholder.
Source code in iron/lm/multimodal.py
logits
¶
Return a causal text model's logits after tokens.
raw at their placeholders (CausalLM.logits).
Source code in iron/lm/multimodal.py
model
¶
Model, a model's graph, every version compiled and loaded before the first call.
Built from a checkpoint's files (load), which generates from a prompt;
OnHost is the model with no device; and Tokenizer is a prompt's text
to its ids.
Tokenizer
¶
Tokenizer(encoding: TokenizerJson, chat_template: str | None = None, bos_token: str = '')
Text to a prompt's token ids, as transformers' tokenizer begins them, and back.
Over encoding, a TokenizerJson. chat_template is the
checkpoint's own.
Source code in iron/lm/model.py
chat
¶
Return the ids of messages, each {"role": ..., "content": ...}.
As chat_template renders them (jinja2, the models extra), the
assistant's turn opened after them.
Raises:
| Type | Description |
|---|---|
ValueError
|
The checkpoint has no chat template. |
ImportError
|
jinja2 is not installed. |
TemplateError
|
The template refuses |
Source code in iron/lm/model.py
TokenizerJson
¶
A Hugging Face tokenizer.json, over tokenizers (not transformers).
Text to token ids, and back.
Source code in iron/lm/model.py
encode
¶
prompt
¶
encode(text) after the special tokens the post-processor puts before a text.
A BOS, or none (Qwen's), as transformers' tokenizer(text) begins.
Any it puts after a text are left off.
Source code in iron/lm/model.py
Stats
dataclass
¶
A generation's timings.
The seconds to the first token, the tokens per second drawn after it
(NaN for one token), and tokens, how many were drawn: those past
the first eos too where they were drawn anyway.
Model
¶
Bases: Graph
A model compiled as several versions of one graph, one per input shape.
load builds it from a checkpoint's files, or from random weights at a
seed (random_weights) for checks without real weights.
Source code in iron/lm/model.py
hugging_face
property
¶
hugging_face: type
What loads the checkpoint into transformers to record its goldens.
A subclass needing another import (torch, transformers) only to record overrides this as a property of its own.
draws_at_once
property
¶
draws_at_once: bool
Whether, after a non-EOS first token, generate draws the rest before the second arrives.
Even those past eos cost no more to read.
eos
cached
property
¶
The tokens generation stops at.
The checkpoint's eos_token_id, one or a list, from its
generation_config.json or else its config.json; none without
a checkpoint or either.
load
classmethod
¶
load(path, *, random_weights: int | None = None, max_seq_len: int | None = None, full_elf: bool | None = None) -> Self
from_checkpoint's model compiled and loaded, weights uploaded.
full_elf is every version's packaging (load_versions).
Source code in iron/lm/model.py
from_checkpoint
classmethod
¶
from_checkpoint(path: str | Path, *, random_weights: int | None = None, max_seq_len: int | None = None) -> Self
Return the model of a Hugging Face repository's files, not yet loaded.
Its context is max_seq_len or else fitted to this host (fit).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
A directory holding |
required |
random_weights
|
int | None
|
A seed: weights (and no tokenizer) drawn at it in the checkpoint's shapes instead of read. |
None
|
max_seq_len
|
int | None
|
The longest context served, at most the checkpoint's own. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/model.py
default_config
classmethod
¶
default_config(weights_path) -> Config
cls.config itself, if it is already one shape.
Else that class's own, built from weights_path's checkpoint
directory (Config.from_checkpoint).
Source code in iron/lm/model.py
fit
classmethod
¶
config at its native context where the model's arena fits budget.
host_budget() by default, else at the longest whole number of
prompt chunks that does, logged either way.
Source code in iron/lm/model.py
cpu_reference
¶
Return what the model is judged by.
With drawn weights, its own graph on the host (OnHost); else
hugging_face's logits recorded beside the checkpoint
(golden.npz), keyed by config.json and
Checkpoint.fingerprint.
Source code in iron/lm/model.py
prompt
¶
chars characters of prompt.txt from skip on, tokenized.
With no tokenizer, chars // 3 tokens drawn at the weights' seed
and skip (roughly what the text would encode to).
Source code in iron/lm/model.py
generate
¶
generate(prompt: str | list[dict] | list[int], max_tokens: int, sampler: Sampler | None = None, **inputs: Any)
stream's pieces joined.
The text drawn after a text prompt or chat messages, else the token ids drawn.
Source code in iron/lm/model.py
stream
¶
stream(prompt: str | list[dict] | list[int], max_tokens: int, sampler: Sampler | None = None, **inputs: Any) -> Iterator
Draw up to max_tokens after prompt.
Each yielded as it is drawn, through the first of eos, and its
timings into stats once the last is drawn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str | list[dict] | list[int]
|
Text or a list of chat messages (rendered by the
checkpoint's chat template, |
required |
max_tokens
|
int
|
The most tokens to draw. |
required |
sampler
|
Sampler | None
|
How each is drawn; None is greedy. |
None
|
inputs
|
Any
|
What the model takes beside the tokens
( |
{}
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
Text or messages, and no tokenizer. |
Source code in iron/lm/model.py
shapes
¶
Return the input shapes of each version, the decode step (no input) first.
A later version on token ids (ids or placed) feeds off it
where it is a full ELF, its Emit starting there.
Source code in iron/lm/model.py
load_versions
¶
load_versions(*, release: Callable[[ndarray], None] | None = None, full_elf: bool | None = None, dev: Device | None = None) -> Self
Compile every version, then load each, in one arena, and hand them to loaded.
Every version is placed before the arena's buffers are made, so none grows them, which would copy them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
release
|
Callable[[ndarray], None] | None
|
Given each piece of each weight once it is on the device. |
None
|
full_elf
|
bool | None
|
Each version's packaging ( |
None
|
dev
|
Device | None
|
The device every version compiles for, else the current one. |
None
|
Source code in iron/lm/model.py
OnHost
¶
A model with its versions stood in by its graph's reference (Graph.reference).
The model with no NPU, mixed in before the model's own class; it
compiles nothing, and generate draws on the host.
host_budget
¶
host_budget() -> int
Return the bytes a model's arena may use on this host.
Physical memory minus HOST_RESERVE, or the cgroup's limit minus
CGROUP_RESERVE when a cgroup caps this process, whichever is smaller.
Source code in iron/lm/model.py
prompt_text
¶
chars characters of prompt.txt, from skip on, repeated past its end.
The text every model's long inputs are drawn from.
Source code in iron/lm/model.py
session
¶
One conversation over a CausalLM's loaded versions.
The tokens its caches hold, and the calls that run a history's new tokens and draw after it. Several sessions share one compiled model; its caches hold one session's tokens at a time, so a session whose tokens another's calls replaced runs its history again from the first token.
Session
¶
A conversation over the versions of lm, a CausalLM (CausalLM.use).
Source code in iron/lm/session.py
logits
¶
logits(tokens) -> ndarray
Return the logits after the last of tokens, the whole history.
One more token than the last call's is a decode step, anything else
runs from the first token the caches do not hold (resume).
Raises:
| Type | Description |
|---|---|
ValueError
|
|
IndexError
|
A token is outside |
Source code in iron/lm/session.py
draw
¶
Draw max_tokens after tokens on the device, each yielded as it is drawn.
As Model.stream does: looped there where the decode step is a
full ELF (device_loop), else each step started from the host.
Raises:
| Type | Description |
|---|---|
ValueError
|
The tokens and those to draw do not fit the caches. |
IndexError
|
A token is outside |
RuntimeError
|
Neither version is loaded: |
Source code in iron/lm/session.py
forget
¶
forget(position: int) -> None
Hold nothing the caches have from position on.
The next call runs from there or an earlier restart boundary, as a change before it (a placeholder's soft tokens) would leave them stale.
Source code in iron/lm/session.py
resume
¶
Return the position calls start from to run the tokens from held on.
held itself, or for a state that cannot restart there
(KVCache.restart), the earliest one of them can.
Source code in iron/lm/session.py
prompt_ids
¶
prompt_ids(tokens) -> ndarray
Return a prompt call's ids for a chunk of tokens, padded to prefill_chunk.
Padded with row 0, which the bound by rows leaves unread.
Source code in iron/lm/session.py
host_draws
¶
Yield max_tokens drawn after the prompt chunks.
The host starts each decode step on the token the last one drew, which it reads alone.
Source code in iron/lm/session.py
device_draws
¶
Yield max_tokens drawn after the prompt chunks of n tokens.
The last chunk starts a device loop of decode steps (CausalLM.loop);
with each, each token is read once its step completes.
Source code in iron/lm/session.py
generation
¶
Sampling, and the loops that generate and check, over any model.
A model is anything with logits(tokens): the logits after the last of
tokens, the whole history so far, as (vocab_size,). A
CausalLM on the NPU is one, and so is a CPU reference.
Sampler
¶
Temperature, then top-k, then a draw from the softmax, exactly as the device draws.
aie.iron.kernels.sample.sample_ref, so the host and device pick the
same token from the same logits and uniform.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
top_k
|
int | None
|
None keeps the whole row; only valid on the host, since the device's top-k is bounded. |
50
|
Source code in iron/lm/generation.py
probabilities
¶
Return the distribution a token is drawn from, float64, over logits (1-D).
Source code in iron/lm/generation.py
rows
¶
count draws for the device, (count, ROW_WORDS) int32, one per position.
Taking the uniforms count host draws would, in order.
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/generation.py
greedy
¶
generate_on_host
¶
Return the num_tokens drawn after tokens, each from model's logits.
Source code in iron/lm/generation.py
divergence
¶
KL(reference || model) of two rows of logits, and whether both rank the same token first.
Source code in iron/lm/generation.py
accuracy
¶
model's next-token distributions against reference's, over num_tokens steps from tokens.
Teacher-forced on the reference's greedy token so a divergence is the model's own error, not an earlier different choice's consequence.
Source code in iron/lm/generation.py
kl_stats
¶
Return the mean, p90 and max KL of accuracy's results, over every step, prefill and decode alike.
One step's KL depends as much on how confident the reference is there as on the model.
Source code in iron/lm/generation.py
greedy_logits
¶
greedy_logits(model, tokens, num_tokens) -> ndarray
Return the logits of num_tokens greedy steps from tokens, one row each.
The first the prompt's.
Source code in iron/lm/generation.py
differing_steps
¶
Return the steps at which two greedy_logits runs differ bitwise, NaNs and signed zeros included.
Source code in iron/lm/generation.py
determinism
¶
determinism(model, prompts, num_tokens, rounds) -> int
How many runs' logits differ bitwise from the first of their prompt, over rounds repeats of each prompt.
Alternating so a host write that never reaches the device reads the other prompt's data, not a leftover copy of its own.
Source code in iron/lm/generation.py
golden
¶
What a model is judged by.
Hugging Face transformers' own model in float32 (HuggingFace), its
logits recorded once to a file (Golden) so a test or an accuracy
run needs neither torch nor transformers, which are not IRON's
requirements, only to record.
Golden
¶
Golden(path: str | Path, reference: Callable[[], Any] | None = None, inputs: tuple[Any, ...] = (), *, record: bool | None = None)
Logits by token sequence and any arrays beside it (a clip, an image).
Each is the logits after a sequence's last token, read from
path (an .npz).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
The file; it need not exist yet. |
required |
reference
|
Callable[[], Any] | None
|
Builds what records a sequence |
None
|
inputs
|
tuple[Any, ...]
|
What the reference is built from (a config, weights); the
file holds their |
()
|
record
|
bool | None
|
Whether to record what |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/golden.py
read
¶
read() -> dict
Return the file's rows; none if it is stale and this records.
Source code in iron/lm/golden.py
logits
¶
logits(tokens, *arrays) -> ndarray
Return the logits after tokens given arrays.
The reference's logits takes arrays after tokens.
Source code in iron/lm/golden.py
write
¶
Write the rows to the file, replaced whole so no reader sees half.
Source code in iron/lm/golden.py
HuggingFace
¶
transformers' own model in float32, of the auto class model_class names.
A causal model's by default: a checkpoint directory's (load) or
one built from an IRON config and weights (build).
build
classmethod
¶
build(model_type: str, config, weights, **json) -> HuggingFace
Build model_type's model on config's fields as its config.json names them.
json's merged in, over weights written to a checkpoint
and loaded, so transformers renames (and stacks) them as it
loads one.
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/lm/golden.py
logits
¶
Return the logits after the last of tokens, float32.
A subclass whose model takes more than token ids (an image, a clip)
reads them from arrays.
Source code in iron/lm/golden.py
fingerprint
¶
fingerprint(*values) -> str
Return a digest of values, a weights tree's leaves by name (leaves).
Arrays by their bytes, shapes and types, anything else by repr.
Source code in iron/lm/golden.py
flat
¶
tree's arrays by their dotted names, in float32.
testing
¶
What a model's device test checks: speed, accuracy against the CPU reference, and determinism.
Each over a model Model.load loaded, its figures through the test's
record_property; and where it finds the model's files.
Download
dataclass
¶
A file a test reads that the repository does not hold, fetched from url and pinned by its sha256.
fetch
¶
fetch() -> Path
Return the file in cache_dir(), by digest.
Fetched unless a file of the pinned content is already there.
Raises:
| Type | Description |
|---|---|
ValueError
|
|
RuntimeError
|
What |
Source code in iron/lm/testing.py
weights_dir
¶
Where the tests find a model's files: $IRON_EXAMPLE_WEIGHTS_DIR/<name>.
cache_dir
¶
cache_dir() -> Path
Where a Download is cached.
IRON's own writable cache (NPU_CACHE_HOME), not the weights
directory, which CI mounts read-only.
require
¶
Skip the test unless every one of files exists, except in CI where they are staged.
There a missing file fails it, naming what the runner must hold.
Source code in iron/lm/testing.py
load_from_flags
¶
load_from_flags(request, model, weights: Path, tokenizer='tokenizer.json', staged=True)
model.load of weights' directory, to call with any further options.
As the CLI loads it at pytest's --random-weights and
--max-seq-len; requires the files it needs.
Source code in iron/lm/testing.py
prompt
¶
Return a prompt of chars characters' tokens with room for num_tokens after it.
Source code in iron/lm/testing.py
check_generation
¶
Sample num_tokens on the device after a prompt of prompt_len characters.
CausalLM.generate, the whole prompt prefilled; record the time to
the first token and the tokens per second after it.
Source code in iron/lm/testing.py
check_prompt_time
¶
Record the median seconds to the first token after a prompt of length tokens.
Over runs timed runs, each a fresh prompt (random ids, since a
prompt's time does not follow its ids).
Source code in iron/lm/testing.py
check_device_sampling
¶
Sample num_tokens on the device (CausalLM.generate) after a prompt of prompt_len characters.
Then sample again on the host from each step's logits at the same seed: the text must match, token for token.
Source code in iron/lm/testing.py
assert_close
¶
got within share of want, the reference's logits, at their largest magnitude.
And, where the reference's top two differ by more than that bound, the same argmax.
Source code in iron/lm/testing.py
check_accuracy
¶
KL(reference || model) of the next-token distribution, teacher-forced over num_tokens steps.
From a chars-character prompt, within bounds
({"Mean": .., "P90": .., "Max": ..}).
Source code in iron/lm/testing.py
check_embedding
¶
check_embedding(got: ndarray, want: ndarray, min_cosine: float, max_error: float, *, percentile: float = 1, record: Callable[[str, float], object])
got's rows against want's embeddings.
The token cosines' low percentile (one bad token should not fail every other) and the relative error of the whole array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
got
|
ndarray
|
The array judged. |
required |
want
|
ndarray
|
The reference array |
required |
min_cosine
|
float
|
The percentile cosine must reach. |
required |
max_error
|
float
|
|
required |
percentile
|
float
|
Which percentile of the per-row cosines is judged (1:
every row but the worst may miss |
1
|
record
|
Callable[[str, float], object]
|
A test's |
required |
Source code in iron/lm/testing.py
check_determinism
¶
Repeated runs produce bit-identical logits, over two prompts of chars characters.
Alternated so a host write that never reaches the device reads the other prompt's data, failing every run rather than some.
Source code in iron/lm/testing.py
check_chat_turn
¶
Extend a prompt by a turn, as a chat's next message extends it.
The model runs from the turn's first token (CausalLM.resume), and
its logits match bit for bit a fresh run of the whole prompt from scratch.
Source code in iron/lm/testing.py
check_deep_decode
¶
Check a decode step at position, deep in the caches, against the graph's own reference.
Graph.reference, for a context transformers would take too long
over, within bound of its KL.
Source code in iron/lm/testing.py
check_deep_prompt
¶
Check the deepest prompt chunk (holding position max_seq_len - 1), at most rows rows of it, against the graph's own reference.
Within bound of its KL, every earlier position filled as
check_deep_decode fills them.
Source code in iron/lm/testing.py
cli
¶
main, a model's command line.
exports
¶
exports(package: str, names)
Build a model package's __getattr__, each of names from its model module.
Imported on first use, so python -m <package>.model runs that
module once.
Source code in iron/lm/cli.py
sampling_arguments
¶
sampling_arguments(parser: ArgumentParser) -> None
--temperature and --top-k, a Sampler's, at its defaults.
Source code in iron/lm/cli.py
parse_args
¶
parse_args(parser: ArgumentParser, args=None) -> Namespace
parser's arguments with -v, which logs iron's INFO lines.
Such as each compile's.
Source code in iron/lm/cli.py
main
¶
Run the command line of model_class, loaded as Model.load loads it.
Sample, or check it.
Source code in iron/lm/cli.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | |