A plumbline is the weighted string a builder hangs before laying anything: it establishes what true is, so every later measurement has something to be taken against. This package does that job for Trellis-180M, a 180M-parameter code-reasoning language model. It declares the typed configuration tree that names every dimension the model uses, and it computes — closed-form, from those fields alone — the parameter, FLOP, memory, bandwidth, and training-cost accounting the architecture was published with.
It is the project's root unit. It consumes nothing, it gates dimensioning before any layer, kernel, or data code exists, and every other unit imports its dimensions from here rather than re-declaring them. There is no tensor library anywhere in it and no model is ever instantiated: the whole thing is integers, floats, and arithmetic. A configuration that violates a named rule never becomes a config object, and a report that disagreed with the 180.72M serving total would fail the run that produced it.
pip install -e . # or: pip install -e ".[test]"Zero runtime dependencies, Python 3.9 or newer.
budget.py report --preset default # both report forms, from one computation
budget.py check --preset default # recompute and compare against the published table
budget.py diff default proxy_50m # two configurations, side by side
budget.py list-presetspython -m trellis.config.budget is equivalent. As a library:
from trellis.config import get_preset, build_report, render_table
config = get_preset("default") # validated on the way out, or it raises
report = build_report(config)
print(render_table(report))
report.value("parameters.serving_total_m") # 180.72
report.failures # () -- nothing unexplainedEvery figure below is derived from configuration fields and checked against the
published value at that value's own precision, under round-half-up. budget.py check exits zero only when all of them still hold.
| Reproduced from config | |
|---|---|
| Parameter components | attention layer 6.09M · GDN layer 7.70M · prelude/coda 13.79M · tied core 44.58M · per-loop LoRA 2.06M · embeddings 37.75M · bank 68.68M |
| Totals | 180.72M serving · 186.72M training · embeddings 20.9% of budget · bank 38% |
| Decode FLOPs (MFLOPs/token) | 452 / 628 / 1232 average and 584 / 760 / 1364 worst case, at 4k / 32k / 131k |
| KV + state (MB) | 12.8 / 42.1 / 142.8 bf16 at 4k / 32k / 131k, falling to 17.0 / 42.1 at 32k / 131k with 4-bit global KV, against 352 / 2819 / 11274 for a dense MHA twin |
| Footprint | weights 361MB · bank 137MB, of which the 134MB value store is host-residable · ~420MB on device at 32k |
| Decode bandwidth | 245.2 core re-read + 75.5 head + 55.2 untied + cache ≈ 396MB/token, and ceilings of 4050 / 2278 / 152 tok/s |
| Prefill | 14.6 TFLOPs and 0.12s for 32k at K=2 on one A100 at 40% MFU |
| Training | 7.3e19 FLOPs · 7.3h ideal on 8×H100 at 35% MFU · 1.9GB/GPU activations |
The dense-160M comparator columns are computed by running the same calculators over a baseline configuration, never transcribed. If the FLOP model is wrong, both sides of the comparison move together.
Published figures are engineering bounds, not measured claims, and every emitted report says so.
Three published figures cannot be derived by the model that derives all the others exactly. Each is reported as a named reconciliation with the computed value treated as authoritative, rather than absorbed into a widened tolerance:
- dense-160M full attention at 131k computes to 11,594 MFLOPs against a
published
12,000+. The same arithmetic reproduces that baseline's 4k (672) and 32k (3,138) cells exactly, so the coefficient is pinned. - dense-160M SWA 5:1 at 131k computes to 2,492 MFLOPs against
~2,700. The 4,096-token window is fixed by the exactly-reproduced 32k cell, so no consistent window reaches the published figure. - the 4-bit KV column at 4k is published as 9.8MB, which requires quantisation below the stated 8,192-token threshold and contradicts the property that the quantised and full-precision columns agree below it. The threshold rule wins.
A further nine reconciliations annotate figures that do reproduce, where two accountings of the same quantity circulate and the report says which one a number belongs to — the three-block parameter split that sums 1.97M below the component total, the mixture's 56B seen tokens against a headline 60B, and the bank's per-read-point FLOP cost against its whole-bank presentation.
Building trellis.bank against these figures surfaced two more, both recorded in
docs/modules/memory-bank.md rather than smoothed
over. The published 137MB is the whole booked bank — all 68,681,728 parameters
at bf16 — while the value store alone is 134.2MB; the difference is the two read
points' projections, which belong in the compute path, so what is actually
host-residable is the smaller figure. And the value dimension is published as 512
"in all cases" while this repository's own sweep_25m preset sets 256; the module
reads it from config and hardcodes nothing.
trellis/config/
schema.py eleven frozen config groups; derived quantities are properties
validate.py 49 named cross-field rules, each with a field path
presets.py 13 configurations: default, proxy, sweep, twins, C-9 growth points
loader.py strict document loading; unknown fields and major versions rejected
errors.py the error taxonomy
budget/
params.py parameter accounting and the block-view reconciliation
flops.py decode and prefill FLOPs
memory.py KV, recurrent state, footprint, activations
bandwidth.py per-token bytes and device ceilings
training.py 6NT compute, mixture and stage accounting
baselines.py dense comparators, via the same calculators
goldens.py the published table -- read by the report, never by a calculator
report.py one computation, two deterministic renderings
__main__.py the report / check / diff entry point
trellis/structure/ the code-structure analysis core: the system's only tree-sitter
integration, and the declarative lexical-boundary tables the
tokenizer scans at encode time with no parser present
trellis/bank/ the product-key parametric memory bank: 131,072 value slots on a
512x256 composite key grid, exact top-32 selection, index-before-gather
select.py the exact top-k procedure and its brute-force oracle
store.py the shared value store, both codebooks, muP and warm-start init
read_point.py per-read-point W_q, query BatchNorm, W_up
stats.py windowed utilisation statistics and both regulariser terms
bank.py MemoryBank: select / gather / forward, and the reports
trellis/model/ the backbone: ten unique layers at effective depth 4 + 6K, a
weight-tied six-layer core entered K in [1,4] times, and the
parameter audit that reproduces 180.72M / 186.72M exactly
router.py the expert-choice capacity router; exact counts, nested, deterministic
lora.py per-loop adapters and norm gains, switched by tensor selection
loop.py the loop engine: the recurrence, the routing, the segmentation
heads.py the tied embedding/output head, the exit heads, the readout path
aux.py the four training-only heads, and their detachment
state.py the model-level state bundle: 22 GDN instances, 6.5 MB
backbone.py TrellisModel: forward / step, the graph, the bank read points
audit.py constructed tensors against the budget report, component by component
trellis/tokenizer/
vocab.py the 49,152-id partition and its frozen reserved blocks
specials.py 26 reserved protocol tokens, seven frames, both FIM layouts
pretokenize.py newline and indentation anchors; the parser-free segmenter
train.py the boundary-constrained BPE trainer and keyword-coverage pass
encode.py the four-stage encoder and the Tokenizer facade
decode.py batch decode and the incremental streaming decoder
artifact.py the versioned artifact: manifest, metadata table, mismatch detection
settings.py the one place the `tokenizer` config group is read and checked
variants.py the three A18 arms
bench.py bytes/token against the generic reference, throughput, latency
trellis/verify/ the deterministic verification pipeline: the sandbox, the
toolchain adapters, the cascade, the failure classifier, the budget
controller, and the verdict record
trellis/repoindex/ the M3 external repository index and its context-assembly layer:
the only memory tier that grows with repository size, on disk and
mmap-backed, and the system's single content-hash authority
codec.py the fixed-width record layouts and the closed-enum codes
container.py the mmap region container and the binary searches over it
strings.py the interned string table whose ids are sort ranks
extsort.py bounded-memory sorting with a deterministic merge
facts.py the one place Prompt 2's facts enter the index
resolve.py specifier-to-file and callee-to-symbol resolution
compact.py the four compaction passes; the only writer of the stores
stores.py the read side: one handle over the seven containers
build.py build and update, and the skip policy
query.py defs / uses / imports / validate_pointer
scorer.py BM25 plus graph proximity, and the declared embedding slot
assemble.py budgeted, dependency-ordered context assembly
frames.py repo-map, retrieval and pointer frame content
header.py the recorded header and the rebuild-required signal
metrics.py the A14 hooks: build time, query latency, index size
trellis/data/curation/
the offline data plane: licence filtering, exact and MinHash
near-dedup at corpus scale, quality and complexity filtering,
dual-path benchmark decontamination, and the nine versioned
slice manifests the training mixture is built from
normalize.py the one normalisation applied before hashing and matching
licensing.py SPDX resolution with a total evidence precedence; unresolved excludes
spill.py bounded-memory sorting with a fan-in-capped deterministic merge
dedup/minhash.py shingling, seeded permutations, signatures and banding
dedup/lsh.py the on-disk signature store, verified candidates, clustering
quality.py ten filters, each recording the measurement that decided
complexity.py structural signals from Prompt 2's facts; weighting, never a gate
decontaminate/ string keys, AST fingerprints, and the sort-merge join over both
slices.py the nine slots, exclusive assignment and token accounting
manifest.py the versioned contract, and the generations it refuses to mix
report.py the governance report: attrition, attribution, and measurements
pipeline.py the ordered, streaming, resumable driver
docs/ module, API contract, and architecture references
tools/ dump_tree.py, train_tokenizer.py, bench_repoindex.py
tests/ 2,215 tests, including byte-stable golden reports, id sequences,
fixed-seed model goldens in both train and step mode, and
planted duplicate and benchmark-leak corpora for the data plane
trellis.config computes; it does not build. Layer and kernel code, loss
computation, stage orchestration, runtime caches and quantisation, repository index
measurement, and the execution of ablation arms all belong to units that consume
this one's dimensions. Its I/O is limited to reading
configuration documents and writing reports.
trellis.structure accepts bytes and returns values: it holds no persistence and
walks no repository. It is the system's only tree-sitter integration, and every
consumer takes structural facts from it rather than parsing source itself.
trellis.bank is the storage tier and is self-contained: it imports trellis.config
at build time and no other unit in the package, so it instantiates without the model.
It is frozen at inference by design -- there is no write path other than gradients, and
the test suite asserts the absence of one by name. Read-point placement, prefetch
orchestration and loss weighting all belong to units that consume its interface.
trellis.repoindex is the only tier that grows with the repository, and it grows
on disk. It parses nothing -- exactly one parse per file happens in
trellis.structure and its facts are consumed here -- hashes nothing, and chunks
nothing; it holds the facts, the graph, the chunk index and the content-hash map,
and it answers. It owns the single hash-validity surface in the system,
validate_pointer, and it answers rather than repairs: a stale pointer comes back
stale with the caller's own fields untouched. Session frames, pointer compaction,
LRU and rehydration control flow belong to the harness that consumes it.
trellis.model is the graph and nothing below or above it. It composes
trellis.layers through the Layer/state contract and trellis.bank through the Bank
interface, and it owns no loss, no threshold, no decode-time cache layout and no
grammar mask. The sharpest case is the exit heads: the model reports a halting logit
for every token at every executed loop and never reads one back, because acting on it
is a serving policy and a serving policy inside the graph is one nobody can change.
trellis.tokenizer owns the vocabulary and the token protocol, and nothing else
may re-derive either. It reads and writes tokenizer artifacts and nothing more.
No parser runs in its encode or decode path, so the whole unit works with no
tree-sitter installed at all — CI runs its full suite in an environment with no
parser present, because a constraint checked only by grep is one that erodes.
No unit here opens a socket or shells out, and only trellis.layers,
trellis.bank and trellis.model import a tensor library or instantiate anything.
CI asserts every one of those boundaries rather than trusting them -- including that
the other three units' suites still pass in an environment with no torch installed at
all.
| Document | Contents |
|---|---|
| docs/modules/config-and-budget.md | design of the schema tree and the calculators; how the entry point is invoked |
| docs/api/config-schema.md | the normative contract: every field, type, unit, default, rule, and the report JSON schema |
| docs/modules/code-structure-core.md | design of the single-parse architecture, the extraction pipelines, and the boundary scanner |
| docs/api/structural-facts.md | the normative contract: every fact record, hash kind, and version constant |
| docs/modules/tokenizer.md | design of the four-stage pipeline, the constrained trainer, the artifact, and the A18 arms |
| docs/api/token-protocol.md | the normative contract: the id-space partition, every protocol token and frame, and the artifact manifest schema |
| docs/modules/memory-bank.md | design of the product-key addressing scheme, the exactness argument, the collapse countermeasures, and the invariants |
| docs/api/bank-interface.md | the normative contract: select / gather / forward, the statistic definitions, and the published figures |
| docs/modules/backbone.md | design of the ten-layer graph, the loop engine, the capacity router, per-loop specialisation, the heads, and the parameter audit |
| docs/api/model-interfaces.md | the normative contract: the training forward, the step-mode API, the router's masks, the bank read points' exposed indices, and the guarantee table |
| docs/modules/verifier-infrastructure.md | design of the six verification layers, the sandbox, the cascade, the classifier, and the budget controller |
| docs/api/verdict-schema.md | the normative contract: the verdict record, the failure taxonomy, the verifier tags, and the budget-controller API |
| docs/development/sandbox-and-toolchains.md | how to provision and pin each language toolchain, verify the isolation locally, and run the security and fuzz suites |
| docs/modules/repo-index.md | design of the five stores and the disk/mmap layout, incremental update and invalidation, retrieval scoring, assembly and frame formatting, and the invariants |
| docs/api/index-api.md | the normative contract: defs / uses / imports, retrieval, statement-aligned window assembly, validate_pointer, the frame formats, and the on-disk header fields |
| docs/modules/corpus-curation.md | design of the curation pipeline: stage order and the streaming/resumability model, the licence policy, the dedup algorithms and their parameters, the quality and complexity signals, dual-path decontamination, the nine-slice table, and the manifest invariants |
| docs/development/data-pipeline.md | how to configure and run a curation job, resume one after an interruption, read the governance report, and what each downstream stage reads from the artifacts |
| docs/architecture/budget-and-dimensions.md | the authoritative dimension vocabulary and the full budget tables |
docs/api/* is normative. The implementation is expected to match it exactly, and
for three of the contracts that expectation is a test: tests/test_tokenizer_docs.py
extracts every id, range, enumerated tuple and exported name from the token
protocol and compares it against the module, tests/test_bank_docs.py does the
same for the Bank interface's exported names, validation field paths, statistic
definitions, FLOP terms and byte figures, and tests/test_model_docs.py does the
same for the Model interfaces' exports, output fields, forward signature and audit
table -- and additionally checks that the three-part skipped-token policy is worded
identically in every document that states it.
python -m pytest tests/ -q
python -m ruff check .
python -m trellis.config.budget check --all-presetsThe bank's own gate is exactness: tests/test_bank_units.py compares its top-32
selection against a scan of all 131,072 composites over 1,000 random queries,
including planted ties. tests/test_bank_docs.py holds the module to its normative
contract.
Golden reports under tests/goldens/ are compared byte for byte. Regenerating
them is deliberate:
TRELLIS_UPDATE_GOLDENS=1 python -m pytest tests/test_regression.pyRead the resulting diff before committing it. A changed golden is either an intended dimension change or a regression, and the diff is how you tell.