[AMD][Bugfix] Make the nested-RoPE patch reach automatic validation (#57289)

Signed-off-by: Oxana Korzh <okorzh@amd.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Andreas Karatzas <akaratza@amd.com>

Oxana Korzh committed Sep 17, 2026 at 17:09 UTC a524f80b1f0dd85889bf227310670ee8fdcd5fd2
1 file changed +41 -7
vllm/transformers_utils/config.py
+41 -7
@@ -162,11 +162,12 @@ _SPECULATIVE_DECODING_CONFIGS: set[str] = {"eagle", "speculators", "medusa"}
162
163 _PATCH_HF_VALIDATE_ROPE: set[str] = {"sarvam_mla"}
164
165 -# Model types whose checkpoints store shared RoPE parameters alongside the
166 -# per-layer-type dicts (e.g. Laguna's `original_max_position_embeddings`).
167 -# Since transformers 5.17, `validate_rope` treats every top-level value of such
168 -# a dict as a layer's parameters and raises on the shared ones.
169 -_PATCH_HF_NESTED_ROPE_VALIDATION: set[str] = {"laguna"}
165 +# Model types whose checkpoints carry shared RoPE parameters alongside the
166 +# per-layer-type dicts. Since transformers 5.17, `validate_rope` treats every
167 +# top-level value of such a dict as a layer's parameters and raises on the
168 +# shared ones. `laguna` gets them injected by `convert_rope_params_to_dict`;
169 +# `gemma4_text` ships them in the checkpoint itself.
170 +_PATCH_HF_NESTED_ROPE_VALIDATION: set[str] = {"laguna", "gemma4_text"}
171
172 # Model types whose checkpoints declare `layer_types` entries that upstream
173 # transformers has not added to `ALLOWED_LAYER_TYPES` yet, so its strict config
@@ -224,6 +225,35 @@ def _mistral_patch_hf_hub_constants() -> Iterator[None]:
225 constants.SAFETENSORS_INDEX_FILE = hf_safetensors_index_file
226
227
228 +def _install_hf_config_validator(
229 + name: str, validator: Callable, supersedes: Callable
230 +) -> None:
231 + """Replace a ``PretrainedConfig`` validator on every ``@strict`` snapshot.
232 +
233 + ``@strict`` snapshots each ``validate_*`` method into ``__class_validators__``
234 + at class creation and automatic post-``__init__`` validation dispatches off
235 + that frozen list, so assigning the class attribute alone only affects
236 + explicit ``config.validate_*()`` calls. Every ``@strict``-decorated config
237 + class owns a snapshot, so rewrite them all, matching ``supersedes`` by
238 + identity to leave a genuine per-model override in place.
239 + """
240 + setattr(PretrainedConfig, name, validator)
241 +
242 + seen: set[int] = set()
243 + stack = [PretrainedConfig]
244 + while stack:
245 + cls = stack.pop()
246 + if id(cls) in seen:
247 + continue
248 + seen.add(id(cls))
249 + validators = cls.__dict__.get("__class_validators__")
250 + if isinstance(validators, list) and any(v is supersedes for v in validators):
251 + cls.__class_validators__ = [
252 + validator if v is supersedes else v for v in validators
253 + ]
254 + stack.extend(cls.__subclasses__())
255 +
256 +
257 def _patch_hf_transformers_validate_rope():
258 """Transformers v5 moved the ignore_keys option from the method signature of
259 validate_rope and replaced it with the ignore_keys_at_rope_validation parameter
@@ -250,7 +280,9 @@ def _patch_hf_transformers_validate_rope():
280 return result
281
282 patched_validate_rope.__vllm_patched__ = True # type: ignore[attr-defined]
253 - PretrainedConfig.validate_rope = patched_validate_rope
283 + _install_hf_config_validator(
284 + "validate_rope", patched_validate_rope, _original_validate_rope
285 + )
286
287
288 def _patch_hf_transformers_nested_rope_validation() -> None:
@@ -284,7 +316,9 @@ def _patch_hf_transformers_nested_rope_validation() -> None:
316 return _original_validate_rope(self, *args, **kwargs)
317
318 patched_validate_rope.__vllm_nested_rope_patched__ = True # type: ignore[attr-defined]
287 - PretrainedConfig.validate_rope = patched_validate_rope
319 + _install_hf_config_validator(
320 + "validate_rope", patched_validate_rope, _original_validate_rope
321 + )
322
323
324 def _patch_hf_transformers_allowed_layer_types(