@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.
Files changed (56) hide show
  1. package/README.md +8 -2
  2. package/manifest.json +181 -3
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +1 -0
  5. package/scripts/lib/recommend.js +6 -0
  6. package/skills/deep-learning/SKILL.md +263 -0
  7. package/skills/deep-learning/evals/README.md +11 -0
  8. package/skills/deep-learning/evals/cases.yaml +48 -0
  9. package/skills/deep-learning/references/distributed.md +105 -0
  10. package/skills/deep-learning/references/training-recipes.md +135 -0
  11. package/skills/finetuning/SKILL.md +244 -0
  12. package/skills/finetuning/evals/README.md +13 -0
  13. package/skills/finetuning/evals/cases.yaml +50 -0
  14. package/skills/finetuning/references/hyperparameters-and-eval.md +74 -0
  15. package/skills/finetuning/references/methods.md +189 -0
  16. package/skills/harness/references/providers.yaml +35 -0
  17. package/skills/huggingface/SKILL.md +1 -1
  18. package/skills/machine-learning/SKILL.md +260 -0
  19. package/skills/machine-learning/evals/README.md +15 -0
  20. package/skills/machine-learning/evals/cases.yaml +54 -0
  21. package/skills/machine-learning/references/gbdt-and-tuning.md +83 -0
  22. package/skills/machine-learning/references/metrics-and-imbalance.md +82 -0
  23. package/skills/machine-learning/references/pipelines-and-cv.md +135 -0
  24. package/skills/nlp/SKILL.md +225 -0
  25. package/skills/nlp/evals/README.md +10 -0
  26. package/skills/nlp/evals/cases.yaml +45 -0
  27. package/skills/nlp/references/evaluation.md +107 -0
  28. package/skills/nlp/references/tokenization.md +95 -0
  29. package/skills/open-weights/SKILL.md +239 -0
  30. package/skills/open-weights/evals/README.md +14 -0
  31. package/skills/open-weights/evals/cases.yaml +42 -0
  32. package/skills/open-weights/references/licenses.md +123 -0
  33. package/skills/open-weights/references/model-families.md +83 -0
  34. package/skills/open-weights/references/sizing-and-quant.md +89 -0
  35. package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
  36. package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
  37. package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
  38. package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
  39. package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
  40. package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
  41. package/skills/training-data/SKILL.md +226 -0
  42. package/skills/training-data/evals/README.md +13 -0
  43. package/skills/training-data/evals/cases.yaml +41 -0
  44. package/skills/training-data/references/formats.md +118 -0
  45. package/skills/training-data/references/synthesis-dedup-quality.md +134 -0
  46. package/skills/unsloth/SKILL.md +254 -0
  47. package/skills/unsloth/evals/README.md +10 -0
  48. package/skills/unsloth/evals/cases.yaml +42 -0
  49. package/skills/unsloth/references/export.md +75 -0
  50. package/skills/unsloth/references/grpo.md +91 -0
  51. package/skills/unsloth/references/masking-and-templates.md +91 -0
  52. package/skills/vllm/SKILL.md +235 -0
  53. package/skills/vllm/evals/README.md +9 -0
  54. package/skills/vllm/evals/cases.yaml +47 -0
  55. package/skills/vllm/references/flags-and-endpoints.md +105 -0
  56. 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.