mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-25 07:34:58 -04:00
* feat(vllm-cpp): add GLiNER2.5 NER via TokenClassify Wire the vllm-cpp backend to the C ABI NER surface (vllm_gliner_ner, ABI v27) so LocalAI can serve zero-shot named entity recognition through the existing TokenClassify gRPC method. backend.go: TokenClassify method on *VllmCpp calls vllm_gliner_ner with the text and labels, copies the C-owned entity array into protobuf TokenClassifyEntity messages, and frees the result. govllmcpp.go: cNerEntity and cNerResult Go POD mirrors matching the C structs; vllmGlinerNer and vllmNerResultFree purego bindings; abiVersion bumped 26 -> 27. options.go: ner_labels, ner_threshold, ner_max_width parsed from engine_args. pkg/grpc: ClassifyModel interface and TokenClassify server handler (follows the Embedding locking pattern). core/config: vllm-cpp backend declares MethodTokenClassify and UsecaseTokenClassify. docs/content/features/vllm-cpp.md: NER section documenting the engine_args keys and the host-forward contract. Assisted-by: MAKI:regolo/glm5.2 [maki] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(vllm-cpp): correct NER pointer lint directive Use the govet directive for the C-owned NER array, matching the other purego pointer conversions. The array remains valid until its deferred free; the misspelled directive caused CI to flag this conversion. Assisted-by: Codex:gpt-6 golangci-lint Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * feat(vllm-cpp): add kev-compatible SystemOne API endpoints Add POST /v1/systemone, /v1/systemone/permute, and /v1/systemone/separate to LocalAI, mirroring the kev project's structured-extraction API. Each endpoint runs zero-shot NER over the rendered state text and builds kev-compatible answers for three question types: noul (binary entity presence), choice (pick one option), and score (pick one level). The TokenClassifyRequest proto gains a `repeated string labels` field so each question can supply its own labels at inference time, and TokenClassifier gains TokenClassifyWithLabels for per-call label selection. The vllm-cpp backend uses request labels when non-empty, falling back to configured ner_labels then the built-in defaults. Helpers (renderState, softmax, choiceConfidence, scoreConfidence, r2) are ported from kev/api.py and mirrored in vllm.cpp's api_server.cpp so both servers produce the same answer shape. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:regolo/glm5.2 [maki] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(vllm-cpp): suppress gosec G404 on seeded permutation RNG The SystemOne permute endpoint uses math/rand with a caller-supplied seed for reproducible option permutations, matching kev's random.seed. gosec flags this as G404 (weak RNG). Add #nosec with a comment naming the intent: this is reproducibility, not cryptography. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:regolo/glm5.2 [maki] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * chore(vllm-cpp): bump vllm.cpp pin to GLiNER2.5 merge commit Advance VLLM_CPP_VERSION from f3cd97e to 5058268d, the commit that landed GLiNER2.5 zero-shot NER support (PR #3224) in vllm.cpp. This brings the DeBERTa v2 encoder, GLiNER2 boundary head, C ABI NER functions, and server endpoints into the LocalAI vllm-cpp backend. The ABI version (27) and Go struct mirrors already match. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:regolo/glm5.2 [maki] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(vllm-cpp): use instruction text as NER label in SystemOne handler The SystemOne handler was passing question IDs as NER labels for noul questions and bare key names for choice questions, so the model never matched any entities. Port the label mapping from vllm.cpp's ParseSystemOneBody: - noul: use the rendered instructions field (with instr alias) as the NER label, not the question ID - choice: use optionText(name, desc) — "name: description" or "name" when the description is null/empty — not the bare key - score: already correct (rendered criteria text) - permute: shuffle indices and build parallel key/label arrays so the NER call uses the optionText labels while the response is keyed by the original option names Also add the instructions field to the SystemOneQuestion schema struct (accepted alongside the instr backward-compat alias). Verified end-to-end against the real GLiNER2.5 model: noul questions now find "Apple Inc. is" (organization, 0.999) and "Tim Cook is" (person, 0.852) where they previously returned zero entities. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:regolo/glm5.2 [maki] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: localai-org-maint-bot <306269227+localai-org-maint-bot@users.noreply.github.com>
184 lines
8.0 KiB
Markdown
184 lines
8.0 KiB
Markdown
+++
|
|
disableToc = false
|
|
title = "vllm.cpp backend"
|
|
weight = 16
|
|
url = "/features/vllm-cpp/"
|
|
+++
|
|
|
|
[vllm.cpp](https://github.com/mudler/vllm.cpp) is the LocalAI team's C++20 port of
|
|
vLLM: the same continuous-batching scheduler, paged KV cache and automatic prefix
|
|
caching, with no Python at inference time. LocalAI serves it through the native
|
|
`vllm-cpp` backend, which loads either a HuggingFace safetensors directory or a
|
|
`.gguf` file and applies the chat template, tool-call parsing and reasoning split
|
|
inside the engine.
|
|
|
|
This page covers installing the backend and the models LocalAI ships ready to run
|
|
on it. For the full `engine_args` reference (KV sizing, scheduling policy,
|
|
speculative decoding, LMCache), see the
|
|
[vllm.cpp section of the text generation guide]({{% relref "features/text-generation" %}}#vllmcpp).
|
|
|
|
## Installing
|
|
|
|
```bash
|
|
local-ai backends install vllm-cpp
|
|
```
|
|
|
|
Or install it from the **Backends** page in the web UI. Images are published for
|
|
CPU, CUDA 13, Vulkan, Metal and Jetson L4T.
|
|
|
|
### Which GPUs the CUDA images cover
|
|
|
|
The CUDA images are currently built for **Blackwell-family architectures only**:
|
|
`sm_120a` (RTX 50 series, RTX PRO 6000 Blackwell) and `sm_121a` (GB10 / DGX
|
|
Spark) on x86-64, and `sm_121a` alone on arm64. CUDA 13 is required, so there is
|
|
no CUDA 12 variant.
|
|
|
|
That is narrower than vllm.cpp itself, which builds ten architectures. On an
|
|
Ampere, Ada, Hopper, Jetson Orin or Jetson Thor GPU the CUDA backend installs
|
|
successfully and then fails at the first request with `no kernel image is
|
|
available for execution on the device`. Until the build widens, use the Vulkan
|
|
or CPU image on those cards: `vulkan-vllm-cpp` builds with CUDA off entirely and
|
|
gives them a GPU path, without the NVFP4 and Marlin kernels.
|
|
|
|
## Ready-made models
|
|
|
|
The model gallery carries a curated set of vllm.cpp configurations. Each one
|
|
arrives with the engine settings already applied, so tool calling, the reasoning
|
|
split and speculative decoding work without hand-editing YAML.
|
|
|
|
| Gallery entry | Model | Size | Needs |
|
|
|---|---|---|---|
|
|
| `qwen3.6-27b-nvfp4-vllm-cpp` | Qwen3.6-27B, NVFP4 | 25 GB | Blackwell GPU |
|
|
| `qwen3.6-27b-nvfp4-mtp-vllm-cpp` | the same, with MTP speculative decoding | 25 GB | Blackwell GPU |
|
|
| `qwen3.6-27b-nvfp4-dflash-vllm-cpp` | the same, with DFlash speculative decoding | 28 GB | Blackwell GPU |
|
|
| `qwen3.6-35b-a3b-nvfp4-vllm-cpp` | Qwen3.6-35B-A3B MoE, NVFP4 | 23 GB | Blackwell GPU |
|
|
| `qwen3.6-35b-a3b-nvfp4-mtp-vllm-cpp` | the same, with MTP speculative decoding | 23 GB | Blackwell GPU |
|
|
| `qwen3-coder-30b-a3b-vllm-cpp` | Qwen3-Coder-30B-A3B, bf16 | 57 GB | Blackwell GPU, or CPU |
|
|
| `qwen3-4b-vllm-cpp` | Qwen3-4B, bf16 | 8 GB | CPU, Metal, Vulkan, Blackwell GPU |
|
|
| `qwen3-0.6b-vllm-cpp` | Qwen3-0.6B, bf16 | 1.4 GB | CPU, Metal, Vulkan, Blackwell GPU |
|
|
|
|
```bash
|
|
local-ai models install qwen3-0.6b-vllm-cpp
|
|
```
|
|
|
|
The two small bf16 entries are the ones that run anywhere the backend does,
|
|
including CPU. The NVFP4 entries need a Blackwell-class NVIDIA GPU on two counts:
|
|
NVFP4 has no kernel on older architectures, and the CUDA images are built only
|
|
for Blackwell in any case.
|
|
|
|
Sizing note: every entry sets `num_blocks` to give roughly one to four full
|
|
contexts of KV cache, which is a starting point rather than a tuned value. KV is
|
|
not free; the 4B entry, for instance, spends 144 KiB per token, so its 1024
|
|
blocks are about 4.5 GB on top of the weights. Raise `num_blocks` for more
|
|
concurrency, lower it on a small box.
|
|
|
|
### Why the 27B entries pin a revision
|
|
|
|
The Qwen3.6-27B entries pin their weights to a specific HuggingFace commit rather
|
|
than tracking the repository's default branch. This is deliberate and worth
|
|
understanding before you copy one of these configs.
|
|
|
|
The upstream repository was later re-quantized in place, under the same name, from
|
|
NVFP4 to FP8 W8A8. A config that names the repository without a revision therefore
|
|
resolves to entirely different weights, with different numerics and different
|
|
performance, and nothing about the load reports that anything changed. Pinning is
|
|
what makes the entry reproducible:
|
|
|
|
```yaml
|
|
artifacts:
|
|
- name: model
|
|
target: model
|
|
source:
|
|
type: huggingface
|
|
repo: unsloth/Qwen3.6-27B-NVFP4
|
|
revision: 890bdef7a42feba6d83b6e17a03315c694112f2a
|
|
```
|
|
|
|
The same reasoning applies to any quantized community repository you depend on.
|
|
|
|
### Choosing between the speculative variants
|
|
|
|
Speculative decoding trades memory for decode throughput. All three Qwen3.6-27B
|
|
entries serve the same weights and produce the same quality; they differ only in
|
|
how tokens are proposed.
|
|
|
|
| Entry | Method | Extra weights | Extra memory |
|
|
|---|---|---|---|
|
|
| `qwen3.6-27b-nvfp4-vllm-cpp` | none | none | none |
|
|
| `qwen3.6-27b-nvfp4-mtp-vllm-cpp` | MTP, depth 1 | none, the draft head ships inside the checkpoint | about 3.6 GB |
|
|
| `qwen3.6-27b-nvfp4-dflash-vllm-cpp` | DFlash, 16-token blocks | a separate 3.5 GB drafter | drafter plus draft cache |
|
|
|
|
MTP drafts one token per step from a head that already lives in the target
|
|
checkpoint's own `mtp.*` tensors, so it costs no extra download. DFlash drafts a
|
|
whole 16-token block in one non-autoregressive pass from a separate drafter, which
|
|
is the larger win at the cost of a second checkpoint on disk.
|
|
|
|
Start with the plain entry if you are short on memory, and with the DFlash entry
|
|
if you are not.
|
|
|
|
### Tool calling
|
|
|
|
Every entry above sets `use_tokenizer_template: true` and disables LocalAI's
|
|
Go-side grammar path, so tool calls are detected and parsed by the engine's own
|
|
streaming parsers and arrive as real `tool_calls` on the OpenAI response.
|
|
|
|
The parser is normally auto-detected from the chat template, but one case cannot
|
|
be: Qwen3-Coder's tool dialect is byte-identical on the wire to another family's,
|
|
so template sniffing would pick the wrong parser. The
|
|
`qwen3-coder-30b-a3b-vllm-cpp` entry therefore names it explicitly, and any
|
|
Qwen3-Coder config you write yourself should do the same:
|
|
|
|
```yaml
|
|
engine_args:
|
|
tool_parser: qwen3_coder
|
|
```
|
|
|
|
## Named entity recognition (GLiNER2.5)
|
|
|
|
The `vllm-cpp` backend serves [GLiNER2.5](https://huggingface.co/fastino/gliner2.5-multi-v1),
|
|
a zero-shot NER and structured-extraction model. Point the backend at the
|
|
safetensors directory and the backend exposes the `TokenClassify` gRPC method,
|
|
which LocalAI maps to its standard NER API surface.
|
|
|
|
Labels are supplied at inference time, not baked into the model config. Set
|
|
them in `engine_args`:
|
|
|
|
```yaml
|
|
engine_args:
|
|
ner_labels: "person,organization,location,date,time,money,quantity"
|
|
ner_threshold: 0.5
|
|
ner_max_width: 12
|
|
```
|
|
|
|
`ner_labels` is a comma-separated list. When omitted, the backend falls back to
|
|
a built-in default set (`person`, `organization`, `location`, `date`, `time`,
|
|
`money`, `quantity`). `ner_threshold` is the sigmoid cutoff (default 0.5);
|
|
`ner_max_width` is the maximum span length in tokens (default 12).
|
|
|
|
The model runs the DeBERTa v2 encoder with disentangled attention on the host
|
|
forward, which is the required contract for pooling models in vllm.cpp. A
|
|
device-resident forward is tracked as a performance optimization, not a
|
|
correctness gap.
|
|
|
|
### SystemOne structured-extraction API
|
|
|
|
The `vllm-cpp` backend also exposes kev-compatible SystemOne endpoints that
|
|
turn zero-shot NER into structured question answering. These mirror the API
|
|
from the [kev](https://github.com/jaredpalmer/kev) project:
|
|
|
|
| Endpoint | Method | Description |
|
|
|---|---|---|
|
|
| `/v1/systemone` | POST | Answer all questions in one NER pass |
|
|
| `/v1/systemone/permute` | POST | Re-run one choice question under n_perm option orders |
|
|
| `/v1/systemone/separate` | POST | Answer each question in its own NER pass (N passes) |
|
|
|
|
Each question has a `type` of `noul` (binary entity presence), `choice` (pick
|
|
one option), or `score` (pick one level). The `model` field in the request body
|
|
selects the NER model. Labels are derived from the question definition, so no
|
|
`ner_labels` configuration is needed for these endpoints.
|
|
|
|
## Beyond text generation
|
|
|
|
The `vllm-cpp` backend also serves MiniMax-H3, which generates video and audio
|
|
jointly. See [Video generation]({{% relref "features/video-generation" %}}#minimax-h3-vllmcpp).
|