Skip to content

add deepseek-v4-flash offline infer - #143

Merged
bumble0918 merged 1 commit into
hw-native-sys:mainfrom
superxf:ds_offline
Aug 7, 2026
Merged

add deepseek-v4-flash offline infer#143
bumble0918 merged 1 commit into
hw-native-sys:mainfrom
superxf:ds_offline

Conversation

@superxf

@superxf superxf commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add offline generation support for DeepSeek V4 Flash W8A8 without starting an HTTP server. The offline entry reuses the serving scheduler, distributed worker, grouped KV-cache, and MTP acceptance paths so its execution topology stays aligned with online serving.

Key changes

  • Add an eight-device DeepSeek V4 offline engine configuration using overlapped attention DP=8 and MoE EP=8 placement.
  • Add checkpoint, device topology, sequence length, batch capacity, and runtime configuration validation.
  • Add an offline CLI supporting single-prompt and continuous-batch generation, streaming output, sampling controls, optional MTP, and generation-window profiling.
  • Extend AsyncLLMEngine with structured generate_result and generate_batch APIs, final token IDs, normalized finish reasons, and sibling-task cleanup when a batch request fails.
  • Use the 128-token DeepSeek prefill tile in the offline CLI and keep padded static-kernel positions within the configured sequence range.
  • Document the offline workflow and add unit coverage for configuration, structured output delivery, batching, and prefill padding.

Current constraints

  • Requires a DeepSeek V4 W8A8 compressed-tensors checkpoint.
  • Requires exactly eight Ascend devices.
  • MTP offline generation currently uses greedy sampling (temperature=0).
  • The offline CLI requires --long-prefill-token-threshold 128.

Verification

  • platform-build passed.
  • pre-commit passed.
  • unit-tests passed.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 73858d68-fa4c-4896-95db-179f6eeb8ce3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds DeepSeek V4 Flash W8A8 offline generation for Ascend NPUs. The change includes an eight-device engine configuration, structured generation APIs, a CLI, documentation, and unit tests.

Changes

DeepSeek V4 offline generation

