Load the text tower of dual-registered composite configs - #52
Closed
can-goodfire wants to merge 2 commits into
Closed
can-goodfire wants to merge 2 commits into
can-goodfire wants to merge 2 commits into
Conversation
nnsight's LanguageModel refuses any config registered with AutoModelForImageTextToText, on the premise that AutoModelForCausalLM would fail on it because the text fields sit under config.text_config. That premise holds for genuine VLMs — llava and qwen2_vl have no causal-LM mapping at all — but not for a config that registers both. qwen3_5_moe (Qwen3.6-35B-A3B) maps to Qwen3_5MoeForConditionalGeneration *and* to Qwen3_5MoeForCausalLM, so the text tower loads cleanly and refusing it locks nnterp out of the whole architecture. Detect exactly that case — model_type in both auto mappings — and opt out of the refusal via the escape hatch nnsight documents for it: the guard stands down for any non-default automodel, so hand it a marker subclass that dispatches through the identical _model_mapping. Ordinary text models and multimodal-only configs take neither branch, and text_tower=False restores nnsight's own handling. A failure to read the config ahead of time returns None rather than raising, so looking ahead can never be the thing that fails a load; nnsight still reports. The vision tower is not loaded, by design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The repo pins black 25.1.0 via pre-commit; one monkeypatch call fit on a single line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Member
|
bug doesn't happenb in the dev branch, but i added an option in #57 to only load the vision tower |
goodatticus
pushed a commit
to goodfire-ai/causalab
that referenced
this pull request
Sep 1, 2026
nnsight's LanguageModel refuses any config registered with AutoModelForImageTextToText, so it cannot load qwen3_5_moe — the text tower of Qwen3.6-35B-A3B and the target of the hookpoint-vocabulary work — even though AutoModelForCausalLM resolves to that tower cleanly. ndif-team/nnterp#52 fixes it and is open for review; this unpins us from that review landing. 45f386b is the upstream pin b4a3127 plus the two commits in #52, so this is a strict superset of what we had — the packaging fix (#49) that motivated the git source in the first place is still in there, and the comment now records both reasons and how each retires. Verified installed from the fork's public URL rather than a local path: nnterp 1.3.1.dev16+g45f386b7c loads tiny-random/qwen3.5-moe as Qwen3_5MoeForCausalLM with no vision tower attached, and traces to logits. Note this branch still resolves transformers 4.57.1, which carries no qwen3_5_moe, so the fix ships here but cannot be exercised until a transformers >= 5.16 bump reaches this lineage. That bump landed downstream on can/protocol-refactor (#46) and is a separate decision for the path to main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
StandardizedTransformer("Qwen/Qwen3.6-35B-A3B")fails before any renaming happens:nnsight's
LanguageModel._check_is_text_onlyrefuses anymodel_typeinMODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES, on the premise thatAutoModelForCausalLMwould then fail because the text fields sit underconfig.text_config.That premise does not hold for every such config:
qwen3_5_moeQwen3_5MoeForConditionalGenerationQwen3_5MoeForCausalLMqwen2_vlQwen2VLForConditionalGenerationNonellavaLlavaForConditionalGenerationNoneqwen3_5_moeis registered in both tables.AutoModelForCausalLMresolves to the text tower and loads it cleanly. Genuine VLMs have no causal-LM mapping at all, which is the case the guard is actually for — so the refusal is over-broad, and it locks nnterp out of a whole architecture family (Qwen3.6-35B-A3B and its siblings).What this does
nnterp/text_tower.pydetects exactly that case —model_typepresent in both auto mappings — and opts out of the refusal using the escape hatch the guard documents for it: it stands down for any non-defaultautomodel, so nnterp hands it a marker subclass ofAutoModelForCausalLMthat dispatches through the identical_model_mapping.New
text_tower: bool = Truekwarg onStandardizedTransformer. Ordinary text models and multimodal-only configs take neither branch;text_tower=Falserestores nnsight's own handling. The vision tower is never loaded, by design.A failure to read the config ahead of time returns
Nonerather than raising, so looking ahead can never be the thing that fails a load — nnsight still reports the real error.Verified
On
tiny-random/qwen3.5-moe(transformers 5.16.0, nnsight 0.7.0):Qwen3_5MoeForCausalLM;hasattr(model, "visual")isFalsenum_layers4,hidden_size8,vocab_size248320,num_heads8 all resolve — the existingtext_config()helper already handles the composite configtoken_embeddings,layers_output[0]andlogitsall trace correctlygpt2,Maykeye/TinyLLama-v0andyujiepan/qwen3-moe-tiny-randomstill load with full renaming validationScope
This fixes loading only.
check_renaming=Trueon this architecture then hits the next problem —RenamingError: Could not find self_attn module, becauselayer_types = ([linear_attention] * 3 + [full_attention]) * 10meanslayers[0]is a Gated DeltaNet layer with noself_attn. That is the hybrid-layer work in #18 and is deliberately not touched here.An alternative worth considering
The cleaner fix is upstream in nnsight — narrow the guard to refuse only when there is no causal-LM mapping to fall back on:
try: from transformers.models.auto.modeling_auto import ( + MODEL_FOR_CAUSAL_LM_MAPPING_NAMES, MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES, ) except ImportError: return model_type = getattr(self.config, "model_type", None) - if model_type and model_type in MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES: + if ( + model_type + and model_type in MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES + and model_type not in MODEL_FOR_CAUSAL_LM_MAPPING_NAMES + ): raise ValueError(I applied that to nnsight 0.7.0 locally and confirmed
StandardizedTransformer(..., text_tower=False)then loads the text tower with no nnterp involvement. Happy to file it there instead or as well — but nnterp needs to work against released nnsight either way, which is what this PR provides.Unrelated finding, reported separately
yujiepan/qwen1.5-moe-tiny-random— in nnterp's owncore_test_models— fails to load under transformers 5.16 + fp32 withRuntimeError: Expected inputs of BF16 typefromtorch._grouped_mmon the fake-tensor path, soscan()fails and thetrace()fallback then fails the IO check. Pre-existing atb4a3127and independent of this change (qwen2_moeis not dual-registered, so this code path never engages, andtext_tower=Falsefails identically).🤖 Generated with Claude Code