Repository navigation
feat: d3:lite decision model -- flm serve --decision, POST /v1/systemone - #816
Conversation
tawei-amd
left a comment
There was a problem hiding this comment.
Self-review (author). Four inline findings plus one on the vendored binaries, most important first. I'm happy to fix any of them here or in a follow-up.
Vendored engine binaries. The libd3_npu.so / d3_npu.dll / d3_npu.lib (XRT and HRX) here come from an IRON CI build of AMD-ROCm-Internal/FastFlowLM_IRON#68 at 6506b014. Since then #68 gained 0a9025d5 (review fixes; the default path's output is bit-identical, checked on 311 prompts). Before merging, re-vendor them from the IRON commit that actually lands on IRON main, so the binaries match a commit there. The Windows DLL has also never run on an NPU: a Windows smoke run (flm pull d3:lite, flm serve --decision d3:lite, one request) is still open.
🤖 Generated with Claude Code
| path == "/v1/chat/completions" || path == "/v1/completions" || | ||
| path == "/v1/audio/transcriptions" || path == "/v1/embeddings"; | ||
| path == "/v1/audio/transcriptions" || path == "/v1/embeddings" || | ||
| path == "/v1/systemone" || path == "/v1/decisions"; |
There was a problem hiding this comment.
Decisions queue behind chat generations. /v1/systemone is an NPU route, so with flm serve <chat model> --decision d3:lite a decision waits for any in-flight chat completion to finish.
Measured with qwen3.5:0.8b next to d3:lite:
- alone, the docs example decision takes 146 ms;
- sent 1 s into a 600-token chat completion (13.6 s), it took 12.7 s.
For vLLM-SR this matters, because the router asks for the decision before it sends the chat. Options, cheapest first:
- Say it in
docs/docs/models/d3.md: for latency-sensitive routing, serve the decision model on its own (flm serve --decision d3:lite, decision only). - Let a decision request run between decode steps of a chat. The engines are independent, so they only need the device, not the chat's whole turn.
- Give decisions their own queue.
There was a problem hiding this comment.
Documented in 25f4d10. The scheduling itself is unchanged, and changing it is a call for the maintainers.
I tested the cheap way around it, a second flm serve for the decision model alone, and it does not help. The two processes contend for the NPU: a 600-token chat took 65 s instead of 13.6 s, and the first decision sent during it still waited 64 s. docs/models/d3.md now states the queueing with the numbers, and says a second server does not avoid it.
A real fix would be scheduler work in the server: let decision requests run between decode steps of an in-flight chat, or give them their own queue. I've left that open for you.
fd17fb8 to
25f4d10
Compare
|
On the vendored binaries from my self-review (no thread): fixed in 25f4d10. All six libraries are re-vendored from FastFlowLM_IRON#68's PR Check run 39445931404 on d51e5a85. The CI Linux library gives bit-identical rows to the locally validated build on all 311 text goldens, and the Windows XRT and HRX builds pass there. They still need re-vendoring from whichever IRON commit finally merges. The Windows NPU smoke run is still open. 🤖 Generated with Claude Code |
f936ce6 to
8dd2bc6
Compare
Serves vllm-sr/d3-lite (Decision 3.0) on the NPU next to, or without, a chat
model, over the upstream package's own HTTP API:
flm serve [<llm>] --decision d3:lite # -d
POST /v1/systemone (alias /v1/decisions)
- AutoDecisionModel: the package's decision_config checks, the answer codes
re-derived from the tokenizer, the chat template cross-checked against the
contract with minja, the fp32 readout, warmup; the engine is libd3_npu
(vendored XRT + HRX builds from FastFlowLM_IRON, mm.xclbin as Qwen3.5-0.8B).
- Server: the route parses the raw body with Python json semantics and replies
exactly as d3_server (422 detail texts, invalid_questions, per-question
max_length_exceeded in a 200); NPU-exclusive like the other inference routes.
Replies may now carry their own HTTP status (__http_status / __http_body).
- Catalog: d3:lite -> D3-Lite-NPU2 (family d3), hidden from /api/tags and
/v1/models like embed-gemma; flm run/serve/bench d3:lite explains how to serve
it. Prompts over 4,096 tokens are refused per question, never truncated.
- src/test/d3_npu: AutoDecisionModel without HTTP (harness + perf driver).
- Docs: docs/docs/models/d3.md.
Windows: linked only where the engine library is vendored; until d3_npu.dll
ships the decision model refuses to load there.
Co-Authored-By: Claude <[email protected]>
The list operation of vLLM Semantic Router's vllm-sr/systemone@1 protocol
(systemone-compatible providers declare create and list_models), in the
router's own format: {"object": "list", "data": [{"id", "object", "api"}]}.
Empty while no --decision model is loaded.
Co-Authored-By: Claude <[email protected]>
FastFlowLM_IRON d3_npu now packs up to four 512-row GEMM tiles per pass by default: 16 questions of 330 tokens 156 -> 133 ms per question, 4 questions 150 -> 122, bit-exact against one prompt per pass. XRT and HRX builds. Co-Authored-By: Claude <[email protected]>
The harness links libd3_npu directly, so it compiles AutoDecisionModel with FLM_HAS_D3_NPU like the CMake build does. Its replies for the 308 golden requests match FastFlowLM_IRON's engine harness: top-1 identical on every question, probabilities within 3e-6 (fp32 readout summation order). Co-Authored-By: Claude <[email protected]>
Generated from the 4.9 measurements (d3-lite plan): gold accuracy on 6,824 Decision Index public-suite questions (upstream BF16 61.2%, NPU 60.6%), top-1 agreement, and per-request latency at 128-4,096 tokens and 4/16 questions. Co-Authored-By: Claude <[email protected]>
lib/<runtime>/ holds both the Linux .so and the Windows .lib of every engine, so testing for either file made the Windows build link d3_npu.lib, which does not ship yet (LNK1181). Test the platform's own library. Co-Authored-By: Claude <[email protected]>
The engine libraries now come from IRON CI (FastFlowLM_IRON#68, run 39245613348): Linux XRT/HRX .so and Windows XRT/HRX d3_npu.dll + .lib. The CI-built Linux library gives bit-identical hidden states to the locally validated build on all 311 golden prompts, and passes sweep_decision.py. The installers (WiX, Inno) ship d3_npu.dll. Co-Authored-By: Claude <[email protected]>
The Hugging Face tree of FastFlowLM/D3-Lite-NPU2 (commit cd6df2ea), which flm pull verifies downloads against. Checked: flm pull d3:lite into an empty model directory downloads and verifies all 9 catalog files (sha256 equal to the package manifest), and sweep_decision.py passes on the pulled copy. Co-Authored-By: Claude <[email protected]>
- warmup() encodes one pass at 2,048, 1,024 and 512 rows. The engine now keeps each bucket's sequences for its lifetime, and the scratch of buckets that fit one 4,096-row pass together (these three do). The old comment said buckets were kept when the engine kept only the last one, so the first one-question request rebuilt everything after warming 700 tokens. - AutoDecisionModel and the d3_npu test harness no longer cite the local plan document. Co-Authored-By: Claude <[email protected]>
…umbers - Engine libraries from FastFlowLM_IRON#68 PR Check run 39445931404 (d51e5a85): Linux XRT/HRX .so, Windows XRT/HRX .dll + .lib. They add per-bucket sequence/scratch caching and the blocked host attention. The CI Linux library gives bit-identical rows to the locally validated build on all 311 text goldens. - docs/models/d3.md: the example reply is now the server's real reply (rounded, and said so); before, it was made up. The docs also note that a decision waits for an in-flight chat completion on the same NPU (146 ms alone, 12.7 s during a 600-token completion), and that a second flm serve does not help (the processes contend for the NPU). - docs/benchmarks/d3_results.md regenerated from the new measurements: 4,096 tokens 3,652 -> 2,135 ms, 1,024 tokens 518 -> 430 ms; gold accuracy 60.5% (was 60.6%, within noise: paired sign test p = 0.44). Co-Authored-By: Claude <[email protected]>
- Max input 4,096 -> 32,768 tokens per question (AutoDecisionModel and the catalog entry); longer prompts still get max_length_exceeded, untruncated. - Engine libraries from FastFlowLM_IRON#68 PR Check run 39840309905 (569a9617), all four (Linux/Windows, XRT/HRX): prompts of 1,536 tokens or more run attention on the NPU, shorter ones on the CPU as before (bit-identical below 1,536). Long-prompt buckets are freed after each call. - src/xclbins/D3-Lite-NPU2/attn.xclbin: Qwen3.5's prefill attention made bidirectional (xclbins-Qwen3_5_bidirectional_attention_prefill, same CI run). Loaded on the first long prompt, so the decision model holds a second NPU hardware context only after one arrives; if it cannot be loaded, attention stays on the CPU. - One question: 2,048 tokens 765 ms (CPU attention: 906), 4,096 1.6 s (2.1), 16,384 8.4 s (21.1), 32,768 22.7 s (107). Over HTTP with this build: a 32,000-token two-question request answers in 43 s; short requests return the same answers before and after. - Accuracy, 1,078 Decision Index questions of 1,024+ tokens, NPU vs CPU attention: same top answer 97.8%, top-1 agreement with upstream BF16 88.2% vs 87.6%, gold accuracy +0.46 pt (95% CI -0.08 ... +1.01). - docs: max input, latency rows to 32,768 tokens. Co-Authored-By: Claude <[email protected]>
ec777ad to
6771da0
Compare
Summary
Serves
d3:lite, vLLM Semantic Router's Decision 3.0 decision model (vllm-sr/d3-litev3.1.3), on the NPU, next to or without a chat model, through the upstream package's own HTTP API:Stacked on #815 (the contract library this uses).
AutoDecisionModel(src/{include,common}/AutoDecisionModel). It checks the package'sdecision_config.jsonthe way the upstream runtime does. It re-derives the 255 answer codes from the tokenizer and refuses to load on any mismatch. It cross-checks the chat template against the contract with minja. It runs the fp32 readout and warms up at load. The engine islibd3_npu(ROCm-Internal FastFlowLM_IRON#68), vendored for XRT and HRX;xclbins/D3-Lite-NPU2/mm.xclbinis Qwen3.5-0.8B's.POST /v1/systemone(alias/v1/decisions) parses the raw body with Python json semantics and replies exactly asd3_server.py: 422 detail texts,invalid_questions, and per-questionmax_length_exceededinside a 200. It is NPU-exclusive like the other inference routes.GET /v1/systemone/modelsis the list operation of vLLM-SR'svllm-sr/systemone@1protocol.__http_status/__http_body).d3:lite→D3-Lite-NPU2(familyd3, labeldecision), hidden from/api/tagsand/v1/modelslikeembed-gemma.flm run|serve|bench d3:liteexplains how to serve it.max_length_exceeded; nothing is ever truncated.src/test/d3_npu: AutoDecisionModel without HTTP (harness and engine-level perf driver).docs/docs/models/d3.md,docs/docs/benchmarks/d3_results.md.Engine libraries come from FastFlowLM_IRON CI (#68's run): Linux XRT/HRX
.soand Windows XRT/HRXd3_npu.dll+.lib. The CI-built Linux library is bit-identical in output to the locally validated build on all 311 golden prompts. The installers shipd3_npu.dll. CMake links d3 only where the platform's library is vendored (FLM_HAS_D3_NPU).Test results (Linux, XRT and HRX builds, this NPU)
test_d3_contract, + 10,000-request local fuzz run)d3_server.py(616 requests incl. 300 fuzz bodies)max_length_exceededby design)sweep_decision.pyBackendInvokerreplay (vllm-sr/systemone@1)sweep_llm.py, qwen3.5:0.8b)Weights: published at FastFlowLM/D3-Lite-NPU2 (commit
cd6df2ea); a fresh download matches the package manifest,flm pull d3:liteinto an empty model directory verifies all 9 catalog files, andsweep_decision.pypasses on the pulled copy. ModelScope (amd/D3-Lite-NPU2) is not uploaded yet, soflm pull --modelscope d3:litewon't work until it is.Not in this PR
images/videoslists are refused with upstream's 422 for now).🤖 Generated with Claude Code