Skip to content

Commit 8ece383

Browse files
authored
common: support remote preset (ggml-org#18520)
* arg: support remote preset * proof reading * allow one HF repo to point to multiple HF repos * docs: mention about multiple GGUF use case * correct clean_file_name * download: also return HTTP status code * fix case with cache file used * fix --offline option
1 parent 046d5fd commit 8ece383

File tree

6 files changed

+324
-82
lines changed

6 files changed

+324
-82
lines changed

common/arg.cpp

Lines changed: 116 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "log.h"
77
#include "sampling.h"
88
#include "download.h"
9+
#include "preset.h"
910

1011
// fix problem with std::min and std::max
1112
#if defined(_WIN32)
@@ -268,6 +269,46 @@ static void parse_tensor_buffer_overrides(const std::string & value, std::vector
268269
}
269270
}
270271

272+
static std::string clean_file_name(const std::string & fname) {
273+
std::string clean_fname = fname;
274+
string_replace_all(clean_fname, "\\", "_");
275+
string_replace_all(clean_fname, "/", "_");
276+
return clean_fname;
277+
}
278+
279+
static bool common_params_handle_remote_preset(common_params & params, llama_example ex) {
280+
GGML_ASSERT(!params.model.hf_repo.empty());
281+
282+
const bool offline = params.offline;
283+
std::string model_endpoint = get_model_endpoint();
284+
auto preset_url = model_endpoint + params.model.hf_repo + "/resolve/main/preset.ini";
285+
286+
// prepare local path for caching
287+
auto preset_fname = clean_file_name(params.model.hf_repo + "_preset.ini");
288+
auto preset_path = fs_get_cache_file(preset_fname);
289+
const int status = common_download_file_single(preset_url, preset_path, params.hf_token, offline);
290+
const bool has_preset = status >= 200 && status < 400;
291+
292+
// remote preset is optional, so we don't error out if not found
293+
if (has_preset) {
294+
LOG_INF("applying remote preset from %s\n", preset_url.c_str());
295+
common_preset_context ctx(ex, /* only_remote_allowed */ true);
296+
common_preset global; // unused for now
297+
auto remote_presets = ctx.load_from_ini(preset_path, global);
298+
if (remote_presets.find(COMMON_PRESET_DEFAULT_NAME) != remote_presets.end()) {
299+
common_preset & preset = remote_presets.at(COMMON_PRESET_DEFAULT_NAME);
300+
LOG_INF("\n%s", preset.to_ini().c_str()); // to_ini already added trailing newline
301+
preset.apply_to_params(params);
302+
} else {
303+
throw std::runtime_error("Remote preset.ini does not contain [" + std::string(COMMON_PRESET_DEFAULT_NAME) + "] section");
304+
}
305+
} else {
306+
LOG_INF("%s", "no remote preset found, skipping\n");
307+
}
308+
309+
return has_preset;
310+
}
311+
271312
struct handle_model_result {
272313
bool found_mmproj = false;
273314
common_params_model mmproj;
@@ -309,9 +350,7 @@ static handle_model_result common_params_handle_model(
309350
// make sure model path is present (for caching purposes)
310351
if (model.path.empty()) {
311352
// this is to avoid different repo having same file name, or same file name in different subdirs
312-
std::string filename = model.hf_repo + "_" + model.hf_file;
313-
// to make sure we don't have any slashes in the filename
314-
string_replace_all(filename, "/", "_");
353+
std::string filename = clean_file_name(model.hf_repo + "_" + model.hf_file);
315354
model.path = fs_get_cache_file(filename);
316355
}
317356

@@ -425,61 +464,87 @@ static bool common_params_parse_ex(int argc, char ** argv, common_params_context
425464
}
426465
};
427466

428-
std::set<std::string> seen_args;
467+
auto parse_cli_args = [&]() {
468+
std::set<std::string> seen_args;
429469

430-
for (int i = 1; i < argc; i++) {
431-
const std::string arg_prefix = "--";
470+
for (int i = 1; i < argc; i++) {
471+
const std::string arg_prefix = "--";
432472

433-
std::string arg = argv[i];
434-
if (arg.compare(0, arg_prefix.size(), arg_prefix) == 0) {
435-
std::replace(arg.begin(), arg.end(), '_', '-');
436-
}
437-
if (arg_to_options.find(arg) == arg_to_options.end()) {
438-
throw std::invalid_argument(string_format("error: invalid argument: %s", arg.c_str()));
439-
}
440-
if (!seen_args.insert(arg).second) {
441-
LOG_WRN("DEPRECATED: argument '%s' specified multiple times, use comma-separated values instead (only last value will be used)\n", arg.c_str());
442-
}
443-
auto & tmp = arg_to_options[arg];
444-
auto opt = *tmp.first;
445-
bool is_positive = tmp.second;
446-
if (opt.has_value_from_env()) {
447-
fprintf(stderr, "warn: %s environment variable is set, but will be overwritten by command line argument %s\n", opt.env, arg.c_str());
448-
}
449-
try {
450-
if (opt.handler_void) {
451-
opt.handler_void(params);
452-
continue;
473+
std::string arg = argv[i];
474+
if (arg.compare(0, arg_prefix.size(), arg_prefix) == 0) {
475+
std::replace(arg.begin(), arg.end(), '_', '-');
453476
}
454-
if (opt.handler_bool) {
455-
opt.handler_bool(params, is_positive);
456-
continue;
477+
if (arg_to_options.find(arg) == arg_to_options.end()) {
478+
throw std::invalid_argument(string_format("error: invalid argument: %s", arg.c_str()));
457479
}
458-
459-
// arg with single value
460-
check_arg(i);
461-
std::string val = argv[++i];
462-
if (opt.handler_int) {
463-
opt.handler_int(params, std::stoi(val));
464-
continue;
480+
if (!seen_args.insert(arg).second) {
481+
LOG_WRN("DEPRECATED: argument '%s' specified multiple times, use comma-separated values instead (only last value will be used)\n", arg.c_str());
482+
}
483+
auto & tmp = arg_to_options[arg];
484+
auto opt = *tmp.first;
485+
bool is_positive = tmp.second;
486+
if (opt.has_value_from_env()) {
487+
fprintf(stderr, "warn: %s environment variable is set, but will be overwritten by command line argument %s\n", opt.env, arg.c_str());
465488
}
466-
if (opt.handler_string) {
467-
opt.handler_string(params, val);
468-
continue;
489+
try {
490+
if (opt.handler_void) {
491+
opt.handler_void(params);
492+
continue;
493+
}
494+
if (opt.handler_bool) {
495+
opt.handler_bool(params, is_positive);
496+
continue;
497+
}
498+
499+
// arg with single value
500+
check_arg(i);
501+
std::string val = argv[++i];
502+
if (opt.handler_int) {
503+
opt.handler_int(params, std::stoi(val));
504+
continue;
505+
}
506+
if (opt.handler_string) {
507+
opt.handler_string(params, val);
508+
continue;
509+
}
510+
511+
// arg with 2 values
512+
check_arg(i);
513+
std::string val2 = argv[++i];
514+
if (opt.handler_str_str) {
515+
opt.handler_str_str(params, val, val2);
516+
continue;
517+
}
518+
} catch (std::exception & e) {
519+
throw std::invalid_argument(string_format(
520+
"error while handling argument \"%s\": %s\n\n"
521+
"usage:\n%s\n\nto show complete usage, run with -h",
522+
arg.c_str(), e.what(), opt.to_string().c_str()));
469523
}
524+
}
525+
};
470526

471-
// arg with 2 values
472-
check_arg(i);
473-
std::string val2 = argv[++i];
474-
if (opt.handler_str_str) {
475-
opt.handler_str_str(params, val, val2);
476-
continue;
477-
}
478-
} catch (std::exception & e) {
479-
throw std::invalid_argument(string_format(
480-
"error while handling argument \"%s\": %s\n\n"
481-
"usage:\n%s\n\nto show complete usage, run with -h",
482-
arg.c_str(), e.what(), opt.to_string().c_str()));
527+
// parse the first time to get -hf option (used for remote preset)
528+
parse_cli_args();
529+
530+
// maybe handle remote preset
531+
if (!params.model.hf_repo.empty()) {
532+
std::string cli_hf_repo = params.model.hf_repo;
533+
bool has_preset = common_params_handle_remote_preset(params, ctx_arg.ex);
534+
535+
// special case: if hf_repo explicitly set by preset, we need to preserve it (ignore CLI value)
536+
// this is useful when we have one HF repo pointing to other HF repos (one model - multiple GGUFs)
537+
std::string preset_hf_repo = params.model.hf_repo;
538+
bool preset_has_hf_repo = preset_hf_repo != cli_hf_repo;
539+
540+
if (has_preset) {
541+
// re-parse CLI args to override preset values
542+
parse_cli_args();
543+
}
544+
545+
// preserve hf_repo from preset if needed
546+
if (preset_has_hf_repo) {
547+
params.model.hf_repo = preset_hf_repo;
483548
}
484549
}
485550

0 commit comments

Comments
 (0)