[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(