Add Gemini 4 Carbon and Hydrogen support

This commit is contained in:
silveroxides
2026-11-13 01:07:19 +01:00
parent ead498a4f0
commit f29cdd0290
6 changed files with 309 additions and 30 deletions
+50 -17
View File
@@ -249,7 +249,16 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
GEMINI_3_8_FLASH = "gemini-3.8-flash"
GEMINI_4_ARGON = "gemini-4-argon"
GEMINI_4_ARGON_CYBER_PERMISSIVE = "gemini-4-argon-cyber-permissive"
GEMINI_4_CARBON = "gemini-4-carbon"
GEMINI_4_CARBON_CYBER_PERMISSIVE = "gemini-4-carbon-cyber-permissive"
GEMINI_4_HYDROGEN = "gemini-4-hydrogen"
ARGON_MODELS = (GEMINI_4_ARGON, GEMINI_4_ARGON_CYBER_PERMISSIVE)
GEMINI_4_MODELS = (
*ARGON_MODELS,
GEMINI_4_CARBON,
GEMINI_4_CARBON_CYBER_PERMISSIVE,
GEMINI_4_HYDROGEN,
)
VIDEO_MIME_TYPE = "video/mp4"
VIDEO_INLINE_LIMIT_BYTES = 100 * 1024 * 1024
VERTEX_CACHE_INLINE_LIMIT_BYTES = 10 * 1024 * 1024
@@ -263,11 +272,11 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
THINKING_MODELS = [
"gemini-1.5-pro-002", "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-flash", "gemini-2.5-flash-preview-04-17", "gemini-2.5-pro-exp-03-25",
"gemini-3-flash-preview", "gemini-3.1-pro-preview", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.6-flash", GEMINI_3_7_FLASH, GEMINI_3_8_FLASH, GEMINI_4_ARGON, GEMINI_4_ARGON_CYBER_PERMISSIVE, GEMINI_4_FLASH_PREVIEW, "gemini-pro-latest", "gemini-flash-latest", "gemini-flash-lite-latest"
"gemini-3-flash-preview", "gemini-3.1-pro-preview", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.6-flash", GEMINI_3_7_FLASH, GEMINI_3_8_FLASH, GEMINI_4_ARGON, GEMINI_4_ARGON_CYBER_PERMISSIVE, GEMINI_4_CARBON, GEMINI_4_CARBON_CYBER_PERMISSIVE, GEMINI_4_HYDROGEN, GEMINI_4_FLASH_PREVIEW, "gemini-pro-latest", "gemini-flash-latest", "gemini-flash-lite-latest"
]
GEN3_THINKING_MODELS = [
"gemini-pro-latest", "gemini-flash-latest", "gemini-3.1-pro-preview",
"gemini-3-flash-preview", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.6-flash", GEMINI_3_7_FLASH, GEMINI_3_8_FLASH, GEMINI_4_ARGON, GEMINI_4_ARGON_CYBER_PERMISSIVE, GEMINI_4_FLASH_PREVIEW
"gemini-3-flash-preview", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.6-flash", GEMINI_3_7_FLASH, GEMINI_3_8_FLASH, GEMINI_4_ARGON, GEMINI_4_ARGON_CYBER_PERMISSIVE, GEMINI_4_CARBON, GEMINI_4_CARBON_CYBER_PERMISSIVE, GEMINI_4_HYDROGEN, GEMINI_4_FLASH_PREVIEW
]
IMAGE_MODELS = [
"gemini-2.5-flash-image",
@@ -281,7 +290,7 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
}
MEDIA_RES_MODELS = [
"gemini-3.1-flash-lite", "gemini-3-flash-preview", "gemini-3.1-pro-preview",
"gemini-3.5-flash", "gemini-pro-latest", "gemini-flash-latest", "gemini-flash-lite-latest"
"gemini-3.5-flash", GEMINI_4_CARBON, GEMINI_4_CARBON_CYBER_PERMISSIVE, GEMINI_4_HYDROGEN, "gemini-pro-latest", "gemini-flash-latest", "gemini-flash-lite-latest"
]
@classmethod
@@ -301,11 +310,11 @@ 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=["gemini-1.5-pro-002", "gemini-2.0-flash", "gemini-2.0-flash-lite", "gemini-2.5-flash-preview-04-17", "gemini-2.5-pro-exp-03-25", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", "gemini-3-flash-preview", "gemini-3.1-flash-lite", "gemini-3.1-pro-preview", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.6-flash", cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE, "gemini-3.1-flash-image", "gemini-3.1-flash-lite-image", "gemini-3-pro-image", "gemini-pro-latest", "gemini-flash-latest", "gemini-flash-lite-latest"], default="gemini-2.5-flash"),
IO.Combo.Input("model", options=["gemini-1.5-pro-002", "gemini-2.0-flash", "gemini-2.0-flash-lite", "gemini-2.5-flash-preview-04-17", "gemini-2.5-pro-exp-03-25", "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", "gemini-3-flash-preview", "gemini-3.1-flash-lite", "gemini-3.1-pro-preview", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.6-flash", cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE, cls.GEMINI_4_CARBON, cls.GEMINI_4_CARBON_CYBER_PERMISSIVE, cls.GEMINI_4_HYDROGEN, "gemini-3.1-flash-image", "gemini-3.1-flash-lite-image", "gemini-3-pro-image", "gemini-pro-latest", "gemini-flash-latest", "gemini-flash-lite-latest"], default="gemini-2.5-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),
IO.Int.Input("max_output_tokens", default=8192, min=1, max=65536, step=1),
IO.Int.Input("max_output_tokens", default=8192, min=1, max=1048576, step=1),
IO.Boolean.Input("include_images", default=False),
IO.Combo.Input("aspect_ratio", options=["None", "1:1", "9:16", "16:9", "3:4", "4:3", "3:2", "2:3", "5:4", "4:5", "21:9"], default="None"),
IO.Combo.Input("bypass_mode", options=["None", "system_instruction", "prompt", "both"], default="None"),
@@ -315,9 +324,9 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
IO.Int.Input("proxy_port", default=7890, min=1, max=65535),
IO.Boolean.Input("use_seed", default=True),
IO.Int.Input("seed", default=0, min=0, max=2147483647),
IO.Int.Input("timeout", default=30, min=15, max=300, step=15),
IO.Int.Input("timeout", default=30, min=15, max=1200, step=15),
IO.Boolean.Input("include_thoughts", default=False),
IO.Combo.Input("thinking_level", options=["None", "low", "medium", "high"], default="None", tooltip="Does not work at the same time as 'thinking_budget'. If this is set, then thinking budget is ignored. Gemini 3.7/3.8 Flash always uses this control, defaults to medium, and ignores thinking_budget."),
IO.Combo.Input("thinking_level", options=["None", "low", "medium", "high", "xhigh"], default="None", tooltip="Does not work at the same time as 'thinking_budget'. If this is set, then thinking budget is ignored. Gemini 3.7/3.8 Flash and Gemini 4 models always use this control and ignore thinking_budget. 'xhigh' is supported only by Gemini 4 Hydrogen."),
IO.Combo.Input("media_resolution", options=["unspecified", "low", "medium", "high"], default="unspecified", tooltip="Set input media resolution for image, video and pdf. This changes tokens consumed."),
IO.String.Input("retry_pattern", default="", optional=True, multiline=False, tooltip="Regex pattern to match in response text. If matched, retry with new seed. Leave empty to disable."),
IO.Int.Input("max_retries", default=3, min=0, max=10, step=1, tooltip="Maximum number of retry attempts when pattern matches. 0 disables retry."),
@@ -549,19 +558,34 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
@classmethod
def _resolve_gemini_3_7_thinking_level(cls, thinking_level, model_name=None):
if thinking_level is None or thinking_level == "None":
if model_name == cls.GEMINI_4_ARGON_CYBER_PERMISSIVE:
if model_name in (
cls.GEMINI_4_ARGON_CYBER_PERMISSIVE,
cls.GEMINI_4_CARBON_CYBER_PERMISSIVE,
cls.GEMINI_4_HYDROGEN,
):
return "high"
return "medium"
if thinking_level == "minimal":
if model_name in (cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE):
if model_name in (cls.GEMINI_3_8_FLASH, *cls.GEMINI_4_MODELS):
allowed = (
"low, medium, high, or xhigh"
if model_name == cls.GEMINI_4_HYDROGEN
else "low, medium, or high"
)
raise ValueError(
f"{model_name} thinking_level must be low, medium, or high."
f"{model_name} thinking_level must be {allowed}."
)
print(
f"[WARNING] {model_name or 'Gemini 3.7 Flash'} does not support minimal "
"thinking; using low instead."
)
return "low"
if thinking_level == "xhigh":
if model_name != cls.GEMINI_4_HYDROGEN:
raise ValueError(
f"{model_name or 'This model'} does not support xhigh thinking_level."
)
return "xhigh"
if thinking_level not in {"low", "medium", "high"}:
raise ValueError(
f"{model_name or 'Gemini 3.7 Flash'} thinking_level must be low, medium, or high."
@@ -660,9 +684,9 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
eff_thinking_level = "None"
eff_thinking_budget = -1
eff_include_thoughts = False
eff_temperature = None if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE) else float(temperature)
eff_top_p = None if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE) else float(top_p)
eff_top_k = None if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE) else int(top_k)
eff_temperature = None if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, *cls.GEMINI_4_MODELS) else float(temperature)
eff_top_p = None if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, *cls.GEMINI_4_MODELS) else float(top_p)
eff_top_k = None if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, *cls.GEMINI_4_MODELS) else int(top_k)
# Logic mirroring _build_generate_content_config exactly
if include_images and model in cls.IMAGE_MODELS:
@@ -671,11 +695,13 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
eff_image_size = "None" if image_size in (None, "None") else str(image_size)
# When generating images, thinking params are ignored
elif model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE):
elif model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, *cls.GEMINI_4_MODELS):
eff_thinking_level = cls._resolve_gemini_3_7_thinking_level(thinking_level, model)
eff_include_thoughts = include_thoughts
elif model in cls.GEN3_THINKING_MODELS and thinking_level is not None and thinking_level != "None":
if thinking_level == "xhigh":
raise ValueError(f"{model} does not support xhigh thinking_level.")
eff_thinking_level = thinking_level
eff_include_thoughts = include_thoughts
# Budget is ignored in this specific branch
@@ -922,7 +948,7 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
system_instruction=[types.Part.from_text(text=padded_system_instruction)],
)
if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE):
if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, *cls.GEMINI_4_MODELS):
return types.GenerateContentConfig(
max_output_tokens=max_output_tokens,
safety_settings=safety,
@@ -937,6 +963,8 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
G3Pro = ["gemini-3.1-pro-preview"]
# Modified: Added check for thinking_level != "None"
if model in G3Pro and thinking_level is not None and thinking_level != "None":
if thinking_level == "xhigh":
raise ValueError(f"{model} does not support xhigh thinking_level.")
return types.GenerateContentConfig(
temperature=temperature,
top_p=top_p,
@@ -1335,10 +1363,10 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
and (config.get("use_vertexai_env", False) or config.get("vertexai_express", False))
and context_inline_bytes > cls.VERTEX_CACHE_INLINE_LIMIT_BYTES
)
or (use_cache and model in cls.ARGON_MODELS)
or (use_cache and model in cls.GEMINI_4_MODELS)
)
if skip_context_cache:
if use_cache and model in cls.ARGON_MODELS:
if use_cache and model in cls.GEMINI_4_MODELS:
print(f"[WARNING] Explicit context cache skipped for {model} (implicit caching only); using full request")
else:
print("[WARNING] Vertex context cache skipped because inline cached media exceeds 10 MB; using full request")
@@ -1365,6 +1393,11 @@ class SSL_GeminiTextPrompt(IO.ComfyNode):
safety_settings=resolved_safety_settings,
)
if generate_content_config.http_options is None:
generate_content_config.http_options = types.HttpOptions(
timeout=max(1, int(float(timeout) * 1000))
)
if schema_snapshot is not None:
generate_content_config.response_mime_type = "application/json"
generate_content_config.response_json_schema = schema_snapshot