Describe the bug
TransformerBridge.forward(..., stop_at_layer=N) (and anything built on it, e.g. input_to_embed()) silently does nothing on a Raven/Huginn bridge -- no error, no early stop. The forward pass runs to full completion as if stop_at_layer had never been passed.
Two independent gaps combine to cause this:
- Top-level wiring only knows about
self.blocks. In TransformerBridge.forward(), the stop_at_layer handling explicitly rejects a fixed list of known non-standard block-list names (L_blocks, H_blocks, encoder_blocks, decoder_blocks) with a clear NotImplementedError, then sets _stop_at_layer_idx on every block only if hasattr(self, "blocks"). RavenArchitectureAdapter.component_mapping has no "blocks" key at all -- Huginn's three block lists are prelude, core_block, and coda (matching its prelude / weight-tied recurrent core / coda structure). Raven isn't in the reject-list either, so the request silently falls through both checks: no error, and _stop_at_layer_idx never gets set on anything.
- Even if it were set,
OpaqueBlockBridge._check_stop_at_layer()'s layer-index regex wouldn't recognize it anyway. It only matches \.layers\.(\d+) or blocks\.(\d+); Raven's per-block names (transformer.core_block.0, transformer.prelude.0, transformer.coda.0) match neither. The standard BlockBridge class already has a more general _extract_layer_idx() ((?:^|\.)(?:blocks|h|layers)\.(\d+), covering GPT-2's h.N too) that OpaqueBlockBridge doesn't share or reuse -- and even that wouldn't match core_block/prelude/coda either way.
Surveyed every other architecture using OpaqueBlockBridge/SSMBlockBridge (mamba, mamba2, nemotron_h, rwkv7, zamba2): all name their list backbone.layers or model.layers, which matches fine. Raven is the only affected architecture currently in the adapter set.
Reproduction (no model download needed -- pure component-level and adapter-level verification)
import torch
import torch.nn as nn
from transformer_lens.model_bridge.generalized_components.opaque_block import OpaqueBlockBridge
from transformer_lens.model_bridge.exceptions import StopAtLayerException
class MockBlock(nn.Module):
def forward(self, hidden_states, **kwargs):
return hidden_states
# Exactly how component_setup.py names Raven's core_block list items.
bridge = OpaqueBlockBridge(name="transformer.core_block", submodules={})
bridge.name = "transformer.core_block.0"
bridge.set_original_component(MockBlock())
bridge._stop_at_layer_idx = 0
try:
bridge(torch.randn(1, 3, 4))
print("BUG: no StopAtLayerException raised")
except StopAtLayerException:
print("correctly raised")
# -> prints "BUG: no StopAtLayerException raised"
from types import SimpleNamespace
from transformer_lens.model_bridge.supported_architectures.raven import RavenArchitectureAdapter
cfg = SimpleNamespace(d_model=8, d_head=4, n_heads=2, n_layers=1, n_ctx=16, d_vocab=32,
d_mlp=16, act_fn="gelu", normalization_type="RMS", default_prepend_bos=False)
adapter = RavenArchitectureAdapter(cfg)
print("blocks" in adapter.component_mapping) # -> False
System Info
- Installed from source,
dev-4.x
- OS-independent (pure Python logic, no weights involved); the real checkpoint (
tomg-group-umd/huginn-0125, ~14GB) was not downloaded for this report -- the component-level and adapter-level checks above fully isolate and confirm the bug without it.
Additional context
This is the same general failure class as #1632/#1633 (stop_at_layer never stopping on the native Bridge) -- a different, non-standard block-list shape falling outside what the stop_at_layer wiring accounts for. HRM-Text's L_blocks/H_blocks and encoder-decoder's encoder_blocks/decoder_blocks already got an explicit reject-with-clear-error carve-out at some point; Raven's three-way prelude/core_block/coda split was missed.
Expected behaviour & fix pointers
At minimum, Raven should join the explicit reject-list so stop_at_layer raises a clear NotImplementedError instead of silently no-op'ing -- matching the existing L_blocks/H_blocks/encoder_blocks/decoder_blocks precedent. Actually supporting it would need the top-level wiring to walk all of prelude/core_block/coda (not just a single self.blocks), and OpaqueBlockBridge._check_stop_at_layer() to recognize those list names -- complicated further by core_block being a recurrent list (each of its 4 blocks runs N times per forward), so "stop at core_block layer i" would need a defined semantic for which recurrence step to stop at.
Acceptance:
Checklist
Describe the bug
TransformerBridge.forward(..., stop_at_layer=N)(and anything built on it, e.g.input_to_embed()) silently does nothing on a Raven/Huginn bridge -- no error, no early stop. The forward pass runs to full completion as ifstop_at_layerhad never been passed.Two independent gaps combine to cause this:
self.blocks. InTransformerBridge.forward(), thestop_at_layerhandling explicitly rejects a fixed list of known non-standard block-list names (L_blocks,H_blocks,encoder_blocks,decoder_blocks) with a clearNotImplementedError, then sets_stop_at_layer_idxon every block onlyif hasattr(self, "blocks").RavenArchitectureAdapter.component_mappinghas no"blocks"key at all -- Huginn's three block lists areprelude,core_block, andcoda(matching its prelude / weight-tied recurrent core / coda structure). Raven isn't in the reject-list either, so the request silently falls through both checks: no error, and_stop_at_layer_idxnever gets set on anything.OpaqueBlockBridge._check_stop_at_layer()'s layer-index regex wouldn't recognize it anyway. It only matches\.layers\.(\d+)orblocks\.(\d+); Raven's per-block names (transformer.core_block.0,transformer.prelude.0,transformer.coda.0) match neither. The standardBlockBridgeclass already has a more general_extract_layer_idx()((?:^|\.)(?:blocks|h|layers)\.(\d+), covering GPT-2'sh.Ntoo) thatOpaqueBlockBridgedoesn't share or reuse -- and even that wouldn't matchcore_block/prelude/codaeither way.Surveyed every other architecture using
OpaqueBlockBridge/SSMBlockBridge(mamba, mamba2, nemotron_h, rwkv7, zamba2): all name their listbackbone.layersormodel.layers, which matches fine. Raven is the only affected architecture currently in the adapter set.Reproduction (no model download needed -- pure component-level and adapter-level verification)
System Info
dev-4.xtomg-group-umd/huginn-0125, ~14GB) was not downloaded for this report -- the component-level and adapter-level checks above fully isolate and confirm the bug without it.Additional context
This is the same general failure class as #1632/#1633 (
stop_at_layernever stopping on the native Bridge) -- a different, non-standard block-list shape falling outside what thestop_at_layerwiring accounts for. HRM-Text'sL_blocks/H_blocksand encoder-decoder'sencoder_blocks/decoder_blocksalready got an explicit reject-with-clear-error carve-out at some point; Raven's three-way prelude/core_block/coda split was missed.Expected behaviour & fix pointers
At minimum, Raven should join the explicit reject-list so
stop_at_layerraises a clearNotImplementedErrorinstead of silently no-op'ing -- matching the existingL_blocks/H_blocks/encoder_blocks/decoder_blocksprecedent. Actually supporting it would need the top-level wiring to walk all ofprelude/core_block/coda(not just a singleself.blocks), andOpaqueBlockBridge._check_stop_at_layer()to recognize those list names -- complicated further bycore_blockbeing a recurrent list (each of its 4 blocks runs N times per forward), so "stop at core_block layer i" would need a defined semantic for which recurrence step to stop at.Acceptance:
stop_at_layeron a Raven/Huginn bridge either works correctly or raises a clear, explicit error -- never silently no-opsinput_to_embed()(which callsstop_at_layer=0internally) does not silently return full logits instead of the post-embedding residual for Ravenmake unit-testanduv run mypy .passChecklist