Reject unknown union ControlNet control types instead of defaulting to canny
union_control_mode fell back to control_mode=0 (the canny head) for ANY unmapped control type, so a typo'd or unsupported value like 'detph' silently conditioned the map as canny instead of failing. preprocess_control passes non-canny maps through unchanged, so that map would be interpreted under the wrong mode with no error. Now only 'passthrough' (or an empty type) keeps the deliberate mode-0 default; any other unknown type raises ValueError, which the generate route maps to a 400. Known modes are unchanged.
This commit is contained in:
parent
46a0a21d53
commit
41bdc116be
2 changed files with 30 additions and 4 deletions
|
|
@ -220,13 +220,24 @@ _UNION_CONTROL_MODES: dict[str, int] = {
|
|||
def union_control_mode(spec_id: str, control_type: str) -> Optional[int]:
|
||||
"""The integer ``control_mode`` for a union ControlNet, or None.
|
||||
|
||||
A union model requires a concrete mode (diffusers raises on None), so a curated union
|
||||
entry always gets an index, defaulting to 0 for types like 'passthrough' that carry
|
||||
none. A non-union entry returns None so the caller omits the kwarg."""
|
||||
A union model requires a concrete mode (diffusers raises on None). A known mode maps to its
|
||||
index; ``passthrough`` (or an empty type) carries no intrinsic mode and defaults to 0 (the
|
||||
canny head). An unknown/typo'd type (e.g. 'detph') raises ValueError so the route rejects it
|
||||
with a 400 instead of silently running the canny head against a map meant for another mode,
|
||||
which would produce wrong conditioning. A non-union entry returns None so the caller omits the
|
||||
kwarg."""
|
||||
entry = _catalog_by_id().get(spec_id)
|
||||
if entry is None or not entry.is_union:
|
||||
return None
|
||||
return _UNION_CONTROL_MODES.get((control_type or "").strip().lower(), 0)
|
||||
ct = (control_type or "").strip().lower()
|
||||
if ct in _UNION_CONTROL_MODES:
|
||||
return _UNION_CONTROL_MODES[ct]
|
||||
if ct in ("", "passthrough"):
|
||||
return 0 # already-preprocessed map with no intrinsic mode; canny is the default head
|
||||
raise ValueError(
|
||||
f"Unknown control type {control_type!r} for a union ControlNet. Use one of: "
|
||||
f"{', '.join(sorted(_UNION_CONTROL_MODES))}, or passthrough."
|
||||
)
|
||||
|
||||
|
||||
def preprocess_control(image: Any, control_type: str) -> Any:
|
||||
|
|
|
|||
|
|
@ -66,6 +66,21 @@ def test_union_control_mode_maps_only_union_entries():
|
|||
assert dc.union_control_mode("some/bare-repo", "canny") is None
|
||||
|
||||
|
||||
def test_union_control_mode_rejects_unknown_type():
|
||||
# An unknown / typo'd control type (e.g. 'detph') must NOT silently fall back to the canny
|
||||
# head (0): preprocess_control passes non-canny maps through unchanged, so mode 0 would
|
||||
# condition a map meant for another mode as canny -- silently wrong. Only passthrough (or an
|
||||
# empty type) defaults to 0; anything else raises so the route returns a 400.
|
||||
with pytest.raises(ValueError, match = "Unknown control type"):
|
||||
dc.union_control_mode("flux-union-pro", "detph")
|
||||
with pytest.raises(ValueError, match = "Unknown control type"):
|
||||
dc.union_control_mode("flux-union-pro", "scribble")
|
||||
# passthrough and empty still default to 0 (the intended no-intrinsic-mode case); a non-union
|
||||
# entry is unaffected (returns None, never raises).
|
||||
assert dc.union_control_mode("flux-union-pro", "") == 0
|
||||
assert dc.union_control_mode("some/bare-repo", "detph") is None
|
||||
|
||||
|
||||
def test_resolve_controlnet_local(tmp_path, monkeypatch):
|
||||
d = tmp_path / "controlnets"
|
||||
d.mkdir()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue