@ericrisco/rsc 0.1.39 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/manifest.json +181 -3
- package/package.json +1 -1
- package/scripts/lib/domains.js +1 -0
- package/scripts/lib/recommend.js +6 -0
- package/skills/deep-learning/SKILL.md +263 -0
- package/skills/deep-learning/evals/README.md +11 -0
- package/skills/deep-learning/evals/cases.yaml +48 -0
- package/skills/deep-learning/references/distributed.md +105 -0
- package/skills/deep-learning/references/training-recipes.md +135 -0
- package/skills/finetuning/SKILL.md +244 -0
- package/skills/finetuning/evals/README.md +13 -0
- package/skills/finetuning/evals/cases.yaml +50 -0
- package/skills/finetuning/references/hyperparameters-and-eval.md +74 -0
- package/skills/finetuning/references/methods.md +189 -0
- package/skills/harness/references/providers.yaml +35 -0
- package/skills/huggingface/SKILL.md +1 -1
- package/skills/machine-learning/SKILL.md +260 -0
- package/skills/machine-learning/evals/README.md +15 -0
- package/skills/machine-learning/evals/cases.yaml +54 -0
- package/skills/machine-learning/references/gbdt-and-tuning.md +83 -0
- package/skills/machine-learning/references/metrics-and-imbalance.md +82 -0
- package/skills/machine-learning/references/pipelines-and-cv.md +135 -0
- package/skills/nlp/SKILL.md +225 -0
- package/skills/nlp/evals/README.md +10 -0
- package/skills/nlp/evals/cases.yaml +45 -0
- package/skills/nlp/references/evaluation.md +107 -0
- package/skills/nlp/references/tokenization.md +95 -0
- package/skills/open-weights/SKILL.md +239 -0
- package/skills/open-weights/evals/README.md +14 -0
- package/skills/open-weights/evals/cases.yaml +42 -0
- package/skills/open-weights/references/licenses.md +123 -0
- package/skills/open-weights/references/model-families.md +83 -0
- package/skills/open-weights/references/sizing-and-quant.md +89 -0
- package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
- package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
- package/skills/training-data/SKILL.md +226 -0
- package/skills/training-data/evals/README.md +13 -0
- package/skills/training-data/evals/cases.yaml +41 -0
- package/skills/training-data/references/formats.md +118 -0
- package/skills/training-data/references/synthesis-dedup-quality.md +134 -0
- package/skills/unsloth/SKILL.md +254 -0
- package/skills/unsloth/evals/README.md +10 -0
- package/skills/unsloth/evals/cases.yaml +42 -0
- package/skills/unsloth/references/export.md +75 -0
- package/skills/unsloth/references/grpo.md +91 -0
- package/skills/unsloth/references/masking-and-templates.md +91 -0
- package/skills/vllm/SKILL.md +235 -0
- package/skills/vllm/evals/README.md +9 -0
- package/skills/vllm/evals/cases.yaml +47 -0
- package/skills/vllm/references/flags-and-endpoints.md +105 -0
- package/skills/vllm/references/memory-and-throughput.md +78 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Chat templates + response-only masking
|
|
2
|
+
|
|
3
|
+
Facts here track docs.unsloth.ai/basics/chat-templates (accessed 2026-07). Template names and turn
|
|
4
|
+
markers change with new model releases — **verify the pair for your exact base model.**
|
|
5
|
+
|
|
6
|
+
## Why a helper and not a string
|
|
7
|
+
|
|
8
|
+
A chat model was pretrained with a specific set of turn markers (`<|start_header_id|>…`,
|
|
9
|
+
`<start_of_turn>…`, `<|im_start|>…`). If you concatenate messages with the wrong markers, the model
|
|
10
|
+
never sees its own format and the fine-tune underperforms or degenerates. `get_chat_template` writes
|
|
11
|
+
the *correct* template onto your tokenizer; `apply_chat_template` then renders your message list into
|
|
12
|
+
the single `text` string the trainer consumes.
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from unsloth.chat_templates import get_chat_template
|
|
16
|
+
|
|
17
|
+
tokenizer = get_chat_template(
|
|
18
|
+
tokenizer,
|
|
19
|
+
chat_template = "llama-3.1", # match the base model
|
|
20
|
+
# mapping = {"role":"from","content":"value","user":"human","assistant":"gpt"}, # if your
|
|
21
|
+
# dataset uses ShareGPT-style keys instead of role/content
|
|
22
|
+
# map_eos_token = True, # map e.g. <|im_end|> -> the model's EOS
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def to_text(example):
|
|
26
|
+
example["text"] = tokenizer.apply_chat_template(
|
|
27
|
+
example["messages"], tokenize = False, add_generation_prompt = False,
|
|
28
|
+
)
|
|
29
|
+
return example
|
|
30
|
+
|
|
31
|
+
dataset = dataset.map(to_text)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Supported template names include (non-exhaustive; verify live): `llama-3`, `llama-3.1`, `chatml`,
|
|
35
|
+
`mistral`, `phi-4`, `gemma-3`, `qwen`, `zephyr`, `vicuna`, `alpaca`, `unsloth`. Use the one that
|
|
36
|
+
matches your checkpoint.
|
|
37
|
+
|
|
38
|
+
Thinking / reasoning models expose a toggle at render time:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
tokenizer.apply_chat_template(msgs, tokenize=False,
|
|
42
|
+
add_generation_prompt=True, enable_thinking=True) # or False
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## `train_on_responses_only` — the mask
|
|
46
|
+
|
|
47
|
+
Supervised fine-tuning of a chat model should compute loss on the **assistant** tokens only. Without
|
|
48
|
+
this, the model is also trained to predict the user's words; loss can even collapse to ~0 on some
|
|
49
|
+
templates (the "zero-loss trap"). Wrap the trainer AFTER constructing it:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from unsloth.chat_templates import train_on_responses_only
|
|
53
|
+
|
|
54
|
+
trainer = train_on_responses_only(
|
|
55
|
+
trainer,
|
|
56
|
+
instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n",
|
|
57
|
+
response_part = "<|start_header_id|>assistant<|end_header_id|>\n\n",
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`instruction_part` / `response_part` are the template's own turn-opening markers. Everything from an
|
|
62
|
+
`instruction_part` up to the next `response_part` is masked (label = `-100`); the assistant span keeps
|
|
63
|
+
its labels. **The strings must match the template you applied.** Common pairs (verify per model):
|
|
64
|
+
|
|
65
|
+
| Base family | `instruction_part` | `response_part` |
|
|
66
|
+
| ---------------- | ----------------------------------------------- | -------------------------------------------------- |
|
|
67
|
+
| Llama-3 / 3.1 | `<\|start_header_id\|>user<\|end_header_id\|>\n\n` | `<\|start_header_id\|>assistant<\|end_header_id\|>\n\n` |
|
|
68
|
+
| Gemma-3 / vision | `<start_of_turn>user\n` | `<start_of_turn>model\n` |
|
|
69
|
+
| ChatML (Qwen…) | `<\|im_start\|>user\n` | `<\|im_start\|>assistant\n` |
|
|
70
|
+
|
|
71
|
+
## Sanity-check the mask (always do this once)
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
row = trainer.train_dataset[0]
|
|
75
|
+
# 1) full sequence — should include both user + assistant turns
|
|
76
|
+
print(tokenizer.decode(row["input_ids"]))
|
|
77
|
+
# 2) UNMASKED labels only — should be the ASSISTANT answer and nothing else
|
|
78
|
+
print(tokenizer.decode(
|
|
79
|
+
[tokenizer.pad_token_id if x == -100 else x for x in row["labels"]]
|
|
80
|
+
).replace(tokenizer.pad_token, " "))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If step 2 prints the user's question, your `instruction_part`/`response_part` don't match the
|
|
84
|
+
template — fix them before training. Spending GPU-hours on an unmasked run is the classic waste.
|
|
85
|
+
|
|
86
|
+
## Vision (VLM)
|
|
87
|
+
|
|
88
|
+
Vision fine-tunes use `FastVisionModel.from_pretrained(...)` and a processor/tokenizer pair; the
|
|
89
|
+
template and mask concepts carry over but the collator handles image tokens. See
|
|
90
|
+
docs.unsloth.ai for the current VLM notebook — the exact API for multimodal collation moves faster
|
|
91
|
+
than the text path, so pull it live rather than trusting a frozen snippet.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vllm
|
|
3
|
+
description: "Use when self-hosting an open-weight LLM for high-throughput, concurrent serving with vLLM — running `vllm serve` as an OpenAI-compatible endpoint, splitting a model across GPUs with tensor/pipeline parallelism, loading AWQ/GPTQ/FP8/compressed-tensors quants, serving one or many LoRA adapters, or debugging KV-cache OOM from --gpu-memory-utilization / --max-model-len. Triggers: 'serve a 70B to many users at once', 'point my OpenAI client at a self-hosted model', 'split a model across 4 GPUs with tensor parallel', 'why does vllm OOM at long context', 'serve my fine-tuned LoRA adapter', 'health check my vllm server', 'servir un modelo abierto con alta concurrencia en mis propias GPUs'. NOT renting/provisioning the GPU box itself (that is runpod / modal), NOT single-user laptop inference (that is ollama), NOT a hosted inference API you do not operate (that is together-fireworks / huggingface)."
|
|
4
|
+
tags: [vllm, llm-serving, inference-server, tensor-parallel, quantization]
|
|
5
|
+
recommends: [open-weights, finetuning, runpod, modal, ollama]
|
|
6
|
+
origin: risco
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# vLLM — high-throughput serving of open-weight models
|
|
10
|
+
|
|
11
|
+
vLLM is the inference **engine** you put in front of an open-weight model when many requests hit it at
|
|
12
|
+
once. Its job — and this skill's — is throughput under concurrency: keep the GPU busy across dozens of
|
|
13
|
+
simultaneous requests, not squeeze one prompt out fast. You own the `vllm serve` flags; the box those
|
|
14
|
+
flags run on is a `runpod`/`modal` concern.
|
|
15
|
+
|
|
16
|
+
**Why not just loop a `transformers` `generate()`?** Naive serving runs one request at a time and pads
|
|
17
|
+
every batch to the longest sequence, so the GPU idles. vLLM fixes both:
|
|
18
|
+
|
|
19
|
+
- **PagedAttention** stores the KV cache in non-contiguous fixed-size blocks (like OS virtual-memory
|
|
20
|
+
paging), so there is almost no padding/reservation waste and long contexts pack tightly.
|
|
21
|
+
- **Continuous batching** admits and retires requests token-by-token instead of per-batch, so a new
|
|
22
|
+
request joins the running batch immediately rather than waiting for the slowest one to finish.
|
|
23
|
+
|
|
24
|
+
Net effect: an order-of-magnitude more concurrent throughput than single-request serving. If you only
|
|
25
|
+
ever have one user on a laptop, that machinery is wasted — that is `ollama`, not this.
|
|
26
|
+
|
|
27
|
+
## Version & setup reality (verify at author time — vLLM ships ~weekly)
|
|
28
|
+
|
|
29
|
+
- **Latest stable is ~0.25.x (July 2026)** — releases land roughly weekly. Check
|
|
30
|
+
[PyPI](https://pypi.org/project/vllm/) / [releases](https://github.com/vllm-project/vllm/releases);
|
|
31
|
+
do not pin to a number you read here.
|
|
32
|
+
- **The V1 engine is the default since v0.8.0** and recent releases have removed the legacy V0 path,
|
|
33
|
+
so treat V1 as the only engine. It runs the scheduler + core loop in a separate process and turns on
|
|
34
|
+
chunked prefill and prefix caching out of the box, so you rarely tune the scheduler by hand
|
|
35
|
+
([V1 guide](https://docs.vllm.ai/en/latest/usage/v1_guide.html), accessed 2026-07). `VLLM_USE_V1`
|
|
36
|
+
historically toggled it — if you see it referenced, it is legacy.
|
|
37
|
+
- **Install:** `pip install vllm` (default build is CUDA/NVIDIA). ROCm, CPU, TPU and other backends
|
|
38
|
+
have separate install paths — see the docs' installation matrix. Needs Python + a supported GPU.
|
|
39
|
+
- **Auth is OFF by default.** A bare `vllm serve` is an *open* endpoint on `0.0.0.0:8000`. To require a
|
|
40
|
+
bearer token, pass `--api-key <KEY>` (or set `VLLM_API_KEY`); clients then send
|
|
41
|
+
`Authorization: Bearer <KEY>`. Do not expose an unauthenticated server publicly.
|
|
42
|
+
|
|
43
|
+
## `vllm serve` → an OpenAI-compatible server
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
vllm serve Qwen/Qwen3-8B # download from HF (or a local path) and serve on :8000
|
|
47
|
+
vllm serve /models/qwen3-8b --api-key sk-local-xyz --port 8000 --host 0.0.0.0
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The server speaks the OpenAI wire protocol, so **any OpenAI client works unchanged** — just repoint
|
|
51
|
+
`base_url` and use a dummy (or your `--api-key`) key. Endpoints
|
|
52
|
+
([online serving docs](https://docs.vllm.ai/en/latest/serving/openai_compatible_server/), accessed 2026-07):
|
|
53
|
+
|
|
54
|
+
| Endpoint | Purpose |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `POST /v1/chat/completions` | chat protocol (messages array) — the usual path |
|
|
57
|
+
| `POST /v1/completions` | raw text completion (single prompt string) |
|
|
58
|
+
| `GET /v1/models` | list the served model + any loaded LoRA adapters |
|
|
59
|
+
| `POST /v1/embeddings` | only for an embedding model (`--task embed`) |
|
|
60
|
+
| `GET /health` | liveness — 200 when ready, **no auth, no body** |
|
|
61
|
+
| `GET /metrics` | Prometheus metrics (queue depth, throughput, cache usage) |
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from openai import OpenAI
|
|
65
|
+
client = OpenAI(base_url="http://localhost:8000/v1", api_key="sk-local-xyz") # key = your --api-key
|
|
66
|
+
r = client.chat.completions.create(
|
|
67
|
+
model="Qwen/Qwen3-8B", # the id `/v1/models` reports (or a LoRA adapter name)
|
|
68
|
+
messages=[{"role": "user", "content": "Name three primes."}],
|
|
69
|
+
)
|
|
70
|
+
print(r.choices[0].message.content)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `model` field must match what `/v1/models` returns — the served model id or a LoRA adapter name
|
|
74
|
+
(below), not an arbitrary string.
|
|
75
|
+
|
|
76
|
+
## Parallelism: fit the model, then scale out
|
|
77
|
+
|
|
78
|
+
Two orthogonal knobs. Reach for parallelism only when the model does **not** fit one GPU — a model that
|
|
79
|
+
fits should stay on a single GPU (no split), because every split adds communication overhead.
|
|
80
|
+
|
|
81
|
+
- **`--tensor-parallel-size N`** — shard each layer's weights *across N GPUs on one node*. Use this
|
|
82
|
+
first: it needs fast intra-node links (NVLink / PCIe) because GPUs sync every layer. `N` must divide
|
|
83
|
+
the model's attention-head count. This is how you serve a model too big for one GPU but fitting the
|
|
84
|
+
node.
|
|
85
|
+
- **`--pipeline-parallel-size M`** — split the model *by layer stages across M nodes*. Tolerates slower
|
|
86
|
+
inter-node network. Use it when the model does not fit even a full node.
|
|
87
|
+
|
|
88
|
+
Rule of thumb from the docs
|
|
89
|
+
([parallelism & scaling](https://docs.vllm.ai/en/latest/serving/distributed_serving.html), accessed 2026-07):
|
|
90
|
+
set **tensor-parallel = GPUs per node**, **pipeline-parallel = number of nodes**.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
vllm serve meta-llama/Llama-3.3-70B-Instruct --tensor-parallel-size 4 # 1 node, 4 GPUs
|
|
94
|
+
vllm serve <huge-model> --tensor-parallel-size 8 --pipeline-parallel-size 2 # 2 nodes × 8 GPUs
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Multi-node needs a Ray cluster wired up first — that orchestration is a `runpod`/`modal` concern, not a
|
|
98
|
+
vLLM flag.
|
|
99
|
+
|
|
100
|
+
## Quantization: smaller weights ≠ automatic speedup
|
|
101
|
+
|
|
102
|
+
Quantization shrinks the weights so a model fits fewer/smaller GPUs and frees VRAM for KV cache. Select
|
|
103
|
+
a method with `--quantization` (often auto-detected from the checkpoint's config). vLLM supports
|
|
104
|
+
**AWQ, GPTQ/GPTQModel, FP8 (W8A8), compressed-tensors (LLM Compressor), INT4/INT8, bitsandbytes** and
|
|
105
|
+
more ([quantization docs](https://docs.vllm.ai/en/latest/features/quantization/), accessed 2026-07).
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
vllm serve TheModel/Qwen3-8B-AWQ --quantization awq
|
|
109
|
+
vllm serve neuralmagic/Model-FP8 --quantization compressed-tensors # FP8 needs Ada/Hopper+
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The honest tradeoffs:
|
|
113
|
+
|
|
114
|
+
- **Support is GPU-arch-specific** — FP8 W8A8 wants Ada/Hopper-class (or AMD) hardware; a method that
|
|
115
|
+
is fast on one GPU may be unsupported or emulated on another. Check the compatibility table.
|
|
116
|
+
- **Quant is a memory win, not a guaranteed throughput win.** At **low batch / low concurrency** the
|
|
117
|
+
workload is memory-bandwidth-bound and a quant can help; but dequant overhead can *cost* latency, and
|
|
118
|
+
at high batch you may be compute-bound where a weight-only quant does little. Benchmark your quant on
|
|
119
|
+
your hardware at your real concurrency before assuming it is faster.
|
|
120
|
+
- **Quality drops** — usually small for 8-bit/well-calibrated 4-bit, larger for aggressive 4-bit. Serve
|
|
121
|
+
a quant to save VRAM, not as a free lunch. Picking *which* quantized checkpoint to pull is an
|
|
122
|
+
`open-weights` decision.
|
|
123
|
+
|
|
124
|
+
## LoRA adapters: serve the fine-tuning output
|
|
125
|
+
|
|
126
|
+
vLLM serves LoRA adapters on top of one loaded base model, so your `finetuning`/`unsloth` output goes
|
|
127
|
+
live without merging or a second server
|
|
128
|
+
([LoRA docs](https://docs.vllm.ai/en/latest/features/lora.html), accessed 2026-07).
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
vllm serve meta-llama/Llama-3.2-3B-Instruct \
|
|
132
|
+
--enable-lora \
|
|
133
|
+
--lora-modules sql=/adapters/sql-lora legal=/adapters/legal-lora \
|
|
134
|
+
--max-loras 2 \ # how many adapters resident at once
|
|
135
|
+
--max-lora-rank 16 # must be >= the rank the adapter was trained at
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Route to an adapter by naming it in the request `model` field: `"model": "sql"` hits the SQL adapter,
|
|
139
|
+
`"model": "meta-llama/Llama-3.2-3B-Instruct"` hits the untuned base — same server, no reload.
|
|
140
|
+
|
|
141
|
+
Load/unload at runtime with `VLLM_ALLOW_RUNTIME_LORA_UPDATING=True`, then
|
|
142
|
+
`POST /v1/load_lora_adapter` / `POST /v1/unload_lora_adapter`. Base + adapter must match (same family
|
|
143
|
+
and dims), and `--max-lora-rank` must be ≥ the trained rank or load fails.
|
|
144
|
+
|
|
145
|
+
## Memory & throughput: where OOM comes from
|
|
146
|
+
|
|
147
|
+
The two knobs that cause (and cure) most OOM
|
|
148
|
+
([engine args](https://docs.vllm.ai/en/latest/configuration/engine_args.html), accessed 2026-07):
|
|
149
|
+
|
|
150
|
+
- **`--gpu-memory-utilization`** (default ~**0.92**) — fraction of each GPU vLLM may claim. Weights are
|
|
151
|
+
loaded, then the **rest of this budget becomes the KV-cache pool**. Raising it toward 1.0 buys more
|
|
152
|
+
concurrent sequences but risks OOM from activation/CUDA-graph spikes; lower it if you get OOM at load
|
|
153
|
+
or under burst.
|
|
154
|
+
- **`--max-model-len`** — max context (prompt + output) per request; auto-derived from the model config
|
|
155
|
+
if unset. This is the single biggest OOM lever: KV cache scales with `max-model-len × concurrent
|
|
156
|
+
sequences`. A model whose weights fit will still OOM if you leave the full 128K context on and let
|
|
157
|
+
many long requests batch. **Cap `--max-model-len` to what you actually need.**
|
|
158
|
+
|
|
159
|
+
The mental model: `KV_pool = gpu_memory_utilization × VRAM − weights`, and
|
|
160
|
+
`concurrent_sequences ≈ KV_pool ÷ (bytes_per_token × context_len)`. Too-long context or too-high
|
|
161
|
+
utilization eats the pool. `--max-num-seqs` and `--max-num-batched-tokens` cap the batch to trade
|
|
162
|
+
latency vs throughput. Full KV math + an OOM playbook: [references/memory-and-throughput.md](references/memory-and-throughput.md).
|
|
163
|
+
|
|
164
|
+
## Self-host health check (no credential needed)
|
|
165
|
+
|
|
166
|
+
`/health` requires no API key, so it is the safe first probe on any endpoint you did not just start:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
export VLLM_BASE_URL=http://localhost:8000
|
|
170
|
+
curl -fsS "$VLLM_BASE_URL/health" && echo " up" # 200, empty body when ready
|
|
171
|
+
curl -fsS "$VLLM_BASE_URL/v1/models" \
|
|
172
|
+
-H "Authorization: Bearer ${VLLM_API_KEY:-sk-local}" # confirms WHICH model/adapters are served
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`/health` says "the server is alive"; `/v1/models` says "and it is serving the model you expect" (plus
|
|
176
|
+
any LoRA adapters). If `--api-key` is set, `/v1/models` needs the bearer header but `/health` never
|
|
177
|
+
does. During load a slow first `/health` is normal — big models take a while to page in.
|
|
178
|
+
|
|
179
|
+
## Honest alternatives — when to prefer another engine
|
|
180
|
+
|
|
181
|
+
- **Text Generation Inference (TGI)** — Hugging Face's server; reach for it if you are all-in on the HF
|
|
182
|
+
ecosystem/tooling and want their supported stack.
|
|
183
|
+
- **SGLang** — competitive throughput with strong RadixAttention prefix-cache reuse; prefer it for
|
|
184
|
+
heavy shared-prefix workloads (agents, long system prompts, structured generation).
|
|
185
|
+
- **TensorRT-LLM** — squeezes maximum latency/throughput on NVIDIA GPUs via compiled engines, at the
|
|
186
|
+
cost of a heavier build/ahead-of-time compile step; prefer it when you must wring out every last ms
|
|
187
|
+
on NVIDIA hardware and can pay the ops complexity.
|
|
188
|
+
- **Ollama / llama.cpp** — single-box / laptop / CPU-or-Metal, one or few users, GGUF quants,
|
|
189
|
+
zero-ceremony. Prefer it when there is no concurrency to exploit — that is the `ollama` skill.
|
|
190
|
+
|
|
191
|
+
## Guardrails
|
|
192
|
+
|
|
193
|
+
- **Cap `--max-model-len`.** Leaving the full context window on is the top OOM cause; KV cache scales
|
|
194
|
+
with context × concurrency ([engine args](https://docs.vllm.ai/en/latest/configuration/engine_args.html)).
|
|
195
|
+
- **Tune `--gpu-memory-utilization` for OOM, not throughput first.** Lower it if OOM at load/burst;
|
|
196
|
+
raise it (carefully) for more concurrency. It is a fraction of *one* GPU's memory.
|
|
197
|
+
- **A model that fits one GPU should stay on one GPU.** Tensor parallelism adds sync overhead — use it
|
|
198
|
+
to *fit*, not to speed up an already-fitting model.
|
|
199
|
+
- **Quant is a VRAM win, not free speed**, and its quality cost is real — verify throughput at your
|
|
200
|
+
concurrency and quality on your task ([quantization](https://docs.vllm.ai/en/latest/features/quantization/)).
|
|
201
|
+
- **Never expose an unauthenticated server.** No `--api-key` = open endpoint on `0.0.0.0:8000`.
|
|
202
|
+
- **`--max-lora-rank` must be ≥ the adapter's trained rank**, and base + adapter must match, or the LoRA
|
|
203
|
+
fails to load.
|
|
204
|
+
- **This is not a laptop tool.** No GPU / one user → `ollama`. The GPU box + autoscaling → `runpod` /
|
|
205
|
+
`modal`.
|
|
206
|
+
|
|
207
|
+
## Related skills
|
|
208
|
+
|
|
209
|
+
- **`open-weights`** — choose the model/size/license/quant to serve. vLLM runs it; it does not pick it.
|
|
210
|
+
- **`finetuning`** (and single-GPU **`unsloth`**) — produce the LoRA adapter or merged weights that
|
|
211
|
+
`--enable-lora`/`vllm serve` then hosts. They train; vLLM serves.
|
|
212
|
+
- **`runpod`** — rent/provision the GPU box vLLM runs on (bring-your-own-container GPU rental).
|
|
213
|
+
- **`modal`** — serverless GPU containers + autoscaling around a vLLM process. The *box and scaling*,
|
|
214
|
+
vs vLLM the *engine*.
|
|
215
|
+
- **`ollama`** — the single-user / laptop counterpart. No concurrency to exploit → use it, not vLLM.
|
|
216
|
+
- Hosted inference you do not operate → `together-fireworks` / `huggingface` (you call an API; here you
|
|
217
|
+
run the server).
|
|
218
|
+
|
|
219
|
+
## Checklist
|
|
220
|
+
|
|
221
|
+
- [ ] Chose vLLM because there is **concurrency** to exploit (else `ollama` / a hosted API).
|
|
222
|
+
- [ ] `vllm serve <model>` up; `/health` returns 200 and `/v1/models` shows the expected model.
|
|
223
|
+
- [ ] `--api-key` (or `VLLM_API_KEY`) set if the endpoint is reachable beyond localhost.
|
|
224
|
+
- [ ] Parallelism only if the model does not fit one GPU: TP = GPUs/node, PP = nodes.
|
|
225
|
+
- [ ] `--max-model-len` capped to real need; `--gpu-memory-utilization` set with OOM headroom.
|
|
226
|
+
- [ ] Quant chosen for VRAM fit and benchmarked at real concurrency (not assumed faster).
|
|
227
|
+
- [ ] LoRA: `--max-lora-rank` ≥ trained rank; adapter reachable by name via the `model` field.
|
|
228
|
+
- [ ] Client points at `base_url=<server>/v1`; `model` matches a `/v1/models` id.
|
|
229
|
+
|
|
230
|
+
## References
|
|
231
|
+
|
|
232
|
+
- [references/flags-and-endpoints.md](references/flags-and-endpoints.md) — the load-bearing `vllm serve`
|
|
233
|
+
flags, the full endpoint catalog with example requests, parallelism sizing, and the LoRA runtime API.
|
|
234
|
+
- [references/memory-and-throughput.md](references/memory-and-throughput.md) — KV-cache math, the OOM
|
|
235
|
+
playbook, quantization tradeoffs at different batch sizes, and throughput-tuning knobs.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# vllm evals
|
|
2
|
+
|
|
3
|
+
- `should_trigger`: concurrent self-hosted serving jobs vLLM owns — `vllm serve` as an OpenAI-compatible
|
|
4
|
+
endpoint, tensor parallelism, serving a LoRA adapter, and KV-cache OOM debugging (incl. a Spanish phrasing).
|
|
5
|
+
- `should_not_trigger`: routes the box/provisioning to `runpod`, autoscaling to `modal`, laptop/single-user
|
|
6
|
+
to `ollama`, model/quant selection to `open-weights`, hosted APIs to `together-fireworks`, and training to `finetuning`.
|
|
7
|
+
- `capability`: asserts a full serve command surfaces the OpenAI-compat endpoints, tensor parallelism for a
|
|
8
|
+
node-sized model, LoRA flags with the rank constraint, the max-model-len/gpu-memory-utilization OOM guardrail,
|
|
9
|
+
auth + health check, and the boundary/version hedge.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
skill: vllm
|
|
2
|
+
|
|
3
|
+
should_trigger:
|
|
4
|
+
- prompt: "I need to serve Llama 3.3 70B to hundreds of concurrent users on our own GPUs — what should I run?"
|
|
5
|
+
why: Core niche — high-throughput concurrent self-hosted serving is exactly what vLLM (PagedAttention + continuous batching) is for.
|
|
6
|
+
- prompt: "Split a model across 4 A100s with vLLM tensor parallelism and expose an OpenAI-compatible endpoint."
|
|
7
|
+
why: --tensor-parallel-size plus the OpenAI-compatible server, both owned here.
|
|
8
|
+
- prompt: "My vllm serve keeps OOMing once requests get to long context — how do I fix it?"
|
|
9
|
+
why: Non-obvious — KV cache scales with max-model-len × concurrency; fix is --max-model-len / --gpu-memory-utilization, the load-bearing guardrail.
|
|
10
|
+
- prompt: "How do I serve my fine-tuned LoRA adapter on top of the base model without merging it?"
|
|
11
|
+
why: --enable-lora + --lora-modules, routing by the request model field — serving the finetuning output.
|
|
12
|
+
- prompt: "Point my existing OpenAI Python client at a self-hosted vLLM server instead of api.openai.com."
|
|
13
|
+
why: The /v1 OpenAI-compatible surface with a repointed base_url — a defining vLLM path.
|
|
14
|
+
- prompt: "Is serving an AWQ quant on vLLM actually faster, or just smaller in VRAM?"
|
|
15
|
+
why: Quantization tradeoff (memory vs throughput by batch regime), an explicit section here.
|
|
16
|
+
- prompt: "Servir un modelo abierto con alta concurrencia en mis propias GPUs con endpoint compatible con OpenAI."
|
|
17
|
+
why: Spanish; the concurrent self-hosted OpenAI-compatible serving framing that defines vLLM.
|
|
18
|
+
|
|
19
|
+
should_not_trigger:
|
|
20
|
+
- prompt: "Rent an A100 box in the cloud so I have somewhere to run my inference server."
|
|
21
|
+
route_to: runpod
|
|
22
|
+
why: Provisioning/renting the GPU box, not the serving engine flags that run on it.
|
|
23
|
+
- prompt: "Wrap my model in a serverless container that autoscales GPUs with traffic."
|
|
24
|
+
route_to: modal
|
|
25
|
+
why: The box + autoscaling around the process; vLLM is the engine inside, not the scaler.
|
|
26
|
+
- prompt: "I just want to run a 7B locally on my laptop for myself, no concurrency."
|
|
27
|
+
route_to: ollama
|
|
28
|
+
why: Single-user / laptop inference — no concurrency to exploit, so vLLM's machinery is wasted.
|
|
29
|
+
- prompt: "Which open model and quant should I even pick for a 24GB GPU under a permissive license?"
|
|
30
|
+
route_to: open-weights
|
|
31
|
+
why: Model/size/license/quant selection is the knowledge layer feeding vLLM, not a serving-flag question.
|
|
32
|
+
- prompt: "Just call a hosted inference API for Llama so I don't run any servers."
|
|
33
|
+
route_to: together-fireworks
|
|
34
|
+
why: A hosted API the user does not operate; vLLM is for a server you run yourself.
|
|
35
|
+
- prompt: "Fine-tune Llama 3 on my dataset to produce a LoRA adapter."
|
|
36
|
+
route_to: finetuning
|
|
37
|
+
why: Producing the adapter is training; vLLM serves the result, it does not train it.
|
|
38
|
+
|
|
39
|
+
capability:
|
|
40
|
+
- scenario: "We have one node with 4 A100 80GB GPUs and need to serve Llama-3.3-70B to many concurrent users behind an OpenAI-compatible endpoint, including one fine-tuned LoRA adapter, without OOMing at long context."
|
|
41
|
+
must_include:
|
|
42
|
+
- Starts the server with `vllm serve <model>` and notes it is OpenAI-compatible (/v1/chat/completions, /v1/models) so an OpenAI client works by repointing base_url.
|
|
43
|
+
- Uses `--tensor-parallel-size 4` because the 70B does not fit one GPU but fits the node, and notes a model that fits one GPU should not be split.
|
|
44
|
+
- Serves the adapter with `--enable-lora` + `--lora-modules name=path`, routing by the request `model` field, and requires `--max-lora-rank` >= the trained rank.
|
|
45
|
+
- Caps `--max-model-len` and sets `--gpu-memory-utilization` (default ~0.92) because KV cache scales with context × concurrency — the top OOM cause.
|
|
46
|
+
- Sets `--api-key` (or VLLM_API_KEY) so the endpoint is not left open, and checks `/health` (no auth) then `/v1/models` to confirm what is served.
|
|
47
|
+
- Names the boundary — renting/provisioning the box is runpod/modal, single-user is ollama — and hedges that vLLM ships ~weekly so versions/defaults should be verified.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# vLLM — flags, endpoints, and the LoRA runtime API
|
|
2
|
+
|
|
3
|
+
Verify flag names/defaults at author time — vLLM ships ~weekly and engine args drift. Authoritative:
|
|
4
|
+
[engine args](https://docs.vllm.ai/en/latest/configuration/engine_args.html),
|
|
5
|
+
[online serving](https://docs.vllm.ai/en/latest/serving/openai_compatible_server/),
|
|
6
|
+
[CLI reference](https://docs.vllm.ai/en/latest/cli/) (all accessed 2026-07).
|
|
7
|
+
|
|
8
|
+
## The `vllm serve` flags you actually reach for
|
|
9
|
+
|
|
10
|
+
| Flag | What it does | Note |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| `<model>` (positional) | HF repo id **or** a local path | first download can be large; pre-stage weights on the box |
|
|
13
|
+
| `--host` / `--port` | bind address / port | default `0.0.0.0:8000` |
|
|
14
|
+
| `--api-key` | require `Authorization: Bearer <key>` | or env `VLLM_API_KEY`; **unset = open endpoint** |
|
|
15
|
+
| `--served-model-name` | override the id clients pass in `model` | otherwise the repo id / path is the id |
|
|
16
|
+
| `--tensor-parallel-size` | shard layers across GPUs on one node | must divide attention-head count |
|
|
17
|
+
| `--pipeline-parallel-size` | split layer stages across nodes | needs a Ray cluster for multi-node |
|
|
18
|
+
| `--quantization` | select quant method (`awq`, `gptq`, `fp8`, `compressed-tensors`, `bitsandbytes`, …) | often auto-detected from the checkpoint |
|
|
19
|
+
| `--dtype` | compute dtype (`auto`, `bfloat16`, `float16`) | `auto` follows the model config |
|
|
20
|
+
| `--max-model-len` | max context (prompt+output) per request | auto-derived from config if unset; **top OOM lever** |
|
|
21
|
+
| `--gpu-memory-utilization` | fraction of each GPU vLLM may use | default ~`0.92`; the rest-of-budget becomes KV pool |
|
|
22
|
+
| `--max-num-seqs` | max concurrent sequences in a batch | throughput vs latency cap |
|
|
23
|
+
| `--max-num-batched-tokens` | token budget per engine step | works with chunked prefill (V1) |
|
|
24
|
+
| `--enable-lora` | turn on LoRA serving | pair with `--lora-modules` |
|
|
25
|
+
| `--lora-modules` | `name=path [name=path …]` adapters to preload | route by `name` in the request `model` |
|
|
26
|
+
| `--max-loras` | adapters resident at once | |
|
|
27
|
+
| `--max-lora-rank` | max adapter rank the server accepts | **must be ≥ the trained rank** |
|
|
28
|
+
| `--task` | `generate` (default) / `embed` / `reward` / … | `embed` enables `/v1/embeddings` |
|
|
29
|
+
| `--chat-template` | override the model's chat template | needed if the checkpoint ships none |
|
|
30
|
+
| `--tensor-parallel-size` + `--enforce-eager` | disable CUDA-graph capture | lower memory / easier debug, slower |
|
|
31
|
+
|
|
32
|
+
`vllm serve --help` prints the full, version-correct list. Do not treat this table as exhaustive.
|
|
33
|
+
|
|
34
|
+
## Endpoint catalog
|
|
35
|
+
|
|
36
|
+
OpenAI-compatible surface (default `http://localhost:8000`):
|
|
37
|
+
|
|
38
|
+
- `POST /v1/chat/completions` — messages array; supports streaming (`"stream": true`), `tools`,
|
|
39
|
+
`response_format` (JSON / guided decoding), `logprobs`.
|
|
40
|
+
- `POST /v1/completions` — single prompt string; legacy completion protocol.
|
|
41
|
+
- `GET /v1/models` — the served model id + any loaded LoRA adapter names.
|
|
42
|
+
- `POST /v1/embeddings` — embedding vectors (server must be started with an embedding `--task`).
|
|
43
|
+
|
|
44
|
+
vLLM-native / operational:
|
|
45
|
+
|
|
46
|
+
- `GET /health` — 200 + empty body when ready. **No auth.** First probe on any endpoint.
|
|
47
|
+
- `GET /metrics` — Prometheus text: queue depth, running/waiting seqs, tokens/s, KV-cache usage,
|
|
48
|
+
prefix-cache hit rate. Scrape this for capacity/throughput monitoring.
|
|
49
|
+
- `GET /version` — server version.
|
|
50
|
+
- `POST /tokenize` · `POST /detokenize` — tokenizer round-trips.
|
|
51
|
+
- `POST /v1/load_lora_adapter` · `POST /v1/unload_lora_adapter` — runtime LoRA (see below).
|
|
52
|
+
|
|
53
|
+
### Example requests
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Chat, streaming, with an API key
|
|
57
|
+
curl -N http://localhost:8000/v1/chat/completions \
|
|
58
|
+
-H "Authorization: Bearer $VLLM_API_KEY" -H 'Content-Type: application/json' \
|
|
59
|
+
-d '{"model":"Qwen/Qwen3-8B",
|
|
60
|
+
"messages":[{"role":"user","content":"Two facts about paging."}],
|
|
61
|
+
"stream":true, "max_tokens":200, "temperature":0.2}'
|
|
62
|
+
|
|
63
|
+
# Guided JSON (structured output) via response_format
|
|
64
|
+
curl http://localhost:8000/v1/chat/completions \
|
|
65
|
+
-H "Authorization: Bearer $VLLM_API_KEY" -H 'Content-Type: application/json' \
|
|
66
|
+
-d '{"model":"Qwen/Qwen3-8B",
|
|
67
|
+
"messages":[{"role":"user","content":"Extract name and age from: Ana is 30."}],
|
|
68
|
+
"response_format":{"type":"json_object"}}'
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Field names track the OpenAI spec; vLLM adds `extra_body` params (e.g. `guided_json`, `guided_regex`,
|
|
72
|
+
`top_k`) documented under [sampling params](https://docs.vllm.ai/en/latest/api/inference_params.html).
|
|
73
|
+
|
|
74
|
+
## Parallelism sizing
|
|
75
|
+
|
|
76
|
+
- **Fits one GPU** → no parallelism flag. Every split costs sync overhead.
|
|
77
|
+
- **Too big for one GPU, fits a node** → `--tensor-parallel-size = GPUs on the node`. Wants fast
|
|
78
|
+
intra-node interconnect (NVLink ≫ PCIe). Head count must be divisible by TP size.
|
|
79
|
+
- **Too big for a node** → add `--pipeline-parallel-size = number of nodes`; keep TP = GPUs/node.
|
|
80
|
+
Example: 2 nodes × 8 GPUs → `--tensor-parallel-size 8 --pipeline-parallel-size 2`. Multi-node needs
|
|
81
|
+
Ray started across the nodes first — that provisioning belongs to `runpod`/`modal`.
|
|
82
|
+
- Rough VRAM: `weights_bytes ≈ params × bytes_per_param` (bf16 = 2, FP8/INT8 ≈ 1, 4-bit ≈ 0.5), split
|
|
83
|
+
across TP GPUs, **plus** the KV pool per GPU. Sizing which model fits which box = `open-weights`.
|
|
84
|
+
|
|
85
|
+
## LoRA runtime API
|
|
86
|
+
|
|
87
|
+
Preload at start with `--lora-modules name=path`, or manage live:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# start with runtime updates allowed
|
|
91
|
+
VLLM_ALLOW_RUNTIME_LORA_UPDATING=True vllm serve <base-model> --enable-lora --max-lora-rank 32
|
|
92
|
+
|
|
93
|
+
# load an adapter without restarting
|
|
94
|
+
curl -X POST http://localhost:8000/v1/load_lora_adapter \
|
|
95
|
+
-H 'Content-Type: application/json' \
|
|
96
|
+
-d '{"lora_name":"sql","lora_path":"/adapters/sql-lora"}'
|
|
97
|
+
|
|
98
|
+
# it now shows in /v1/models and is routable as "model":"sql"; unload to free it
|
|
99
|
+
curl -X POST http://localhost:8000/v1/unload_lora_adapter \
|
|
100
|
+
-H 'Content-Type: application/json' -d '{"lora_name":"sql"}'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Constraints: the adapter must be for the loaded base model (same family/dims); `--max-lora-rank` must
|
|
104
|
+
be ≥ the rank the adapter was trained at; `--max-loras` bounds how many are resident simultaneously.
|
|
105
|
+
A `LoRAResolver` plugin can auto-resolve adapters from a local dir or the HF Hub — see the LoRA docs.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# vLLM — memory, OOM, and throughput tuning
|
|
2
|
+
|
|
3
|
+
Grounded in [engine args](https://docs.vllm.ai/en/latest/configuration/engine_args.html),
|
|
4
|
+
[optimization/conserving memory](https://docs.vllm.ai/en/latest/configuration/optimization.html), and
|
|
5
|
+
the [V1 guide](https://docs.vllm.ai/en/latest/usage/v1_guide.html) (accessed 2026-07). Defaults drift
|
|
6
|
+
between weekly releases — verify.
|
|
7
|
+
|
|
8
|
+
## Where the VRAM goes
|
|
9
|
+
|
|
10
|
+
For each GPU:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
usable = gpu_memory_utilization × total_VRAM # default utilization ≈ 0.92
|
|
14
|
+
KV_cache_pool = usable − weights − activation/CUDA-graph overhead
|
|
15
|
+
max_concurrency ≈ KV_cache_pool ÷ (bytes_per_token × max_model_len)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- **Weights** are fixed once the model (and quant) is chosen; TP splits them across GPUs.
|
|
19
|
+
- **KV cache** is the elastic part — PagedAttention blocks allocated per active token. It grows with
|
|
20
|
+
`max_model_len × concurrent sequences`. This is what runs you out of memory, not the weights.
|
|
21
|
+
- **Overhead** — activations, CUDA graphs, the profiling reservation vLLM makes at startup. `--enforce-eager`
|
|
22
|
+
drops CUDA-graph memory (and some speed); it is a lever when you are just over the line.
|
|
23
|
+
|
|
24
|
+
`bytes_per_token` depends on layers × KV heads × head_dim × 2 (K and V) × dtype bytes. You do not
|
|
25
|
+
usually compute it — you observe `/metrics` (`vllm:gpu_cache_usage_perc`) and adjust.
|
|
26
|
+
|
|
27
|
+
## OOM playbook (in order)
|
|
28
|
+
|
|
29
|
+
1. **Cap `--max-model-len`** to the real max prompt+output. Going from 128K to, say, 8K multiplies how
|
|
30
|
+
many sequences fit. This is the highest-leverage fix.
|
|
31
|
+
2. **Lower `--gpu-memory-utilization`** if OOM happens *at load* or on bursty spikes (e.g. 0.92 → 0.85).
|
|
32
|
+
Counterintuitively, a lower number can stop crashes by leaving headroom for activation spikes.
|
|
33
|
+
3. **Lower `--max-num-seqs`** to bound concurrent sequences (and thus peak KV usage).
|
|
34
|
+
4. **Quantize the weights** (AWQ/GPTQ/FP8/compressed-tensors) to free VRAM for the KV pool — a memory
|
|
35
|
+
win, not necessarily a speed win.
|
|
36
|
+
5. **Add GPUs via `--tensor-parallel-size`** so weights + KV split across devices.
|
|
37
|
+
6. **`--enforce-eager`** or **`--kv-cache-dtype fp8`** to trim graph/cache memory when marginally over.
|
|
38
|
+
|
|
39
|
+
If none fit, the box is too small — that is a `runpod`/`modal` sizing decision, not a vLLM flag.
|
|
40
|
+
|
|
41
|
+
## Quantization tradeoffs by regime
|
|
42
|
+
|
|
43
|
+
| Method | Weights | Typical use | Watch out |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
| FP8 (W8A8) | 8-bit | near-lossless, good on Ada/Hopper+ | needs recent NVIDIA/AMD arch; older GPUs unsupported |
|
|
46
|
+
| INT8 / W8A8 | 8-bit | broad support, small quality hit | activation quant sensitive to calibration |
|
|
47
|
+
| AWQ / GPTQ (4-bit) | ~4-bit | max VRAM savings | quality cost real; dequant can add latency |
|
|
48
|
+
| compressed-tensors | varies | LLM Compressor output, mixed schemes | method-specific kernel/arch support |
|
|
49
|
+
| bitsandbytes | 4/8-bit | convenient, load-time quant | generally slower than dedicated kernels |
|
|
50
|
+
|
|
51
|
+
Regime that decides whether a quant *helps speed*:
|
|
52
|
+
|
|
53
|
+
- **Low batch / low concurrency** — memory-bandwidth-bound; a quant that shrinks weights can raise
|
|
54
|
+
tokens/s. But dequant overhead can eat the win — measure.
|
|
55
|
+
- **High batch / high concurrency** — often compute-bound; a *weight-only* quant does little for
|
|
56
|
+
throughput there (its win is the freed VRAM → bigger KV pool → more concurrency).
|
|
57
|
+
|
|
58
|
+
So: quantize primarily to **fit** and to **grow the KV pool**, and benchmark before claiming it is
|
|
59
|
+
faster. Choosing *which* quantized checkpoint to pull is `open-weights`.
|
|
60
|
+
|
|
61
|
+
## Throughput knobs (once it fits)
|
|
62
|
+
|
|
63
|
+
- **Prefix caching** (on by default in V1) reuses the KV of shared prompt prefixes — big win for repeated
|
|
64
|
+
system prompts / few-shot / agent loops. Watch `vllm:prefix_cache_hit_rate` in `/metrics`.
|
|
65
|
+
- **Chunked prefill** (V1 default) interleaves prefill and decode so long prompts do not stall decoding.
|
|
66
|
+
`--max-num-batched-tokens` sets the per-step budget.
|
|
67
|
+
- **`--max-num-seqs`** raises the concurrency ceiling if you have KV headroom; lowering it cuts latency.
|
|
68
|
+
- **Speculative decoding** (`--speculative-config …`) can raise tokens/s for a request when a draft model
|
|
69
|
+
or n-gram method fits — verify current syntax in the docs; the flag shape changes.
|
|
70
|
+
|
|
71
|
+
## Measuring, not guessing
|
|
72
|
+
|
|
73
|
+
- Scrape `/metrics` for `gpu_cache_usage_perc`, `num_requests_running`/`_waiting`, `prompt`/`generation`
|
|
74
|
+
throughput, and prefix-cache hit rate.
|
|
75
|
+
- Load-test with the bundled benchmark (`vllm bench serve` / `benchmarks/benchmark_serving.py` in the
|
|
76
|
+
repo — name has moved across versions; check `vllm bench --help`) at your real concurrency and prompt
|
|
77
|
+
mix. Tune `--max-model-len`, `--gpu-memory-utilization`, `--max-num-seqs` against those numbers, not a
|
|
78
|
+
single-request feel.
|