Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/diffusers/loaders/lora_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,14 @@ def _load_lora_into_text_encoder(
# convert state dict
state_dict = convert_state_dict_to_peft(state_dict)

# Under transformers>=5, CLIPTextModel was flattened and no longer has the
# `text_model.` wrapper module, so named_modules() returns unprefixed names
# like "encoder.layers.0.self_attn.q_proj". Kohya-sourced LoRA state dict
# keys still carry the "text_model." prefix from the old module layout.
# Strip it so that rank-key matching works regardless of the transformers version.
if not hasattr(text_encoder, "text_model"):
state_dict = {k.removeprefix("text_model."): v for k, v in state_dict.items()}

for name, _ in text_encoder.named_modules():
if name.endswith((".q_proj", ".k_proj", ".v_proj", ".out_proj", ".fc1", ".fc2")):
rank_key = f"{name}.lora_B.weight"
Expand Down
18 changes: 18 additions & 0 deletions tests/pipelines/flux/test_pipeline_flux.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,24 @@ class TestFluxPipelineMagCache(FluxPipelineTesterConfig, MagCacheTesterMixin):
class TestFluxPipelineLoRA(FluxPipelineTesterConfig, LoraTesterMixin):
"""LoRA tests for the Flux pipeline."""

def test_kohya_clip_text_encoder_lora_under_flattened_clip(self):
# Regression for huggingface/diffusers#13984: transformers >= 5.6 removed the
# `text_model.` wrapper from CLIPTextModel, but kohya-converted state dicts still
# carry that prefix, which left the rank dict empty and raised IndexError.
pipe = self.get_pipeline().to(torch_device)
if hasattr(pipe.text_encoder, "text_model"):
pytest.skip("transformers still exposes the text_model wrapper")

state_dict = {}
for name, module in pipe.text_encoder.named_modules():
if name.endswith((".q_proj", ".k_proj", ".v_proj", ".out_proj", ".fc1", ".fc2")):
state_dict[f"text_encoder.text_model.{name}.lora_A.weight"] = torch.randn(2, module.in_features)
state_dict[f"text_encoder.text_model.{name}.lora_B.weight"] = torch.randn(module.out_features, 2)

pipe.load_lora_weights(state_dict)
assert check_if_lora_correctly_set(pipe.text_encoder), "Lora not correctly set in text encoder"
pipe.unload_lora_weights()

def test_with_alpha_in_state_dict(self, tmp_path):
pipe = self.get_pipeline().to(torch_device)
self.add_adapters_to_pipeline(pipe, components=["transformer"])
Expand Down
Loading