Skip to content

feat(dflash): native gpt-oss DFlash speculative decoding (engine-integrated, greedy, resident) - #135

Closed
drunkcoding wants to merge 14 commits into
feat/dflash-spec-decodefrom
feat/dflash-native-integration
Closed

feat(dflash): native gpt-oss DFlash speculative decoding (engine-integrated, greedy, resident)#135
drunkcoding wants to merge 14 commits into
feat/dflash-spec-decodefrom
feat/dflash-native-integration

Conversation

@drunkcoding

Copy link
Copy Markdown
Contributor

Native gpt-oss DFlash speculative decoding (engine-integrated, greedy, resident)

Implements the RFC (PR #131) natively: replaces the passthrough DFlashSpeculator.generate() with a native draft → verify → rollback loop wired into GenerationEngine via a flag-gated strategy seam. Builds on the existing feat/dflash-spec-decode scaffold; drafter loaded via trust_remote_code.

What's included (13 commits)

Area Change
Engine seam Extract _generate_standard + SpecDecodeStrategy protocol in engine/generation_loop.pyspec-off path is a verbatim extraction, byte-identical.
Forward/capture _native_model_forward_rich (on-device (logits, hidden, past_kv)) + extract_context_feature (5-layer [1,9,17,25,33]) in entrypoints/big_modeling.py.
Accept-rule ops Pure build_block / acceptance_length / committed_tokens in spec_decode/_dflash_ops.py.
Drafter loader Contract asserts (dim/vocab/mask/block/layer-ids), shared embed_tokens/lm_head by reference.
Native loop DFlashSpeculator.generate() — bonus emitted-not-cached, full-logits verify, non-causal drafter, dual-cache rollback, suffix-only hidden refresh.
Edge cases EOS mid-block, max_new_tokens boundary, accept 0/9, batch==1 guard.
Engine wiring MoE.generate(..., speculative_draft=...) → routes greedy/batch-1 through the native strategy; no drafter ⇒ standard path (byte-identical).
Tests + docs tiny-model E2E parity, spec-off regression, GPU-gated 120B harness, docs/dflash.md, example.

Notable deviation from the RFC (Oracle-reviewed)

The RFC assumed DynamicCache.crop() for KV rollback. This is invalid for gpt-oss's sliding-window attention (transformers 5.12 DynamicSlidingWindowLayer evicts prefix KV and refuses to crop once the window is saturated). Replaced with a snapshot-before-verify + rebuild-after-accept rollback for sliding layers (crop() retained for full-attention layers). Requires block_size ≤ sliding_window − 1 (real gpt-oss: 10 ≤ 128 ✓); the tiny test fixture window was raised 8 → 128 accordingly.

Testing

  • Autonomous (CPU, tiny model)pytest tests/python/dflash -q103 passed, 2 skipped.
    • Losslessness gate: test_native_e2e.py — native DFlash == plain greedy, token-identical (engine path, ≥64 tokens, multi-prompt).
    • test_spec_off_regression.py — spec-off byte-identical to the committed baseline.
      • accept-rule, capture, drafter-contract, edge-cases, engine-wire, native-step parity.
  • GPU-gated (120B)test_gpu_120b.py skips cleanly; run with MOE_DFLASH_GPU=1 + cached checkpoints (TP=2 SM120) for agreement-rate / acceptance-length / tok-s.
  • ruff (CI-pinned 0.6.9): clean. generation_loop.py non-spec path only extracted, not altered.

Scope (v1)

Greedy + resident + batch==1 + sync path. Deferred: sampled speculative decoding, async serving, and expert-offload/prefetch coupling (offload remains a tunable knob via device_memory_ratio, not coupled to the spec loop in v1).

Base: this PR stacks on the feat/dflash-spec-decode scaffold branch. An unrelated in-progress GLM FP8 commit that was present on the local branch is excluded from this PR.

🤖 Generated with opencode

@drunkcoding

Copy link
Copy Markdown
Contributor Author

Closing as superseded by #139 (already merged into feat/dflash-spec-decode).

#139 (tracks-abcd) was built on top of this branch's commits and extended them substantially. After merging #139 first, feat/dflash-spec-decode now contains patch-equivalents of all 14 commits here, plus the newer work (sampled decoding, route-ahead prefetch, serving integration, batched decoding).

Verification that this branch is a strict subset:

  • git cherry feat/dflash-spec-decode feat/dflash-native-integration → all 14 commits already patch-present (-)
  • Files unique to this branch: none
  • Files spec-decode has that this branch lacks: 18 (_dflash_sample_ops.py, _prefetch_route.py, _route_ahead_ctx/stats.py, serving/spec_state.py, serving/spec_verify.py, + 13 test files)
  • Net tree diff: 475 insertions vs 6,741 deletions (spec-decode is the superset)

The merge 'conflict' was git seeing two independent copies of the same feature line (common ancestor predates all DFlash work → add/add conflicts). Resolving it would either be a no-op or revert #139's newer work. No merge/rebase needed.

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.

1 participant