Skip to content

feat(tito): add DeepSeek V3.2 speculative session verification - #1628

Merged
guapisolo merged 7 commits into
mainfrom
fix/dsv32-thinking-parser-mode
Jul 26, 2026
Merged

feat(tito): add DeepSeek V3.2 speculative session verification#1628
guapisolo merged 7 commits into
mainfrom
fix/dsv32-thinking-parser-mode

Conversation

@guapisolo

@guapisolo guapisolo commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

DeepSeek V3.2 TITO session verification plus a vendored encoder that keeps historical thinking.

Motivation

Add a manual TP8/EP8 session test and expose the fixed EAGLE serving recipe through ModelConfig. Keep SGLang TP/DP/PP/EP namespace fields on Miles' short-name contract, and forward the effective V3.2 thinking mode so SGLang separates reasoning before TITO comparison.

Upstream encoding_dsv32 gates every thinking block on last_user_idx: a new user turn strips historical think and rewrites the rendered prefix, which is why V3.2 registered only the {tool} surface. Vendoring the encoder lets Miles fix this at the render level without forking sglang.

Usage

DEEPSEEK_V32_MODEL=/cluster-storage/models/deepseek-ai/DeepSeek-V3.2 python -m pytest tests/manual/tito/deepseek_v32.py -q -s

The test launches one session verifier with tp=8, ep=8, and EAGLE steps/top-k/draft-tokens set to 2/1/3.

Design Notes

  • Key choices: encoding_dsv32 is vendored verbatim under miles/utils/chat_template_utils/templates/ (provenance header only, formatter-excluded); V4 stays on sglang's encoding_dsv4.
  • Key choices: the protocol.Tool seam stays on sglang so tool schemas keep serializing with the server's defaults.
  • Key choices: the vendored dsv32 mirrors dsv4's gates — drop_thinking=False renders every thinking block position-independently while drop_thinking=True stays byte-identical to upstream.
  • Key choices: every V3.2 surface pins drop_thinking=False, which legalizes the {tool, user} surface.
  • Key choices: DeepSeekV32TITOTokenizer forwards the resolved thinking mode as chat_template_kwargs["thinking"]; add_sglang_arguments maps parallel-size aliases to Miles' short Namespace fields.
  • Alternatives considered: preserve-think via bridge-side message preprocessing — rejected because the cut happens in the encoder's render gates, not in message content.

Verification

  • Tests added: upstream-parity corpus plus raise parity pin every drop_thinking=True path to the installed sglang encoder.
  • Tests added: append-only byte tests cover user appends, the tool-tail flip shape, plus tool-loop drop-mode equivalence.
  • Tests added: test_deepseek_v32_forwards_effective_thinking_mode, test_sglang_parallel_sizes_use_short_namespace_fields, test_namespace_to_train_args_enables_eagle_speculative_decoding.
  • Existing test suite: 2566 fast tests pass on the rebased branch.
  • Manual E2E: V3.2 without speculative decoding plus V4 with EAGLE both completed 64/64 samples with zero TITO mismatches.
  • Known blocker: V3.2 EAGLE stops in SGLang's NextN loader because DeepseekV3ForCausalLMNextN lacks fuse_qkv_a_proj.

Review Focus

  • Scrutinize the vendored dsv32 gate rewrites against upstream: header, four gate sites, encode_messages pass-through.
  • Scrutinize DeepSeekV32TITOTokenizer effective-mode precedence.
  • Scrutinize namespace_to_train_args fixed EAGLE option serialization.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces support for speculative decoding in sglang-based session verification, updates sglang parallel size argument handling to use shorter destination names (such as sglang_ep_size), and implements thinking mode forwarding for DeepSeek V3.2. The reviewer suggests avoiding hardcoded speculative decoding parameters in namespace_to_train_args and instead retrieving them dynamically from the namespace or model configuration.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread miles/utils/test_utils/session_verify_runner.py
guapisolo and others added 5 commits July 23, 2026 22:27
Add a manual TP8/EP8 session test and expose the fixed EAGLE serving recipe through ModelConfig.

