A .aimodel can declare states — tensors the graph mutates in place — surfaced via a state=
kwarg (Python) / InferenceFunction.MutableViews (Swift). This is how KV / SSM caches persist
across decode steps without re-feeding the whole sequence.
Export a single graph where offset = position_ids.len − query_len:
query_len = S tokens, full position_ids [0..S), zero states.query_len = 1, full position_ids [0..past+1), persisted states.Trace with offset > 0 (position length > query length) so the query-length and position-length
dims stay independent. The cache tensors are static-allocated at context length (or grown 2×).
In-place writes use slice_update and require remove_functionalization(ep) before converting.
Standard models have 2 states (key/value). Newer architectures have more:
keyCache, valueCache (full-attn
layers) + convState, recState (Mamba/SSM linear layers). The full KV grows with seq; the
conv/rec states are fixed-shape.Apple’s Swift CoreAISequentialEngine hard-codes 2 states; drive these with a generic N-state
runner (see swift-runtime.md).
A sliding-window cache only needs the last W keys. Instead of a ctx-sized linear cache, use a
width-W ring buffer: write at pos % W, attend over the whole ring under a position-derived
mask. Since RoPE is baked into K at write time, slot order doesn’t change the scores — the mask
just marks which slots hold an already-written, causal, in-window position (built in-graph from
position_ids, so the bundle signature is unchanged).
aten.remainder (tensor) doesn’t lower → compute (Pmax−r) % W with a scalar symint + where.