Instructions to use devanshbatham/nyx with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use devanshbatham/nyx with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf devanshbatham/nyx # Run inference directly in the terminal: llama cli -hf devanshbatham/nyx
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf devanshbatham/nyx # Run inference directly in the terminal: llama cli -hf devanshbatham/nyx
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf devanshbatham/nyx # Run inference directly in the terminal: ./llama-cli -hf devanshbatham/nyx
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf devanshbatham/nyx # Run inference directly in the terminal: ./build/bin/llama-cli -hf devanshbatham/nyx
Use Docker
docker model run hf.co/devanshbatham/nyx
- LM Studio
- Jan
- vLLM
How to use devanshbatham/nyx with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "devanshbatham/nyx" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "devanshbatham/nyx", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/devanshbatham/nyx
- Ollama
How to use devanshbatham/nyx with Ollama:
ollama run hf.co/devanshbatham/nyx
- Unsloth Desktop
- Pi
How to use devanshbatham/nyx with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf devanshbatham/nyx
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "devanshbatham/nyx" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use devanshbatham/nyx with Docker Model Runner:
docker model run hf.co/devanshbatham/nyx
- Lemonade
How to use devanshbatham/nyx with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull devanshbatham/nyx
Run and chat with the model
lemonade run user.nyx-{{QUANT_TAG}}List all available models
lemonade list
- Hermes Agent
How to use devanshbatham/nyx with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf devanshbatham/nyx
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default devanshbatham/nyx
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use devanshbatham/nyx with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf devanshbatham/nyx
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "devanshbatham/nyx" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Devansh Batham commited on
Commit ·
d0a349e
1
Parent(s): 39d1566
Release compact nyx GGUF
Browse files- .gitattributes +1 -0
- NOTICE +11 -7
- README.md +52 -84
- assets/IOSEVKA-OFL.md +112 -0
- assets/nyx-benchmarks.svg +0 -0
- benchmarks/prequant-h1.md +0 -24
- benchmarks/random-six-report.md +62 -29
- benchmarks/summary.json +0 -26
- build_manifest.py +1 -2
- chat_template.jinja +0 -154
- config.json +0 -96
- generation_config.json +0 -13
- assets/nyx-vs-jev-benchmarks.png → nyx.gguf +2 -2
- provenance.json +25 -20
- release-manifest.json +21 -97
- runtime/entrypoint.sh +0 -8
- runtime/label-token-ids.json +0 -1
- runtime/patches/qwen35-text-quark-generated-scales.patch +0 -32
- runtime/patches/sglang-batch-result-processor.py +0 -1378
- runtime/patches/sglang-logits-processor.py +0 -1272
- runtime/patches/sglang-qwen35-text.py +0 -298
- runtime/patches/sglang-qwen35.py +0 -0
- serve.sh +0 -45
- tokenizer.json +0 -3
- tokenizer_config.json +0 -32
- weights/TP-1-sig-e793a9432f30cd4e/READY +0 -1
- weights/TP-1-sig-e793a9432f30cd4e/checksum.json +0 -0
- weights/TP-1-sig-e793a9432f30cd4e/model-00000-common.safetensor +0 -3
- weights/TP-1-sig-e793a9432f30cd4e/model-00001-common.safetensor +0 -3
- weights/TP-1-sig-e793a9432f30cd4e/model-00002-common.safetensor +0 -3
- weights/TP-1-sig-e793a9432f30cd4e/model-00003-common.safetensor +0 -3
.gitattributes
CHANGED
|
@@ -9,3 +9,4 @@ tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
|
| 9 |
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 9 |
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.gguf filter=lfs diff=lfs merge=lfs -text
|
NOTICE
CHANGED
|
@@ -1,12 +1,16 @@
|
|
| 1 |
nyx
|
| 2 |
-
Copyright 2026
|
| 3 |
|
| 4 |
-
Derived from Qwen/Qwen3.5-
|
| 5 |
-
|
| 6 |
|
| 7 |
Qwen3.5 was created by the Qwen team at Alibaba Cloud and is distributed under
|
| 8 |
-
the Apache License 2.0. nyx adds
|
| 9 |
-
|
| 10 |
-
retained in LICENSE.
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
nyx
|
| 2 |
+
Copyright 2026 Devansh Batham
|
| 3 |
|
| 4 |
+
Derived from Qwen/Qwen3.5-4B, revision
|
| 5 |
+
851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a.
|
| 6 |
|
| 7 |
Qwen3.5 was created by the Qwen team at Alibaba Cloud and is distributed under
|
| 8 |
+
the Apache License 2.0. nyx adds decision-task adaptation, task-vector
|
| 9 |
+
composition, quantization, packaging, and runtime integration. The original
|
| 10 |
+
Qwen license is retained in LICENSE.
|
| 11 |
|
| 12 |
+
The benchmark SVG embeds an Iosevka font subset licensed separately under the
|
| 13 |
+
SIL Open Font License 1.1; see assets/IOSEVKA-OFL.md.
|
| 14 |
+
|
| 15 |
+
Qwen, Alibaba Cloud, TypeSafe, and Jev do not endorse and are not affiliated
|
| 16 |
+
with nyx.
|
README.md
CHANGED
|
@@ -1,127 +1,95 @@
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
-
library_name: sglang
|
| 4 |
pipeline_tag: text-generation
|
| 5 |
base_model:
|
| 6 |
-
- Qwen/Qwen3.5-
|
| 7 |
tags:
|
| 8 |
- nyx
|
| 9 |
- qwen3.5
|
| 10 |
-
-
|
| 11 |
-
-
|
| 12 |
-
-
|
| 13 |
-
-
|
| 14 |
-
- sglang
|
| 15 |
---
|
| 16 |
|
| 17 |
# nyx
|
| 18 |
|
| 19 |
-
nyx is a
|
| 20 |
|
| 21 |
-

