Skip to content

Fix static-cache generate() with host-side inputs and under TP - #49317

Merged
dacorvo merged 16 commits into
mainfrom
fix-generate-dict-masks-and-tp-cache-heads
Oct 14, 2026
Merged

dacorvo merged 16 commits into
mainfrom
fix-generate-dict-masks-and-tp-cache-heads

Conversation

@dacorvo

@dacorvo dacorvo commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

🤗 Transformers CI

What does this PR do?

Fixes #49261, two generate() bugs with a static cache:

  1. With inputs kept on another device, tensors nested in a model input (e.g. the mask dict keyed by layer type) stayed behind. They are now moved at any depth.
  2. Under TP, the early static cache divided the KV heads by tp_size even when the key projection isn't sharded. It now checks the TP plan.

Note for #47811: colwise_units should count as sharded in get_kv_heads_per_rank. cc @kaixuanliu

Tests

  • test_generate_with_inputs_on_cpu_moves_tensors_in_dict_inputs (new)
  • test_get_kv_heads_per_rank (new)
  • test_chunked_prefill_initializes_static_cache_eagerly (extended: empty plan, gathered k_proj)

Also checked on Neuron, including Qwen3-VL-8B at TP=2.

Who can review?

@ArthurZucker @vasqu @Cyrilvallez @3outeille

dacorvo and others added 3 commits October 5, 2026 09:33
With inputs kept on another device (#46590), `prepare_inputs_for_generation`
moves only the top-level tensors of `model_inputs` onto the model device, so
a tensor nested in a dict stayed behind (example: causal masks for some models).

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

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

Under tensor parallelism `_get_static_cache_init_shape` divided the KV
head count by `tp_size` whenever `tp_size > 1`, whether or not the plan
shards attention. With no plan (Qwen3-VL), a gathered key projection
(`colwise_gather_output`) or an expert-parallel plan, every rank keeps
all heads, so the chunked-prefill cache is allocated with too few and
the first `index_copy_` fails: "Incompatible shapes for broadcasting:
(1, 8, 512, 128) and requested shape (1, 4, 512, 128)" for
Qwen3-VL-8B-Instruct at tp=2.

Only divide when the key projection resolves to `colwise`, the style
that shards heads; skip early init when no key projection is found.

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.

@vasqu vasqu left a comment

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.

Some initial comments, definitely see the overall issues but we can keep the PR description a bit shorter especially since the issue is already detailed

Comment thread src/transformers/generation/utils.py Resolved
Comment thread src/transformers/generation/utils.py Resolved
Comment thread src/transformers/generation/utils.py Resolved
Comment thread tests/generation/test_utils.py Resolved
dacorvo and others added 3 commits October 5, 2026 16:53
- Move tensors nested at any depth in dicts, lists and tuples to the device.
- Read the KV heads per rank from the TP plan instead of scanning parameters.
- Use a tiny random checkpoint in the nested inputs test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dacorvo
dacorvo requested a review from vasqu October 6, 2026 06:47

@vasqu vasqu left a comment

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.

Smaller comments overall but cc @3outeille whenever you have time to sanity check as well

Comment thread src/transformers/generation/utils.py Resolved
@dacorvo
dacorvo requested a review from vasqu October 6, 2026 09:52
@kaixuanliu

Copy link
Copy Markdown
Contributor

@dacorvo Thx for the heads up! Not sure #47811 will be adopted, will keep this in mind and make related update if so.

@vasqu
vasqu requested a review from ArthurZucker October 8, 2026 13:43
@dacorvo

dacorvo commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

gently pinging @3outeille

@dacorvo
dacorvo requested a review from 3outeille October 12, 2026 07:05
Comment thread src/transformers/generation/utils.py Outdated
Comment on lines +780 to +788
def _move_to_device(self: "GenerativePreTrainedModel", value):
"""Moves the tensors in `value`, including those nested in dicts, lists and tuples, onto the model device."""
if isinstance(value, torch.Tensor):
return value.to(self.device)
if isinstance(value, dict):
return {k: self._move_to_device(v) for k, v in value.items()}
if isinstance(value, (list, tuple)):
return type(value)(self._move_to_device(v) for v in value)
return value

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 don't think it should be a method of GenerationMixin, better inside pytorch_utils or something as a separate function

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.

Lol just saw @vasqu said the opposite before on a resolved comment 😆 IMO it should rather be a general util, let me know your thoughts and sorry for the back and forth 😅

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.

I dont mind either way, I see the point of it being more general as well

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.

OK, just tell me which you prefer, and i will lazily ask claude to move it 😅

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.

Lets move it to something more general, e.g. torch utils or similar (not sure about the exact file name again)

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.

Moved to pytorch_utils.py

@dacorvo
dacorvo requested a review from Cyrilvallez October 13, 2026 14:38
@dacorvo

dacorvo commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

@vasqu @Cyrilvallez any blockers ?

@github-actions

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 38531945519:1
Result: success | Jobs: 15 | Tests: 200,517 | Failures: 0 | Duration: 15h 44m

@vasqu vasqu left a comment

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.

Should be good, sorry for the delays

@dacorvo
dacorvo added this pull request to the merge queue Oct 14, 2026
Merged via the queue into main with commit 245167e Oct 14, 2026
116 checks passed
@dacorvo
dacorvo deleted the fix-generate-dict-masks-and-tp-cache-heads branch October 14, 2026 13:36
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.

generate() with a static cache: host-side inputs miss tensors nested in dicts, and TP undersizes the early cache

5 participants