From fea745deb9848fb073822236f1adf771066763e6 Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Fri, 3 Apr 2026 08:20:20 -0700 Subject: [PATCH] fix: use example prompts instead of trigger words, serve LoRA preview images (#52) - Prompt text now uses civitai example prompts (images[].meta.prompt) when available, falling back to model name instead of trigger words - Image serving now allows paths within LoRA directories when the integration is enabled, fixing 403 errors on preview images --- py/api/images.py | 23 +++++++++++++++++++---- py/api/lora_integration.py | 6 ++++-- py/lora_utils.py | 18 ++++++++++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/py/api/images.py b/py/api/images.py index 0acdb4ef..47c3fca5 100644 --- a/py/api/images.py +++ b/py/api/images.py @@ -303,11 +303,26 @@ class ImageRoutesMixin: image_path = Path(image["image_path"]).resolve() - # Validate path is within any configured output directory - output_dirs = self._get_all_output_dirs() - if output_dirs: + # Validate path is within any allowed directory + allowed_dirs = list(self._get_all_output_dirs()) + + # Also allow LoRA directories when integration is enabled + try: + from ..config import IntegrationConfig + + if IntegrationConfig.LORA_MANAGER_ENABLED: + from ..lora_utils import find_lora_directories + + lora_dirs = find_lora_directories( + IntegrationConfig.LORA_MANAGER_PATH + ) + allowed_dirs.extend(Path(d) for d in lora_dirs) + except Exception: + pass + + if allowed_dirs: allowed = any( - image_path.is_relative_to(d.resolve()) for d in output_dirs + image_path.is_relative_to(d.resolve()) for d in allowed_dirs ) if not allowed: return web.json_response( diff --git a/py/api/lora_integration.py b/py/api/lora_integration.py index f9d0e4ff..942012cc 100644 --- a/py/api/lora_integration.py +++ b/py/api/lora_integration.py @@ -154,6 +154,7 @@ class LoraIntegrationMixin: from ..config import IntegrationConfig from ..lora_utils import ( find_lora_directories, + get_example_prompt_from_metadata, get_preview_image_from_metadata, get_trigger_words_from_metadata, get_model_name_from_metadata, @@ -228,8 +229,9 @@ class LoraIntegrationMixin: get_preview_image_from_metadata, metadata, meta_file ) - # Build prompt text from trigger words or model name - prompt_text = ", ".join(trigger_words) if trigger_words else model_name + # Build prompt text: prefer example prompt, then model name + example_prompt = get_example_prompt_from_metadata(metadata) + prompt_text = example_prompt or model_name # Build tags tags = ["lora-manager", f"lora:{model_name}"] diff --git a/py/lora_utils.py b/py/lora_utils.py index feafb17f..bdc2b4a0 100644 --- a/py/lora_utils.py +++ b/py/lora_utils.py @@ -208,6 +208,24 @@ def get_trigger_words_from_metadata(metadata: Dict) -> List[str]: return [] +def get_example_prompt_from_metadata(metadata: Dict) -> Optional[str]: + """Extract an example prompt from civitai image metadata. + + Looks at civitai.images[].meta.prompt for the first available example. + """ + civitai = metadata.get("civitai", {}) + images = civitai.get("images", []) + for img in images: + if not isinstance(img, dict): + continue + meta = img.get("meta") + if isinstance(meta, dict): + prompt = meta.get("prompt", "") + if isinstance(prompt, str) and prompt.strip(): + return prompt.strip() + return None + + def get_model_name_from_metadata(metadata: Dict) -> str: """Extract the model display name from metadata.""" # Try civitai model name first, then file_name, then fallback