Skip to content

Commit

Permalink
feat: namespacing and additional adjustments
Browse files Browse the repository at this point in the history
- namespaces nodes
- Adds illustrative labels
- Adds seed for LLM generations
- optimizes several nodes
  • Loading branch information
pixelass committed May 21, 2024
1 parent 817f265 commit 969d14c
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,4 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Welcome to the ComfyUI Captain Nodes repository! This collection of nodes enhances [Captain](https://get-captain.com)—a desktop tool that allows you to run AI models locally on your computer. With Captain, you can quickly start applications, search data using natural language, and build custom AI apps, all while keeping your data private and offline.

<div align="center">
<img src="https://github.com/blib-la/ComfyUI-Captain-Nodes/assets/1148334/01c09224-6788-438d-a006-2bae4afcd78a" alt="nodes example"/ >
</div>

## Features of ComfyUI Captain Nodes

This package extends Captain's capabilities by providing specialized nodes for AI integration and utility functions, making it easier to harness powerful AI features directly on your desktop:
Expand All @@ -10,6 +14,7 @@ This package extends Captain's capabilities by providing specialized nodes for A

- **[Image to Base64](nodes/image_to_base64.py)**: Converts images to Base64, facilitating easier image handling and storage.
- **[Join Text](nodes/join_text.py)**: Combines multiple text inputs into one string, simplifying data aggregation.
- **[Preview Text](nodes/previw_text.py)**: Outputs a preview of the generated text and passes it through.
- **[OpenAI Chat](nodes/openai_chat.py)**: Integrates OpenAI’s conversational models to enable dynamic interactions with AI.
- **[OpenAI Image Analysis](nodes/openai_image_analysis.py)**: Utilizes AI to analyze and interpret images, providing detailed insights and descriptions.

Expand Down
24 changes: 13 additions & 11 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,52 @@
import shutil
import folder_paths


comfy_path = os.path.dirname(folder_paths.__file__)
captain_nodes_path = os.path.abspath(os.path.dirname(__file__))


def setup_js():
try:
js_dest_path = os.path.join(comfy_path, "web", "extensions", "ComfyUI-Captain-Nodes")
js_src_path = os.path.join(captain_nodes_path, "extensions")
js_dest_path = os.path.join(comfy_path, "web", "extensions", "ComfyUI-Captain-Nodes")
js_src_path = os.path.join(captain_nodes_path, "web")
print(js_src_path)
print(js_dest_path)
if os.path.exists(js_dest_path):
shutil.rmtree(js_dest_path)

shutil.copytree(js_src_path, js_dest_path)

except Exception as e:
print(f"An error occurred: {e}")


setup_js()


def do_install():
try:
# Construct the file path to install.py
install_script_path = os.path.join(os.path.dirname(__file__), 'install.py')
# Check if the install script actually exists
install_script_path = os.path.join(os.path.dirname(__file__), "install.py")

# Check if the installation script actually exists
if not os.path.exists(install_script_path):
print("Captain says: Installation script not found. Please check the presence of 'install.py'.")
return

# Load and execute the installation script
spec = importlib.util.spec_from_file_location('captain_install', install_script_path)
spec = importlib.util.spec_from_file_location("captain_install", install_script_path)
captain_install = importlib.util.module_from_spec(spec)
spec.loader.exec_module(captain_install)
print("Captain says: Dependencies installed successfully.")
except Exception as e:
print(f"Captain says: Failed to install dependencies: {str(e)}")


# Ensure dependencies are installed before loading any custom nodes
do_install()

# List of node files
node_list = [
node_list = [
"join_text",
"openai_chat",
"openai_image_analysis",
Expand All @@ -69,4 +71,4 @@ def do_install():
except Exception as e:
print(f"Captain says: An error occurred while loading module {module_name}. Error: {e}")

__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
6 changes: 3 additions & 3 deletions nodes/image_to_base64.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def INPUT_TYPES(cls):

RETURN_TYPES = ("STRING",)
FUNCTION = "image_to_base64"
CATEGORY = "Captain/Image"
CATEGORY = "🧑‍✈️ Captain/🖼️ Image"

def image_to_base64(self, image):
"""Converts an image to a Base64 string.
Expand Down Expand Up @@ -51,9 +51,9 @@ def image_to_base64(self, image):
return ("Failed to convert image",)

NODE_CLASS_MAPPINGS = {
"ImageToBase64": ImageToBase64,
"Captain__ImageToBase64": ImageToBase64,
}

NODE_DISPLAY_NAME_MAPPINGS = {
"ImageToBase64": "Convert Image to Base64",
"Captain__ImageToBase64": "🧑‍✈️ Convert Image to Base64",
}
18 changes: 12 additions & 6 deletions nodes/join_text.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class JoinTextsNode:
class JoinTexts:
"""
A node that joins two input strings with a newline.
Expand All @@ -18,7 +18,7 @@ class JoinTextsNode:
"""
FUNCTION = "execute"
RETURN_TYPES = ("STRING",)
CATEGORY = "Captain/Utilities"
CATEGORY = "🧑‍✈️ Captain/🛠️ Utilities"

@classmethod
def INPUT_TYPES(cls):
Expand All @@ -32,30 +32,36 @@ def INPUT_TYPES(cls):
"multiline": True,
"default": "Second String"
}),
"delimiter": ("STRING", {
"delimiter": ("STRING", {
"multiline": False,
"default": ", "
}),
"add_newline": ("BOOLEAN", {
"default": False
}),
},
}

def execute(self, first_string, second_string, delimiter):
def execute(self, first_string, second_string, delimiter, add_newline):
""" Joins two strings with a newline.
Parameters:
first_string (str): The first string to join.
second_string (str): The second string to join.
delimiter (str): The delimiter used to join the two strings.
add_newline (bool): Adds a newline after the delimiter.
Returns:
tuple: Contains the joined strings.
"""
if add_newline:
delimiter += "\n"
return (f"{first_string}{delimiter}{second_string}",)

NODE_CLASS_MAPPINGS = {
"JoinTextsNode": JoinTextsNode
"Captain__JoinTexts": JoinTexts
}

NODE_DISPLAY_NAME_MAPPINGS = {
"JoinTextsNode": "Join Texts Node"
"Captain__JoinTexts": "🧑‍✈️ Join Texts"
}
20 changes: 8 additions & 12 deletions nodes/openai_chat.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import json
from openai import OpenAI

class OpenAIChat:
Expand All @@ -22,15 +21,15 @@ class OpenAIChat:
"""
FUNCTION = "execute"
RETURN_TYPES = ("STRING",)
CATEGORY = "Captain/Chat"
CATEGORY = "🧑‍✈️ Captain/💬 LLM"

@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"api_key": ("STRING", {
"multiline": False,
"default": "Enter your OpenAI API key"
"default": "sk-xxxxxxxxxx"
}),
"model": (["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo", "gpt-4o"], {
"default": "gpt-3.5-turbo"
Expand All @@ -50,11 +49,6 @@ def INPUT_TYPES(cls):
},
}

## @classmethod
## def IS_CHANGED(cls):
## # Force re-evaluation of the node
## return float("NaN")

def execute(self, api_key, model, system_message, user_message, seed):
""" Sends formatted messages to OpenAI API using the provided API key and prints the response.
Expand All @@ -63,13 +57,14 @@ def execute(self, api_key, model, system_message, user_message, seed):
model (str): The OpenAI model.
system_message (str): The message from the system.
user_message (str): The message from the user.
seed (str): The seed of the generation.
Returns:
tuple: Contains the response from the API.
"""

client = OpenAI(
api_key=api_key
api_key = api_key
)

messages = [
Expand All @@ -78,14 +73,15 @@ def execute(self, api_key, model, system_message, user_message, seed):
]
completion = client.chat.completions.create(
model = model,
messages = messages
messages = messages,
seed = seed
)
return (completion.choices[0].message.content,)

NODE_CLASS_MAPPINGS = {
"OpenAIChat": OpenAIChat
"Captain__OpenAIChat": OpenAIChat
}

NODE_DISPLAY_NAME_MAPPINGS = {
"OpenAIChat": "OpenAI Chat"
"Captain__OpenAIChat": "🧑‍✈️ OpenAI Chat"
}
29 changes: 18 additions & 11 deletions nodes/openai_image_analysis.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from openai import OpenAI


class ImageAnalysis:
"""
A node that creates a formatted message object with a system message and a user message,
Expand All @@ -22,17 +23,17 @@ class ImageAnalysis:
"""
FUNCTION = "execute"
RETURN_TYPES = ("STRING",)
CATEGORY = "Captain/Chat"
CATEGORY = "🧑‍✈️ Captain/💬 LLM"

@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"api_key": ("STRING", {
"multiline": False,
"default": "Enter your OpenAI API key"
"default": "sk-xxxxxxxxxx"
}),
"model": (["gpt-4-vision-preview", "gpt-4o"], {
"model": (["gpt-4-vision-preview", "gpt-4o"], {
"default": "gpt-4o"
}),
"system_message": ("STRING", {
Expand All @@ -43,27 +44,31 @@ def INPUT_TYPES(cls):
"multiline": True,
"default": "User message goes here."
}),
"seed": ("INT", {
"default": 0,
"display": "number"
}),
"base64": ("STRING", {
"multiline": True,
"default": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
}),
})
},
}

def execute(self, api_key, model, system_message, user_message, base64):
def execute(self, api_key, model, system_message, user_message, seed, base64):
""" Sends formatted messages to OpenAI API using the provided API key and prints the response.
Parameters:
api_key (str): The OpenAI API key.
model (str): The OpenAI model.
system_message (str): The message from the system.
user_message (str): The message from the user.
seed (str): The seed of the generation.
base64 (str): The image from the user.
Returns:
tuple: Contains the response from the API.
"""

client = OpenAI(
api_key=api_key
)
Expand All @@ -84,15 +89,17 @@ def execute(self, api_key, model, system_message, user_message, base64):
}
]
completion = client.chat.completions.create(
model = model,
messages = messages
model=model,
messages=messages,
seed=seed
)
return (completion.choices[0].message.content,)


