diff --git a/CHANGELOG.md b/CHANGELOG.md index adbe101e5..a78ec0779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Change Log for SD.Next -## Update for 2026-09-09 +## Update for 2026-09-10 -### Highlights for 2026-09-09 +### Highlights for 2026-09-10 *What's New*? Well, code-wise, this is a big one... First, a-lot-of-optimizations: @@ -23,7 +23,7 @@ Plus inevitable bug-fixes... [Home](https://vladmandic.github.io/sdnext/) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic) -### Details for 2026-09-09 +### Details for 2026-09-10 - **Models** - [Anima 2.9B Preview v1](https://huggingface.co/yeoj34760/Anima-2.9B) @@ -109,24 +109,25 @@ Plus inevitable bug-fixes... - new articles: *Attention, Modular-Pipelines* - updated: *LoRA, MiniMax* - **Fixes** - - prompt: unnecessary secondary prompt if same - - ui: js fetch exception handling + - api: prompt enhance with vision + - compile: keep model compiled state - detailer: handling of stop/skip/pause - - rife: cleanup dead code, thanks @Anai-Guo + - log: ansi color handling + - lora: cleanup tags + - lucida: handle requirements - lumina-dimoo: attention-kwargs, thanks @Anai-Guo - network: improve type/version lookup - - lora: cleanup tags - - xyz grid: apply bool values - - vdm scheduler: fix steps, thanks @zjn20030811 - - openvino: optimize recompile checks and lora loading - - log: ansi color handling - - compile: keep model compiled state - - prompt: cache checks when cfg changes - - lucida: handle requirements - - vae: fetch scale factor from the model - - todo: remove dead code, thanks @Anai-Guo - offline: honor offline mode for more models, thanks @ryanmeador + - openvino: optimize recompile checks and lora loading - prompt enhance: cloud models use correct system prompt + - prompt: cache checks when cfg changes + - prompt: unnecessary secondary prompt if same + - rife: cleanup dead code, thanks @Anai-Guo + - todo: remove dead code, thanks @Anai-Guo + - ui: js fetch exception handling + - vae: fetch scale factor from the model + - vdm scheduler: fix steps, thanks @zjn20030811 + - xyz grid: apply bool values ## Update for 2026-08-26 diff --git a/cli/api-enhance.py b/cli/api-enhance.py index 0acb7d1ab..f84201375 100755 --- a/cli/api-enhance.py +++ b/cli/api-enhance.py @@ -59,6 +59,7 @@ def enhance(args): # pylint: disable=redefined-outer-name options['model'] = str(args.model) if args.image: options['image'] = encode(args.image) + options['use_vision'] = True response = post('/sdapi/v1/prompt-enhance', options) return response @@ -72,6 +73,6 @@ if __name__ == "__main__": parser.add_argument('--image', type=str, default=None, required=False, help='optional input image') parser.add_argument('--nsfw', type=bool, action=argparse.BooleanOptionalAction, required=False, help='nsfw allowed') args = parser.parse_args() - log.info(f'api-upscale: {args}') + log.info(f'api-enhance: {args}') result = enhance(args) log.info(result) diff --git a/modules/api/helpers.py b/modules/api/helpers.py index f5087e5e8..f3db5de09 100644 --- a/modules/api/helpers.py +++ b/modules/api/helpers.py @@ -46,6 +46,7 @@ def decode_base64_to_image(encoding, quiet=False): decoded = base64.b64decode(encoding) data = io.BytesIO(decoded) image = Image.open(data) + image = image.convert('RGB') return image except Exception as e: log.warning(f'API cannot decode image: {e}') diff --git a/modules/api/models.py b/modules/api/models.py index eeb36e9ae..fb44567a1 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -366,11 +366,11 @@ class ReqPromptEnhance(BaseModel): repetition_penalty: Optional[float] = Field(title="Repetition penalty", default=None, description="Penalizes repeated tokens to reduce repetition (1.0=no penalty)") top_k: Optional[int] = Field(title="Top K", default=None, description="Limits token selection to the K most likely candidates") top_p: Optional[float] = Field(title="Top P", default=None, description="Nucleus sampling threshold (0-1)") - thinking: bool = Field(title="Thinking", default=False, description="Enable thinking/reasoning mode") - keep_thinking: bool = Field(title="Keep thinking", default=False, description="Keep thinking tokens in output") - use_vision: bool = Field(title="Use vision", default=True, description="Use vision if model supports it") + thinking: Optional[bool] = Field(title="Thinking", default=False, description="Enable thinking/reasoning mode") + keep_thinking: Optional[bool] = Field(title="Keep thinking", default=False, description="Keep thinking tokens in output") + use_vision: Optional[bool] = Field(title="Use vision", default=True, description="Use vision if model supports it") prefill: Optional[str] = Field(title="Prefill", default=None, description="Text to prefill the model response with") - keep_prefill: bool = Field(title="Keep prefill", default=False, description="Keep prefill text in the output") + keep_prefill: Optional[bool] = Field(title="Keep prefill", default=False, description="Keep prefill text in the output") custom_args: Optional[str] = Field(title="Custom args", default=None, description="Custom arguments for the model") process_words: Optional[str] = Field(title="Banned words", default=None, description="List of words to process") semantic_threshold: Optional[float] = Field(title="Semantic threshold", default=None, description="Semantic similarity threshold for processed words") diff --git a/modules/api/process.py b/modules/api/process.py index 19b712ad6..c25da7cca 100644 --- a/modules/api/process.py +++ b/modules/api/process.py @@ -226,6 +226,7 @@ class APIProcess: if len(instance) == 0: raise HTTPException(status_code=500, detail="Prompt enhancement script not found") instance = instance[0] + decoded = decode_base64_to_image(req.image) if req.image else None prompt = instance.enhance( model=model, prompt=req.prompt, @@ -244,7 +245,7 @@ class APIProcess: use_vision=req.use_vision, prefill=req.prefill or '', keep_prefill=req.keep_prefill, - image=decode_base64_to_image(req.image) if req.image else None, + image=decoded, seed=seed, nsfw=req.nsfw, custom_args=req.custom_args, diff --git a/scripts/prompt_enhance/options.py b/scripts/prompt_enhance/options.py index 4f7bef4b2..785b2af80 100644 --- a/scripts/prompt_enhance/options.py +++ b/scripts/prompt_enhance/options.py @@ -144,6 +144,12 @@ class Options: Keep the result faithful to the original prompt and the intended image. Do not add unrelated concepts, non-visual commentary, or fluff. ''') + t2i_noprompt: str = textwrap.dedent('''\ + You are an expert AI image prompt engineer. + Create an original, imaginative and visually rich prompt for image generation. + Be Creative! + Use concrete, image-generation language and avoid unrelated fluff or commentary. + ''') i2i_prompt: str = textwrap.dedent('''\ You are an expert AI image prompt engineer. You will receive an image and a user prompt for editing or refinement. @@ -159,6 +165,7 @@ class Options: Base the prompt on what is present in the image, its style, and its atmosphere. Use concrete, image-generation language and avoid unrelated fluff or commentary. ''') + t2v_prompt: str = textwrap.dedent('''\ You are an expert AI video prompt engineer. You will receive a user prompt for video generation. @@ -185,6 +192,7 @@ class Options: Specify subject and camera movement, scene transitions, and temporal dynamics. Use concrete, video-generation language and avoid unrelated fluff or commentary. ''') + nsfw_ok: str = textwrap.dedent('''\ NSFW, nudity and explicit content and language are allowed. ''') diff --git a/scripts/prompt_enhance/template.py b/scripts/prompt_enhance/template.py index b59154f53..e800cd834 100644 --- a/scripts/prompt_enhance/template.py +++ b/scripts/prompt_enhance/template.py @@ -85,15 +85,16 @@ def set_template( has_image = image is not None and isinstance(image, Image.Image) is_video = module == 'video' - debug_log(f'Prompt enhance template: module={module} prompt={has_prompt} image={has_image} video={is_video} model="{model}" nsfw={nsfw} processor={has_processor}') + debug_log(f'Prompt enhance template: module={module} prompt={has_prompt} image={has_image} video={is_video} model="{model}" nsfw={nsfw} processor={has_processor} cloud={is_cloud_model(model)}') + """ if has_image: if is_cloud_model(model): pass - elif options.processor is None: + elif not has_processor: log.error('Prompt enhance: image not supported by model') return prompt if prompt is not None else '' # Return original text part if image cannot be processed - + """ if has_image: chat_template = get_image_template(system, prompt, options, nsfw, has_prompt, has_processor, is_video, image) else: diff --git a/scripts/prompt_enhance_ext.py b/scripts/prompt_enhance_ext.py index 9c11545d5..1386f38bc 100644 --- a/scripts/prompt_enhance_ext.py +++ b/scripts/prompt_enhance_ext.py @@ -325,11 +325,8 @@ class PromptEnhanceScript(scripts_manager.Script): # Strip symbols from model name if present model = get_model_repo_from_display(model) if model else self.options.default prompt = prompt or (self.prompt.value if self.prompt else "") # Check if self.prompt is None - image = None if use_vision and is_vision_model(model): # handle vision toggle image = image or self.image - if image is None: - use_vision = False prefix = prefix or '' suffix = suffix or '' min_tokens = min_tokens or self.options.min_tokens @@ -363,7 +360,9 @@ class PromptEnhanceScript(scripts_manager.Script): # Only process images if vision is enabled and model supports it if use_vision and is_vision_model(model): current_image = self.get_image(image) - debug_log(f'Prompt enhance: image={current_image}') + if current_image is None: + use_vision = False + debug_log(f'Prompt enhance: image={current_image} use_vision={use_vision}') # Check if vision was requested but no image is available if use_vision and is_vision_model(model) and current_image is None: diff --git a/ui/startup.ts b/ui/startup.ts index 65d506c2f..3fb1e7f32 100644 --- a/ui/startup.ts +++ b/ui/startup.ts @@ -35,9 +35,6 @@ async function waitForOpts() { const t0 = performance.now(); let t1 = performance.now(); while (true) { - if (t1 - t0 > 15000) { - log('waitForOpts delayed', t1 - t0); - } if (t1 - t0 > 60000) { log('waitForOpts timeout'); break; @@ -50,6 +47,9 @@ async function waitForOpts() { break; } } + if (t1 - t0 > 15000) { + log('waitForOpts delayed', Math.round(t1 - t0)); + } await sleep(100); t1 = performance.now(); }