Modern stack
0.5539bits per byte against 0.5877 for the baseline, three seeds each
octlm is a nine-day lab in PyTorch. It writes the tokenizer and decoder from scratch, trains a 26M model on TinyStories, loads Qwen3-0.6B into the same code, and teaches it to fix bugs with a LoRA written by hand. Every number on this page comes from a logged run.
Each experiment opens with a problem, a hypothesis, a baseline, a measurement and a stop condition. It closes with keep or revert. Failed ideas stay in the notes with their numbers. The code leans on the standard library and PyTorch, and on nothing it can avoid.
Three seeds set the noise floor. A result counts only when its gap beats the spread.
Papers, model reports and official code, each named in the day note next to the decision it caused.
BPE, attention, RoPE, KV cache, int8, LoRA, a safetensors reader and the agent harness all live in octlm.
Each day has one note, a set of experiments and an exit check. Hover a day to see what it built and the number that closed it.
Rank-16 adapters on all seven projections, trained on 74 scripted traces for about five minutes. The merged int8 model keeps the pass rate and halves the stored block weights.
Read 4 postsOne decoder, trained once and trained properly. 24,000 steps of 32 sequences of 512 tokens, float16 with a gradient scaler, cosine decay to a tenth of the peak rate. Validation loss is the same 131,072 held-out tokens at every checkpoint.
The Day 4 model writing a story, recorded with the real demo command. Each token is colored by the probability the model gave it.
Once upon a time, there was a little girl named Lily.
Three seeds of each stack on 98.3M tokens set the floor. The six dots below are those runs. Anything smaller than the spread inside a group is not a result.
bits per byte against 0.5877 for the baseline, three seeds each
bits per byte on code at matched parameters
KV cache at 4K context, quality within noise
bits per byte worse on code than pre-norm
bits per byte, against a 0.0050 noise threshold
model bytes for a 0.000025 bits-per-byte change, 20% slower on a T4
as naive decoding at temperature 0, at 64, 256 and 512 tokens
pass^1 unchanged after merging the LoRA and quantizing
Largest float32 logit difference between octlm and Hugging Face Transformers, Qwen3-0.6B on a T4. Tokenizer, chat template and greedy tokens match exactly.
┌──────────┬────────────────────┬─────────────────────────┐ │ u64 n │ JSON header (n B) │ raw tensor bytes ··· │ └──────────┴────────────────────┴─────────────────────────┘ struct json.loads memoryview → torch
The weights load through struct and json from the standard library. No safetensors package.
The harness shows the model a failing repository and five tools. It parses each call, runs it in a sandbox and feeds the result back until the model answers or hits eight turns. Both recordings run the real eval loop on a laptop CPU.
task ──▶ model ──▶ <tool_call> ──▶ parse + validate ──▶ sandbox
▲ │
└──────────────── tool result ◀─────────────────┘
tools list_files · read_file · grep · write_file · run_testsOne CPU run is one sample. It shows the result, it does not measure it. The measurement is the grid below.
SFT taught the tool format but not debugging. Day 9 samples six rollouts per task, scores each with the task check, and pushes up the ones that beat their group. Groups where every rollout agrees carry no gradient and get dropped. On the T4, 13 of 16 groups agreed, so the run stopped before its first update. Click the rollouts to see why a uniform group teaches nothing.
Python 3.14, uv and the CPU build of PyTorch. The demos need their weights in artifacts/; the README lists which files.
# Python 3.14 and uv$git clone https://github.com/whynotramaa/octlm.git$cd octlm && uv sync$uv run python -m unittest # the test suite, on CPU
$uv run python -m octlm.demo agent # Qwen + LoRA on bug-low-stock$uv run python -m octlm.demo agent --base # same task, no adapter$uv run python -m octlm.demo agent --list # the 40 eval tasks$uv run python -m octlm.demo agent --task cart-clear
$uv run python -m octlm.train --config configs/day1.toml --dry-run # config hash, params, logits shape$uv run python -m octlm.tokenizer --vocab-sizes 512 1024 2048$uv run python -m octlm.train --tokenizer bpe --overfit # the memorization gate$python -m octlm.day4 train # EXP-067, on a T4
$python -m octlm.harness eval # 40 tasks × 3 seeds → JSONL$python -m octlm.day8 # traces, LoRA training, eval$python -m octlm.day9 probe # GRPO signal check
Each post explains one piece, what it measured, and what it chose not to build. Tests check the playground math against the Python code.