• Stefy Lanza (nextime / spora )'s avatar
    images: make IP-Adapter character conditioning actually work · 8aaf7e8b
    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: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01R6iugoNeEgt9PkajKrFyq4
    8aaf7e8b
images.py 117 KB