Skip to content

[Bugfix][Qwen3.5] Give image prompts embedding spans so prefix hits past an image don't crash - #3041

Draft
syang-genai wants to merge 1 commit into
ROCm:mainfrom
syang-genai:fix/qwen3_5-image-embedding-spans
Draft

syang-genai wants to merge 1 commit into
ROCm:mainfrom
syang-genai:fix/qwen3_5-image-embedding-spans

Conversation

@syang-genai

Copy link
Copy Markdown

Motivation: fixes #2865. Qwen3.5 image prompts reach the runner without embedding_spans, so they take the whole-image merge in ModelRunner.run_model: every vision row is scattered into the forward's image tokens. After a prefix-cache hit that ends inside or past the image, the forward holds fewer image tokens than the vision tower returns rows, and merge_multimodal_embeddings fails with a shape mismatch ([784, 2048] into [0, 2048] in the issue's A, B, A repro).

Technical details:

  • build_qwen_vl_inputs returns each image's run of image tokens as an embedding_spans entry, plus the media cache_seed. This applies to the qwen3_5 and qwen3_5_moe model types, the same gate as the Qwen3.5 MRoPE path. The runner then takes the span path that already exists: it scatters only the slice of each image a forward contains, and the image prompt no longer needs one atomic prefill.
  • qwen3_5_image_spans walks the prompt and claims each image's t*h*w / merge**2 tokens in turn, so adjacent images without separators split correctly. Prompts whose image tokens don't match the grids are rejected when the request is built, instead of failing in the forward.
  • Qwen3.8 (qwen4_exp) stays on the whole-image path. Its processor output differs, and I'd rather handle it separately.

Test Plan:

  • tests/test_multimodal_inputs.py:
    • one span per image, for both model types;
    • cache_seed tells different images apart;
    • prompts that disagree with the grid are rejected;
    • other Qwen model types keep the whole-image merge.
  • tests/test_multimodal_runtime.py:
    • after prefix hits ending before, inside and past the image, the forward scatters only the image rows left in it;
    • an image prompt is chunked and resumes after a prefix hit.
python -m pytest tests/test_multimodal_inputs.py tests/test_multimodal_runtime.py

Test Result:

  • On this branch: 69 passed.
  • With atom/models/qwen3_5_vl.py reverted to main: 13 of the 14 new tests fail. The one that passes is the check that other model types are unchanged.
  • black and ruff are clean apart from two findings that are already on main.

Not done yet: I haven't run an end-to-end check on MI3xx/MI355X. I don't have one, and ATOM has no gfx11 path to test on my machine. If someone could rerun the issue's ATOM_GDN_STATE_MIDSTEP=1 A, B, A repro on this branch, that would close the gap. Opening as a draft until then.

@ZLkanyo009, if you'd rather fold this into #2636, I'll close this one.

…an image don't crash

Qwen3.5 image prompts reached the runner without `embedding_spans`, so they
took the whole-image merge: every vision row is scattered into the image
tokens of the forward. After a prefix-cache hit that ends inside or past the
image, the forward holds fewer image tokens than the vision tower returns
rows, and `merge_multimodal_embeddings` dies with a shape mismatch.

`build_qwen_vl_inputs` now returns each image's run of image tokens as an
embedding span for the `qwen3_5` and `qwen3_5_moe` model types (the same gate
as the Qwen3.5 MRoPE path), plus the media `cache_seed`. The runner then
scatters only the slice of each image a forward contains, and image prompts
can be chunked instead of prefilled in one forward. Image tokens that do not
match the grids are rejected when the request is built.

Qwen3.8 (`qwen4_exp`) is left on the old path for now.

Fixes ROCm#2865
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every eligible PR before approval:

  • ✅ Pre Checkin: Black, Ruff, catalog schema validation, non-GPU unit tests

Heavy model tests:

  • ✅ Run after the PR is approved and Pre Checkin passes
  • ✅ Run immediately when an approval review is submitted
  • ✅ Can be requested before approval with labels
Label Tests
ci:full Run all heavy PR model tests: native ATOM, vLLM, and SGLang
ci:atom Run native ATOM model accuracy tests
ci:vllm Run ATOM vLLM OOT model accuracy tests
ci:sglang Run ATOM SGLang model accuracy tests

Heavy jobs are skipped when the PR is not approved and no matching ci:* label is present.
Add labels via the sidebar or gh pr edit 3041 --add-label <label>

This branch has not been deployed

No deployments
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.

Qwen3.5 image prompt: prefix-cache hit covering image tokens crashes merge_multimodal_embeddings (shape mismatch), reproduces on main without offload

1 participant