|
| 20 |
|
| 21 |
## Model
|
| 22 |
|
| 23 |
| Property | Value |
|
| 24 |
|---|---|
|
| 25 |
+
| Base | [`Qwen/Qwen3.5-4B`](https://huggingface.co/Qwen/Qwen3.5-4B) |
|
| 26 |
+
| Base revision | `851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a` |
|
| 27 |
+
| File | `nyx.gguf` |
|
| 28 |
+
| Size | 3,288,582,784 bytes (3.29 GB / 3.06 GiB) |
|
| 29 |
+
| Quantization | Importance-matrix IQ4_XS with selected Q5_K/Q6_K tensors |
|
| 30 |
+
| SHA-256 | `39d64fb19863cfc067e2411fcb2a0444113dda13798b15de5617736d91914359` |
|
| 31 |
+
| Runtime | Pinned llama.cpp build in [`devanshbatham/nyx`](https://github.com/devanshbatham/nyx) |
|
| 32 |
|
| 33 |
+
The architecture, tokenizer, and base weights originate from Qwen. nyx adds decision-task adaptation, task-vector composition, quantization, and a production API integration.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
## Run
|
| 36 |
|
| 37 |
```bash
|
| 38 |
+
git clone https://github.com/devanshbatham/nyx.git /opt/nyx
|
| 39 |
+
hf download devanshbatham/nyx --local-dir /opt/nyx-model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
+
# auto-detects CPU, Metal, CUDA, or HIP
|
| 42 |
+
/opt/nyx/scripts/build-llama-cpp.sh /opt/llama.cpp
|
| 43 |
|
| 44 |
+
export LLAMA_SERVER_BIN=/opt/llama.cpp/build/bin/llama-server
|
| 45 |
+
export NYX_GGUF_PATH=/opt/nyx-model/nyx.gguf
|
| 46 |
+
/opt/nyx/scripts/serve-model.sh
|
|
|
|
|
|
|
| 47 |
```
|
| 48 |
|
| 49 |
+
Budget at least 8 GB of free RAM, VRAM, or unified memory for one 4,096-token slot and 6 GB of free disk. More context and concurrent slots require more memory. The model backend binds to unauthenticated loopback port `30000`; do not expose it publicly.
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
For the authenticated `POST /v1/systemone` API and typed Python/TypeScript clients, use [`github.com/devanshbatham/nyx`](https://github.com/devanshbatham/nyx). The gateway implements the TypeSafe System One wire contract for Choice, Score, Noul, model discovery, bearer authentication, and request IDs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
## Evaluation
|
| 54 |
|
| 55 |
+
The public comparison below is for the pre-quantization nyx reference. The pretrained Qwen base and nyx reference used the same local runtime; all three systems received the same 2,277 frozen requests.
|
| 56 |
|
| 57 |
+
| Benchmark | Qwen3.5-4B base | nyx reference | Jev 1.13 |
|
| 58 |
+
|---|---:|---:|---:|
|
| 59 |
+
| AG News | **87.75%** | 85.75% | 86.50% |
|
| 60 |
+
| TREC coarse | 86.60% | 84.20% | **93.00%** |
|
| 61 |
+
| CoLA | 77.75% | **81.50%** | 77.75% |
|
| 62 |
+
| RTE | 84.12% | 88.09% | **91.34%** |
|
| 63 |
+
| Emotion | **47.67%** | 46.33% | 47.00% |
|
| 64 |
+
| IMDb | 95.75% | 95.75% | **97.00%** |
|
| 65 |
+
| Dataset-macro accuracy | 79.94% | 80.27% | **82.10%** |
|
| 66 |
+
| Pooled accuracy | 81.42% | 81.51% | **83.62%** |
|
| 67 |
+
| Round trip, median / p95 | 54.85 / 67.96 ms | 55.56 / 72.77 ms | 182.33 / 245.12 ms |
|
| 68 |
+
| Throughput | 17.44 req/s | 17.15 req/s | 42.43 req/s |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
+
Local timings used one AMD Instinct MI325X, one execution slot, Q8 KV cache, Flash Attention, and a 4,096-token context. Jev used remote HTTPS at concurrency 8, so its timing is descriptive rather than a hardware-controlled speed comparison.
|
| 71 |
|
| 72 |
+
### Compact-release fidelity
|
| 73 |
|
| 74 |
+
The released GGUF was compared with the 8.42 GB reference on a separate frozen 1,024-case set.
|
| 75 |
|
| 76 |
+
| Metric | Reference | Released GGUF |
|
| 77 |
+
|---|---:|---:|
|
| 78 |
+
| Correct | 873/1,024 | 874/1,024 |
|
| 79 |
+
| Accuracy | 85.25% | 85.35% |
|
| 80 |
+
| Decision agreement | — | 98.73% (1,011/1,024) |
|
| 81 |
+
| Decision disagreements | — | 13 |
|
| 82 |
|
| 83 |
+
The compact model gained seven decisions and lost six relative to the reference. It did not meet the experiment's original 99.5% agreement target; publication followed an explicit decision to accept that tradeoff. See the [evaluation record](benchmarks/random-six-report.md).
|
| 84 |
|
| 85 |
## Limitations
|
| 86 |
|
| 87 |
- Optimized for structured classification and decision workloads; general assistant quality was not established.
|
| 88 |
+
- The released GGUF has not been rerun on the full public 2,277-case suite.
|
| 89 |
+
- NVIDIA and Apple performance and cross-vendor decision parity have not been measured.
|
| 90 |
+
- Public-benchmark pretraining overlap is unknown.
|
| 91 |
+
- Outputs can be incorrect or biased. Validate consequential decisions independently.
|
| 92 |
|
| 93 |
## License and attribution
|
| 94 |
|
| 95 |
+
Apache License 2.0. nyx is derived from [`Qwen/Qwen3.5-4B`](https://huggingface.co/Qwen/Qwen3.5-4B), also distributed under Apache-2.0. Qwen and Alibaba Cloud are credited as the original model authors. This project is not endorsed by or affiliated with Qwen, Alibaba Cloud, TypeSafe, or Jev.
|
assets/IOSEVKA-OFL.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2015-2026, Renzhi Li (aka. Belleve Invis, belleve@typeof.net)
|
| 2 |
+
|
| 3 |
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
| 4 |
+
|
| 5 |
+
This license is copied below, and is also available with a FAQ at:
|
| 6 |
+
http://scripts.sil.org/OFL
|
| 7 |
+
|
| 8 |
+
--------------------------
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
SIL Open Font License v1.1
|
| 12 |
+
====================================================
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
Preamble
|
| 16 |
+
----------
|
| 17 |
+
|
| 18 |
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
| 19 |
+
development of collaborative font projects, to support the font creation
|
| 20 |
+
efforts of academic and linguistic communities, and to provide a free and
|
| 21 |
+
open framework in which fonts may be shared and improved in partnership
|
| 22 |
+
with others.
|
| 23 |
+
|
| 24 |
+
The OFL allows the licensed fonts to be used, studied, modified and
|
| 25 |
+
redistributed freely as long as they are not sold by themselves. The
|
| 26 |
+
fonts, including any derivative works, can be bundled, embedded,
|
| 27 |
+
redistributed and/or sold with any software provided that any reserved
|
| 28 |
+
names are not used by derivative works. The fonts and derivatives,
|
| 29 |
+
however, cannot be released under any other type of license. The
|
| 30 |
+
requirement for fonts to remain under this license does not apply
|
| 31 |
+
to any document created using the fonts or their derivatives.
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
Definitions
|
| 35 |
+
-------------
|
| 36 |
+
|
| 37 |
+
`"Font Software"` refers to the set of files released by the Copyright
|
| 38 |
+
Holder(s) under this license and clearly marked as such. This may
|
| 39 |
+
include source files, build scripts and documentation.
|
| 40 |
+
|
| 41 |
+
`"Reserved Font Name"` refers to any names specified as such after the
|
| 42 |
+
copyright statement(s).
|
| 43 |
+
|
| 44 |
+
`"Original Version"` refers to the collection of Font Software components as
|
| 45 |
+
distributed by the Copyright Holder(s).
|
| 46 |
+
|
| 47 |
+
`"Modified Version"` refers to any derivative made by adding to, deleting,
|
| 48 |
+
or substituting -- in part or in whole -- any of the components of the
|
| 49 |
+
Original Version, by changing formats or by porting the Font Software to a
|
| 50 |
+
new environment.
|
| 51 |
+
|
| 52 |
+
`"Author"` refers to any designer, engineer, programmer, technical
|
| 53 |
+
writer or other person who contributed to the Font Software.
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
Permission & Conditions
|
| 57 |
+
------------------------
|
| 58 |
+
|
| 59 |
+
Permission is hereby granted, free of charge, to any person obtaining
|
| 60 |
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
| 61 |
+
redistribute, and sell modified and unmodified copies of the Font
|
| 62 |
+
Software, subject to the following conditions:
|
| 63 |
+
|
| 64 |
+
1. Neither the Font Software nor any of its individual components,
|
| 65 |
+
in Original or Modified Versions, may be sold by itself.
|
| 66 |
+
|
| 67 |
+
2. Original or Modified Versions of the Font Software may be bundled,
|
| 68 |
+
redistributed and/or sold with any software, provided that each copy
|
| 69 |
+
contains the above copyright notice and this license. These can be
|
| 70 |
+
included either as stand-alone text files, human-readable headers or
|
| 71 |
+
in the appropriate machine-readable metadata fields within text or
|
| 72 |
+
binary files as long as those fields can be easily viewed by the user.
|
| 73 |
+
|
| 74 |
+
3. No Modified Version of the Font Software may use the Reserved Font
|
| 75 |
+
Name(s) unless explicit written permission is granted by the corresponding
|
| 76 |
+
Copyright Holder. This restriction only applies to the primary font name as
|
| 77 |
+
presented to the users.
|
| 78 |
+
|
| 79 |
+
4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
| 80 |
+
Software shall not be used to promote, endorse or advertise any
|
| 81 |
+
Modified Version, except to acknowledge the contribution(s) of the
|
| 82 |
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
| 83 |
+
permission.
|
| 84 |
+
|
| 85 |
+
5. The Font Software, modified or unmodified, in part or in whole,
|
| 86 |
+
must be distributed entirely under this license, and must not be
|
| 87 |
+
distributed under any other license. The requirement for fonts to
|
| 88 |
+
remain under this license does not apply to any document created
|
| 89 |
+
using the Font Software.
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
Termination
|
| 94 |
+
-----------
|
| 95 |
+
|
| 96 |
+
This license becomes null and void if any of the above conditions are
|
| 97 |
+
not met.
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
DISCLAIMER
|
| 101 |
+
|
| 102 |
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| 103 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
| 104 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
| 105 |
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
| 106 |
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
| 107 |
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
| 108 |
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
| 109 |
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
| 110 |
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
| 111 |
+
Source font: Iosevka Regular webfont 34.8.0 from https://github.com/iosevka-webfonts/iosevka/tree/main/woff2.
|
| 112 |
+
This repository bundles an ASCII subset for reproducible chart generation.\n
|
assets/nyx-benchmarks.svg
ADDED
|
|
benchmarks/prequant-h1.md
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
# Pre-quantization H1 comparison
|
| 2 |
-
|
| 3 |
-
This record isolates the exact intersection shared by three completed evaluations: the official `Qwen/Qwen3.5-35B-A3B` base in BF16, the pre-quantization `nyx` BF16 checkpoint, and Jev 1.13.0.
|
| 4 |
-
|
| 5 |
-
## Results
|
| 6 |
-
|
| 7 |
-
| Task | Cases | Qwen base BF16 | `nyx` BF16 | Jev 1.13.0 |
|
| 8 |
-
|---|---:|---:|---:|---:|
|
| 9 |
-
| Comment triage | 500 | 295 (59.00%) | 462 (92.40%) | 455 (91.00%) |
|
| 10 |
-
| Report completeness | 500 | 439 (87.80%) | 488 (97.60%) | 468 (93.60%) |
|
| 11 |
-
| Broad assets, exact set | 500 | 285 (57.00%) | 298 (59.60%) | 284 (56.80%) |
|
| 12 |
-
| **Overall** | **1,500** | **1,019 (67.93%)** | **1,248 (83.20%)** | **1,207 (80.47%)** |
|
| 13 |
-
|
| 14 |
-
## Protocol
|
| 15 |
-
|
| 16 |
-
- The intersection contains comment fixtures 501–1000 plus all 500 authored report fixtures, scored once for completeness and once for broad-asset exact-set classification.
|
| 17 |
-
- Case IDs and expected labels were matched across the three result sets. All 1,500 expected labels matched exactly; no case was excluded after matching.
|
| 18 |
-
- Qwen and `nyx` used BF16 before any FP8 or INT4 conversion, the same AMD Instinct MI325X, the same SGLang/API stack, and server concurrency 8.
|
| 19 |
-
- The base was the official `Qwen/Qwen3.5-35B-A3B` revision `59d61f3ce65a6d9863b86d2e96597125219dc754`.
|
| 20 |
-
- Jev was evaluated as `jev-1.13.0` through its remote API with the same typed requests, prompts, thresholds, and reference labels. Because the serving infrastructure differs, this record makes no speed comparison.
|
| 21 |
-
|
| 22 |
-
## Scope
|
| 23 |
-
|
| 24 |
-
These are synthetic, authored H1 development fixtures with known scenario-family overlap. They are useful for measuring task adaptation on this workload, but they are not an independent public holdout and do not establish general model superiority.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
benchmarks/random-six-report.md
CHANGED
|
@@ -1,38 +1,71 @@
|
|
| 1 |
-
# Six public classification benchmarks
|
| 2 |
|
| 3 |
-
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|---|---:|---:|---:|
|
| 7 |
-
| AG News | 400 | 84.75% (339/400) | 86.50% (346/400) |
|
| 8 |
-
| TREC coarse | 500 | 92.00% (460/500) | 93.00% (465/500) |
|
| 9 |
-
| CoLA | 400 | 79.00% (316/400) | 77.75% (311/400) |
|
| 10 |
-
| RTE | 277 | 93.14% (258/277) | 91.34% (253/277) |
|
| 11 |
-
| Emotion | 300 | 43.33% (130/300) | 47.00% (141/300) |
|
| 12 |
-
| IMDb | 400 | 96.50% (386/400) | 97.00% (388/400) |
|
| 13 |
|
| 14 |
-
|
|
| 15 |
-
|---|---:|---:|
|
| 16 |
-
|
|
| 17 |
-
|
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
|
| 21 |
|
| 22 |
-
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
| 27 |
-
|
|
| 28 |
-
|
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
- AG News, CoLA, Emotion, and IMDb are fixed class-stratified samples; TREC test and RTE validation are complete.
|
| 35 |
-
-
|
| 36 |
-
-
|
| 37 |
-
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Six public classification benchmarks
|
| 2 |
|
| 3 |
+
These results describe the 8.42 GB pre-quantization nyx reference, not the 3.29 GB release GGUF. The compact file was selected using the separate fidelity evaluation reported below.
|
| 4 |
|
| 5 |
+
Frozen paired cohort: 2,277 cases; case SHA-256 `046a38c24747fd724003aca7e5f1d1b6e148d599092222361c0717ecff84ecad`; every failure counts as wrong.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
+
| Dataset | Cases | Qwen3.5-4B base | nyx | Jev 1.13 |
|
| 8 |
+
|---|---:|---:|---:|---:|
|
| 9 |
+
| AG News | 400 | 87.75% (351/400) | 85.75% (343/400) | 86.50% (346/400) |
|
| 10 |
+
| TREC coarse | 500 | 86.60% (433/500) | 84.20% (421/500) | 93.00% (465/500) |
|
| 11 |
+
| CoLA | 400 | 77.75% (311/400) | 81.50% (326/400) | 77.75% (311/400) |
|
| 12 |
+
| RTE | 277 | 84.12% (233/277) | 88.09% (244/277) | 91.34% (253/277) |
|
| 13 |
+
| Emotion | 300 | 47.67% (143/300) | 46.33% (139/300) | 47.00% (141/300) |
|
| 14 |
+
| IMDb | 400 | 95.75% (383/400) | 95.75% (383/400) | 97.00% (388/400) |
|
| 15 |
+
| Dataset-macro accuracy | 2,277 | 79.94% | 80.27% | 82.10% |
|
| 16 |
+
| Dataset-macro F1 | 2,277 | 79.83% | 80.48% | 81.85% |
|
| 17 |
+
| Pooled micro accuracy | 2,277 | 81.42% (1,854/2,277) | 81.51% (1,856/2,277) | 83.62% (1,904/2,277) |
|
| 18 |
|
| 19 |
+
Jev-only versus nyx-only correct cases were 121 to 73; the exact two-sided McNemar p-value is `0.0006995`. nyx-only versus Qwen-only correct cases were 112 to 110 (`p=0.9465`).
|
| 20 |
|
| 21 |
+
## Runtime
|
| 22 |
|
| 23 |
+
The release profile uses one llama.cpp execution slot because it was the fastest measured configuration for this latency-oriented workload.
|
| 24 |
+
|
| 25 |
+
| Measurement | Result |
|
| 26 |
+
|---|---:|
|
| 27 |
+
| Reference model file | 8,424,393,056 bytes |
|
| 28 |
+
| Measured VRAM | 9,927,208,960 bytes |
|
| 29 |
+
| Round trip, median | 55.56 ms |
|
| 30 |
+
| Round trip, p95 | 72.77 ms |
|
| 31 |
+
| Round trip, mean | 57.92 ms |
|
| 32 |
+
| Full-run wall time | 132,780.73 ms |
|
| 33 |
+
| Sustained throughput | 17.15 requests/s |
|
| 34 |
+
|
| 35 |
+
The nyx timing is direct loopback HTTP to llama.cpp on one AMD Instinct MI325X, with full GPU offload, Flash Attention, Q8 KV cache, 4,096 tokens per slot, and client/server concurrency 1. It excludes the authenticated gateway and external network. The Qwen base used the identical local profile and measured 54.85 ms median, 67.96 ms p95, and 17.44 requests/s.
|
| 36 |
+
|
| 37 |
+
The previously observed Jev remote-HTTPS run used client concurrency 8 and reported 182.33 ms median, 245.12 ms p95, and 42.43 requests/s. Different hardware, network topology, and concurrency make that timing descriptive rather than a controlled speed comparison.
|
| 38 |
+
|
| 39 |
+
## Batching check
|
| 40 |
+
|
| 41 |
+
The first 120 frozen cases were repeated at three concurrency levels. The server had the same number of 4,096-token slots as the client concurrency.
|
| 42 |
|
| 43 |
+
| Concurrency | Correct | Median / p95 | Throughput |
|
| 44 |
+
|---:|---:|---:|---:|
|
| 45 |
+
| 1 | 94/120 | 57.20 / 89.01 ms | 15.79 requests/s |
|
| 46 |
+
| 2 | 94/120 | 162.80 / 219.25 ms | 11.77 requests/s |
|
| 47 |
+
| 8 | 94/120 | 606.22 / 887.68 ms | 11.61 requests/s |
|
| 48 |
|
| 49 |
+
All 120 class decisions were identical across the three runs. Batching was rejected because it reduced throughput and increased latency.
|
| 50 |
+
|
| 51 |
+
## Protocol and limits
|
| 52 |
+
|
| 53 |
+
- Dataset selection, sampled examples, request order, and labels were frozen before provider scoring.
|
| 54 |
- AG News, CoLA, Emotion, and IMDb are fixed class-stratified samples; TREC test and RTE validation are complete.
|
| 55 |
+
- Each request contained one Choice question. Both systems completed 2,277/2,277 cases with zero errors.
|
| 56 |
+
- Public-benchmark pretraining overlap is unknown. This finite suite does not establish universal classification quality.
|
| 57 |
+
- The NVIDIA RTX 4000 Ada target still requires an on-device latency and class-decision parity run before production rollout.
|
| 58 |
+
|
| 59 |
+
## Compact release fidelity
|
| 60 |
+
|
| 61 |
+
The released importance-matrix IQ4_XS candidate was evaluated against the reference on a frozen 1,024-case selection spanning Banking77, BoolQ, CLINC150, code-defect annotation, commit type, DBpedia14, email spam, SNLI, structured rules, and tweet sentiment.
|
| 62 |
+
|
| 63 |
+
| Metric | Reference | Released GGUF |
|
| 64 |
+
|---|---:|---:|
|
| 65 |
+
| File size | 8,424,393,056 bytes | 3,288,582,784 bytes |
|
| 66 |
+
| Correct | 873/1,024 | 874/1,024 |
|
| 67 |
+
| Accuracy | 85.25% | 85.35% |
|
| 68 |
+
| Decision agreement | — | 98.73% (1,011/1,024) |
|
| 69 |
+
| Decision disagreements | — | 13 |
|
| 70 |
+
|
| 71 |
+
The released candidate gained seven decisions and lost six relative to the reference. This is fidelity evidence, not a fresh public-benchmark run. The candidate did not meet the experiment's original 99.5% agreement target and was released only after that threshold was explicitly waived.
|
benchmarks/summary.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"format_version": 1,
|
| 3 |
-
"cases_sha256": "046a38c24747fd724003aca7e5f1d1b6e148d599092222361c0717ecff84ecad",
|
| 4 |
-
"cases": 2277,
|
| 5 |
-
"concurrency": 8,
|
| 6 |
-
"errors": {
|
| 7 |
-
"nyx-int4fp8": 0,
|
| 8 |
-
"jev-1.13": 0
|
| 9 |
-
},
|
| 10 |
-
"dataset_macro_accuracy": {
|
| 11 |
-
"nyx-int4fp8": 0.8145402125952668,
|
| 12 |
-
"jev-1.13": 0.8209762334536702
|
| 13 |
-
},
|
| 14 |
-
"pooled_accuracy": {
|
| 15 |
-
"nyx-int4fp8": 0.8296003513394817,
|
| 16 |
-
"jev-1.13": 0.8361879666227492
|
| 17 |
-
},
|
| 18 |
-
"paired_mcnemar_two_sided_p": {
|
| 19 |
-
"nyx-int4fp8_vs_jev-1.13": 0.23153716588439505
|
| 20 |
-
},
|
| 21 |
-
"limitations": [
|
| 22 |
-
"Public benchmark contamination is unknown for both models.",
|
| 23 |
-
"Fixed samples are not complete official-suite scores except TREC test and RTE validation.",
|
| 24 |
-
"Jev timing used unmatched remote infrastructure."
|
| 25 |
-
]
|
| 26 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
build_manifest.py
CHANGED
|
@@ -32,10 +32,9 @@ for path in sorted(ROOT.rglob("*")):
|
|
| 32 |
payload = {
|
| 33 |
"format_version": 1,
|
| 34 |
"repository": "devanshbatham/nyx",
|
| 35 |
-
"artifact": "nyx-
|
| 36 |
"files": files,
|
| 37 |
"total_bytes": sum(item["bytes"] for item in files.values()),
|
| 38 |
}
|
| 39 |
(ROOT / "release-manifest.json").write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
|
| 40 |
print(json.dumps({"files": len(files), "total_bytes": payload["total_bytes"]}))
|
| 41 |
-
|
|
|
|
| 32 |
payload = {
|
| 33 |
"format_version": 1,
|
| 34 |
"repository": "devanshbatham/nyx",
|
| 35 |
+
"artifact": "nyx-4b-compact-iq4xs-mixed",
|
| 36 |
"files": files,
|
| 37 |
"total_bytes": sum(item["bytes"] for item in files.values()),
|
| 38 |
}
|
| 39 |
(ROOT / "release-manifest.json").write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
|
| 40 |
print(json.dumps({"files": len(files), "total_bytes": payload["total_bytes"]}))
|
|
|
chat_template.jinja
DELETED
|
@@ -1,154 +0,0 @@
|
|
| 1 |
-
{%- set image_count = namespace(value=0) %}
|
| 2 |
-
{%- set video_count = namespace(value=0) %}
|
| 3 |
-
{%- macro render_content(content, do_vision_count, is_system_content=false) %}
|
| 4 |
-
{%- if content is string %}
|
| 5 |
-
{{- content }}
|
| 6 |
-
{%- elif content is iterable and content is not mapping %}
|
| 7 |
-
{%- for item in content %}
|
| 8 |
-
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
-
{%- if is_system_content %}
|
| 10 |
-
{{- raise_exception('System message cannot contain images.') }}
|
| 11 |
-
{%- endif %}
|
| 12 |
-
{%- if do_vision_count %}
|
| 13 |
-
{%- set image_count.value = image_count.value + 1 %}
|
| 14 |
-
{%- endif %}
|
| 15 |
-
{%- if add_vision_id %}
|
| 16 |
-
{{- 'Picture ' ~ image_count.value ~ ': ' }}
|
| 17 |
-
{%- endif %}
|
| 18 |
-
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
| 19 |
-
{%- elif 'video' in item or item.type == 'video' %}
|
| 20 |
-
{%- if is_system_content %}
|
| 21 |
-
{{- raise_exception('System message cannot contain videos.') }}
|
| 22 |
-
{%- endif %}
|
| 23 |
-
{%- if do_vision_count %}
|
| 24 |
-
{%- set video_count.value = video_count.value + 1 %}
|
| 25 |
-
{%- endif %}
|
| 26 |
-
{%- if add_vision_id %}
|
| 27 |
-
{{- 'Video ' ~ video_count.value ~ ': ' }}
|
| 28 |
-
{%- endif %}
|
| 29 |
-
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
|
| 30 |
-
{%- elif 'text' in item %}
|
| 31 |
-
{{- item.text }}
|
| 32 |
-
{%- else %}
|
| 33 |
-
{{- raise_exception('Unexpected item type in content.') }}
|
| 34 |
-
{%- endif %}
|
| 35 |
-
{%- endfor %}
|
| 36 |
-
{%- elif content is none or content is undefined %}
|
| 37 |
-
{{- '' }}
|
| 38 |
-
{%- else %}
|
| 39 |
-
{{- raise_exception('Unexpected content type.') }}
|
| 40 |
-
{%- endif %}
|
| 41 |
-
{%- endmacro %}
|
| 42 |
-
{%- if not messages %}
|
| 43 |
-
{{- raise_exception('No messages provided.') }}
|
| 44 |
-
{%- endif %}
|
| 45 |
-
{%- if tools and tools is iterable and tools is not mapping %}
|
| 46 |
-
{{- '<|im_start|>system\n' }}
|
| 47 |
-
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 48 |
-
{%- for tool in tools %}
|
| 49 |
-
{{- "\n" }}
|
| 50 |
-
{{- tool | tojson }}
|
| 51 |
-
{%- endfor %}
|
| 52 |
-
{{- "\n</tools>" }}
|
| 53 |
-
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 54 |
-
{%- if messages[0].role == 'system' %}
|
| 55 |
-
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 56 |
-
{%- if content %}
|
| 57 |
-
{{- '\n\n' + content }}
|
| 58 |
-
{%- endif %}
|
| 59 |
-
{%- endif %}
|
| 60 |
-
{{- '<|im_end|>\n' }}
|
| 61 |
-
{%- else %}
|
| 62 |
-
{%- if messages[0].role == 'system' %}
|
| 63 |
-
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 64 |
-
{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
|
| 65 |
-
{%- endif %}
|
| 66 |
-
{%- endif %}
|
| 67 |
-
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 68 |
-
{%- for message in messages[::-1] %}
|
| 69 |
-
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 70 |
-
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 71 |
-
{%- set content = render_content(message.content, false)|trim %}
|
| 72 |
-
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 73 |
-
{%- set ns.multi_step_tool = false %}
|
| 74 |
-
{%- set ns.last_query_index = index %}
|
| 75 |
-
{%- endif %}
|
| 76 |
-
{%- endif %}
|
| 77 |
-
{%- endfor %}
|
| 78 |
-
{%- if ns.multi_step_tool %}
|
| 79 |
-
{{- raise_exception('No user query found in messages.') }}
|
| 80 |
-
{%- endif %}
|
| 81 |
-
{%- for message in messages %}
|
| 82 |
-
{%- set content = render_content(message.content, true)|trim %}
|
| 83 |
-
{%- if message.role == "system" %}
|
| 84 |
-
{%- if not loop.first %}
|
| 85 |
-
{{- raise_exception('System message must be at the beginning.') }}
|
| 86 |
-
{%- endif %}
|
| 87 |
-
{%- elif message.role == "user" %}
|
| 88 |
-
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 89 |
-
{%- elif message.role == "assistant" %}
|
| 90 |
-
{%- set reasoning_content = '' %}
|
| 91 |
-
{%- if message.reasoning_content is string %}
|
| 92 |
-
{%- set reasoning_content = message.reasoning_content %}
|
| 93 |
-
{%- else %}
|
| 94 |
-
{%- if '</think>' in content %}
|
| 95 |
-
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 96 |
-
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 97 |
-
{%- endif %}
|
| 98 |
-
{%- endif %}
|
| 99 |
-
{%- set reasoning_content = reasoning_content|trim %}
|
| 100 |
-
{%- if loop.index0 > ns.last_query_index %}
|
| 101 |
-
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
|
| 102 |
-
{%- else %}
|
| 103 |
-
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 104 |
-
{%- endif %}
|
| 105 |
-
{%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
|
| 106 |
-
{%- for tool_call in message.tool_calls %}
|
| 107 |
-
{%- if tool_call.function is defined %}
|
| 108 |
-
{%- set tool_call = tool_call.function %}
|
| 109 |
-
{%- endif %}
|
| 110 |
-
{%- if loop.first %}
|
| 111 |
-
{%- if content|trim %}
|
| 112 |
-
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 113 |
-
{%- else %}
|
| 114 |
-
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 115 |
-
{%- endif %}
|
| 116 |
-
{%- else %}
|
| 117 |
-
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 118 |
-
{%- endif %}
|
| 119 |
-
{%- if tool_call.arguments is defined %}
|
| 120 |
-
{%- for args_name, args_value in tool_call.arguments|items %}
|
| 121 |
-
{{- '<parameter=' + args_name + '>\n' }}
|
| 122 |
-
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
| 123 |
-
{{- args_value }}
|
| 124 |
-
{{- '\n</parameter>\n' }}
|
| 125 |
-
{%- endfor %}
|
| 126 |
-
{%- endif %}
|
| 127 |
-
{{- '</function>\n</tool_call>' }}
|
| 128 |
-
{%- endfor %}
|
| 129 |
-
{%- endif %}
|
| 130 |
-
{{- '<|im_end|>\n' }}
|
| 131 |
-
{%- elif message.role == "tool" %}
|
| 132 |
-
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 133 |
-
{{- '<|im_start|>user' }}
|
| 134 |
-
{%- endif %}
|
| 135 |
-
{{- '\n<tool_response>\n' }}
|
| 136 |
-
{{- content }}
|
| 137 |
-
{{- '\n</tool_response>' }}
|
| 138 |
-
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 139 |
-
{{- '<|im_end|>\n' }}
|
| 140 |
-
{%- elif loop.last %}
|
| 141 |
-
{{- '<|im_end|>\n' }}
|
| 142 |
-
{%- endif %}
|
| 143 |
-
{%- else %}
|
| 144 |
-
{{- raise_exception('Unexpected message role.') }}
|
| 145 |
-
{%- endif %}
|
| 146 |
-
{%- endfor %}
|
| 147 |
-
{%- if add_generation_prompt %}
|
| 148 |
-
{{- '<|im_start|>assistant\n' }}
|
| 149 |
-
{%- if enable_thinking is defined and enable_thinking is false %}
|
| 150 |
-
{{- '<think>\n\n</think>\n\n' }}
|
| 151 |
-
{%- else %}
|
| 152 |
-
{{- '<think>\n' }}
|
| 153 |
-
{%- endif %}
|
| 154 |
-
{%- endif %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config.json
DELETED
|
@@ -1,96 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"architectures": [
|
| 3 |
-
"Qwen3_5MoeForCausalLM"
|
| 4 |
-
],
|
| 5 |
-
"attention_bias": false,
|
| 6 |
-
"attention_dropout": 0.0,
|
| 7 |
-
"attn_output_gate": true,
|
| 8 |
-
"bos_token_id": null,
|
| 9 |
-
"dtype": "bfloat16",
|
| 10 |
-
"eos_token_id": 248044,
|
| 11 |
-
"full_attention_interval": 4,
|
| 12 |
-
"head_dim": 256,
|
| 13 |
-
"hidden_act": "silu",
|
| 14 |
-
"hidden_size": 2048,
|
| 15 |
-
"initializer_range": 0.02,
|
| 16 |
-
"layer_types": [
|
| 17 |
-
"linear_attention",
|
| 18 |
-
"linear_attention",
|
| 19 |
-
"linear_attention",
|
| 20 |
-
"full_attention",
|
| 21 |
-
"linear_attention",
|
| 22 |
-
"linear_attention",
|
| 23 |
-
"linear_attention",
|
| 24 |
-
"full_attention",
|
| 25 |
-
"linear_attention",
|
| 26 |
-
"linear_attention",
|
| 27 |
-
"linear_attention",
|
| 28 |
-
"full_attention",
|
| 29 |
-
"linear_attention",
|
| 30 |
-
"linear_attention",
|
| 31 |
-
"linear_attention",
|
| 32 |
-
"full_attention",
|
| 33 |
-
"linear_attention",
|
| 34 |
-
"linear_attention",
|
| 35 |
-
"linear_attention",
|
| 36 |
-
"full_attention",
|
| 37 |
-
"linear_attention",
|
| 38 |
-
"linear_attention",
|
| 39 |
-
"linear_attention",
|
| 40 |
-
"full_attention",
|
| 41 |
-
"linear_attention",
|
| 42 |
-
"linear_attention",
|
| 43 |
-
"linear_attention",
|
| 44 |
-
"full_attention",
|
| 45 |
-
"linear_attention",
|
| 46 |
-
"linear_attention",
|
| 47 |
-
"linear_attention",
|
| 48 |
-
"full_attention",
|
| 49 |
-
"linear_attention",
|
| 50 |
-
"linear_attention",
|
| 51 |
-
"linear_attention",
|
| 52 |
-
"full_attention",
|
| 53 |
-
"linear_attention",
|
| 54 |
-
"linear_attention",
|
| 55 |
-
"linear_attention",
|
| 56 |
-
"full_attention"
|
| 57 |
-
],
|
| 58 |
-
"linear_conv_kernel_dim": 4,
|
| 59 |
-
"linear_key_head_dim": 128,
|
| 60 |
-
"linear_num_key_heads": 16,
|
| 61 |
-
"linear_num_value_heads": 32,
|
| 62 |
-
"linear_value_head_dim": 128,
|
| 63 |
-
"mamba_ssm_dtype": "float32",
|
| 64 |
-
"max_position_embeddings": 262144,
|
| 65 |
-
"mlp_only_layers": [],
|
| 66 |
-
"model_type": "qwen3_5_moe_text",
|
| 67 |
-
"moe_intermediate_size": 512,
|
| 68 |
-
"mtp_num_hidden_layers": 1,
|
| 69 |
-
"mtp_use_dedicated_embeddings": false,
|
| 70 |
-
"num_attention_heads": 16,
|
| 71 |
-
"num_experts": 256,
|
| 72 |
-
"num_experts_per_tok": 8,
|
| 73 |
-
"num_hidden_layers": 40,
|
| 74 |
-
"num_key_value_heads": 2,
|
| 75 |
-
"output_router_logits": false,
|
| 76 |
-
"pad_token_id": null,
|
| 77 |
-
"partial_rotary_factor": 0.25,
|
| 78 |
-
"rms_norm_eps": 1e-06,
|
| 79 |
-
"rope_parameters": {
|
| 80 |
-
"mrope_interleaved": true,
|
| 81 |
-
"mrope_section": [
|
| 82 |
-
11,
|
| 83 |
-
11,
|
| 84 |
-
10
|
| 85 |
-
],
|
| 86 |
-
"partial_rotary_factor": 0.25,
|
| 87 |
-
"rope_theta": 10000000,
|
| 88 |
-
"rope_type": "default"
|
| 89 |
-
},
|
| 90 |
-
"router_aux_loss_coef": 0.001,
|
| 91 |
-
"shared_expert_intermediate_size": 512,
|
| 92 |
-
"tie_word_embeddings": false,
|
| 93 |
-
"transformers_version": "5.17.0",
|
| 94 |
-
"use_cache": true,
|
| 95 |
-
"vocab_size": 248320
|
| 96 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
generation_config.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"bos_token_id": 248044,
|
| 3 |
-
"do_sample": true,
|
| 4 |
-
"eos_token_id": [
|
| 5 |
-
248046,
|
| 6 |
-
248044
|
| 7 |
-
],
|
| 8 |
-
"pad_token_id": 248044,
|
| 9 |
-
"temperature": 1.0,
|
| 10 |
-
"top_k": 20,
|
| 11 |
-
"top_p": 0.95,
|
| 12 |
-
"transformers_version": "5.17.0"
|
| 13 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/nyx-vs-jev-benchmarks.png → nyx.gguf
RENAMED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:39d64fb19863cfc067e2411fcb2a0444113dda13798b15de5617736d91914359
|
| 3 |
+
size 3288582784
|
provenance.json
CHANGED
|
@@ -1,28 +1,33 @@
|
|
| 1 |
{
|
| 2 |
"format_version": 1,
|
| 3 |
"model": "devanshbatham/nyx",
|
| 4 |
-
"base_model": "Qwen/Qwen3.5-
|
| 5 |
-
"base_revision": "
|
| 6 |
"license": "Apache-2.0",
|
| 7 |
-
"
|
| 8 |
-
"
|
| 9 |
-
"
|
| 10 |
-
"
|
| 11 |
-
"
|
| 12 |
-
"
|
| 13 |
-
"ssm_state": "FP32"
|
| 14 |
},
|
| 15 |
-
"
|
| 16 |
-
"
|
| 17 |
-
"
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
},
|
| 20 |
-
"
|
| 21 |
-
"
|
| 22 |
-
"
|
| 23 |
-
"
|
| 24 |
-
"
|
| 25 |
-
"h1_result_sha256": "b10697d6a03fb19c56e7193eb85b932c92aee2a27d4b6a088f684051dff1c01e",
|
| 26 |
-
"source_checksum_manifest_sha256": "829aa54dd4f440c10d85c6253a41891a69ba1717ace2d193cbfab3c1a7546c6e"
|
| 27 |
}
|
| 28 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"format_version": 1,
|
| 3 |
"model": "devanshbatham/nyx",
|
| 4 |
+
"base_model": "Qwen/Qwen3.5-4B",
|
| 5 |
+
"base_revision": "851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a",
|
| 6 |
"license": "Apache-2.0",
|
| 7 |
+
"artifact": {
|
| 8 |
+
"path": "nyx.gguf",
|
| 9 |
+
"bytes": 3288582784,
|
| 10 |
+
"sha256": "39d64fb19863cfc067e2411fcb2a0444113dda13798b15de5617736d91914359",
|
| 11 |
+
"format": "GGUF",
|
| 12 |
+
"quantization": "importance-matrix IQ4_XS with selected Q5_K/Q6_K tensors"
|
|
|
|
| 13 |
},
|
| 14 |
+
"reference": {
|
| 15 |
+
"bytes": 8424393056,
|
| 16 |
+
"sha256": "b225f62bb79e2f0bc0bcd9fa00dba4579bade89e6b9581537da7fa2482a6b500"
|
| 17 |
+
},
|
| 18 |
+
"fidelity_evaluation": {
|
| 19 |
+
"cases": 1024,
|
| 20 |
+
"reference_correct": 873,
|
| 21 |
+
"artifact_correct": 874,
|
| 22 |
+
"decision_agreement": 0.9873046875,
|
| 23 |
+
"decision_disagreements": 13,
|
| 24 |
+
"gains": 7,
|
| 25 |
+
"losses": 6
|
| 26 |
},
|
| 27 |
+
"tested_runtime": {
|
| 28 |
+
"llama_cpp_revision": "e6cef8152f6e8351a870d8e1a98627139c9c379a",
|
| 29 |
+
"cpu_load_smoke_test": true,
|
| 30 |
+
"full_public_benchmark_on_release_artifact": false,
|
| 31 |
+
"nvidia_qualification": false
|
|
|
|
|
|
|
| 32 |
}
|
| 33 |
}
|
release-manifest.json
CHANGED
|
@@ -1,120 +1,44 @@
|
|
| 1 |
{
|
| 2 |
-
"artifact": "nyx-
|
| 3 |
"files": {
|
| 4 |
".gitattributes": {
|
| 5 |
-
"bytes":
|
| 6 |
-
"sha256": "
|
| 7 |
},
|
| 8 |
"LICENSE": {
|
| 9 |
"bytes": 11544,
|
| 10 |
"sha256": "bbedc3fda3305820b977265f01b8619d87570a6739de3a5582c3464840f1e57a"
|
| 11 |
},
|
| 12 |
"NOTICE": {
|
| 13 |
-
"bytes":
|
| 14 |
-
"sha256": "
|
| 15 |
},
|
| 16 |
"README.md": {
|
| 17 |
-
"bytes":
|
| 18 |
-
"sha256": "
|
| 19 |
},
|
| 20 |
-
"assets/
|
| 21 |
-
"bytes":
|
| 22 |
-
"sha256": "
|
| 23 |
},
|
| 24 |
-
"
|
| 25 |
-
"bytes":
|
| 26 |
-
"sha256": "
|
| 27 |
},
|
| 28 |
"benchmarks/random-six-report.md": {
|
| 29 |
-
"bytes":
|
| 30 |
-
"sha256": "
|
| 31 |
},
|
| 32 |
-
"
|
| 33 |
-
"bytes":
|
| 34 |
-
"sha256": "
|
| 35 |
-
},
|
| 36 |
-
"chat_template.jinja": {
|
| 37 |
-
"bytes": 7756,
|
| 38 |
-
"sha256": "a4aee8afcf2e0711942cf848899be66016f8d14a889ff9ede07bca099c28f715"
|
| 39 |
-
},
|
| 40 |
-
"config.json": {
|
| 41 |
-
"bytes": 2336,
|
| 42 |
-
"sha256": "27e3575d049c5a0187b783aecff914135be455439134368aba8cdcc6d678ebb7"
|
| 43 |
-
},
|
| 44 |
-
"generation_config.json": {
|
| 45 |
-
"bytes": 214,
|
| 46 |
-
"sha256": "df6f86c3fdce573ecdb55cec35f502ddda70e70abe78288a78164aef120232e7"
|
| 47 |
},
|
| 48 |
"provenance.json": {
|
| 49 |
-
"bytes":
|
| 50 |
-
"sha256": "
|
| 51 |
-
},
|
| 52 |
-
"runtime/entrypoint.sh": {
|
| 53 |
-
"bytes": 276,
|
| 54 |
-
"sha256": "742824765cb2f5361a476674e16a5d347abb9ae905e2798a2183121ea1ee4b07"
|
| 55 |
-
},
|
| 56 |
-
"runtime/label-token-ids.json": {
|
| 57 |
-
"bytes": 761,
|
| 58 |
-
"sha256": "8cc5610b02d5ba14358ebef59cd16f567767632fb6ea03de1565b18f1c003a40"
|
| 59 |
-
},
|
| 60 |
-
"runtime/patches/qwen35-text-quark-generated-scales.patch": {
|
| 61 |
-
"bytes": 1680,
|
| 62 |
-
"sha256": "920a8cabd8d2f6565e4f0c3296b1b0e154e48c227e4fd12f019033792cbe567f"
|
| 63 |
-
},
|
| 64 |
-
"runtime/patches/sglang-batch-result-processor.py": {
|
| 65 |
-
"bytes": 57803,
|
| 66 |
-
"sha256": "f7f58ef7f377e3df111c87d1803b6e0008bdf76726d80cbf1012275839e77da7"
|
| 67 |
-
},
|
| 68 |
-
"runtime/patches/sglang-logits-processor.py": {
|
| 69 |
-
"bytes": 54217,
|
| 70 |
-
"sha256": "aed498368a4402a84a4ef71a5962f1ff78cad265d4becbe5b3b677f1cb8ad9e2"
|
| 71 |
-
},
|
| 72 |
-
"runtime/patches/sglang-qwen35-text.py": {
|
| 73 |
-
"bytes": 12308,
|
| 74 |
-
"sha256": "52cdbda541ff612353adf5d5b04a6668954040b48cfa5209f59abbd662a023e1"
|
| 75 |
-
},
|
| 76 |
-
"runtime/patches/sglang-qwen35.py": {
|
| 77 |
-
"bytes": 106934,
|
| 78 |
-
"sha256": "f6975a9b3303002c25418b0d5b32c6bd40502ba6568ab7d5c847c6983705c01e"
|
| 79 |
-
},
|
| 80 |
-
"serve.sh": {
|
| 81 |
-
"bytes": 2065,
|
| 82 |
-
"sha256": "5f308855054cd6dd86fde051cc9771e02e1e160dc0ddbad1f5eed8e7b81a4b65"
|
| 83 |
-
},
|
| 84 |
-
"tokenizer.json": {
|
| 85 |
-
"bytes": 19989325,
|
| 86 |
-
"sha256": "06b9509352d2af50381ab2247e083b80d32d5c0aba91c272ca9ff729b6a0e523"
|
| 87 |
-
},
|
| 88 |
-
"tokenizer_config.json": {
|
| 89 |
-
"bytes": 1123,
|
| 90 |
-
"sha256": "bee8eba30f0eb4af73c0fe2cd06d0f89b657d7819941c438157ec42f7c80ea87"
|
| 91 |
-
},
|
| 92 |
-
"weights/TP-1-sig-e793a9432f30cd4e/READY": {
|
| 93 |
-
"bytes": 70,
|
| 94 |
-
"sha256": "b20cd5cb19a0b44c0189bb79b85ee0300f0453a31bfef7b39ac825b409cbb578"
|
| 95 |
-
},
|
| 96 |
-
"weights/TP-1-sig-e793a9432f30cd4e/checksum.json": {
|
| 97 |
-
"bytes": 359616,
|
| 98 |
-
"sha256": "829aa54dd4f440c10d85c6253a41891a69ba1717ace2d193cbfab3c1a7546c6e"
|
| 99 |
-
},
|
| 100 |
-
"weights/TP-1-sig-e793a9432f30cd4e/model-00000-common.safetensor": {
|
| 101 |
-
"bytes": 5268047400,
|
| 102 |
-
"sha256": "16e3906e9a161a6337a4a0f6d06e352d09360b37a9d4c4ea341ea978192ee289"
|
| 103 |
-
},
|
| 104 |
-
"weights/TP-1-sig-e793a9432f30cd4e/model-00001-common.safetensor": {
|
| 105 |
-
"bytes": 5120198752,
|
| 106 |
-
"sha256": "7ad7affbcc8912528ff2859568b51ee9427cc17119312ea8750cf118544ea5d3"
|
| 107 |
-
},
|
| 108 |
-
"weights/TP-1-sig-e793a9432f30cd4e/model-00002-common.safetensor": {
|
| 109 |
-
"bytes": 5254941984,
|
| 110 |
-
"sha256": "fa4a08a1e0e0de30543d343a629c6710e59575cd6f98f5beae789f34a59b79b7"
|
| 111 |
-
},
|
| 112 |
-
"weights/TP-1-sig-e793a9432f30cd4e/model-00003-common.safetensor": {
|
| 113 |
-
"bytes": 4014197624,
|
| 114 |
-
"sha256": "dfb557a70a97b91208837dac145569c844971062970f41466738e61e77d53eb3"
|
| 115 |
}
|
| 116 |
},
|
| 117 |
"format_version": 1,
|
| 118 |
"repository": "devanshbatham/nyx",
|
| 119 |
-
"total_bytes":
|
| 120 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"artifact": "nyx-4b-compact-iq4xs-mixed",
|
| 3 |
"files": {
|
| 4 |
".gitattributes": {
|
| 5 |
+
"bytes": 533,
|
| 6 |
+
"sha256": "ce45845c47d05a5fe3881ff4fcf541f1cbdd3d8d9fdb33bb395e6bdbbf240074"
|
| 7 |
},
|
| 8 |
"LICENSE": {
|
| 9 |
"bytes": 11544,
|
| 10 |
"sha256": "bbedc3fda3305820b977265f01b8619d87570a6739de3a5582c3464840f1e57a"
|
| 11 |
},
|
| 12 |
"NOTICE": {
|
| 13 |
+
"bytes": 601,
|
| 14 |
+
"sha256": "a7c85805e2a02824e60d752e1d543649918c081b45b150615a926ea2f79f2e91"
|
| 15 |
},
|
| 16 |
"README.md": {
|
| 17 |
+
"bytes": 4518,
|
| 18 |
+
"sha256": "dcb056c79af04ccbc3662af1bee3a480885cac0451d3322f2399bfca6cdead35"
|
| 19 |
},
|
| 20 |
+
"assets/IOSEVKA-OFL.md": {
|
| 21 |
+
"bytes": 4674,
|
| 22 |
+
"sha256": "4fbfa00e9fb9e9a774ab4c099bc6e8b2ac1a46f6492a2c260f958b004d172da6"
|
| 23 |
},
|
| 24 |
+
"assets/nyx-benchmarks.svg": {
|
| 25 |
+
"bytes": 121931,
|
| 26 |
+
"sha256": "d0096f7d804d89f1cebec9e1e966cb05c855382b9842f5741cb162e3f7b884d5"
|
| 27 |
},
|
| 28 |
"benchmarks/random-six-report.md": {
|
| 29 |
+
"bytes": 4372,
|
| 30 |
+
"sha256": "68f53d0f47f25a3cf92fe73be76537164270b18b10d374e2247456f7daf3036f"
|
| 31 |
},
|
| 32 |
+
"nyx.gguf": {
|
| 33 |
+
"bytes": 3288582784,
|
| 34 |
+
"sha256": "39d64fb19863cfc067e2411fcb2a0444113dda13798b15de5617736d91914359"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
},
|
| 36 |
"provenance.json": {
|
| 37 |
+
"bytes": 1000,
|
| 38 |
+
"sha256": "4e3775066d90006cf3376a3d64f72751d01eeb8de24a35e3badc3ac08866cef2"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
}
|
| 40 |
},
|
| 41 |
"format_version": 1,
|
| 42 |
"repository": "devanshbatham/nyx",
|
| 43 |
+
"total_bytes": 3288731957
|
| 44 |
}
|
runtime/entrypoint.sh
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env bash
|
| 2 |
-
set -euo pipefail
|
| 3 |
-
|
| 4 |
-
target=/sgl-workspace/sglang/python/sglang/srt/models/qwen3_5_text.py
|
| 5 |
-
cp /model/runtime/patches/sglang-qwen35-text.py "$target"
|
| 6 |
-
patch --batch "$target" < /model/runtime/patches/qwen35-text-quark-generated-scales.patch
|
| 7 |
-
exec python3 "$@"
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runtime/label-token-ids.json
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
[32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 5840, 1803, 1646, 1745, 13276, 8018, 1825, 28946, 15015, 29595, 11568, 939, 1354, 1058, 18183, 2456, 88898, 905, 1846, 802, 33869, 7839, 14006, 2860, 2926, 22828, 6844, 9798, 4738, 9265, 11261, 19278, 36513, 93801, 8335, 14544, 85266, 9110, 28000, 15137, 4525, 25261, 12717, 7116, 17078, 14497, 57339, 74909, 52072, 19305, 4887, 12607, 3580, 6281, 2036, 9362, 8533, 2080, 10911, 2925, 3040, 9690, 27731, 8023, 6901, 8702, 6211, 1123, 16307, 18990, 64045, 63037, 33380, 6151, 3392, 5449, 3967, 1113, 5095, 51923, 49600, 17099, 51483, 17756, 16037, 8135, 30237, 5683, 9992, 7444, 5751, 10284, 20887, 59884, 52396, 16103, 67547, 18535, 8006, 7263, 1425, 6878]
|
|
|
|
|
|
runtime/patches/qwen35-text-quark-generated-scales.patch
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
--- qwen3_5_text.py
|
| 2 |
-
+++ qwen3_5_text.py
|
| 3 |
-
@@ -242,17 +242,24 @@
|
| 4 |
-
loaded_params.add("lm_head.weight")
|
| 5 |
-
|
| 6 |
-
missing = set(params_dict) - loaded_params
|
| 7 |
-
- if (self.quant_config is not None
|
| 8 |
-
- and self.quant_config.get_name() == "fp8"
|
| 9 |
-
+ quant_name = (
|
| 10 |
-
+ self.quant_config.get_name() if self.quant_config is not None else None
|
| 11 |
-
+ )
|
| 12 |
-
+ if (quant_name in {"fp8", "quark_int4fp8_moe"}
|
| 13 |
-
and not self.quant_config.is_checkpoint_fp8_serialized):
|
| 14 |
-
- # Online BF16->FP8 creates these scales itself. They are deliberately
|
| 15 |
-
- # absent from the source BF16 checkpoint; all model weights still
|
| 16 |
-
- # have to load. Do not apply this exception to serialized FP8.
|
| 17 |
-
+ # Online quantizers create their scales from source weights. They are
|
| 18 |
-
+ # deliberately absent from the BF16 checkpoint; all source model
|
| 19 |
-
+ # weights still have to load. Never apply this to serialized FP8.
|
| 20 |
-
generated_suffixes = (
|
| 21 |
-
".mlp.experts.w13_weight_scale", ".mlp.experts.w2_weight_scale",
|
| 22 |
-
".mlp.experts.w13_weight_scale1", ".mlp.experts.w2_weight_scale1",
|
| 23 |
-
".attn.k_scale", ".attn.v_scale",
|
| 24 |
-
)
|
| 25 |
-
+ if quant_name == "quark_int4fp8_moe":
|
| 26 |
-
+ generated_suffixes += (
|
| 27 |
-
+ ".mlp.experts.w13_fp8_scale", ".mlp.experts.w2_fp8_scale",
|
| 28 |
-
+ ".mlp.experts.w13_int4_scale", ".mlp.experts.w2_int4_scale",
|
| 29 |
-
+ )
|
| 30 |
-
generated = {name for name in missing if name.endswith(generated_suffixes)}
|
| 31 |
-
missing -= generated
|
| 32 |
-
if missing:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runtime/patches/sglang-batch-result-processor.py
DELETED
|
@@ -1,1378 +0,0 @@
|
|
| 1 |
-
from __future__ import annotations
|
| 2 |
-
|
| 3 |
-
import logging
|
| 4 |
-
from dataclasses import dataclass
|
| 5 |
-
from typing import (
|
| 6 |
-
TYPE_CHECKING,
|
| 7 |
-
Callable,
|
| 8 |
-
List,
|
| 9 |
-
Optional,
|
| 10 |
-
Tuple,
|
| 11 |
-
Union,
|
| 12 |
-
)
|
| 13 |
-
|
| 14 |
-
import torch
|
| 15 |
-
|
| 16 |
-
from sglang.srt.disaggregation.utils import DisaggregationMode
|
| 17 |
-
from sglang.srt.environ import envs
|
| 18 |
-
from sglang.srt.layers.logits_processor import LogitsProcessorOutput
|
| 19 |
-
from sglang.srt.managers.schedule_batch import (
|
| 20 |
-
FINISH_ABORT,
|
| 21 |
-
FINISH_MATCHED_TOKEN,
|
| 22 |
-
Req,
|
| 23 |
-
ScheduleBatch,
|
| 24 |
-
mamba_lazy_spec_in_window,
|
| 25 |
-
)
|
| 26 |
-
from sglang.srt.mem_cache.common import (
|
| 27 |
-
maybe_cache_unfinished_req,
|
| 28 |
-
release_kv_cache,
|
| 29 |
-
)
|
| 30 |
-
from sglang.srt.model_executor.forward_batch_info import (
|
| 31 |
-
CaptureHiddenMode,
|
| 32 |
-
get_required_capture_hidden_mode,
|
| 33 |
-
get_server_return_hidden_states_mode,
|
| 34 |
-
)
|
| 35 |
-
from sglang.srt.runtime_context import (
|
| 36 |
-
get_disagg,
|
| 37 |
-
get_memory,
|
| 38 |
-
get_observability,
|
| 39 |
-
mamba_extra_buffer_lazy_enabled,
|
| 40 |
-
mamba_track_grid,
|
| 41 |
-
max_speculative_num_draft_tokens,
|
| 42 |
-
)
|
| 43 |
-
from sglang.srt.sampling.sampling_observer import CommittedTokens
|
| 44 |
-
from sglang.srt.speculative.base_spec_worker import BaseSpecWorker
|
| 45 |
-
from sglang.srt.state_capturer.indexer_topk import get_global_indexer_capturer
|
| 46 |
-
from sglang.srt.state_capturer.routed_experts import get_global_experts_capturer
|
| 47 |
-
|
| 48 |
-
if TYPE_CHECKING:
|
| 49 |
-
from sglang.srt.beam_search.coordinator import BeamCoordinator
|
| 50 |
-
from sglang.srt.configs.model_config import ModelConfig
|
| 51 |
-
from sglang.srt.disaggregation.decode_kvcache_offload_manager import (
|
| 52 |
-
DecodeKVCacheOffloadManager,
|
| 53 |
-
)
|
| 54 |
-
from sglang.srt.managers.hisparse_coordinator import HiSparseCoordinator
|
| 55 |
-
from sglang.srt.managers.scheduler_components.logprob_result_processor import (
|
| 56 |
-
SchedulerLogprobResultProcessor,
|
| 57 |
-
)
|
| 58 |
-
from sglang.srt.managers.scheduler_components.metrics_reporter import (
|
| 59 |
-
SchedulerMetricsReporter,
|
| 60 |
-
)
|
| 61 |
-
from sglang.srt.managers.scheduler_components.output_streamer import (
|
| 62 |
-
SchedulerOutputStreamer,
|
| 63 |
-
)
|
| 64 |
-
from sglang.srt.managers.tp_worker import BaseTpWorker
|
| 65 |
-
from sglang.srt.managers.utils import (
|
| 66 |
-
EmbeddingBatchResult,
|
| 67 |
-
GenerationBatchResult,
|
| 68 |
-
)
|
| 69 |
-
from sglang.srt.mem_cache.allocator import BaseTokenToKVPoolAllocator
|
| 70 |
-
from sglang.srt.mem_cache.base_prefix_cache import BasePrefixCache
|
| 71 |
-
from sglang.srt.mem_cache.memory_pool import ReqToTokenPool
|
| 72 |
-
from sglang.srt.observability.metrics_collector import SchedulerMetricsCollector
|
| 73 |
-
from sglang.srt.sampling.sampling_observer import HostAuxiliaryOutput
|
| 74 |
-
|
| 75 |
-
logger = logging.getLogger(__name__)
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
@dataclass(kw_only=True, slots=True, frozen=True)
|
| 79 |
-
class SchedulerBatchResultProcessor:
|
| 80 |
-
is_generation: bool
|
| 81 |
-
disaggregation_mode: DisaggregationMode
|
| 82 |
-
enable_overlap: bool
|
| 83 |
-
enable_overlap_mlx: bool
|
| 84 |
-
model_config: ModelConfig
|
| 85 |
-
token_to_kv_pool_allocator: BaseTokenToKVPoolAllocator
|
| 86 |
-
tree_cache: BasePrefixCache
|
| 87 |
-
hisparse_coordinator: Optional[HiSparseCoordinator]
|
| 88 |
-
req_to_token_pool: ReqToTokenPool
|
| 89 |
-
decode_offload_manager: Optional[DecodeKVCacheOffloadManager]
|
| 90 |
-
metrics_collector: SchedulerMetricsCollector
|
| 91 |
-
metrics_reporter: SchedulerMetricsReporter
|
| 92 |
-
draft_worker: BaseTpWorker
|
| 93 |
-
model_worker: BaseTpWorker
|
| 94 |
-
logprob_result_processor: SchedulerLogprobResultProcessor
|
| 95 |
-
output_streamer: SchedulerOutputStreamer
|
| 96 |
-
beam_coordinator: BeamCoordinator
|
| 97 |
-
abort_request: Callable
|
| 98 |
-
|
| 99 |
-
def process_batch_result_prebuilt(self, batch: ScheduleBatch):
|
| 100 |
-
assert self.disaggregation_mode == DisaggregationMode.DECODE
|
| 101 |
-
use_free_group = get_disagg().disaggregation_decode_enable_radix_cache
|
| 102 |
-
if use_free_group:
|
| 103 |
-
self.token_to_kv_pool_allocator.free_group_begin()
|
| 104 |
-
for req in batch.reqs:
|
| 105 |
-
req.time_stats.set_decode_prebuilt_finish_time()
|
| 106 |
-
req.update_finish_state()
|
| 107 |
-
if req.finished():
|
| 108 |
-
req.time_stats.set_quick_finish_time()
|
| 109 |
-
if get_memory().enable_hisparse:
|
| 110 |
-
self.hisparse_coordinator.request_finished(req)
|
| 111 |
-
release_kv_cache(req, self.tree_cache)
|
| 112 |
-
|
| 113 |
-
# Note: Logprobs should be handled on the prefill engine.
|
| 114 |
-
self.output_streamer.stream_output(batch.reqs, batch.return_logprob)
|
| 115 |
-
if use_free_group:
|
| 116 |
-
self.token_to_kv_pool_allocator.free_group_end()
|
| 117 |
-
|
| 118 |
-
def _maybe_collect_routed_experts(self, req: Req):
|
| 119 |
-
"""Collect routed experts for a finished request.
|
| 120 |
-
|
| 121 |
-
Returns immediately if `return_routed_experts` was not set on the
|
| 122 |
-
request, so non-opted-in reqs don't pay the host-gather cost.
|
| 123 |
-
|
| 124 |
-
Honors the caller's absolute start so the response covers
|
| 125 |
-
`[start_len, seqlen - 1)`. The default start_len is 0, which returns
|
| 126 |
-
the full sequence.
|
| 127 |
-
|
| 128 |
-
Logs a soft warning if the resulting tensor's row count differs from
|
| 129 |
-
the expected `seqlen - 1 - start_len`, to catch silent regressions.
|
| 130 |
-
"""
|
| 131 |
-
if not req.return_routed_experts:
|
| 132 |
-
return
|
| 133 |
-
capturer = get_global_experts_capturer()
|
| 134 |
-
if capturer is None:
|
| 135 |
-
return
|
| 136 |
-
start_len = req.routed_experts_start_len
|
| 137 |
-
seqlen = len(req.origin_input_ids) + len(req.output_ids_through_stop)
|
| 138 |
-
req.routed_experts = capturer.get_topk(
|
| 139 |
-
req_pool_idx=req.kv.req_pool_idx,
|
| 140 |
-
seqlen=seqlen,
|
| 141 |
-
req_to_token_pool=self.req_to_token_pool,
|
| 142 |
-
start_len=start_len,
|
| 143 |
-
)
|
| 144 |
-
|
| 145 |
-
expected_rows = max(0, seqlen - 1 - start_len)
|
| 146 |
-
if (
|
| 147 |
-
req.routed_experts is not None
|
| 148 |
-
and req.routed_experts.shape[0] != expected_rows
|
| 149 |
-
):
|
| 150 |
-
logger.warning(
|
| 151 |
-
"routed_experts row-count mismatch for req %s: got %d, expected %d "
|
| 152 |
-
"(seqlen=%d, raw_seqlen=%d, cached_tokens=%d, start_len=%s). "
|
| 153 |
-
"This indicates a silent bug.",
|
| 154 |
-
req.rid,
|
| 155 |
-
req.routed_experts.shape[0],
|
| 156 |
-
expected_rows,
|
| 157 |
-
seqlen,
|
| 158 |
-
req.seqlen,
|
| 159 |
-
req.cached_tokens,
|
| 160 |
-
req.routed_experts_start_len,
|
| 161 |
-
)
|
| 162 |
-
|
| 163 |
-
def _maybe_collect_indexer_topk(self, req: Req):
|
| 164 |
-
capturer = get_global_indexer_capturer()
|
| 165 |
-
if capturer is None:
|
| 166 |
-
return
|
| 167 |
-
seqlen = len(req.origin_input_ids) + len(req.output_ids_through_stop)
|
| 168 |
-
req.indexer_topk = capturer.get_topk(
|
| 169 |
-
req_pool_idx=req.kv.req_pool_idx,
|
| 170 |
-
seqlen=seqlen,
|
| 171 |
-
req_to_token_pool=self.req_to_token_pool,
|
| 172 |
-
)
|
| 173 |
-
|
| 174 |
-
def _maybe_collect_customized_info(
|
| 175 |
-
self,
|
| 176 |
-
i: int,
|
| 177 |
-
req: Req,
|
| 178 |
-
logits_output: LogitsProcessorOutput,
|
| 179 |
-
):
|
| 180 |
-
if logits_output is not None and logits_output.customized_info is not None:
|
| 181 |
-
if req.customized_info is None:
|
| 182 |
-
req.customized_info = {}
|
| 183 |
-
for k, v in logits_output.customized_info.items():
|
| 184 |
-
if k not in req.customized_info:
|
| 185 |
-
req.customized_info[k] = []
|
| 186 |
-
# Copy the element so it doesn't retain the entire batch
|
| 187 |
-
# tensor/array via a view reference.
|
| 188 |
-
elem = v[i]
|
| 189 |
-
if isinstance(elem, torch.Tensor):
|
| 190 |
-
elem = elem.clone()
|
| 191 |
-
elif hasattr(elem, "copy") and callable(elem.copy):
|
| 192 |
-
elem = elem.copy()
|
| 193 |
-
req.customized_info[k].append(elem)
|
| 194 |
-
|
| 195 |
-
@staticmethod
|
| 196 |
-
def _visible_output_len(req: Req) -> int:
|
| 197 |
-
return req.finished_len if req.finished_len is not None else len(req.output_ids)
|
| 198 |
-
|
| 199 |
-
@classmethod
|
| 200 |
-
def snapshot_auxiliary_output_starts(
|
| 201 |
-
cls,
|
| 202 |
-
batch: ScheduleBatch,
|
| 203 |
-
result: GenerationBatchResult,
|
| 204 |
-
) -> Optional[List[int]]:
|
| 205 |
-
if result.auxiliary_host_output is None:
|
| 206 |
-
return None
|
| 207 |
-
return [cls._visible_output_len(req) for req in batch.reqs]
|
| 208 |
-
|
| 209 |
-
@classmethod
|
| 210 |
-
def _build_auxiliary_commits(
|
| 211 |
-
cls,
|
| 212 |
-
batch: ScheduleBatch,
|
| 213 |
-
output_starts: List[int],
|
| 214 |
-
) -> List[Optional[CommittedTokens]]:
|
| 215 |
-
commits: List[Optional[CommittedTokens]] = []
|
| 216 |
-
for req, output_start in zip(batch.reqs, output_starts, strict=True):
|
| 217 |
-
output_end = cls._visible_output_len(req)
|
| 218 |
-
if output_end < output_start:
|
| 219 |
-
raise RuntimeError("committed output length moved backwards")
|
| 220 |
-
if output_end == output_start:
|
| 221 |
-
commits.append(None)
|
| 222 |
-
continue
|
| 223 |
-
commits.append(
|
| 224 |
-
CommittedTokens(
|
| 225 |
-
output_index=output_start,
|
| 226 |
-
token_ids=tuple(req.output_ids[output_start:output_end]),
|
| 227 |
-
)
|
| 228 |
-
)
|
| 229 |
-
return commits
|
| 230 |
-
|
| 231 |
-
@classmethod
|
| 232 |
-
def consume_auxiliary_output(
|
| 233 |
-
cls,
|
| 234 |
-
batch: ScheduleBatch,
|
| 235 |
-
output: HostAuxiliaryOutput,
|
| 236 |
-
output_starts: List[int],
|
| 237 |
-
) -> None:
|
| 238 |
-
output.consume(batch, cls._build_auxiliary_commits(batch, output_starts))
|
| 239 |
-
|
| 240 |
-
def process_batch_result_prefill(
|
| 241 |
-
self,
|
| 242 |
-
batch: ScheduleBatch,
|
| 243 |
-
result: Union[GenerationBatchResult, EmbeddingBatchResult],
|
| 244 |
-
):
|
| 245 |
-
skip_stream_req = None
|
| 246 |
-
self.token_to_kv_pool_allocator.free_group_begin()
|
| 247 |
-
|
| 248 |
-
if self.is_generation:
|
| 249 |
-
if result.copy_done is not None:
|
| 250 |
-
result.copy_done.synchronize()
|
| 251 |
-
auxiliary_output_starts = self.snapshot_auxiliary_output_starts(
|
| 252 |
-
batch, result
|
| 253 |
-
)
|
| 254 |
-
auxiliary_output = result.auxiliary_host_output
|
| 255 |
-
if result.routed_experts_output is not None:
|
| 256 |
-
result.routed_experts_output.finalize()
|
| 257 |
-
result.routed_experts_output = None
|
| 258 |
-
if result.indexer_topk_output is not None:
|
| 259 |
-
result.indexer_topk_output.finalize()
|
| 260 |
-
result.indexer_topk_output = None
|
| 261 |
-
|
| 262 |
-
(
|
| 263 |
-
logits_output,
|
| 264 |
-
next_token_ids,
|
| 265 |
-
extend_input_len_per_req,
|
| 266 |
-
extend_logprob_start_len_per_req,
|
| 267 |
-
) = (
|
| 268 |
-
result.logits_output,
|
| 269 |
-
result.next_token_ids,
|
| 270 |
-
result.extend_input_len_per_req,
|
| 271 |
-
result.extend_logprob_start_len_per_req,
|
| 272 |
-
)
|
| 273 |
-
|
| 274 |
-
# Move next_token_ids and logprobs to cpu
|
| 275 |
-
next_token_ids = next_token_ids.tolist()
|
| 276 |
-
self.move_logprobs_to_cpu(batch=batch, logits_output=logits_output)
|
| 277 |
-
|
| 278 |
-
self._validate_pp_skip_output_comm(batch, result)
|
| 279 |
-
|
| 280 |
-
hidden_state_offset = 0
|
| 281 |
-
prefill_hidden_capture_mode = self._get_prefill_hidden_capture_mode(
|
| 282 |
-
batch,
|
| 283 |
-
)
|
| 284 |
-
|
| 285 |
-
# Check finish conditions
|
| 286 |
-
logprob_pt = 0
|
| 287 |
-
|
| 288 |
-
for i, (req, next_token_id) in enumerate(zip(batch.reqs, next_token_ids)):
|
| 289 |
-
if (
|
| 290 |
-
batch.return_hidden_states
|
| 291 |
-
and logits_output.hidden_states is not None
|
| 292 |
-
):
|
| 293 |
-
assert extend_input_len_per_req is not None
|
| 294 |
-
hidden_state_offset = self._append_prefill_hidden_states(
|
| 295 |
-
req=req,
|
| 296 |
-
logits_output=logits_output,
|
| 297 |
-
hidden_state_offset=hidden_state_offset,
|
| 298 |
-
capture_hidden_mode=prefill_hidden_capture_mode,
|
| 299 |
-
extend_input_len=extend_input_len_per_req[i],
|
| 300 |
-
store=(
|
| 301 |
-
not req.finished()
|
| 302 |
-
and not req.is_retracted
|
| 303 |
-
and req.inflight_middle_chunks <= 0
|
| 304 |
-
),
|
| 305 |
-
)
|
| 306 |
-
|
| 307 |
-
if (
|
| 308 |
-
req.finished() and req.inflight_middle_chunks <= 0
|
| 309 |
-
) or req.is_retracted:
|
| 310 |
-
# Decode req in a mixed batch, or a retracted req. Keep an
|
| 311 |
-
# aborted middle chunk in the chunked branch long enough to
|
| 312 |
-
# drain its accounting without streaming it.
|
| 313 |
-
continue
|
| 314 |
-
|
| 315 |
-
if req.inflight_middle_chunks <= 0:
|
| 316 |
-
req.time_stats.set_prefill_finished_time()
|
| 317 |
-
|
| 318 |
-
if req.beam_group is not None:
|
| 319 |
-
# The relay point already replaced the sampled-token
|
| 320 |
-
# append; the group owns all finish semantics.
|
| 321 |
-
self.beam_coordinator.commit_prefill(
|
| 322 |
-
req, up_to_tick=batch.forward_iter
|
| 323 |
-
)
|
| 324 |
-
else:
|
| 325 |
-
# req output_ids are set here
|
| 326 |
-
req.output_ids.append(next_token_id)
|
| 327 |
-
|
| 328 |
-
self._maybe_update_reasoning_tokens(req, next_token_id)
|
| 329 |
-
|
| 330 |
-
req.update_finish_state()
|
| 331 |
-
# A mixed spec tail committed its pending bonus token; advance
|
| 332 |
-
# so the next spec prepare_for_decode reserves from the right base.
|
| 333 |
-
if (
|
| 334 |
-
not req.finished()
|
| 335 |
-
and batch.decoding_reqs
|
| 336 |
-
and req in batch.decoding_reqs
|
| 337 |
-
and not batch.spec_algorithm.is_none()
|
| 338 |
-
):
|
| 339 |
-
req.kv.kv_committed_len += 1
|
| 340 |
-
if req.finished():
|
| 341 |
-
self._maybe_collect_routed_experts(req)
|
| 342 |
-
self._maybe_collect_indexer_topk(req)
|
| 343 |
-
release_kv_cache(req, self.tree_cache)
|
| 344 |
-
req.time_stats.set_completion_time()
|
| 345 |
-
elif not batch.decoding_reqs or req not in batch.decoding_reqs:
|
| 346 |
-
maybe_cache_unfinished_req(req, self.tree_cache)
|
| 347 |
-
if get_memory().enable_hisparse:
|
| 348 |
-
self.hisparse_coordinator.admit_request_into_staging(req)
|
| 349 |
-
|
| 350 |
-
self._maybe_collect_customized_info(i, req, logits_output)
|
| 351 |
-
|
| 352 |
-
if batch.return_logprob:
|
| 353 |
-
logprob_pt = self._apply_prefill_logprobs(
|
| 354 |
-
req=req,
|
| 355 |
-
i=i,
|
| 356 |
-
logits_output=logits_output,
|
| 357 |
-
extend_input_len_per_req=extend_input_len_per_req,
|
| 358 |
-
extend_logprob_start_len_per_req=extend_logprob_start_len_per_req,
|
| 359 |
-
next_token_ids=next_token_ids,
|
| 360 |
-
logprob_pt=logprob_pt,
|
| 361 |
-
)
|
| 362 |
-
|
| 363 |
-
if req.return_sampling_mask:
|
| 364 |
-
self.add_sampling_mask_return_values(i, req, logits_output)
|
| 365 |
-
|
| 366 |
-
if req.grammar is not None:
|
| 367 |
-
self._apply_prefill_grammar(
|
| 368 |
-
req=req,
|
| 369 |
-
next_token_id=next_token_id,
|
| 370 |
-
already_advanced=result.grammar_advanced,
|
| 371 |
-
)
|
| 372 |
-
|
| 373 |
-
else:
|
| 374 |
-
# being chunked reqs' prefill is not finished
|
| 375 |
-
req.inflight_middle_chunks -= 1
|
| 376 |
-
# There is only at most one request being currently chunked.
|
| 377 |
-
# Because this request does not finish prefill,
|
| 378 |
-
# we don't want to stream the request currently being chunked.
|
| 379 |
-
skip_stream_req = req
|
| 380 |
-
|
| 381 |
-
# Incrementally update input logprobs.
|
| 382 |
-
if batch.return_logprob:
|
| 383 |
-
logprob_pt = self._apply_chunked_prefill_logprobs(
|
| 384 |
-
req=req,
|
| 385 |
-
i=i,
|
| 386 |
-
logits_output=logits_output,
|
| 387 |
-
extend_input_len_per_req=extend_input_len_per_req,
|
| 388 |
-
extend_logprob_start_len_per_req=extend_logprob_start_len_per_req,
|
| 389 |
-
logprob_pt=logprob_pt,
|
| 390 |
-
)
|
| 391 |
-
|
| 392 |
-
req.time_stats.set_last_chunked_prefill_finish_time()
|
| 393 |
-
|
| 394 |
-
if auxiliary_output is not None:
|
| 395 |
-
self.consume_auxiliary_output(
|
| 396 |
-
batch,
|
| 397 |
-
auxiliary_output,
|
| 398 |
-
auxiliary_output_starts,
|
| 399 |
-
)
|
| 400 |
-
|
| 401 |
-
else: # embedding or reward model
|
| 402 |
-
if result.copy_done is not None:
|
| 403 |
-
result.copy_done.synchronize()
|
| 404 |
-
|
| 405 |
-
embeddings = self._convert_embeddings(result=result)
|
| 406 |
-
phs = result.pooled_hidden_states
|
| 407 |
-
|
| 408 |
-
if phs is not None:
|
| 409 |
-
if isinstance(phs, list):
|
| 410 |
-
phs = [t.cpu().detach() for t in phs]
|
| 411 |
-
else:
|
| 412 |
-
phs = phs.cpu().detach()
|
| 413 |
-
|
| 414 |
-
# Check finish conditions
|
| 415 |
-
for i, req in enumerate(batch.reqs):
|
| 416 |
-
if req.is_retracted:
|
| 417 |
-
continue
|
| 418 |
-
|
| 419 |
-
req.embedding = embeddings[i]
|
| 420 |
-
if req.return_pooled_hidden_states and phs is not None:
|
| 421 |
-
req.pooled_hidden_state = phs[i]
|
| 422 |
-
if req.inflight_middle_chunks <= 0:
|
| 423 |
-
req.time_stats.set_prefill_finished_time()
|
| 424 |
-
# Dummy output token for embedding models
|
| 425 |
-
req.output_ids.append(0)
|
| 426 |
-
req.update_finish_state()
|
| 427 |
-
|
| 428 |
-
if req.finished():
|
| 429 |
-
release_kv_cache(req, self.tree_cache)
|
| 430 |
-
req.time_stats.set_completion_time()
|
| 431 |
-
else:
|
| 432 |
-
maybe_cache_unfinished_req(req, self.tree_cache)
|
| 433 |
-
else:
|
| 434 |
-
# being chunked reqs' prefill is not finished
|
| 435 |
-
req.inflight_middle_chunks -= 1
|
| 436 |
-
req.time_stats.set_last_chunked_prefill_finish_time()
|
| 437 |
-
|
| 438 |
-
self.token_to_kv_pool_allocator.free_group_end()
|
| 439 |
-
self.output_streamer.stream_output(
|
| 440 |
-
batch.reqs, batch.return_logprob, skip_stream_req
|
| 441 |
-
)
|
| 442 |
-
|
| 443 |
-
can_run_cuda_graph = result.can_run_cuda_graph
|
| 444 |
-
# None on decode->extend converted batches; they are decode work and
|
| 445 |
-
# have no prefill stats to report.
|
| 446 |
-
if batch.prefill_stats is not None:
|
| 447 |
-
self.metrics_reporter.report_prefill_stats(
|
| 448 |
-
batch=batch,
|
| 449 |
-
prefill_stats=batch.prefill_stats,
|
| 450 |
-
can_run_cuda_graph=can_run_cuda_graph,
|
| 451 |
-
dp_cooperation_info=batch.dp_cooperation_info,
|
| 452 |
-
)
|
| 453 |
-
|
| 454 |
-
def _convert_embeddings(self, *, result: EmbeddingBatchResult) -> list:
|
| 455 |
-
is_sparse = envs.SGLANG_EMBEDDINGS_SPARSE_HEAD.is_set()
|
| 456 |
-
|
| 457 |
-
embeddings = result.embeddings
|
| 458 |
-
|
| 459 |
-
if is_sparse:
|
| 460 |
-
batch_ids, token_ids = embeddings.indices()
|
| 461 |
-
values = embeddings.values()
|
| 462 |
-
|
| 463 |
-
embeddings = [{} for _ in range(embeddings.size(0))]
|
| 464 |
-
for i in range(batch_ids.shape[0]):
|
| 465 |
-
embeddings[batch_ids[i].item()][token_ids[i].item()] = values[i].item()
|
| 466 |
-
else:
|
| 467 |
-
if isinstance(embeddings, torch.Tensor):
|
| 468 |
-
embeddings = embeddings.tolist()
|
| 469 |
-
else:
|
| 470 |
-
embeddings = [tensor.tolist() for tensor in embeddings]
|
| 471 |
-
return embeddings
|
| 472 |
-
|
| 473 |
-
def move_logprobs_to_cpu(
|
| 474 |
-
self,
|
| 475 |
-
*,
|
| 476 |
-
batch: ScheduleBatch,
|
| 477 |
-
logits_output: LogitsProcessorOutput,
|
| 478 |
-
) -> None:
|
| 479 |
-
if batch.return_logprob:
|
| 480 |
-
if logits_output.next_token_logprobs is not None:
|
| 481 |
-
logits_output.next_token_logprobs = (
|
| 482 |
-
logits_output.next_token_logprobs.tolist()
|
| 483 |
-
)
|
| 484 |
-
if logits_output.input_token_logprobs is not None:
|
| 485 |
-
logits_output.input_token_logprobs = tuple(
|
| 486 |
-
logits_output.input_token_logprobs.tolist()
|
| 487 |
-
)
|
| 488 |
-
if logits_output.next_token_top_logprobs_val:
|
| 489 |
-
logits_output.next_token_top_logprobs_val = [
|
| 490 |
-
v.tolist() for v in logits_output.next_token_top_logprobs_val
|
| 491 |
-
]
|
| 492 |
-
logits_output.next_token_top_logprobs_idx = [
|
| 493 |
-
x.tolist() for x in logits_output.next_token_top_logprobs_idx
|
| 494 |
-
]
|
| 495 |
-
if logits_output.next_token_token_ids_logprobs_val:
|
| 496 |
-
logits_output.next_token_token_ids_logprobs_val = [
|
| 497 |
-
v.tolist() if isinstance(v, torch.Tensor) else v
|
| 498 |
-
for v in logits_output.next_token_token_ids_logprobs_val
|
| 499 |
-
]
|
| 500 |
-
|
| 501 |
-
def _apply_prefill_logprobs(
|
| 502 |
-
self,
|
| 503 |
-
*,
|
| 504 |
-
req: Req,
|
| 505 |
-
i: int,
|
| 506 |
-
logits_output: LogitsProcessorOutput,
|
| 507 |
-
extend_input_len_per_req: Optional[List[int]],
|
| 508 |
-
extend_logprob_start_len_per_req: Optional[List[int]],
|
| 509 |
-
next_token_ids: List[int],
|
| 510 |
-
logprob_pt: int,
|
| 511 |
-
) -> int:
|
| 512 |
-
assert extend_logprob_start_len_per_req is not None
|
| 513 |
-
assert extend_input_len_per_req is not None
|
| 514 |
-
extend_logprob_start_len = extend_logprob_start_len_per_req[i]
|
| 515 |
-
extend_input_len = extend_input_len_per_req[i]
|
| 516 |
-
|
| 517 |
-
num_input_logprobs = self.logprob_result_processor.calculate_num_input_logprobs(
|
| 518 |
-
req,
|
| 519 |
-
extend_input_len,
|
| 520 |
-
extend_logprob_start_len,
|
| 521 |
-
)
|
| 522 |
-
|
| 523 |
-
if req.return_logprob:
|
| 524 |
-
self.logprob_result_processor.add_logprob_return_values(
|
| 525 |
-
i,
|
| 526 |
-
req,
|
| 527 |
-
logprob_pt,
|
| 528 |
-
next_token_ids,
|
| 529 |
-
num_input_logprobs,
|
| 530 |
-
logits_output,
|
| 531 |
-
)
|
| 532 |
-
logprob_pt += num_input_logprobs
|
| 533 |
-
return logprob_pt
|
| 534 |
-
|
| 535 |
-
@staticmethod
|
| 536 |
-
def _validate_pp_skip_output_comm(
|
| 537 |
-
batch: ScheduleBatch,
|
| 538 |
-
result: Union[GenerationBatchResult, EmbeddingBatchResult],
|
| 539 |
-
):
|
| 540 |
-
"""Validate PP skip output comm correctness.
|
| 541 |
-
|
| 542 |
-
- When skip=True: all reqs must be middle chunks (inflight_middle_chunks > 0)
|
| 543 |
-
so placeholder zeros are never consumed via req.output_ids.append().
|
| 544 |
-
- When skip=False: at least one req should consume next_token_ids
|
| 545 |
-
(inflight_middle_chunks <= 0), otherwise warn.
|
| 546 |
-
"""
|
| 547 |
-
if not envs.SGLANG_PP_SKIP_PURE_CHUNKED_OUTPUT_COMM.get():
|
| 548 |
-
return
|
| 549 |
-
|
| 550 |
-
if not getattr(result, "skipped_output_comm", False):
|
| 551 |
-
if batch.forward_mode.is_extend() and not batch.forward_mode.is_prebuilt():
|
| 552 |
-
has_consumed_output = any(
|
| 553 |
-
req.inflight_middle_chunks <= 0
|
| 554 |
-
for req in batch.reqs
|
| 555 |
-
if not req.finished() and not req.is_retracted
|
| 556 |
-
)
|
| 557 |
-
if not has_consumed_output and len(batch.reqs) > 0:
|
| 558 |
-
chunks = list([r.inflight_middle_chunks for r in batch.reqs])
|
| 559 |
-
logger.warning(
|
| 560 |
-
f"PP non-skip output comm: no req consumed next_token_ids. "
|
| 561 |
-
f"contains_last_prefill_chunk={batch.contains_last_prefill_chunk}, "
|
| 562 |
-
f"num_reqs={len(batch.reqs)}, all inflight_middle_chunks={chunks}"
|
| 563 |
-
)
|
| 564 |
-
return
|
| 565 |
-
|
| 566 |
-
for req in batch.reqs:
|
| 567 |
-
if not req.finished() and not req.is_retracted:
|
| 568 |
-
assert req.inflight_middle_chunks > 0, (
|
| 569 |
-
f"PP skip output comm invariant violated: req {req.rid} "
|
| 570 |
-
f"has inflight_middle_chunks={req.inflight_middle_chunks} "
|
| 571 |
-
f"but output was skipped (contains_last_prefill_chunk="
|
| 572 |
-
f"{batch.contains_last_prefill_chunk}). "
|
| 573 |
-
f"Placeholder zeros would be appended to output_ids."
|
| 574 |
-
)
|
| 575 |
-
|
| 576 |
-
def _append_prefill_hidden_states(
|
| 577 |
-
self,
|
| 578 |
-
*,
|
| 579 |
-
req: Req,
|
| 580 |
-
logits_output: LogitsProcessorOutput,
|
| 581 |
-
hidden_state_offset: int,
|
| 582 |
-
capture_hidden_mode: CaptureHiddenMode,
|
| 583 |
-
extend_input_len: int,
|
| 584 |
-
store: bool = True,
|
| 585 |
-
) -> int:
|
| 586 |
-
if capture_hidden_mode.is_full():
|
| 587 |
-
start = hidden_state_offset
|
| 588 |
-
hidden_state_offset += extend_input_len
|
| 589 |
-
if not store or not req.return_hidden_states:
|
| 590 |
-
return hidden_state_offset
|
| 591 |
-
|
| 592 |
-
req_hidden_states = logits_output.hidden_states[start:hidden_state_offset]
|
| 593 |
-
if req.return_hidden_states is True:
|
| 594 |
-
req.hidden_states.append(req_hidden_states.cpu().clone().tolist())
|
| 595 |
-
elif req.return_hidden_states == "last":
|
| 596 |
-
req.hidden_states.append(req_hidden_states[-1].cpu().tolist())
|
| 597 |
-
elif capture_hidden_mode.is_last():
|
| 598 |
-
index = hidden_state_offset
|
| 599 |
-
hidden_state_offset += 1
|
| 600 |
-
if store and req.return_hidden_states:
|
| 601 |
-
req.hidden_states.append(
|
| 602 |
-
logits_output.hidden_states[index].cpu().tolist()
|
| 603 |
-
)
|
| 604 |
-
else:
|
| 605 |
-
raise ValueError(
|
| 606 |
-
f"Unexpected hidden states capture mode: {capture_hidden_mode}"
|
| 607 |
-
)
|
| 608 |
-
return hidden_state_offset
|
| 609 |
-
|
| 610 |
-
@staticmethod
|
| 611 |
-
def _append_decode_hidden_states(
|
| 612 |
-
*,
|
| 613 |
-
req: Req,
|
| 614 |
-
hidden_states: torch.Tensor,
|
| 615 |
-
start: int,
|
| 616 |
-
accept_len: int,
|
| 617 |
-
) -> None:
|
| 618 |
-
if accept_len <= 0:
|
| 619 |
-
return
|
| 620 |
-
|
| 621 |
-
if req.return_hidden_states == "last":
|
| 622 |
-
valid_accept_len = accept_len
|
| 623 |
-
if req.finished_len is not None:
|
| 624 |
-
step_start = len(req.output_ids) - accept_len
|
| 625 |
-
valid_accept_len = max(
|
| 626 |
-
0,
|
| 627 |
-
min(accept_len, req.finished_len - step_start),
|
| 628 |
-
)
|
| 629 |
-
if valid_accept_len > 0:
|
| 630 |
-
req.hidden_states[:] = [
|
| 631 |
-
hidden_states[start + valid_accept_len - 1].cpu().tolist()
|
| 632 |
-
]
|
| 633 |
-
else:
|
| 634 |
-
req.hidden_states.extend(
|
| 635 |
-
hidden_states[start : start + accept_len].cpu().tolist()
|
| 636 |
-
)
|
| 637 |
-
|
| 638 |
-
@staticmethod
|
| 639 |
-
def _get_prefill_hidden_capture_mode(batch: ScheduleBatch) -> CaptureHiddenMode:
|
| 640 |
-
return get_required_capture_hidden_mode(
|
| 641 |
-
max(
|
| 642 |
-
batch.return_hidden_states_mode,
|
| 643 |
-
get_server_return_hidden_states_mode(),
|
| 644 |
-
),
|
| 645 |
-
batch.spec_info,
|
| 646 |
-
)
|
| 647 |
-
|
| 648 |
-
def _apply_prefill_grammar(
|
| 649 |
-
self, *, req: Req, next_token_id: int, already_advanced: bool = False
|
| 650 |
-
) -> None:
|
| 651 |
-
# The grammar barrier may have already advanced the FSM over this prefilled
|
| 652 |
-
# token (spec overlap path); only advance if not, but always sync
|
| 653 |
-
# grammar.finished.
|
| 654 |
-
if not already_advanced:
|
| 655 |
-
# FIXME: this try-except block is for handling unexpected xgrammar issue.
|
| 656 |
-
try:
|
| 657 |
-
req.grammar.accept_token(next_token_id)
|
| 658 |
-
except ValueError as e:
|
| 659 |
-
# Grammar accept_token can raise ValueError if the token is not in the
|
| 660 |
-
# grammar. This can happen if the grammar is not set correctly or the
|
| 661 |
-
# token is invalid.
|
| 662 |
-
logger.error(
|
| 663 |
-
f"Grammar accept_token failed for req {req.rid} with token {next_token_id}: {e}"
|
| 664 |
-
)
|
| 665 |
-
req.to_finish = FINISH_ABORT()
|
| 666 |
-
req.grammar.finished = req.finished()
|
| 667 |
-
|
| 668 |
-
def _apply_chunked_prefill_logprobs(
|
| 669 |
-
self,
|
| 670 |
-
*,
|
| 671 |
-
req: Req,
|
| 672 |
-
i: int,
|
| 673 |
-
logits_output: LogitsProcessorOutput,
|
| 674 |
-
extend_input_len_per_req: Optional[List[int]],
|
| 675 |
-
extend_logprob_start_len_per_req: Optional[List[int]],
|
| 676 |
-
logprob_pt: int,
|
| 677 |
-
) -> int:
|
| 678 |
-
extend_logprob_start_len = extend_logprob_start_len_per_req[i]
|
| 679 |
-
extend_input_len = extend_input_len_per_req[i]
|
| 680 |
-
if extend_logprob_start_len < extend_input_len:
|
| 681 |
-
# Update input logprobs.
|
| 682 |
-
num_input_logprobs = (
|
| 683 |
-
self.logprob_result_processor.calculate_num_input_logprobs(
|
| 684 |
-
req,
|
| 685 |
-
extend_input_len,
|
| 686 |
-
extend_logprob_start_len,
|
| 687 |
-
)
|
| 688 |
-
)
|
| 689 |
-
if req.return_logprob:
|
| 690 |
-
self.logprob_result_processor.add_input_logprob_return_values(
|
| 691 |
-
i,
|
| 692 |
-
req,
|
| 693 |
-
logits_output,
|
| 694 |
-
logprob_pt,
|
| 695 |
-
num_input_logprobs,
|
| 696 |
-
last_prefill_chunk=False,
|
| 697 |
-
)
|
| 698 |
-
logprob_pt += num_input_logprobs
|
| 699 |
-
return logprob_pt
|
| 700 |
-
|
| 701 |
-
def _resolve_spec_v2_tokens(
|
| 702 |
-
self,
|
| 703 |
-
result: GenerationBatchResult,
|
| 704 |
-
batch: ScheduleBatch,
|
| 705 |
-
) -> List[List[int]]:
|
| 706 |
-
"""Resolve the padded next token ids for spec-v2 (overlap and non-overlap)."""
|
| 707 |
-
assert result.next_token_ids.is_cpu
|
| 708 |
-
assert result.accept_lens.is_cpu
|
| 709 |
-
|
| 710 |
-
next_token_ids = result.next_token_ids.tolist()
|
| 711 |
-
accept_lens = result.accept_lens.tolist()
|
| 712 |
-
result.num_correct_drafts = sum(accept_lens) - len(batch.reqs)
|
| 713 |
-
result.num_correct_drafts_per_req_cpu = [x - 1 for x in accept_lens]
|
| 714 |
-
|
| 715 |
-
block_accept_lens = (
|
| 716 |
-
result.block_accept_lens.tolist()
|
| 717 |
-
if result.block_accept_lens is not None
|
| 718 |
-
else None
|
| 719 |
-
)
|
| 720 |
-
result.num_block_accept_tokens = (
|
| 721 |
-
sum(block_accept_lens) if block_accept_lens else 0
|
| 722 |
-
)
|
| 723 |
-
cap_lens = result.cap_lens.tolist() if result.cap_lens is not None else None
|
| 724 |
-
result.num_cap_tokens = sum(cap_lens) if cap_lens else 0
|
| 725 |
-
|
| 726 |
-
# Feed the adaptive controller now that accept_lens is on CPU,
|
| 727 |
-
# instead of doing a synchronous GPU→CPU copy in the worker hot path.
|
| 728 |
-
# BaseSpecWorker provides a no-op default for non-adaptive workers.
|
| 729 |
-
self.model_worker.on_verify_complete_cpu(
|
| 730 |
-
result.num_correct_drafts_per_req_cpu, batch_size=len(batch.reqs)
|
| 731 |
-
)
|
| 732 |
-
|
| 733 |
-
# Advance the grammar FSM over this batch's committed tokens (idempotent):
|
| 734 |
-
# the EAGLE overlap path already did this inside verify() via the grammar
|
| 735 |
-
# barrier; otherwise advance now. advance_grammar_fsm self-gates on per-req
|
| 736 |
-
# grammar (the queued batch.copy() does not carry has_grammar) and consumes
|
| 737 |
-
# result.grammar_retained_tokens below instead of re-advancing.
|
| 738 |
-
self.advance_grammar_fsm(result, batch)
|
| 739 |
-
|
| 740 |
-
predict_tokens = []
|
| 741 |
-
# In adaptive spec-v2, the worker state may already have switched when this
|
| 742 |
-
# delayed result is processed. Use the draft token count recorded on result.
|
| 743 |
-
stride = result.speculative_num_draft_tokens
|
| 744 |
-
assert stride is not None, "spec-v2 result missing speculative_num_draft_tokens"
|
| 745 |
-
|
| 746 |
-
for i, req in enumerate(batch.reqs):
|
| 747 |
-
accept_tokens = next_token_ids[i * stride : i * stride + accept_lens[i]]
|
| 748 |
-
|
| 749 |
-
if req.is_retracted or req.finished():
|
| 750 |
-
# Nothing to settle: no worker pre-claims the bonus, so
|
| 751 |
-
# kv_committed_len already holds the committed prefix.
|
| 752 |
-
pass
|
| 753 |
-
else:
|
| 754 |
-
if req.grammar is not None:
|
| 755 |
-
# FSM already advanced + truncated by advance_grammar_fsm; reuse
|
| 756 |
-
# the retained (grammar-legal) run instead of advancing again.
|
| 757 |
-
accept_tokens = result.grammar_retained_tokens[i]
|
| 758 |
-
|
| 759 |
-
# Commit the full accepted run (drafts + bonus).
|
| 760 |
-
num_accept_tokens = len(accept_tokens)
|
| 761 |
-
req.kv.kv_committed_len += num_accept_tokens
|
| 762 |
-
req.spec_verify_ct += 1
|
| 763 |
-
|
| 764 |
-
num_correct_drafts = result.num_correct_drafts_per_req_cpu[i]
|
| 765 |
-
req.spec_num_correct_drafts += num_correct_drafts
|
| 766 |
-
req.update_spec_correct_drafts_histogram(num_correct_drafts)
|
| 767 |
-
|
| 768 |
-
if block_accept_lens is not None:
|
| 769 |
-
req.spec_num_block_accept_tokens += block_accept_lens[i]
|
| 770 |
-
if cap_lens is not None:
|
| 771 |
-
req.spec_num_cap_tokens += cap_lens[i]
|
| 772 |
-
req.update_spec_cap_lens_histogram(cap_lens[i])
|
| 773 |
-
|
| 774 |
-
predict_tokens.append(accept_tokens)
|
| 775 |
-
|
| 776 |
-
return predict_tokens
|
| 777 |
-
|
| 778 |
-
def _accept_grammar_tokens(
|
| 779 |
-
self, req: Req, tokens: Union[int, List[int]]
|
| 780 |
-
) -> List[int]:
|
| 781 |
-
"""Advance the grammar over the accepted token(s), stopping at the token
|
| 782 |
-
that terminates it.
|
| 783 |
-
|
| 784 |
-
``tokens`` is a single sampled token (normal decode) or the whole
|
| 785 |
-
verified run (spec decode). Returns the retained prefix; for spec the
|
| 786 |
-
suffix past grammar completion is dropped so it is never committed to KV
|
| 787 |
-
nor emitted. Advances the grammar FSM only -- ``grammar.finished`` is
|
| 788 |
-
synced by the caller once the finish state is updated.
|
| 789 |
-
"""
|
| 790 |
-
if isinstance(tokens, int):
|
| 791 |
-
tokens = [tokens]
|
| 792 |
-
retained = []
|
| 793 |
-
try:
|
| 794 |
-
for token_id in tokens:
|
| 795 |
-
req.grammar.accept_token(token_id)
|
| 796 |
-
retained.append(token_id)
|
| 797 |
-
if req.grammar.is_terminated():
|
| 798 |
-
break
|
| 799 |
-
except ValueError as e:
|
| 800 |
-
# accept_token raises ValueError if the token is not in the grammar
|
| 801 |
-
# (misconfigured grammar or invalid token); abort the request.
|
| 802 |
-
logger.error(
|
| 803 |
-
f"Grammar accept_token failed for req {req.rid} with token "
|
| 804 |
-
f"{tokens}: {e}"
|
| 805 |
-
)
|
| 806 |
-
req.to_finish = FINISH_ABORT()
|
| 807 |
-
return retained
|
| 808 |
-
|
| 809 |
-
def advance_grammar_fsm(
|
| 810 |
-
self, result: GenerationBatchResult, batch: ScheduleBatch
|
| 811 |
-
) -> None:
|
| 812 |
-
"""Advance each req's grammar FSM over the tokens THIS batch committed, and
|
| 813 |
-
(for decode) memoize the grammar-truncated run on ``result``.
|
| 814 |
-
|
| 815 |
-
This is the single place the spec-v2 FSM advances. It is idempotent
|
| 816 |
-
(``result.grammar_advanced``) so it runs either eagerly — inside ``verify(N)``
|
| 817 |
-
via the scheduler's grammar barrier, so the advance overlaps the target-verify
|
| 818 |
-
forward — or lazily from the result processors on the non-overlap / non-EAGLE
|
| 819 |
-
paths. It handles both decode (the accepted spec run) and extend (the single
|
| 820 |
-
prefilled token) results, so the barrier can resolve whatever the previous
|
| 821 |
-
batch was — e.g. the extend->decode boundary.
|
| 822 |
-
"""
|
| 823 |
-
if result.grammar_advanced or not batch.has_grammar:
|
| 824 |
-
return
|
| 825 |
-
is_decode = batch.forward_mode.is_decode()
|
| 826 |
-
if not (is_decode or batch.forward_mode.is_extend()):
|
| 827 |
-
return
|
| 828 |
-
if result.copy_done is not None:
|
| 829 |
-
result.copy_done.synchronize()
|
| 830 |
-
next_token_ids = result.next_token_ids.tolist()
|
| 831 |
-
|
| 832 |
-
if not is_decode:
|
| 833 |
-
# Extend: advance over the single token each completed-prefill req emitted
|
| 834 |
-
# (mirrors process_batch_result_prefill's per-req token indexing).
|
| 835 |
-
for i, req in enumerate(batch.reqs):
|
| 836 |
-
if (
|
| 837 |
-
req.grammar is None
|
| 838 |
-
or req.is_retracted
|
| 839 |
-
or req.finished()
|
| 840 |
-
or req.inflight_middle_chunks > 0
|
| 841 |
-
):
|
| 842 |
-
continue
|
| 843 |
-
self._accept_grammar_tokens(req, next_token_ids[i])
|
| 844 |
-
result.grammar_advanced = True
|
| 845 |
-
return
|
| 846 |
-
|
| 847 |
-
# Decode: only the spec-v2 path reaches here (the grammar barrier for
|
| 848 |
-
# spec-overlap workers and _resolve_spec_v2_tokens). Non-spec grammar decode
|
| 849 |
-
# advances its FSM in process_batch_result_decode and has no accept_lens, so
|
| 850 |
-
# bail out defensively.
|
| 851 |
-
if result.accept_lens is None:
|
| 852 |
-
return
|
| 853 |
-
accept_lens = result.accept_lens.tolist()
|
| 854 |
-
stride = result.speculative_num_draft_tokens
|
| 855 |
-
assert stride is not None, "spec-v2 result missing speculative_num_draft_tokens"
|
| 856 |
-
retained = [None] * len(batch.reqs)
|
| 857 |
-
for i, req in enumerate(batch.reqs):
|
| 858 |
-
if req.grammar is None or req.is_retracted or req.finished():
|
| 859 |
-
continue
|
| 860 |
-
accept_tokens = next_token_ids[i * stride : i * stride + accept_lens[i]]
|
| 861 |
-
# Stop accepting once the grammar terminates so the over-drafted suffix
|
| 862 |
-
# is never committed to KV nor emitted; this advances the FSM.
|
| 863 |
-
retained[i] = self._accept_grammar_tokens(req, accept_tokens)
|
| 864 |
-
result.grammar_retained_tokens = retained
|
| 865 |
-
result.grammar_advanced = True
|
| 866 |
-
|
| 867 |
-
def process_batch_result_idle(
|
| 868 |
-
self,
|
| 869 |
-
batch: ScheduleBatch,
|
| 870 |
-
result: GenerationBatchResult,
|
| 871 |
-
):
|
| 872 |
-
if result.copy_done is not None:
|
| 873 |
-
result.copy_done.synchronize()
|
| 874 |
-
|
| 875 |
-
self.output_streamer._stream_output_generation(
|
| 876 |
-
batch.reqs, batch.return_logprob, is_idle_batch=True
|
| 877 |
-
)
|
| 878 |
-
|
| 879 |
-
def process_batch_result_decode(
|
| 880 |
-
self,
|
| 881 |
-
batch: ScheduleBatch,
|
| 882 |
-
result: GenerationBatchResult,
|
| 883 |
-
):
|
| 884 |
-
if result.copy_done is not None:
|
| 885 |
-
result.copy_done.synchronize()
|
| 886 |
-
auxiliary_output_starts = self.snapshot_auxiliary_output_starts(batch, result)
|
| 887 |
-
auxiliary_output = result.auxiliary_host_output
|
| 888 |
-
if result.routed_experts_output is not None:
|
| 889 |
-
result.routed_experts_output.finalize()
|
| 890 |
-
result.routed_experts_output = None
|
| 891 |
-
if result.indexer_topk_output is not None:
|
| 892 |
-
result.indexer_topk_output.finalize()
|
| 893 |
-
result.indexer_topk_output = None
|
| 894 |
-
|
| 895 |
-
logits_output, next_token_ids, can_run_cuda_graph = (
|
| 896 |
-
result.logits_output,
|
| 897 |
-
result.next_token_ids,
|
| 898 |
-
result.can_run_cuda_graph,
|
| 899 |
-
)
|
| 900 |
-
|
| 901 |
-
next_token_ids, next_token_logprobs = self._normalize_decode_outputs(
|
| 902 |
-
batch=batch,
|
| 903 |
-
result=result,
|
| 904 |
-
logits_output=logits_output,
|
| 905 |
-
next_token_ids=next_token_ids,
|
| 906 |
-
)
|
| 907 |
-
|
| 908 |
-
self.metrics_reporter.num_generated_tokens += len(batch.reqs)
|
| 909 |
-
if not batch.spec_algorithm.is_none():
|
| 910 |
-
self.metrics_reporter.update_spec_metrics(
|
| 911 |
-
batch.batch_size(),
|
| 912 |
-
result.num_correct_drafts,
|
| 913 |
-
num_block_accept_tokens=result.num_block_accept_tokens,
|
| 914 |
-
num_cap_tokens=result.num_cap_tokens,
|
| 915 |
-
)
|
| 916 |
-
if get_observability().enable_metrics:
|
| 917 |
-
self.metrics_collector.increment_decode_cuda_graph_pass(
|
| 918 |
-
value=can_run_cuda_graph
|
| 919 |
-
)
|
| 920 |
-
|
| 921 |
-
self.token_to_kv_pool_allocator.free_group_begin()
|
| 922 |
-
|
| 923 |
-
# Folds the relay point's selection into the DAG and sets the finish
|
| 924 |
-
# states the loop below observes. Beam + spec is rejected at admission.
|
| 925 |
-
newly_finished_beam_groups = set()
|
| 926 |
-
if batch.spec_algorithm.is_none() and logits_output is not None:
|
| 927 |
-
newly_finished_beam_groups = self.beam_coordinator.commit_decode(batch)
|
| 928 |
-
|
| 929 |
-
for i, req in enumerate(batch.reqs):
|
| 930 |
-
req: Req
|
| 931 |
-
|
| 932 |
-
if req.beam_group is not None:
|
| 933 |
-
# Under overlap a finished row reappears for one overshoot tick;
|
| 934 |
-
# gate on the committing tick so this runs exactly once.
|
| 935 |
-
if req.finished() and (
|
| 936 |
-
id(req.beam_group) not in newly_finished_beam_groups
|
| 937 |
-
):
|
| 938 |
-
continue
|
| 939 |
-
req.time_stats.set_last_decode_finish_time()
|
| 940 |
-
self._handle_finish_state_updated_req(
|
| 941 |
-
req, batch, result, i, logits_output
|
| 942 |
-
)
|
| 943 |
-
continue
|
| 944 |
-
|
| 945 |
-
if (self.enable_overlap or self.enable_overlap_mlx) and (
|
| 946 |
-
req.finished() or req.is_retracted
|
| 947 |
-
):
|
| 948 |
-
# NOTE: This (req.finished() or req.is_retracted) should only happen when overlap scheduling is enabled.
|
| 949 |
-
# And all the over-allocated tokens will be freed in `release_kv_cache`.
|
| 950 |
-
continue
|
| 951 |
-
|
| 952 |
-
# next_token_id is a per-req list: 1 token for non-spec, the verified
|
| 953 |
-
# run for spec (already grammar-truncated in _resolve_spec_v2_tokens).
|
| 954 |
-
next_token_id = next_token_ids[i]
|
| 955 |
-
is_spec = not batch.spec_algorithm.is_none()
|
| 956 |
-
|
| 957 |
-
req.output_ids.extend(next_token_id)
|
| 958 |
-
new_accept_len = len(next_token_id)
|
| 959 |
-
|
| 960 |
-
self._maybe_update_reasoning_tokens(req, next_token_id)
|
| 961 |
-
req.time_stats.set_last_decode_finish_time()
|
| 962 |
-
req.update_finish_state(new_accept_len)
|
| 963 |
-
|
| 964 |
-
self._handle_finish_state_updated_req(req, batch, result, i, logits_output)
|
| 965 |
-
|
| 966 |
-
if req.return_logprob:
|
| 967 |
-
self._apply_decode_logprobs(
|
| 968 |
-
req=req,
|
| 969 |
-
i=i,
|
| 970 |
-
batch=batch,
|
| 971 |
-
next_token_id=next_token_id,
|
| 972 |
-
next_token_logprobs=next_token_logprobs,
|
| 973 |
-
logits_output=logits_output,
|
| 974 |
-
)
|
| 975 |
-
|
| 976 |
-
if req.return_sampling_mask:
|
| 977 |
-
# return_sampling_mask + speculative decoding is rejected at
|
| 978 |
-
# request entry, so this remains one support mask per token.
|
| 979 |
-
self.add_sampling_mask_return_values(i, req, logits_output)
|
| 980 |
-
|
| 981 |
-
if req.return_hidden_states and logits_output.hidden_states is not None:
|
| 982 |
-
# hidden_states is [bs * stride, hidden_dim], one row per emitted
|
| 983 |
-
# token; stride = speculative_num_draft_tokens for spec, 1 for non-spec.
|
| 984 |
-
stride = result.speculative_num_draft_tokens or 1
|
| 985 |
-
accept_len = len(next_token_id)
|
| 986 |
-
start = i * stride
|
| 987 |
-
self._append_decode_hidden_states(
|
| 988 |
-
req=req,
|
| 989 |
-
hidden_states=logits_output.hidden_states,
|
| 990 |
-
start=start,
|
| 991 |
-
accept_len=accept_len,
|
| 992 |
-
)
|
| 993 |
-
|
| 994 |
-
if req.grammar is not None:
|
| 995 |
-
if not is_spec:
|
| 996 |
-
# Normal decode advances the grammar for its single token
|
| 997 |
-
# here; spec already advanced it in _resolve_spec_v2_tokens.
|
| 998 |
-
self._accept_grammar_tokens(req, next_token_id)
|
| 999 |
-
req.grammar.finished = req.finished()
|
| 1000 |
-
|
| 1001 |
-
if auxiliary_output is not None:
|
| 1002 |
-
self.consume_auxiliary_output(
|
| 1003 |
-
batch,
|
| 1004 |
-
auxiliary_output,
|
| 1005 |
-
auxiliary_output_starts,
|
| 1006 |
-
)
|
| 1007 |
-
|
| 1008 |
-
self.output_streamer.stream_output(batch.reqs, batch.return_logprob)
|
| 1009 |
-
self.token_to_kv_pool_allocator.free_group_end()
|
| 1010 |
-
|
| 1011 |
-
self.metrics_reporter.forward_ct_decode = (
|
| 1012 |
-
self.metrics_reporter.forward_ct_decode + 1
|
| 1013 |
-
) % (1 << 30)
|
| 1014 |
-
self.metrics_reporter.report_decode_stats(
|
| 1015 |
-
can_run_cuda_graph,
|
| 1016 |
-
running_batch=batch,
|
| 1017 |
-
num_correct_drafts=result.num_correct_drafts,
|
| 1018 |
-
)
|
| 1019 |
-
|
| 1020 |
-
def _normalize_decode_outputs(
|
| 1021 |
-
self,
|
| 1022 |
-
*,
|
| 1023 |
-
batch: ScheduleBatch,
|
| 1024 |
-
result: GenerationBatchResult,
|
| 1025 |
-
logits_output: LogitsProcessorOutput,
|
| 1026 |
-
next_token_ids: Union[torch.Tensor, List[int]],
|
| 1027 |
-
) -> Tuple[Union[List[int], List[List[int]]], Optional[List[float]]]:
|
| 1028 |
-
next_token_logprobs = None
|
| 1029 |
-
# Normalize to a uniform per-req list of accepted tokens (List[List[int]]):
|
| 1030 |
-
# spec unpacks the padded verify output; non-spec wraps its single token.
|
| 1031 |
-
if not batch.spec_algorithm.is_none():
|
| 1032 |
-
next_token_ids = self._resolve_spec_v2_tokens(result, batch)
|
| 1033 |
-
else:
|
| 1034 |
-
# CUDA workers return a device tensor, MLX a host list[int]; both -> list.
|
| 1035 |
-
ids = (
|
| 1036 |
-
next_token_ids.tolist()
|
| 1037 |
-
if torch.is_tensor(next_token_ids)
|
| 1038 |
-
else next_token_ids
|
| 1039 |
-
)
|
| 1040 |
-
next_token_ids = [[t] for t in ids]
|
| 1041 |
-
|
| 1042 |
-
if batch.return_logprob:
|
| 1043 |
-
next_token_logprobs = logits_output.next_token_logprobs.tolist()
|
| 1044 |
-
if logits_output.next_token_top_logprobs_val:
|
| 1045 |
-
logits_output.next_token_top_logprobs_val = [
|
| 1046 |
-
v.tolist() for v in logits_output.next_token_top_logprobs_val
|
| 1047 |
-
]
|
| 1048 |
-
logits_output.next_token_top_logprobs_idx = [
|
| 1049 |
-
x.tolist() for x in logits_output.next_token_top_logprobs_idx
|
| 1050 |
-
]
|
| 1051 |
-
|
| 1052 |
-
if logits_output.next_token_token_ids_logprobs_val:
|
| 1053 |
-
logits_output.next_token_token_ids_logprobs_val = [
|
| 1054 |
-
v.tolist() if isinstance(v, torch.Tensor) else v
|
| 1055 |
-
for v in logits_output.next_token_token_ids_logprobs_val
|
| 1056 |
-
]
|
| 1057 |
-
return next_token_ids, next_token_logprobs
|
| 1058 |
-
|
| 1059 |
-
def _apply_decode_logprobs(
|
| 1060 |
-
self,
|
| 1061 |
-
*,
|
| 1062 |
-
req: Req,
|
| 1063 |
-
i: int,
|
| 1064 |
-
batch: ScheduleBatch,
|
| 1065 |
-
next_token_id: Union[int, List[int]],
|
| 1066 |
-
next_token_logprobs: list,
|
| 1067 |
-
logits_output: LogitsProcessorOutput,
|
| 1068 |
-
) -> None:
|
| 1069 |
-
# accepted_ids is already a per-req list; non-spec logprobs are flat, so
|
| 1070 |
-
# the scalar logprob still needs wrapping.
|
| 1071 |
-
if not batch.spec_algorithm.is_none():
|
| 1072 |
-
accepted_logprobs = next_token_logprobs[i]
|
| 1073 |
-
accepted_ids = next_token_id
|
| 1074 |
-
max_accept = len(accepted_logprobs)
|
| 1075 |
-
else:
|
| 1076 |
-
accepted_logprobs = [next_token_logprobs[i]]
|
| 1077 |
-
accepted_ids = next_token_id
|
| 1078 |
-
max_accept = 1
|
| 1079 |
-
|
| 1080 |
-
for j, tok_id in enumerate(accepted_ids):
|
| 1081 |
-
req.logprob.output_token_logprobs_val.append(accepted_logprobs[j])
|
| 1082 |
-
req.logprob.output_token_logprobs_idx.append(tok_id)
|
| 1083 |
-
if req.logprob.top_logprobs_num > 0:
|
| 1084 |
-
flat_idx = i * max_accept + j
|
| 1085 |
-
req.logprob.output_top_logprobs_val.append(
|
| 1086 |
-
logits_output.next_token_top_logprobs_val[flat_idx]
|
| 1087 |
-
)
|
| 1088 |
-
req.logprob.output_top_logprobs_idx.append(
|
| 1089 |
-
logits_output.next_token_top_logprobs_idx[flat_idx]
|
| 1090 |
-
)
|
| 1091 |
-
if req.logprob.token_ids_logprob is not None:
|
| 1092 |
-
flat_idx = i * max_accept + j
|
| 1093 |
-
req.logprob.output_token_ids_logprobs_val.append(
|
| 1094 |
-
logits_output.next_token_token_ids_logprobs_val[flat_idx]
|
| 1095 |
-
)
|
| 1096 |
-
req.logprob.output_token_ids_logprobs_idx.append(
|
| 1097 |
-
logits_output.next_token_token_ids_logprobs_idx[flat_idx]
|
| 1098 |
-
)
|
| 1099 |
-
|
| 1100 |
-
def add_sampling_mask_return_values(
|
| 1101 |
-
self,
|
| 1102 |
-
i: int,
|
| 1103 |
-
req: Req,
|
| 1104 |
-
output: LogitsProcessorOutput,
|
| 1105 |
-
) -> None:
|
| 1106 |
-
"""Attach sparse sampling support metadata to the return values."""
|
| 1107 |
-
mask = output.next_token_sampling_mask_idx
|
| 1108 |
-
logprobs = output.next_token_sampling_logprobs
|
| 1109 |
-
req.output_token_sampling_mask.append(None if mask is None else mask[i])
|
| 1110 |
-
req.output_token_sampling_logprobs.append(
|
| 1111 |
-
None if logprobs is None else logprobs[i]
|
| 1112 |
-
)
|
| 1113 |
-
|
| 1114 |
-
def _handle_finish_state_updated_req(
|
| 1115 |
-
self,
|
| 1116 |
-
req: Req,
|
| 1117 |
-
batch: ScheduleBatch,
|
| 1118 |
-
result: GenerationBatchResult,
|
| 1119 |
-
i: int,
|
| 1120 |
-
logits_output: LogitsProcessorOutput,
|
| 1121 |
-
):
|
| 1122 |
-
lazy = mamba_extra_buffer_lazy_enabled()
|
| 1123 |
-
known_mamba_boundary = None
|
| 1124 |
-
completed_mamba_boundary = None
|
| 1125 |
-
lookahead = 0
|
| 1126 |
-
if batch.mamba_track_mask_cpu is not None:
|
| 1127 |
-
completed_mamba_boundary = bool(batch.mamba_track_mask_cpu[i])
|
| 1128 |
-
lookahead = req.decode_batch_idx - batch.mamba_decode_batch_idx_cpu[i]
|
| 1129 |
-
assert lookahead in (0, 1), (
|
| 1130 |
-
f"mamba result lookahead={lookahead} for req {req.rid}; "
|
| 1131 |
-
"overlap advanced more than one decode batch"
|
| 1132 |
-
)
|
| 1133 |
-
if lookahead == 0:
|
| 1134 |
-
known_mamba_boundary = bool(batch.mamba_track_mask_cpu[i])
|
| 1135 |
-
else:
|
| 1136 |
-
known_mamba_boundary = bool(batch.mamba_track_mask_next_cpu[i])
|
| 1137 |
-
|
| 1138 |
-
if completed_mamba_boundary and not lazy:
|
| 1139 |
-
req.kv.mamba_last_track_idx = batch.mamba_track_buffer_indices[i]
|
| 1140 |
-
req.kv.mamba_last_track_seqlen = req.kv.kv_committed_len - lookahead
|
| 1141 |
-
elif (
|
| 1142 |
-
req.finished()
|
| 1143 |
-
and lazy
|
| 1144 |
-
and lookahead == 1
|
| 1145 |
-
and known_mamba_boundary
|
| 1146 |
-
and req.kv.mamba_next_track_idx == req.kv.mamba_last_track_idx
|
| 1147 |
-
):
|
| 1148 |
-
req.mamba_lazy_is_insert = False
|
| 1149 |
-
|
| 1150 |
-
# Called here (after update_finish_state) so req.finished() is valid
|
| 1151 |
-
# for mamba_lazy_post_decode_at_boundary inside.
|
| 1152 |
-
should_update = completed_mamba_boundary if lazy else known_mamba_boundary
|
| 1153 |
-
if should_update is None or should_update:
|
| 1154 |
-
self._mamba_prefix_cache_update(
|
| 1155 |
-
req,
|
| 1156 |
-
batch,
|
| 1157 |
-
result,
|
| 1158 |
-
i,
|
| 1159 |
-
known_boundary=not lazy and known_mamba_boundary is True,
|
| 1160 |
-
)
|
| 1161 |
-
|
| 1162 |
-
if (
|
| 1163 |
-
get_disagg().disaggregation_decode_enable_offload_kvcache
|
| 1164 |
-
and not req.finished()
|
| 1165 |
-
):
|
| 1166 |
-
self.decode_offload_manager.offload_kv_cache(req)
|
| 1167 |
-
|
| 1168 |
-
if req.finished():
|
| 1169 |
-
# isinstance narrowing: create_worker may also return plain
|
| 1170 |
-
# TpModelWorker-based drafts, which carry no spec-worker hooks.
|
| 1171 |
-
if isinstance(self.draft_worker, BaseSpecWorker):
|
| 1172 |
-
self.draft_worker.note_request_finished(
|
| 1173 |
-
rid=req.rid,
|
| 1174 |
-
natural_stop=isinstance(req.finished_reason, FINISH_MATCHED_TOKEN),
|
| 1175 |
-
)
|
| 1176 |
-
|
| 1177 |
-
# delete feature to save memory
|
| 1178 |
-
if req.multimodal_inputs is not None and req.session is None:
|
| 1179 |
-
req.multimodal_inputs.release_features()
|
| 1180 |
-
self._maybe_collect_routed_experts(req)
|
| 1181 |
-
self._maybe_collect_indexer_topk(req)
|
| 1182 |
-
|
| 1183 |
-
if get_disagg().disaggregation_decode_enable_offload_kvcache:
|
| 1184 |
-
# Asynchronously offload KV cache; release_kv_cache will be called after Device->Host transfer completes
|
| 1185 |
-
if not self.decode_offload_manager.offload_kv_cache(req):
|
| 1186 |
-
self.decode_offload_manager.finalize_release_on_finish(req)
|
| 1187 |
-
else:
|
| 1188 |
-
if get_memory().enable_hisparse:
|
| 1189 |
-
self.hisparse_coordinator.request_finished(req)
|
| 1190 |
-
prepare_release = getattr(
|
| 1191 |
-
self.model_worker, "prepare_for_kv_cache_release", None
|
| 1192 |
-
)
|
| 1193 |
-
if callable(prepare_release):
|
| 1194 |
-
prepare_release(req)
|
| 1195 |
-
is_insert = (
|
| 1196 |
-
req.mamba_lazy_is_insert
|
| 1197 |
-
if mamba_extra_buffer_lazy_enabled()
|
| 1198 |
-
else True
|
| 1199 |
-
)
|
| 1200 |
-
release_kv_cache(req, self.tree_cache, is_insert=is_insert)
|
| 1201 |
-
|
| 1202 |
-
req.time_stats.set_completion_time()
|
| 1203 |
-
|
| 1204 |
-
self._maybe_collect_customized_info(i, req, logits_output)
|
| 1205 |
-
|
| 1206 |
-
def _maybe_update_reasoning_tokens(
|
| 1207 |
-
self,
|
| 1208 |
-
req: Req,
|
| 1209 |
-
next_token_id: Union[int, List[int]],
|
| 1210 |
-
):
|
| 1211 |
-
think_end_ids = self.model_config.think_end_ids
|
| 1212 |
-
if req.require_reasoning and think_end_ids:
|
| 1213 |
-
req.update_reasoning_tokens(next_token_id, think_end_ids)
|
| 1214 |
-
|
| 1215 |
-
def _mamba_prefix_cache_update(
|
| 1216 |
-
self,
|
| 1217 |
-
req: Req,
|
| 1218 |
-
batch: ScheduleBatch,
|
| 1219 |
-
result: GenerationBatchResult,
|
| 1220 |
-
i: int,
|
| 1221 |
-
known_boundary: bool = False,
|
| 1222 |
-
) -> None:
|
| 1223 |
-
"""Update mamba track state at ping-pong boundaries.
|
| 1224 |
-
|
| 1225 |
-
Non-lazy: swap the ping-pong index so the next forward writes to
|
| 1226 |
-
the alternate slot.
|
| 1227 |
-
Lazy: keep the same index (prealloc handles the swap) and run
|
| 1228 |
-
post-decode cleanup to free the temporary second slot.
|
| 1229 |
-
"""
|
| 1230 |
-
if req.kv.mamba_ping_pong_track_buffer is None:
|
| 1231 |
-
return
|
| 1232 |
-
|
| 1233 |
-
lazy = mamba_extra_buffer_lazy_enabled()
|
| 1234 |
-
if known_boundary:
|
| 1235 |
-
self._mamba_assert_committed_len_lookahead(req)
|
| 1236 |
-
track_seqlen = req.kv.kv_committed_len
|
| 1237 |
-
assert track_seqlen % mamba_track_grid(self.tree_cache.page_size) == 0
|
| 1238 |
-
at_boundary = True
|
| 1239 |
-
else:
|
| 1240 |
-
at_boundary, track_seqlen = self._mamba_check_track_boundary(
|
| 1241 |
-
req, batch, result, i
|
| 1242 |
-
)
|
| 1243 |
-
|
| 1244 |
-
if lazy and not batch.spec_algorithm.is_none():
|
| 1245 |
-
# For spec, at_boundary means this step actually crossed an interval.
|
| 1246 |
-
self._mamba_lazy_spec_update(req, batch, i, at_boundary, track_seqlen)
|
| 1247 |
-
return
|
| 1248 |
-
|
| 1249 |
-
if not at_boundary:
|
| 1250 |
-
return
|
| 1251 |
-
|
| 1252 |
-
track_idx = req.kv.mamba_next_track_idx
|
| 1253 |
-
if not known_boundary and batch.mamba_track_buffer_indices is not None:
|
| 1254 |
-
track_idx = batch.mamba_track_buffer_indices[i]
|
| 1255 |
-
if not known_boundary:
|
| 1256 |
-
req.kv.mamba_last_track_seqlen = track_seqlen
|
| 1257 |
-
if lazy:
|
| 1258 |
-
self.mamba_lazy_post_decode_at_boundary(req, batch, track_idx)
|
| 1259 |
-
else:
|
| 1260 |
-
if not known_boundary:
|
| 1261 |
-
req.kv.mamba_last_track_idx = track_idx
|
| 1262 |
-
req.kv.mamba_next_track_idx = (
|
| 1263 |
-
batch.req_to_token_pool.get_mamba_ping_pong_other_idx(track_idx)
|
| 1264 |
-
)
|
| 1265 |
-
|
| 1266 |
-
def _mamba_lazy_spec_update(
|
| 1267 |
-
self, req: Req, batch: ScheduleBatch, i: int, crossed: bool, track_seqlen: int
|
| 1268 |
-
) -> None:
|
| 1269 |
-
"""Lazy + spec post-processing.
|
| 1270 |
-
|
| 1271 |
-
Running req with a confirmed crossing: the commit scattered the
|
| 1272 |
-
crossing state into the planned position; promote it to keep unless
|
| 1273 |
-
the plan already pointed at the keep slot (in-place fallback, or
|
| 1274 |
-
promoted by an earlier overlapped confirmation).
|
| 1275 |
-
Finishing req: donate the keep slot only if no scatter wrote it
|
| 1276 |
-
(this step) or may still write it (the in-flight next step).
|
| 1277 |
-
"""
|
| 1278 |
-
positions = batch.mamba_lazy_spec_track_positions_cpu
|
| 1279 |
-
planned_pos = (
|
| 1280 |
-
positions[i]
|
| 1281 |
-
if positions is not None and i < len(positions)
|
| 1282 |
-
else None # filtered/merged snapshot without a plan: be conservative
|
| 1283 |
-
)
|
| 1284 |
-
|
| 1285 |
-
if req.finished():
|
| 1286 |
-
# Skip the donation if a scatter wrote or may still write the keep slot.
|
| 1287 |
-
keep_written_by_this_step = (
|
| 1288 |
-
crossed and planned_pos == req.kv.mamba_next_track_idx
|
| 1289 |
-
)
|
| 1290 |
-
other_idx = 1 - req.kv.mamba_next_track_idx
|
| 1291 |
-
# Recompute the in-flight verify's plan (kv_committed_len is
|
| 1292 |
-
# frozen since its prepare, so the recompute is exact).
|
| 1293 |
-
keep_may_be_written_in_flight = req.kv.mamba_ping_pong_track_buffer[
|
| 1294 |
-
other_idx
|
| 1295 |
-
].item() == -1 and mamba_lazy_spec_in_window(
|
| 1296 |
-
req,
|
| 1297 |
-
mamba_track_grid(self.tree_cache.page_size),
|
| 1298 |
-
max_speculative_num_draft_tokens(),
|
| 1299 |
-
)
|
| 1300 |
-
if (
|
| 1301 |
-
planned_pos is None
|
| 1302 |
-
or keep_written_by_this_step
|
| 1303 |
-
or keep_may_be_written_in_flight
|
| 1304 |
-
):
|
| 1305 |
-
req.mamba_lazy_is_insert = False
|
| 1306 |
-
return
|
| 1307 |
-
|
| 1308 |
-
if not crossed or planned_pos is None:
|
| 1309 |
-
return
|
| 1310 |
-
if planned_pos != req.kv.mamba_next_track_idx:
|
| 1311 |
-
# Promote pending -> keep: free the old checkpoint, repoint.
|
| 1312 |
-
pool = batch.req_to_token_pool
|
| 1313 |
-
keep_idx = req.kv.mamba_next_track_idx
|
| 1314 |
-
keep_val = req.kv.mamba_ping_pong_track_buffer[keep_idx]
|
| 1315 |
-
pool.mamba_allocator.free(keep_val.unsqueeze(0))
|
| 1316 |
-
pool.set_mamba_ping_pong_slot(req, keep_idx, -1)
|
| 1317 |
-
req.kv.mamba_next_track_idx = planned_pos
|
| 1318 |
-
# else: in-place fallback, or promoted by an earlier confirmation —
|
| 1319 |
-
# keep holds the track_seqlen state either way.
|
| 1320 |
-
req.kv.mamba_last_track_idx = planned_pos
|
| 1321 |
-
req.kv.mamba_last_track_seqlen = track_seqlen
|
| 1322 |
-
|
| 1323 |
-
@staticmethod
|
| 1324 |
-
def _mamba_assert_committed_len_lookahead(req: Req) -> None:
|
| 1325 |
-
"""Alarm if overlap advances beyond the scheduler's modeled window."""
|
| 1326 |
-
assert req.output_ids, (
|
| 1327 |
-
"mamba track boundary reached before a decode token was appended "
|
| 1328 |
-
f"(req {req.rid}); output_ids is empty"
|
| 1329 |
-
)
|
| 1330 |
-
token_seq_len = len(req.origin_input_ids) + len(req.output_ids) - 1
|
| 1331 |
-
assert (req.kv.kv_committed_len - token_seq_len) in (0, 1), (
|
| 1332 |
-
f"mamba track boundary: kv_committed_len={req.kv.kv_committed_len} "
|
| 1333 |
-
f"leads seq_len={token_seq_len} by more than one (req {req.rid}); "
|
| 1334 |
-
"overlap lookahead wider than assumed"
|
| 1335 |
-
)
|
| 1336 |
-
|
| 1337 |
-
def _mamba_check_track_boundary(self, req, batch, result, i):
|
| 1338 |
-
"""Check if this decode step crosses a mamba track interval boundary.
|
| 1339 |
-
|
| 1340 |
-
Returns (at_boundary, track_seqlen). The boundary condition
|
| 1341 |
-
matches what the forward's tracking mask used:
|
| 1342 |
-
``prepare_for_decode`` increments both ``seq_lens_cpu`` and
|
| 1343 |
-
``kv_committed_len`` by 1, then checks
|
| 1344 |
-
``seq_lens_cpu % interval == 0``. Subtracting the overlap
|
| 1345 |
-
lookahead from ``kv_committed_len`` reproduces that check.
|
| 1346 |
-
|
| 1347 |
-
For spec decode, the boundary is detected by comparing the
|
| 1348 |
-
accepted seq_len range against interval boundaries.
|
| 1349 |
-
"""
|
| 1350 |
-
interval = mamba_track_grid(self.tree_cache.page_size)
|
| 1351 |
-
|
| 1352 |
-
if batch.spec_algorithm.is_none():
|
| 1353 |
-
lookahead = req.decode_batch_idx - batch.mamba_decode_batch_idx_cpu[i]
|
| 1354 |
-
committed_len = req.kv.kv_committed_len - lookahead
|
| 1355 |
-
if committed_len % interval == 0:
|
| 1356 |
-
return True, committed_len
|
| 1357 |
-
elif result.num_correct_drafts_per_req_cpu is not None:
|
| 1358 |
-
cur = req.seqlen - 1
|
| 1359 |
-
prev = cur - result.num_correct_drafts_per_req_cpu[i] - 1
|
| 1360 |
-
if cur // interval != prev // interval:
|
| 1361 |
-
return True, cur // interval * interval
|
| 1362 |
-
|
| 1363 |
-
return False, 0
|
| 1364 |
-
|
| 1365 |
-
def mamba_lazy_post_decode_at_boundary(
|
| 1366 |
-
self, req: Req, batch: ScheduleBatch, track_idx: int
|
| 1367 |
-
):
|
| 1368 |
-
"""Commit a completed lazy-mode boundary and free its old slot."""
|
| 1369 |
-
req.kv.mamba_last_track_idx = track_idx
|
| 1370 |
-
req.kv.mamba_next_track_idx = track_idx
|
| 1371 |
-
other_idx = 1 - track_idx
|
| 1372 |
-
other_val = req.kv.mamba_ping_pong_track_buffer[other_idx].item()
|
| 1373 |
-
if other_val != -1:
|
| 1374 |
-
pool = batch.req_to_token_pool
|
| 1375 |
-
pool.mamba_allocator.free(
|
| 1376 |
-
req.kv.mamba_ping_pong_track_buffer[other_idx].unsqueeze(0)
|
| 1377 |
-
)
|
| 1378 |
-
pool.set_mamba_ping_pong_slot(req, other_idx, -1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runtime/patches/sglang-logits-processor.py
DELETED
|
@@ -1,1272 +0,0 @@
|
|
| 1 |
-
# Copyright 2023-2024 SGLang Team
|
| 2 |
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 3 |
-
# you may not use this file except in compliance with the License.
|
| 4 |
-
# You may obtain a copy of the License at
|
| 5 |
-
#
|
| 6 |
-
# http://www.apache.org/licenses/LICENSE-2.0
|
| 7 |
-
#
|
| 8 |
-
# Unless required by applicable law or agreed to in writing, software
|
| 9 |
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 10 |
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 11 |
-
# See the License for the specific language governing permissions and
|
| 12 |
-
# limitations under the License.
|
| 13 |
-
# ==============================================================================
|
| 14 |
-
"""Logits processing."""
|
| 15 |
-
|
| 16 |
-
import dataclasses
|
| 17 |
-
import logging
|
| 18 |
-
from contextlib import contextmanager
|
| 19 |
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 20 |
-
|
| 21 |
-
import torch
|
| 22 |
-
from torch import nn
|
| 23 |
-
|
| 24 |
-
from sglang.kernels.ops.activation.softcap import (
|
| 25 |
-
softcap_inplace_logits as fused_softcap,
|
| 26 |
-
)
|
| 27 |
-
from sglang.srt.beam_search.logits_capture import BeamLogitsCapture
|
| 28 |
-
from sglang.srt.distributed import get_tp_group
|
| 29 |
-
from sglang.srt.distributed.device_communicators import triton_symm_mem_ag
|
| 30 |
-
from sglang.srt.environ import envs
|
| 31 |
-
from sglang.srt.layers import layernorm_sp
|
| 32 |
-
from sglang.srt.layers.aux_hidden_states import (
|
| 33 |
-
AuxHiddenStates,
|
| 34 |
-
pack_aux_hidden_states,
|
| 35 |
-
)
|
| 36 |
-
from sglang.srt.layers.dp_attention import (
|
| 37 |
-
DpPaddingMode,
|
| 38 |
-
attn_tp_all_gather,
|
| 39 |
-
attn_tp_all_gather_into_tensor,
|
| 40 |
-
dp_gather_replicate,
|
| 41 |
-
dp_scatter,
|
| 42 |
-
get_dp_device,
|
| 43 |
-
get_dp_dtype,
|
| 44 |
-
get_dp_hidden_size,
|
| 45 |
-
)
|
| 46 |
-
from sglang.srt.layers.logprob_processor import (
|
| 47 |
-
InputLogprobProcessor,
|
| 48 |
-
LogprobStage,
|
| 49 |
-
get_token_ids_logprobs_raw,
|
| 50 |
-
get_top_logprobs_raw,
|
| 51 |
-
)
|
| 52 |
-
from sglang.srt.layers.vocab_parallel_embedding import VocabParallelEmbedding
|
| 53 |
-
from sglang.srt.model_executor.forward_batch_info import (
|
| 54 |
-
CaptureHiddenMode,
|
| 55 |
-
ForwardBatch,
|
| 56 |
-
ForwardMode,
|
| 57 |
-
)
|
| 58 |
-
from sglang.srt.runtime_context import get_exec, get_parallel
|
| 59 |
-
from sglang.srt.sampling.sampling_observer import DeviceAuxiliaryOutput
|
| 60 |
-
from sglang.srt.utils.common import (
|
| 61 |
-
is_cpu,
|
| 62 |
-
is_npu,
|
| 63 |
-
is_pin_memory_available,
|
| 64 |
-
use_intel_amx_backend,
|
| 65 |
-
)
|
| 66 |
-
|
| 67 |
-
logger = logging.getLogger(__name__)
|
| 68 |
-
|
| 69 |
-
_is_npu = is_npu()
|
| 70 |
-
_is_cpu = is_cpu()
|
| 71 |
-
|
| 72 |
-
_UNQUANTIZED_LM_HEAD_METHODS = {
|
| 73 |
-
"UnquantizedEmbeddingMethod",
|
| 74 |
-
"UnquantizedLinearMethod",
|
| 75 |
-
"PackWeightMethod",
|
| 76 |
-
}
|
| 77 |
-
|
| 78 |
-
# None outside a FlashInfer autotune pass; inside one, whether that pass runs the
|
| 79 |
-
# LM head. Not-None means the forward's output is discarded -- attention backends
|
| 80 |
-
# read that via get_in_autotune_dummy_run() to skip a cross-node exchange.
|
| 81 |
-
# Skipping the LM head skips its [batch * dp_size, vocab] all-gather, which OOMs
|
| 82 |
-
# under DP attention with a tight mem_fraction_static.
|
| 83 |
-
_autotune_run_lm_head: Optional[bool] = None
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
def _trace_e2e_logits(stage: str, **fields) -> None:
|
| 87 |
-
if not envs.SGLANG_TRACE_LOGITS_E2E.get():
|
| 88 |
-
return
|
| 89 |
-
try:
|
| 90 |
-
parallel = get_parallel()
|
| 91 |
-
rank = f"dp={parallel.attn_dp_rank} " f"tp={parallel.tp_rank}"
|
| 92 |
-
except Exception:
|
| 93 |
-
rank = "rank=unknown"
|
| 94 |
-
details = " ".join(f"{key}={value}" for key, value in fields.items())
|
| 95 |
-
print(f"SGLANG_TRACE_LOGITS_E2E {rank} stage={stage} {details}", flush=True)
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
def _has_lm_head_runtime_attrs(lm_head, attr_names: Tuple[str, ...]) -> bool:
|
| 99 |
-
return all(hasattr(lm_head, attr_name) for attr_name in attr_names)
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
def should_apply_lm_head_quant_method(lm_head, quant_method) -> bool:
|
| 103 |
-
if (
|
| 104 |
-
quant_method is None
|
| 105 |
-
or not hasattr(lm_head, "weight")
|
| 106 |
-
or not callable(getattr(quant_method, "apply", None))
|
| 107 |
-
):
|
| 108 |
-
return False
|
| 109 |
-
|
| 110 |
-
method_name = type(quant_method).__name__
|
| 111 |
-
if method_name in _UNQUANTIZED_LM_HEAD_METHODS:
|
| 112 |
-
return False
|
| 113 |
-
|
| 114 |
-
# A shared target lm_head can retain the draft's stale ModelOpt method; use it
|
| 115 |
-
# only when the runtime tensor layout matches that method.
|
| 116 |
-
if method_name == "ModelOptFp4LinearMethod":
|
| 117 |
-
if lm_head.weight.dtype == torch.int32 and _has_lm_head_runtime_attrs(
|
| 118 |
-
lm_head,
|
| 119 |
-
(
|
| 120 |
-
"weight_scale",
|
| 121 |
-
"weight_global_scale",
|
| 122 |
-
"workspace",
|
| 123 |
-
"input_size_per_partition",
|
| 124 |
-
"output_size_per_partition",
|
| 125 |
-
),
|
| 126 |
-
):
|
| 127 |
-
return True
|
| 128 |
-
return lm_head.weight.dtype == torch.uint8 and _has_lm_head_runtime_attrs(
|
| 129 |
-
lm_head,
|
| 130 |
-
(
|
| 131 |
-
"weight_scale_interleaved",
|
| 132 |
-
"alpha",
|
| 133 |
-
"input_scale_inv",
|
| 134 |
-
"input_size_per_partition",
|
| 135 |
-
"output_size_per_partition",
|
| 136 |
-
),
|
| 137 |
-
)
|
| 138 |
-
if method_name == "ModelOptNvFp4A16LinearMethod":
|
| 139 |
-
return lm_head.weight.dtype == torch.int32 and _has_lm_head_runtime_attrs(
|
| 140 |
-
lm_head,
|
| 141 |
-
(
|
| 142 |
-
"weight_scale",
|
| 143 |
-
"weight_global_scale",
|
| 144 |
-
"workspace",
|
| 145 |
-
"input_size_per_partition",
|
| 146 |
-
"output_size_per_partition",
|
| 147 |
-
),
|
| 148 |
-
)
|
| 149 |
-
if method_name == "ModelOptFp8LinearMethod":
|
| 150 |
-
return (
|
| 151 |
-
lm_head.weight.dtype == torch.float8_e4m3fn
|
| 152 |
-
and _has_lm_head_runtime_attrs(lm_head, ("weight_scale", "input_scale"))
|
| 153 |
-
)
|
| 154 |
-
|
| 155 |
-
return True
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
# FlashInfer autotune skips the unprofiled LM-head all-gather; its
|
| 159 |
-
# [batch * dp_size, vocab] output can OOM under tight DP-attention memory.
|
| 160 |
-
_in_autotune_dummy_run = False
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
def get_in_autotune_dummy_run() -> bool:
|
| 164 |
-
return _autotune_run_lm_head is not None
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
@contextmanager
|
| 168 |
-
def autotune_dummy_run_mode(*, run_lm_head: bool):
|
| 169 |
-
global _autotune_run_lm_head
|
| 170 |
-
_autotune_run_lm_head = run_lm_head
|
| 171 |
-
try:
|
| 172 |
-
yield
|
| 173 |
-
finally:
|
| 174 |
-
_autotune_run_lm_head = None
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
@dataclasses.dataclass
|
| 178 |
-
class LogitsProcessorOutput:
|
| 179 |
-
## Part 1: This part will be assigned in python/sglang/srt/layers/logits_processor.py::LogitsProcessor
|
| 180 |
-
# The logits of the next tokens. shape: [#seq, vocab_size]
|
| 181 |
-
# Can be None for certain prefill-only requests (e.g., multi-item scoring) that don't need next token generation
|
| 182 |
-
next_token_logits: Optional[torch.Tensor]
|
| 183 |
-
# Used by speculative decoding (EAGLE)
|
| 184 |
-
# The last hidden layers
|
| 185 |
-
hidden_states: Optional[torch.Tensor] = None
|
| 186 |
-
|
| 187 |
-
## Part 2: This part will be assigned in python/sglang/srt/layers/sampler.py::Sampler
|
| 188 |
-
# he log probs of output tokens, if SGLANG_RETURN_ORIGINAL_LOGPROB = True, will get the log probs before applying temperature. If False, will get the log probs before applying temperature.
|
| 189 |
-
next_token_logprobs: Optional[torch.Tensor] = None
|
| 190 |
-
# The logprobs and ids of the top-k tokens in output positions. shape: [#seq, k]
|
| 191 |
-
next_token_top_logprobs_val: Optional[List] = None
|
| 192 |
-
next_token_top_logprobs_idx: Optional[List] = None
|
| 193 |
-
# The logprobs and ids of the requested token ids in output positions. shape: [#seq, n] (n is the number of requested token ids)
|
| 194 |
-
# Can contain either lists or GPU tensors (for delayed copy optimization in prefill-only requests)
|
| 195 |
-
next_token_token_ids_logprobs_val: Optional[
|
| 196 |
-
List[Union[List[float], torch.Tensor]]
|
| 197 |
-
] = None
|
| 198 |
-
next_token_token_ids_logprobs_idx: Optional[List] = None
|
| 199 |
-
# Sparse top-k/top-p/min-p support ids and selected-token logprob after
|
| 200 |
-
# truncation/renormalization. Only populated when requested.
|
| 201 |
-
next_token_sampling_mask_idx: Optional[List[Optional[List[int]]]] = None
|
| 202 |
-
next_token_sampling_logprobs: Optional[List[Optional[float]]] = None
|
| 203 |
-
|
| 204 |
-
## Part 3: Prefill-only. This part will be assigned in python/sglang/srt/layers/logits_processor.py::LogitsProcessor
|
| 205 |
-
# The logprobs of input tokens. shape: [#token]
|
| 206 |
-
input_token_logprobs: Optional[torch.Tensor] = None
|
| 207 |
-
# The logprobs and ids of the top-k tokens in input positions. shape: [#seq, #token, k]
|
| 208 |
-
input_top_logprobs_val: Optional[List] = None
|
| 209 |
-
input_top_logprobs_idx: Optional[List] = None
|
| 210 |
-
# The logprobs and ids of the requested token ids in input positions. shape: [#seq, n] (n is the number of requested token ids)
|
| 211 |
-
# Can contain either lists or GPU tensors (for delayed GPU-to-CPU transfer optimization)
|
| 212 |
-
input_token_ids_logprobs_val: Optional[List[Union[List[float], torch.Tensor]]] = (
|
| 213 |
-
None
|
| 214 |
-
)
|
| 215 |
-
input_token_ids_logprobs_idx: Optional[List] = None
|
| 216 |
-
|
| 217 |
-
## Part 4: Diffusion LLM only.
|
| 218 |
-
full_logits: Optional[torch.Tensor] = None
|
| 219 |
-
|
| 220 |
-
# Beam search only: raw pre-sample logits for the scheduler-side joint
|
| 221 |
-
# selection; see beam_search.logits_capture.
|
| 222 |
-
beam: Optional[BeamLogitsCapture] = None
|
| 223 |
-
|
| 224 |
-
## Part 5: Customized Info
|
| 225 |
-
customized_info: Optional[Dict[str, List[Any]]] = None
|
| 226 |
-
|
| 227 |
-
## Part 6: Temporary variables
|
| 228 |
-
# FIXME: These fields are not logits-related but are passed through here as a
|
| 229 |
-
# workaround since ForwardBatch is local to forward_batch_generation().
|
| 230 |
-
# They should be moved to GenerationBatchResult to keep this class clean.
|
| 231 |
-
mm_input_embeds: Optional[torch.Tensor] = None
|
| 232 |
-
|
| 233 |
-
# Scheduler-local output copied alongside the ordinary generation result.
|
| 234 |
-
auxiliary_device_output: Optional[DeviceAuxiliaryOutput] = None
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
@dataclasses.dataclass
|
| 238 |
-
class LogitsMetadata:
|
| 239 |
-
forward_mode: ForwardMode
|
| 240 |
-
capture_hidden_mode: CaptureHiddenMode = CaptureHiddenMode.NULL
|
| 241 |
-
next_token_logits_buffer: Optional[torch.Tensor] = None
|
| 242 |
-
|
| 243 |
-
extend_return_logprob: bool = False
|
| 244 |
-
extend_return_top_logprob: bool = False
|
| 245 |
-
extend_token_ids_logprob: bool = False
|
| 246 |
-
extend_seq_lens: Optional[torch.Tensor] = None
|
| 247 |
-
extend_seq_lens_cpu: Optional[List[int]] = None
|
| 248 |
-
extend_logprob_start_lens_cpu: Optional[List[int]] = None
|
| 249 |
-
extend_logprob_pruned_lens_cpu: Optional[List[int]] = None
|
| 250 |
-
top_logprobs_nums: Optional[List[int]] = None
|
| 251 |
-
extend_input_logprob_token_ids_gpu: Optional[torch.Tensor] = None
|
| 252 |
-
token_ids_logprobs: Optional[List[List[int]]] = None
|
| 253 |
-
|
| 254 |
-
# logits and logprobs post processing
|
| 255 |
-
temperature: torch.Tensor = None
|
| 256 |
-
top_p: torch.Tensor = None
|
| 257 |
-
|
| 258 |
-
# DP attention metadata. Not needed when DP attention is not used.
|
| 259 |
-
# Number of tokens in the request.
|
| 260 |
-
global_num_tokens_gpu: Optional[torch.Tensor] = None
|
| 261 |
-
# The start position of local hidden states.
|
| 262 |
-
dp_local_start_pos: Optional[torch.Tensor] = None
|
| 263 |
-
dp_local_num_tokens: Optional[torch.Tensor] = None
|
| 264 |
-
global_dp_buffer_len: Optional[int] = None
|
| 265 |
-
# Number of tokens to sample per DP rank
|
| 266 |
-
global_num_tokens_for_logprob_cpu: Optional[torch.Tensor] = None
|
| 267 |
-
global_num_tokens_for_logprob_gpu: Optional[torch.Tensor] = None
|
| 268 |
-
# The gather mode for DP attention
|
| 269 |
-
dp_padding_mode: Optional[DpPaddingMode] = None
|
| 270 |
-
|
| 271 |
-
# Whether this batch is prefill-only (no token generation needed)
|
| 272 |
-
is_prefill_only: bool = False
|
| 273 |
-
|
| 274 |
-
mm_input_embeds: Optional[torch.Tensor] = None
|
| 275 |
-
|
| 276 |
-
# DRAFT_EXTEND_V2: when set, lm_head runs only on these rows (see
|
| 277 |
-
# EagleDraftExtendInput.select_index).
|
| 278 |
-
draft_extend_select_index: Optional[torch.Tensor] = None
|
| 279 |
-
|
| 280 |
-
@classmethod
|
| 281 |
-
def from_forward_batch(cls, forward_batch: ForwardBatch):
|
| 282 |
-
if (
|
| 283 |
-
forward_batch.forward_mode.is_extend()
|
| 284 |
-
and forward_batch.return_logprob
|
| 285 |
-
and not forward_batch.forward_mode.is_target_verify()
|
| 286 |
-
):
|
| 287 |
-
extend_return_top_logprob = any(
|
| 288 |
-
x > 0 for x in forward_batch.top_logprobs_nums
|
| 289 |
-
)
|
| 290 |
-
extend_token_ids_logprob = any(
|
| 291 |
-
x is not None for x in forward_batch.token_ids_logprobs
|
| 292 |
-
)
|
| 293 |
-
extend_return_logprob = False
|
| 294 |
-
extend_logprob_pruned_lens_cpu = []
|
| 295 |
-
for extend_len, start_len in zip(
|
| 296 |
-
forward_batch.extend_seq_lens_cpu,
|
| 297 |
-
forward_batch.extend_logprob_start_lens_cpu,
|
| 298 |
-
):
|
| 299 |
-
if extend_len - start_len > 0:
|
| 300 |
-
extend_return_logprob = True
|
| 301 |
-
extend_logprob_pruned_lens_cpu.append(extend_len - start_len)
|
| 302 |
-
else:
|
| 303 |
-
extend_return_logprob = extend_return_top_logprob = (
|
| 304 |
-
extend_token_ids_logprob
|
| 305 |
-
) = extend_logprob_pruned_lens_cpu = False
|
| 306 |
-
|
| 307 |
-
if forward_batch.forward_mode.is_draft_extend_v2():
|
| 308 |
-
draft_extend_select_index = forward_batch.spec_info.select_index
|
| 309 |
-
else:
|
| 310 |
-
draft_extend_select_index = None
|
| 311 |
-
|
| 312 |
-
return cls(
|
| 313 |
-
forward_mode=forward_batch.forward_mode,
|
| 314 |
-
capture_hidden_mode=forward_batch.capture_hidden_mode,
|
| 315 |
-
next_token_logits_buffer=forward_batch.next_token_logits_buffer,
|
| 316 |
-
extend_return_logprob=extend_return_logprob,
|
| 317 |
-
extend_return_top_logprob=extend_return_top_logprob,
|
| 318 |
-
extend_token_ids_logprob=extend_token_ids_logprob,
|
| 319 |
-
extend_seq_lens=forward_batch.extend_seq_lens,
|
| 320 |
-
extend_seq_lens_cpu=forward_batch.extend_seq_lens_cpu,
|
| 321 |
-
extend_logprob_start_lens_cpu=forward_batch.extend_logprob_start_lens_cpu,
|
| 322 |
-
extend_logprob_pruned_lens_cpu=extend_logprob_pruned_lens_cpu,
|
| 323 |
-
top_logprobs_nums=forward_batch.top_logprobs_nums,
|
| 324 |
-
token_ids_logprobs=forward_batch.token_ids_logprobs,
|
| 325 |
-
extend_input_logprob_token_ids_gpu=forward_batch.extend_input_logprob_token_ids_gpu,
|
| 326 |
-
is_prefill_only=forward_batch.is_prefill_only,
|
| 327 |
-
global_num_tokens_gpu=forward_batch.global_num_tokens_gpu,
|
| 328 |
-
dp_local_start_pos=forward_batch.dp_local_start_pos,
|
| 329 |
-
dp_local_num_tokens=forward_batch.dp_local_num_tokens,
|
| 330 |
-
global_dp_buffer_len=forward_batch.global_dp_buffer_len,
|
| 331 |
-
global_num_tokens_for_logprob_cpu=forward_batch.global_num_tokens_for_logprob_cpu,
|
| 332 |
-
global_num_tokens_for_logprob_gpu=forward_batch.global_num_tokens_for_logprob_gpu,
|
| 333 |
-
dp_padding_mode=DpPaddingMode.SUM_LEN,
|
| 334 |
-
mm_input_embeds=forward_batch.mm_input_embeds,
|
| 335 |
-
draft_extend_select_index=draft_extend_select_index,
|
| 336 |
-
)
|
| 337 |
-
|
| 338 |
-
def compute_dp_attention_metadata(self):
|
| 339 |
-
cumtokens = torch.cumsum(self.global_num_tokens_for_logprob_gpu, dim=0)
|
| 340 |
-
dp_rank = get_parallel().attn_dp_rank
|
| 341 |
-
if dp_rank == 0:
|
| 342 |
-
dp_local_start_pos = torch.zeros_like(
|
| 343 |
-
self.global_num_tokens_for_logprob_gpu[0]
|
| 344 |
-
)
|
| 345 |
-
else:
|
| 346 |
-
dp_local_start_pos = cumtokens[dp_rank - 1]
|
| 347 |
-
|
| 348 |
-
self.dp_local_start_pos = dp_local_start_pos
|
| 349 |
-
self.dp_local_num_tokens = self.global_num_tokens_for_logprob_gpu[dp_rank]
|
| 350 |
-
|
| 351 |
-
hidden_size = get_dp_hidden_size()
|
| 352 |
-
dtype = get_dp_dtype()
|
| 353 |
-
device = get_dp_device()
|
| 354 |
-
|
| 355 |
-
if self.global_num_tokens_for_logprob_cpu is not None:
|
| 356 |
-
# create a smaller buffer to reduce peak memory usage
|
| 357 |
-
self.global_dp_buffer_len = sum(self.global_num_tokens_for_logprob_cpu)
|
| 358 |
-
else:
|
| 359 |
-
self.global_dp_buffer_len = self.global_dp_buffer_len
|
| 360 |
-
|
| 361 |
-
self.gathered_buffer = torch.empty(
|
| 362 |
-
(
|
| 363 |
-
self.global_dp_buffer_len,
|
| 364 |
-
hidden_size,
|
| 365 |
-
),
|
| 366 |
-
dtype=dtype,
|
| 367 |
-
device=device,
|
| 368 |
-
)
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
class LogitsProcessor(nn.Module):
|
| 372 |
-
def __init__(
|
| 373 |
-
self,
|
| 374 |
-
config,
|
| 375 |
-
skip_all_gather: bool = False,
|
| 376 |
-
logit_scale: Optional[float] = None,
|
| 377 |
-
return_full_logits: bool = False,
|
| 378 |
-
):
|
| 379 |
-
super().__init__()
|
| 380 |
-
self.config = config
|
| 381 |
-
# Decision-only optional projection. Public API renormalizes selected labels.
|
| 382 |
-
import os, json
|
| 383 |
-
self.jev_selected_ids = None
|
| 384 |
-
if os.getenv("JEV_SELECTED_HEAD", "0") == "1":
|
| 385 |
-
if get_parallel().tp_size != 1:
|
| 386 |
-
raise ValueError("Selected decision head requires TP1")
|
| 387 |
-
with open("/jev-label-token-ids.json") as label_file:
|
| 388 |
-
self.jev_selected_ids = json.load(label_file)
|
| 389 |
-
self.jev_head_cache = None
|
| 390 |
-
self.vocab_size = config.vocab_size
|
| 391 |
-
self.logit_scale = logit_scale
|
| 392 |
-
self.use_attn_tp_group = get_parallel().enable_dp_lm_head
|
| 393 |
-
self.use_tp_lm_head_all_to_all = get_parallel().enable_tp_lm_head_all_to_all
|
| 394 |
-
self.use_fp32_lm_head = get_exec().features.enable_fp32_lm_head
|
| 395 |
-
if self.use_attn_tp_group:
|
| 396 |
-
self.attn_tp_size = get_parallel().attn_tp_size
|
| 397 |
-
self.do_tensor_parallel_all_gather = (
|
| 398 |
-
not skip_all_gather and self.attn_tp_size > 1
|
| 399 |
-
)
|
| 400 |
-
self.do_tensor_parallel_all_gather_dp_attn = False
|
| 401 |
-
else:
|
| 402 |
-
self.do_tensor_parallel_all_gather = (
|
| 403 |
-
not skip_all_gather and get_parallel().tp_size > 1
|
| 404 |
-
)
|
| 405 |
-
self.do_tensor_parallel_all_gather_dp_attn = (
|
| 406 |
-
self.do_tensor_parallel_all_gather and get_parallel().attn_dp_size != 1
|
| 407 |
-
)
|
| 408 |
-
self.final_logit_softcapping = getattr(
|
| 409 |
-
self.config, "final_logit_softcapping", None
|
| 410 |
-
)
|
| 411 |
-
if (
|
| 412 |
-
self.final_logit_softcapping is not None
|
| 413 |
-
and self.final_logit_softcapping < 0
|
| 414 |
-
):
|
| 415 |
-
self.final_logit_softcapping = None
|
| 416 |
-
|
| 417 |
-
self.return_full_logits = return_full_logits
|
| 418 |
-
self.enable_mis = get_exec().features.enable_mis
|
| 419 |
-
self.rl_on_policy_target = get_exec().deterministic.rl_on_policy_target
|
| 420 |
-
|
| 421 |
-
self._logits_gatherer = triton_symm_mem_ag.MultimemAllGatherer(
|
| 422 |
-
max_tokens=triton_symm_mem_ag.recommended_max_tokens(
|
| 423 |
-
include_prefill=False, floor=128
|
| 424 |
-
),
|
| 425 |
-
enabled=self.do_tensor_parallel_all_gather and not self.use_attn_tp_group,
|
| 426 |
-
skip_entry_sync=True,
|
| 427 |
-
)
|
| 428 |
-
|
| 429 |
-
self.input_logprob_processor = InputLogprobProcessor()
|
| 430 |
-
|
| 431 |
-
def forward(
|
| 432 |
-
self,
|
| 433 |
-
input_ids,
|
| 434 |
-
hidden_states,
|
| 435 |
-
lm_head: VocabParallelEmbedding,
|
| 436 |
-
logits_metadata: Union[LogitsMetadata, ForwardBatch],
|
| 437 |
-
aux_hidden_states: Optional[AuxHiddenStates] = None,
|
| 438 |
-
hidden_states_before_norm: Optional[torch.Tensor] = None,
|
| 439 |
-
) -> LogitsProcessorOutput:
|
| 440 |
-
# Extract MIS indices before ForwardBatch → LogitsMetadata conversion
|
| 441 |
-
multi_item_delimiter_indices = None
|
| 442 |
-
if isinstance(logits_metadata, ForwardBatch):
|
| 443 |
-
multi_item_delimiter_indices = logits_metadata.multi_item_delimiter_indices
|
| 444 |
-
logits_metadata = LogitsMetadata.from_forward_batch(logits_metadata)
|
| 445 |
-
|
| 446 |
-
# Autotune dummy run discards this output. `is False` not `not`: None
|
| 447 |
-
# means no autotune pass, which must not skip. Placed before the MIS /
|
| 448 |
-
# DLLM / common dispatch so all three LM-head paths are skipped.
|
| 449 |
-
if _autotune_run_lm_head is False:
|
| 450 |
-
return LogitsProcessorOutput(next_token_logits=None)
|
| 451 |
-
|
| 452 |
-
# Under LayerNorm SP the decoder loop leaves these sequence-sharded; undo
|
| 453 |
-
# that before the LM head, which must not participate.
|
| 454 |
-
hidden_states, hidden_states_before_norm = layernorm_sp.maybe_exit_gather(
|
| 455 |
-
hidden_states=hidden_states,
|
| 456 |
-
hidden_states_before_norm=hidden_states_before_norm,
|
| 457 |
-
input_ids=input_ids,
|
| 458 |
-
forward_mode=logits_metadata.forward_mode,
|
| 459 |
-
)
|
| 460 |
-
|
| 461 |
-
# Multi-item scoring only for prefill-only requests with pre-computed indices.
|
| 462 |
-
if multi_item_delimiter_indices is not None and logits_metadata.is_prefill_only:
|
| 463 |
-
return self.compute_logprobs_for_multi_item_scoring(
|
| 464 |
-
input_ids,
|
| 465 |
-
hidden_states,
|
| 466 |
-
lm_head,
|
| 467 |
-
logits_metadata,
|
| 468 |
-
multi_item_delimiter_indices,
|
| 469 |
-
)
|
| 470 |
-
|
| 471 |
-
# Diffusion LLM only.
|
| 472 |
-
if logits_metadata.forward_mode.is_dllm_extend():
|
| 473 |
-
return self._get_dllm_logits(hidden_states, lm_head, logits_metadata)
|
| 474 |
-
|
| 475 |
-
# Get the last hidden states and last logits for the next token prediction
|
| 476 |
-
(
|
| 477 |
-
pruned_states,
|
| 478 |
-
pruned_states_before_norm,
|
| 479 |
-
aux_pruned_states,
|
| 480 |
-
sample_indices,
|
| 481 |
-
input_logprob_indices,
|
| 482 |
-
token_to_seq_idx,
|
| 483 |
-
) = self._get_pruned_states(
|
| 484 |
-
hidden_states,
|
| 485 |
-
hidden_states_before_norm,
|
| 486 |
-
aux_hidden_states,
|
| 487 |
-
logits_metadata,
|
| 488 |
-
)
|
| 489 |
-
|
| 490 |
-
hidden_states_to_store = self._get_hidden_states_to_store(
|
| 491 |
-
hidden_states,
|
| 492 |
-
hidden_states_before_norm,
|
| 493 |
-
aux_hidden_states,
|
| 494 |
-
pruned_states,
|
| 495 |
-
pruned_states_before_norm,
|
| 496 |
-
aux_pruned_states,
|
| 497 |
-
sample_indices,
|
| 498 |
-
logits_metadata,
|
| 499 |
-
)
|
| 500 |
-
del hidden_states
|
| 501 |
-
|
| 502 |
-
if not logits_metadata.extend_return_logprob:
|
| 503 |
-
# Compute logits for both input and sampled tokens.
|
| 504 |
-
logits = self._get_logits(pruned_states, lm_head, logits_metadata)
|
| 505 |
-
sampled_logits = (
|
| 506 |
-
logits[sample_indices] if sample_indices is not None else logits
|
| 507 |
-
)
|
| 508 |
-
|
| 509 |
-
# Decode mode or extend mode without return_logprob.
|
| 510 |
-
return LogitsProcessorOutput(
|
| 511 |
-
next_token_logits=sampled_logits,
|
| 512 |
-
hidden_states=hidden_states_to_store,
|
| 513 |
-
mm_input_embeds=logits_metadata.mm_input_embeds,
|
| 514 |
-
)
|
| 515 |
-
|
| 516 |
-
logprobs_result, sampled_logits = self.input_logprob_processor.forward(
|
| 517 |
-
pruned_states=pruned_states,
|
| 518 |
-
sample_indices=sample_indices,
|
| 519 |
-
input_logprob_indices=input_logprob_indices,
|
| 520 |
-
token_to_seq_idx=token_to_seq_idx,
|
| 521 |
-
lm_head=lm_head,
|
| 522 |
-
get_logits_fn=self._get_logits,
|
| 523 |
-
logits_metadata=logits_metadata,
|
| 524 |
-
skip_chunking_for_dp_attn=self.do_tensor_parallel_all_gather_dp_attn,
|
| 525 |
-
)
|
| 526 |
-
|
| 527 |
-
logits_output = LogitsProcessorOutput(
|
| 528 |
-
next_token_logits=sampled_logits,
|
| 529 |
-
hidden_states=hidden_states_to_store,
|
| 530 |
-
mm_input_embeds=logits_metadata.mm_input_embeds,
|
| 531 |
-
)
|
| 532 |
-
logprobs_result.write_input_to(logits_output)
|
| 533 |
-
return logits_output
|
| 534 |
-
|
| 535 |
-
def _get_pruned_states(
|
| 536 |
-
self,
|
| 537 |
-
hidden_states: torch.Tensor,
|
| 538 |
-
hidden_states_before_norm: Optional[torch.Tensor],
|
| 539 |
-
aux_hidden_states: Optional[AuxHiddenStates],
|
| 540 |
-
logits_metadata: LogitsMetadata,
|
| 541 |
-
):
|
| 542 |
-
pruned_states_before_norm: Optional[torch.Tensor] = None
|
| 543 |
-
aux_pruned_states = None
|
| 544 |
-
token_to_seq_idx = []
|
| 545 |
-
|
| 546 |
-
if (
|
| 547 |
-
logits_metadata.forward_mode.is_decode_or_idle()
|
| 548 |
-
or logits_metadata.forward_mode.is_target_verify()
|
| 549 |
-
or logits_metadata.forward_mode.is_draft_extend_v2()
|
| 550 |
-
):
|
| 551 |
-
if logits_metadata.draft_extend_select_index is not None:
|
| 552 |
-
# Only next_token_logits narrows to [bs, vocab]; the
|
| 553 |
-
# FULL-capture hidden stays unpruned.
|
| 554 |
-
pruned_states = hidden_states[logits_metadata.draft_extend_select_index]
|
| 555 |
-
else:
|
| 556 |
-
pruned_states = hidden_states
|
| 557 |
-
pruned_states_before_norm = hidden_states_before_norm
|
| 558 |
-
if aux_hidden_states is not None:
|
| 559 |
-
aux_pruned_states = (
|
| 560 |
-
aux_hidden_states
|
| 561 |
-
if isinstance(aux_hidden_states, torch.Tensor)
|
| 562 |
-
else [hidden for hidden in aux_hidden_states]
|
| 563 |
-
)
|
| 564 |
-
sample_indices = None
|
| 565 |
-
input_logprob_indices = None
|
| 566 |
-
|
| 567 |
-
elif (
|
| 568 |
-
logits_metadata.forward_mode.is_extend()
|
| 569 |
-
and not logits_metadata.extend_return_logprob
|
| 570 |
-
):
|
| 571 |
-
# Prefill without input logprobs.
|
| 572 |
-
last_index = torch.cumsum(logits_metadata.extend_seq_lens, dim=0) - 1
|
| 573 |
-
pruned_states = hidden_states[last_index]
|
| 574 |
-
if hidden_states_before_norm is not None:
|
| 575 |
-
pruned_states_before_norm = hidden_states_before_norm[last_index]
|
| 576 |
-
if aux_hidden_states is not None:
|
| 577 |
-
aux_pruned_states = (
|
| 578 |
-
aux_hidden_states[last_index]
|
| 579 |
-
if isinstance(aux_hidden_states, torch.Tensor)
|
| 580 |
-
else [hidden[last_index] for hidden in aux_hidden_states]
|
| 581 |
-
)
|
| 582 |
-
sample_indices = None
|
| 583 |
-
input_logprob_indices = None
|
| 584 |
-
else:
|
| 585 |
-
# Prefill with input logprobs.
|
| 586 |
-
# Find 4 different indices.
|
| 587 |
-
# 1. pruned_states: hidden states that we want logprobs from.
|
| 588 |
-
# 2. sample_indices: Indices that have sampled tokens.
|
| 589 |
-
# 3. input_logprob_indices: Indices that have input logprob tokens.
|
| 590 |
-
# 4. token_to_seq_idx: map each token to its sequence index
|
| 591 |
-
#
|
| 592 |
-
# Example
|
| 593 |
-
# -------
|
| 594 |
-
# Suppose a batch (flattened by sequence):
|
| 595 |
-
# [t00, t01, t02, t03, t10, t11, t12, t13, t14, t20, t21, t22, t23, t24, t25]
|
| 596 |
-
# extend_seq_lens_cpu = [4, 5, 6]
|
| 597 |
-
# extend_logprob_start_lens_cpu = [0, 5, 3]
|
| 598 |
-
#
|
| 599 |
-
# Then, the indices are:
|
| 600 |
-
# pruned_states -> [t00, t01, t02, t03, t14, t23, t24, t25]
|
| 601 |
-
# sample_indices -> [3, 4, 7]
|
| 602 |
-
# input_logprob_indices -> [0, 1, 2, 3, 5, 6, 7]
|
| 603 |
-
# token_to_seq_idx -> [0, 0, 0, 0, 1, 2, 2, 2]
|
| 604 |
-
#
|
| 605 |
-
# If chunk is enabled and chunk_size = 3, the chunks will be computed in a chunked manner:
|
| 606 |
-
# [t00, t01, t02], [t03, t14, t23], [t24, t25]
|
| 607 |
-
|
| 608 |
-
sample_index_pt = -1
|
| 609 |
-
sample_indices = []
|
| 610 |
-
input_logprob_indices_pt = 0
|
| 611 |
-
input_logprob_indices = []
|
| 612 |
-
pt, pruned_states_list, pruned_states_before_norm_list = 0, [], []
|
| 613 |
-
is_packed_aux_hidden_states = isinstance(aux_hidden_states, torch.Tensor)
|
| 614 |
-
aux_pruned_states_lists = None
|
| 615 |
-
if aux_hidden_states is not None:
|
| 616 |
-
aux_pruned_states_lists = (
|
| 617 |
-
[]
|
| 618 |
-
if is_packed_aux_hidden_states
|
| 619 |
-
else [[] for _ in aux_hidden_states]
|
| 620 |
-
)
|
| 621 |
-
|
| 622 |
-
for idx, (extend_logprob_start_len, extend_len) in enumerate(
|
| 623 |
-
zip(
|
| 624 |
-
logits_metadata.extend_logprob_start_lens_cpu,
|
| 625 |
-
logits_metadata.extend_seq_lens_cpu,
|
| 626 |
-
)
|
| 627 |
-
):
|
| 628 |
-
# It can happen in chunked prefill. We still need to sample 1 token,
|
| 629 |
-
# But we don't want to include it in input logprob.
|
| 630 |
-
if extend_len == extend_logprob_start_len:
|
| 631 |
-
start_len = extend_logprob_start_len - 1
|
| 632 |
-
else:
|
| 633 |
-
start_len = extend_logprob_start_len
|
| 634 |
-
|
| 635 |
-
# We always need at least 1 token to sample because that's required
|
| 636 |
-
# by a caller.
|
| 637 |
-
assert extend_len > start_len
|
| 638 |
-
pruned_states_list.append(
|
| 639 |
-
hidden_states[pt + start_len : pt + extend_len]
|
| 640 |
-
)
|
| 641 |
-
if hidden_states_before_norm is not None:
|
| 642 |
-
pruned_states_before_norm_list.append(
|
| 643 |
-
hidden_states_before_norm[pt + start_len : pt + extend_len]
|
| 644 |
-
)
|
| 645 |
-
if aux_pruned_states_lists is not None:
|
| 646 |
-
if is_packed_aux_hidden_states:
|
| 647 |
-
aux_pruned_states_lists.append(
|
| 648 |
-
aux_hidden_states[pt + start_len : pt + extend_len]
|
| 649 |
-
)
|
| 650 |
-
else:
|
| 651 |
-
for j, hidden in enumerate(aux_hidden_states):
|
| 652 |
-
aux_pruned_states_lists[j].append(
|
| 653 |
-
hidden[pt + start_len : pt + extend_len]
|
| 654 |
-
)
|
| 655 |
-
# Map each token to its sequence index, for chunked computation
|
| 656 |
-
# of input logprobs
|
| 657 |
-
token_to_seq_idx.extend([idx] * (extend_len - start_len))
|
| 658 |
-
pt += extend_len
|
| 659 |
-
sample_index_pt += extend_len - start_len
|
| 660 |
-
sample_indices.append(sample_index_pt)
|
| 661 |
-
input_logprob_indices.extend(
|
| 662 |
-
[
|
| 663 |
-
input_logprob_indices_pt + i
|
| 664 |
-
for i in range(extend_len - extend_logprob_start_len)
|
| 665 |
-
]
|
| 666 |
-
)
|
| 667 |
-
input_logprob_indices_pt += extend_len - start_len
|
| 668 |
-
|
| 669 |
-
pruned_states = torch.cat(pruned_states_list)
|
| 670 |
-
if hidden_states_before_norm is not None:
|
| 671 |
-
pruned_states_before_norm = torch.cat(pruned_states_before_norm_list)
|
| 672 |
-
if aux_pruned_states_lists is not None:
|
| 673 |
-
aux_pruned_states = (
|
| 674 |
-
torch.cat(aux_pruned_states_lists)
|
| 675 |
-
if is_packed_aux_hidden_states
|
| 676 |
-
else [torch.cat(lst) for lst in aux_pruned_states_lists]
|
| 677 |
-
)
|
| 678 |
-
|
| 679 |
-
# Build the index tensors via pinned host memory + non-blocking H2D
|
| 680 |
-
# so the small copy doesn't drain the stream.
|
| 681 |
-
sample_indices = torch.tensor(
|
| 682 |
-
sample_indices,
|
| 683 |
-
dtype=torch.int64,
|
| 684 |
-
pin_memory=is_pin_memory_available(),
|
| 685 |
-
).to(pruned_states.device, non_blocking=True)
|
| 686 |
-
input_logprob_indices = torch.tensor(
|
| 687 |
-
input_logprob_indices,
|
| 688 |
-
dtype=torch.int64,
|
| 689 |
-
pin_memory=is_pin_memory_available(),
|
| 690 |
-
).to(pruned_states.device, non_blocking=True)
|
| 691 |
-
|
| 692 |
-
return (
|
| 693 |
-
pruned_states,
|
| 694 |
-
pruned_states_before_norm,
|
| 695 |
-
aux_pruned_states,
|
| 696 |
-
sample_indices,
|
| 697 |
-
input_logprob_indices,
|
| 698 |
-
token_to_seq_idx,
|
| 699 |
-
)
|
| 700 |
-
|
| 701 |
-
def _get_hidden_states_to_store(
|
| 702 |
-
self,
|
| 703 |
-
hidden_states: torch.Tensor,
|
| 704 |
-
hidden_states_before_norm: Optional[torch.Tensor],
|
| 705 |
-
aux_hidden_states: Optional[AuxHiddenStates],
|
| 706 |
-
pruned_states: torch.Tensor,
|
| 707 |
-
pruned_states_before_norm: Optional[torch.Tensor],
|
| 708 |
-
aux_pruned_states: Optional[AuxHiddenStates],
|
| 709 |
-
sample_indices: Optional[torch.Tensor],
|
| 710 |
-
logits_metadata: LogitsMetadata,
|
| 711 |
-
) -> Optional[torch.Tensor]:
|
| 712 |
-
hidden_states_to_store: Optional[torch.Tensor] = None
|
| 713 |
-
hidden_states_to_store_before_norm: Optional[torch.Tensor] = None
|
| 714 |
-
if logits_metadata.capture_hidden_mode.need_capture():
|
| 715 |
-
if logits_metadata.capture_hidden_mode.is_full():
|
| 716 |
-
if aux_hidden_states is not None:
|
| 717 |
-
hidden_states_to_store = pack_aux_hidden_states(aux_hidden_states)
|
| 718 |
-
else:
|
| 719 |
-
hidden_states_to_store = hidden_states
|
| 720 |
-
hidden_states_to_store_before_norm = hidden_states_before_norm
|
| 721 |
-
elif logits_metadata.capture_hidden_mode.is_last():
|
| 722 |
-
# Get the last token hidden states. If sample_indices is None,
|
| 723 |
-
# pruned states only contain the last tokens already.
|
| 724 |
-
if aux_hidden_states is not None:
|
| 725 |
-
assert aux_pruned_states is not None
|
| 726 |
-
aux_pruned_states = pack_aux_hidden_states(aux_pruned_states)
|
| 727 |
-
hidden_states_to_store = (
|
| 728 |
-
aux_pruned_states[sample_indices]
|
| 729 |
-
if sample_indices is not None
|
| 730 |
-
else aux_pruned_states
|
| 731 |
-
)
|
| 732 |
-
else:
|
| 733 |
-
hidden_states_to_store = (
|
| 734 |
-
pruned_states[sample_indices]
|
| 735 |
-
if sample_indices is not None
|
| 736 |
-
else pruned_states
|
| 737 |
-
)
|
| 738 |
-
if hidden_states_before_norm is not None:
|
| 739 |
-
hidden_states_to_store_before_norm = (
|
| 740 |
-
pruned_states_before_norm[sample_indices]
|
| 741 |
-
if sample_indices is not None
|
| 742 |
-
else pruned_states_before_norm
|
| 743 |
-
)
|
| 744 |
-
else:
|
| 745 |
-
assert False, "Should never reach"
|
| 746 |
-
|
| 747 |
-
if hidden_states_to_store_before_norm is not None:
|
| 748 |
-
# NOTE: when hidden_states_before_norm is provided, we always
|
| 749 |
-
# prefer to return it.
|
| 750 |
-
hidden_states_to_store = hidden_states_to_store_before_norm
|
| 751 |
-
|
| 752 |
-
return hidden_states_to_store
|
| 753 |
-
|
| 754 |
-
def _get_logits(
|
| 755 |
-
self,
|
| 756 |
-
hidden_states: torch.Tensor,
|
| 757 |
-
lm_head: VocabParallelEmbedding,
|
| 758 |
-
logits_metadata: LogitsMetadata,
|
| 759 |
-
embedding_bias: Optional[torch.Tensor] = None,
|
| 760 |
-
use_logits_buffer: bool = True,
|
| 761 |
-
) -> torch.Tensor:
|
| 762 |
-
"""Get logits from hidden_states.
|
| 763 |
-
|
| 764 |
-
If sampled_logits_only is True, it means hidden_states only contain the
|
| 765 |
-
last position (e.g., extend without input logprobs). The caller should
|
| 766 |
-
guarantee the given hidden_states follow this constraint.
|
| 767 |
-
"""
|
| 768 |
-
_trace_e2e_logits(
|
| 769 |
-
"get_logits_enter",
|
| 770 |
-
hidden_shape=tuple(hidden_states.shape),
|
| 771 |
-
dp_gather=self.do_tensor_parallel_all_gather_dp_attn,
|
| 772 |
-
tp_gather=self.do_tensor_parallel_all_gather,
|
| 773 |
-
)
|
| 774 |
-
hidden_states, local_hidden_states = self._gather_dp_attn_hidden_states(
|
| 775 |
-
hidden_states, logits_metadata
|
| 776 |
-
)
|
| 777 |
-
_trace_e2e_logits(
|
| 778 |
-
"dp_hidden_gather_returned",
|
| 779 |
-
global_shape=tuple(hidden_states.shape),
|
| 780 |
-
local_shape=tuple(local_hidden_states.shape),
|
| 781 |
-
)
|
| 782 |
-
|
| 783 |
-
if envs.SGLANG_TRACE_LOGITS_E2E_SYNC.get():
|
| 784 |
-
_trace_e2e_logits("pre_lm_head_sync_enter")
|
| 785 |
-
torch.cuda.synchronize()
|
| 786 |
-
_trace_e2e_logits("pre_lm_head_sync_returned")
|
| 787 |
-
|
| 788 |
-
_trace_e2e_logits("lm_head_enter", hidden_shape=tuple(hidden_states.shape))
|
| 789 |
-
logits = self._compute_lm_head(hidden_states, lm_head, embedding_bias)
|
| 790 |
-
_trace_e2e_logits("lm_head_returned", logits_shape=tuple(logits.shape))
|
| 791 |
-
if envs.SGLANG_TRACE_LOGITS_E2E_SYNC.get():
|
| 792 |
-
_trace_e2e_logits("post_lm_head_sync_enter")
|
| 793 |
-
torch.cuda.synchronize()
|
| 794 |
-
_trace_e2e_logits("post_lm_head_sync_returned")
|
| 795 |
-
|
| 796 |
-
if self.logit_scale is not None:
|
| 797 |
-
logits.mul_(self.logit_scale)
|
| 798 |
-
|
| 799 |
-
used_tp_lm_head_all_to_all = False
|
| 800 |
-
if self.do_tensor_parallel_all_gather:
|
| 801 |
-
_trace_e2e_logits(
|
| 802 |
-
"tp_logits_gather_enter", logits_shape=tuple(logits.shape)
|
| 803 |
-
)
|
| 804 |
-
if self.use_attn_tp_group:
|
| 805 |
-
logits = self._gather_attn_tp_logits(logits)
|
| 806 |
-
elif self._can_use_tp_lm_head_all_to_all(
|
| 807 |
-
logits, local_hidden_states, lm_head, logits_metadata
|
| 808 |
-
):
|
| 809 |
-
logits = self._tp_lm_head_all_to_all(logits)
|
| 810 |
-
used_tp_lm_head_all_to_all = True
|
| 811 |
-
else:
|
| 812 |
-
logits = self._logits_gatherer(logits)
|
| 813 |
-
_trace_e2e_logits(
|
| 814 |
-
"tp_logits_gather_returned", logits_shape=tuple(logits.shape)
|
| 815 |
-
)
|
| 816 |
-
|
| 817 |
-
if not used_tp_lm_head_all_to_all:
|
| 818 |
-
_trace_e2e_logits(
|
| 819 |
-
"dp_logits_scatter_enter", logits_shape=tuple(logits.shape)
|
| 820 |
-
)
|
| 821 |
-
logits = self._scatter_dp_attn_logits(
|
| 822 |
-
logits, local_hidden_states, logits_metadata
|
| 823 |
-
)
|
| 824 |
-
_trace_e2e_logits(
|
| 825 |
-
"dp_logits_scatter_returned", logits_shape=tuple(logits.shape)
|
| 826 |
-
)
|
| 827 |
-
|
| 828 |
-
logits = self._copy_logits_to_buffer(
|
| 829 |
-
logits, logits_metadata, use_buffer=use_logits_buffer
|
| 830 |
-
)
|
| 831 |
-
|
| 832 |
-
if self.final_logit_softcapping:
|
| 833 |
-
if not (_is_npu or _is_cpu):
|
| 834 |
-
fused_softcap(logits, self.final_logit_softcapping)
|
| 835 |
-
else:
|
| 836 |
-
logits = self.final_logit_softcapping * torch.tanh(
|
| 837 |
-
logits / self.final_logit_softcapping
|
| 838 |
-
)
|
| 839 |
-
|
| 840 |
-
return logits
|
| 841 |
-
|
| 842 |
-
def _compute_lm_head(
|
| 843 |
-
self,
|
| 844 |
-
hidden_states: torch.Tensor,
|
| 845 |
-
lm_head: VocabParallelEmbedding,
|
| 846 |
-
embedding_bias: Optional[torch.Tensor] = None,
|
| 847 |
-
) -> torch.Tensor:
|
| 848 |
-
if self.jev_selected_ids is not None:
|
| 849 |
-
if not hasattr(lm_head, "weight") or lm_head.weight.dtype not in (torch.bfloat16, torch.float16, torch.float32) or hasattr(lm_head, "set_lora"):
|
| 850 |
-
raise ValueError("Selected head requires a frozen unquantized output projection")
|
| 851 |
-
if embedding_bias is not None or self.final_logit_softcapping:
|
| 852 |
-
raise ValueError("Selected head does not support output bias or softcapping")
|
| 853 |
-
if self.jev_head_cache is None:
|
| 854 |
-
ids = torch.tensor(self.jev_selected_ids, device=lm_head.weight.device, dtype=torch.long)
|
| 855 |
-
weight = lm_head.weight.index_select(0, ids).contiguous()
|
| 856 |
-
self.jev_head_cache = (ids, weight)
|
| 857 |
-
ids, weight = self.jev_head_cache
|
| 858 |
-
if self.use_fp32_lm_head:
|
| 859 |
-
selected = torch.matmul(hidden_states.float(), weight.float().T)
|
| 860 |
-
else:
|
| 861 |
-
selected = torch.matmul(hidden_states.to(weight.dtype), weight.T)
|
| 862 |
-
logits = torch.full((hidden_states.shape[0], self.vocab_size), -float("inf"), device=hidden_states.device, dtype=selected.dtype)
|
| 863 |
-
return logits.index_copy_(1, ids, selected)
|
| 864 |
-
quant_method = getattr(lm_head, "quant_method", None)
|
| 865 |
-
if hasattr(lm_head, "set_lora") and hasattr(lm_head, "apply_lora"):
|
| 866 |
-
# This is a LoRA-wrapped module, use its forward method
|
| 867 |
-
logits = lm_head(hidden_states)
|
| 868 |
-
elif should_apply_lm_head_quant_method(lm_head, quant_method):
|
| 869 |
-
logits = quant_method.apply(lm_head, hidden_states, embedding_bias)
|
| 870 |
-
elif hasattr(lm_head, "weight"):
|
| 871 |
-
# Normal linear layer
|
| 872 |
-
if self.use_fp32_lm_head:
|
| 873 |
-
# Avoid materializing FP32 copies for same-dtype CUDA FP16/BF16
|
| 874 |
-
# inputs. Retain explicit FP32 casts for unsupported devices or
|
| 875 |
-
# dtype combinations.
|
| 876 |
-
use_mm_out_dtype = (
|
| 877 |
-
hidden_states.is_cuda
|
| 878 |
-
and hidden_states.dtype == lm_head.weight.dtype
|
| 879 |
-
and hidden_states.dtype in (torch.float16, torch.bfloat16)
|
| 880 |
-
)
|
| 881 |
-
if use_mm_out_dtype:
|
| 882 |
-
logits = torch.mm(
|
| 883 |
-
hidden_states,
|
| 884 |
-
lm_head.weight.T,
|
| 885 |
-
out_dtype=torch.float32,
|
| 886 |
-
)
|
| 887 |
-
else:
|
| 888 |
-
logits = torch.matmul(
|
| 889 |
-
hidden_states.to(torch.float32),
|
| 890 |
-
lm_head.weight.to(torch.float32).T,
|
| 891 |
-
)
|
| 892 |
-
elif use_intel_amx_backend(lm_head):
|
| 893 |
-
logits = torch.ops.sgl_kernel.weight_packed_linear(
|
| 894 |
-
hidden_states.to(lm_head.weight.dtype),
|
| 895 |
-
lm_head.weight,
|
| 896 |
-
None, # bias
|
| 897 |
-
True, # is_vnni
|
| 898 |
-
)
|
| 899 |
-
elif self.rl_on_policy_target is not None:
|
| 900 |
-
# Due to tie-weight, we may not be able to change lm_head's weight dtype
|
| 901 |
-
logits = torch.matmul(
|
| 902 |
-
hidden_states.bfloat16(), lm_head.weight.T.bfloat16()
|
| 903 |
-
)
|
| 904 |
-
else:
|
| 905 |
-
logits = torch.matmul(
|
| 906 |
-
hidden_states.to(lm_head.weight.dtype), lm_head.weight.T
|
| 907 |
-
)
|
| 908 |
-
else:
|
| 909 |
-
# GGUF models
|
| 910 |
-
# TODO: use weight_packed_linear for GGUF models
|
| 911 |
-
if self.use_fp32_lm_head:
|
| 912 |
-
with torch.cuda.amp.autocast(enabled=False):
|
| 913 |
-
logits = lm_head.quant_method.apply(
|
| 914 |
-
lm_head, hidden_states.to(torch.float32), embedding_bias
|
| 915 |
-
)
|
| 916 |
-
else:
|
| 917 |
-
logits = lm_head.quant_method.apply(
|
| 918 |
-
lm_head, hidden_states, embedding_bias
|
| 919 |
-
)
|
| 920 |
-
return logits
|
| 921 |
-
|
| 922 |
-
def _gather_dp_attn_hidden_states(
|
| 923 |
-
self, hidden_states: torch.Tensor, logits_metadata: LogitsMetadata
|
| 924 |
-
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 925 |
-
if self.do_tensor_parallel_all_gather_dp_attn:
|
| 926 |
-
_trace_e2e_logits(
|
| 927 |
-
"dp_metadata_enter",
|
| 928 |
-
local_shape=tuple(hidden_states.shape),
|
| 929 |
-
global_counts_cpu=logits_metadata.global_num_tokens_for_logprob_cpu,
|
| 930 |
-
)
|
| 931 |
-
logits_metadata.compute_dp_attention_metadata()
|
| 932 |
-
_trace_e2e_logits(
|
| 933 |
-
"dp_metadata_returned",
|
| 934 |
-
buffer_shape=tuple(logits_metadata.gathered_buffer.shape),
|
| 935 |
-
local_start=logits_metadata.dp_local_start_pos,
|
| 936 |
-
local_tokens=logits_metadata.dp_local_num_tokens,
|
| 937 |
-
)
|
| 938 |
-
local_hidden_states = hidden_states
|
| 939 |
-
hidden_states = logits_metadata.gathered_buffer
|
| 940 |
-
_trace_e2e_logits(
|
| 941 |
-
"dp_hidden_gather_enter",
|
| 942 |
-
global_shape=tuple(hidden_states.shape),
|
| 943 |
-
local_shape=tuple(local_hidden_states.shape),
|
| 944 |
-
)
|
| 945 |
-
dp_gather_replicate(hidden_states, local_hidden_states, logits_metadata)
|
| 946 |
-
_trace_e2e_logits("dp_hidden_gather_collective_returned")
|
| 947 |
-
return hidden_states, local_hidden_states
|
| 948 |
-
return hidden_states, hidden_states
|
| 949 |
-
|
| 950 |
-
def _gather_attn_tp_logits(self, logits: torch.Tensor) -> torch.Tensor:
|
| 951 |
-
if self.vocab_size % self.attn_tp_size == 0:
|
| 952 |
-
global_logits = torch.empty(
|
| 953 |
-
(
|
| 954 |
-
self.attn_tp_size,
|
| 955 |
-
logits.shape[0],
|
| 956 |
-
self.vocab_size // self.attn_tp_size,
|
| 957 |
-
),
|
| 958 |
-
device=logits.device,
|
| 959 |
-
dtype=logits.dtype,
|
| 960 |
-
)
|
| 961 |
-
attn_tp_all_gather_into_tensor(global_logits, logits)
|
| 962 |
-
global_logits = global_logits.permute(1, 0, 2).reshape(
|
| 963 |
-
logits.shape[0], self.vocab_size
|
| 964 |
-
)
|
| 965 |
-
else:
|
| 966 |
-
global_logits = torch.empty(
|
| 967 |
-
(self.vocab_size, logits.shape[0]),
|
| 968 |
-
device=logits.device,
|
| 969 |
-
dtype=logits.dtype,
|
| 970 |
-
)
|
| 971 |
-
global_logits = global_logits.T
|
| 972 |
-
attn_tp_all_gather(
|
| 973 |
-
list(global_logits.tensor_split(self.attn_tp_size, dim=-1)),
|
| 974 |
-
logits,
|
| 975 |
-
)
|
| 976 |
-
return global_logits
|
| 977 |
-
|
| 978 |
-
def _can_use_tp_lm_head_all_to_all(
|
| 979 |
-
self,
|
| 980 |
-
logits: torch.Tensor,
|
| 981 |
-
local_hidden_states: torch.Tensor,
|
| 982 |
-
lm_head: VocabParallelEmbedding,
|
| 983 |
-
logits_metadata: LogitsMetadata,
|
| 984 |
-
) -> bool:
|
| 985 |
-
if not self.use_tp_lm_head_all_to_all:
|
| 986 |
-
return False
|
| 987 |
-
|
| 988 |
-
tp_size = get_parallel().tp_size
|
| 989 |
-
base_lm_head = getattr(lm_head, "base_layer", lm_head)
|
| 990 |
-
if getattr(base_lm_head, "tp_size", None) != tp_size:
|
| 991 |
-
# Tied embeddings may be replicated across DP ranks (tp_size=1),
|
| 992 |
-
# even though the logits processor runs in a larger global TP
|
| 993 |
-
# group. Such logits are full-vocabulary rather than TP shards and
|
| 994 |
-
# therefore do not satisfy the all-to-all layout contract.
|
| 995 |
-
return False
|
| 996 |
-
|
| 997 |
-
# Every participant must make the same collective choice. Decode CUDA
|
| 998 |
-
# graphs omit CPU counts and fill every GPU count with the same padded
|
| 999 |
-
# bucket size. Eager batches carry the same global CPU count list on
|
| 1000 |
-
# every rank, so they are also safe when all entries are equal.
|
| 1001 |
-
global_counts_cpu = logits_metadata.global_num_tokens_for_logprob_cpu
|
| 1002 |
-
is_equal_padded_graph_layout = global_counts_cpu is None and (
|
| 1003 |
-
logits_metadata.global_num_tokens_for_logprob_gpu is not None
|
| 1004 |
-
)
|
| 1005 |
-
is_equal_eager_layout = (
|
| 1006 |
-
global_counts_cpu is not None
|
| 1007 |
-
and len(global_counts_cpu) == tp_size
|
| 1008 |
-
and len(global_counts_cpu) > 0
|
| 1009 |
-
and all(count == global_counts_cpu[0] for count in global_counts_cpu)
|
| 1010 |
-
)
|
| 1011 |
-
if not (is_equal_padded_graph_layout or is_equal_eager_layout):
|
| 1012 |
-
return False
|
| 1013 |
-
|
| 1014 |
-
local_rows = local_hidden_states.shape[0]
|
| 1015 |
-
return local_rows > 0 and logits.shape[0] == local_rows * tp_size
|
| 1016 |
-
|
| 1017 |
-
def _tp_lm_head_all_to_all(self, logits: torch.Tensor) -> torch.Tensor:
|
| 1018 |
-
"""Exchange only the row block owned by each destination DP rank."""
|
| 1019 |
-
logits = logits.contiguous()
|
| 1020 |
-
all_to_all_output = torch.empty_like(logits)
|
| 1021 |
-
get_tp_group().all_to_all_single(all_to_all_output.view(-1), logits.view(-1))
|
| 1022 |
-
return _reassemble_tp_lm_head_all_to_all_output(
|
| 1023 |
-
all_to_all_output, get_parallel().tp_size
|
| 1024 |
-
)
|
| 1025 |
-
|
| 1026 |
-
def _scatter_dp_attn_logits(
|
| 1027 |
-
self,
|
| 1028 |
-
logits: torch.Tensor,
|
| 1029 |
-
local_hidden_states: torch.Tensor,
|
| 1030 |
-
logits_metadata: LogitsMetadata,
|
| 1031 |
-
) -> torch.Tensor:
|
| 1032 |
-
if self.do_tensor_parallel_all_gather_dp_attn:
|
| 1033 |
-
global_logits = logits
|
| 1034 |
-
logits = torch.empty(
|
| 1035 |
-
(local_hidden_states.shape[0], global_logits.shape[1]),
|
| 1036 |
-
device=global_logits.device,
|
| 1037 |
-
dtype=global_logits.dtype,
|
| 1038 |
-
)
|
| 1039 |
-
dp_scatter(logits, global_logits, logits_metadata)
|
| 1040 |
-
return logits
|
| 1041 |
-
|
| 1042 |
-
def _copy_logits_to_buffer(
|
| 1043 |
-
self,
|
| 1044 |
-
logits: torch.Tensor,
|
| 1045 |
-
logits_metadata: LogitsMetadata,
|
| 1046 |
-
use_buffer: bool = True,
|
| 1047 |
-
) -> torch.Tensor:
|
| 1048 |
-
logits_buffer = logits_metadata.next_token_logits_buffer if use_buffer else None
|
| 1049 |
-
if logits.shape[-1] > self.vocab_size:
|
| 1050 |
-
logits = logits[:, : self.vocab_size]
|
| 1051 |
-
logits_width = logits.shape[-1]
|
| 1052 |
-
# The shared logits buffer is keyed by vocab width and rows; skip it
|
| 1053 |
-
# when this batch has a different logits shape than the graph buffer.
|
| 1054 |
-
if logits_buffer is not None and tuple(logits_buffer.shape) == tuple(
|
| 1055 |
-
logits.shape
|
| 1056 |
-
):
|
| 1057 |
-
assert logits_buffer.dtype == torch.float
|
| 1058 |
-
logits_buffer.copy_(logits)
|
| 1059 |
-
logits = logits_buffer
|
| 1060 |
-
else:
|
| 1061 |
-
logits = logits.float()
|
| 1062 |
-
return logits
|
| 1063 |
-
|
| 1064 |
-
def _get_dllm_logits(
|
| 1065 |
-
self,
|
| 1066 |
-
hidden_states: torch.Tensor,
|
| 1067 |
-
lm_head: VocabParallelEmbedding,
|
| 1068 |
-
logits_metadata: LogitsMetadata,
|
| 1069 |
-
) -> LogitsProcessorOutput:
|
| 1070 |
-
assert self.return_full_logits
|
| 1071 |
-
full_logits = self._get_logits(hidden_states, lm_head, logits_metadata)
|
| 1072 |
-
return LogitsProcessorOutput(
|
| 1073 |
-
full_logits=full_logits,
|
| 1074 |
-
next_token_logits=None,
|
| 1075 |
-
)
|
| 1076 |
-
|
| 1077 |
-
def compute_logprobs_for_multi_item_scoring(
|
| 1078 |
-
self,
|
| 1079 |
-
input_ids,
|
| 1080 |
-
hidden_states,
|
| 1081 |
-
lm_head: VocabParallelEmbedding,
|
| 1082 |
-
logits_metadata: Union[LogitsMetadata, ForwardBatch],
|
| 1083 |
-
multi_item_delimiter_indices: List[torch.Tensor],
|
| 1084 |
-
):
|
| 1085 |
-
"""
|
| 1086 |
-
Compute logprobs for multi-item scoring using pre-computed delimiter indices.
|
| 1087 |
-
|
| 1088 |
-
Sequence format: Query<delimiter>Item1<delimiter>Item2<delimiter>...
|
| 1089 |
-
Scoring positions: Extracts logprobs at positions before each <delimiter>
|
| 1090 |
-
|
| 1091 |
-
Args:
|
| 1092 |
-
input_ids: Input token IDs. Shape: [total_sequence_length].
|
| 1093 |
-
hidden_states: Hidden states from the model. Shape: [sequence_length, hidden_dim].
|
| 1094 |
-
lm_head: Language model head for computing logits.
|
| 1095 |
-
logits_metadata: Metadata containing batch info and logprob specs.
|
| 1096 |
-
multi_item_delimiter_indices: Pre-computed delimiter positions per request (CPU tensors).
|
| 1097 |
-
"""
|
| 1098 |
-
# Compute positions just before each delimiter.
|
| 1099 |
-
# Build offset-adjusted indices on CPU, then do a single CPU→GPU transfer.
|
| 1100 |
-
device = input_ids.device
|
| 1101 |
-
all_tensors = []
|
| 1102 |
-
if logits_metadata.extend_seq_lens_cpu is not None:
|
| 1103 |
-
offset = 0
|
| 1104 |
-
for req_seq_len, indices_tensor in zip(
|
| 1105 |
-
logits_metadata.extend_seq_lens_cpu, multi_item_delimiter_indices
|
| 1106 |
-
):
|
| 1107 |
-
if len(indices_tensor) > 0:
|
| 1108 |
-
# Note: if the first delimiter is at position 0 (empty query),
|
| 1109 |
-
# indices - 1 wraps to -1. This is harmless — the first
|
| 1110 |
-
# delimiter entry is always discarded by
|
| 1111 |
-
# _process_multi_item_scoring_results.
|
| 1112 |
-
all_tensors.append(indices_tensor + (offset - 1))
|
| 1113 |
-
offset += req_seq_len
|
| 1114 |
-
else:
|
| 1115 |
-
all_tensors.append(multi_item_delimiter_indices[0] - 1)
|
| 1116 |
-
multi_item_indices = torch.cat(all_tensors).to(device, non_blocking=True)
|
| 1117 |
-
|
| 1118 |
-
# Extract hidden states at delimiter positions for multi-item scoring
|
| 1119 |
-
sliced_hidden = hidden_states[multi_item_indices]
|
| 1120 |
-
|
| 1121 |
-
sliced_logits = self._get_logits(sliced_hidden, lm_head, logits_metadata)
|
| 1122 |
-
sliced_logprobs = torch.nn.functional.log_softmax(sliced_logits, dim=-1)
|
| 1123 |
-
|
| 1124 |
-
# Initialize return values
|
| 1125 |
-
input_token_ids_logprobs_val = []
|
| 1126 |
-
input_token_ids_logprobs_idx = []
|
| 1127 |
-
input_top_logprobs_val = None
|
| 1128 |
-
input_top_logprobs_idx = None
|
| 1129 |
-
|
| 1130 |
-
# Recalculate extend_logprob_pruned_lens_cpu to match delimiter counts per request
|
| 1131 |
-
if (
|
| 1132 |
-
logits_metadata.token_ids_logprobs
|
| 1133 |
-
or logits_metadata.extend_return_top_logprob
|
| 1134 |
-
):
|
| 1135 |
-
logits_metadata.extend_logprob_pruned_lens_cpu = [
|
| 1136 |
-
len(t) for t in multi_item_delimiter_indices
|
| 1137 |
-
]
|
| 1138 |
-
|
| 1139 |
-
# Get the logprobs of specified token ids
|
| 1140 |
-
if logits_metadata.extend_token_ids_logprob:
|
| 1141 |
-
(
|
| 1142 |
-
input_token_ids_logprobs_val,
|
| 1143 |
-
input_token_ids_logprobs_idx,
|
| 1144 |
-
) = get_token_ids_logprobs_raw(
|
| 1145 |
-
sliced_logprobs,
|
| 1146 |
-
logits_metadata.token_ids_logprobs,
|
| 1147 |
-
stage=LogprobStage.PREFILL,
|
| 1148 |
-
extend_logprob_pruned_lens_cpu=logits_metadata.extend_logprob_pruned_lens_cpu,
|
| 1149 |
-
no_copy_to_cpu=True,
|
| 1150 |
-
)
|
| 1151 |
-
|
| 1152 |
-
# Get the logprob of top-k tokens
|
| 1153 |
-
if logits_metadata.extend_return_top_logprob:
|
| 1154 |
-
(
|
| 1155 |
-
input_top_logprobs_val,
|
| 1156 |
-
input_top_logprobs_idx,
|
| 1157 |
-
) = get_top_logprobs_raw(
|
| 1158 |
-
sliced_logprobs,
|
| 1159 |
-
logits_metadata.top_logprobs_nums,
|
| 1160 |
-
stage=LogprobStage.PREFILL,
|
| 1161 |
-
extend_logprob_pruned_lens_cpu=logits_metadata.extend_logprob_pruned_lens_cpu,
|
| 1162 |
-
)
|
| 1163 |
-
|
| 1164 |
-
# MIS scores come from input_token_ids_logprobs_val (label-token logprobs),
|
| 1165 |
-
# not from per-position input_token_logprobs. However, the shared logprob
|
| 1166 |
-
# pipeline (add_input_logprob_return_values) asserts input_token_logprobs is
|
| 1167 |
-
# non-None, converts it to a tuple, slices it, and validates its length —
|
| 1168 |
-
# all before score_request() ever sees the result. We can't set it to None
|
| 1169 |
-
# without changing those shared asserts, so we fill with zeros to satisfy
|
| 1170 |
-
# the pipeline. score_request() ignores this field entirely.
|
| 1171 |
-
input_token_logprobs = torch.zeros(multi_item_indices.shape[0], device=device)
|
| 1172 |
-
|
| 1173 |
-
return LogitsProcessorOutput(
|
| 1174 |
-
next_token_logits=None,
|
| 1175 |
-
input_token_logprobs=input_token_logprobs,
|
| 1176 |
-
input_top_logprobs_val=input_top_logprobs_val,
|
| 1177 |
-
input_top_logprobs_idx=input_top_logprobs_idx,
|
| 1178 |
-
input_token_ids_logprobs_val=input_token_ids_logprobs_val,
|
| 1179 |
-
input_token_ids_logprobs_idx=input_token_ids_logprobs_idx,
|
| 1180 |
-
mm_input_embeds=logits_metadata.mm_input_embeds,
|
| 1181 |
-
)
|
| 1182 |
-
|
| 1183 |
-
|
| 1184 |
-
def _reassemble_tp_lm_head_all_to_all_output(
|
| 1185 |
-
all_to_all_output: torch.Tensor, tp_size: int
|
| 1186 |
-
) -> torch.Tensor:
|
| 1187 |
-
"""Convert source-major all-to-all output to row-major full-vocab logits.
|
| 1188 |
-
|
| 1189 |
-
Each source TP rank contributes ``[local_rows, vocab_shard]`` for this
|
| 1190 |
-
destination DP rank. ``all_to_all_single`` concatenates those contributions
|
| 1191 |
-
along dim 0, while the sampler expects the vocab shards concatenated along
|
| 1192 |
-
dim 1.
|
| 1193 |
-
"""
|
| 1194 |
-
assert all_to_all_output.shape[0] % tp_size == 0
|
| 1195 |
-
local_rows = all_to_all_output.shape[0] // tp_size
|
| 1196 |
-
vocab_shard = all_to_all_output.shape[1]
|
| 1197 |
-
return (
|
| 1198 |
-
all_to_all_output.view(tp_size, local_rows, vocab_shard)
|
| 1199 |
-
.permute(1, 0, 2)
|
| 1200 |
-
.reshape(local_rows, tp_size * vocab_shard)
|
| 1201 |
-
)
|
| 1202 |
-
|
| 1203 |
-
|
| 1204 |
-
def _has_lm_head_runtime_attrs(lm_head, attr_names: Tuple[str, ...]) -> bool:
|
| 1205 |
-
return all(hasattr(lm_head, attr_name) for attr_name in attr_names)
|
| 1206 |
-
|
| 1207 |
-
|
| 1208 |
-
def should_apply_lm_head_quant_method(lm_head, quant_method) -> bool:
|
| 1209 |
-
if (
|
| 1210 |
-
quant_method is None
|
| 1211 |
-
or not hasattr(lm_head, "weight")
|
| 1212 |
-
or not callable(getattr(quant_method, "apply", None))
|
| 1213 |
-
):
|
| 1214 |
-
return False
|
| 1215 |
-
|
| 1216 |
-
method_name = type(quant_method).__name__
|
| 1217 |
-
if method_name in _UNQUANTIZED_LM_HEAD_METHODS:
|
| 1218 |
-
return False
|
| 1219 |
-
|
| 1220 |
-
# Some draft models share an unquantized target lm_head tensor while still
|
| 1221 |
-
# carrying the draft model's stale ModelOpt quant_method. Only use the
|
| 1222 |
-
# ModelOpt lm_head kernel when the runtime quantization state matches it.
|
| 1223 |
-
if method_name == "ModelOptFp4LinearMethod":
|
| 1224 |
-
if quant_method.quant_mode == "w4a16":
|
| 1225 |
-
return lm_head.weight.dtype == torch.uint8 and _has_lm_head_runtime_attrs(
|
| 1226 |
-
lm_head,
|
| 1227 |
-
(
|
| 1228 |
-
"weight_scale_interleaved",
|
| 1229 |
-
"alpha",
|
| 1230 |
-
"input_size_per_partition",
|
| 1231 |
-
"output_size_per_partition",
|
| 1232 |
-
),
|
| 1233 |
-
)
|
| 1234 |
-
if lm_head.weight.dtype == torch.int32 and _has_lm_head_runtime_attrs(
|
| 1235 |
-
lm_head,
|
| 1236 |
-
(
|
| 1237 |
-
"weight_scale",
|
| 1238 |
-
"weight_global_scale",
|
| 1239 |
-
"workspace",
|
| 1240 |
-
"input_size_per_partition",
|
| 1241 |
-
"output_size_per_partition",
|
| 1242 |
-
),
|
| 1243 |
-
):
|
| 1244 |
-
return True
|
| 1245 |
-
return lm_head.weight.dtype == torch.uint8 and _has_lm_head_runtime_attrs(
|
| 1246 |
-
lm_head,
|
| 1247 |
-
(
|
| 1248 |
-
"weight_scale_interleaved",
|
| 1249 |
-
"alpha",
|
| 1250 |
-
"input_scale_inv",
|
| 1251 |
-
"input_size_per_partition",
|
| 1252 |
-
"output_size_per_partition",
|
| 1253 |
-
),
|
| 1254 |
-
)
|
| 1255 |
-
if method_name == "ModelOptNvFp4A16LinearMethod":
|
| 1256 |
-
return lm_head.weight.dtype == torch.int32 and _has_lm_head_runtime_attrs(
|
| 1257 |
-
lm_head,
|
| 1258 |
-
(
|
| 1259 |
-
"weight_scale",
|
| 1260 |
-
"weight_global_scale",
|
| 1261 |
-
"workspace",
|
| 1262 |
-
"input_size_per_partition",
|
| 1263 |
-
"output_size_per_partition",
|
| 1264 |
-
),
|
| 1265 |
-
)
|
| 1266 |
-
if method_name == "ModelOptFp8LinearMethod":
|
| 1267 |
-
return (
|
| 1268 |
-
lm_head.weight.dtype == torch.float8_e4m3fn
|
| 1269 |
-
and _has_lm_head_runtime_attrs(lm_head, ("weight_scale", "input_scale"))
|
| 1270 |
-
)
|
| 1271 |
-
|
| 1272 |
-
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runtime/patches/sglang-qwen35-text.py
DELETED
|
@@ -1,298 +0,0 @@
|
|
| 1 |
-
# Copyright 2025 Qwen Team
|
| 2 |
-
# Copyright 2025 SGLang Team
|
| 3 |
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
-
# you may not use this file except in compliance with the License.
|
| 5 |
-
# You may obtain a copy of the License at
|
| 6 |
-
#
|
| 7 |
-
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
-
#
|
| 9 |
-
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
-
# See the License for the specific language governing permissions and
|
| 13 |
-
# limitations under the License.
|
| 14 |
-
# ==============================================================================
|
| 15 |
-
|
| 16 |
-
import logging
|
| 17 |
-
from typing import Iterable, Optional, Set, Tuple, Union
|
| 18 |
-
|
| 19 |
-
import torch
|
| 20 |
-
from torch import nn
|
| 21 |
-
|
| 22 |
-
from sglang.srt.distributed import get_pp_group
|
| 23 |
-
from sglang.srt.eplb.expert_location import ModelConfigForExpertLocation
|
| 24 |
-
from sglang.srt.layers.logits_processor import LogitsProcessor
|
| 25 |
-
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
| 26 |
-
from sglang.srt.layers.utils import PPMissingLayer
|
| 27 |
-
from sglang.srt.layers.vocab_parallel_embedding import ParallelLMHead
|
| 28 |
-
from sglang.srt.model_executor.forward_batch_info import ForwardBatch, PPProxyTensors
|
| 29 |
-
from sglang.srt.model_loader.weight_utils import default_weight_loader
|
| 30 |
-
from sglang.srt.models import qwen3_5
|
| 31 |
-
from sglang.srt.models.qwen2_moe import Qwen2MoeSparseMoeBlock
|
| 32 |
-
from sglang.srt.runtime_context import get_parallel
|
| 33 |
-
from sglang.srt.utils import LazyValue, add_prefix
|
| 34 |
-
|
| 35 |
-
logger = logging.getLogger(__name__)
|
| 36 |
-
|
| 37 |
-
_MODEL_PREFIX = "model."
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
class Qwen3_5ForCausalLM(nn.Module):
|
| 41 |
-
body_cls = qwen3_5.Qwen3_5ForCausalLM
|
| 42 |
-
|
| 43 |
-
packed_modules_mapping = qwen3_5.Qwen3_5ForCausalLM.packed_modules_mapping
|
| 44 |
-
supported_lora_modules = qwen3_5.Qwen3_5ForCausalLM.supported_lora_modules
|
| 45 |
-
|
| 46 |
-
@classmethod
|
| 47 |
-
def shared_experts_fusion_disable_reason(cls, hf_config, quant_config):
|
| 48 |
-
# The body decides; it is handed this config and quantization verbatim.
|
| 49 |
-
return cls.body_cls.shared_experts_fusion_disable_reason(
|
| 50 |
-
hf_config, quant_config
|
| 51 |
-
)
|
| 52 |
-
|
| 53 |
-
def __init__(
|
| 54 |
-
self,
|
| 55 |
-
config,
|
| 56 |
-
quant_config: Optional[QuantizationConfig] = None,
|
| 57 |
-
prefix: str = "",
|
| 58 |
-
) -> None:
|
| 59 |
-
super().__init__()
|
| 60 |
-
self.config = config
|
| 61 |
-
self.quant_config = quant_config
|
| 62 |
-
self.pp_group = get_pp_group()
|
| 63 |
-
|
| 64 |
-
if quant_config is not None and hasattr(quant_config, "packed_modules_mapping"):
|
| 65 |
-
quant_config.packed_modules_mapping = self.packed_modules_mapping
|
| 66 |
-
|
| 67 |
-
self.model = self.body_cls(
|
| 68 |
-
config=config,
|
| 69 |
-
quant_config=quant_config,
|
| 70 |
-
prefix=add_prefix("model", prefix),
|
| 71 |
-
)
|
| 72 |
-
|
| 73 |
-
if self.pp_group.is_last_rank:
|
| 74 |
-
if self.pp_group.world_size == 1 and config.tie_word_embeddings:
|
| 75 |
-
self.lm_head = self.model.embed_tokens
|
| 76 |
-
else:
|
| 77 |
-
self.lm_head = ParallelLMHead(
|
| 78 |
-
config.vocab_size,
|
| 79 |
-
config.hidden_size,
|
| 80 |
-
quant_config=quant_config,
|
| 81 |
-
org_num_embeddings=config.vocab_size,
|
| 82 |
-
prefix=add_prefix("lm_head", prefix),
|
| 83 |
-
use_attn_tp_group=get_parallel().enable_dp_lm_head,
|
| 84 |
-
)
|
| 85 |
-
else:
|
| 86 |
-
self.lm_head = PPMissingLayer()
|
| 87 |
-
|
| 88 |
-
self.logits_processor = LogitsProcessor(config)
|
| 89 |
-
|
| 90 |
-
# Text-only checkpoints retain mrope_section, but identical position rows
|
| 91 |
-
# make its rotary embedding equivalent to 1-D RoPE.
|
| 92 |
-
rope_config = getattr(config, "rope_parameters", None) or getattr(
|
| 93 |
-
config, "rope_scaling", None
|
| 94 |
-
)
|
| 95 |
-
self.is_mrope_enabled = bool(rope_config) and "mrope_section" in rope_config
|
| 96 |
-
|
| 97 |
-
self.capture_aux_hidden_states = False
|
| 98 |
-
|
| 99 |
-
@property
|
| 100 |
-
def start_layer(self) -> int:
|
| 101 |
-
return self.model.start_layer
|
| 102 |
-
|
| 103 |
-
@property
|
| 104 |
-
def end_layer(self) -> int:
|
| 105 |
-
return self.model.end_layer
|
| 106 |
-
|
| 107 |
-
def get_input_embeddings(self) -> nn.Embedding:
|
| 108 |
-
return self.model.embed_tokens
|
| 109 |
-
|
| 110 |
-
def prepare_before_cuda_graph_capture(self, model_runner) -> None:
|
| 111 |
-
"""Forward model-owned warmup to the text backbone."""
|
| 112 |
-
self.model.prepare_before_cuda_graph_capture(model_runner)
|
| 113 |
-
|
| 114 |
-
def set_dflash_layers_to_capture(self, layer_ids: list[int]) -> None:
|
| 115 |
-
if self.pp_group.world_size > 1:
|
| 116 |
-
raise NotImplementedError("DFLASH/DSPARK aux hidden capture requires PP=1.")
|
| 117 |
-
num_layers = len(self.model.layers)
|
| 118 |
-
if sorted(set(layer_ids)) != list(layer_ids) or not all(
|
| 119 |
-
0 <= layer_id < num_layers - 1 for layer_id in layer_ids
|
| 120 |
-
):
|
| 121 |
-
raise ValueError(
|
| 122 |
-
"target_layer_ids must be unique, strictly increasing, and in "
|
| 123 |
-
f"[0, {num_layers - 1}); got {layer_ids}"
|
| 124 |
-
)
|
| 125 |
-
self.capture_aux_hidden_states = True
|
| 126 |
-
self.model.set_dflash_layers_to_capture(
|
| 127 |
-
[layer_id + 1 for layer_id in layer_ids]
|
| 128 |
-
)
|
| 129 |
-
|
| 130 |
-
def get_embed_and_head(self):
|
| 131 |
-
# PP splits embedding and lm_head across first/last stages; the draft keeps
|
| 132 |
-
# its own copy of whichever half its stage cannot receive.
|
| 133 |
-
embed = (
|
| 134 |
-
None
|
| 135 |
-
if isinstance(self.model.embed_tokens, PPMissingLayer)
|
| 136 |
-
else self.model.embed_tokens.weight
|
| 137 |
-
)
|
| 138 |
-
head = None if isinstance(self.lm_head, PPMissingLayer) else self.lm_head.weight
|
| 139 |
-
return embed, head
|
| 140 |
-
|
| 141 |
-
def set_embed_and_head(self, embed, head):
|
| 142 |
-
del self.model.embed_tokens.weight
|
| 143 |
-
del self.lm_head.weight
|
| 144 |
-
self.model.embed_tokens.weight = embed
|
| 145 |
-
self.lm_head.weight = head
|
| 146 |
-
torch.cuda.empty_cache()
|
| 147 |
-
torch.cuda.synchronize()
|
| 148 |
-
|
| 149 |
-
def set_dflash_layers_to_capture(self, layers_to_capture: list[int]):
|
| 150 |
-
if not self.pp_group.is_last_rank:
|
| 151 |
-
return
|
| 152 |
-
if layers_to_capture is None:
|
| 153 |
-
raise ValueError(
|
| 154 |
-
"DFLASH requires explicit layer ids for aux hidden capture."
|
| 155 |
-
)
|
| 156 |
-
self.capture_aux_hidden_states = True
|
| 157 |
-
self.model.set_dflash_layers_to_capture(layers_to_capture)
|
| 158 |
-
|
| 159 |
-
@torch.no_grad()
|
| 160 |
-
def forward(
|
| 161 |
-
self,
|
| 162 |
-
input_ids: torch.Tensor,
|
| 163 |
-
positions: torch.Tensor,
|
| 164 |
-
forward_batch: ForwardBatch,
|
| 165 |
-
input_embeds: Optional[torch.Tensor] = None,
|
| 166 |
-
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
| 167 |
-
**kwargs,
|
| 168 |
-
) -> Union[torch.Tensor, PPProxyTensors]:
|
| 169 |
-
if self.is_mrope_enabled:
|
| 170 |
-
positions = forward_batch.mrope_positions
|
| 171 |
-
|
| 172 |
-
hidden_states = self.model(
|
| 173 |
-
input_ids,
|
| 174 |
-
positions,
|
| 175 |
-
forward_batch,
|
| 176 |
-
input_embeds,
|
| 177 |
-
pp_proxy_tensors=pp_proxy_tensors,
|
| 178 |
-
)
|
| 179 |
-
|
| 180 |
-
if not self.pp_group.is_last_rank:
|
| 181 |
-
return hidden_states
|
| 182 |
-
|
| 183 |
-
aux_hidden_states = None
|
| 184 |
-
if isinstance(hidden_states, tuple):
|
| 185 |
-
hidden_states, aux_hidden_states = hidden_states
|
| 186 |
-
|
| 187 |
-
return self.logits_processor(
|
| 188 |
-
input_ids, hidden_states, self.lm_head, forward_batch, aux_hidden_states
|
| 189 |
-
)
|
| 190 |
-
|
| 191 |
-
def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]) -> Set[str]:
|
| 192 |
-
params_dict = dict(self.named_parameters())
|
| 193 |
-
loaded_params: Set[str] = set()
|
| 194 |
-
|
| 195 |
-
# Keep prefix stripping lazy: materializing mmap-backed checkpoint tensors
|
| 196 |
-
# across model processes can exceed host memory before GPU copies finish.
|
| 197 |
-
def portable_weights():
|
| 198 |
-
# HF native export packs experts and shared gate/up matrices. Expand
|
| 199 |
-
# lazily into the loader's per-expert format; no tensor values change.
|
| 200 |
-
for name, tensor in weights:
|
| 201 |
-
name = name.replace("model.language_model.", "model.")
|
| 202 |
-
if ".mlp.experts.gate_up_proj" in name:
|
| 203 |
-
stem = name.split(".mlp.experts.gate_up_proj")[0]
|
| 204 |
-
for expert in range(tensor.shape[0]):
|
| 205 |
-
gate, up = tensor[expert].chunk(2, dim=0)
|
| 206 |
-
yield f"{stem}.mlp.experts.{expert}.gate_proj.weight", gate
|
| 207 |
-
yield f"{stem}.mlp.experts.{expert}.up_proj.weight", up
|
| 208 |
-
elif ".mlp.experts.down_proj" in name:
|
| 209 |
-
stem = name.split(".mlp.experts.down_proj")[0]
|
| 210 |
-
for expert in range(tensor.shape[0]):
|
| 211 |
-
yield f"{stem}.mlp.experts.{expert}.down_proj.weight", tensor[expert]
|
| 212 |
-
elif ".mlp.shared_expert.gate_up_proj.weight" in name:
|
| 213 |
-
gate, up = tensor.chunk(2, dim=0)
|
| 214 |
-
yield name.replace("gate_up_proj", "gate_proj"), gate
|
| 215 |
-
yield name.replace("gate_up_proj", "up_proj"), up
|
| 216 |
-
else:
|
| 217 |
-
yield name, tensor
|
| 218 |
-
|
| 219 |
-
def body_weights():
|
| 220 |
-
fused_shared = max((getattr(layer.mlp, "num_fused_shared_experts", 0) for layer in self.model.layers), default=0)
|
| 221 |
-
for name, loaded_weight in portable_weights():
|
| 222 |
-
if fused_shared and ".mlp.shared_expert." in name:
|
| 223 |
-
name = name.replace(".mlp.shared_expert.", f".mlp.experts.{self.config.num_experts}.")
|
| 224 |
-
if name.startswith(_MODEL_PREFIX):
|
| 225 |
-
yield name[len(_MODEL_PREFIX) :], loaded_weight
|
| 226 |
-
elif name == "lm_head.weight":
|
| 227 |
-
if self.config.tie_word_embeddings:
|
| 228 |
-
continue
|
| 229 |
-
if "lm_head.weight" not in params_dict:
|
| 230 |
-
continue
|
| 231 |
-
param = params_dict["lm_head.weight"]
|
| 232 |
-
weight_loader = getattr(
|
| 233 |
-
param, "weight_loader", default_weight_loader
|
| 234 |
-
)
|
| 235 |
-
weight_loader(param, loaded_weight)
|
| 236 |
-
loaded_params.add("lm_head.weight")
|
| 237 |
-
|
| 238 |
-
body_loaded = self.model.load_weights(body_weights())
|
| 239 |
-
loaded_params.update(f"{_MODEL_PREFIX}{n}" for n in body_loaded)
|
| 240 |
-
|
| 241 |
-
if self.config.tie_word_embeddings and self.pp_group.is_last_rank:
|
| 242 |
-
loaded_params.add("lm_head.weight")
|
| 243 |
-
|
| 244 |
-
missing = set(params_dict) - loaded_params
|
| 245 |
-
if (self.quant_config is not None
|
| 246 |
-
and self.quant_config.get_name() == "fp8"
|
| 247 |
-
and not self.quant_config.is_checkpoint_fp8_serialized):
|
| 248 |
-
# Online BF16->FP8 creates these scales itself. They are deliberately
|
| 249 |
-
# absent from the source BF16 checkpoint; all model weights still
|
| 250 |
-
# have to load. Do not apply this exception to serialized FP8.
|
| 251 |
-
generated_suffixes = (
|
| 252 |
-
".mlp.experts.w13_weight_scale", ".mlp.experts.w2_weight_scale",
|
| 253 |
-
".mlp.experts.w13_weight_scale1", ".mlp.experts.w2_weight_scale1",
|
| 254 |
-
".attn.k_scale", ".attn.v_scale",
|
| 255 |
-
)
|
| 256 |
-
generated = {name for name in missing if name.endswith(generated_suffixes)}
|
| 257 |
-
missing -= generated
|
| 258 |
-
if missing:
|
| 259 |
-
raise RuntimeError(f"Incomplete text checkpoint load: {sorted(missing)[:20]}")
|
| 260 |
-
return loaded_params
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
class Qwen3_5MoeForCausalLM(Qwen3_5ForCausalLM):
|
| 264 |
-
body_cls = qwen3_5.Qwen3_5MoeForCausalLM
|
| 265 |
-
|
| 266 |
-
packed_modules_mapping = qwen3_5.Qwen3_5MoeForCausalLM.packed_modules_mapping
|
| 267 |
-
supported_lora_modules = qwen3_5.Qwen3_5MoeForCausalLM.supported_lora_modules
|
| 268 |
-
|
| 269 |
-
def __init__(
|
| 270 |
-
self,
|
| 271 |
-
config,
|
| 272 |
-
quant_config: Optional[QuantizationConfig] = None,
|
| 273 |
-
prefix: str = "",
|
| 274 |
-
) -> None:
|
| 275 |
-
super().__init__(config=config, quant_config=quant_config, prefix=prefix)
|
| 276 |
-
|
| 277 |
-
self._routed_experts_weights_of_layer = LazyValue(
|
| 278 |
-
lambda: {
|
| 279 |
-
layer_id: layer.mlp.get_moe_weights()
|
| 280 |
-
for layer_id, layer in enumerate(self.model.layers)
|
| 281 |
-
if isinstance(layer.mlp, Qwen2MoeSparseMoeBlock)
|
| 282 |
-
}
|
| 283 |
-
)
|
| 284 |
-
|
| 285 |
-
@property
|
| 286 |
-
def routed_experts_weights_of_layer(self):
|
| 287 |
-
return self._routed_experts_weights_of_layer.value
|
| 288 |
-
|
| 289 |
-
@classmethod
|
| 290 |
-
def get_model_config_for_expert_location(cls, config):
|
| 291 |
-
return ModelConfigForExpertLocation(
|
| 292 |
-
num_layers=config.num_hidden_layers,
|
| 293 |
-
num_logical_experts=config.num_experts,
|
| 294 |
-
num_groups=None,
|
| 295 |
-
)
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
EntryClass = [Qwen3_5MoeForCausalLM, Qwen3_5ForCausalLM]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runtime/patches/sglang-qwen35.py
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
serve.sh
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env bash
|
| 2 |
-
set -euo pipefail
|
| 3 |
-
|
| 4 |
-
model_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
| 5 |
-
image='lmsysorg/sglang@sha256:a0ffcdd013af6d79c9b7c4348c42b14a79a48f6c43dd3ca0235ec3802da45f75'
|
| 6 |
-
container_name=${NYX_CONTAINER_NAME:-nyx}
|
| 7 |
-
port=${NYX_PORT:-30000}
|
| 8 |
-
cache_dir=${NYX_CACHE_DIR:-/tmp/nyx-sglang-cache}
|
| 9 |
-
memory_fraction=${NYX_MEM_FRACTION:-0.75}
|
| 10 |
-
|
| 11 |
-
mkdir -p "$cache_dir/aiter-jit" "$cache_dir/sglang"
|
| 12 |
-
|
| 13 |
-
exec docker run --rm --name "$container_name" \
|
| 14 |
-
--device /dev/kfd --device /dev/dri --group-add video \
|
| 15 |
-
--ipc=host -p "127.0.0.1:${port}:30000" \
|
| 16 |
-
-e JEV_SELECTED_HEAD=0 \
|
| 17 |
-
-e SGLANG_USE_AITER_UNIFIED_ATTN=0 \
|
| 18 |
-
-e SGLANG_MAMBA_SSM_DTYPE=float32 \
|
| 19 |
-
-e AITER_JIT_DIR=/aiter-jit \
|
| 20 |
-
-v "$model_dir:/model:ro" \
|
| 21 |
-
-v "$cache_dir/aiter-jit:/aiter-jit" \
|
| 22 |
-
-v "$cache_dir/sglang:/root/.cache" \
|
| 23 |
-
-v "$model_dir/runtime/label-token-ids.json:/jev-label-token-ids.json:ro" \
|
| 24 |
-
-v "$model_dir/runtime/patches/sglang-logits-processor.py:/sgl-workspace/sglang/python/sglang/srt/layers/logits_processor.py:ro" \
|
| 25 |
-
-v "$model_dir/runtime/patches/sglang-qwen35.py:/sgl-workspace/sglang/python/sglang/srt/models/qwen3_5.py:ro" \
|
| 26 |
-
-v "$model_dir/runtime/patches/sglang-batch-result-processor.py:/sgl-workspace/sglang/python/sglang/srt/managers/scheduler_components/batch_result_processor.py:ro" \
|
| 27 |
-
--entrypoint /model/runtime/entrypoint.sh "$image" \
|
| 28 |
-
-m sglang.launch_server \
|
| 29 |
-
--model-path /model \
|
| 30 |
-
--load-format presharded \
|
| 31 |
-
--model-loader-extra-config '{"presharded_path":"/model/weights","max_file_bytes":5368709120,"verify_on_load":true}' \
|
| 32 |
-
--host 0.0.0.0 --port 30000 \
|
| 33 |
-
--dtype bfloat16 \
|
| 34 |
-
--quantization quark_int4fp8_moe \
|
| 35 |
-
--fp8-gemm-backend aiter \
|
| 36 |
-
--moe-runner-backend aiter \
|
| 37 |
-
--tp-size 1 --mem-fraction-static "$memory_fraction" \
|
| 38 |
-
--context-length 262144 --max-running-requests 64 \
|
| 39 |
-
--chunked-prefill-size 4096 \
|
| 40 |
-
--mamba-radix-cache-strategy extra_buffer \
|
| 41 |
-
--page-size 64 --attention-backend aiter \
|
| 42 |
-
--cuda-graph-backend-prefill breakable \
|
| 43 |
-
--cuda-graph-max-bs-decode 64 \
|
| 44 |
-
--cuda-graph-bs-prefill 128 256 512 1024 2048 4096
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenizer.json
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:06b9509352d2af50381ab2247e083b80d32d5c0aba91c272ca9ff729b6a0e523
|
| 3 |
-
size 19989325
|
|
|
|
|
|
|
|
|
|
|
|
tokenizer_config.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"add_prefix_space": false,
|
| 3 |
-
"audio_bos_token": "<|audio_start|>",
|
| 4 |
-
"audio_eos_token": "<|audio_end|>",
|
| 5 |
-
"audio_token": "<|audio_pad|>",
|
| 6 |
-
"backend": "tokenizers",
|
| 7 |
-
"bos_token": null,
|
| 8 |
-
"clean_up_tokenization_spaces": false,
|
| 9 |
-
"eos_token": "<|im_end|>",
|
| 10 |
-
"errors": "replace",
|
| 11 |
-
"image_token": "<|image_pad|>",
|
| 12 |
-
"is_local": true,
|
| 13 |
-
"local_files_only": true,
|
| 14 |
-
"model_max_length": 262144,
|
| 15 |
-
"model_specific_special_tokens": {
|
| 16 |
-
"audio_bos_token": "<|audio_start|>",
|
| 17 |
-
"audio_eos_token": "<|audio_end|>",
|
| 18 |
-
"audio_token": "<|audio_pad|>",
|
| 19 |
-
"image_token": "<|image_pad|>",
|
| 20 |
-
"video_token": "<|video_pad|>",
|
| 21 |
-
"vision_bos_token": "<|vision_start|>",
|
| 22 |
-
"vision_eos_token": "<|vision_end|>"
|
| 23 |
-
},
|
| 24 |
-
"pad_token": "<|endoftext|>",
|
| 25 |
-
"pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
| 26 |
-
"split_special_tokens": false,
|
| 27 |
-
"tokenizer_class": "Qwen2Tokenizer",
|
| 28 |
-
"unk_token": null,
|
| 29 |
-
"video_token": "<|video_pad|>",
|
| 30 |
-
"vision_bos_token": "<|vision_start|>",
|
| 31 |
-
"vision_eos_token": "<|vision_end|>"
|
| 32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
weights/TP-1-sig-e793a9432f30cd4e/READY
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
{"plan_version": 1, "world_size": 1, "created_at": 1789971566.5357497}
|
|
|
|
|
|
weights/TP-1-sig-e793a9432f30cd4e/checksum.json
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
weights/TP-1-sig-e793a9432f30cd4e/model-00000-common.safetensor
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:16e3906e9a161a6337a4a0f6d06e352d09360b37a9d4c4ea341ea978192ee289
|
| 3 |
-
size 5268047400
|
|
|
|
|
|
|
|
|
|
|
|
weights/TP-1-sig-e793a9432f30cd4e/model-00001-common.safetensor
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:7ad7affbcc8912528ff2859568b51ee9427cc17119312ea8750cf118544ea5d3
|
| 3 |
-
size 5120198752
|
|
|
|
|
|
|
|
|
|
|
|
weights/TP-1-sig-e793a9432f30cd4e/model-00002-common.safetensor
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:fa4a08a1e0e0de30543d343a629c6710e59575cd6f98f5beae789f34a59b79b7
|
| 3 |
-
size 5254941984
|
|
|
|
|
|
|
|
|
|
|
|
weights/TP-1-sig-e793a9432f30cd4e/model-00003-common.safetensor
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:dfb557a70a97b91208837dac145569c844971062970f41466738e61e77d53eb3
|
| 3 |
-
size 4014197624
|
|
|
|
|
|
|
|
|
|
|
|