Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ body:
- **Unverified, best effort:** TP2/TP8, V100 16 GB, other GPU counts,
other models and other topologies.
- **Out of scope:** GPUs other than SM70, ROCm/CPU/XPU/TPU, GGUF.
INT8 KV with the SSD tier is not supported in 0.1.0.
INT8 KV with the SSD tier is not supported in 0.1.4 either.

Remove credentials, tokens and private paths from logs.
Report security vulnerabilities through GitHub private vulnerability
Expand Down
13 changes: 9 additions & 4 deletions .github/MULTIMODAL_INPUTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ defaults above:
- With `--limit-mm-per-prompt` that leaves out `"video"`, video is set to 0 (disabled).
Other modalities the value leaves out keep the defaults above.

This V100 behaviour is the same as in 0.1.0 and 0.1.1, and 0.1.2 keeps it. Set
`--limit-mm-per-prompt` explicitly and include `"video"`. To copy:

- the generic 0.1.2 default: `--limit-mm-per-prompt '{"image": 8, "video": 1, "audio": 1}'`;
This V100 behaviour is the same as in 0.1.0 and 0.1.1, and 0.1.2 through 0.1.4 keep it.
Set `--limit-mm-per-prompt` explicitly and include `"video"`. To copy:

- the generic default counts (8 images, 1 video, 1 audio item):
`--limit-mm-per-prompt '{"image": 8, "video": 1, "audio": 1}'`. Qwen3.8 Flash-Next has
no audio input, so the shorter `--limit-mm-per-prompt '{"image": 8, "video": 1}'` — the
form used in [Configuration](../docs/configuration.md#launch-options) and
[Support and known limits](../docs/known-limits.md) — gives the same image and video
counts;
- the reference deployment, as in the README launch command:
`--limit-mm-per-prompt '{"image": 16, "video": 1}'` (Qwen3.8 Flash-Next has no audio input).

Expand Down
7 changes: 4 additions & 3 deletions .github/SUPPORT.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# ReVolta support scope

The [support matrix in the README](../README.md#support-matrix) is the reference.
The [support matrix](../docs/known-limits.md#support-matrix) in
[Support and known limits](../docs/known-limits.md) is the reference.
In short:

| Area | Scope |
| --- | --- |
| GPUs | 4 × NVIDIA V100 32 GB (PCIe or SXM2) |
| GPUs | Four or more NVIDIA V100 32 GB (PCIe or SXM2); validated on four |
| PCIe switch with P2P | Recommended; validated |
| PCIe without P2P | Validated by disabling P2P on P2P-capable hardware; about 30–55% of P2P speed |
| NVLink | Recommended; tested with community results on 4 × V100-SXM2 32 GB ([#2](https://github.com/Leonccaa/ReVolta/issues/2), [#38](https://github.com/Leonccaa/ReVolta/issues/38), [#37](https://github.com/Leonccaa/ReVolta/issues/37)) |
| Host memory | 128 GiB recommended; 80 GiB measured minimum with KV offload off |
| Host memory | 32 GiB tested minimum with the experimental whole-table PLE disk option; 80 GiB with the default pinned PLE table (KV offload off); 128 GiB recommended |
| KV offload | Off, CPU, or CPU + SSD tier |
| KV cache | E4M3 (default) or INT8; INT8 with the SSD tier is not supported |
| Models | Qwen3.8 Flash-Next (QSA, multimodal) |
Expand Down
4 changes: 2 additions & 2 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The release version is `RELEASE_VERSION` in `setup.py` (initially `0.1.0`).
Set `VLLM_VERSION_OVERRIDE` for an exact wheel and runtime version, or pass
`SETUPTOOLS_SCM_PRETEND_VERSION` to the Docker build. Inherited Git tags do not
select the ReVolta version. Use a PEP 440 local version for private deployments,
for example `0.1.3+deployment.20261025.g0123456789`.
for example `0.1.4+deployment.20261025.g0123456789`.

## SM70 source build

Expand All @@ -15,7 +15,7 @@ Use CUDA 12.8; CUDA 13 cannot compile SM70. Limit parallelism to available memor
NVCC thread per job are a conservative starting point on a 32 GB host.

```bash
version=0.1.3
version=0.1.4
docker build --target build -f docker/Dockerfile \
--build-arg torch_cuda_arch_list=7.0 \
--build-arg max_jobs=4 --build-arg nvcc_threads=1 \
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ GPU validation may require maintainer hardware.
Every pull request runs these checks, and all must pass before it can merge:
public text (no private addresses, local paths or credentials in the PR, its
commit messages or added lines), sign-off and added files (every commit signed
off; no added file over 1 MiB unless listed in
`.github/large-files-allowed.txt`), pre-commit on the changed files, and mypy
for Python 3.12.
off; no added file over 1 MiB unless its path is listed one per line in
`.github/large-files-allowed.txt`, which the check reads only when it exists),
pre-commit on the changed files, and mypy for Python 3.12.

Disclose AI assistance. The submitting human must review every changed line,
understand the change, run relevant checks, and be able to explain the result.
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ReVolta turns a server with V100 32 GB GPUs and plenty of system memory into a d

It is built for four or more V100 32 GB GPUs and validated on four. We recommend, and test, the two interconnects that give the best speed: NVLink, or all GPUs under one PCIe switch with P2P. Every release passes the same gate on our own server before it ships: single-request greedy output identical to the previous release token for token, 4 and 8 concurrent requests, images and video, CPU cache readback, and 512K-token prompts. That server runs ReVolta every day.

**You need:** 4 × V100 32 GB (NVLink or PCIe; NVLink or one PCIe switch with P2P recommended), at least 64 GB of system memory (128 GB recommended), Linux x86-64 with glibc 2.34 or newer, and an NVIDIA driver for CUDA 12.8. Tested hardware and limits: [Support and known limits](docs/known-limits.md).
**You need:** four or more V100 32 GB GPUs (validated on four), on NVLink or with all GPUs under one PCIe switch with P2P; at least 32 GiB of system memory with the experimental whole-table PLE disk option (about 23–25% slower single-request decoding), 80 GiB with the default pinned PLE table, 128 GiB recommended; Linux x86-64 with glibc 2.34 or newer; and an NVIDIA driver for CUDA 12.8. Tested hardware and limits: [Support and known limits](docs/known-limits.md).

Current release: [0.1.4](https://github.com/Leonccaa/ReVolta/releases/tag/v0.1.4).

Expand All @@ -23,7 +23,7 @@ Current release: [0.1.4](https://github.com/Leonccaa/ReVolta/releases/tag/v0.1.4

Prefixes evicted from the GPU stay in CPU memory or on disk: a cached 32K-token document comes back from CPU memory in **0.6 s**, and after an engine restart from a local NVMe disk tier in **1.5 s**, instead of **10.1 s** when recomputed (0.1.4, DFlash2). See [KV offload](docs/kv-offload.md).

**Speed** (total output tokens/s, 512-token responses; best of MTP4 and DFlash2)
**Speed** (total output tokens/s, 512-token responses; best of MTP4 and DFlash2; the PCIe row was measured on 0.1.2 and the NVLink row combines 0.1.2 and 0.1.1)

| System | 1 request | 2 | 4 | 8 | Prefill, 8K / 131K prompt |
| --- | ---: | ---: | ---: | ---: | ---: |
Expand All @@ -37,7 +37,7 @@ Versions, power caps and the per-method breakdown are in [Performance](docs/perf
| Model | Weights (bits per weight: routed experts / whole model) | Download | PLE table in host memory | GPU KV pool (MTP4) | Quality vs its BF16 parent: KL ↓ / top-1 ↑ | Notes |
| --- | --- | ---: | ---: | ---: | ---: | --- |
| [Uncensored AWQ g32](https://huggingface.co/leoncca/Qwen3.8-Flash-Next-Uncensored-AWQ-g32) **(recommended)** | 4-bit AWQ g32 experts, FP8 PLE, BF16 rest (**4.63 / 5.95**) | 142 GB, MTP draft included | 48 GiB | 1.15 M (1.37 M with DFlash2) | 0.0214 / 97.1% | Uncensored fine-tune; the reference setup |
| [Base AWQ g32](https://huggingface.co/leoncca/Qwen3.8-Flash-Next-AWQ-g32) | 4-bit AWQ g32 experts, FP8 PLE, BF16 rest (**4.63 / 5.95**) | 142 GB | 48 GiB | Same layout as Uncensored | 0.0207 / 97.1% | The original model |
| [Base AWQ g32](https://huggingface.co/leoncca/Qwen3.8-Flash-Next-AWQ-g32) | 4-bit AWQ g32 experts, FP8 PLE, BF16 rest (**4.63 / 5.95**) | 142 GB, MTP draft included | 48 GiB | Same layout as Uncensored | 0.0207 / 97.1% | The original model |
| [Standard NVFP4](https://huggingface.co/RadixArk/Qwen3.8-Flash-Next-NVFP4) | NVFP4 experts, FP8 PLE, BF16 rest (**4.50 / 5.86**) | 135 GB + 5 GB draft pack | 48 GiB | 1.25 M | 0.0303 / 96.4% | Widely used community format |
| [NVIDIA FP4-Local](https://huggingface.co/nvidia/Qwen3.8-Flash-Next-FP4-Local) | NVFP4 experts and PLE, MXFP8 attention and shared experts (**4.26 / 4.53**) | 102 GB, MTP layer built in | **27 GiB** | **1.47 M** | 0.0380 / 95.9% | NVIDIA's official checkpoint; least host memory |

Expand Down Expand Up @@ -86,7 +86,7 @@ For another checkpoint, use its [preparation guide](docs/checkpoints.md) before
- V100 16 GB, TP2/TP8, and other models have not been tested.
- This release builds SM70 only; other GPU architectures, ROCm, CPU, XPU, TPU, and GGUF are outside the build.
- Greedy outputs at C2–C8 can change between runs; fresh compiler autotuning can also change outputs.
- The AWQ PLE table needs about 48 GiB of host memory by default. An experimental option keeps the whole table on a local SSD instead: in 0.1.4 it served under 64 GiB and 32 GiB memory limits, also with a 32 GiB CPU KV pool under 64 GiB, and greedy output matched the default path. Single-request decoding is about 23–25% slower, and a cold first token takes 1.7–2.2 times as long. See [Support and known limits](docs/known-limits.md).
- The AWQ PLE table needs about 48 GiB of host memory by default. An experimental option keeps the whole table on a local SSD instead: in 0.1.4 it served under 64 GiB and 32 GiB memory limits, also with a 32 GiB CPU KV pool under 64 GiB, and greedy output matched the default path. Single-request decoding is about 23–25% slower, and a cold first token takes 1.6–2.3 times as long. See [Support and known limits](docs/known-limits.md).
- PLE cascade (spilling part of the table to disk, `ple_disk_cascade=true`) is not supported yet and is off by default in the ReVolta profile; full support is planned for 0.2.
- INT8 KV cannot be combined with DFlash2; the disk tier does not support INT8 yet.

Expand Down Expand Up @@ -188,7 +188,7 @@ See [Support and known limits](docs/known-limits.md).

### Support matrix

See [Support matrix](docs/known-limits.md).
See [Support matrix](docs/known-limits.md#support-matrix).

### Reporting issues

Expand Down
32 changes: 27 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ or disable custom all-reduce with `--disable-custom-all-reduce`.
to eight concurrent requests, at temperature 1 with thinking, including tool calls and
structured output. `greedy` still works with DFlash2. This setting is about drafting and
draft verification only; clients can still use any sampling parameters.
- **`--watchdog-config` (opt-in stack-dump watchdog, from 0.1.4, #33).** JSON with three
fields: `dump_dir` (directory for the dump files; the empty default disables the
watchdog), `timeout` (seconds without progress after which the process counts as stuck,
default 300, must be greater than 0) and `check_interval` (seconds between checks,
default 10). Example:
`--watchdog-config '{"timeout": 300, "check_interval": 10, "dump_dir": "/data/revolta/dumps"}'`.
When enabled it runs a background thread in the engine core process (`engine_<dp_rank>`)
and in each worker (`worker_<rank>`); if that process makes no progress for `timeout`
seconds it appends a numbered dump of all its Python thread stacks to
`<dump_dir>/VLLM_STACK_DUMP_for_<name>_<pid>.log`, at most once per stall, and logs
`[Watchdog]Dumped stack to ...`. The engine core also dumps its stacks on SIGTERM.
Waiting for incoming work does not count as a stall: the timeout window restarts when
the process resumes. This is a diagnostic aid, not a recovery mechanism: the process is
not restarted or killed.
- **`--gpu-memory-utilization 0.95`** passed the full gate, including the multimodal
stress test with 8 concurrent requests. Higher values ran out of memory in earlier
multimodal stress tests. If you change concurrency or media limits, measure again.
Expand All @@ -39,10 +53,12 @@ or disable custom all-reduce with `--disable-custom-all-reduce`.
- `cpu_bytes_to_use` is the total for all four workers (48 GiB = 12 GiB per worker).
- **Keep `--limit-mm-per-prompt` on V100.** With FlashAttention-V100, the engine allows
1 image and no video per request without it, and a value that leaves out `"video"`
disables video. 0.1.3 keeps this V100 default. `{"image": 16, "video": 1}`
disables video. 0.1.4 keeps this V100 default. `{"image": 16, "video": 1}`
keeps the reference deployment's limits; Qwen3.8 Flash-Next takes images and video,
not audio, so no audio limit is set. To use the generic image count instead, set
`--limit-mm-per-prompt '{"image": 8, "video": 1}'`; a lower image count leaves more
`--limit-mm-per-prompt '{"image": 8, "video": 1}'` (the inherited generic default is
8 images, 1 video and 1 audio item, and the audio field is left out here because this
model has no audio input); a lower image count leaves more
host memory for bursts of large images. Large images are resized
either way (see [Support and known limits](known-limits.md)).
- `--structured-outputs-config '{"backend": "xgrammar"}'` pins the structured-output
Expand Down Expand Up @@ -102,9 +118,15 @@ embedding table uses about 47.7 GiB of pinned host memory even with KV offload d
| 128 GiB | Off, or 48 GiB CPU pool | Full acceptance passed |
| 80 GiB | Off | Memory stress passed; full acceptance not run |
| 64 GiB | Off | Out of memory during startup |

80 GiB is the smallest tested limit that passed memory stress, without full acceptance. With less than 128 GiB,
reduce or disable the CPU KV pool.
| 64 GiB, whole PLE table on disk (experimental) | Off, or 32 GiB CPU pool | Served; greedy output matched the pinned path (0.1.4) |
| 32 GiB, whole PLE table on disk (experimental) | Off | Served; greedy output matched the pinned path (0.1.4) |

With the default pinned PLE table, 80 GiB is the smallest tested limit that passed memory
stress, with KV offload off and without full acceptance. With less than 128 GiB,
reduce or disable the CPU KV pool. The experimental whole-table PLE disk option
([Support and known limits](known-limits.md)) keeps the table on a local SSD instead and
brings the tested minimum down to 32 GiB; single-request decoding is about 23–25% slower
and a cold first token takes 1.6–2.3 times as long.

Image and video preprocessing also uses host memory, in the API server process. In one
test, 8 concurrent requests with 8 different 2048-pixel images each used about 33 GB at
Expand Down
9 changes: 7 additions & 2 deletions docs/dflash2.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ Use this guide to replace MTP4 with DFlash2 and choose its settings. First prepa
> Volta (SM70) GPUs, with V100 attention kernels and CUDA graphs for the drafter and the
> verifier, in the 512K-context setup with prefix caching and CPU KV offload. On one
> request it writes English code at up to **234 tokens/s instead of 180** with MTP4 (1.30×)
> on community NVLink hardware. In 0.1.3, the GPU KV pool reaches **1.38 million tokens (2.63× at 512K)** with the recommended DFlash2 settings, and 1.54 million (2.94×) with state replay on. MTP4
> on community NVLink hardware. With 0.1.4 and the recommended DFlash2 settings (probabilistic
> drafting, state replay off) the GPU KV pool reaches **1.37 million tokens (2.61× at 512K)**;
> with greedy drafting it is 1.38 million (2.63×), and the 0.1.3 measurement with state replay
> on (greedy drafting) reached 1.54 million (2.94×). MTP4
> stays the default: Chinese text and eight concurrent requests are still faster with MTP4.
> [How to enable it](#enable-dflash2).

Expand All @@ -19,7 +22,9 @@ instead (tested revision `a22a1744`): 5 sliding-window layers, an 8-token block
2048-token window. It is off by default. Without a `dflash` speculative config, the KV
cache layout, block size and MoE routes are the same as in 0.1.1.

The configuration below describes 0.1.3. With 0.1.2, also set `VLLM_V2_CUDAGRAPH_MEM_MIB=1500`; its profile does not reserve enough memory for DFlash2's graphs.
The configuration below is the recommended 0.1.4 configuration. With 0.1.2, also set
`VLLM_V2_CUDAGRAPH_MEM_MIB=1500`; its profile does not reserve enough memory for DFlash2's
graphs.

## Enable DFlash2

Expand Down
31 changes: 17 additions & 14 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ concurrent requests in testing, and a 48 GiB CPU KV pool.

## 1. Install

The distribution name is `revolta` (version `0.1.3`). The Python import name and the
The distribution name is `revolta` (version `0.1.4`). The Python import name and the
CLI are still **`vllm`** (`import vllm`, `vllm serve`); this is intentional. Only
compute capability 7.0 is built, with CUDA 12.8, Python 3.12 and PyTorch 2.10.

Expand All @@ -18,8 +18,8 @@ From PyPI (Linux x86-64 with glibc 2.34 or newer, Python 3.12):
```bash
python3.12 -m venv /data/revolta/venv
. /data/revolta/venv/bin/activate
pip install revolta==0.1.3
vllm --version # 0.1.3
pip install revolta==0.1.4
vllm --version # 0.1.4
```

The wheel pins `torch==2.10.0`, `torchaudio==2.10.0` and `torchvision==0.25.0`,
Expand All @@ -31,15 +31,15 @@ differs: it pins the same versions to the CUDA 12.8 builds on `download.pytorch.
and has the generic `linux_x86_64` tag.

```bash
pip install "https://github.com/Leonccaa/ReVolta/releases/download/v0.1.3/revolta-0.1.3-cp312-cp312-linux_x86_64.whl"
pip install "https://github.com/Leonccaa/ReVolta/releases/download/v0.1.4/revolta-0.1.4-cp312-cp312-linux_x86_64.whl"
```

ReVolta does not send the anonymous usage statistics that upstream vLLM reports.

Or use the Docker image:

```bash
docker pull ghcr.io/leonccaa/revolta:0.1.3
docker pull ghcr.io/leonccaa/revolta:0.1.4
```

## 2. Download the checkpoint and make 512K views
Expand Down Expand Up @@ -164,11 +164,13 @@ vllm serve /data/revolta/views/target-512k \
Notes:

- **Startup** takes roughly 10–15 minutes: weight and PLE loading, `torch.compile`
and CUDA graph capture. Later starts reuse the compile cache. Upgrading from 0.1.2
to 0.1.3 invalidates that cache, even with MTP or no speculation, because the traced
GDN source changed; allow a cold compile after upgrading. On a first start with
an empty compile cache, the reported GPU KV capacity can be one allocation unit
(7,182 tokens) lower.
and CUDA graph capture. Later starts reuse the compile cache. After an upgrade that
cache can still be incomplete: keeping the 0.1.3 cache, MTP4 and DFlash2 each compiled
four graphs again on the first 0.1.4 start (about 5 s on the reference machine), and
later starts loaded them from the cache. The 0.1.2 to 0.1.3 upgrade invalidated the
whole cache, even with MTP or no speculation, because the traced GDN source changed;
allow a cold compile after upgrading. On a first start with an empty compile cache,
the reported GPU KV capacity can be one allocation unit (7,182 tokens) lower.

See [Launch options](configuration.md#launch-options) before changing these settings.

Expand All @@ -195,9 +197,10 @@ For request defaults and the model vendor's recommendations, see
[Sampling parameters](configuration.md#sampling-parameters).

The server also provides Anthropic-compatible `/v1/messages` (including streaming)
and `/v1/messages/count_tokens`. On current main, an overlong prompt returns HTTP 400
with `invalid_request_error`; an unknown model returns HTTP 404 with `not_found_error`.
In 0.1.3, the overlong-prompt case returns HTTP 500 with `api_error` (#26).
and `/v1/messages/count_tokens`. From 0.1.4, an overlong prompt returns HTTP 400 with
`invalid_request_error` and an unknown model returns HTTP 404 with `not_found_error`;
internal errors stay HTTP 500 (#32). In 0.1.3 the overlong-prompt case returned HTTP 500
with `api_error` (#26).

## Docker

Expand All @@ -219,7 +222,7 @@ docker run --rm --gpus all \
-e VLLM_PLE_OFFLOAD_READY_TIMEOUT=1200 \
-e VLLM_QWEN4EXP_QSA_E4M3_STRICT_SCALES=1 \
-e TILELANG_CACHE_DIR=/data/revolta/cache/tilelang \
ghcr.io/leonccaa/revolta:0.1.3 \
ghcr.io/leonccaa/revolta:0.1.4 \
/data/revolta/views/target-512k --served-model-name Qwen3.8-Flash-Next ...
```

Expand Down
Loading
Loading