Skip to content

Step one: describe the published model

The output of this step is a reference: authored HIR that computes what the published model computes. Everything after it is measured against this, so it is worth getting right before anything is made fast.

The code on this page is not an example written for the page. It is the shipped source of qwen3_5_35b_a3b, quoted by name — a hybrid model, because a dense one cannot show you the thing that catches people out: layers of two different kinds in a published cycle.

tilefoundry models qwen3_5_35b_a3b --source names the directory that holds it and lists the shipped files.

The five access faces

One Module, five things you can ask it for. Mixing them up is the most common first-day confusion, so they are listed together:

expression what you get
Mod.some_func(*all parameters) runs it; nothing is bound, so ConstTensor parameters are passed explicitly
Mod.some_child the child Module node
Mod.lookup("some_func") the ModuleFunction IR node
loaded.some_func(*activations only) runs it; ConstTensor parameters come from this loading's bindings
loaded.some_child the child LoadedModule

What entry means, how forward relates to __call__, and what a twin may and may not declare are stated once, normatively, in tilefoundry spec runtime 1.1. Read it there rather than here: a second copy is the one that goes stale.

The four things to get right

Each of these is a decision you make in the first ten minutes and pay for later. The comment on the biting line in the shipped source says what it costs.

1. A weight is a ConstTensor parameter

Weights are declared, not passed. A ConstTensor parameter is what puts a tensor in Module.weights, which is what load(resource) binds and what the runtime twin reads by name. Declare it as a plain Tensor and none of that happens: the Module owns no weights, load has nothing to bind, and every caller has to hand the tensor over positionally, forever.

{{fixture: qwen3_5_35b_a3b/model.py:Qwen3_5Router}}

2. The decode contract: one token, a prior cache, and this step's own K/V

A decode step is handed the cache before it and the token it is adding. Two declarations state that, and they are the whole contract:

{{fixture: qwen3_5_35b_a3b/model.py:S}}

{{fixture: qwen3_5_35b_a3b/model.py:C}}

ctx_len is the length of the prior cache and starts at 0 — a first step has nothing cached and attends the one position it brings itself. The exclusive upper bound is one past the longest prior cache the model admits, which for this model is config.max_ctx; read the field's own meaning before copying the form, because it differs between published models.

The step takes k_cache / v_cache and hands back the entry its caller appends. Two forms fit different callers; which one fits is a property of the caller, not a preference.

Form Fits when Cost
Caller-managed concat The cache grows each step, execution is eager, or ctx_len participates in types as a DimVar. Each step replaces the cache buffer, so one fixed-address graph cannot replay it.
cache_update The cache has fixed capacity, its write window advances each step, or it must be captured and replayed in a CUDA graph. Shape stays static, the write window is runtime data, and traffic falls back to the whole tensor when its bounds cannot be derived.

See spec hir § CacheUpdate for the operation's contract.

{{fixture: qwen3_5_35b_a3b/model.py:Qwen3_5FullAttention.full_attention}}

3. Dimensions come from the published config, not from arithmetic

head_dim is a published field. It is not hidden ÷ num_heads — for this model those differ, and a fixture that computed it would be wrong in a way every shape check would accept. The same holds for rotary_dim, which is partial_rotary_factor of the head, and for vocab.

{{fixture: qwen3_5_35b_a3b/model.py:_D}}

{{fixture: qwen3_5_35b_a3b/model.py:_ROT}}

4. The layer-type cycle is stated, not inferred

A hybrid model publishes which kind each layer is. The root reads that list and builds the stack from it, so the cycle is a fact of the model rather than something a reader of the fixture has to work out.

{{fixture: qwen3_5_35b_a3b/model.py:Qwen3_5_35B_A3B}}

When step one is finished

Prepare real weights first (runtime §1.1.2). Step one is finished when the authored Module agrees with the published implementation at production dimensions. That is a comparison, which is what step two's tool does — so the next page is the one that runs it, and you will use it here first.