Skip to content

[model] fix: use the transformers 5 rotary API in the GLM-4V attention patch - #7620

Merged
wuxibin89 merged 2 commits into
verl-project:mainfrom
alanhuangyoo:fix/glm4v-rotary-transformers-5
Sep 2, 2026
Merged

wuxibin89 merged 2 commits into
verl-project:mainfrom
alanhuangyoo:fix/glm4v-rotary-transformers-5

Conversation

@alanhuangyoo

Copy link
Copy Markdown
Contributor

What does this PR do?

GLM-4V training dies at the first attention layer on every transformers version this repo
supports.

glm4v_attn_forward replaces Glm4vTextAttention.forward whenever use_remove_padding or
ulysses_sp_size > 1 (monkey_patch.py:473), so this is the ordinary GLM-4V path, not a corner:

from transformers.models.glm4v.modeling_glm4v import apply_multimodal_rotary_pos_emb, repeat_kv
transformers 5.3.0
ImportError: cannot import name 'apply_multimodal_rotary_pos_emb'
             from 'transformers.models.glm4v.modeling_glm4v'

transformers 5.0 replaced apply_multimodal_rotary_pos_emb(q, k, cos, sin, mrope_section) with
apply_rotary_pos_emb(q, k, cos, sin) and moved the sectioning into
Glm4vTextRotaryEmbedding, which reads mrope_section from config.rope_parameters and folds
it into cos/sin before the attention ever sees them. Glm4vTextAttention.forward upstream is
now just:

cos, sin = position_embeddings
query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)

Absent in every supported version โ€” requirements.txt asks for
transformers>=5.5.3,!=5.6.0,<5.11, and the extras pin 5.3.0 / 5.5.3:

transformers apply_multimodal_rotary_pos_emb in modeling_glm4v
4.57.1 yes
5.0.0, 5.3.0, 5.5.3, 5.10.4, 5.16.1 no

It came in with #5724 (transformers upgrade to 5.3.0), which updated the config-side
rope_scaling โ†’ rope_parameters migration a few lines above but not the function that moved
with it.

qwen2_vl.py uses the same call and is not affected: modeling_qwen2_vl still exports
apply_multimodal_rotary_pos_emb. Only GLM-4V was migrated.

The change

Call the function upstream calls, and drop the mrope_section lookup that fed the old one.

That lookup was dead twice over โ€” it reads self.rope_scaling or self.rope_parameter off the
attention module, and on transformers 5 the attention module has neither (the config
attribute is rope_parameters, plural, and lives on the config):

transformers 5.3.0, Glm4vTextAttention
  self.rope_scaling    -> <ABSENT>
  self.rope_parameter  -> <ABSENT>

so the assert under it would have fired had the import ever got that far.

Restoring the old function instead would be wrong, not just unavailable: the 4.x call expects
cos/sin with a leading 3-section dim, and applying it to what transformers 5 produces raises
IndexError: index 1 is out of bounds for dimension 0 with size 1. The sectioning genuinely
moved.

Checklist Before Starting

Test

tests/models/test_glm4v_rotary_on_cpu.py, following the on_cpu.py convention so it runs in
cpu_unit_tests.yml. It builds a real Glm4vTextAttention and Glm4vTextRotaryEmbedding, runs
glm4v_attn_forward with the CUDA-only flash-attention call patched out, and asserts the q/k
reaching attention equal apply_rotary_pos_emb(q, k, cos, sin) โ€” the same rotary upstream
applies โ€” with KV repeated to the query head count. A second case records why the argument had
to go rather than be restored.

Load-bearing, run against a clean origin/main worktree in the same environment:

$ pytest tests/models/test_glm4v_rotary_on_cpu.py          # origin/main
>       from transformers.models.glm4v.modeling_glm4v import apply_multimodal_rotary_pos_emb, repeat_kv
E       ImportError: cannot import name 'apply_multimodal_rotary_pos_emb' ...
1 failed, 1 passed

$ pytest tests/models/test_glm4v_rotary_on_cpu.py          # this branch
2 passed

$ ruff check / ruff format --check                          # ruff 0.12.2, as pinned in .pre-commit-config.yaml
All checks passed! / 2 files already formatted

$ python3 tests/special_sanity/check_license.py --directories .
exit 0

The flash-attention call itself is CUDA-only and unchanged by this PR, so it stays covered by
the existing GPU tests.

API and Usage Example

No API change.

Design & Code Changes

One import and one call site in verl/models/transformers/glm4v.py; the mrope_section
derivation is removed with them.

Checklist Before Submitting

โ€ฆn patch

glm4v_attn_forward imported apply_multimodal_rotary_pos_emb and passed it an
mrope_section. transformers 5 folds mrope_section into cos/sin inside
Glm4vTextRotaryEmbedding and exposes only apply_rotary_pos_emb, so that import
raised ImportError at the first attention layer on every version this repo
supports (transformers>=5.5.3,!=5.6.0,<5.11). The patch is installed whenever
remove_padding or ulysses_sp_size > 1, which is the ordinary GLM-4V path.

The mrope_section lookup went with it: it read self.rope_scaling or
self.rope_parameter off the attention module, and neither exists there in
transformers 5.
"""

import torch
from transformers.models.glm4v.configuration_glm4v import Glm4vTextConfig

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

CI not needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed. The import and rotary-call fix in glm4v.py stands on its own โ€” apply_multimodal_rotary_pos_emb does not exist in any transformers this repo supports, so the GLM-4V path raises ImportError at the first attention layer with remove_padding or ulysses_sp_size > 1.

Removed at review request; the API fix stands on its own.
@alanhuangyoo

Copy link
Copy Markdown
Contributor Author

The vllm / vllm_ascend reds here are not from this PR โ€” it touches one file,
verl/models/transformers/glm4v.py, and neither job exercises it.

tests/workers/rollout/rollout_vllm/test_vllm_abort.py::test_abort_all_requests_propagates_pause_failure
    AttributeError: 'vLLMHttpServer' object has no attribute '_resume_event'
    verl/workers/rollout/vllm_rollout/vllm_async_server.py:942

That test builds the server with vLLMHttpServer.__new__(vLLMHttpServer), so __init__ never
runs and neither _submission_paused nor _resume_event is set. abort_all_requests started
touching both in #7511 ("gate rollout submission during weight-sync drain for DP>1"), which
lands before the pause_generation call the test is actually asserting on. The test file has
not been updated since.

Happy to send that as a separate one-liner if it is not already being handled.

@wuxibin89
wuxibin89 merged commit 0ace40e into verl-project:main Sep 2, 2026
81 of 87 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants