diff --git a/__init__.py b/__init__.py index 6dad382..dae5a75 100644 --- a/__init__.py +++ b/__init__.py @@ -14,7 +14,7 @@ from .modules.apiManager import apiManager from .modules.imageCaptioning import imageCaptioning from .modules.controlNetCombine import controlNetCombine -RUNWARE_COMFYUI_VERSION = "Alpha 0.8" +RUNWARE_COMFYUI_VERSION = "0.9 Beta" RESET_COLOR = "\033[0m" BLUE_COLOR = "\033[94m" @@ -38,7 +38,7 @@ NODE_CLASS_MAPPINGS = { "Runware Image Masking": imageMasking, "Runware ControlNet PreProcessor": controlNetPreprocessor, "Runware API Manager": apiManager, - # "Runware Image Caption": imageCaptioning, + "Runware Image Caption": imageCaptioning, "Runware ControlNet Combine": controlNetCombine, } diff --git a/clientlibs/main.js b/clientlibs/main.js index 2fbb90e..db18f16 100644 --- a/clientlibs/main.js +++ b/clientlibs/main.js @@ -1,11 +1,12 @@ import { app } from "../../scripts/app.js"; -import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler } from "./utils.js"; +import { api } from "../../scripts/api.js"; +import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler, handleCustomErrors } from "./utils.js"; import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js"; app.registerExtension({ name: "runware.ai", async setup() { - // pass + api.addEventListener('runwareError', handleCustomErrors); }, async nodeCreated(node) { diff --git a/clientlibs/utils.js b/clientlibs/utils.js index 7b6fcac..5bffdaf 100644 --- a/clientlibs/utils.js +++ b/clientlibs/utils.js @@ -1,9 +1,11 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js"; -import { DEFAULT_DIMENSIONS_LIST, DEFAULT_MODELS_ARCH_LIST, SEARCH_TERMS, - DEFAULT_CONTROLNET_CONDITIONING_LIST, RUNWARE_NODE_TYPES, MODEL_TYPES_TERMS, MODEL_LIST_TERMS +import { DEFAULT_DIMENSIONS_LIST, DEFAULT_MODELS_ARCH_LIST, DEFAULT_CONTROLNET_CONDITIONING_LIST, + RUNWARE_NODE_TYPES, MODEL_TYPES_TERMS, MODEL_LIST_TERMS } from "./types.js"; +let openDialog = false; + async function enhancePrompt(userPrompt) { const resp = await api.fetchApi('/promptEnhance', { method: "POST", @@ -82,6 +84,7 @@ async function APIKeyHandler(apiManagerNode) { for(const widget of widgets) { if(widget.name === "API Key") { appendWidgetCB(widget, async function(...args) { + if(openDialog) return; const apiKey = args[0].trim(); if(apiKey.length < 30) { notifyUser("Invalid API Key Set, Please Try Again!", "error", "Runware API Manager"); @@ -101,6 +104,77 @@ async function APIKeyHandler(apiManagerNode) { } } +function handleCustomErrors(errObj) { + const errData = errObj.detail; + const errCode = errData.errorCode; + if(errCode === 401 && !openDialog) { + const dialog = new comfyAPI.asyncDialog.ComfyAsyncDialog(); + const htmlContent = ` +
+
+

Runware Inference Error

+

Your API Key is Invalid Or Undefined. You Need To Update It.

+
+ + +
+ + +
+
+
+
+ `; + dialog.showModal(htmlContent).then(() => { + openDialog = false; + }); + openDialog = true; + dialog.element.addEventListener('close', () => { + openDialog = false; + }); + const runwareAPIForm = document.getElementById("runwareAPIKey"); + const apiKeyInput = document.getElementById("apiKey"); + const getAPIKeyBTN = document.getElementById("getAPIKeyBTN"); + const setAPIKeyBTN = document.getElementById("setAPIKeyBTN"); + + getAPIKeyBTN.onclick = (e) => { + e.preventDefault(); + window.open("https://my.runware.ai/keys?utm_source=comfyui&utm_medium=referral&utm_campaign=comfyui_api_key_creation", "_blank"); + }; + + async function authUser(e) { + e.preventDefault(); + e.stopPropagation(); + const apiKey = apiKeyInput.value.trim(); + if(apiKey.length < 30) { + notifyUser("Invalid API Key Set, Please Try Again!", "error", "Runware API Manager"); + return; + } else { + const setAPIKeyResults = await setAPIKey(apiKey); + if(setAPIKeyResults.success) { + dialog.close(); + openDialog = false; + notifyUser("API Key Set Successfully!", "success", "Runware API Manager"); + } else { + notifyUser(setAPIKeyResults.error, "error", "Runware API Manager"); + } + } + }; + + setAPIKeyBTN.onclick = async (e) => { + authUser(e); + }; + + runwareAPIForm.onsubmit = async (e) => { + authUser(e); + }; + } +} + function appendWidgetCB(node, newCB) { const oldNodeCB = node.callback; if(typeof oldNodeCB === "function") { @@ -245,5 +319,6 @@ export { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, - APIKeyHandler, + handleCustomErrors, + APIKeyHandler }; \ No newline at end of file diff --git a/modules/apiManager.py b/modules/apiManager.py index c772278..a266871 100644 --- a/modules/apiManager.py +++ b/modules/apiManager.py @@ -4,12 +4,11 @@ class apiManager: return { "required": { "API Key": ("STRING", { - "tooltip": "Set Your Runware API Key To Start Using Runware Image Inference Services.\n\n(eg: BcVr1JVQM8FGzrRwb3GsbCq5ww1QwabV)\n\nIf You Don't Have One, You Can Get It From: https://my.runware.ai/keys", + "tooltip": "Set Your Runware API Key To Start Using Runware Image Inference Services.\n\n(e.g; BcVr1JVQM8FGzrRwb3GsbCq5ww1QwabV)\n\nIf You Don't Have One, You Can Get It From: https://my.runware.ai/keys", }), }, } - DESCRIPTION = "API Managers is a Runware Utility That helps you set your API Key From The ComfyUI Interface Without having to adjust the env file locally." + DESCRIPTION = "API Managers is a Runware Utility That helps you define or change your API Key From The ComfyUI Interface Without having to adjust the env file locally." CATEGORY = "Runware" - RETURN_TYPES = () - OUTPUT_NODE = True \ No newline at end of file + RETURN_TYPES = () \ No newline at end of file diff --git a/modules/bridges/mainRoute.py b/modules/bridges/mainRoute.py index 9805672..33ee558 100644 --- a/modules/bridges/mainRoute.py +++ b/modules/bridges/mainRoute.py @@ -9,9 +9,14 @@ async def setAPIKey(reqPayload): reqData = await reqPayload.json() apiKey = reqData.get('apiKey', None) if(apiKey is None or apiKey == "" or len(apiKey) < 30): - return web.json_response({'success': False, 'error': 'API Key Not Provided!'}) + return web.json_response({'success': False, 'error': 'Invalid API Key!'}) try: apiKey = apiKey.strip() + apiCheckResult = rwUtils.checkAPIKey(apiKey) + if(apiCheckResult == False): + return web.json_response({'success': False, 'error': 'Failed To Set Your API Key, Please Try Again!'}) + elif(apiCheckResult != True): + return web.json_response({'success': False, 'error': apiCheckResult}) rwUtils.setAPIKey(apiKey) except Exception as e: return web.json_response({'success': False, 'error': str(e)}) diff --git a/modules/imageInference.py b/modules/imageInference.py index 1230202..f1ca5ec 100644 --- a/modules/imageInference.py +++ b/modules/imageInference.py @@ -125,7 +125,7 @@ class txt2img: @classmethod def VALIDATE_INPUTS(cls, positivePrompt, negativePrompt): - if (positivePrompt is None or len(positivePrompt) < 3 or len(positivePrompt) > 2000): + if (positivePrompt is not None and (positivePrompt == "" or len(positivePrompt) < 3 or len(positivePrompt) > 2000)): raise Exception("Positive Prompt Must Be Between 3 And 2000 characters!") if (negativePrompt is not None and negativePrompt != "" and (len(negativePrompt) < 3 or len(negativePrompt) > 2000)): raise Exception("Negative Prompt Must Be Between 3 And 2000 characters!") diff --git a/modules/photoMaker.py b/modules/photoMaker.py index 42ba682..e6cd54a 100644 --- a/modules/photoMaker.py +++ b/modules/photoMaker.py @@ -115,7 +115,7 @@ class photoMaker: @classmethod def VALIDATE_INPUTS(cls, positivePrompt, negativePrompt): - if (positivePrompt is None or len(positivePrompt) < 3 or len(positivePrompt) > 2000): + if (positivePrompt is not None and (positivePrompt == "" or len(positivePrompt) < 3 or len(positivePrompt) > 2000)): raise Exception("Positive Prompt Must Be Between 3 And 2000 characters!") if (negativePrompt is not None and negativePrompt != "" and (len(negativePrompt) < 3 or len(negativePrompt) > 2000)): raise Exception("Negative Prompt Must Be Between 3 And 2000 characters!") diff --git a/modules/utils/runwareUtils.py b/modules/utils/runwareUtils.py index 917e60c..14ab78e 100644 --- a/modules/utils/runwareUtils.py +++ b/modules/utils/runwareUtils.py @@ -1,4 +1,6 @@ +from comfy.model_management import InterruptProcessingException from requests.adapters import HTTPAdapter +from server import PromptServer from dotenv import load_dotenv from pathlib import Path from PIL import Image @@ -58,6 +60,28 @@ def genRandSeed(minSeed = 1000, maxSeed = 9223372036854776000): def genRandUUID(): return str(uuid.uuid4()) +def checkAPIKey(apiKey): + headers = { + "Accept": "application/json", + "Content-Type": "application/json", + "Accept-Encoding": "gzip, deflate, br, zstd", + } + + genConfig = [{ + "taskType": "authentication", + "apiKey": apiKey, + }] + + try: + genResult = session.post(RUNWARE_API_BASE_URL, headers=headers, json=genConfig, timeout=SESSION_TIMEOUT, allow_redirects=False, stream=True) + genResult = genResult.json() + if("errors" in genResult): + return str(genResult["errors"][0]["message"]) + else: + return True + except Exception as e: + return False + def inferenecRequest(genConfig): global RUNWARE_API_KEY RUNWARE_API_KEY = os.getenv("RUNWARE_API_KEY") @@ -80,7 +104,12 @@ def inferenecRequest(genConfig): raise Exception("Error: Runware Request Failed!") except Exception as e: if "invalid api key" in str(e).lower(): - raise Exception("Invalid API Key - Please Make Sure you set your correctly API Key Either in the env file or through the API Manager Node!\n\nIf you don't have an API Key, you can get it from: https://my.runware.ai/keys") + PromptServer.instance.send_sync("runwareError", { + "success": False, + "errorMessage": str(e), + "errorCode": 401, + }) + raise InterruptProcessingException() else: raise Exception(f"Error: {e}") return False