diff --git a/ChatGLMNode/README.md b/ChatGLMNode/README.md new file mode 100644 index 0000000..f124710 --- /dev/null +++ b/ChatGLMNode/README.md @@ -0,0 +1,16 @@ +# ChatGLM nodes for ComfyUI + +Translation is carried out with the help of artificial intelligence using GLM models. + +### Note: To use it, you need to register on the site [bigmodel.cn](https://bigmodel.cn/) and get an API key. Free model is the 'glm-4-flash'! + +> Includes: +> **ChatGLM4TranslateCLIPTextEncodeNode** - translate text, and return CONDITIONING +> +> **ChatGLM4TranslateTextNode** - translate text and return text (STRING) + +## Image: + +![ChatGLMTranslateNode](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/raw/master/ChatGLMNode/image_ChatGLM_translate_node.jpg) + +**Used** **Zhipu AI**: https://bigmodel.cn/ diff --git a/ChatGLMNode/chatglm_translate_node.py b/ChatGLMNode/chatglm_translate_node.py new file mode 100644 index 0000000..36fd514 --- /dev/null +++ b/ChatGLMNode/chatglm_translate_node.py @@ -0,0 +1,162 @@ +import os +import json +import requests + +ALL_CODES_LANGS = ['af', 'sq', 'am', 'ar', 'hy', 'as', 'ay', 'az', 'bm', 'eu', 'be', 'bn', 'bho', 'bs', 'bg', 'ca', 'ceb', 'ny', 'zh-CN', 'zh-TW', 'co', 'hr', 'cs', 'da', 'dv', 'doi', 'nl', 'en', 'eo', 'et', 'ee', 'tl', 'fi', 'fr', 'fy', 'gl', 'ka', 'de', 'el', 'gn', 'gu', 'ht', 'ha', 'haw', 'iw', 'hi', 'hmn', 'hu', 'is', 'ig', 'ilo', 'id', 'ga', 'it', 'ja', 'jw', 'kn', 'kk', 'km', 'rw', 'gom', 'ko', 'kri', 'ku', 'ckb', 'ky', 'lo', 'la', 'lv', 'ln', 'lt', 'lg', 'lb', 'mk', 'mai', 'mg', 'ms', 'ml', 'mt', 'mi', 'mr', 'mni-Mtei', 'lus', 'mn', 'my', 'ne', 'no', 'or', 'om', 'ps', 'fa', 'pl', 'pt', 'pa', 'qu', 'ro', 'ru', 'sm', 'sa', 'gd', 'nso', 'sr', 'st', 'sn', 'sd', 'si', 'sk', 'sl', 'so', 'es', 'su', 'sw', 'sv', 'tg', 'ta', 'tt', 'te', 'th', 'ti', 'ts', 'tr', 'tk', 'ak', 'uk', 'ur', 'ug', 'uz', 'vi', 'cy', 'xh', 'yi', 'yo', 'zu'] + +ZHIPUAI_API_KEY = None +ENDPOINT_URL = "https://open.bigmodel.cn/api/paas/v4/chat/completions" + + +# Directory translate node and config file +dir_translate_node = os.path.dirname(__file__) +config_path = os.path.join(os.path.abspath(dir_translate_node), "config.json") + +# Load config.js file +if not os.path.exists(config_path): + print("File config.js file not found! Reinstall extensions!") +else: + with open(config_path, "r") as f: + CONFIG = json.load(f) + + # GET ZHIPUAI_API_KEY from json + ZHIPUAI_API_KEY = CONFIG.get("ZHIPUAI_API_KEY") +# ===== + + +def translate(prompt, srcTrans, toTrans, model, max_tokens, temperature, top_p): + if prompt and prompt.strip() != "": + + # Create body request + payload = { + "model": model, + "messages": [ + { + "role": "user", + "content": f"Translate from {srcTrans} to {toTrans}: {prompt}", + }, + ], + "max_tokens": max_tokens, + "temperature": temperature, + "top_p": top_p, + } + + # Header + headers = { + "Authorization": f"Bearer {ZHIPUAI_API_KEY}", + "Content-Type": "application/json", + } + + try: + response = requests.post(ENDPOINT_URL, headers=headers, json=payload) + response.raise_for_status() + + if response.status_code == 200: + json_data = response.json() + translate_text_prompt = json_data.get("choices")[0]["message"][ + "content" + ].strip() + + return ( + translate_text_prompt if translate_text_prompt and not None else "" + ) + + except requests.HTTPError as e: + print( + f"Error translate text ChatGLM: {response.status_code}, {response.text}" + ) + raise e + except Exception as e: + print(f"Error translate text ChatGLM: {e}") + raise e + + +class ChatGLM4TranslateCLIPTextEncodeNode: + @classmethod + def INPUT_TYPES(self): + return { + "required": { + "from_translate": (ALL_CODES_LANGS, {"default": "ru", "tooltip": "Translation from"}), + "to_translate": (ALL_CODES_LANGS, {"default": "en", "tooltip": "Translation to"}), + "model": ( + [ + "glm-4-plus", + "glm-4-0520", + "glm-4", + "glm-4-air", + "glm-4-airx", + "glm-4-long", + "glm-4-flash", + ], + {"default": "glm-4-flash", "tooltip": "The model code to be called. Model 'glm-4-flash' is free!"}, + ), + "max_tokens": ("INT", {"default": 1024, "tooltip": "The maximum number of tokens for model output, maximum output is 4095, default value is 1024."}), + "temperature": ( + "FLOAT", + {"default": 0.95, "min": 0.0, "max": 1.0, "step": 0.05, "tooltip": "Sampling temperature, controls the randomness of the output, must be a positive number within the range: [0.0, 1.0], default value is 0.95."}, + ), + "top_p": ( + "FLOAT", + {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.05, "tooltip": "Another method of temperature sampling, value range is: [0.0, 1.0], default value is 0.7."}, + ), + "text": ("STRING", {"multiline": True, "placeholder": "Input text"}), + "clip": ("CLIP",), + } + } + + RETURN_TYPES = ( + "CONDITIONING", + "STRING", + ) + FUNCTION = "chatglm_translate_text" + DESCRIPTION = ( + "This is a node that translates the prompt into another language using ChatGLM." + ) + CATEGORY = "AlekPet Nodes/conditioning" + + def chatglm_translate_text( + self, + from_translate, + to_translate, + model, + max_tokens, + temperature, + top_p, + text, + clip, + ): + if ZHIPUAI_API_KEY is None or ZHIPUAI_API_KEY.strip() == "" or ZHIPUAI_API_KEY == "your_api_key": + raise ValueError("ZHIPUAI_API_KEY value is empty or missing") + + text = translate( + text, from_translate, to_translate, model, max_tokens, temperature, top_p + ) + tokens = clip.tokenize(text) + cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True) + return ([[cond, {"pooled_output": pooled}]], text) + + +class ChatGLM4TranslateTextNode(ChatGLM4TranslateCLIPTextEncodeNode): + @classmethod + def INPUT_TYPES(self): + return_types = super().INPUT_TYPES() + del return_types["required"]["clip"] + return return_types + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("text",) + FUNCTION = "chatglm_translate_text" + + CATEGORY = "AlekPet Nodes/text" + + def chatglm_translate_text( + self, from_translate, to_translate, model, max_tokens, temperature, top_p, text + ): + if ZHIPUAI_API_KEY is None or ZHIPUAI_API_KEY.strip() == "" or ZHIPUAI_API_KEY == "your_api_key": + raise ValueError("ZHIPUAI_API_KEY value is empty or missing") + + text = translate( + text, from_translate, to_translate, model, max_tokens, temperature, top_p + ) + + return (text,) diff --git a/ChatGLMNode/config.json b/ChatGLMNode/config.json new file mode 100644 index 0000000..95b4391 --- /dev/null +++ b/ChatGLMNode/config.json @@ -0,0 +1,4 @@ +{ + "__comment": "Register on the site https://bigmodel.cn and get a key and add it to the field ZHIPUAI_API_KEY", + "ZHIPUAI_API_KEY": "your_api_key" +} diff --git a/ChatGLMNode/image_ChatGLM_translate_node.jpg b/ChatGLMNode/image_ChatGLM_translate_node.jpg new file mode 100644 index 0000000..291a58a Binary files /dev/null and b/ChatGLMNode/image_ChatGLM_translate_node.jpg differ diff --git a/README.md b/README.md index 8f519fc..094dafe 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,22 @@ Custom nodes that extend the capabilities of [ComfyUI](https://github.com/comfya # List Nodes: -| Name | Description | ComfyUI category | -| :---------------------------------- | :------------------------------------------------------------------------------------------------------------------: | :-----------------------: | -| _PoseNode_ | The node set pose ControlNet | AlekPet Node/image | -| _PainterNode_ | The node set sketch, scrumble image ControlNet and other nodes | AlekPet Node/image | -| _GoogleTranslateTextNode_ | The node translate promt uses module **googletrans** from other languages into english and return string | AlekPet Node/conditioning | -| _GoogleTranslateCLIPTextEncodeNode_ | The node translate promt uses module **googletrans** from other languages into english, and return conditioning | AlekPet Node/text | -| _DeepTranslatorTextNode_ | The node translate promt uses module **Deep Translator** from other languages into english and return string | AlekPet Node/text | -| _DeepTranslatorCLIPTextEncodeNode_ | The node translate promt uses module **Deep Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning | -| _ArgosTranslateTextNode_ | The node translate promt uses module **Argos Translator** from other languages into english and return string | AlekPet Node/text | -| _ArgosTranslateCLIPTextEncodeNode_ | The node translate promt uses module **Argos Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning | -| _PreviewTextNode_ | The node displays the input text | AlekPet Node/extras | -| _ColorsCorrectNode_ | The node for correcting image colors | AlekPet Node/extras | -| _HexToHueNode_ | The node convert HEX color to HUE (degrees and normal [-0.5, 0.5]) | AlekPet Node/extras | -| _IDENode_ | The node that allows you to run code written in **Python** or **Javascript** directly in the node | AlekPet Node/experiments | +| Name | Description | ComfyUI category | +| :------------------------------------ | :------------------------------------------------------------------------------------------------------------------: | :-----------------------: | +| _PoseNode_ | The node set pose ControlNet | AlekPet Node/image | +| _PainterNode_ | The node set sketch, scrumble image ControlNet and other nodes | AlekPet Node/image | +| _GoogleTranslateTextNode_ | The node translate promt uses module **googletrans** from other languages into english and return string | AlekPet Node/conditioning | +| _GoogleTranslateCLIPTextEncodeNode_ | The node translate promt uses module **googletrans** from other languages into english, and return conditioning | AlekPet Node/text | +| _DeepTranslatorTextNode_ | The node translate promt uses module **Deep Translator** from other languages into english and return string | AlekPet Node/text | +| _DeepTranslatorCLIPTextEncodeNode_ | The node translate promt uses module **Deep Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning | +| _ArgosTranslateTextNode_ | The node translate promt uses module **Argos Translator** from other languages into english and return string | AlekPet Node/text | +| _ArgosTranslateCLIPTextEncodeNode_ | The node translate promt uses module **Argos Translator** from other languages into english, and return conditioning | AlekPet Node/conditioning | +| _ChatGLM4TranslateTextNode_ | This translator node uses artificial intelligence to translate prompts and return string | AlekPet Node/text | +| _ChatGLM4TranslateCLIPTextEncodeNode_ | This translator node uses artificial intelligence to translate prompts and return conditioning | AlekPet Node/conditioning | +| _PreviewTextNode_ | The node displays the input text | AlekPet Node/extras | +| _ColorsCorrectNode_ | The node for correcting image colors | AlekPet Node/extras | +| _HexToHueNode_ | The node convert HEX color to HUE (degrees and normal [-0.5, 0.5]) | AlekPet Node/extras | +| _IDENode_ | The node that allows you to run code written in **Python** or **Javascript** directly in the node | AlekPet Node/experiments | # Installing diff --git a/__init__.py b/__init__.py index 51587cc..cc025a7 100644 --- a/__init__.py +++ b/__init__.py @@ -34,7 +34,7 @@ # NODE_CLASS_MAPPINGS = dict() # dynamic class nodes append in mappings # NODE_DISPLAY_NAME_MAPPINGS = dict() # dynamic display names nodes append mappings names -humanReadableTextReg = re.compile("(?<=[a-z])([A-Z])|(?<=[A-Z])([A-Z][a-z]+)") +humanReadableTextReg = re.compile("(?<=[a-z0-9])([A-Z])|(?<=[A-Z0-9])([A-Z][a-z]+)") module_name_cut_version = re.compile("[>=<]") installed_modules = {} @@ -276,6 +276,10 @@ def installNodes(): ArgosTranslateCLIPTextEncodeNode, ArgosTranslateTextNode, ) +from .ChatGLMNode.chatglm_translate_node import ( + ChatGLM4TranslateCLIPTextEncodeNode, + ChatGLM4TranslateTextNode, +) from .DeepTranslatorNode.deep_translator_node import ( DeepTranslatorCLIPTextEncodeNode, DeepTranslatorTextNode, @@ -293,6 +297,8 @@ def installNodes(): NODE_CLASS_MAPPINGS = { "ArgosTranslateCLIPTextEncodeNode": ArgosTranslateCLIPTextEncodeNode, "ArgosTranslateTextNode": ArgosTranslateTextNode, + "ChatGLM4TranslateCLIPTextEncodeNode": ChatGLM4TranslateCLIPTextEncodeNode, + "ChatGLM4TranslateTextNode": ChatGLM4TranslateTextNode, "DeepTranslatorCLIPTextEncodeNode": DeepTranslatorCLIPTextEncodeNode, "DeepTranslatorTextNode": DeepTranslatorTextNode, "PreviewTextNode": PreviewTextNode, @@ -309,6 +315,8 @@ def installNodes(): NODE_DISPLAY_NAME_MAPPINGS = { "ArgosTranslateCLIPTextEncodeNode": "Argos Translate CLIP Text Encode Node", "ArgosTranslateTextNode": "Argos Translate Text Node", + "ChatGLM4TranslateCLIPTextEncodeNode": "ChatGLM-4 Translate CLIP Text Encode Node", + "ChatGLM4TranslateTextNode": "ChatGLM-4 Translate Text Node", "DeepTranslatorCLIPTextEncodeNode": "Deep Translator CLIP Text Encode Node", "DeepTranslatorTextNode": "Deep Translator Text Node", "PreviewTextNode": "Preview Text Node", diff --git a/pyproject.toml b/pyproject.toml index d596675..0e0377b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_custom_nodes_alekpet" description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode." -version = "1.0.30" +version = "1.0.31" license = { file = "LICENSE" } [project.urls]