Skip to content

Commit 06fbe48

Browse files
authored
feat(llama.cpp): wire speculative decoding settings (#9238)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 232e324 commit 06fbe48

2 files changed

Lines changed: 130 additions & 2 deletions

File tree

backend/cpp/llama-cpp/grpc-server.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ json parse_options(bool streaming, const backend::PredictOptions* predict, const
284284
data["ignore_eos"] = predict->ignoreeos();
285285
data["embeddings"] = predict->embeddings();
286286

287+
// Speculative decoding per-request overrides
288+
// NDraft maps to speculative.n_max (maximum draft tokens per speculation step)
289+
if (predict->ndraft() > 0) {
290+
data["speculative.n_max"] = predict->ndraft();
291+
}
292+
287293
// Add the correlationid to json data
288294
data["correlation_id"] = predict->correlationid();
289295

@@ -402,6 +408,16 @@ static void params_parse(server_context& /*ctx_server*/, const backend::ModelOpt
402408
if (!request->mmproj().empty()) {
403409
params.mmproj.path = request->mmproj();
404410
}
411+
412+
// Draft model for speculative decoding
413+
if (!request->draftmodel().empty()) {
414+
params.speculative.mparams_dft.path = request->draftmodel();
415+
// Default to draft type if a draft model is set but no explicit type
416+
if (params.speculative.type == COMMON_SPECULATIVE_TYPE_NONE) {
417+
params.speculative.type = COMMON_SPECULATIVE_TYPE_DRAFT;
418+
}
419+
}
420+
405421
// params.model_alias ??
406422
params.model_alias.insert(request->modelfile());
407423
if (!request->cachetypekey().empty()) {
@@ -609,6 +625,48 @@ static void params_parse(server_context& /*ctx_server*/, const backend::ModelOpt
609625
// If conversion fails, keep default value (8)
610626
}
611627
}
628+
// Speculative decoding options
629+
} else if (!strcmp(optname, "spec_type") || !strcmp(optname, "speculative_type")) {
630+
auto type = common_speculative_type_from_name(optval_str);
631+
if (type != COMMON_SPECULATIVE_TYPE_COUNT) {
632+
params.speculative.type = type;
633+
}
634+
} else if (!strcmp(optname, "spec_n_max") || !strcmp(optname, "draft_max")) {
635+
if (optval != NULL) {
636+
try { params.speculative.n_max = std::stoi(optval_str); } catch (...) {}
637+
}
638+
} else if (!strcmp(optname, "spec_n_min") || !strcmp(optname, "draft_min")) {
639+
if (optval != NULL) {
640+
try { params.speculative.n_min = std::stoi(optval_str); } catch (...) {}
641+
}
642+
} else if (!strcmp(optname, "spec_p_min") || !strcmp(optname, "draft_p_min")) {
643+
if (optval != NULL) {
644+
try { params.speculative.p_min = std::stof(optval_str); } catch (...) {}
645+
}
646+
} else if (!strcmp(optname, "spec_p_split")) {
647+
if (optval != NULL) {
648+
try { params.speculative.p_split = std::stof(optval_str); } catch (...) {}
649+
}
650+
} else if (!strcmp(optname, "spec_ngram_size_n") || !strcmp(optname, "ngram_size_n")) {
651+
if (optval != NULL) {
652+
try { params.speculative.ngram_size_n = (uint16_t)std::stoi(optval_str); } catch (...) {}
653+
}
654+
} else if (!strcmp(optname, "spec_ngram_size_m") || !strcmp(optname, "ngram_size_m")) {
655+
if (optval != NULL) {
656+
try { params.speculative.ngram_size_m = (uint16_t)std::stoi(optval_str); } catch (...) {}
657+
}
658+
} else if (!strcmp(optname, "spec_ngram_min_hits") || !strcmp(optname, "ngram_min_hits")) {
659+
if (optval != NULL) {
660+
try { params.speculative.ngram_min_hits = (uint16_t)std::stoi(optval_str); } catch (...) {}
661+
}
662+
} else if (!strcmp(optname, "draft_gpu_layers")) {
663+
if (optval != NULL) {
664+
try { params.speculative.n_gpu_layers = std::stoi(optval_str); } catch (...) {}
665+
}
666+
} else if (!strcmp(optname, "draft_ctx_size")) {
667+
if (optval != NULL) {
668+
try { params.speculative.n_ctx = std::stoi(optval_str); } catch (...) {}
669+
}
612670
}
613671
}
614672

docs/content/advanced/model-configuration.md

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ These settings apply to most LLM backends (llama.cpp, vLLM, etc.):
189189
| Field | Type | Description |
190190
|-------|------|-------------|
191191
| `no_mulmatq` | bool | Disable matrix multiplication queuing |
192-
| `draft_model` | string | Draft model for speculative decoding |
193-
| `n_draft` | int32 | Number of draft tokens |
192+
| `draft_model` | string | Draft model GGUF file for speculative decoding (see [Speculative Decoding](#speculative-decoding)) |
193+
| `n_draft` | int32 | Maximum number of draft tokens per speculative step (default: 16) |
194194
| `quantization` | string | Quantization format |
195195
| `load_format` | string | Model load format |
196196
| `numa` | bool | Enable NUMA (Non-Uniform Memory Access) |
@@ -211,6 +211,76 @@ YARN (Yet Another RoPE extensioN) settings for context extension:
211211
| `yarn_beta_fast` | float32 | YARN beta fast parameter |
212212
| `yarn_beta_slow` | float32 | YARN beta slow parameter |
213213

214+
### Speculative Decoding
215+
216+
Speculative decoding speeds up text generation by predicting multiple tokens ahead and verifying them in a single forward pass. The output is identical to normal decoding — only faster. This feature is only available with the `llama-cpp` backend.
217+
218+
There are two approaches:
219+
220+
#### Draft Model Speculative Decoding
221+
222+
Uses a smaller, faster model from the same model family to draft candidate tokens, which the main model then verifies. Requires a separate GGUF file for the draft model.
223+
224+
```yaml
225+
name: my-model
226+
backend: llama-cpp
227+
parameters:
228+
model: large-model.gguf
229+
draft_model: small-draft-model.gguf
230+
n_draft: 8
231+
options:
232+
- spec_p_min:0.8
233+
- draft_gpu_layers:99
234+
```
235+
236+
#### N-gram Self-Speculative Decoding
237+
238+
Uses patterns from the token history to predict future tokens — no extra model required. Works well for repetitive or structured output (code, JSON, lists).
239+
240+
```yaml
241+
name: my-model
242+
backend: llama-cpp
243+
parameters:
244+
model: my-model.gguf
245+
options:
246+
- spec_type:ngram_simple
247+
- spec_n_max:16
248+
```
249+
250+
#### Speculative Decoding Options
251+
252+
These are set via the `options:` array in the model configuration (format: `key:value`):
253+
254+
| Option | Type | Default | Description |
255+
|--------|------|---------|-------------|
256+
| `spec_type` | string | `none` | Speculative decoding type (see table below) |
257+
| `spec_n_max` / `draft_max` | int | 16 | Maximum number of tokens to draft per step |
258+
| `spec_n_min` / `draft_min` | int | 0 | Minimum draft tokens required to use speculation |
259+
| `spec_p_min` / `draft_p_min` | float | 0.75 | Minimum probability threshold for greedy acceptance |
260+
| `spec_p_split` | float | 0.1 | Split probability for tree-based branching |
261+
| `spec_ngram_size_n` / `ngram_size_n` | int | 12 | N-gram lookup size |
262+
| `spec_ngram_size_m` / `ngram_size_m` | int | 48 | M-gram proposal size |
263+
| `spec_ngram_min_hits` / `ngram_min_hits` | int | 1 | Minimum hits for accepting n-gram proposals |
264+
| `draft_gpu_layers` | int | -1 | GPU layers for the draft model (-1 = use default) |
265+
| `draft_ctx_size` | int | 0 | Context size for the draft model (0 = auto) |
266+
267+
#### Speculative Type Values
268+
269+
| Type | Description |
270+
|------|-------------|
271+
| `none` | No speculative decoding (default) |
272+
| `draft` | Draft model-based speculation (auto-set when `draft_model` is configured) |
273+
| `eagle3` | EAGLE3 draft model architecture |
274+
| `ngram_simple` | Simple self-speculative using token history |
275+
| `ngram_map_k` | N-gram with key-only map |
276+
| `ngram_map_k4v` | N-gram with keys and 4 m-gram values |
277+
| `ngram_mod` | Modified n-gram speculation |
278+
| `ngram_cache` | 3-level n-gram cache |
279+
280+
{{% notice note %}}
281+
Speculative decoding is automatically disabled when multimodal models (with `mmproj`) are active. The `n_draft` parameter can also be overridden per-request.
282+
{{% /notice %}}
283+
214284
### Prompt Caching
215285

216286
| Field | Type | Description |

0 commit comments

Comments
 (0)