Skip to content

[OpenVINO] Export DFlash drafts for PagedAttention and continuous batching - #2096

Merged
rkazants merged 10 commits into
huggingface:mainfrom
ofirzaf:dflash-cb-ir
Nov 6, 2026
Merged

rkazants merged 10 commits into
huggingface:mainfrom
ofirzaf:dflash-cb-ir

Conversation

@ofirzaf

@ofirzaf ofirzaf commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Changes the DFlash draft export (DFlash, and DeepSpec / DSpark drafts with markov_rank == 0) so the draft runs on PagedAttention in OpenVINO GenAI continuous batching. This replaces the continuous-batching PoC in #2028.

Changes to the exported draft:

  • Per-row inputs: inputs_embeds, hidden_states, position_ids and the new token_type_ids input all cover the new rows of a step, [context; block]. token_type_ids is 0 for context rows, which read the target hidden states, and 1 for the seed + mask block rows, which read their embeddings.
  • Attention: standard Qwen3 attention with the plain KV-cache update. The mask is built from token_type_ids: causal, bidirectional inside the block, and sliding-window for the sliding layers. SDPAToPagedAttention converts it like any decoder attention: token_type_ids reaches every PagedAttentionExtension, and each layer gets its own sliding window.
  • Cache: the committed-prefix cache helpers, the target/block split inside attention, and the manual cache slicing are removed. The block rows are stored in the cache, and the next step starts at the committed length and overwrites them.
  • Compute: only the block rows get queries, norms, o_proj and the MLP. Context rows only produce keys and values. last_hidden_state covers all rows and is zero in the context rows.
  • DSpark: Qwen3DSparkModel drafts with markov_rank == 0 now export through the same Qwen3DFlashForCausalLM.
  • Requires Support DFlash and DSpark drafts through continuous batching on PagedAttention openvinotoolkit/openvino.genai#4790 for GenAI testing
  1. Export the draft model:
optimum-cli export openvino -m z-lab/Qwen3.5-9B-DFlash --task text-generation-with-past --trust-remote-code --weight-format int4 Qwen3.5-9B-DFlash
  1. Export the main model:
optimum-cli export openvino -m Qwen/Qwen3.5-9B --weight-format int4 Qwen3.5-9B
  1. Run speculative decoding with OpenVINO GenAI:
import openvino_genai

draft = openvino_genai.draft_model("./Qwen3.5-9B-DFlash", "GPU")
pipe = openvino_genai.VLMPipeline("./Qwen3.5-9B", "GPU", draft_model=draft)

config = openvino_genai.GenerationConfig(max_new_tokens=256, num_assistant_tokens=7)
print(pipe.generate("Explain speculative decoding in one paragraph.", generation_config=config))

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you make sure to update the documentation with your changes?
  • Did you write any new necessary tests?

@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.

@echarlaix

Copy link
Copy Markdown
Collaborator

@ofirzaf

ofirzaf commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Hi @echarlaix pushed a fix. Thanks!

ofirzaf added a commit to ofirzaf/openvino.genai that referenced this pull request Nov 2, 2026
The DFlash drafts need the export from huggingface/optimum-intel#2096, which the pinned optimum-intel
does not have, and the VLM DFlash tests need Transformers 5.2. As for the Qwen3.5 MTP tests, a
separate job on Linux, manylinux and Windows installs both and runs the continuous-batching and VLM
DFlash tests; the API and VLM jobs skip them.

Co-authored-by: Cursor <cursoragent@cursor.com>
@echarlaix

Copy link
Copy Markdown
Collaborator

@ofirzaf this PR has merge conflicts with main, could you rebase/merge and resolve them

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	optimum/exporters/openvino/__main__.py
#	optimum/exporters/openvino/model_patcher.py
#	tests/openvino/test_genai.py
@ofirzaf

ofirzaf commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Updated the branch. Please review @echarlaix

@MaximProshin

Copy link
Copy Markdown
Contributor

please don't merge it before #2062

ofirzaf and others added 2 commits November 5, 2026 04:08
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	optimum/exporters/openvino/model_configs.py
#	optimum/exporters/openvino/model_patcher.py
Qwen3DSparkForCausalLM now subclasses Qwen3DFlashForCausalLM and only adds the
Markov head when markov_rank > 0, so DSpark drafts export the same per-row
backbone as DFlash drafts. The backbone emits every row and no longer drops the
anchor row for sample_from_anchor=False: dflash/candidate_position_offset (0 or
1) tells the runtime which rows are candidates. The Markov head model is
unchanged. Qwen3DSparkModel maps back to Qwen3DSparkForCausalLM, and the test
of the removed anchor-row drop is deleted.

Co-authored-by: Igor Margulis <igor.margulis@intel.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread tests/openvino/test_transformations.py Resolved
@ofirzaf

ofirzaf commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Preview models support validation test failed because nightly GenAI doesn't have the new DFlash pipeline merged yet openvinotoolkit/openvino.genai#4790

@rkazants

rkazants commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Preview models support validation test failed because nightly GenAI doesn't have the new DFlash pipeline merged yet openvinotoolkit/openvino.genai#4790

please skip temporarily this test case with a comment.

The preview models workflow runs it against the GenAI nightly, which still has the stateful DFlash draft and cannot run the per-row draft export.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ofirzaf

ofirzaf commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

@rkazants pushed a commit to skip the test with a comment that we can enable it once the genai PR lands in nightly

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.

can you also update test_cb_compatible.py to check that dflash exported model is continous batching compatible and SDPAToPA transformation is applied?

@rkazants
rkazants merged commit f09a56a into huggingface:main Nov 6, 2026
31 of 33 checks passed
ofirzaf added a commit to ofirzaf/openvino.genai that referenced this pull request Nov 8, 2026
huggingface/optimum-intel#2096 merged as f09a56ac, so the DFlash job installs that commit instead of the PR
branch. The branch had pulled in optimum-intel main, where Qwen3.5 export needs transformers>=5.15, so the
VLM DFlash tests failed to export with transformers 5.2.0; the job now installs transformers 5.15.1.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants