From 1be08b9d15bd361edd21c6964d0d3be52184930a Mon Sep 17 00:00:00 2001 From: Wagner Bruna Date: Sat, 18 Apr 2026 14:51:42 -0300 Subject: [PATCH] sd: report all sampler aliases and centralize name mapping (#2149) * debug: allow loading backend libraries without normal arg parsing This is just to be able to test backend functions directly, with e.g.: >> import koboldcpp >> koboldcpp.init_libraries() >> koboldcpp.sd_get_info() * sd: report all sampler aliases and centralize name mapping --- koboldcpp.py | 55 +++++++++++++++++++++++++++--- otherarch/sdcpp/sdtype_adapter.cpp | 10 ++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/koboldcpp.py b/koboldcpp.py index 93985b5dd..b32e81cf4 100755 --- a/koboldcpp.py +++ b/koboldcpp.py @@ -830,7 +830,9 @@ def init_library(): libname = lib_default - if args.noavx2: #failsafe implies noavx2 always + if not args: # debug helper: koboldcpp.py loaded by external script + pass + elif args.noavx2: #failsafe implies noavx2 always if args.failsafe and (args.usevulkan is not None) and file_exists(lib_vulkan_failsafe): libname = lib_vulkan_failsafe elif (args.usevulkan is not None) and file_exists(lib_vulkan_noavx2): @@ -2237,6 +2239,50 @@ def sd_get_available_schedulers(): info = sd_get_info() return info.get('available_schedulers', []) +sampler_aliases = [ + # sd.cpp name, UI name, aliases + ['euler', 'Euler', 'k_euler'], + ['euler_a', 'Euler A', 'k_euler_a', 'euler a'], + ['heun', 'Heun', 'k_heun'], + ['dpm2', 'DPM2', 'k_dpm_2'], + ['lcm', 'LCM', 'k_lcm'], + ['dpm++2m', 'DPM++ 2M', 'k_dpmpp_2m', 'dpm++ 2m karras', 'dpm++ 2m'], + ['ddim_trailing', 'DDIM', 'ddim'], + ['res_multistep', 'Res Multistep', 'k_res_multistep', 'res multistep'], + ['res_2s', 'Res 2s', 'k_res_2s', 'res 2s'], +] + +def sd_sampler_canonical_name(name): + info = sd_get_info() + available = info.get('available_samplers', []) + alias_map = {} + for aliases in sampler_aliases: + for alias in aliases: + alias_map[alias] = aliases[0] + alias_map[alias.lower()] = aliases[0] + cname = alias_map.get(name.lower(), name) + if cname in available: + return cname + return 'default' + +def sd_sdapi_samplers(): + result = [] + info = sd_get_info() + available = set(info.get('available_samplers', [])) + # ensure we only advertise supported samplers + smap = {} + for aliases in sampler_aliases: + if aliases[0] in available: + smap[aliases[1]] = aliases[0:1] + aliases[2:] + available.remove(aliases[0]) + for sampler in available: + if sampler not in smap: + smap[sampler] = [] + result = [{'name': k, 'aliases': v, 'options':{}} + for k, v in smap.items()] + return result + + sd_convdirect_choices = ['off', 'vaeonly', 'full'] def sd_convdirect_option(value): @@ -2563,7 +2609,7 @@ def sd_generate(genparams): seed = tryparseint(genparams.get("seed", -1),-1) if seed < 0: seed = random.randint(100000, 999999) - sample_method = (genparams.get("sampler_name") or "default").lower() + sample_method = (genparams.get("sampler_name") or "default") scheduler = (genparams.get("scheduler") or "default").lower() clip_skip = tryparseint(genparams.get("clip_skip", -1),-1) vid_req_frames = tryparseint(genparams.get("frames", 1),1) @@ -2616,7 +2662,7 @@ def sd_generate(genparams): inputs.width = width inputs.height = height inputs.seed = ((seed + 2**31) % 2**32) - 2**31 - inputs.sample_method = sample_method.encode("UTF-8") + inputs.sample_method = sd_sampler_canonical_name(sample_method).encode("UTF-8") inputs.scheduler = scheduler.encode("UTF-8") inputs.clip_skip = clip_skip inputs.vid_req_frames = vid_req_frames @@ -5519,8 +5565,7 @@ Change Mode
if (friendlysdmodelname=="inactive" or fullsdmodelpath=="") and not(autoswapmode and imageName is not None): response_body = (json.dumps([]).encode()) else: - response_body = (json.dumps([{"name":"Euler","aliases":["k_euler"],"options":{}},{"name":"Euler a","aliases":["k_euler_a","k_euler_ancestral"],"options":{}},{"name":"Heun","aliases":["k_heun"],"options":{}},{"name":"DPM2","aliases":["k_dpm_2"],"options":{}},{"name":"DPM++ 2M","aliases":["k_dpmpp_2m"],"options":{}},{"name":"DDIM","aliases":["ddim"],"options":{}},{"name":"LCM","aliases":["k_lcm"],"options":{}},{"name":"Res 2s","aliases":["k_res_2s"],"options":{}},{"name":"Res Multistep","aliases":["k_res_multistep"],"options":{}}, - {"name":"Default","aliases":["default"],"options":{}}]).encode()) + response_body = (json.dumps(sd_sdapi_samplers()).encode()) elif clean_path.endswith('/sdapi/v1/schedulers'): if (friendlysdmodelname=="inactive" or fullsdmodelpath=="") and not(autoswapmode and imageName is not None): response_body = (json.dumps([]).encode()) diff --git a/otherarch/sdcpp/sdtype_adapter.cpp b/otherarch/sdcpp/sdtype_adapter.cpp index 09032e7d8..091a734b4 100644 --- a/otherarch/sdcpp/sdtype_adapter.cpp +++ b/otherarch/sdcpp/sdtype_adapter.cpp @@ -1640,6 +1640,16 @@ sd_info_outputs sdtype_get_info() } j["available_schedulers"] = available_schedulers; + auto available_samplers = json::array(); + available_samplers.push_back("default"); + for (int i = 0; i < sample_method_t::SAMPLE_METHOD_COUNT; i++) { + std::string name = sd_sample_method_name((sample_method_t)i); + if (name != "NONE") { + available_samplers.push_back(name); + } + } + j["available_samplers"] = available_samplers; + static std::string recent_info = j.dump(); sd_info_outputs output; output.status = 0;