-
Stefy Lanza (nextime / spora ) authored
Verified end to end on real hardware: same prompt, same seed, mean absolute pixel difference 46/255 between a generation with a character profile and one without. Before this, that difference was 0 — the profile changed nothing. IP-Adapter had never once loaded successfully. Four separate blockers, each hidden behind the previous one, all found by running it rather than reading it: 1. Attention slicing (71f88d14). diffusers' adapter loader rebuilds every attention processor; re-instantiating SlicedAttnProcessor without slice_size raised, the failure was caught and logged as a warning, and generation continued without conditioning. Slicing is now off across the load. 2. Quantized dtype cast. _load_ip_adapter_weights() ends with self.to(dtype=self.dtype) on the denoiser — a NO-OP cast to the dtype it already has — but bitsandbytes refuses any dtype argument on a quantized model. Tolerated for the duration of the load. 3. That tolerance then broke the forward pass: the suppressed cast was what brought the NEWLY added IP-Adapter modules up to the model dtype, so they stayed fp16 against a bf16 pipeline ("expected mat1 and mat2 to have the same dtype: BFloat16 != Half"). We now perform that cast ourselves on the non-quantized modules only — 72 of them, confirmed in the log. 4. Reference-image shape. A character profile carries several images, but diffusers matches the OUTER list against the number of ADAPTERS, not images: "Got 4 images and 1 IP Adapters". Multiple images for one adapter must be nested one level deeper. New shared helper ip_adapter_image_arg(), used by both the image and video paths. Also fixes a real production bug found while testing: pipelines are cached, and once an adapter is attached the UNet config demands image_embeds forever after, so the NEXT request without character references 500s with "...encoder_hid_dim_type set to 'ip_image_proj' which requires the keyword argument image_embeds". The adapter is now detached when a request doesn't need it (with slicing kept off across the unload too, which trips the same constructor). Verified lifecycle on one cached pipeline: with-profile -> without -> with again, all 200, and the two with-profile results are bit-identical (0.0 diff), so unload/reload restores conditioning exactly. Co-Authored-By:
Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R6iugoNeEgt9PkajKrFyq4
8aaf7e8b