Skip to content

[Frontend] DeepSeek-V4 native OpenAI/Anthropic/Responses API + DSML tool parser#1563

Draft
yhl-amd wants to merge 6 commits into
ROCm:mainfrom
yhl-amd:feat/dsv4-pro-openai-anthropic-responses-api
Draft

[Frontend] DeepSeek-V4 native OpenAI/Anthropic/Responses API + DSML tool parser#1563
yhl-amd wants to merge 6 commits into
ROCm:mainfrom
yhl-amd:feat/dsv4-pro-openai-anthropic-responses-api

Conversation

@yhl-amd

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

Copy link
Copy Markdown
Contributor

Summary

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

Stacked on #1562 (uses the abort_request disconnect path). Review/merge that first; until then this PR's diff includes that commit.

Changes

  • 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 existing Qwen/GLM/MiniMax).
  • serving_chat: reasoning-filter + tool-call streaming for /v1/chat/completions (reasoning_content deltas during think phase, tool_calls deltas).
  • serving_responses (new) + /v1/responses endpoint: OpenAI Responses translation (input/tools ↔ chat, SSE emitter, reasoning/function_call items). Codex compat: inject a mandatory DSML tool-format instruction; normalize shell tool name/param aliases (exec/shell_exec/… → registered exec tool; command/scriptcmd) so Codex tool calls execute instead of erroring.
  • /v1/messages: pass the tool schema to the parser so tool args are typed correctly (fixes Claude Code "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.

Test plan

  • claude-local (Claude Code → /v1/messages): multi-tool agentic session, tools execute, no "Invalid tool parameters", CJK/box-drawing render cleanly.
  • codex-local (Codex → /v1/responses): tool calls execute (no "unsupported call"/"missing field cmd"), multi-step task completes.
  • /v1/chat/completions streaming: reasoning + tool_calls deltas.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every eligible PR before approval:

  • ✅ Pre Checkin: Black, Ruff, catalog schema validation, non-GPU unit tests

Heavy model tests:

  • ✅ Run after the PR is approved and Pre Checkin passes
  • ✅ Run immediately when an approval review is submitted
  • ✅ Can be requested before approval with labels
Label Tests
ci:full Run all heavy PR model tests: native ATOM, vLLM, and SGLang
ci:atom Run native ATOM model accuracy tests
ci:vllm Run ATOM vLLM OOT model accuracy tests
ci:sglang Run ATOM SGLang model accuracy tests

Heavy jobs are skipped when the PR is not approved and no matching ci:* label is present.
Add labels via the sidebar or gh pr edit 1563 --add-label <label>

@yhl-amd yhl-amd force-pushed the feat/dsv4-pro-openai-anthropic-responses-api branch 2 times, most recently from e884e5c to aea46ad Compare July 10, 2026 23:36
@zufayu zufayu requested review from valarLip and removed request for valarLip July 13, 2026 01:37
yhl-amd and others added 3 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>
@yhl-amd yhl-amd force-pushed the feat/dsv4-pro-openai-anthropic-responses-api branch from aea46ad to f03ac0e Compare July 13, 2026 02:43
yhl-amd and others added 2 commits July 12, 2026 22:22
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>
@yhl-amd yhl-amd force-pushed the feat/dsv4-pro-openai-anthropic-responses-api branch from f03ac0e to 8abadef Compare July 13, 2026 03:23
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 feat/dsv4-pro-openai-anthropic-responses-api branch from 8abadef to 999168e Compare July 13, 2026 03: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