Load Qwen3.5 adapters with the pinned model's attention shape - #959
Merged
Merged
Conversation
Adapter conversion looked the base model's config up by name, ignoring the adapter's pinned revision. An offline cache holding only the pinned snapshot failed, and a different refs/main silently gave the wrong shape. Checkpoint loading now fills missing attention dimensions from the running model, and the by-name fallback resolves the adapter's revision. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bradhilton
deployed
to
trainer-rank-gpu-validation
September 25, 2026 04:30 — with
GitHub Actions
Active
…d paths Treat null adapter dimensions as missing, use the running model's shape only when it has heads, query groups and head size, and apply the same fill on the Megatron service's adapter load. An empty revision means unpinned. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bradhilton
had a problem deploying
to
trainer-rank-gpu-validation
September 25, 2026 04:50 — with
GitHub Actions
Error
… harness Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bradhilton
deployed
to
trainer-rank-gpu-validation
September 25, 2026 04:57 — with
GitHub Actions
Active
Take every dimension from the adapter when set, else the running model, else the base model's config at the adapter's revision. Previously a model missing its head size dropped all of its dimensions, and the handler then defaulted query groups to one per head without a lookup. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bradhilton
had a problem deploying
to
trainer-rank-gpu-validation
September 25, 2026 05:21 — with
GitHub Actions
Error
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bradhilton
deployed
to
trainer-rank-gpu-validation
September 25, 2026 05:28 — with
GitHub Actions
Active
This branch was successfully deployed
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.
Loading a Qwen3.5/3.6 LoRA adapter can fail offline, or silently convert with the wrong attention shape, even though the base model loaded from its pinned snapshot.
Cause
This affects an adapter stored as a vLLM/PEFT checkpoint (no ART manifest). It is converted by
load_lora_tensors_for_megatron, which reads the rawadapter_config.json. Such configs carrybase_model_name_or_pathandrevision, but no attention dimensions. The Qwen3.5 handler needs those dimensions to reorderq_projLoRA-B rows, so it fell back toAutoConfig.from_pretrained(base_model_name_or_path, local_files_only=True):revisionwas never passed on, and the helper's cache key was the name alone. The lookup went through the cache'srefs/main, not the pinned snapshot.refs/main, raisedOSErrorduring adapter load, after the pinned model had initialized. This is the failure Krennic hit in 062's MoE qualification, throughTrainerRank.load_checkpoint.refs/mainpointed at a different revision, the lookup silently returned that revision's shape for the pinned adapter.TrainerRankalready writes the running model's attention dimensions into adapter configs it saves. The disk load paths never did.Change
Each attention dimension now comes from the first of these that has it:
null, because it describes the tensors on disk;num_attention_heads,num_key_value_heads(fromnum_query_groups),head_dim(fromkv_channels),hidden_size;revision.load_lora_tensors_for_megatron(..., provider=...)fills dimensions the adapter config omits or nulls. The following all pass their provider:_checkpoint._load_adapter;_load_adapter_into_modelinmegatron/train.py;hidden_size // heads. Both are wrong for Qwen3.5/3.6, which use grouped queries and head_dim 256. Now any missing dimension comes from the pinned config.hidden_size // headsremains only as a last resort, when no source has a head size. Likewise, one group per head remains only when even the fetched config has no query-group count. One behavior change follows: if the adapter and model still leave a dimension missing, that lookup must succeed. Offline, it now fails loudly where the old code silently used those defaults, which are wrong for this model family.model_attention_dimensions._qwen35_text_configtakes the adapter'srevision, passes it toAutoConfig.from_pretrainedand caches by (name, revision).refs/main, as before. An emptyrevisioncounts as unpinned.Tests
tests/unit/test_qwen35_adapter_config.pyruns on CPU only, with an isolated offline HF cache. There is no network access and no model weights are loaded.refs/mainpoints at a second revision with a different query-group count. Dimension lookups pinned to A, then B, then A return A's, B's and A's shape, so there is no cache collision. Unpinned and empty-revision lookups still followrefs/main._load_adapterconverts aq_projLoRA-B tensor with the running model's shape, with no base-model lookup, for both the dense and MoE Qwen3.5 handlers.nulladapter dimensions are filled from the running model, and the conversion matches the model's grouped layout. The model's hidden size is not heads × head_dim, so deriving head_dim from it would also fail the test.hidden_size // heads.to_vllm_lora_tensors) resolves its revision in the snapshot-only cache.All 13 test cases fail on
main. Existing unit tests that touch adapter loading, checkpoints and the Qwen handlers pass locally (341). The module runs in CI's Megatron lightweight lane with the other trainer-rank tests, and the root step ignores it.Krennic's independent CPU reproducer, from the 062 handoff, exercises the real snapshot-only failure and the drift case. It passed against this branch's first commit. His review of
e2264fb3athen found the partial-model corner that this head fixes.Not covered
config.jsonby name without a revision:gpt_oss,gemma4andnemotron_h.create_identity_lorapasses normalization metadata without dimensions or revision.Neither is on the failing path; each could follow the same pattern separately.
🤖 Generated with Claude Code