Skip to content

[None][feat] Add Qwen3-based DSpark drafter (DeepSpec dense checkpoints) - #16813

Open
chungen04 wants to merge 3 commits into
NVIDIA:mainfrom
chungen04:dspark-qwen3
Open

[None][feat] Add Qwen3-based DSpark drafter (DeepSpec dense checkpoints)#16813
chungen04 wants to merge 3 commits into
NVIDIA:mainfrom
chungen04:dspark-qwen3

Conversation

@chungen04

@chungen04 chungen04 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Dev Engineer Review

  • Added standalone Qwen3 DSpark support for deepseek-ai/dspark_qwen3_{4b,8b,14b}_block7.
  • Implemented GQA, RoPE, RMSNorm, gated-SiLU MLPs, projected context, rolling per-layer K/V windows, bidirectional block attention, proposal heads, and Markov-head refinement.
  • Reused shared DSpark worker, metadata, and CUDA-graph interfaces.
  • Added Qwen3 architecture dispatch in get_draft_model.
  • Added support for unprefixed DSpark configuration keys.
  • Centralized mask-token resolution and shared target embedding and LM-head handling.
  • Added optional confidence-head bias support.
  • No configuration or test-list files changed.
  • No correctness, API consistency, performance, error-handling, or regression issue is identified from the provided change summary.

QA Engineer Review

Added test functions:

  • test_worker_protocol_golden
  • test_prefix_reuse_masks_unseeded_rows
  • test_batched_matches_eager_singletons
  • test_ring_window_wraparound

These tests cover worker-protocol drafting, prefix reuse, batched and singleton parity, and ring-buffer wraparound. No corresponding tests/integration/test_lists/ coverage is listed. Verdict: insufficient.

Description

