From 68fb8ccf94e2aa8fdc510a9bcb3fc5eefe27a42e Mon Sep 17 00:00:00 2001 From: silveroxides Date: Wed, 14 Oct 2026 03:01:12 +0200 Subject: [PATCH] Support Gemini 4 Argon models across GenerateContent and Interactions APIs --- gemini_nodes.py | 41 +++++++----- interactions_nodes.py | 19 ++++-- tests/test_gemini_nodes.py | 108 +++++++++++++++++++++++++++++++ tests/test_interactions_nodes.py | 45 +++++++++++++ 4 files changed, 191 insertions(+), 22 deletions(-) diff --git a/gemini_nodes.py b/gemini_nodes.py index ef18512..7954a4c 100644 --- a/gemini_nodes.py +++ b/gemini_nodes.py @@ -184,6 +184,9 @@ class SSL_GeminiTextPrompt(IO.ComfyNode): _context_cache_lock = threading.Lock() GEMINI_3_7_FLASH = "gemini-3.7-flash" GEMINI_3_8_FLASH = "gemini-3.8-flash" + GEMINI_4_ARGON = "gemini-4-argon" + GEMINI_4_ARGON_CYBER_PERMISSIVE = "gemini-4-argon-cyber-permissive" + ARGON_MODELS = (GEMINI_4_ARGON, GEMINI_4_ARGON_CYBER_PERMISSIVE) VIDEO_MIME_TYPE = "video/mp4" VIDEO_INLINE_LIMIT_BYTES = 100 * 1024 * 1024 VERTEX_CACHE_INLINE_LIMIT_BYTES = 10 * 1024 * 1024 @@ -197,11 +200,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_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_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_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_FLASH_PREVIEW ] IMAGE_MODELS = [ "gemini-2.5-flash-image", @@ -234,7 +237,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=["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, "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, "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), @@ -438,11 +441,13 @@ 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: + return "high" return "medium" if thinking_level == "minimal": - if model_name == cls.GEMINI_3_8_FLASH: + if model_name in (cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE): raise ValueError( - "gemini-3.8-flash thinking_level must be low, medium, or high." + f"{model_name} thinking_level must be low, medium, or high." ) print( f"[WARNING] {model_name or 'Gemini 3.7 Flash'} does not support minimal " @@ -488,9 +493,9 @@ class SSL_GeminiTextPrompt(IO.ComfyNode): eff_thinking_level = "None" eff_thinking_budget = -1 eff_include_thoughts = False - eff_temperature = None if model == cls.GEMINI_3_7_FLASH else float(temperature) - eff_top_p = None if model == cls.GEMINI_3_7_FLASH else float(top_p) - eff_top_k = None if model == cls.GEMINI_3_7_FLASH else int(top_k) + 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) # Logic mirroring _build_generate_content_config exactly if include_images and model in cls.IMAGE_MODELS: @@ -499,7 +504,7 @@ 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): + elif model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE): eff_thinking_level = cls._resolve_gemini_3_7_thinking_level(thinking_level, model) eff_include_thoughts = include_thoughts @@ -750,7 +755,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): + if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE): return types.GenerateContentConfig( max_output_tokens=max_output_tokens, safety_settings=safety, @@ -1097,13 +1102,19 @@ class SSL_GeminiTextPrompt(IO.ComfyNode): ) skip_context_cache = ( - use_cache - and video_bytes is not None - and (config.get("use_vertexai_env", False) or config.get("vertexai_express", False)) - and context_inline_bytes > cls.VERTEX_CACHE_INLINE_LIMIT_BYTES + ( + use_cache + and video_bytes is not None + 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) ) if skip_context_cache: - print("[WARNING] Vertex context cache skipped because inline cached media exceeds 10 MB; using full request") + if use_cache and model in cls.ARGON_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") generate_content_config = cls._build_generate_content_config( model=model, diff --git a/interactions_nodes.py b/interactions_nodes.py index 1ce893d..bbdeb68 100644 --- a/interactions_nodes.py +++ b/interactions_nodes.py @@ -262,6 +262,9 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode): _client_cache: dict = {} # Maps client_key tuple -> genai.Client instance GEMINI_3_7_FLASH = "gemini-3.7-flash" GEMINI_3_8_FLASH = "gemini-3.8-flash" + GEMINI_4_ARGON = "gemini-4-argon" + GEMINI_4_ARGON_CYBER_PERMISSIVE = "gemini-4-argon-cyber-permissive" + ARGON_MODELS = (GEMINI_4_ARGON, GEMINI_4_ARGON_CYBER_PERMISSIVE) VIDEO_MIME_TYPE = "video/mp4" VIDEO_INLINE_LIMIT_BYTES = 100 * 1024 * 1024 @@ -283,11 +286,11 @@ class SSL_GeminiInteractionsTextPrompt(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_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_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_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_FLASH_PREVIEW ] IMAGE_MODELS = [ "gemini-2.5-flash-image", @@ -320,7 +323,7 @@ class SSL_GeminiInteractionsTextPrompt(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, "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, "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, tooltip="Ignored in Interactions API"), IO.Float.Input("top_p", default=0.95, min=0.0, max=1.0, step=0.01, tooltip="Ignored in Interactions API"), IO.Int.Input("top_k", default=40, min=1, max=100, step=1, tooltip="Ignored in Interactions API"), @@ -525,11 +528,13 @@ class SSL_GeminiInteractionsTextPrompt(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: + return "high" return "medium" if thinking_level == "minimal": - if model_name == cls.GEMINI_3_8_FLASH: + if model_name in (cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE): raise ValueError( - "gemini-3.8-flash thinking_level must be low, medium, or high." + f"{model_name} thinking_level must be low, medium, or high." ) print( f"[WARNING] {model_name or 'Gemini 3.7 Flash'} does not support minimal " @@ -606,7 +611,7 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode): eff_aspect_ratio = str(aspect_ratio) eff_image_size = "None" if image_size in (None, "None") else str(image_size) - elif model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH): + elif model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE): eff_thinking_level = cls._resolve_gemini_3_7_thinking_level(thinking_level, model) eff_include_thoughts = include_thoughts @@ -1075,7 +1080,7 @@ class SSL_GeminiInteractionsTextPrompt(IO.ComfyNode): # Assemble generation_config effective_thinking_level = None - if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH): + if model in (cls.GEMINI_3_7_FLASH, cls.GEMINI_3_8_FLASH, cls.GEMINI_4_ARGON, cls.GEMINI_4_ARGON_CYBER_PERMISSIVE): effective_thinking_level = cls._resolve_gemini_3_7_thinking_level(thinking_level, model) elif model in cls.GEN3_THINKING_MODELS and thinking_level not in (None, "None"): effective_thinking_level = thinking_level diff --git a/tests/test_gemini_nodes.py b/tests/test_gemini_nodes.py index 67ad989..eca1fb0 100644 --- a/tests/test_gemini_nodes.py +++ b/tests/test_gemini_nodes.py @@ -56,6 +56,8 @@ def structured_client(monkeypatch): ("gemini-2.5-flash", {}), ("gemini-3.1-pro-preview", {"thinking_level": "high"}), (gemini_nodes.SSL_GeminiTextPrompt.GEMINI_3_7_FLASH, {"thinking_level": "medium"}), (gemini_nodes.SSL_GeminiTextPrompt.GEMINI_3_8_FLASH, {"thinking_level": "medium"}), + (gemini_nodes.SSL_GeminiTextPrompt.GEMINI_4_ARGON, {"thinking_level": "medium"}), + (gemini_nodes.SSL_GeminiTextPrompt.GEMINI_4_ARGON_CYBER_PERMISSIVE, {"thinking_level": "high"}), ]) @pytest.mark.parametrize("client_config", [{}, {"vertexai_express": True}, {"use_vertexai_env": True, "vertexai_project": "test", "vertexai_location": "global"}]) def test_builder_schema_reaches_generate_content(structured_client, monkeypatch, model, settings, client_config): @@ -1193,6 +1195,8 @@ def test_confirmed_current_models_are_visible_without_deprecated_image_aliases() image_size = next(input_ for input_ in schema.inputs if input_.id == "image_size") for model in ( + "gemini-4-argon", + "gemini-4-argon-cyber-permissive", "gemini-3.7-flash", "gemini-3.8-flash", "gemini-3.5-flash", @@ -1390,6 +1394,110 @@ def test_gemini_3_8_rejects_minimal_thinking_level(): ) +def test_gemini_4_argon_uses_gemini_3_thinking_config(): + config = gemini_nodes.SSL_GeminiTextPrompt._build_generate_content_config( + model="gemini-4-argon", + temperature=0.4, + top_p=0.8, + top_k=20, + max_output_tokens=1024, + seed=7, + include_images=False, + response_modalities=["TEXT"], + aspect_ratio="None", + padded_system_instruction="system", + thinking_level="None", + thinking_budget=4096, + include_thoughts=True, + media_resolution="unspecified", + ) + + assert config.thinking_config.thinking_level.value == "MEDIUM" + assert config.thinking_config.thinking_budget is None + assert config.temperature is None + assert config.top_p is None + assert config.top_k is None + + # Cyber permissive variant defaults to HIGH thinking level + config_cyber = gemini_nodes.SSL_GeminiTextPrompt._build_generate_content_config( + model="gemini-4-argon-cyber-permissive", + temperature=0.4, + top_p=0.8, + top_k=20, + max_output_tokens=1024, + seed=7, + include_images=False, + response_modalities=["TEXT"], + aspect_ratio="None", + padded_system_instruction="system", + thinking_level="None", + thinking_budget=4096, + include_thoughts=True, + media_resolution="unspecified", + ) + assert config_cyber.thinking_config.thinking_level.value == "HIGH" + assert config_cyber.thinking_config.thinking_budget is None + assert config_cyber.temperature is None + assert config_cyber.top_p is None + assert config_cyber.top_k is None + + +def test_gemini_4_argon_rejects_minimal_thinking_level(): + with pytest.raises(ValueError, match="gemini-4-argon thinking_level"): + gemini_nodes.SSL_GeminiTextPrompt._resolve_gemini_3_7_thinking_level( + "minimal", gemini_nodes.SSL_GeminiTextPrompt.GEMINI_4_ARGON + ) + with pytest.raises(ValueError, match="gemini-4-argon-cyber-permissive thinking_level"): + gemini_nodes.SSL_GeminiTextPrompt._resolve_gemini_3_7_thinking_level( + "minimal", gemini_nodes.SSL_GeminiTextPrompt.GEMINI_4_ARGON_CYBER_PERMISSIVE + ) + + +def test_gemini_4_argon_fingerprint_omits_sampling_params(): + common = { + "config": {"api_key": "key", "api_version": "v1"}, + "prompt": "prompt", + "system_instruction": "system", + "model": "gemini-4-argon", + "max_output_tokens": 1024, + "include_images": False, + "aspect_ratio": "None", + "bypass_mode": "None", + "use_seed": False, + "seed": 0, + "include_thoughts": True, + } + + fp1, _ = gemini_nodes.SSL_GeminiTextPrompt._compute_fingerprint_and_check_cache( + **common, + temperature=0.1, + top_p=0.5, + top_k=10, + thinking_budget=0, + thinking_level="medium", + ) + fp2, _ = gemini_nodes.SSL_GeminiTextPrompt._compute_fingerprint_and_check_cache( + **common, + temperature=0.9, + top_p=0.99, + top_k=90, + thinking_budget=1000, + thinking_level="medium", + ) + assert fp1 == fp2 + + +def test_gemini_4_argon_skips_explicit_context_cache(structured_client): + structured_client.responses = [[("final", "argon response")]] + args = _execute_kwargs(_config(use_cache=True, cache_seed=42)) + args["model"] = "gemini-4-argon" + output = gemini_nodes.SSL_GeminiTextPrompt.execute(**args) + assert output[0] == "argon response" + assert len(structured_client.calls) == 1 + # Explicit cache was not set on the request config + assert structured_client.calls[0]["config"].cached_content is None + + def test_gemini_3_7_fingerprint_matches_effective_request_controls(): common = { "config": {"api_key": "key", "api_version": "v1"}, diff --git a/tests/test_interactions_nodes.py b/tests/test_interactions_nodes.py index 6db23bc..9fb362a 100644 --- a/tests/test_interactions_nodes.py +++ b/tests/test_interactions_nodes.py @@ -155,6 +155,10 @@ def test_interactions_define_schema(): assert "response_schema" in input_names assert "image_inputs" in input_names + model_input = next(i for i in schema.inputs if i.id == "model") + assert "gemini-4-argon" in model_input.options + assert "gemini-4-argon-cyber-permissive" in model_input.options + output_names = [o.id for o in schema.outputs] assert output_names == ["text", "image", "final_actual_seed", "structured_output", "thoughts", "interaction_id"] @@ -341,6 +345,47 @@ def test_thinking_and_thoughts_extraction(mock_interactions_client): assert call["generation_config"]["thinking_summaries"] == "auto" +def test_interactions_gemini_4_argon_thinking(mock_interactions_client): + mock_interactions_client.responses = [ + {"text": "argon 1", "thoughts": "think 1", "id": "int_argon_1"}, + {"text": "argon 2", "thoughts": "think 2", "id": "int_argon_2"}, + ] + + # gemini-4-argon defaults to medium thinking level + args1 = _execute_kwargs( + model="gemini-4-argon", + thinking_level="None", + include_thoughts=True, + ) + interactions_nodes.SSL_GeminiInteractionsTextPrompt.execute(**args1) + call1 = mock_interactions_client.calls[0] + assert call1["model"] == "gemini-4-argon" + assert call1["generation_config"]["thinking_level"] == "medium" + assert call1["generation_config"]["thinking_summaries"] == "auto" + + # gemini-4-argon-cyber-permissive defaults to high thinking level + args2 = _execute_kwargs( + model="gemini-4-argon-cyber-permissive", + thinking_level="None", + include_thoughts=True, + ) + interactions_nodes.SSL_GeminiInteractionsTextPrompt.execute(**args2) + call2 = mock_interactions_client.calls[1] + assert call2["model"] == "gemini-4-argon-cyber-permissive" + assert call2["generation_config"]["thinking_level"] == "high" + + +def test_interactions_gemini_4_argon_rejects_minimal(): + with pytest.raises(ValueError, match="gemini-4-argon thinking_level"): + interactions_nodes.SSL_GeminiInteractionsTextPrompt._resolve_gemini_3_7_thinking_level( + "minimal", interactions_nodes.SSL_GeminiInteractionsTextPrompt.GEMINI_4_ARGON + ) + with pytest.raises(ValueError, match="gemini-4-argon-cyber-permissive thinking_level"): + interactions_nodes.SSL_GeminiInteractionsTextPrompt._resolve_gemini_3_7_thinking_level( + "minimal", interactions_nodes.SSL_GeminiInteractionsTextPrompt.GEMINI_4_ARGON_CYBER_PERMISSIVE + ) + + def test_multi_turn_chaining(mock_interactions_client): mock_interactions_client.responses = [ {"text": "Turn 1 answer", "id": "int_turn_1"},