Tiny Aya (Cohere2)¶
CircuitKIT ships a TransformerLens compatibility port that teaches
TransformerLens 3.8 how to load Cohere's tiny-aya checkpoints
(model_type="cohere2") so the discovery pipeline can run on them. Upstream
TransformerLens 3.8 does not know about cohere2; the port adds it
additively, without forking or pinning a different TL version.
Now a multi-architecture seam
Tiny-aya was the first model this port supported, and the seam it
established (circuitkit.backends._tl_compat) has since grown to also
cover Command R7B (same cohere2 architecture as tiny-aya),
Aya Expanse 8B (cohere1), and SmolLM3-3B (a new Llama-family
port) — all discovery/evaluation/intervention-capable through the same
seam. See Experimental Models for those three.
Experimental & gated
tiny-aya is an experimental discovery target. The checkpoints are
gated — you need an HF_TOKEN with access to the CohereLabs/tiny-aya-*
repositories — and results should only be trusted once the real-weight
parity gate passes on your machine (see Tests).
Supported checkpoints¶
The port registers five tiny-aya repositories as known TransformerLens models:
CohereLabs/tiny-aya-baseCohereLabs/tiny-aya-globalCohereLabs/tiny-aya-earthCohereLabs/tiny-aya-fireCohereLabs/tiny-aya-water
The *-GGUF variants are not registered: GGUF is llama.cpp's quantized
single-file format, which AutoModelForCausalLM.from_pretrained (what
TransformerLens uses internally) cannot load.
Architecture at a glance¶
| Property | Value |
|---|---|
| Layers | 36 |
d_model |
2048 |
| Query / KV heads | 16 / 4 (GQA) |
d_head |
128 |
d_mlp |
11008 (gated SiLU) |
| Vocab | 262144 |
| Positional encoding | GPT-J–style interleaved RoPE (rope_theta=50000, full-head) |
| Attention | per-layer sliding-window (4096) vs full attention; full-attention layers use NoPE |
| Block topology | parallel attention + MLP off one input_layernorm |
| Normalization | LayerNorm (eps=1e-5) |
logit_scale |
1.0 (fold is a no-op for tiny-aya) |
| Embeddings | tied (W_E == W_U) |
| Tokenizer | CohereTokenizer (BOS id 2, add_bos_token=true, add_eos_token=false) |
The _tl_compat seam¶
All of the port lives in circuitkit.backends._tl_compat:
_tl_compat/__init__.py— an idempotentapply_patches()guarded to TransformerLens 3.8 (with animportlib.metadatafallback for installs wheretransformer_lens.__version__is empty)._tl_compat/cohere.py— the port itself: registers the repo IDs, wrapsconvert_hf_model_configandget_pretrained_state_dict, and monkeypatchesAbstractAttention.apply_rotaryfor NoPE.
The patch is applied at import time from circuitkit/backends/__init__.py:
Every discovery backend imports from circuitkit.backends before touching
TransformerLens, so the patch is always live before any
HookedTransformer.from_pretrained() call.
Config mapping (convert_hf_model_config)¶
Each mapping below is traceable to HuggingFace's transformers.models.cohere2
source, not assumption:
| HF / spec | TL HookedTransformerConfig |
|---|---|
rope_gptj (interleaved pairs) |
rotary_adjacent_pairs=True |
rotary_pct=1.0 |
rotary_dim = d_head (full head rotated) |
rope_theta=50000 |
rotary_base=50000 |
sliding_window=4096 + layer_types |
use_local_attn=True, window_size=4096, attn_types (full_attention→global, else local) |
parallel block, single input_layernorm |
parallel_attn_mlp=True |
num_key_value_heads |
n_key_value_heads (GQA) |
| LayerNorm | normalization_type="LN" |
Weight conversion (get_pretrained_state_dict)¶
- GQA: K/V are stored in TL's compact
_W_K/_W_Vslots, sliced byn_key_value_heads; TL'sGroupedQueryAttentionungroups them at runtime. - Parallel block: Cohere2's single
input_layernormfeeds both branches, so it is duplicated into TL'sln1andln2(tied) — the trick TL already uses for GPT-J. - Tied embeddings: HF's own
tie_weights()already madelm_head.weight == embed_tokens.weight, soW_E/W_Uare read from their own tensors. logit_scale: HF applieslogits = lm_head(hidden) * logit_scale; sincelm_headhas no bias this is a pure linear fold intoW_U(a no-op atlogit_scale=1.0).
NoPE on full-attention layers¶
Cohere2's Cohere2Attention.forward only rotates q/k when
self.sliding_window is not None — the periodic full-attention layers get
no positional embedding at all. The port reproduces this by patching
AbstractAttention.apply_rotary to return its input unchanged for cohere2
global layers, while local layers rotate normally.
Running discovery¶
import circuitkit as ck
model = ck.load_model("CohereLabs/tiny-aya-base") # loads via the TL port
circuit = ck.discover(model, task="greater_than", algorithm="eap-ig")
Task caveat — single-token answers¶
The single-answer-position discovery metrics (EAP/EAP-IG and the IOI / greater-than / SVA logit-difference metrics) assume each answer is one token. On Cohere's 262k-token vocabulary a string that is a single token on GPT-2's 50k BPE can split. CircuitKIT guards this:
greater_thanbuilds its own single-token operand pool against the loaded tokenizer, so it is always valid — the recommended smoke task.ioifails fast (or warns) if the tokenizer splits its name pool; if that fires, prefergreater_thanor a custom single-token task.
See the tokenization guard for details.
Tests¶
Offline unit tests (no gated weights, no network, no GPU) run in ordinary CI:
tests/backends/tl_compat/test_cohere.py— registration, config/weight conversion, NoPE patch, tiny random-init end-to-end.tests/backends/tl_compat/test_cohere_gqa.py— runtime GQA / parallel-block gate on a tiny random-init model.tests/tasks/test_cohere_tokenization.py— the single-token answer guard.
Real-weight gates are opt-in (they download a 3.35B gated checkpoint):
CIRCUITKIT_RUN_TINY_AYA=1 HF_TOKEN=... \
python -m pytest tests/backends/tl_compat/test_cohere_parity.py \
tests/regression/test_cohere_discovery.py -v
test_cohere_parity.py— the gate: TL-vs-HF next-token KL< 1e-4on short prompts and past the 4096-token sliding-window boundary. This is the only test that numerically catches a wrong RoPE interleaving, LayerNorm fold, embedding tie, orlogit_scalefold.test_cohere_discovery.py— end-to-enddiscover_circuitongreater_thanfor EAP, EAP-IG, CD-T, and IBCircuit; asserts finite, non-degenerate scores.
LayerNorm-fold fallback
If the parity gate fails only slightly (LayerNorm-fold accumulation on a
3.35B model), load the TL model with from_pretrained_no_processing /
fold_ln=False and re-compare before suspecting the port.
See also¶
- Experimental Models — Command R7B, Aya Expanse
8B, and SmolLM3-3B, added through the same
_tl_compatseam. - Architecture Registry — the
coherefamily entry andEXPERIMENTAL_FAMILIES. circuitkit.backends._tl_compat— the port source.