diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6a633e8..08582e5b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ All-about-optimizations: - openvino: optimize recompile checks and lora loading - log: ansi color handling - compile: keep model compiled state + - prompt: cache checks when cfg changes ## Update for 2026-08-26 diff --git a/modules/sd_hijack_te.py b/modules/sd_hijack_te.py index 06826cd33..ccb6c7eec 100644 --- a/modules/sd_hijack_te.py +++ b/modules/sd_hijack_te.py @@ -11,29 +11,39 @@ class PromptCache: self.id = None self.max = 16 - def get(self, prompt): + @staticmethod + def _hashable(val): + if isinstance(val, list): + return tuple(val) + return val + + def get(self, prompt, negative_prompt=None, cfg_enabled=None): if self.id != id(shared.sd_model): self.cache.clear() self.id = id(shared.sd_model) log.debug(f'Encode: prompt cache activate id={self.id} depth={len(self.cache)}') + prompt = self._hashable(prompt) + negative_prompt = self._hashable(negative_prompt) if (isinstance(prompt, list) and len(prompt) == 1 and isinstance(prompt[0], str)): - cached = self.cache.get(prompt[0], None) + cached = self.cache.get((prompt[0], negative_prompt, cfg_enabled), None) elif isinstance(prompt, str): - cached = self.cache.get(prompt, None) + cached = self.cache.get((prompt, negative_prompt, cfg_enabled), None) else: cached = None if cached: log.debug(f'Encode: prompt="{prompt}" cache={len(self.cache)} hit') return cached - def set(self, prompt, encoded): + def set(self, prompt, encoded, negative_prompt=None, cfg_enabled=None): if len(self.cache) >= self.max: oldest_key = next(iter(self.cache)) del self.cache[oldest_key] + prompt = self._hashable(prompt) + negative_prompt = self._hashable(negative_prompt) if (isinstance(prompt, list) and len(prompt) == 1 and isinstance(prompt[0], str)): - self.cache[prompt[0]] = encoded + self.cache[(prompt[0], negative_prompt, cfg_enabled)] = encoded elif isinstance(prompt, str): - self.cache[prompt] = encoded + self.cache[(prompt, negative_prompt, cfg_enabled)] = encoded prompt_cache = PromptCache() @@ -61,7 +71,10 @@ def hijack_encode_prompt(*args, **kwargs): if patch_prompt: args_copy[0] = res - cached = prompt_cache.get(prompt) + # cache key must include cfg-affecting kwargs since encode_prompt output (e.g. negative_prompt_embeds) depends on them + negative_prompt = kwargs.get('negative_prompt', None) + cfg_enabled = kwargs.get('do_classifier_free_guidance', None) + cached = prompt_cache.get(prompt, negative_prompt, cfg_enabled) if cached is not None: res = cached else: @@ -71,7 +84,7 @@ def hijack_encode_prompt(*args, **kwargs): res = shared.sd_model.orig_encode_prompt(*args_copy, **kwargs) else: res = shared.sd_model.encode_prompt(*args_copy, **kwargs) - prompt_cache.set(prompt, res) + prompt_cache.set(prompt, res, negative_prompt, cfg_enabled) if hasattr(shared.sd_model, 'after_prompt_encode'): log.debug(f'Encode: prompt="{prompt}" op=after')