Fix CLIPSeg multi-prompt crash: missing padding/truncation on tokenizer call

Live rental run hit it immediately on 'detail_targets' with more than one
target: CLIPSegProcessor tokenizes each prompt to a different length, and
without padding=True torch can't stack them into a batch ('excessive
nesting... type list where int is expected'). Reproduced against the real
CIDAS/clipseg-rd64-refined model before and after the fix. Single-target
runs were unaffected (batch of 1 never needed padding), which is why the
earlier probe steps didn't catch it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-07-19 00:53:36 +03:00
co-authored by Claude Fable 5
parent c864f62480
commit 5727c39189
2 changed files with 41 additions and 1 deletions
+6 -1
View File
@@ -224,8 +224,13 @@ def _clipseg_heat(frames, prompts, debug=False):
with torch.no_grad():
for _, img in frames:
pil = Image.fromarray((img.numpy() * 255.0).astype(np.uint8))
# padding=True is mandatory once len(prompts) > 1: CLIPSeg's tokenizer
# otherwise returns one variable-length id list per prompt and torch can't
# stack them into a batch ("excessive nesting... type list where int is
# expected"). truncation=True guards the rare very-long user-typed target.
inputs = processor(
text=list(prompts), images=[pil] * len(prompts), return_tensors="pt")
text=list(prompts), images=[pil] * len(prompts), return_tensors="pt",
padding=True, truncation=True)
logits = model(**inputs).logits # [P, 352, 352] (or [352, 352] for one prompt)
if logits.dim() == 2:
logits = logits[None]
+35
View File
@@ -99,6 +99,41 @@ def test_find_tube_enforces_minimum_edge():
assert (x1 - x0) >= detailing.MIN_TUBE_EDGE
# ---------------------------------------------------------------------------
# _clipseg_heat
# ---------------------------------------------------------------------------
def test_clipseg_heat_passes_padding_and_truncation(monkeypatch):
"""Regression: a multi-prompt call without padding=True crashes CLIPSeg's
tokenizer ("excessive nesting... type list where int is expected") the moment
detail_targets has more than one entry — caught live on a real rental run.
truncation=True is the same call, cheap to require alongside it."""
calls = []
class _FakeInputs(dict):
def to(self, *a, **k):
return self
class _FakeProcessor:
def __call__(self, text, images, return_tensors, **kwargs):
calls.append(kwargs)
return _FakeInputs()
class _FakeModel:
def __call__(self, **inputs):
n = 2
return types.SimpleNamespace(logits=torch.zeros(n, 8, 8))
def eval(self):
return self
monkeypatch.setattr(detailing, "_get_clipseg", lambda: (_FakeProcessor(), _FakeModel()))
frames = [(0, torch.zeros(16, 16, 3))]
detailing._clipseg_heat(frames, ["hands", "feet"])
assert calls and calls[0].get("padding") is True
assert calls[0].get("truncation") is True
# ---------------------------------------------------------------------------
# resolve_upsampler_name
# ---------------------------------------------------------------------------