Fix a destructive Qwen3 TP-plan override; split out the conversions
`update_tp_plan` carried `if "Qwen3" in config.__class__.__name__:` and then
ASSIGNED a hand-written dense plan over `config.base_model_tp_plan`. Three things
were wrong with it:
- redundant: the module-keyed entries Qwen3 already ships reach a parameter's
scale through the plan matcher's module fallback — `q_proj.weight_scale_inv`
resolves to `colwise`, `down_proj.weight_scale_inv` to `rowwise` — so the
parameter-keyed duplicates bought nothing;
- destructive: assigning dropped what the model ships, including `q_norm`/`k_norm`
`replicated_with_grad_allreduce`, and on Qwen3-MoE the whole expert plan
(`experts: moe_tp_experts`, `experts.gate_up_proj: packed_colwise`, ...). The
companion pass immediately below keys on `moe_tp_experts`, so it then found
nothing and the expert scales got no entry either;
- over-broad: 25 config classes contain "Qwen3". On the multimodal ones, whose
plans live on a sub-config and whose outer config ships none, it stamped a dense
text plan onto the outer config where those paths match nothing.
Removed. Qwen3 and Qwen3-MoE now keep every shipped entry, Qwen3-MoE additionally
gains the three expert companions it was being denied, and a multimodal config's
outer plan stays empty. Three assertions cover it, against REAL configs — the
existing plan tests build a SimpleNamespace, which a branch keyed on the config
CLASS is structurally invisible to.
Structure:
- the conversion ops move to `integrations/finegrained_conversions.py`
(finegrained.py 1631 -> 985). The dependency is one-way;
`keep_swizzle_reverse_for_save` goes with them, which is what removes the cycle
rather than needing a local import to break it.
- `_nvfp4_conversions` splits per checkpoint layout. The two never interact — a
checkpoint matches one set and the other never fires — so they were adjacent,
not related.
- `replace_with_finegrained_layer` hoists the kwargs every swap shares, leaving
each branch showing only what is its own.
Review comments:
- TP styles are registered properly (`moe_experts_colwise` /
`moe_experts_packed_colwise` / `moe_experts_rowwise`) instead of
`ALL_PARALLEL_STYLES.register` at plan-build time.
- the frozen-fp8 deprecation notices fired at IMPORT, for anyone importing
`transformers.integrations` at all, one of them through `warnings.warn` rather
than the logger. Now one `logger.warning_once` on quantizer construction.
- `has_global_scale` dropped: `global_scale_dtype: torch.dtype | None` already
answered it.
- defensive `getattr` on declared config fields replaced with direct access.
- `quantizers_utils.py`, `integrations/tensor_parallel.py` and
`integrations/finegrained_fp8.py` are back to their upstream contents and leave
this diff entirely.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>