From 98ea3ee66093375225e1ebd5f7ec33df7c283406 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 15 Dec 2025 13:40:37 +0100 Subject: [PATCH 1/6] common: fix --override-kv to support comma-separated values --- common/arg.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/common/arg.cpp b/common/arg.cpp index acf4c8f8a8..771038c906 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -2192,12 +2192,14 @@ common_params_context common_params_parser_init(common_params & params, llama_ex } )); add_opt(common_arg( - {"--override-kv"}, "KEY=TYPE:VALUE", - "advanced option to override model metadata by key. may be specified multiple times.\n" - "types: int, float, bool, str. example: --override-kv tokenizer.ggml.add_bos_token=bool:false", + {"--override-kv"}, "KEY=TYPE:VALUE,...", + "advanced option to override model metadata by key. use comma-separated list of overrides.\n" + "types: int, float, bool, str. example: --override-kv tokenizer.ggml.add_bos_token=bool:false,tokenizer.ggml.add_eos_token=bool:false", [](common_params & params, const std::string & value) { - if (!string_parse_kv_override(value.c_str(), params.kv_overrides)) { - throw std::runtime_error(string_format("error: Invalid type for KV override: %s\n", value.c_str())); + for (const auto & kv_override : string_split(value, ',')) { + if (!string_parse_kv_override(kv_override.c_str(), params.kv_overrides)) { + throw std::runtime_error(string_format("error: Invalid type for KV override: %s\n", kv_override.c_str())); + } } } )); From b83309d385a3f2bc8ae2e37672825066a6c9afc1 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 15 Dec 2025 14:22:34 +0100 Subject: [PATCH 2/6] Update common/arg.cpp Co-authored-by: Xuan-Son Nguyen --- common/arg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/arg.cpp b/common/arg.cpp index 771038c906..8e46807f16 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -2193,7 +2193,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex )); add_opt(common_arg( {"--override-kv"}, "KEY=TYPE:VALUE,...", - "advanced option to override model metadata by key. use comma-separated list of overrides.\n" + "advanced option to override model metadata by key. to specify multiple overrides, either use comma-separated or repeat this argument.\n" "types: int, float, bool, str. example: --override-kv tokenizer.ggml.add_bos_token=bool:false,tokenizer.ggml.add_eos_token=bool:false", [](common_params & params, const std::string & value) { for (const auto & kv_override : string_split(value, ',')) { From 502f718162ab4ae5b9fe31b11833a4df9947e4e0 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 15 Dec 2025 19:25:54 +0100 Subject: [PATCH 3/6] common: deprecate repeated arguments, suggest comma-separated values --- common/arg.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/arg.cpp b/common/arg.cpp index 8e46807f16..f5ee3b5663 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -420,6 +420,8 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context } }; + std::set seen_args; + for (int i = 1; i < argc; i++) { const std::string arg_prefix = "--"; @@ -430,6 +432,10 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context if (arg_to_options.find(arg) == arg_to_options.end()) { throw std::invalid_argument(string_format("error: invalid argument: %s", arg.c_str())); } + if (seen_args.count(arg) > 0) { + LOG_WRN("DEPRECATED: argument '%s' specified multiple times, use comma-separated values instead (only last value will be used)\n", arg.c_str()); + } + seen_args.insert(arg); auto & tmp = arg_to_options[arg]; auto opt = *tmp.first; bool is_positive = tmp.second; @@ -750,6 +756,8 @@ bool common_params_to_map(int argc, char ** argv, llama_example ex, std::map seen_args; + for (int i = 1; i < argc; i++) { const std::string arg_prefix = "--"; @@ -760,6 +768,10 @@ bool common_params_to_map(int argc, char ** argv, llama_example ex, std::map 0) { + LOG_WRN("DEPRECATED: argument '%s' specified multiple times, use comma-separated values instead (only last value will be used)\n", arg.c_str()); + } + seen_args.insert(arg); auto opt = *arg_to_options[arg]; std::string val; if (opt.value_hint != nullptr) { From a37539ea9b6eef501565836e09e2f17e197a5cc1 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 15 Dec 2025 19:27:07 +0100 Subject: [PATCH 4/6] common: add comma escape support for --override-kv --- common/arg.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/common/arg.cpp b/common/arg.cpp index f5ee3b5663..e76465fbc4 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -2208,7 +2208,32 @@ common_params_context common_params_parser_init(common_params & params, llama_ex "advanced option to override model metadata by key. to specify multiple overrides, either use comma-separated or repeat this argument.\n" "types: int, float, bool, str. example: --override-kv tokenizer.ggml.add_bos_token=bool:false,tokenizer.ggml.add_eos_token=bool:false", [](common_params & params, const std::string & value) { - for (const auto & kv_override : string_split(value, ',')) { + std::vector kv_overrides; + + std::string current; + bool escaping = false; + + for (const char c : value) { + if (escaping) { + current.push_back(c); + escaping = false; + } else if (c == '\\') { + escaping = true; + } else if (c == ',') { + kv_overrides.push_back(current); + current.clear(); + } else { + current.push_back(c); + } + } + + if (escaping) { + current.push_back('\\'); + } + + kv_overrides.push_back(current); + + for (const auto & kv_override : kv_overrides) { if (!string_parse_kv_override(kv_override.c_str(), params.kv_overrides)) { throw std::runtime_error(string_format("error: Invalid type for KV override: %s\n", kv_override.c_str())); } From ea7044eeb38a594054ef2587be34906600a5c915 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 15 Dec 2025 20:00:33 +0100 Subject: [PATCH 5/6] common: optimize duplicate detection with insert().second Co-authored-by: personalmountains <46615898+personalmountains@users.noreply.github.com> --- common/arg.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/common/arg.cpp b/common/arg.cpp index e76465fbc4..90ba98339c 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -432,10 +432,9 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context if (arg_to_options.find(arg) == arg_to_options.end()) { throw std::invalid_argument(string_format("error: invalid argument: %s", arg.c_str())); } - if (seen_args.count(arg) > 0) { + if (!seen_args.insert(arg).second) { LOG_WRN("DEPRECATED: argument '%s' specified multiple times, use comma-separated values instead (only last value will be used)\n", arg.c_str()); } - seen_args.insert(arg); auto & tmp = arg_to_options[arg]; auto opt = *tmp.first; bool is_positive = tmp.second; @@ -768,10 +767,9 @@ bool common_params_to_map(int argc, char ** argv, llama_example ex, std::map 0) { + if (!seen_args.insert(arg).second) { LOG_WRN("DEPRECATED: argument '%s' specified multiple times, use comma-separated values instead (only last value will be used)\n", arg.c_str()); } - seen_args.insert(arg); auto opt = *arg_to_options[arg]; std::string val; if (opt.value_hint != nullptr) { From cb2321c828a4be7e53dfe80080960205a8137f86 Mon Sep 17 00:00:00 2001 From: Pascal Date: Mon, 15 Dec 2025 23:15:13 +0100 Subject: [PATCH 6/6] common: migrate all repeated args to comma-separated syntax --- common/arg.cpp | 71 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/common/arg.cpp b/common/arg.cpp index 90ba98339c..989fafb560 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -1224,13 +1224,15 @@ common_params_context common_params_parser_init(common_params & params, llama_ex ).set_examples({LLAMA_EXAMPLE_COMPLETION, LLAMA_EXAMPLE_CLI, LLAMA_EXAMPLE_DIFFUSION})); add_opt(common_arg( {"--in-file"}, "FNAME", - "an input file (repeat to specify multiple files)", + "an input file (use comma-separated values to specify multiple files)", [](common_params & params, const std::string & value) { - std::ifstream file(value); - if (!file) { - throw std::runtime_error(string_format("error: failed to open file '%s'\n", value.c_str())); + for (const auto & item : string_split(value, ',')) { + std::ifstream file(item); + if (!file) { + throw std::runtime_error(string_format("error: failed to open file '%s'\n", item.c_str())); + } + params.in_files.push_back(item); } - params.in_files.push_back(value); } ).set_examples({LLAMA_EXAMPLE_IMATRIX})); add_opt(common_arg( @@ -1953,9 +1955,11 @@ common_params_context common_params_parser_init(common_params & params, llama_ex ).set_examples(mmproj_examples).set_env("LLAMA_ARG_MMPROJ_OFFLOAD")); add_opt(common_arg( {"--image", "--audio"}, "FILE", - "path to an image or audio file. use with multimodal models, can be repeated if you have multiple files\n", + "path to an image or audio file. use with multimodal models, use comma-separated values for multiple files\n", [](common_params & params, const std::string & value) { - params.image.emplace_back(value); + for (const auto & item : string_split(value, ',')) { + params.image.emplace_back(item); + } } ).set_examples({LLAMA_EXAMPLE_MTMD, LLAMA_EXAMPLE_CLI})); add_opt(common_arg( @@ -2248,33 +2252,50 @@ common_params_context common_params_parser_init(common_params & params, llama_ex )); add_opt(common_arg( {"--lora"}, "FNAME", - "path to LoRA adapter (can be repeated to use multiple adapters)", + "path to LoRA adapter (use comma-separated values to load multiple adapters)", [](common_params & params, const std::string & value) { - params.lora_adapters.push_back({ std::string(value), 1.0, "", "", nullptr }); + for (const auto & item : string_split(value, ',')) { + params.lora_adapters.push_back({ item, 1.0, "", "", nullptr }); + } } // we define this arg on both COMMON and EXPORT_LORA, so when showing help message of export-lora, it will be categorized as "example-specific" arg ).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_EXPORT_LORA})); add_opt(common_arg( - {"--lora-scaled"}, "FNAME", "SCALE", - "path to LoRA adapter with user defined scaling (can be repeated to use multiple adapters)", - [](common_params & params, const std::string & fname, const std::string & scale) { - params.lora_adapters.push_back({ fname, std::stof(scale), "", "", nullptr }); + {"--lora-scaled"}, "FNAME:SCALE,...", + "path to LoRA adapter with user defined scaling (format: FNAME:SCALE,...)\n" + "note: use comma-separated values", + [](common_params & params, const std::string & value) { + for (const auto & item : string_split(value, ',')) { + auto parts = string_split(item, ':'); + if (parts.size() != 2) { + throw std::invalid_argument("lora-scaled format: FNAME:SCALE"); + } + params.lora_adapters.push_back({ parts[0], std::stof(parts[1]), "", "", nullptr }); + } } // we define this arg on both COMMON and EXPORT_LORA, so when showing help message of export-lora, it will be categorized as "example-specific" arg ).set_examples({LLAMA_EXAMPLE_COMMON, LLAMA_EXAMPLE_EXPORT_LORA})); add_opt(common_arg( {"--control-vector"}, "FNAME", - "add a control vector\nnote: this argument can be repeated to add multiple control vectors", + "add a control vector\nnote: use comma-separated values to add multiple control vectors", [](common_params & params, const std::string & value) { - params.control_vectors.push_back({ 1.0f, value, }); + for (const auto & item : string_split(value, ',')) { + params.control_vectors.push_back({ 1.0f, item, }); + } } )); add_opt(common_arg( - {"--control-vector-scaled"}, "FNAME", "SCALE", + {"--control-vector-scaled"}, "FNAME:SCALE,...", "add a control vector with user defined scaling SCALE\n" - "note: this argument can be repeated to add multiple scaled control vectors", - [](common_params & params, const std::string & fname, const std::string & scale) { - params.control_vectors.push_back({ std::stof(scale), fname }); + "note: use comma-separated values (format: FNAME:SCALE,...)", + [](common_params & params, const std::string & value) { + for (const auto & item : string_split(value, ',')) { + auto parts = string_split(item, ':'); + if (parts.size() != 2) { + throw std::invalid_argument("control-vector-scaled format: FNAME:SCALE"); + } + params.control_vectors.push_back({ std::stof(parts[1]), parts[0] }); + } } )); add_opt(common_arg( @@ -2364,13 +2385,15 @@ common_params_context common_params_parser_init(common_params & params, llama_ex ).set_env("HF_TOKEN")); add_opt(common_arg( {"--context-file"}, "FNAME", - "file to load context from (repeat to specify multiple files)", + "file to load context from (use comma-separated values to specify multiple files)", [](common_params & params, const std::string & value) { - std::ifstream file(value, std::ios::binary); - if (!file) { - throw std::runtime_error(string_format("error: failed to open file '%s'\n", value.c_str())); + for (const auto & item : string_split(value, ',')) { + std::ifstream file(item, std::ios::binary); + if (!file) { + throw std::runtime_error(string_format("error: failed to open file '%s'\n", item.c_str())); + } + params.context_files.push_back(item); } - params.context_files.push_back(value); } ).set_examples({LLAMA_EXAMPLE_RETRIEVAL})); add_opt(common_arg(