22#include " preset.h"
33#include " peg-parser.h"
44#include " log.h"
5+ #include " download.h"
56
67#include < fstream>
78#include < sstream>
@@ -15,9 +16,13 @@ static std::string rm_leading_dashes(const std::string & str) {
1516 return str.substr (pos);
1617}
1718
18- std::vector<std::string> common_preset::to_args () const {
19+ std::vector<std::string> common_preset::to_args (const std::string & bin_path ) const {
1920 std::vector<std::string> args;
2021
22+ if (!bin_path.empty ()) {
23+ args.push_back (bin_path);
24+ }
25+
2126 for (const auto & [opt, value] : options) {
2227 args.push_back (opt.args .back ()); // use the last arg as the main arg
2328 if (opt.value_hint == nullptr && opt.value_hint_2 == nullptr ) {
@@ -63,6 +68,52 @@ std::string common_preset::to_ini() const {
6368 return ss.str ();
6469}
6570
71+ void common_preset::set_option (const common_preset_context & ctx, const std::string & env, const std::string & value) {
72+ // try if option exists, update it
73+ for (auto & [opt, val] : options) {
74+ if (opt.env && env == opt.env ) {
75+ val = value;
76+ return ;
77+ }
78+ }
79+ // if option does not exist, we need to add it
80+ if (ctx.key_to_opt .find (env) == ctx.key_to_opt .end ()) {
81+ throw std::runtime_error (string_format (
82+ " %s: option with env '%s' not found in ctx_params" ,
83+ __func__, env.c_str ()
84+ ));
85+ }
86+ options[ctx.key_to_opt .at (env)] = value;
87+ }
88+
89+ void common_preset::unset_option (const std::string & env) {
90+ for (auto it = options.begin (); it != options.end (); ) {
91+ const common_arg & opt = it->first ;
92+ if (opt.env && env == opt.env ) {
93+ it = options.erase (it);
94+ return ;
95+ } else {
96+ ++it;
97+ }
98+ }
99+ }
100+
101+ bool common_preset::get_option (const std::string & env, std::string & value) const {
102+ for (const auto & [opt, val] : options) {
103+ if (opt.env && env == opt.env ) {
104+ value = val;
105+ return true ;
106+ }
107+ }
108+ return false ;
109+ }
110+
111+ void common_preset::merge (const common_preset & other) {
112+ for (const auto & [opt, val] : other.options ) {
113+ options[opt] = val; // overwrite existing options
114+ }
115+ }
116+
66117static std::map<std::string, std::map<std::string, std::string>> parse_ini_from_file (const std::string & path) {
67118 std::map<std::string, std::map<std::string, std::string>> parsed;
68119
@@ -172,9 +223,12 @@ static std::string parse_bool_arg(const common_arg & arg, const std::string & ke
172223 return value;
173224}
174225
175- common_presets common_presets_load (const std::string & path, common_params_context & ctx_params) {
226+ common_preset_context::common_preset_context (llama_example ex)
227+ : ctx_params(common_params_parser_init(default_params, ex)),
228+ key_to_opt(get_map_key_opt(ctx_params)) {}
229+
230+ common_presets common_preset_context::load_from_ini (const std::string & path, common_preset & global) const {
176231 common_presets out;
177- auto key_to_opt = get_map_key_opt (ctx_params);
178232 auto ini_data = parse_ini_from_file (path);
179233
180234 for (auto section : ini_data) {
@@ -188,7 +242,7 @@ common_presets common_presets_load(const std::string & path, common_params_conte
188242 for (const auto & [key, value] : section.second ) {
189243 LOG_DBG (" option: %s = %s\n " , key.c_str (), value.c_str ());
190244 if (key_to_opt.find (key) != key_to_opt.end ()) {
191- auto & opt = key_to_opt[ key] ;
245+ const auto & opt = key_to_opt. at ( key) ;
192246 if (is_bool_arg (opt)) {
193247 preset.options [opt] = parse_bool_arg (opt, key, value);
194248 } else {
@@ -199,8 +253,137 @@ common_presets common_presets_load(const std::string & path, common_params_conte
199253 // TODO: maybe warn about unknown key?
200254 }
201255 }
256+
257+ if (preset.name == " *" ) {
258+ // handle global preset
259+ global = preset;
260+ } else {
261+ out[preset.name ] = preset;
262+ }
263+ }
264+
265+ return out;
266+ }
267+
268+ common_presets common_preset_context::load_from_cache () const {
269+ common_presets out;
270+
271+ auto cached_models = common_list_cached_models ();
272+ for (const auto & model : cached_models) {
273+ common_preset preset;
274+ preset.name = model.to_string ();
275+ preset.set_option (*this , " LLAMA_ARG_HF_REPO" , model.to_string ());
202276 out[preset.name ] = preset;
203277 }
204278
205279 return out;
206280}
281+
282+ struct local_model {
283+ std::string name;
284+ std::string path;
285+ std::string path_mmproj;
286+ };
287+
288+ common_presets common_preset_context::load_from_models_dir (const std::string & models_dir) const {
289+ if (!std::filesystem::exists (models_dir) || !std::filesystem::is_directory (models_dir)) {
290+ throw std::runtime_error (string_format (" error: '%s' does not exist or is not a directory\n " , models_dir.c_str ()));
291+ }
292+
293+ std::vector<local_model> models;
294+ auto scan_subdir = [&models](const std::string & subdir_path, const std::string & name) {
295+ auto files = fs_list (subdir_path, false );
296+ common_file_info model_file;
297+ common_file_info first_shard_file;
298+ common_file_info mmproj_file;
299+ for (const auto & file : files) {
300+ if (string_ends_with (file.name , " .gguf" )) {
301+ if (file.name .find (" mmproj" ) != std::string::npos) {
302+ mmproj_file = file;
303+ } else if (file.name .find (" -00001-of-" ) != std::string::npos) {
304+ first_shard_file = file;
305+ } else {
306+ model_file = file;
307+ }
308+ }
309+ }
310+ // single file model
311+ local_model model{
312+ /* name */ name,
313+ /* path */ first_shard_file.path .empty () ? model_file.path : first_shard_file.path ,
314+ /* path_mmproj */ mmproj_file.path // can be empty
315+ };
316+ if (!model.path .empty ()) {
317+ models.push_back (model);
318+ }
319+ };
320+
321+ auto files = fs_list (models_dir, true );
322+ for (const auto & file : files) {
323+ if (file.is_dir ) {
324+ scan_subdir (file.path , file.name );
325+ } else if (string_ends_with (file.name , " .gguf" )) {
326+ // single file model
327+ std::string name = file.name ;
328+ string_replace_all (name, " .gguf" , " " );
329+ local_model model{
330+ /* name */ name,
331+ /* path */ file.path ,
332+ /* path_mmproj */ " "
333+ };
334+ models.push_back (model);
335+ }
336+ }
337+
338+ // convert local models to presets
339+ common_presets out;
340+ for (const auto & model : models) {
341+ common_preset preset;
342+ preset.name = model.name ;
343+ preset.set_option (*this , " LLAMA_ARG_MODEL" , model.path );
344+ if (!model.path_mmproj .empty ()) {
345+ preset.set_option (*this , " LLAMA_ARG_MMPROJ" , model.path_mmproj );
346+ }
347+ out[preset.name ] = preset;
348+ }
349+
350+ return out;
351+ }
352+
353+ common_preset common_preset_context::load_from_args (int argc, char ** argv) const {
354+ common_preset preset;
355+ preset.name = COMMON_PRESET_DEFAULT_NAME;
356+
357+ bool ok = common_params_to_map (argc, argv, ctx_params.ex , preset.options );
358+ if (!ok) {
359+ throw std::runtime_error (" failed to parse CLI arguments into preset" );
360+ }
361+
362+ return preset;
363+ }
364+
365+ common_presets common_preset_context::cascade (const common_presets & base, const common_presets & added) const {
366+ common_presets out = base; // copy
367+ for (const auto & [name, preset_added] : added) {
368+ if (out.find (name) != out.end ()) {
369+ // if exists, merge
370+ common_preset & target = out[name];
371+ target.merge (preset_added);
372+ } else {
373+ // otherwise, add directly
374+ out[name] = preset_added;
375+ }
376+ }
377+ return out;
378+ }
379+
380+ common_presets common_preset_context::cascade (const common_preset & base, const common_presets & presets) const {
381+ common_presets out;
382+ for (const auto & [name, preset] : presets) {
383+ common_preset tmp = base; // copy
384+ tmp.name = name;
385+ tmp.merge (preset);
386+ out[name] = std::move (tmp);
387+ }
388+ return out;
389+ }
0 commit comments