Layer / File(s) Summary
Structured generation results
pypto_serving/serving/engine/async_engine.py, tests/unit/serving/engine/*
Final outputs now include complete token IDs. AsyncLLMEngine adds non-streaming single-prompt and batch generation APIs with finish-reason normalization and validation.
DeepSeek V4 offline engine configuration
pypto_serving/model/deepseek/offline.py, tests/unit/model/deepseek/*
Adds validation and construction of the eight-rank overlapped engine configuration, grouped KV cache, MTP settings, runtime limits, parallelism, and kernel paths.
Offline generation CLI and documentation
examples/model/deepseek_v4/npu_generate.py, README.md, docs/dev/model/deepseek-v4.md
Adds model, device, batching, decoding, profiling, and kernel options. The CLI supports streaming and batch generation with cleanup and throughput reporting. Documentation provides eight-device commands and runtime requirements.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI as npu_generate.py
  participant Config as build_deepseek_v4_offline_engine_config
  participant Engine as AsyncLLMEngine
  CLI->>Config: Build validated DeepSeek V4 EngineConfig
  Config-->>CLI: Return offline engine configuration
  CLI->>Engine: Start engine and submit prompts
  Engine-->>CLI: Stream outputs or return GenerateResult values
  CLI->>Engine: Stop profiling and engine
Loading

Possibly related PRs

Poem

A rabbit submits prompts in a row,
Eight NPU ranks begin to glow.
Tokens stream, then results align,
With IDs preserved in every line.
MTP hops through the cache so bright—
DeepSeek generates through the night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% 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
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.
Title check ✅ Passed The title clearly identifies the main change: adding offline inference support for DeepSeek V4 Flash.
Description check ✅ Passed The description directly explains the offline inference implementation, validation, CLI, APIs, documentation, and tests.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@pypto_serving/serving/engine/async_engine.py`:
- Around line 1042-1045: Update generate_batch to create explicit tasks for each
generate_result call, then gather those tasks; on any gather failure, cancel
unfinished sibling tasks, await them with return_exceptions enabled so cleanup
completes, and re-raise the original exception. Add a regression test covering
one failing request and one long-blocking request, asserting the blocker is
cancelled and its cleanup finishes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 86d572f9-c810-4447-a365-ec092b1d5d01

📥 Commits

Reviewing files that changed from the base of the PR and between bbd5224 and d72afb5.

📒 Files selected for processing (8)
  • README.md
  • docs/dev/model/deepseek-v4.md
  • examples/model/deepseek_v4/npu_generate.py
  • pypto_serving/model/deepseek/offline.py
  • pypto_serving/serving/engine/async_engine.py
  • tests/unit/model/deepseek/test_offline.py
  • tests/unit/serving/engine/test_async_engine_replicas.py
  • tests/unit/serving/engine/test_output_delivery.py

Comment on lines +1042 to +1045
return list(
await asyncio.gather(
*(self.generate_result(prompt, generate_config) for prompt in prompts)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline pypto_serving/serving/engine/async_engine.py --items all
rg -n -C 8 'asyncio\.gather|async def generate_batch|finish_reason == "error"|async def add_request' \
  pypto_serving/serving/engine/async_engine.py \
  tests/unit/serving/engine/test_async_engine_replicas.py

Repository: hw-native-sys/pypto-serving

Length of output: 16741


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== async_engine around generate_result/generate_batch/add_request =="
sed -n '900,1175p' pypto_serving/serving/engine/async_engine.py

echo
echo "== request cleanup/abort implementations =="
rg -n -C 6 'async def abort_request|def \.abort|abort_request\(|def add_request|def _set_profile_active|cleanup|finished|final_output|finish_reason == "error"' pypto_serving/serving

echo
echo "== existing async engine tests around batching/errors =="
fd -t f . tests | xargs rg -n -C 5 'generate_batch|generate_result|cancel|return_exceptions|on_queued|queued' || true

echo
echo "== asyncio gather cancellation probe =="
python3 - <<'PY'
import asyncio

async def task(idx, event):
    if idx == 0:
        await event.wait()
        raise RuntimeError("sibling blocked forever unless gather cancels")
    await asyncio.sleep(0.001)
    return idx

async def main():
    events = [asyncio.Event() for _ in range(2)]
    gathered = asyncio.gather(task(0, events[0]), task(1, events[1]))
    try:
        await gathered
    except Exception as exc:
        print(type(exc).__name__, str(exc))
    # Give event loop a chance to run any cleanup/continuation from cancellation.
    await asyncio.sleep(0.01)
    print("task0 done", asyncio.isfuture(asyncio.current_task()), "cancelled" if gathered.cancelled() else str(gathered))

try:
    asyncio.run(main(), debug=True)
except Exception as exc:
    print(type(exc).__name__, str(exc))
PY

Repository: hw-native-sys/pypto-serving

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail
timeout 60 bash -c '
echo "== async_engine generate_result/generate_batch/add_request =="
sed -n "1020,1060p" pypto_serving/serving/engine/async_engine.py

echo
echo "== request cleanup candidates =="
rg -n -C 4 'abort_request|add_request|on_queued|request_id|context|cleanup|finish_reason == "error"|final_output is None' pypto_serving/serving/engine/async_engine.py pypto_serving/serving -g "*.py" || true

echo
echo "== existing batch/error cancellation tests =="
fd -t f . tests | xargs rg -n -C 4 'generate_batch|generate_result|cancel|return_exceptions|on_queued|queued' || true
' || true

python3 - <<'PY'
import asyncio

async def task(idx, event):
    if idx == 0:
        await event.wait()
        raise RuntimeError("sibling failed")
    await asyncio.sleep(0.01)
    return idx

async def main():
    events = [asyncio.Event() for _ in range(2)]
    gathered = asyncio.gather(task(0, events[0]), task(1, events[1]))
    try:
        await gathered
    except Exception as exc:
        print(type(exc).__name__, str(exc))
    await asyncio.sleep(0.02)
    print("gathered done", gathered.done(), "gathered cancelled", gathered.cancelled())

asyncio.run(main())
PY

Repository: hw-native-sys/pypto-serving

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== async_engine generate_result/generate_batch/add_request =="
sed -n '1000,1075p' pypto_serving/serving/engine/async_engine.py

echo
echo "== request cleanup/abort implementations =="
rg -n -C 5 'async def abort_request|async def add_request|function add_request|class .*Context|final_output is None|finish_reason == "error"|def cleanup|context' pypto_serving/serving/engine/async_engine.py pypto_serving/serving -g "*.py" || true

echo
echo "== existing async engine tests around batching/errors/cancellation =="
fd -t f . tests | xargs rg -n -C 4 'generate_batch|generate_result|cancel|return_exceptions|on_queued|queued|aborted|cleanup|finished' || true

echo
echo "== asyncio.gather propagation/probe =="
python3 - <<'PY'
import asyncio

async def blocking(idx):
    await asyncio.sleep(10)

async def failing(idx):
    await asyncio.sleep(0)
    raise RuntimeError(f"sibling {idx} failed")

async def main():
    gathered = asyncio.gather(failing(0), blocking(1))
    try:
        await gathered
    except Exception as exc:
        print("exception_type", type(exc).__name__)
        print("exception_msg", str(exc))
    # Let the event loop try to schedule continuation from any pending cancellation.
    await asyncio.sleep(0.01)
    print("gathered done", gathered.done())
    print("gathered cancelled", gathered.cancelled())

asyncio.run(main(), debug=True)
PY

Repository: hw-native-sys/pypto-serving

Length of output: 50383


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== add_request body =="
sed -n '1055,1092p' pypto_serving/serving/engine/async_engine.py

echo
echo "== abort_request bodies =="
sed -n '351,430p' pypto_serving/serving/engine/async_engine.py
sed -n '1092,1104p' pypto_serving/serving/engine/async_engine.py

echo
echo "== focused async engine replica tests =="
sed -n '130,200p' tests/unit/serving/engine/test_async_engine_replicas.py

Repository: hw-native-sys/pypto-serving

Length of output: 7884


Cancel sibling batch requests when one fails.

generate_batch directly passes generate_result coroutines to asyncio.gather(), so a failure propagates without cancelling the remaining coroutines. If a sibling remains blocked, it can continue occupying scheduler/state while the caller receives an error.

Create tasks for each generate_result(), catch the gather exception, cancel unfinished tasks awaiting return_exceptions, then re-raise. Add a regression test with one failing request and one long-blocking request asserting the blocker is cancelled and its cleanup completes.

🤖 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 `@pypto_serving/serving/engine/async_engine.py` around lines 1042 - 1045,
Update generate_batch to create explicit tasks for each generate_result call,
then gather those tasks; on any gather failure, cancel unfinished sibling tasks,
await them with return_exceptions enabled so cleanup completes, and re-raise the
original exception. Add a regression test covering one failing request and one
long-blocking request, asserting the blocker is cancelled and its cleanup
finishes.

Source: Coding guidelines

@superxf superxf changed the title add deespeek-v4-flash offline infer add deepseek-v4-flash offline infer Aug 6, 2026
Comment thread pypto_serving/model/deepseek/offline.py Outdated
max_new_tokens: int = 32,
max_num_seqs: int = 32,
max_num_batched_tokens: int = 512,
long_prefill_token_threshold: int = 2048,

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.

这个现在是不是只支持128

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.

已修改

@bumble0918
bumble0918 merged commit 272b874 into hw-native-sys:main Aug 7, 2026
4 checks passed
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.

2 participants