fix some caching related issues

This commit is contained in:
silveroxides
2025-11-25 21:28:09 +01:00
parent d8c2380292
commit caa97c15f1
+92 -32
View File
@@ -19,6 +19,7 @@ import sys
import importlib
import subprocess
import random
import hashlib
from typing import Any, Tuple
def check_and_install_dependencies():
@@ -151,6 +152,22 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
GemConfig = io.Custom("GEMINI_CONFIG")
_cache: dict = {}
# Define model lists centrally to ensure consistency between cache logic and execution logic
THINKING_MODELS = [
"gemini-2.0-flash-thinking-exp", "gemini-2.0-flash-thinking-exp-01-21",
"gemini-2.0-flash-thinking-exp-1219", "gemini-2.5-pro",
"gemini-2.5-pro-preview-05-06", "gemini-2.5-flash",
"gemini-2.5-flash-preview-09-2025",
"gemini-3-pro-preview"
]
IMAGE_MODELS = ["gemini-2.5-flash-image-preview", "gemini-3-pro-image-preview"]
MEDIA_RES_MODELS = [
"gemini-2.0-flash-thinking-exp", "gemini-2.0-flash-thinking-exp-01-21",
"gemini-2.0-flash-thinking-exp-1219", "gemini-2.5-pro",
"gemini-2.5-pro-preview-05-06", "gemini-2.5-flash",
"gemini-3-pro-preview"
]
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
@@ -161,7 +178,7 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
cls.GemConfig.Input("config"),
io.String.Input("prompt", multiline=True),
io.String.Input("system_instruction", default="You are a helpful AI assistant.", multiline=True),
io.Combo.Input("model", options=["learnlm-2.0-flash-experimental", "gemini-exp-1206", "gemini-2.0-flash", "gemini-2.0-flash-lite-001", "gemini-2.0-flash-exp", "gemini-2.0-flash-thinking-exp", "gemini-2.0-flash-thinking-exp-01-21", "gemini-2.0-flash-thinking-exp-1219", "gemini-2.5-pro", "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash", "gemini-2.5-flash-preview-09-2025", "gemini-2.5-flash-lite-preview-06-17", "gemini-3-pro-preview", "gemini-2.5-flash-image-preview"], default="gemini-2.0-flash"),
io.Combo.Input("model", options=["learnlm-2.0-flash-experimental", "gemini-exp-1206", "gemini-2.0-flash", "gemini-2.0-flash-lite-001", "gemini-2.0-flash-exp", "gemini-2.0-flash-thinking-exp", "gemini-2.0-flash-thinking-exp-01-21", "gemini-2.0-flash-thinking-exp-1219", "gemini-2.5-pro", "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash", "gemini-2.5-flash-preview-09-2025", "gemini-3-pro-preview", "gemini-2.5-flash-image-preview"], default="gemini-2.0-flash"),
io.Float.Input("temperature", default=1.0, min=0.0, max=1.0, step=0.01),
io.Float.Input("top_p", default=0.95, min=0.0, max=1.0, step=0.01),
io.Int.Input("top_k", default=40, min=1, max=100, step=1),
@@ -196,7 +213,7 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
patternperiod = r"\."
patternspace = r"\s"
patterncomma = r","
patterncomma = r","
patterndash = r"\-"
patternsingq = r"\'"
patterndoubq = r'\"'
@@ -250,7 +267,51 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
@classmethod
def _compute_fingerprint_and_check_cache(cls, config, prompt, system_instruction, model, temperature, top_p, top_k, max_output_tokens,
include_images, aspect_ratio, bypass_mode, thinking_budget, use_seed, seed,
input_image=None, input_image_2=None):
input_image=None, input_image_2=None,
use_proxy=False, proxy_host="127.0.0.1", proxy_port=7890, timeout=30,
include_thoughts=False, thinking_level=None, media_resolution=None):
# 1. Hashing Images
def get_tensor_hash(tensor):
if tensor is None:
return "None"
try:
return hashlib.sha256(np.ascontiguousarray(tensor.cpu().numpy()).tobytes()).hexdigest()
except Exception as e:
print(f"[WARNING] Cache hashing failed for image: {e}")
return "Error"
image_1_hash = get_tensor_hash(input_image)
image_2_hash = get_tensor_hash(input_image_2)
# 2. Determine Effective Parameters based on Model
# This ensures we don't cache-miss if an irrelevant parameter changes
# Defaults
eff_include_images = False
eff_aspect_ratio = "None"
eff_thinking_level = "None"
eff_thinking_budget = -1
eff_include_thoughts = False
# Logic mirroring _build_generate_content_config exactly
if include_images and model in cls.IMAGE_MODELS:
eff_include_images = True
eff_aspect_ratio = str(aspect_ratio)
# When generating images, thinking params are ignored
elif model == "gemini-3-pro-preview" and thinking_level is not None and thinking_level != "None":
eff_thinking_level = thinking_level
eff_include_thoughts = include_thoughts
# Budget is ignored in this specific branch
elif model in cls.THINKING_MODELS:
eff_thinking_budget = int(thinking_budget)
eff_include_thoughts = include_thoughts
# Thinking level is ignored in this branch
# If none of the above, all effective thinking/image params remain default/ignored
fingerprint = (
str(config),
prompt,
@@ -260,12 +321,21 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
float(top_p),
int(top_k),
int(max_output_tokens),
bool(include_images),
str(aspect_ratio),
eff_include_images, # EFFECTIVE include_images
eff_aspect_ratio, # EFFECTIVE aspect_ratio
str(bypass_mode),
int(thinking_budget),
eff_thinking_budget, # EFFECTIVE thinking_budget
use_seed,
int(seed)
int(seed) if use_seed else 0, # Only use seed in cache if use_seed is True
image_1_hash,
image_2_hash,
bool(use_proxy),
str(proxy_host),
int(proxy_port),
int(timeout),
eff_include_thoughts, # EFFECTIVE include_thoughts
eff_thinking_level, # EFFECTIVE thinking_level
str(media_resolution)
)
cached = cls._cache.get(fingerprint)
@@ -325,7 +395,8 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
types.SafetySetting(category="HARM_CATEGORY_CIVIC_INTEGRITY", threshold="BLOCK_NONE"), # type: ignore
]
if include_images:
# Modified: Only trigger image config if include_images is True AND the model is actually an image model
if include_images and model in cls.IMAGE_MODELS:
return types.GenerateContentConfig(
temperature=temperature,
top_p=top_p,
@@ -338,7 +409,8 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
system_instruction=[types.Part.from_text(text=padded_system_instruction)],
)
if model == "gemini-3-pro-preview" and thinking_level is not None:
# Modified: Added check for thinking_level != "None"
if model == "gemini-3-pro-preview" and thinking_level is not None and thinking_level != "None":
return types.GenerateContentConfig(
temperature=temperature,
top_p=top_p,
@@ -350,10 +422,7 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
system_instruction=[types.Part.from_text(text=padded_system_instruction)],
)
if model in [
"gemini-2.0-flash-thinking-exp", "gemini-2.0-flash-thinking-exp-01-21", "gemini-2.0-flash-thinking-exp-1219",
"gemini-2.5-pro", "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash", "gemini-2.5-flash-preview-09-2025",
"gemini-2.5-flash-lite-preview-06-17", "gemini-3-pro-preview"]:
if model in cls.THINKING_MODELS:
return types.GenerateContentConfig(
temperature=temperature,
top_p=top_p,
@@ -381,35 +450,24 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
use_proxy=False, proxy_host="127.0.0.1", proxy_port=7890, use_seed=False, seed=0, timeout=30,
include_thoughts=False, thinking_level=None, media_resolution=None) -> io.NodeOutput:
def tensor_equal(t1, t2):
if t1 is None and t2 is None:
return True
if t1 is not None and t2 is not None:
try:
return torch.equal(t1, t2)
except Exception:
return False
return False
fingerprint, cached = cls._compute_fingerprint_and_check_cache(
config, prompt, system_instruction, model, temperature, top_p, top_k, max_output_tokens,
include_images, aspect_ratio, bypass_mode, thinking_budget, use_seed, seed, input_image, input_image_2
include_images, aspect_ratio, bypass_mode, thinking_budget, use_seed, seed,
input_image, input_image_2,
use_proxy, proxy_host, proxy_port, timeout,
include_thoughts, thinking_level, media_resolution
)
if cached is not None:
cached_text, cached_image, cached_seed = cached
if tensor_equal(cached_image, input_image) or tensor_equal(cached_image, input_image_2):
print(f"[INFO] Returning cached result for fingerprint {fingerprint}")
return io.NodeOutput(cached_text, cached_image, cached_seed)
print(f"[INFO] Returning cached result for fingerprint {fingerprint}")
return io.NodeOutput(cached_text, cached_image, cached_seed)
# --- keep most of the original implementation but converted to classmethod usage ---
original_http_proxy = os.environ.get('HTTP_PROXY')
original_https_proxy = os.environ.get('HTTPS_PROXY')
original_http_proxy_lower = os.environ.get('http_proxy')
original_https_proxy_lower = os.environ.get('https_proxy')
thinking_models = ["gemini-2.0-flash-thinking-exp", "gemini-2.0-flash-thinking-exp-01-21", "gemini-2.0-flash-thinking-exp-1219", "gemini-2.5-pro", "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash", "gemini-2.5-flash-preview-09-2025", "gemini-2.5-flash-lite-preview-06-17", "gemini-3-pro-preview"]
media_res_models = ["gemini-2.0-flash-thinking-exp", "gemini-2.0-flash-thinking-exp-01-21", "gemini-2.0-flash-thinking-exp-1219", "gemini-2.5-pro", "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash", "gemini-2.5-flash-preview-09-2025", "gemini-2.5-flash-lite-preview-06-17", "gemini-3-pro-preview"]
thinking_levels = ["low", "medium", "high"]
print(f"[INFO] Starting generation, model: {model}, temperature: {temperature}")
@@ -519,7 +577,7 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
img_byte_arr = stdlib_io.BytesIO()
pil_img.save(img_byte_arr, format='PNG')
img_bytes = img_byte_arr.getvalue()
if model in media_res_models and media_resolution is not None:
if model in cls.MEDIA_RES_MODELS and media_resolution is not None and media_resolution != "unspecified":
img_part = {"inline_data": {"mime_type": "image/png", "data": img_bytes}, "media_resolution": {"level": f"MEDIA_RESOLUTION_{media_resolution.upper()}"}}
else:
img_part = {"inline_data": {"mime_type": "image/png", "data": img_bytes}}
@@ -531,7 +589,9 @@ class SSL_GeminiTextPrompt(io.ComfyNode):
else:
contents = padded_prompt
response_modalities = ["IMAGE", "TEXT"] if include_images else ["TEXT"]
# Logic Update: Only request IMAGE modality if include_images is TRUE AND it's the correct model
# Otherwise we stick to TEXT modality
response_modalities = ["IMAGE", "TEXT"] if (include_images and model in cls.IMAGE_MODELS) else ["TEXT"]
generate_content_config = cls._build_generate_content_config(
model=model,