From 085e0d73d1f1ff826302dd3521d977cd0d664058 Mon Sep 17 00:00:00 2001 From: Sirshendu Ganguly Date: Fri, 17 Apr 2026 19:11:28 +0530 Subject: [PATCH 1/3] Revert "Revert "Added new settings params for textInference"" This reverts commit 6c0e1331985f638d6d579ddd4f7dbbadeba7faf1. --- clientlibs/utils.js | 20 ++- modules/textInferenceSettings.py | 163 +++++++++++++++--- modules/textModelSearch.py | 4 + modules/utils/runwareUtils.py | 2 +- workflows/Runware_Text_Inference_GLM_5_1.json | 1 + 5 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 workflows/Runware_Text_Inference_GLM_5_1.json diff --git a/clientlibs/utils.js b/clientlibs/utils.js index 7dd3ecc..fe06b2d 100644 --- a/clientlibs/utils.js +++ b/clientlibs/utils.js @@ -1337,7 +1337,7 @@ function textInferenceSettingsToggleHandler(settingsNode) { if (!settingsNode?.widgets) return; const useSystemPromptWidget = settingsNode.widgets.find(w => w && w.name === "useSystemPrompt"); - const systemWidget = settingsNode.widgets.find(w => w && w.name === "system"); + const systemWidget = settingsNode.widgets.find(w => w && (w.name === "systemPrompt" || w.name === "system")); const useMaxTokensWidget = settingsNode.widgets.find(w => w && w.name === "useMaxTokens"); const maxTokensWidget = settingsNode.widgets.find(w => w && w.name === "maxTokens"); const useTemperatureWidget = settingsNode.widgets.find(w => w && w.name === "useTemperature"); @@ -1346,6 +1346,16 @@ function textInferenceSettingsToggleHandler(settingsNode) { const topPWidget = settingsNode.widgets.find(w => w && w.name === "topP"); const useTopKWidget = settingsNode.widgets.find(w => w && w.name === "useTopK"); const topKWidget = settingsNode.widgets.find(w => w && w.name === "topK"); + const useStopSequencesWidget = settingsNode.widgets.find(w => w && w.name === "useStopSequences"); + const stopSequencesWidget = settingsNode.widgets.find(w => w && w.name === "stopSequences"); + const usePresencePenaltyWidget = settingsNode.widgets.find(w => w && w.name === "usePresencePenalty"); + const presencePenaltyWidget = settingsNode.widgets.find(w => w && w.name === "presencePenalty"); + const useFrequencyPenaltyWidget = settingsNode.widgets.find(w => w && w.name === "useFrequencyPenalty"); + const frequencyPenaltyWidget = settingsNode.widgets.find(w => w && w.name === "frequencyPenalty"); + const useToolsWidget = settingsNode.widgets.find(w => w && w.name === "useTools"); + const toolsWidget = settingsNode.widgets.find(w => w && w.name === "tools"); + const useToolChoiceWidget = settingsNode.widgets.find(w => w && w.name === "useToolChoice"); + const toolChoiceWidget = settingsNode.widgets.find(w => w && w.name === "toolChoice"); function toggleWidgetState(useWidget, paramWidget) { if (!useWidget || !paramWidget) return; @@ -1368,6 +1378,11 @@ function textInferenceSettingsToggleHandler(settingsNode) { if (useTemperatureWidget && temperatureWidget) toggleWidgetState(useTemperatureWidget, temperatureWidget); if (useTopPWidget && topPWidget) toggleWidgetState(useTopPWidget, topPWidget); if (useTopKWidget && topKWidget) toggleWidgetState(useTopKWidget, topKWidget); + if (useStopSequencesWidget && stopSequencesWidget) toggleWidgetState(useStopSequencesWidget, stopSequencesWidget); + if (usePresencePenaltyWidget && presencePenaltyWidget) toggleWidgetState(usePresencePenaltyWidget, presencePenaltyWidget); + if (useFrequencyPenaltyWidget && frequencyPenaltyWidget) toggleWidgetState(useFrequencyPenaltyWidget, frequencyPenaltyWidget); + if (useToolsWidget && toolsWidget) toggleWidgetState(useToolsWidget, toolsWidget); + if (useToolChoiceWidget && toolChoiceWidget) toggleWidgetState(useToolChoiceWidget, toolChoiceWidget); } function videoSettingsToggleHandler(settingsNode) { @@ -3047,6 +3062,9 @@ function textModelSearchFilterHandler(textModelSearchNode) { "Google": [ "google:gemini@3-flash (Gemini 3 Flash)", ], + "Zai": [ + "zai-glm-5-1 (ZAI GLM 5.1)", + ], }; function filterModelList() { diff --git a/modules/textInferenceSettings.py b/modules/textInferenceSettings.py index 4725e5b..7cc977b 100644 --- a/modules/textInferenceSettings.py +++ b/modules/textInferenceSettings.py @@ -1,11 +1,15 @@ -from typing import Any, Dict, Tuple +import json +from typing import Any, Dict, List, Tuple class RunwareTextInferenceSettings: """Optional generation controls for Runware Text Inference (flat task.settings per API).""" _MAX_SYSTEM_CHARS = 200000 - _MAX_TOKENS_CAP = 196608 + _MAX_TOKENS_CAP = 131072 + _MAX_TOP_K = 100647 + _MAX_STOP_ITEMS = 5 + _MAX_STOP_ITEM_CHARS = 50 @classmethod def INPUT_TYPES(cls): @@ -16,17 +20,17 @@ class RunwareTextInferenceSettings: "default": False, "tooltip": "Include systemPrompt in settings.", }), - "system": ("STRING", { + "systemPrompt": ("STRING", { "multiline": True, "default": "", - "tooltip": "System instruction (max 200,000 characters). Sent as systemPrompt.", + "tooltip": "System instruction (max 200,000 chars).", }), "useMaxTokens": ("BOOLEAN", { "default": False, "tooltip": "Include maxTokens in settings.", }), "maxTokens": ("INT", { - "default": 32768, + "default": 4096, "min": 1, "max": cls._MAX_TOKENS_CAP, "step": 1, @@ -38,21 +42,21 @@ class RunwareTextInferenceSettings: }), "temperature": ("FLOAT", { "default": 1.0, - "min": 0.01, + "min": 0.0, "max": 1.0, "step": 0.01, - "tooltip": "Sampling temperature on (0, 1].", + "tooltip": "Sampling temperature (0.0–1.0).", }), "useTopP": ("BOOLEAN", { "default": False, "tooltip": "Include topP in settings.", }), "topP": ("FLOAT", { - "default": 0.95, - "min": 0.0, + "default": 1.0, + "min": 0.01, "max": 1.0, "step": 0.01, - "tooltip": "Nucleus sampling (0.0–1.0).", + "tooltip": "Nucleus sampling (0.01–1.0).", }), "useTopK": ("BOOLEAN", { "default": False, @@ -61,13 +65,62 @@ class RunwareTextInferenceSettings: "topK": ("INT", { "default": 40, "min": 1, - "max": 128, + "max": cls._MAX_TOP_K, "step": 1, - "tooltip": "Top-k sampling (1–128).", + "tooltip": f"Top-k sampling (1–{cls._MAX_TOP_K}).", }), - "thinkingLevel": (["off", "minimal", "low", "medium", "high"], { - "default": "off", - "tooltip": "thinkingLevel in settings (omit when off).", + "useStopSequences": ("BOOLEAN", { + "default": False, + "tooltip": "Include stopSequences in settings.", + }), + "stopSequences": ("STRING", { + "multiline": True, + "default": "", + "tooltip": "Stop sequences: one per line (max 5 items, 50 chars each).", + }), + "usePresencePenalty": ("BOOLEAN", { + "default": False, + "tooltip": "Include presencePenalty in settings.", + }), + "presencePenalty": ("FLOAT", { + "default": 0.0, + "min": -2.0, + "max": 2.0, + "step": 0.01, + "tooltip": "Presence penalty (-2.0 to 2.0).", + }), + "useFrequencyPenalty": ("BOOLEAN", { + "default": False, + "tooltip": "Include frequencyPenalty in settings.", + }), + "frequencyPenalty": ("FLOAT", { + "default": 0.0, + "min": -2.0, + "max": 2.0, + "step": 0.01, + "tooltip": "Frequency penalty (-2.0 to 2.0).", + }), + "useTools": ("BOOLEAN", { + "default": False, + "tooltip": "Include tools in settings.", + }), + "tools": ("STRING", { + "multiline": True, + "default": "[]", + "tooltip": "JSON array for tools.", + }), + "useToolChoice": ("BOOLEAN", { + "default": False, + "tooltip": "Include toolChoice in settings.", + }), + "toolChoice": ("STRING", { + "multiline": True, + "default": "{}", + "tooltip": "JSON object for toolChoice.", + }), + "thinkingLevel": (["none", "low", "medium", "high"], { + "default": "none", + "tooltip": "Extended reasoning level: none, low, medium, high.", }), }, } @@ -77,34 +130,102 @@ class RunwareTextInferenceSettings: FUNCTION = "create_settings" CATEGORY = "Runware/Text" DESCRIPTION = ( - "Optional text inference settings (flat: systemPrompt, maxTokens, temperature, topP, topK, thinkingLevel). " + "Optional text inference settings (systemPrompt, maxTokens, temperature, topP, topK, " + "stopSequences, presencePenalty, frequencyPenalty, tools, toolChoice, thinkingLevel). " "Connect to Runware Text Inference." ) + @staticmethod + def _parse_json_setting(raw: Any, expected_type: type, field_name: str) -> Any: + if isinstance(raw, expected_type): + return raw + if raw is None: + return expected_type() + + text = str(raw).strip() + if not text: + return expected_type() + + try: + parsed = json.loads(text) + except Exception as exc: + raise Exception(f"{field_name} must be valid JSON.") from exc + + if not isinstance(parsed, expected_type): + expected = "array" if expected_type is list else "object" + raise Exception(f"{field_name} must be a JSON {expected}.") + return parsed + + def _parse_stop_sequences(self, raw: Any) -> List[str]: + if raw is None: + return [] + + items: List[str] = [] + if isinstance(raw, (list, tuple)): + for item in raw: + text = str(item).strip() + if text: + items.append(text) + else: + text = str(raw).strip() + if text: + normalized = text.replace("\r\n", "\n").replace(",", "\n") + items = [segment.strip() for segment in normalized.split("\n") if segment.strip()] + + if len(items) > self._MAX_STOP_ITEMS: + raise Exception(f"stopSequences supports up to {self._MAX_STOP_ITEMS} items.") + for idx, item in enumerate(items, start=1): + if len(item) > self._MAX_STOP_ITEM_CHARS: + raise Exception( + f"stopSequences item {idx} exceeds {self._MAX_STOP_ITEM_CHARS} characters." + ) + return items + def create_settings(self, **kwargs) -> Tuple[Dict[str, Any], ...]: out: Dict[str, Any] = {} if kwargs.get("useSystemPrompt", False): - sys_text = (kwargs.get("system") or "").strip() + sys_text = (kwargs.get("systemPrompt") or kwargs.get("system") or "").strip() if sys_text: if len(sys_text) > self._MAX_SYSTEM_CHARS: sys_text = sys_text[: self._MAX_SYSTEM_CHARS] out["systemPrompt"] = sys_text if kwargs.get("useMaxTokens", False): - out["maxTokens"] = int(kwargs.get("maxTokens", 32768)) + out["maxTokens"] = int(kwargs.get("maxTokens", 4096)) if kwargs.get("useTemperature", False): out["temperature"] = float(kwargs.get("temperature", 1.0)) if kwargs.get("useTopP", False): - out["topP"] = float(kwargs.get("topP", 0.95)) + out["topP"] = float(kwargs.get("topP", 1.0)) if kwargs.get("useTopK", False): out["topK"] = int(kwargs.get("topK", 40)) - tl = kwargs.get("thinkingLevel", "off") - if tl != "off": + if kwargs.get("useStopSequences", False): + parsed_stop = self._parse_stop_sequences(kwargs.get("stopSequences", "")) + if parsed_stop: + out["stopSequences"] = parsed_stop + + if kwargs.get("usePresencePenalty", False): + out["presencePenalty"] = float(kwargs.get("presencePenalty", 0.0)) + + if kwargs.get("useFrequencyPenalty", False): + out["frequencyPenalty"] = float(kwargs.get("frequencyPenalty", 0.0)) + + if kwargs.get("useTools", False): + parsed_tools = self._parse_json_setting(kwargs.get("tools"), list, "tools") + if parsed_tools: + out["tools"] = parsed_tools + + if kwargs.get("useToolChoice", False): + parsed_choice = self._parse_json_setting(kwargs.get("toolChoice"), dict, "toolChoice") + if parsed_choice: + out["toolChoice"] = parsed_choice + + tl = kwargs.get("thinkingLevel", "none") + if tl != "none": out["thinkingLevel"] = tl if not out: diff --git a/modules/textModelSearch.py b/modules/textModelSearch.py index 46c6e93..d81e7ed 100644 --- a/modules/textModelSearch.py +++ b/modules/textModelSearch.py @@ -16,6 +16,9 @@ class RunwareTextModelSearch: "Google": [ "google:gemini@3-flash (Gemini 3 Flash)", ], + "Zai": [ + "zai-glm-5-1 (ZAI GLM 5.1)", + ], } MODEL_PROVIDERS = [ @@ -23,6 +26,7 @@ class RunwareTextModelSearch: "Google", "MiniMax", "OpenAI", + "Zai", ] @classmethod diff --git a/modules/utils/runwareUtils.py b/modules/utils/runwareUtils.py index 0c97b0f..75ba920 100644 --- a/modules/utils/runwareUtils.py +++ b/modules/utils/runwareUtils.py @@ -158,7 +158,7 @@ def getCustomEndpoint(): custom_endpoint = os.getenv("RUNWARE_CUSTOM_ENDPOINT") if custom_endpoint and isinstance(custom_endpoint, str): return custom_endpoint - return "https://api.runware.ai/v1" + return "https://api.runware.dev/v1" SESSION_TIMEOUT = getTimeout() RUNWARE_API_KEY = getAPIKey() diff --git a/workflows/Runware_Text_Inference_GLM_5_1.json b/workflows/Runware_Text_Inference_GLM_5_1.json new file mode 100644 index 0000000..893030b --- /dev/null +++ b/workflows/Runware_Text_Inference_GLM_5_1.json @@ -0,0 +1 @@ +{"id":"f1dab8a9-11a7-4fa3-9a3b-35351c84a964","revision":0,"last_node_id":10,"last_link_id":6,"nodes":[{"id":1,"type":"Runware Text Inference","pos":[497.6562194824219,734.759521484375],"size":[270,122],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"model","name":"model","type":"RUNWARETEXTMODEL","link":1},{"localized_name":"messages","name":"messages","type":"RUNWARETEXTINFERENCEMESSAGES","link":4},{"localized_name":"settings","name":"settings","shape":7,"type":"RUNWARETEXTINFERENCESETTINGS","link":6},{"localized_name":"providerSettings","name":"providerSettings","shape":7,"type":"RUNWAREPROVIDERSETTINGS","link":null}],"outputs":[{"localized_name":"text","name":"text","type":"STRING","links":[2]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"6d47a67b859745d5a86f463d8edfd7da0d1cfa6b","Node name for S&R":"Runware Text Inference"},"widgets_values":[],"bgcolor":"#5345bf"},{"id":5,"type":"Runware Text Inference Messages","pos":[503.0782165527344,919.4880981445312],"size":[255.45077514648438,166],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"Message Segment 1","name":"Message Segment 1","type":"RUNWARETEXTINFERENCEMESSAGE","link":5},{"localized_name":"Message Segment 2","name":"Message Segment 2","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 3","name":"Message Segment 3","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 4","name":"Message Segment 4","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 5","name":"Message Segment 5","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 6","name":"Message Segment 6","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 7","name":"Message Segment 7","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null},{"localized_name":"Message Segment 8","name":"Message Segment 8","shape":7,"type":"RUNWARETEXTINFERENCEMESSAGE","link":null}],"outputs":[{"localized_name":"messages","name":"messages","type":"RUNWARETEXTINFERENCEMESSAGES","links":[4]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"6d47a67b859745d5a86f463d8edfd7da0d1cfa6b","Node name for S&R":"Runware Text Inference Messages"},"widgets_values":[],"bgcolor":"#5345bf"},{"id":9,"type":"Runware Text Inference Settings","pos":[-6.560511112213135,948.0151977539062],"size":[400,658],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"useSystemPrompt","name":"useSystemPrompt","shape":7,"type":"BOOLEAN","widget":{"name":"useSystemPrompt"},"link":null},{"localized_name":"systemPrompt","name":"systemPrompt","shape":7,"type":"STRING","widget":{"name":"systemPrompt"},"link":null},{"localized_name":"useMaxTokens","name":"useMaxTokens","shape":7,"type":"BOOLEAN","widget":{"name":"useMaxTokens"},"link":null},{"localized_name":"maxTokens","name":"maxTokens","shape":7,"type":"INT","widget":{"name":"maxTokens"},"link":null},{"localized_name":"useTemperature","name":"useTemperature","shape":7,"type":"BOOLEAN","widget":{"name":"useTemperature"},"link":null},{"localized_name":"temperature","name":"temperature","shape":7,"type":"FLOAT","widget":{"name":"temperature"},"link":null},{"localized_name":"useTopP","name":"useTopP","shape":7,"type":"BOOLEAN","widget":{"name":"useTopP"},"link":null},{"localized_name":"topP","name":"topP","shape":7,"type":"FLOAT","widget":{"name":"topP"},"link":null},{"localized_name":"useTopK","name":"useTopK","shape":7,"type":"BOOLEAN","widget":{"name":"useTopK"},"link":null},{"localized_name":"topK","name":"topK","shape":7,"type":"INT","widget":{"name":"topK"},"link":null},{"localized_name":"useStopSequences","name":"useStopSequences","shape":7,"type":"BOOLEAN","widget":{"name":"useStopSequences"},"link":null},{"localized_name":"stopSequences","name":"stopSequences","shape":7,"type":"STRING","widget":{"name":"stopSequences"},"link":null},{"localized_name":"usePresencePenalty","name":"usePresencePenalty","shape":7,"type":"BOOLEAN","widget":{"name":"usePresencePenalty"},"link":null},{"localized_name":"presencePenalty","name":"presencePenalty","shape":7,"type":"FLOAT","widget":{"name":"presencePenalty"},"link":null},{"localized_name":"useFrequencyPenalty","name":"useFrequencyPenalty","shape":7,"type":"BOOLEAN","widget":{"name":"useFrequencyPenalty"},"link":null},{"localized_name":"frequencyPenalty","name":"frequencyPenalty","shape":7,"type":"FLOAT","widget":{"name":"frequencyPenalty"},"link":null},{"localized_name":"useTools","name":"useTools","shape":7,"type":"BOOLEAN","widget":{"name":"useTools"},"link":null},{"localized_name":"tools","name":"tools","shape":7,"type":"STRING","widget":{"name":"tools"},"link":null},{"localized_name":"useToolChoice","name":"useToolChoice","shape":7,"type":"BOOLEAN","widget":{"name":"useToolChoice"},"link":null},{"localized_name":"toolChoice","name":"toolChoice","shape":7,"type":"STRING","widget":{"name":"toolChoice"},"link":null},{"localized_name":"thinkingLevel","name":"thinkingLevel","shape":7,"type":"COMBO","widget":{"name":"thinkingLevel"},"link":null}],"outputs":[{"localized_name":"settings","name":"settings","type":"RUNWARETEXTINFERENCESETTINGS","links":[6]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"6d47a67b859745d5a86f463d8edfd7da0d1cfa6b","Node name for S&R":"Runware Text Inference Settings"},"widgets_values":[false,"",false,32768,false,1,true,0.95,false,40,"high","",false,0,false,0,false,"[]",false,"{}","none"],"bgcolor":"#5345bf"},{"id":3,"type":"Runware Text Model Search","pos":[81.07406616210938,741.3767700195312],"size":[270,130],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"Model Search","name":"Model Search","type":"STRING","widget":{"name":"Model Search"},"link":null},{"localized_name":"Model Provider","name":"Model Provider","type":"COMBO","widget":{"name":"Model Provider"},"link":null},{"localized_name":"TextList","name":"TextList","type":"COMBO","widget":{"name":"TextList"},"link":null},{"localized_name":"Use Search Value","name":"Use Search Value","type":"BOOLEAN","widget":{"name":"Use Search Value"},"link":null}],"outputs":[{"localized_name":"model","name":"model","type":"RUNWARETEXTMODEL","links":[1]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"6d47a67b859745d5a86f463d8edfd7da0d1cfa6b","Node name for S&R":"Runware Text Model Search"},"widgets_values":["","Zai","zai-glm-5-1 (ZAI GLM 5.1)",false],"bgcolor":"#5345bf"},{"id":8,"type":"Runware Text Inference Message","pos":[457.5366516113281,1134.87060546875],"size":[400,200],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"role","name":"role","type":"COMBO","widget":{"name":"role"},"link":null},{"localized_name":"content","name":"content","type":"STRING","widget":{"name":"content"},"link":null}],"outputs":[{"localized_name":"message","name":"message","type":"RUNWARETEXTINFERENCEMESSAGE","links":[5]}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"6d47a67b859745d5a86f463d8edfd7da0d1cfa6b","Node name for S&R":"Runware Text Inference Message"},"widgets_values":["user","Outline a 5-step plan to buy beta fish from a pet store"],"bgcolor":"#5345bf"},{"id":4,"type":"Runware Save Text","pos":[950.5037841796875,745.9119262695312],"size":[621.9185791015625,757.6689453125],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":2}],"outputs":[{"localized_name":"text","name":"text","type":"STRING","links":null}],"properties":{"cnr_id":"ComfyUI-Runware","ver":"6d47a67b859745d5a86f463d8edfd7da0d1cfa6b","Node name for S&R":"Runware Save Text"},"widgets_values":["Here is a 5-step plan to successfully buy a healthy betta fish from a pet store:\n\n**Step 1: Set Up the Tank First (The \"Cycle Before You Buy\" Rule)**\nNever buy the fish and the tank on the same day. A betta needs a properly cycled tank to survive, which means building up beneficial bacteria that break down toxic fish waste. \n* **Buy the essentials:** A tank (5 gallons minimum is highly recommended), a gentle filter, an adjustable heater (bettas need 78-80°F water), substrate, hiding spots, and a lid (bettas are jumpers). \n* **Start the Nitrogen Cycle:** Set up the tank, turn on the filter and heater, and add a water conditioner and beneficial bacteria starter. This cycling process takes 2 to 6 weeks. Only move to Step 2 once the tank can process ammonia and nitrite down to 0 ppm.\n\n**Step 2: Choose a Reputable Store**\nNot all pet stores care for their fish equally. Scout out a few stores in your area before committing to buying.\n* **Inspect the store:** Look at the overall cleanliness of the fish area. Are the aisles tidy? Do the tanks look well-maintained?\n* **Look at the other fish:** Are the tanks overcrowded? Are there dead fish in the tanks? If a store has multiple dead or visibly sick fish, walk away.\n\n**Step 3: Inspect and Select a Healthy Betta**\nBettas are often kept in small cups, which makes them vulnerable to illness. You need to observe the fish closely before choosing one.\n* **Look at the water:** A little discoloration is normal, but if the water is thick, foul-smelling, or filled with floating debris, the fish is likely stressed.\n* **Observe the fish's body:** Look for clear, bright eyes, intact fins (no ragged, black, or white-edged tears), and smooth scales. Avoid fish with white fuzzy patches (fungus), white spots (ich), or bloated stomachs.\n* **Observe the fish's behavior:** A healthy betta is alert. Gently tap the cup or wave your hand near it. The fish should respond by flaring its gills, swimming actively, or looking at you. Avoid fish that float listlessly at the top or sink to the bottom unresponsive.\n\n**Step 4: Purchase and Transport Safely**\nOnce you have chosen your fish, it is time to bring it home. Minimizing stress during transport is crucial.\n* **Buy the right food:** If you haven't already, purchase high-quality betta pellets (with fish meal or whole fish as the first ingredient) and maybe some freeze-dried bloodworms for treats.\n* **Transport smartly:** Pet stores usually give you a small plastic bag. If it is very cold or hot outside, place the bagged fish inside a paper bag to block out light (which reduces stress), and then inside a small cooler or insulated bag to regulate the temperature. Go straight home—do not run other errands.\n\n**Step 5: Acclimate to the New Tank**\nDo not simply pour the fish directly into its new tank. The water parameters (temperature, pH) from the store's cup are very different from your established tank. Sudden changes can send a fish into shock.\n* **Float the cup:** Gently pour the fish and the store water into a small, clean container (or keep it in the bag). Float this container in your prepared tank for 15–20 minutes so the temperatures equalize.\n* **Drip acclimate:** Over the next 20–30 minutes, add small amounts (about a tablespoon at a time) of your tank water into the container with the fish. This slowly adjusts the fish to your water's chemistry.\n* **Net and release:** Once the water volume in the container has roughly doubled with your tank water, use a soft net to gently scoop the fish out and place it into its new tank. **Do not** pour the water from the store into your tank, as it may contain parasites or diseases. Turn off the tank lights for the first few hours to let the fish decompress."],"bgcolor":"#5345bf"}],"links":[[1,3,0,1,0,"RUNWARETEXTMODEL"],[2,1,0,4,0,"STRING"],[4,5,0,1,1,"RUNWARETEXTINFERENCEMESSAGES"],[5,8,0,5,0,"RUNWARETEXTINFERENCEMESSAGE"],[6,9,0,1,2,"RUNWARETEXTINFERENCESETTINGS"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.6830134553650705,"offset":[307.04397258867573,-505.9291598819246]}},"version":0.4} \ No newline at end of file From 652b8a19463b1dbf9c22b4ee5d81d6dd2bcc75f0 Mon Sep 17 00:00:00 2001 From: Sirshendu Ganguly Date: Fri, 17 Apr 2026 19:14:18 +0530 Subject: [PATCH 2/3] Fixed base URL --- modules/utils/runwareUtils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/utils/runwareUtils.py b/modules/utils/runwareUtils.py index 75ba920..0c97b0f 100644 --- a/modules/utils/runwareUtils.py +++ b/modules/utils/runwareUtils.py @@ -158,7 +158,7 @@ def getCustomEndpoint(): custom_endpoint = os.getenv("RUNWARE_CUSTOM_ENDPOINT") if custom_endpoint and isinstance(custom_endpoint, str): return custom_endpoint - return "https://api.runware.dev/v1" + return "https://api.runware.ai/v1" SESSION_TIMEOUT = getTimeout() RUNWARE_API_KEY = getAPIKey() From cf7cb90c38483d4075fc188205abcff68a719d46 Mon Sep 17 00:00:00 2001 From: Sirshendu Ganguly Date: Fri, 17 Apr 2026 20:04:43 +0530 Subject: [PATCH 3/3] Fixed backward comp --- modules/textInferenceSettings.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/modules/textInferenceSettings.py b/modules/textInferenceSettings.py index 7cc977b..aed5a30 100644 --- a/modules/textInferenceSettings.py +++ b/modules/textInferenceSettings.py @@ -69,6 +69,11 @@ class RunwareTextInferenceSettings: "step": 1, "tooltip": f"Top-k sampling (1–{cls._MAX_TOP_K}).", }), + # Keep this in the original slot to avoid shifting legacy widgets_values. + "thinkingLevel": (["none", "low", "medium", "high"], { + "default": "none", + "tooltip": "Extended reasoning level: none, low, medium, high.", + }), "useStopSequences": ("BOOLEAN", { "default": False, "tooltip": "Include stopSequences in settings.", @@ -118,10 +123,6 @@ class RunwareTextInferenceSettings: "default": "{}", "tooltip": "JSON object for toolChoice.", }), - "thinkingLevel": (["none", "low", "medium", "high"], { - "default": "none", - "tooltip": "Extended reasoning level: none, low, medium, high.", - }), }, } @@ -181,6 +182,19 @@ class RunwareTextInferenceSettings: ) return items + @staticmethod + def _normalize_thinking_level(raw: Any) -> str: + value = str(raw or "none").strip().lower() + # Backward compatibility with older workflows. + aliases = { + "off": "none", + "minimal": "low", + } + value = aliases.get(value, value) + if value not in ("none", "low", "medium", "high"): + return "none" + return value + def create_settings(self, **kwargs) -> Tuple[Dict[str, Any], ...]: out: Dict[str, Any] = {} @@ -224,7 +238,7 @@ class RunwareTextInferenceSettings: if parsed_choice: out["toolChoice"] = parsed_choice - tl = kwargs.get("thinkingLevel", "none") + tl = self._normalize_thinking_level(kwargs.get("thinkingLevel", "none")) if tl != "none": out["thinkingLevel"] = tl