Skip to content

Fix rope_scaling setter for configs that assign it during init - #50080

Merged
hmellor merged 2 commits into
huggingface:mainfrom
hmellor:fix-legacy-rope-remote-code
Oct 28, 2026
Merged

hmellor merged 2 commits into
huggingface:mainfrom
hmellor:fix-legacy-rope-remote-code

Conversation

@hmellor

@hmellor hmellor commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

🤗 Transformers CI

What does this PR do?

Fixes a v5.21.0 regression that breaks remote-code configs (found bumping vLLM to 5.21, vllm-project/vllm#64049).

rope_scaling setter (#49515). The setter now calls standardize_rope_params() immediately. Remote code (and vLLM's speculators config) assigns rope_scaling in __init__ before super().__init__(), so standardizing there:

  • mutates the caller's dict, adding rope_type/rope_theta, which fails remote validators that check its exact keys (Phi-3/3.5-vision, Phi-3.5-MoE, Phi-4-multimodal, InternVL2, InternLM2.5);
  • bakes rope_theta=None when rope_theta is assigned after rope_scaling (EAGLE3 speculators, ERNIE-4.5-VL), crashing RoPE init;
  • reads max_position_embeddings before it exists (Step-3.7-Flash, Step-5).

The setter now only standardizes after __post_init__ has finished, so assigning rope_scaling to an initialised config is still normalised as #49515 intended. __post_init__ standardizes everything assigned before it, as in 5.20.

Tests

  • New test_rope_scaling_setter_during_init fails on main and passes here.
  • pytest tests/utils/test_configuration_utils.py tests/utils/test_modeling_rope_utils.py tests/test_image_transforms.py tests/models/llama/test_modeling_llama.py tests/models/phi3/test_modeling_phi3.py tests/models/qwen2_vl/test_image_processing_qwen2_vl.py tests/models/chameleon/test_image_processing_chameleon.py: 462 passed, 389 skipped.
  • vLLM's EAGLE3 speculator config (nm-testing/Llama3_2_1B_speculator.eagle3) gets rope_theta=500000.0 again (None on main), and vLLM's Step5Config builds again.

This PR was written with AI assistance (Claude Code).

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you read the contributor guideline and the
    Pull Request checks?
  • Was this discussed/approved via a Github issue or the forum? Please add a link
    to it if that's the case.
  • Did you make sure to update the documentation with your changes according to the guidelines?
  • Did you write any new necessary tests?

🤖 Generated with Claude Code

Only standardize RoPE params in the rope_scaling setter after __post_init__, so configs that assign
rope_scaling during init keep their original dict and rope_theta. Restore convert_to_rgb's
pass-through for inputs that are neither PIL nor numpy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hmellor hmellor changed the title Fix rope_scaling setter and convert_to_rgb regressions for remote code Fix rope_scaling setter for configs that assign it during init Oct 28, 2026
@hmellor
hmellor marked this pull request as ready for review October 28, 2026 13:57
@hmellor
hmellor requested review from ArthurZucker and zucchini-nlp and a balanced review from Copilot and removed request for Copilot October 28, 2026 14:01
)
self.rope_parameters = value
if value is not None:
if value is not None and getattr(self, "_post_init_done", False):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate this field 😭

yeah, so the goal is to allow re-setting field via rope_scaling name on local/new models in unified format and keep remote code happy by allowing them set whatever they want
Maybe we can standardize only if getattr rope_parameters is not None

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The issue is that we are validating rope too eagerly before custom configs have had the chance to set everything.

i.e. the following snippet raises an error with that suggestion:

class MyConfig(PreTrainedConfig):
    def __init__(...):
        self.rope_scaling = {...}
        self.__post_init__()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iiuc these remote models they don't yet have any rope param set in init anyway, no?
Just don't want to add another private field to track if initialisation is done

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do. Here's another example https://huggingface.co/microsoft/Phi-3.5-vision-instruct/blob/main/configuration_phi3_v.py#L166-L167L

    def __init__(
        ...
    ):
        self.rope_scaling = rope_scaling
        self._rope_scaling_validation()

That validation method checks the number of keys in rope_scaling, and eagerly calling standardize_rope_params breaks that.

@zucchini-nlp zucchini-nlp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oke ig with a variety of custom code on the hub, this is the most reliable way

@hmellor
hmellor added this pull request to the merge queue Oct 28, 2026
@hmellor hmellor added the for patch Tag issues / labels that should be included in the next patch label Oct 28, 2026
@Rocketknight1

Rocketknight1 commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

I think there should be a simpler solution that avoids the extra post_init_done attribute, right? @zucchini-nlp's suggestion of something like

if getattr(self, "rope_parameters", None) is not None and value is not None:
    self.rope_parameters = value
    self.standardize_rope_params()
else:
    self.rope_parameters = value

It's not critical, so if we want to rush the current version into the patch that's fine, but this might be tidier, and I think it passes for @hmellor's snippets above

Merged via the queue into huggingface:main with commit 5e83c28 Oct 28, 2026
125 checks passed
@hmellor
hmellor deleted the fix-legacy-rope-remote-code branch October 28, 2026 15:02
@hmellor

hmellor commented Oct 28, 2026

Copy link
Copy Markdown
Member Author

The issue is not the existence of rope_parameter or value. The issue is several:

  • standardize_rope_params reads other fields which might not exist yet in custom configs
  • standardize_rope_params changes rope_parameters which may break the custom config's own validation
  • standardize_rope_params copies rope_theta from root if it's not in rope_parameters, but a custom config might not have set rope_theta at the root yet so it stays None in rope_parameters

hmellor added a commit that referenced this pull request Oct 28, 2026
)

