diff --git a/common/arg.cpp b/common/arg.cpp index 2669cacd6c..3080e394c9 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -355,7 +355,7 @@ static std::string get_default_local_path(const std::string & url) { } static bool spec_types_is_default(const common_params & params) { - return params.speculative.types == std::vector{COMMON_SPECULATIVE_TYPE_NONE}; + return !params.speculative.spec_type_specified; } common_models_handler common_models_handler_init(const common_params & params, llama_example curr_ex) { @@ -1401,8 +1401,11 @@ common_params_context common_params_parser_init(common_params & params, llama_ex } else if (ex == LLAMA_EXAMPLE_MTMD) { params.use_jinja = false; // disable jinja by default params.sampling.temp = 0.2; // lower temp by default for better quality - } else if (ex == LLAMA_EXAMPLE_SERVER) { - params.n_parallel = -1; // auto by default + } else if (ex == LLAMA_EXAMPLE_SERVER || ex == LLAMA_EXAMPLE_CLI) { + params.speculative = common_speculative_default_config(); + if (ex == LLAMA_EXAMPLE_SERVER) { + params.n_parallel = -1; // auto by default + } } else if (ex == LLAMA_EXAMPLE_TOKENIZE) { params.parse_special = true; // parse special tokens by default, like the old tokenize tool } else if (ex == LLAMA_EXAMPLE_TTS) { @@ -4262,9 +4265,48 @@ common_params_context common_params_parser_init(common_params & params, llama_ex [](common_params & params, const std::string & value) { const auto types_str = string_split(value, ','); auto types = common_speculative_types_from_names(types_str); - params.speculative.types.insert(params.speculative.types.end(), types.begin(), types.end()); + + params.speculative.spec_type_specified = true; + + if (types.size() == 1 && types.front() == COMMON_SPECULATIVE_TYPE_NONE) { + params.speculative.types = { COMMON_SPECULATIVE_TYPE_NONE }; + return; + } + + params.speculative.types.erase( + std::remove(params.speculative.types.begin(), params.speculative.types.end(), COMMON_SPECULATIVE_TYPE_NONE), + params.speculative.types.end()); + + for (const auto & type : types) { + if (std::find(params.speculative.types.begin(), params.speculative.types.end(), type) == params.speculative.types.end()) { + params.speculative.types.push_back(type); + } + } } ).set_spec().set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_SPEC_TYPE")); + add_opt(common_arg( + {"--no-spec-type"}, common_speculative_all_types_str(), + string_format("comma-separated list of types of speculative decoding to remove\n"), + [](common_params & params, const std::string & value) { + const auto types_str = string_split(value, ','); + auto types = common_speculative_types_from_names(types_str); + + if (types.size() == 1 && types.front() == COMMON_SPECULATIVE_TYPE_NONE) { + params.speculative.spec_type_specified = true; + params.speculative.types = { COMMON_SPECULATIVE_TYPE_NONE }; + return; + } + + for (const auto & type : types) { + params.speculative.types.erase( + std::remove(params.speculative.types.begin(), params.speculative.types.end(), type), + params.speculative.types.end()); + } + if (params.speculative.types.empty()) { + params.speculative.types = { COMMON_SPECULATIVE_TYPE_NONE }; + } + } + ).set_spec().set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_NO_SPEC_TYPE")); add_opt(common_arg( {"--spec-ngram-mod-n-min"}, "N", string_format("minimum number of ngram tokens to use for ngram-based speculative decoding (default: %d)", params.speculative.ngram_mod.n_min), @@ -4731,20 +4773,11 @@ common_params_context common_params_parser_init(common_params & params, llama_ex add_opt(common_arg( {"--spec-default"}, - string_format("enable default speculative decoding config"), - [](common_params & params) { - params.speculative.types.push_back(COMMON_SPECULATIVE_TYPE_NGRAM_MOD); - params.speculative.ngram_mod.n_match = 24; - params.speculative.ngram_mod.n_min = 48; - params.speculative.ngram_mod.n_max = 64; - - // TODO: not sure if this is a good config - explore more settings and potentially enable it - //params.speculative.types.push_back(COMMON_SPECULATIVE_TYPE_NGRAM_MAP_K4V); - //params.speculative.ngram_map_k4v.size_n = 8; - //params.speculative.ngram_map_k4v.size_m = 24; - //params.speculative.ngram_map_k4v.min_hits = 2; + string_format("DEPRECATED: default speculative decoding is enabled by default, use --no-spec-type to remove types"), + [](common_params &) { + LOG_WRN("DEPRECATED: --spec-default is no longer needed; default speculative decoding is enabled by default\n"); } - ).set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI})); + ).set_spec().set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI})); return ctx_arg; } diff --git a/common/common.h b/common/common.h index 63d0badd0f..946c0d3180 100644 --- a/common/common.h +++ b/common/common.h @@ -369,6 +369,7 @@ struct common_params_speculative_ngram_cache { struct common_params_speculative { std::vector types = { COMMON_SPECULATIVE_TYPE_NONE }; + bool spec_type_specified = false; // set when --spec-type is used double synth_len = -1.0; std::vector synth_rates; diff --git a/common/speculative.cpp b/common/speculative.cpp index 851a47b9a5..41b886920e 100644 --- a/common/speculative.cpp +++ b/common/speculative.cpp @@ -44,6 +44,15 @@ const std::map common_speculative_type_fro {"ngram-cache", COMMON_SPECULATIVE_TYPE_NGRAM_CACHE} }; +common_params_speculative common_speculative_default_config() { + common_params_speculative result; + result.types = { COMMON_SPECULATIVE_TYPE_NGRAM_MOD }; + result.ngram_mod.n_match = 24; + result.ngram_mod.n_min = 48; + result.ngram_mod.n_max = 64; + return result; +} + static std::string common_speculative_get_devices_str(const std::vector & devices) { std::string result; for (size_t i = 0; i < devices.size(); i++) { diff --git a/common/speculative.h b/common/speculative.h index 22505891f7..90c31b2c9e 100644 --- a/common/speculative.h +++ b/common/speculative.h @@ -5,6 +5,9 @@ struct common_speculative; +// return the default speculative decoding configuration +common_params_speculative common_speculative_default_config(); + // comma separated list the provided types std::string common_speculative_type_name_str(const std::vector & types); diff --git a/docs/preset.md b/docs/preset.md index 3d85467e87..0540a01834 100644 --- a/docs/preset.md +++ b/docs/preset.md @@ -26,7 +26,7 @@ ctx-size = 0 mmap = 1 kv-unified = 1 parallel = 4 -spec-default = 1 +no-spec-type = ngram-mod [Qwen3.5-4B] hf = unsloth/Qwen3.5-4B-GGUF:Q4_K_M @@ -52,6 +52,9 @@ chat-template-kwargs = {"reasoning_effort": "high"} The preset will be loaded similarly to the `--models-preset` option. Therefore, you can also override certain params via CLI arguments: +> [!NOTE] +> A default speculative decoding is enabled by default for the server and CLI. + ```sh # Force temp = 0.1, overriding the preset value llama-cli -hf username/my-preset --temp 0.1 diff --git a/docs/speculative.md b/docs/speculative.md index ffb1e34c7f..d961156bee 100644 --- a/docs/speculative.md +++ b/docs/speculative.md @@ -226,10 +226,12 @@ Use exactly one of these options: ``` --spec-type [none|draft-simple|draft-eagle3|draft-dflash|draft-dspark|draft-mtp|ngram-cache|ngram-simple|ngram-map-k|ngram-map-k4v|ngram-mod] comma-separated list of types of speculative decoding to use - (default: none) + (default: server/CLI default config, none for other tools) (env: LLAMA_ARG_SPEC_TYPE) ---spec-default use default speculative decoding config - (enables ngram-mod) +--no-spec-type [none|draft-simple|draft-eagle3|draft-dflash|draft-dspark|draft-mtp|ngram-cache|ngram-simple|ngram-map-k|ngram-map-k4v|ngram-mod] + comma-separated list of types of speculative decoding to remove + (env: LLAMA_ARG_NO_SPEC_TYPE) +--spec-default DEPRECATED: default speculative decoding is enabled by default ``` ### Draft Model Parameters diff --git a/tests/test-arg-parser.cpp b/tests/test-arg-parser.cpp index e0907631ab..e340ea69fc 100644 --- a/tests/test-arg-parser.cpp +++ b/tests/test-arg-parser.cpp @@ -254,6 +254,117 @@ static void test(void) { assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), params, LLAMA_EXAMPLE_SPECULATIVE)); assert(params.speculative.draft.n_max == 123); + { + const auto types_ngram_mod = std::vector{ COMMON_SPECULATIVE_TYPE_NGRAM_MOD }; + const auto types_draft_mtp = std::vector{ COMMON_SPECULATIVE_TYPE_DRAFT_MTP }; + const auto types_none = std::vector{ COMMON_SPECULATIVE_TYPE_NONE }; + + { + common_params spec_params; + argv = {"binary_name"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_ngram_mod); + } + + { + common_params spec_params; + argv = {"binary_name", "--no-spec-type", "ngram-mod"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_none); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-type", "draft-mtp"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == std::vector({ COMMON_SPECULATIVE_TYPE_NGRAM_MOD, COMMON_SPECULATIVE_TYPE_DRAFT_MTP })); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-type", "ngram-mod"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_ngram_mod); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-type", "ngram-mod,draft-mtp"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == std::vector({ COMMON_SPECULATIVE_TYPE_NGRAM_MOD, COMMON_SPECULATIVE_TYPE_DRAFT_MTP })); + } + + { + common_params spec_params; + argv = {"binary_name", "--no-spec-type", "ngram-mod", "--spec-type", "draft-mtp"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_draft_mtp); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-type", "draft-mtp", "--no-spec-type", "ngram-mod"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_draft_mtp); + } + + { + common_params spec_params; + argv = {"binary_name", "--no-spec-type", "draft-mtp"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_ngram_mod); + } + + { + common_params spec_params; + argv = {"binary_name", "--no-spec-type", "ngram-mod", "--spec-type", "ngram-mod"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_ngram_mod); + } + + { + common_params spec_params; + argv = {"binary_name", "--no-spec-type", "ngram-mod", "--spec-type", "ngram-mod,draft-mtp"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == std::vector({ COMMON_SPECULATIVE_TYPE_NGRAM_MOD, COMMON_SPECULATIVE_TYPE_DRAFT_MTP })); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-type", "none"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_none); + } + + { + common_params spec_params; + argv = {"binary_name", "--no-spec-type", "none"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_none); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-type", "none", "--spec-type", "draft-mtp"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_draft_mtp); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-default"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_ngram_mod); + } + + { + common_params spec_params; + argv = {"binary_name", "--spec-default", "--no-spec-type", "ngram-mod"}; + assert(true == common_params_parse(argv.size(), list_str_to_char(argv).data(), spec_params, LLAMA_EXAMPLE_SERVER)); + assert(spec_params.speculative.types == types_none); + } + } + { common_params synth_params; argv = {"binary_name", "--spec-synth-len", "3.4"}; diff --git a/tests/test-model-resolution.cpp b/tests/test-model-resolution.cpp index 5191e77514..249a2d65e2 100644 --- a/tests/test-model-resolution.cpp +++ b/tests/test-model-resolution.cpp @@ -453,6 +453,13 @@ static void test_task_assembly() { // -hfd on a repo without sidecars keeps resolving a full model as draft common_params params; assemble({"server", "-hf", "test/main:Q8_0", "-hfd", "test/small"}, params); + REQUIRE(params.speculative.types == std::vector{COMMON_SPECULATIVE_TYPE_NGRAM_MOD}); + REQUIRE_EQ(params.speculative.draft.mparams.path, cached("test/small", "draft-model-Q4_K_M.gguf")); + } + { + // --no-spec-type removes the default speculative type and keeps the fallback draft resolution + common_params params; + assemble({"server", "-hf", "test/main:Q8_0", "-hfd", "test/small", "--no-spec-type", "ngram-mod"}, params); REQUIRE(params.speculative.types == std::vector{COMMON_SPECULATIVE_TYPE_NONE}); REQUIRE_EQ(params.speculative.draft.mparams.path, cached("test/small", "draft-model-Q4_K_M.gguf")); } diff --git a/tools/cli/README.md b/tools/cli/README.md index b874d02073..fcf95c36ce 100644 --- a/tools/cli/README.md +++ b/tools/cli/README.md @@ -209,7 +209,8 @@ | `--spec-draft-device, -devd, --device-draft ` | comma-separated list of devices to use for offloading the draft model (none = don't offload)
use --list-devices to see a list of available devices | | `--spec-draft-ngl, -ngld, --gpu-layers-draft, --n-gpu-layers-draft N` | max. number of draft model layers to store in VRAM, either an exact number, 'auto', or 'all' (default: auto)
(env: LLAMA_ARG_N_GPU_LAYERS_DRAFT) | | `--spec-draft-model, -md, --model-draft FNAME` | draft model for speculative decoding (default: unused)
(env: LLAMA_ARG_SPEC_DRAFT_MODEL) | -| `--spec-type none,draft-simple,draft-eagle3,draft-mtp,draft-dflash,draft-dspark,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to use (default: none)

(env: LLAMA_ARG_SPEC_TYPE) | +| `--spec-type none,draft-simple,draft-eagle3,draft-mtp,draft-dflash,draft-dspark,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to use (default: default speculative config)

(env: LLAMA_ARG_SPEC_TYPE) | +| `--no-spec-type none,draft-simple,draft-eagle3,draft-mtp,draft-dflash,draft-dspark,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to remove

(env: LLAMA_ARG_NO_SPEC_TYPE) | | `--spec-ngram-mod-n-min N` | minimum number of ngram tokens to use for ngram-based speculative decoding (default: 48) | | `--spec-ngram-mod-n-max N` | maximum number of ngram tokens to use for ngram-based speculative decoding (default: 64) | | `--spec-ngram-mod-n-match N` | ngram-mod lookup length (default: 24) | @@ -228,6 +229,6 @@ | `--gpt-oss-120b-default` | use gpt-oss-120b (note: can download weights from the internet) | | `--vision-gemma-4b-default` | use Gemma 3 4B QAT (note: can download weights from the internet) | | `--vision-gemma-12b-default` | use Gemma 3 12B QAT (note: can download weights from the internet) | -| `--spec-default` | enable default speculative decoding config | +| `--spec-default` | DEPRECATED: default speculative decoding is enabled by default | diff --git a/tools/server/README.md b/tools/server/README.md index c6e907ba91..a973057994 100644 --- a/tools/server/README.md +++ b/tools/server/README.md @@ -270,7 +270,8 @@ For the full list of features, please refer to [server's changelog](https://gith | `--spec-draft-device, -devd, --device-draft ` | comma-separated list of devices to use for offloading the draft model (none = don't offload)
use --list-devices to see a list of available devices | | `--spec-draft-ngl, -ngld, --gpu-layers-draft, --n-gpu-layers-draft N` | max. number of draft model layers to store in VRAM, either an exact number, 'auto', or 'all' (default: auto)
(env: LLAMA_ARG_N_GPU_LAYERS_DRAFT) | | `--spec-draft-model, -md, --model-draft FNAME` | draft model for speculative decoding (default: unused)
(env: LLAMA_ARG_SPEC_DRAFT_MODEL) | -| `--spec-type none,draft-simple,draft-eagle3,draft-mtp,draft-dflash,draft-dspark,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to use (default: none)

(env: LLAMA_ARG_SPEC_TYPE) | +| `--spec-type none,draft-simple,draft-eagle3,draft-mtp,draft-dflash,draft-dspark,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to use (default: default speculative config)

(env: LLAMA_ARG_SPEC_TYPE) | +| `--no-spec-type none,draft-simple,draft-eagle3,draft-mtp,draft-dflash,draft-dspark,ngram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache` | comma-separated list of types of speculative decoding to remove

(env: LLAMA_ARG_NO_SPEC_TYPE) | | `--spec-ngram-mod-n-min N` | minimum number of ngram tokens to use for ngram-based speculative decoding (default: 48) | | `--spec-ngram-mod-n-max N` | maximum number of ngram tokens to use for ngram-based speculative decoding (default: 64) | | `--spec-ngram-mod-n-match N` | ngram-mod lookup length (default: 24) | @@ -299,7 +300,7 @@ For the full list of features, please refer to [server's changelog](https://gith | `--gpt-oss-120b-default` | use gpt-oss-120b (note: can download weights from the internet) | | `--vision-gemma-4b-default` | use Gemma 3 4B QAT (note: can download weights from the internet) | | `--vision-gemma-12b-default` | use Gemma 3 12B QAT (note: can download weights from the internet) | -| `--spec-default` | enable default speculative decoding config | +| `--spec-default` | DEPRECATED: default speculative decoding is enabled by default |