mirror of
https://github.com/vladmandic/automatic
synced 2026-08-26 15:16:01 +02:00
Merge pull request #4897 from vladmandic/fix/api-sampler-resolution
Fix/api sampler resolution
This commit is contained in:
@@ -17,9 +17,15 @@ def register_upload_store(getter_fn):
|
||||
|
||||
def validate_sampler_name(name):
|
||||
config = sd_samplers.all_samplers_map.get(name, None)
|
||||
if config is None:
|
||||
raise HTTPException(status_code=404, detail="Sampler not found")
|
||||
return name
|
||||
if config is not None:
|
||||
return name
|
||||
# accept case-insensitive and alias variants, returning the canonical name so the
|
||||
# exact-match lookup in create_sampler resolves instead of silently using the model default
|
||||
if isinstance(name, str) and name not in ('', 'None'):
|
||||
sampler = sd_samplers.find_sampler(name)
|
||||
if sampler is not None:
|
||||
return sampler.name
|
||||
raise HTTPException(status_code=404, detail="Sampler not found")
|
||||
|
||||
|
||||
def decode_base64_to_image(encoding, quiet=False):
|
||||
|
||||
@@ -302,7 +302,7 @@ ReqImg2Img = PydanticModelGenerator(
|
||||
StableDiffusionProcessingImg2Img,
|
||||
[
|
||||
{"key": "sampler_index", "type": Union[int, str], "default": 0},
|
||||
{"key": "sampler_name", "type": str, "default": "UniPC"},
|
||||
{"key": "sampler_name", "type": str, "default": "Default"},
|
||||
{"key": "hr_sampler_name", "type": str, "default": "Same as primary"},
|
||||
{"key": "init_images", "type": list, "default": None},
|
||||
{"key": "denoising_strength", "type": float, "default": 0.5},
|
||||
|
||||
@@ -742,7 +742,7 @@ def create_settings(cmd_opts):
|
||||
"schedulers_solver_order": OptionInfo(0, "Solver order (where", gr.Slider, {"minimum": 0, "maximum": 5, "step": 1, "visible": False}),
|
||||
"schedulers_use_loworder": OptionInfo(True, "Use simplified solvers in final steps", gr.Checkbox, {"visible": False}),
|
||||
"schedulers_prediction_type": OptionInfo("default", "Override model prediction type", gr.Radio, {"choices": ["default", "epsilon", "sample", "v_prediction", "flow_prediction"], "visible": False}),
|
||||
"schedulers_sigma": OptionInfo("default", "Sigma algorithm", gr.Radio, {"choices": ["default", "karras", "exponential", "polyexponential"], "visible": False}),
|
||||
"schedulers_sigma": OptionInfo("default", "Sigma algorithm", gr.Radio, {"choices": ["default", "karras", "betas", "exponential", "lambdas", "flowmatch"], "visible": False}),
|
||||
"schedulers_beta_schedule": OptionInfo("default", "Beta schedule", gr.Dropdown, {"choices": ["default", "linear", "scaled_linear", "squaredcos_cap_v2", "sigmoid"], "visible": False}),
|
||||
"schedulers_use_thresholding": OptionInfo(False, "Use dynamic thresholding", gr.Checkbox, {"visible": False}),
|
||||
"schedulers_timestep_spacing": OptionInfo("default", "Timestep spacing", gr.Dropdown, {"choices": ["default", "linspace", "leading", "trailing"], "visible": False}),
|
||||
|
||||
@@ -210,6 +210,36 @@ class GenerationAPITest:
|
||||
data, elapsed = self._txt2img({'sampler_name': sampler})
|
||||
self._check_generation(data, f'generate_{sampler}', elapsed)
|
||||
|
||||
def test_sampler_name_resolution(self, available_samplers):
|
||||
"""Sampler name resolution: a case-insensitive name resolves to the canonical sampler
|
||||
(and is applied, not silently swapped for the model default), while an unknown name is
|
||||
rejected rather than falling back to the default scheduler."""
|
||||
self._category = 'samplers'
|
||||
print("\n--- Sampler Name Resolution ---")
|
||||
|
||||
if self._critical_error:
|
||||
self.skip('sampler_lenient_case', self._critical_error)
|
||||
self.skip('sampler_unknown_rejected', self._critical_error)
|
||||
return
|
||||
|
||||
canonical = next((s for s in ('Euler a', 'DPM++ 2M', 'UniPC') if s in available_samplers), None)
|
||||
if canonical is None:
|
||||
self.skip('sampler_lenient_case', 'no known sampler available')
|
||||
else:
|
||||
data, _ = self._txt2img({'sampler_name': canonical.lower()})
|
||||
if 'error' in data:
|
||||
self.record(False, 'sampler_lenient_case', f"lowercase '{canonical.lower()}' rejected: {data}")
|
||||
else:
|
||||
resolved = canonical in self._get_info(data)
|
||||
self.record(resolved, 'sampler_lenient_case',
|
||||
f"'{canonical.lower()}' -> '{canonical}'" if resolved
|
||||
else f"generated but '{canonical}' not in info (model default used?)")
|
||||
|
||||
data, _ = self._txt2img({'sampler_name': 'ThisIsNotARealSampler'})
|
||||
rejected = 'error' in data
|
||||
self.record(rejected, 'sampler_unknown_rejected',
|
||||
'unknown name rejected' if rejected else 'unknown name was NOT rejected')
|
||||
|
||||
# =========================================================================
|
||||
# Tests: Color Grading Params
|
||||
# =========================================================================
|
||||
@@ -577,6 +607,7 @@ class GenerationAPITest:
|
||||
# Samplers
|
||||
available = self.test_samplers_list()
|
||||
self.test_samplers_generate(available)
|
||||
self.test_sampler_name_resolution(available)
|
||||
|
||||
# Grading
|
||||
self.run_grading_tests()
|
||||
|
||||
Reference in New Issue
Block a user