Keep SGLang TP/DP/PP/EP namespace fields on Miles' short-name contract, and forward the effective V3.2 thinking mode so SGLang separates reasoning before TITO comparison.
Remove the old-to-new mapping table and select tp_size, dp_size, pp_size, or ep_size directly from SGLang's registered aliases without changing the CLI surface.
Document that argparse derives its destination from the first alias, which is why Miles selects SGLang's short parallel-size field names.
…lates/

Copy sglang's encoding_dsv32/encoding_dsv4 (0.5.14.dev37+gf8cfad3) into
miles/utils/chat_template_utils/templates/ verbatim (provenance header only)
and route the deepseek bridge plus the test reference encoders through the
vendored modules. Miles-side render modifications land separately; the
sglang protocol.Tool seam stays on sglang so tool schemas keep serializing
with the server's defaults. The vendored files are excluded from pre-commit
formatters so they stay byte-diffable against upstream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tool,user} surface

Upstream encoding_dsv32 gates every thinking block on last_user_idx, so a
new user turn strips historical think from the render and rewrites the
prefix — the reason V3.2 registered only the {tool} surface. Mirror
encoding_dsv4's gates in the vendored copy: drop_thinking=False now keeps
every assistant's thinking block and renders <think> openers position-
independently, while drop_thinking=True paths stay byte-identical to
upstream (corpus + raise parity tests). Pin drop_thinking=False on every
V3.2 surface and register {tool, user}; pure tool-loop histories render
byte-identically under either drop mode, so existing {tool} configs are
unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@guapisolo
guapisolo force-pushed the fix/dsv32-thinking-parser-mode branch from 4f01de1 to 1e338c0 Compare July 23, 2026 23:22
guapisolo added a commit that referenced this pull request Jul 25, 2026
…server v2

The v5 multi-lineage design lands as an OPT-IN second implementation
instead of replacing v1: --use-session-server grows an optional value
(bare flag or "v1" = the untouched linear server, "v2" = tree serving),
and the whole tree stack lives in miles/rollout/session/v2/. The v1
modules stay byte-identical to their pre-branch state.

v2 package (carried from the v5 line, imports adjusted only):
- session_state: always-branch serving over the forest — deepest attach
  point, suffix becomes the branch delta, non-extensions grow siblings or
  new roots; retry semantics moves entirely to the samples-op picker.
  Branch suffixes may carry client assistants (compaction); snapshot
  splice with zero-inheritance-root fallback on canonical-prefix
  divergence. Truncated-path extension is 409 (TruncatedGenerationError,
  defined here — v1 never raises it). --session-strict-append-only is the
  single-chain guard: branch shapes fail loud with attach diagnostics.
- assembly: per-leaf fold (compute -> truncate -> fold), default pick
  (temporal-supersession retry trim; roots never trimmed; non-retry-shaped
  trees 422) and default merge (exactly-once completion masking over the
  surviving set, rewards keyed by response id, fold < agent < server
  metadata layering); both replaceable via --session-sample-picker-path /
  --session-merge-function-path (sync-only, loaded in-process).
- core: v2 twin of SessionCore reusing v1's HTTP plumbing unchanged.

Shared seams, each inert for v1 by construction:
- sessions.py dispatches registry/core on the flag and forwards the
  collect body's "metadata" (agent semantic layer) only under v2.
- codec: encode/decode parameterized by a fields tuple; v1 default keeps
  the wire byte-identical, v2 adds reward + per-sample metadata
  (COMPUTED_FIELDS_V2) with conditional overlay semantics on decode.
- arguments: flag upgrade (nargs="?"), unknown values rejected, and the
  three v2 flags require --use-session-server v2.

Tests: v2 HTTP matrix (tree pins incl. deep/root divergence branching,
strict guard, truncation + compaction), session_state unit matrix, and
the samples-op suite (golden, multi-leaf trim/masking/rewards, hook
lanes) — all against a v2-flagged server; the restored v1 suites and the
byte-exact A-list (git diff against the pre-branch base is empty) pin
that the default path did not move.