DSpark support today (#15808) covers only the DeepSeek-V4 drafter, whose draft weights live in the target checkpoint and in V4 blocks. DeepSeek's release (https://github.com/deepseek-ai/DeepSpec) also ships standalone dense DSpark drafters for Qwen3 targets, deepseek-ai/dspark_qwen3_{4b,8b,14b}_block7, which this PR enables. This PR also serves as a stepping stone to serve other Qwen3-based DSpark drafter, e.g. novita/kimi-k2.6-dspark

  • modeling_dspark_qwen3.py (new): Qwen3DSparkDraftModel / Qwen3DSparkForCausalLM: a pure-torch dense GQA draft backbone (fc + hidden_norm captured-context projection, Qwen3 decoder layers with per-head q/k RMSNorm and RoPE, bidirectional block attention over a per-layer context-K/V ring cache, Markov-head block refinement). It implements the same worker-facing protocol as the V4 DSparkDraftModel, so DSparkWorker, DSparkSpecMetadata, and the CUDA-graph plumbing are reused unchanged. The worker-owned rolling buffer holds per-layer context K/V as a ring over the last TRTLLM_DSPARK_QWEN3_CTX_WINDOW (default 2048) committed positions.
  • get_draft_model: dispatch on the drafter checkpoint's Qwen3DSparkModel architecture (mirrors the DFlash/Laguna pattern).
  • llm_args DSpark validation: additionally resolve the unprefixed top-level config keys (block_size / target_layer_ids / mask_token_id / markov_rank) used by the DeepSpec drafter checkpoints.

Usage:

speculative_config:
  decoding_type: DSpark
  max_draft_len: 7
  speculative_model: deepseek-ai/dspark_qwen3_8b_block7

Test Coverage

  • tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py (new): golden tests against a torch-only port of the DeepSpec reference (deepspec/modeling/dspark/qwen3/modeling.py + eval/dspark/draft_ops.py) — token-exact agreement across multi-step decode driven through the exact DSparkWorker conventions (prefill seeding, interim back-fill, frame offsets), batched-vs-singleton parity, and ring-window wraparound.
  • Existing DSpark unit tests (62) and test_llm_args.py DSpark validation tests (9) pass unchanged.
  • E2E (1xB300, 1.3.0rc22 backport of this diff): results below.
  • Benchmarks: aiperf against trtllm-serve (1xB300, bf16, greedy, chat template with thinking disabled, natural EOS, max_tokens 1024, max_batch_size 64, CUDA graphs on, overlap scheduler off for all configs), GSM8K / MATH-500 / HumanEval in the DeepSpec eval prompt format. Eagle3 baseline swept at draft length 1 and 3.

Per-user decode rate, tok/s (speedup vs vanilla):

Engine launch command:

trtllm-serve /models/Qwen3-8B --host 0.0.0.0 --port 8000 --config /bench/cfg_dspark.yaml

with cfg_dspark.yaml

max_batch_size: 64
max_seq_len: 4096
disable_overlap_scheduler: true
kv_cache_config:
  free_gpu_memory_fraction: 0.85
  enable_block_reuse: false
cuda_graph_config:
  enable_padding: true
  max_batch_size: 64
speculative_config:
  decoding_type: DSpark
  max_draft_len: 7
  speculative_model: /models/dspark_qwen3_8b_block7  

Qwen3-8B

dataset conc vanilla eagle3 len1 eagle3 len3 dspark block7
gsm8k 1 200.7 301.7 (1.50x) 367.8 (1.83x) 750.8 (3.74x)
gsm8k 4 182.4 270.5 (1.48x) 306.0 (1.68x) 488.3 (2.68x)
gsm8k 16 151.1 205.4 (1.36x) 216.5 (1.43x) 241.8 (1.60x)
gsm8k 64 88.2 107.7 (1.22x) 108.4 (1.23x) 97.1 (1.10x)
math500 1 203.7 303.0 (1.49x) 362.4 (1.78x) 749.1 (3.68x)
math500 4 188.3 274.3 (1.46x) 321.6 (1.71x) 540.2 (2.87x)
math500 16 158.6 223.1 (1.41x) 254.1 (1.60x) 300.3 (1.89x)
math500 64 106.0 138.8 (1.31x) 136.4 (1.29x) 118.2 (1.11x)
humaneval 1 200.8 302.6 (1.51x) 365.6 (1.82x) 661.4 (3.29x)
humaneval 4 185.7 267.1 (1.44x) 312.4 (1.68x) 470.6 (2.53x)
humaneval 16 157.1 212.8 (1.35x) 251.0 (1.60x) 267.6 (1.70x)
humaneval 64 108.1 137.6 (1.27x) 150.0 (1.39x) 110.3 (1.02x)

Qwen3-4B

dataset conc vanilla eagle3 len1 eagle3 len3 dspark block7
gsm8k 1 264.0 412.9 (1.56x) 512.6 (1.94x) 855.6 (3.24x)
gsm8k 4 233.2 339.2 (1.45x) 416.9 (1.79x) 532.9 (2.28x)
gsm8k 16 175.0 250.0 (1.43x) 264.2 (1.51x) 261.0 (1.49x)
gsm8k 64 97.0 119.6 (1.23x) 124.4 (1.28x) 98.3 (1.01x)
math500 1 267.0 423.9 (1.59x) 492.3 (1.84x) 886.8 (3.32x)
math500 4 243.5 353.9 (1.45x) 442.7 (1.82x) 627.1 (2.58x)
math500 16 198.6 272.1 (1.37x) 332.7 (1.68x) 331.1 (1.67x)
math500 64 126.4 158.3 (1.25x) 166.8 (1.32x) 126.1 (1.00x)
humaneval 1 267.5 397.3 (1.49x) 479.7 (1.79x) 782.4 (2.93x)
humaneval 4 242.5 351.0 (1.45x) 400.1 (1.65x) 529.2 (2.18x)
humaneval 16 194.2 252.2 (1.30x) 310.0 (1.60x) 272.6 (1.40x)
humaneval 64 125.2 175.1 (1.40x) 161.5 (1.29x) 112.1 (0.90x)

Qwen3-14B

dataset conc vanilla eagle3 len1 eagle3 len3 dspark block7
gsm8k 1 142.3 225.4 (1.58x) 291.3 (2.05x) 560.8 (3.94x)
gsm8k 4 131.0 209.0 (1.60x) 270.5 (2.06x) 431.7 (3.29x)
gsm8k 16 110.1 154.5 (1.40x) 183.1 (1.66x) 257.1 (2.34x)
gsm8k 64 72.2 90.6 (1.25x) 113.4 (1.57x) 92.3 (1.28x)
math500 1 143.4 227.7 (1.59x) 299.7 (2.09x) 574.7 (4.01x)
math500 4 137.1 210.7 (1.54x) 274.7 (2.00x) 434.0 (3.16x)
math500 16 118.4 182.6 (1.54x) 224.3 (1.89x) 265.3 (2.24x)
math500 64 84.9 121.1 (1.43x) 129.1 (1.52x) 101.2 (1.19x)
humaneval 1 142.0 218.6 (1.54x) 277.1 (1.95x) 523.0 (3.68x)
humaneval 4 135.8 198.2 (1.46x) 245.4 (1.81x) 378.5 (2.79x)
humaneval 16 118.7 169.6 (1.43x) 200.1 (1.69x) 220.8 (1.86x)
humaneval 64 84.2 113.2 (1.34x) 128.6 (1.53x) 94.8 (1.13x)

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ac5168b1-9f94-4824-9e4c-e5ab7f14ea11

📥 Commits

Reviewing files that changed from the base of the PR and between a23949d and f301332.

📒 Files selected for processing (3)
  • tensorrt_llm/_torch/models/dspark/draft.py
  • tensorrt_llm/_torch/models/modeling_dspark.py
  • tensorrt_llm/_torch/models/modeling_speculative.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tensorrt_llm/_torch/models/modeling_speculative.py
  • tensorrt_llm/_torch/models/dspark/draft.py
  • tensorrt_llm/_torch/models/modeling_dspark.py

Walkthrough

Changes

Qwen3 DSpark drafting is added with rolling K/V windows, batched execution, checkpoint loading, and shared target heads. DSpark mask-token resolution and speculative dispatch are updated. Golden tests cover protocol execution, prefix reuse, batching, and ring-buffer wraparound.

Qwen3 DSpark drafter

Layer / File(s) Summary
Model components and initialization
tensorrt_llm/_torch/models/dspark/*, tensorrt_llm/_torch/models/modeling_dspark.py
Adds Qwen3 decoder components, draft-model construction, centralized noise-token resolution, and optional confidence-head bias.
Context ring and draft decoding
tensorrt_llm/_torch/models/modeling_dspark.py
Adds projected context K/V rows, rolling-window management, graph-safe batched decoding, and Markov-refined proposals.
Wrapper, loading, and dispatch
tensorrt_llm/_torch/models/modeling_dspark.py, tensorrt_llm/_torch/models/modeling_speculative.py, tensorrt_llm/llmapi/llm_args.py
Adds shared causal-LM wrappers, Qwen3 checkpoint loading, top-level DSpark configuration fallback, and architecture-specific speculative dispatch.
Worker protocol validation
tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py
Adds deterministic reference-based tests for worker protocol drafting, prefix reuse, batched requests, and ring-buffer wraparound.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SpeculativeConfig
  participant Qwen3DSparkForCausalLM
  participant Qwen3DSparkDraftModel
  participant ContextRing
  participant ProposalHeads
  SpeculativeConfig->>Qwen3DSparkForCausalLM: architecture, block_size, mask_token_id
  Qwen3DSparkForCausalLM->>Qwen3DSparkDraftModel: construct and load checkpoint
  Qwen3DSparkDraftModel->>ContextRing: seed projected context K/V rows
  Qwen3DSparkDraftModel->>ContextRing: read valid rolling window
  Qwen3DSparkDraftModel->>ProposalHeads: produce draft hidden states
  ProposalHeads-->>Qwen3DSparkForCausalLM: proposed tokens and logits
Loading

Suggested reviewers: lori-ren, aswinvisva, yihuilu512

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the feature: Qwen3-based DSpark drafter support for DeepSpec dense checkpoints.
Description check ✅ Passed The description explains the motivation, implementation, test coverage, benchmarks, usage, and checklist for the Qwen3 DSpark feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

return self.proj(features.float()).squeeze(-1)


class Qwen3DSparkDraftModel(nn.Module):

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.

Hey @chungen04, thanks for the contribution! Is it possible to merge any of this code with the existing dspark drafter? Would be nice to consolidate all logic related to dspark drafting.

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.

@mikeiovine Thank you for the review. I am refactoring the previous modeling_dspark_qwen3.py into existing frameworks and creating the base class, class DSparkForCausalLMBase. Let me know if you have any suggestions while I refine the PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
tensorrt_llm/_torch/models/modeling_dspark.py (2)

1717-1724: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add the other cross-module public names to __all__.

modeling_speculative.get_draft_model imports count_dspark_stages from this module, and it is absent from __all__. Direct imports still work, so nothing breaks now, but the list no longer describes the module's public interface.

Based on the guideline "keep __all__ updated for public interfaces".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/models/modeling_dspark.py` around lines 1717 - 1724, Add
count_dspark_stages to the __all__ declaration in modeling_dspark.py so the
public export list includes the cross-module symbol imported by
modeling_speculative.get_draft_model. Preserve all existing public names.

Source: Coding guidelines


1462-1468: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid the explicit GQA materialization.

repeat_interleave materializes full K/V tensors at every layer and decode step. The supported PyTorch range accepts enable_gqa=True with attn_mask, but SDPA's fallback also performs repeat_interleave; the flag alone does not remove the copy. Use a tested attention path or tensor layout that consumes grouped K/V without materialization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/models/modeling_dspark.py` around lines 1462 - 1468,
Update the attention flow around scaled_dot_product_attention to avoid
materializing grouped K/V via repeat_interleave; replace the current path with
the tested attention implementation or tensor layout that directly consumes
grouped K/V while preserving attn_mask, softmax_scale, and output behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_torch/models/modeling_dspark.py`:
- Around line 1189-1191: Replace the assertion validating
self.num_capture_layers in the Qwen3 DSpark drafter configuration with an
explicit ValueError, preserving the existing message and ensuring invalid
target_layer_ids fail before self.fc is constructed.
- Around line 1505-1519: In the wrapper containing the shown initialization
logic, add an explicit validation that every normalized start_pos value is
strictly greater than zero before calling forward_batched or writing context
state. Reject scalar and batched inputs consistently, preserving valid positive
positions and failing immediately for any start_pos <= 0.
- Around line 1329-1338: Constrain the interim back-fill around the
stage_windows write in the layer loop so max_draft_len never exceeds the
configured window_size, or explicitly handle wrapped positions without duplicate
indexed slots. Preserve the existing absolute-position conversion and ensure
each stage_windows slot receives the correct context when interim rows exceed
one window.

In `@tensorrt_llm/_torch/speculative/dspark.py`:
- Around line 250-271: Initialize `_batch_to_slot` in the DSpark setup alongside
`_dummy_slot` using the scratch-slot index rather than zero. Preserve updates
for real request slots during preparation, and add a regression test covering
the first forward call with mixed context and generation rows before
`_dspark_worker` is assigned.

---

Nitpick comments:
In `@tensorrt_llm/_torch/models/modeling_dspark.py`:
- Around line 1717-1724: Add count_dspark_stages to the __all__ declaration in
modeling_dspark.py so the public export list includes the cross-module symbol
imported by modeling_speculative.get_draft_model. Preserve all existing public
names.
- Around line 1462-1468: Update the attention flow around
scaled_dot_product_attention to avoid materializing grouped K/V via
repeat_interleave; replace the current path with the tested attention
implementation or tensor layout that directly consumes grouped K/V while
preserving attn_mask, softmax_scale, and output behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f346db2e-39b7-4de1-84d0-38ae3cdb06d6

📥 Commits

Reviewing files that changed from the base of the PR and between 5476284 and 28ea78f.

📒 Files selected for processing (7)
  • tensorrt_llm/_torch/models/dspark/draft.py
  • tensorrt_llm/_torch/models/dspark/heads.py
  • tensorrt_llm/_torch/models/modeling_dspark.py
  • tensorrt_llm/_torch/models/modeling_speculative.py
  • tensorrt_llm/_torch/speculative/dspark.py
  • tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py
  • tests/unittest/_torch/speculative/hw_agnostic/test_dspark_worker.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/_torch/models/modeling_speculative.py

Comment on lines +1189 to +1191
assert self.num_capture_layers > 0, (
"Qwen3 DSpark drafter config must provide target_layer_ids"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Raise ValueError instead of asserting on the config.

python -O removes the assertion. Without target_layer_ids, self.num_capture_layers becomes 0 and self.fc is built with in_features=0, so the failure surfaces later as a confusing shape error. Raise an explicit exception for the invalid configuration.

🛠️ Proposed fix
-        assert self.num_capture_layers > 0, (
-            "Qwen3 DSpark drafter config must provide target_layer_ids"
-        )
+        if self.num_capture_layers == 0:
+            raise ValueError("Qwen3 DSpark drafter config must provide target_layer_ids")

Based on the guideline "raise ValueError rather than assertions".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert self.num_capture_layers > 0, (
"Qwen3 DSpark drafter config must provide target_layer_ids"
)
if self.num_capture_layers == 0:
raise ValueError("Qwen3 DSpark drafter config must provide target_layer_ids")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/models/modeling_dspark.py` around lines 1189 - 1191,
Replace the assertion validating self.num_capture_layers in the Qwen3 DSpark
drafter configuration with an explicit ValueError, preserving the existing
message and ensuring invalid target_layer_ids fail before self.fc is
constructed.

Source: Coding guidelines

Comment thread tensorrt_llm/_torch/models/modeling_dspark.py
Comment thread tensorrt_llm/_torch/models/modeling_dspark.py
Comment on lines +250 to +271
# Reserve slot index max_batch as a scratch slot for cuda-graph padding
# rows and warmup dummies; real requests only draw slots 0..max_batch-1,
# so their windows can never be written by a padded row (which the
# per-step context write would otherwise do, unmasked, through the
# duplicated slot index).
self._dummy_slot = max_batch
num_slots = max_batch + 1

self._kv_windows = torch.zeros(
(max_batch, num_stages, self._win, head_dim),
(num_slots, num_stages, self._win, head_dim),
dtype=torch.bfloat16,
device="cuda",
)
self._ctx_len = torch.zeros(max_batch, dtype=torch.long, device="cuda")
self._ctx_len = torch.zeros(num_slots, dtype=torch.long, device="cuda")
self._batch_to_slot = torch.zeros(max_batch, dtype=torch.long, device="cuda")
self._free_slots = deque(range(max_batch))
self._req_to_slot = {}
self._win_inited = True
logger.info(
f"DSpark: allocated rolling KV windows "
f"[{max_batch}, {num_stages}, {self._win}, {head_dim}]"
f"[{num_slots}, {num_stages}, {self._win}, {head_dim}] "
f"({max_batch} request slots + 1 scratch)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 \
  '_dspark_worker\s*=|\.prepare\(\)|_lazy_init\(' \
  tensorrt_llm/_torch/speculative \
  tests/unittest/_torch/speculative

Repository: NVIDIA/TensorRT-LLM

Length of output: 28812


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline tensorrt_llm/_torch/speculative/dspark.py
rg -n -C 12 \
  'class DSparkSpecMetadata|def prepare\(|def _forward_impl|def forward\(|_dspark_worker|prepare\(\)' \
  tensorrt_llm/_torch/speculative/dspark.py \
  tensorrt_llm/_torch/speculative/speculative_interface.py \
  tensorrt_llm/_torch/speculative/speculative_decoding.py \
  tensorrt_llm/_torch/speculative/drafting_loops.py \
  tests/unittest/_torch/speculative/hw_agnostic/test_dspark_worker.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 35293


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,230p' tensorrt_llm/_torch/speculative/dspark.py
sed -n '230,520p' tensorrt_llm/_torch/speculative/dspark.py
sed -n '1,180p' tensorrt_llm/_torch/speculative/drafting_loops.py
sed -n '1,330p' tests/unittest/_torch/speculative/hw_agnostic/test_dspark_worker.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 44693


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 10 \
  'DSparkSpecMetadata|DSparkWorker|spec_metadata\.prepare|draft_model.*forward|worker.*forward|forward\(.*spec_metadata' \
  tensorrt_llm tests \
  -g '*.py' | head -n 1200

Repository: NVIDIA/TensorRT-LLM

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 18 'spec_metadata\.prepare\(\)|\.prepare\(\).*spec_metadata|prepare_spec|prepare\(' \
  tensorrt_llm/_torch/model_engine.py \
  tensorrt_llm/_torch/models/modeling_speculative.py \
  tensorrt_llm/_torch/speculative \
  tensorrt_llm | rg -v '(^|/)(test|tests)/' | head -n 1200

sed -n '1960,2085p' tensorrt_llm/_torch/models/modeling_speculative.py
sed -n '1020,1110p' tensorrt_llm/_torch/speculative/interface.py
sed -n '430,550p' tensorrt_llm/_torch/speculative/dflash.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '480,545p' tensorrt_llm/_torch/speculative/model_drafter.py
rg -n -C 25 'spec_worker\.forward|self\.spec_worker\(|spec_worker' \
  tensorrt_llm/_torch/models/modeling_speculative.py \
  tensorrt_llm/_torch/model_engine.py \
  tensorrt_llm/_torch | head -n 1400

rg -n -C 12 'def forward\(|_forward_impl\(' tensorrt_llm/_torch/speculative/interface.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '490,535p' tensorrt_llm/_torch/speculative/model_drafter.py
rg -n -C 20 'spec_worker\.forward|spec_worker\(' tensorrt_llm/_torch --glob '*.py'
rg -n -C 20 'def forward\(|_forward_impl\(' tensorrt_llm/_torch/speculative/interface.py | head -n 500

Repository: NVIDIA/TensorRT-LLM

Length of output: 29021


Initialize _batch_to_slot with _dummy_slot
should_forward_draft_model() skips DSpark’s first context chunk, so the first worker call can contain both context and generation rows. prepare() cannot map these rows before _dspark_worker is assigned; zero-initialization can route a generation row to a real context slot. Add a first-forward mixed-batch regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/speculative/dspark.py` around lines 250 - 271, Initialize
`_batch_to_slot` in the DSpark setup alongside `_dummy_slot` using the
scratch-slot index rather than zero. Preserve updates for real request slots
during preparation, and add a regression test covering the first forward call
with mixed context and generation rows before `_dspark_worker` is assigned.

chungen04 and others added 2 commits August 5, 2026 18:34
Support the DeepSpec-released dense DSpark drafters for Qwen3 targets
(e.g. deepseek-ai/dspark_qwen3_8b_block7) alongside the existing
DeepSeek-V4 mtp.*-namespace drafter:

- modeling_dspark_qwen3.py: Qwen3DSparkDraftModel/Qwen3DSparkForCausalLM,
  a pure-torch dense GQA draft backbone (fc+hidden_norm context projection,
  Qwen3 layers with captured-context K/V and bidirectional block attention,
  Markov head refinement) implementing the same worker-facing protocol as
  DSparkDraftModel, so DSparkWorker / DSparkSpecMetadata / CUDA-graph
  plumbing are reused unchanged. The worker rolling buffer holds per-layer
  context K/V as a ring over the last TRTLLM_DSPARK_QWEN3_CTX_WINDOW
  (default 2048) committed positions.
- get_draft_model: dispatch on the drafter checkpoint's Qwen3DSparkModel
  architecture.
- llm_args DSpark validation: also resolve unprefixed top-level config
  keys (block_size / target_layer_ids / mask_token_id / markov_rank) used
  by the DeepSpec drafter checkpoints (no schema change; golden manifest
  unchanged).
- tests: golden tests vs a torch-only port of the DeepSpec reference
  (worker frame conventions across multi-step decode, batched-vs-singleton
  parity, ring wraparound).

Validation: golden unit tests vs a torch-only DeepSpec reference port;
end-to-end + GSM8K/MATH-500/HumanEval benchmarks (aiperf, 1xB300,
conc 1..64) were run on the v1.3.0rc22 backport of this change (branch
dspark-qwen3-rc22, same diff) inside the 1.3.0rc22 release container:
3.3-3.7x per-user speedup over vanilla at concurrency 1
(~750 tok/s/user), 6.17 avg decoded tokens/iter on GSM8K (block=7).

Signed-off-by: chungen04 <cho322@gatech.edu>
…ramework

Fold modeling_dspark_qwen3.py into the shared DSpark modeling path instead of
keeping a parallel Qwen3-specific implementation, so drafter construction,
heads and worker plumbing go through one code path.

Signed-off-by: chungen04 <b09901027@ntu.edu.tw>
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py (4)

332-334: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the unused weights unpack.

test_batched_matches_eager_singletons never uses weights. Ruff reports RUF059 on Line 334. If RUF rules gate CI, this fails the lint stage.

♻️ Proposed fix
-    weights, model, device = setup
+    _, model, device = setup
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py` around
lines 332 - 334, Remove the unused weights unpacking from
test_batched_matches_eager_singletons by binding only the model and device
values returned by setup, while preserving the test’s existing behavior.

Source: Linters/SAST tools


395-469: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse _Ref.draft instead of duplicating it inline.

Lines 416-469 re-implement _Ref.draft verbatim. _WrapRef adds nothing over _Ref. _Ref.draft already derives pos_c from first_pos, so first_pos = start_pos - win produces exactly the same context positions (total+1-win .. total) and satisfies the internal length assertion. The duplicated block will drift from _Ref when the reference path changes.

♻️ Proposed fix
-    # Reference limited to the last `win` context positions.
-    ref = _Ref(weights, device)
-    ref.append_ctx(h)
-    ref.ctx_x = ref.ctx_x[-win:]
-
     bonus = torch.tensor([42], device=device)
     start = torch.tensor([total + 1], device=device)
     main = _rand_hidden(gen, 1).to(device)
-    # keep ref in sync: main_hidden row is position `total` (= start-1)
+
+    # Reference limited to the last `win` context positions; the main_hidden
+    # row is position `total` (= start-1).
+    ref = _Ref(weights, device)
+    ref.append_ctx(h)
     ref.append_ctx(main)
     ref.ctx_x = ref.ctx_x[-win:]
+    ref.first_pos = int(start[0]) - win
 
     got_toks, _, got_logits = model.forward_batched(
         main,
         bonus,
         start,
         kv_windows=kv_windows,
         slots=torch.tensor([0], device=device),
         return_logits=True,
     )
-
-    # Reference drafts with positions: ctx = last `win` absolute positions.
-    class _WrapRef(_Ref):
-        pass
-
-    wref = _WrapRef(weights, device)
-    wref.ctx_x = ref.ctx_x
-    # override position bookkeeping: ctx positions are total+1-win .. total
-    w = wref.w
-    ids = torch.full((BLOCK,), MASK_ID, dtype=torch.long, device=device)
-    ids[0] = bonus[0]
-    hh = F.embedding(ids, w["embed_tokens.weight"])
-    pos_q = start[0] + torch.arange(BLOCK, device=device)
-    pos_c = torch.arange(start[0] - win, start[0], device=device)
-    for i in range(N_LAYERS):
-        p = f"layers.{i}."
-        x = wref._norm(hh, p + "input_layernorm.weight")
-        q = F.linear(x, w[p + "self_attn.q_proj.weight"]).view(BLOCK, N_HEADS, HEAD_DIM)
-        q = wref._norm(q, p + "self_attn.q_norm.weight")
-        q = _apply_rope(q, wref.cos[pos_q].to(DTYPE), wref.sin[pos_q].to(DTYPE))
-        src = torch.cat([wref.ctx_x, x], dim=0)
-        pos_k = torch.cat([pos_c, pos_q])
-        k = F.linear(src, w[p + "self_attn.k_proj.weight"]).view(-1, N_KV_HEADS, HEAD_DIM)
-        k = wref._norm(k, p + "self_attn.k_norm.weight")
-        k = _apply_rope(k, wref.cos[pos_k].to(DTYPE), wref.sin[pos_k].to(DTYPE))
-        v = F.linear(src, w[p + "self_attn.v_proj.weight"]).view(-1, N_KV_HEADS, HEAD_DIM)
-        rep = N_HEADS // N_KV_HEADS
-        kk = k.transpose(0, 1).repeat_interleave(rep, dim=0)
-        vv = v.transpose(0, 1).repeat_interleave(rep, dim=0)
-        o = F.scaled_dot_product_attention(q.transpose(0, 1), kk, vv, scale=HEAD_DIM**-0.5)
-        o = o.transpose(0, 1).reshape(BLOCK, N_HEADS * HEAD_DIM)
-        hh = hh + F.linear(o, w[p + "self_attn.o_proj.weight"])
-        x = wref._norm(hh, p + "post_attention_layernorm.weight")
-        mlp = F.linear(
-            F.silu(F.linear(x, w[p + "mlp.gate_proj.weight"]))
-            * F.linear(x, w[p + "mlp.up_proj.weight"]),
-            w[p + "mlp.down_proj.weight"],
-        )
-        hh = hh + mlp
-    hh = wref._norm(hh, "norm.weight")
-    base = F.linear(hh, w["lm_head.weight"])
-    toks, logits = [], []
-    prev = bonus.long()
-    for kstep in range(BLOCK):
-        bias = F.linear(
-            F.embedding(prev, w["markov_head.markov_w1.weight"]), w["markov_head.markov_w2.weight"]
-        )
-        step = base[kstep : kstep + 1] + bias
-        logits.append(step)
-        prev = step.argmax(dim=-1)
-        toks.append(prev)
-    torch.testing.assert_close(
-        got_logits[0].float(), torch.cat(logits).float(), atol=0.05, rtol=0.05
-    )
-    assert torch.equal(got_toks[0].long(), torch.cat(toks).long())
+    exp_toks, exp_logits = ref.draft(bonus[0], int(start[0]))
+    torch.testing.assert_close(got_logits[0].float(), exp_logits.float(), atol=0.05, rtol=0.05)
+    assert torch.equal(got_toks[0].long().cpu(), exp_toks.long().cpu())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py` around
lines 395 - 469, Replace the inline reference forward computation and no-op
_WrapRef subclass with a call to _Ref.draft, passing the existing context, bonus
tokens, and first_pos derived as start[0] - win. Preserve the current logits and
token comparisons while relying on _Ref.draft to derive the context positions
and validate the window length.

372-373: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use explicit tolerances for the batched-vs-singleton logit comparison.

This call relies on the default assert_close tolerances, while every other logit assertion in this file uses atol=0.05, rtol=0.05. Batched and singleton calls use different tensor shapes, so GEMM reduction order can differ and produce small numeric drift. Set explicit tolerances to keep the test stable.

♻️ Proposed fix
-    torch.testing.assert_close(got_logits, torch.cat(exp_logits, dim=0))
+    torch.testing.assert_close(
+        got_logits.float(), torch.cat(exp_logits, dim=0).float(), atol=0.05, rtol=0.05
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py` around
lines 372 - 373, Update the torch.testing.assert_close call comparing got_logits
with torch.cat(exp_logits, dim=0) to pass explicit atol=0.05 and rtol=0.05,
matching the other logit assertions in this test file while leaving the token
comparison unchanged.

229-469: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Test coverage summary.

  • test_worker_protocol_golden — added and registered.
  • test_prefix_reuse_masks_unseeded_rows — added and registered.
  • test_batched_matches_eager_singletons — added and registered.
  • test_ring_window_wraparound — added and registered.

The module is listed in tests/integration/test_lists/test-db/l0_cpu.yml and tests/integration/test_lists/test-db/l0_h100.yml. Coverage verdict: sufficient.

Consider adding assertions for return_logits=False and confidence-head outputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py` around
lines 229 - 469, Extend the existing forward_batched coverage in
test_worker_protocol_golden or test_batched_matches_eager_singletons to exercise
return_logits=False and verify its returned confidence-head outputs against the
corresponding return_logits=True behavior. Preserve the current token assertions
and ensure both batched and singleton paths remain consistent.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py`:
- Around line 332-334: Remove the unused weights unpacking from
test_batched_matches_eager_singletons by binding only the model and device
values returned by setup, while preserving the test’s existing behavior.
- Around line 395-469: Replace the inline reference forward computation and
no-op _WrapRef subclass with a call to _Ref.draft, passing the existing context,
bonus tokens, and first_pos derived as start[0] - win. Preserve the current
logits and token comparisons while relying on _Ref.draft to derive the context
positions and validate the window length.
- Around line 372-373: Update the torch.testing.assert_close call comparing
got_logits with torch.cat(exp_logits, dim=0) to pass explicit atol=0.05 and
rtol=0.05, matching the other logit assertions in this test file while leaving
the token comparison unchanged.
- Around line 229-469: Extend the existing forward_batched coverage in
test_worker_protocol_golden or test_batched_matches_eager_singletons to exercise
return_logits=False and verify its returned confidence-head outputs against the
corresponding return_logits=True behavior. Preserve the current token assertions
and ensure both batched and singleton paths remain consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3fb8a693-9ae5-4dd7-8b2a-c86cd5eff1e8

📥 Commits

Reviewing files that changed from the base of the PR and between 5533f66 and a23949d.

📒 Files selected for processing (6)
  • tensorrt_llm/_torch/models/dspark/draft.py
  • tensorrt_llm/_torch/models/dspark/heads.py
  • tensorrt_llm/_torch/models/modeling_dspark.py
  • tensorrt_llm/_torch/models/modeling_speculative.py
  • tensorrt_llm/llmapi/llm_args.py
  • tests/unittest/_torch/speculative/hw_agnostic/test_dspark_qwen3.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • tensorrt_llm/llmapi/llm_args.py
  • tensorrt_llm/_torch/models/dspark/draft.py
  • tensorrt_llm/_torch/models/dspark/heads.py
  • tensorrt_llm/_torch/models/modeling_speculative.py

Signed-off-by: chungen04 <b09901027@ntu.edu.tw>
@zhaoyuanh-nvidia

Copy link
Copy Markdown
Collaborator

There's no CI accuracy gate on the real checkpoint. Could we add a single-GPU test mirroring test_dflash (tests/integration/defs/accuracy/test_llm_api_pytorch.py:656), plus a test-list entry? The existing TestDeepSeekV4ProDSpark test needs 8 GPUs, but a Qwen3-8B drafter fits the standard single-GPU harness.

self._build_rope_tables()

# Ring-window length for the worker-owned context K/V buffer.
window = int(os.environ.get(_CTX_WINDOW_ENV, _DEFAULT_CTX_WINDOW))

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.

Is this required? Can we make it flow through the decoding config?

@zhaoyuanh-nvidia

Copy link
Copy Markdown
Collaborator

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64645 [ run ] triggered by Bot. Commit: f301332 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64645 [ run ] completed with state SUCCESS. Commit: f301332
/LLM/main/L0_MergeRequest_PR pipeline #52505 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@chungen04

Copy link
Copy Markdown
Contributor Author

Looking into the context length argument issue, I found another thing that worth a revisit.

The code I made earlier is trying to indicate the context window for the DSpark speculator, which is assigned to 128 in DSv4 (see, for example, https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash-0731/blob/main/config.json, "sliding_window": 128). However, in the DeepSpec checkpoints (for example, https://huggingface.co/deepseek-ai/dspark_qwen3_14b_block7/blob/main/config.json), it is indicated "use_sliding_window": false and "sliding_window": null. Shipping DeepSpec checkpoints and other DSpark checkpoints support (ex. https://huggingface.co/novita/kimi-k2.6-dspark) with sliding window might cause performance degradation.

Yet, in an earlier issue and PR also made by me (#16005, #16150 ), I pointed that since current drafter context is a contiguous buffer rather than paged, the buffer can be too large for long max_seq_len or batch size configured, limiting the KV memory pool and request admission with high concurrency. DSv4's DSpark drafter is fine since the context of drafter is using sliding window, so the size of the drafter context only scales with the maximum batch size.

So the support of DSpark drafter without sliding window is related to #16005, #16150, while that PR requires a mid-size code change.

I would like to know the feedbacks in the upstream team regarding this. Possible directions and design choices include:

  • Limiting the DSpark drafters' context window: the performance of the drafter without the intended "use_sliding_window": false has to be justified.
  • Support full context for DSpark drafter with contiguous buffer, following current DSpark drafter and DFlash drafter's design: Causing large memory waste due to the contiguous buffer as reported in [Feature]: Paged / KV-manager-resident storage for the DFlash draft context, replacing the dense buffer #16005.
  • Support paged context for DSpark and DFlash drafter: this requires a mid-size code scope change.

cc @zhaoyangwang-nvidia as you reviewed #16150, would appreciate your suggestions.

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

Approving — the comments below are optional touch-ups, not blockers.

Solid addition — the golden tests against a torch-only DeepSpec reference port are exactly the right coverage for a pure-torch drafter, and I verified the worker-side contracts the new model relies on (_seed_context_windows slices each chunk to min(win, chunk_len), so write_context_windows never sees duplicate ring indices in one scatter; interim back-fill frames and the bonus row are disjoint, so the masked torch.where scatter in write_context_windows_batched is well-defined). Note the new test file needs no test-list change: unittest/_torch/speculative/hw_agnostic is already registered as a directory in l0_cpu.yml and l0_h100.yml, so the CodeRabbit "insufficient test-list coverage" note in the description is wrong.

Two whole-PR notes:

  • TRTLLM_DSPARK_QWEN3_CTX_WINDOW is user-facing but undocumented — it appears only in the PR description. DSpark has no feature docs yet, so there's no obvious home, but at minimum a mention wherever DSpark docs eventually land, including the memory cost: the worker allocates (max_batch+1) × num_layers × window × 2·kv_dim bf16, which for a multi-layer drafter at window 2048 is nontrivial per batch slot.
  • The inline comments are all robustness/diagnosability items (out-of-range noise-token fallback, silent RoPE clamp past max_position_embeddings, unguarded env-var parse, config-key fallback scope) — none block merge given the E2E validation.

if mask_token_id is None:
mask_token_id = getattr(config, ckpt_attr, None)
if mask_token_id is None:
mask_token_id = config.vocab_size

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.

The config.vocab_size fallback produces an out-of-range token id for the Qwen3 drafter: embed_tokens is the target's nn.Embedding(vocab_size, hidden), so embedding id vocab_size triggers a device-side assert deep inside the first draft forward — a hard-to-diagnose failure mode. Since the DeepSpec checkpoints always carry mask_token_id, raising a clear ValueError when both the spec config and the checkpoint attribute are missing would fail fast with an actionable message. (The fallback predates this PR on the V4 path, but now that the helper is shared it's worth hardening.) Separately, the docstring's first sentence is truncated: "either indicated in DSparkDecodingConfig validation" doesn't parse.

def _gather_cos_sin(self, positions: torch.Tensor, dtype: torch.dtype):
# Clamp for graph-safety: masked-out entries may carry arbitrary
# (already clamped by the worker) positions.
p = positions.long().clamp(min=0, max=self._freqs_cap - 1)

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.

Positions past _freqs_cap - 1 (≈ max_position_embeddings, 40960 for these checkpoints) silently clamp to the last RoPE row, so once a sequence runs past the drafter's trained range every draft query/context position collapses to the same rotary phase. Output stays correct via target verification, so this shows up only as an unexplained acceptance-rate/perf cliff at long context. A warning inside this method isn't graph-safe, but a one-time construction-time warning when the serving max sequence length exceeds the drafter's max_position_embeddings would make the cliff attributable. Related: only rope_theta is read here — a rope_scaling/YaRN entry in a drafter config would be silently ignored; worth asserting it's absent.

# DeepSpec-released dense drafter checkpoints
# (e.g. Qwen3DSparkModel) use unprefixed top-level
# keys in their own config.json.
value = draft_cfg.get(key)

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.

This unprefixed fallback runs for every DSpark checkpoint, not just the DeepSpec dense drafters: a V4-style checkpoint that happens to carry an unrelated top-level block_size or mask_token_id (both plausible generic key names) would silently adopt it — best case a confusing block_size != max_draft_len validation error, worst case a wrong mask token that only degrades acceptance. Consider gating this branch on the checkpoint's architectures containing Qwen3DSpark, mirroring the dispatch in get_draft_model.

self._build_rope_tables()

# Ring-window length for the worker-owned context K/V buffer.
window = int(os.environ.get(_CTX_WINDOW_ENV, _DEFAULT_CTX_WINDOW))

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.

int(os.environ.get(...)) raises a bare ValueError on a non-integer value, and the next line silently clamps out-of-range values into [block_size + 2, max_position_embeddings]. A logger.warning when the clamp changes the requested value (and a clearer error on unparseable input) would save a user who sets the env var and wonders why it had no effect. The env var itself is user-facing and currently documented only in the PR description.

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.

5 participants