fix(api): resolve sampler names case-insensitively to canonical form

validate_sampler_name only matched the exact, case-sensitive name, so near-miss client names such as lowercase variants were rejected while an omitted name silently used the model scheduler via the Default sentinel. Fall back to find_sampler and return the canonical name so create_sampler applies the intended sampler; unknown names still return 404. Add an API test covering case-insensitive resolution and rejection of unknown names.
This commit is contained in:
CalamitousFelicitousness
2026-06-07 06:57:07 +01:00
parent 338cbbfb28
commit 0c0d455c11
2 changed files with 40 additions and 3 deletions
+9 -3
View File
@@ -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):
+31
View File
@@ -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()