* Fix rope_scaling setter and convert_to_rgb regressions for remote code

Only standardize RoPE params in the rope_scaling setter after __post_init__, so configs that assign
rope_scaling during init keep their original dict and rope_theta. Restore convert_to_rgb's
pass-through for inputs that are neither PIL nor numpy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Revert convert_to_rgb pass-through

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@zucchini-nlp

Copy link
Copy Markdown
Member

Yeah, that is also what i thought at first tho it seems remote models set and reset again the rope field several times, e.g. the custom standardisation from Harry's message above

hmellor added a commit to hmellor/vllm that referenced this pull request Oct 28, 2026
Picks up huggingface/transformers#50080.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
@Rocketknight1

Rocketknight1 commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Hmmn, are there examples of failing ones? I think those criticisms are correct if standardize_rope_params is called during __init__() but if we call it afterwards like that code snippet then it should be fine.

I sent an agent to test it with Phi-3 / Phi-3.5-vision / Phi-3.5-MoE / Phi-4-multimodal / InternLM2.5 / Step-3.7 / Step5 / EAGLE3 and your __post_init__ snippet and they look good, but maybe I'm misunderstanding!

@hmellor

hmellor commented Oct 28, 2026 •

Copy link
Copy Markdown
Member Author

standardize_rope_params will always be called during __init__() for models that call self.rope_scaling = ... in __init__(). This is true for all custom models which pre-date the:

  • rope_scaling -> rope_parameters rename
  • move to dataclass based configs

The snippet you proposed would work if we could guarantee that all custom models would only ever write rope_scaling once, but I'm not sure if that is a safe assumption?

adarshxs added a commit to adarshxs/sglang that referenced this pull request Oct 29, 2026
5.22.0 carries the rope_scaling setter fix (huggingface/transformers#50080)
that 5.21.0 lacked, so the pin moves straight to it. The 5.21 adaptations
already on this branch cover 5.22 as well: every transformers symbol SGLang
imports still exists at v5.22.0, the chat-template mirror still matches
apply_chat_template, and the shipped TP plan styles are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

for patch Tag issues / labels that should be included in the next patch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants