From 1fb2a64b5649c32862994ee5dffc080f103265c8 Mon Sep 17 00:00:00 2001 From: Victor Kuznetsov Date: Mon, 8 Jun 2026 16:18:52 -0700 Subject: [PATCH] fix(photomaker): pass pm_version='v1' to load_photomaker_adapter Modal cert sweep #3 ran past the `insightface` import error and into a real state_dict mismatch: Error(s) in loading state_dict for PhotoMakerIDEncoder_CLIPInsightfaceExtendtoken: Missing key(s) ... qformer_perceiver.token_proj.0.weight ... The upstream `load_photomaker_adapter` defaults to `pm_version='v2'` regardless of the .bin file passed -- the loader builds a V2 encoder (PhotoMakerIDEncoder_CLIPInsightfaceExtendtoken) and then tries to load V1 weights into it. We must pass `pm_version='v1'` explicitly so the loader instantiates the CLIP-only PhotoMakerIDEncoder. The pipeline-level `input_id_images` API is the same across V1 and V2, so the call site does not change. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/remove_ai_watermarks/photomaker_restore.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/remove_ai_watermarks/photomaker_restore.py b/src/remove_ai_watermarks/photomaker_restore.py index b82cb25..6b6a466 100644 --- a/src/remove_ai_watermarks/photomaker_restore.py +++ b/src/remove_ai_watermarks/photomaker_restore.py @@ -151,11 +151,17 @@ def _get_pipeline() -> Any: ) adapter_path = hf_hub_download(repo_id=_PHOTOMAKER_REPO, filename=_PHOTOMAKER_FILE) pipe = PhotoMakerStableDiffusionXLPipeline.from_pretrained(_SDXL_MODEL_ID, torch_dtype=dtype) + # ``pm_version="v1"`` is REQUIRED: the upstream loader defaults to v2 and would + # build the V2 encoder (PhotoMakerIDEncoder_CLIPInsightfaceExtendtoken), then + # error on load_state_dict because the v1 weights have a different shape. + # Passing v1 builds the CLIP-only PhotoMakerIDEncoder, which is the + # commercial-safe path we want. pipe.load_photomaker_adapter( str(Path(adapter_path).parent), subfolder="", weight_name=_PHOTOMAKER_FILE, trigger_word="img", + pm_version="v1", ) pipe.to(device) pipe.fuse_lora()