Rebased onto the tito/session PR stack (#1628/#1777/#1778 +
#1759/#1760), with the stack's landed shape taken as authoritative:

- supports_midpath_assistant_rerender (True on the base class) moves in
  here with its consumer, the v2 suffix gate. No family sets it False
  anymore: the vendored DeepSeek-V3.2 encoder renders
  position-independently under drop_thinking=False, so the gate text no
  longer names it; a stub family pins the fail-loud branch.
- v2 serving fills SessionRecord.request_timestamp like v1, so folded
  samples keep their lifecycle req_ts across the wire.
- codec: COMPUTED_FIELDS_V2 is re-expressed over the ValueSpec table
  (SAMPLES_VALUE_SPEC_V2 superset), same wire semantics.
- v2 unit tests pin allowed_append_roles=["tool"] explicitly; the ctor
  default is tool+user since the append-roles PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pytest.mark.parametrize("scenario", list(_UPSTREAM_PARITY_SCENARIOS), ids=list(_UPSTREAM_PARITY_SCENARIOS))
@pytest.mark.parametrize("thinking", [False, True], ids=["chat", "thinking"])
def test_drop_thinking_true_matches_upstream_sglang(scenario, thinking):
from sglang.srt.entrypoints.openai import encoding_dsv32 as upstream

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.

please hoist this import to the beginning of the script

def test_drop_thinking_true_raise_parity_with_upstream():
# thinking mode + a post-last-user assistant without reasoning_content or
# tool_calls raises upstream; the vendored copy keeps that contract.
from sglang.srt.entrypoints.openai import encoding_dsv32 as upstream

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.

please hoist this import to the beginning of the script

# tool_calls raises upstream; the vendored copy keeps that contract.
from sglang.srt.entrypoints.openai import encoding_dsv32 as upstream

from miles.utils.chat_template_utils.templates import encoding_dsv32 as vendored

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.

please hoist this import to the beginning of the script

Keep the upstream and vendored encoder modules at test module scope so the parity helpers and exception checks share the reviewed dependencies without changing behavior.
Comment thread miles/utils/chat_template_utils/templates/encoding_dsv4.py Outdated

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.

The PR description says that this file is vendored "verbatim" but it doesn't seem so. Perhaps you'd want to update the PR description.

@guapisolo guapisolo Jul 25, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

no in this pr it's the same to keep consistancy behavior with existing code. template change happens in #1778

reasoning_parser="deepseek-v3",
tool_call_parser="deepseekv32",
tito_model="deepseekv32",
allowed_append_roles=("tool",),

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.

Aren't we supposed to test ("tool", "user") here?

@guapisolo guapisolo Jul 25, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this is modified in #1777

Keep the Miles-owned V3.2 encoder copy for drop_thinking while restoring V4 to SGLang encoding_dsv4. Narrow the pre-commit exclusion and parity-test import to the remaining V3.2 vendoring.

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

Overall LGTM. Added one comment and the PR description might need updates.

num_gpus=8,
tp_size=8,
ep_size=8,
enable_spec=True,

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.

In the PR description it says that EAGLE fails with Dpsk v3.2? Why is speculative decoding enabled in this test?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Enable spec to speed up the test, it's rollout-only and can speed up a lot.
There is actually some issue for dsv32 + mtp. So I naively skip it.

@guapisolo
guapisolo merged commit 52403d0 into main Jul 26, 2026
40 checks passed
@guapisolo
guapisolo deleted the fix/dsv32-thinking-parser-mode branch July 26, 2026 09:06
guapisolo added a commit that referenced this pull request Jul 26, 2026
The session verifier and its e2e callers serialize through sglang's real
argparse destinations (the renames landed upstream with #1628). Add a
regression test pinning the parser contract: external CLI spellings
parse into SGLang's ServerArgs short destinations (sglang_dp_size,
sglang_pp_size, sglang_ep_size, sglang_attn_cp_size, with sglang_tp_size
resolved by validation) so removed long destinations cannot silently
come back.

Co-authored-by: Jiajun Li <jiajun.li@radixark.ai>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.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.

2 participants