Skip to content

fix: make GLM sparse indexer decode top-k deterministic - #4

Open
yhl-amd wants to merge 8 commits into
feat/dsv4-pro-openai-anthropic-responses-apifrom
fix/glm52-deterministic-indexer-topk
Open

fix: make GLM sparse indexer decode top-k deterministic#4
yhl-amd wants to merge 8 commits into
feat/dsv4-pro-openai-anthropic-responses-apifrom
fix/glm52-deterministic-indexer-topk

Conversation

@yhl-amd

@yhl-amd yhl-amd commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an opt-in deterministic GLM sparse-indexer decode top-k fallback
  • break equal-score boundaries by logical token position so every TP rank selects the same KV subset
  • document the cache-write investigation, TP=8 replay evidence, and the causal diagram below

根因图

同一份 logits(所有 TP rank 一致)
              │
              ▼
同分边界上的 native top-k 选出不同 token
              │
              ▼
rank 0: KV 集合 A    rank 1: KV 集合 B    rank 2: KV 集合 C
              │
              ▼
不同 sparse MLA 输出进入同一个 all-reduce
              │
              ▼
不是合法 attention 的结果 → 后续层放大 → 乱码

Validation

  • python3 -m py_compile atom/models/deepseek_v2.py
  • TP=8 end-to-end replay of the recorded 60,753-token request: full prefill plus two 60,752-token prefix-cache replays returned identical response-content hashes at temperature=0

Notes

Enable with ATOM_DETERMINISTIC_GLM_INDEXER_TOPK=1. The fallback reads dynamic context lengths and is intentionally eager-only; a graph-safe AITER kernel tie-break remains the production follow-up.

yhl-amd and others added 7 commits July 12, 2026 21:38
Non-streaming API handlers were not cancelled when the client hung up
(Starlette only cancels StreamingResponse, not plain handlers), so the engine
kept generating and the sequence's KV blocks leaked until it hit max_tokens.

Add a client-disconnect abort path:
- engine: EngineCoreMgr.abort_request broadcasts an "abort_request" utility
  command; _handle_abort_request marks the seq aborted; the scheduler finishes
  it at the next step via the normal stop path (frees KV, emits a finished
  RequestOutput). Adds Sequence.aborted.
- api_server: _run_nonstream_with_disconnect runs generate_async in a task and
  polls request.is_disconnected(); on disconnect it cancels the task, whose
  teardown aborts + pops the request. Wired into /v1/chat/completions and
  /v1/completions (return HTTP 499 on disconnect). generate_async /
  generate_async_multimodal / generate_async_fanout / cleanup_streaming_request
  abort + pop on early exit to avoid leaks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the 0.5s is_disconnected() polling loop in
_run_nonstream_with_disconnect with vLLM-style event-driven cancellation:
race the generator-collector task against a task that awaits the ASGI
http.disconnect event (request.receive()), FIRST_COMPLETED wins.

Detection is now immediate (0ms vs up to 500ms) and costs nothing while
the client stays connected (no periodic wakeups). The abort path is
unchanged: cancelling the collector still propagates into generate_async's
finally -> abort_request + io_processor.requests.pop, freeing leaked KV.

request.receive() is safe here because FastAPI parses the request body
into a pydantic model before the handler runs, so there is no unread body
to race against.

Verified on DeepSeek-V4-Pro tp8: curl --max-time drop -> immediate
"Client disconnected ... aborting request" + abort_request found=True;
normal non-stream and streaming requests unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: yihonglie <hyi@amd.com>
…el except

Address review of the client-disconnect abort:

- Factor the disconnect race into `_race_disconnect(coro, ...)` and make
  `_run_nonstream_with_disconnect` a thin wrapper that collects the
  async-generator. This lets the fan-out path (whose `generate_async_fanout`
  is a coroutine returning a list, not an async generator) reuse the same
  cancellation machinery.

- Wrap the previously-unguarded non-stream branches so an abandoned request
  is aborted instead of running to max_tokens: multimodal (chat), n>1 fan-out
  (chat, both multimodal and text), and n>1 fan-out (completions).
  generate_async_fanout's try/finally aborts every sibling on cancel, so a
  disconnect frees all n sibling seqs.

- Narrow the post-cancel teardown handler from `except BaseException` to
  `except asyncio.CancelledError` (expected) + `except Exception` (logged),
  letting KeyboardInterrupt/SystemExit propagate.

Verified on DeepSeek-V4-Flash tp4: plain and n=2 fan-out non-stream drops both
log "Client disconnected ... aborting" with abort_request found=True for every
sibling (2/2 for n=2); normal plain, n=2 fan-out, and streaming unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: yihonglie <hyi@amd.com>
Fixes the "Check Code Style with Black" CI check on the disconnect-abort
changes (extra blank lines, single-line logger call).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: yihonglie <hyi@amd.com>
…ool parser

Serve DeepSeek-V4 (Pro/Flash) so Claude Code (Anthropic /v1/messages) and Codex
CLI (OpenAI Responses /v1/responses) talk to ATOM directly, with DSML tool-call
parsing shared across /v1/chat/completions, /v1/messages and /v1/responses.

- tool_parser: DeepSeek-V4 DSML tool-call format (<|DSML|invoke ...>) with
  marker-less / self-closing / direct-JSON recovery, schema-driven type coercion
  and key aliases; streaming + non-streaming (alongside Qwen/GLM/MiniMax).
- serving_chat: reasoning-filter + tool-call streaming for /v1/chat/completions.
- serving_responses (new) + /v1/responses: OpenAI Responses translation
  (input/tools<->chat, SSE emitter, reasoning/function_call items). Codex compat:
  inject a mandatory DSML tool-format instruction and normalize shell tool
  name/param aliases (exec/shell_exec/... -> registered exec tool; command/script
  -> cmd) so Codex tool calls execute instead of erroring.
- /v1/messages: pass the tool schema to the parser so tool args are typed
  correctly (fixes "Invalid tool parameters"); streaming UTF-8 incremental
  detokenization (vLLM-style sliding window) so multi-byte chars (CJK,
  box-drawing) aren't split into U+FFFD.
- reasoning / chat_encoders: DeepSeek-V4 message encoding + reasoning tags.

Builds on the abort-on-disconnect path from the previous commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes "Check Code Style with Black" CI on the DSV4 API + DSML tool parser
changes (serving_responses.py, tool_parser.py, api_server.py).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: yihonglie <hyi@amd.com>
@yhl-amd
yhl-amd force-pushed the fix/glm52-deterministic-indexer-topk branch from 0b3d7f2 to 14cf46a Compare July 14, 2026 12:15
@yhl-amd
yhl-amd force-pushed the feat/dsv4-pro-openai-anthropic-responses-api branch 2 times, most recently from dcef850 to 4721c02 Compare July 28, 2026 12:24
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