NODE_CLASS_MAPPINGS = {
"ImageAnalysis": ImageAnalysis
"Captain__ImageAnalysis": ImageAnalysis
}

NODE_DISPLAY_NAME_MAPPINGS = {
"ImageAnalysis": "OpenAI Image Analysis"
"Captain__ImageAnalysis": "🧑‍✈️ OpenAI Image Analysis"
}
18 changes: 7 additions & 11 deletions nodes/preview_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class PreviewText:
FUNCTION = "preview_text"
RETURN_TYPES = ("STRING",)
OUTPUT_NODE = True
CATEGORY = "Captain/Utilities"
CATEGORY = "🧑‍✈️ Captain/🛠️ Utilities"

def __init__(self):
pass
Expand All @@ -29,30 +29,26 @@ def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"forceInput": True}),
},
"hidden": {
"prompt": "PROMPT",
"extra_pnginfo": "EXTRA_PNGINFO"
},
}
}

def preview_text(self, text, prompt=None, extra_pnginfo=None):
def preview_text(self, text):
""" Previews the given text.
Parameters:
text (str): The text to preview.
prompt (str, optional): The prompt associated with the text.
extra_pnginfo (str, optional): Additional PNG info.
Returns:
dict: Contains the preview text in the UI and the result text.
"""
return {"ui": {"string": [text]}, "result": (text,)}

# WEB_DIRECTORY = "../web"

NODE_CLASS_MAPPINGS = {
"PreviewText": PreviewText
"Captain__PreviewText": PreviewText
}

NODE_DISPLAY_NAME_MAPPINGS = {
"PreviewText": "Preview Text"
"Captain__PreviewText": "🧑‍✈️ Preview Text"
}
1 change: 1 addition & 0 deletions web/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const PREVIEW_TEXT_NODE_NAME = "Captain__PreviewText"
Loading

0 comments on commit 969d14c

Please sign in to comment.