Remove obsolete files and add new node implementations
This commit removes some obsolete Python files related to latent, logging, and prompt functionalities. It also adds new implementations for node classes, including ShowText, ImageSizer, ImageDimensionResizer, RandomTitleCharacter, and others. The new implementations restructure and optimize the existing functionalities.
This commit is contained in:
+14
-8
@@ -1,21 +1,25 @@
|
||||
from custom_nodes.GR85.latent import ImageSizer, ImageDimensionResizer
|
||||
from custom_nodes.GR85.logging import ShowString
|
||||
from custom_nodes.GR85.prompt import RandomTitleCharacter, InsertCharacter, RandomElements, ElementsConcatenator, \
|
||||
LlmEnhancer
|
||||
from custom_nodes.GR85.nodes.latent.image_dimension_resizer import ImageDimensionResizer
|
||||
from custom_nodes.GR85.nodes.latent.image_sizer import ImageSizer
|
||||
from custom_nodes.GR85.nodes.logging.show_text import ShowText
|
||||
from custom_nodes.GR85.nodes.prompt.element_concatenator import ElementConcatenator
|
||||
from custom_nodes.GR85.nodes.prompt.insert_character import InsertCharacter
|
||||
from custom_nodes.GR85.nodes.prompt.llm_enhance import LlmEnhancer
|
||||
from custom_nodes.GR85.nodes.prompt.wildcard.random_elements import RandomElements
|
||||
from custom_nodes.GR85.nodes.prompt.wildcard.random_title_character import RandomTitleCharacter
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"GR85_ShowString": ShowString,
|
||||
"GR85_ShowText": ShowText,
|
||||
"GR85_ImageSizer": ImageSizer,
|
||||
"GR85_ImageDimensionResizer": ImageDimensionResizer,
|
||||
"GR85_RandomTitleCharacter": RandomTitleCharacter,
|
||||
"GR85_InsertCharacter": InsertCharacter,
|
||||
"GR85_RandomElements": RandomElements,
|
||||
"GR85_ElementsConcatenator": ElementsConcatenator,
|
||||
"GR85_ElementsConcatenator": ElementConcatenator,
|
||||
"GR85_LlmEnhancer": LlmEnhancer,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"GR85_ShowString": "Show String",
|
||||
"GR85_ShowText": "Show Text",
|
||||
"GR85_ImageSizer": "Image Sizer",
|
||||
"GR85_ImageDimensionResizer": "Image Dimension Resizer",
|
||||
"GR85_RandomTitleCharacter": "Random Title Character",
|
||||
@@ -25,4 +29,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"GR85_LlmEnhancer": "Llm Enhancer",
|
||||
}
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
WEB_DIRECTORY = "./js"
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import {app} from '../../scripts/app.js'
|
||||
import {ComfyWidgets} from '../../scripts/widgets.js'
|
||||
|
||||
/**
|
||||
* @typedef {import("types/comfy").ComfyExtension} ComfyExtension
|
||||
*/
|
||||
|
||||
// Displays input text on a node
|
||||
app.registerExtension({
|
||||
name: 'GR85.ShowText',
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === 'GR85_ShowText') {
|
||||
/**
|
||||
* @param text {string[]}
|
||||
* @this {ComfyNode}
|
||||
*/
|
||||
function populate(text) {
|
||||
console.log(this)
|
||||
if (this.widgets) {
|
||||
for (let i = 1; i < this.widgets.length; i++) {
|
||||
this.widgets[i].onRemove?.()
|
||||
}
|
||||
this.widgets.length = 1
|
||||
}
|
||||
|
||||
const v = [...text]
|
||||
if (!v[0]) {
|
||||
v.shift()
|
||||
}
|
||||
|
||||
for (const list of v) {
|
||||
const w = ComfyWidgets['STRING'](this, 'text', ['STRING', {multiline: true}], app).widget
|
||||
w.inputEl.readOnly = true
|
||||
w.inputEl.style.opacity = 0.6
|
||||
w.value = list
|
||||
}
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
populate.call(this, message.text)
|
||||
}
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
onConfigure?.apply(this, arguments)
|
||||
if (this.widgets_values?.length) {
|
||||
populate.call(this, this.widgets_values)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -1,116 +0,0 @@
|
||||
import math
|
||||
|
||||
|
||||
class ImageSizer:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"original_dimensions": (["512x512", "512*768", "768x768", "1024x1024", "1280x720", "1920x1080"],),
|
||||
"ratio_1": ("INT", {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"ratio_2": ("INT", {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"orientation": (["landscape", "portrait"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
|
||||
FUNCTION = "resize_dimensions"
|
||||
|
||||
CATEGORY = "GR85/Latent"
|
||||
|
||||
def resize_dimensions(self, original_dimensions, ratio_1, ratio_2, orientation):
|
||||
original_width, original_height = map(int, original_dimensions.split("x"))
|
||||
|
||||
"""Calculates new dimensions for an image while maintaining the same pixel count
|
||||
and a specified aspect ratio.
|
||||
|
||||
Args:
|
||||
original_width: The original width of the image in pixels.
|
||||
original_height: The original height of the image in pixels.
|
||||
ratio_1: The first part of the desired aspect ratio.
|
||||
ratio_2: The second part of the desired aspect ratio.
|
||||
orientation: Either "landscape" or "portrait" (used for clarity, not calculation)
|
||||
|
||||
Returns:
|
||||
A tuple containing the new width and height of the image.
|
||||
"""
|
||||
|
||||
total_pixels = original_width * original_height
|
||||
new_width = math.sqrt(total_pixels * ratio_1 / ratio_2)
|
||||
new_height = new_width * ratio_2 / ratio_1
|
||||
|
||||
# Adjust if the desired orientation is different from the original
|
||||
if (orientation == 'landscape' and original_height > original_width) or \
|
||||
(orientation == 'portrait' and original_width > original_height):
|
||||
new_width, new_height = new_height, new_width # Swap dimensions
|
||||
|
||||
return int(round(new_width)), int(round(new_height))
|
||||
|
||||
|
||||
class ImageDimensionResizer:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"original_width": ("INT", {
|
||||
"forceInput": True,
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"original_height": ("INT", {
|
||||
"forceInput": True,
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"target_dimensions": (["512x512", "512*768", "768x768", "1024x1024", "1280x720", "1920x1080"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
|
||||
FUNCTION = "resize_dimensions"
|
||||
|
||||
CATEGORY = "GR85/Latent"
|
||||
|
||||
def resize_dimensions(self, original_width, original_height, target_dimensions):
|
||||
"""
|
||||
Calculates new dimensions while maintaining pixel count
|
||||
and maintaining the ratio of the original dimensions.
|
||||
|
||||
first calculate the original ratio
|
||||
then calculate the target pixel count
|
||||
then calculate the new dimensions
|
||||
"""
|
||||
original_ratio = original_width / original_height
|
||||
target_width, target_height = map(int, target_dimensions.split("x"))
|
||||
target_pixels = target_width * target_height
|
||||
new_width = math.sqrt(target_pixels * original_ratio)
|
||||
new_height = new_width / original_ratio
|
||||
return int(round(new_width)), int(round(new_height))
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
class ShowString:
|
||||
@classmethod
|
||||
def INPUT_TYPES(self):
|
||||
return {
|
||||
"required": {
|
||||
"str": ("STRING", {"forceInput": True}),
|
||||
"key": ('STRING', {"default": "text"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
FUNCTION = "show"
|
||||
|
||||
CATEGORY = "GR85/Logging"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def show(self, str, key):
|
||||
return {"ui": {key: (str,)}, "result": (str,)}
|
||||
@@ -0,0 +1,53 @@
|
||||
import math
|
||||
|
||||
|
||||
class ImageDimensionResizer:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"original_width": ("INT", {
|
||||
"forceInput": True,
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"original_height": ("INT", {
|
||||
"forceInput": True,
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"target_dimensions": (["512x512", "512*768", "768x768", "1024x1024", "1280x720", "1920x1080"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
|
||||
FUNCTION = "resize_dimensions"
|
||||
|
||||
CATEGORY = "GR85/Latent"
|
||||
|
||||
def resize_dimensions(self, original_width, original_height, target_dimensions):
|
||||
"""
|
||||
Calculates new dimensions while maintaining pixel count
|
||||
and maintaining the ratio of the original dimensions.
|
||||
|
||||
first calculate the original ratio
|
||||
then calculate the target pixel count
|
||||
then calculate the new dimensions
|
||||
"""
|
||||
original_ratio = original_width / original_height
|
||||
target_width, target_height = map(int, target_dimensions.split("x"))
|
||||
target_pixels = target_width * target_height
|
||||
new_width = math.sqrt(target_pixels * original_ratio)
|
||||
new_height = new_width / original_ratio
|
||||
return int(round(new_width)), int(round(new_height))
|
||||
@@ -0,0 +1,65 @@
|
||||
import math
|
||||
|
||||
|
||||
class ImageSizer:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"original_dimensions": (["512x512", "512*768", "768x768", "1024x1024", "1280x720", "1920x1080"],),
|
||||
"ratio_1": ("INT", {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"ratio_2": ("INT", {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 4096,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"orientation": (["landscape", "portrait"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
|
||||
FUNCTION = "resize_dimensions"
|
||||
|
||||
CATEGORY = "GR85/Latent"
|
||||
|
||||
def resize_dimensions(self, original_dimensions, ratio_1, ratio_2, orientation):
|
||||
original_width, original_height = map(int, original_dimensions.split("x"))
|
||||
|
||||
"""Calculates new dimensions for an image while maintaining the same pixel count
|
||||
and a specified aspect ratio.
|
||||
|
||||
Args:
|
||||
original_width: The original width of the image in pixels.
|
||||
original_height: The original height of the image in pixels.
|
||||
ratio_1: The first part of the desired aspect ratio.
|
||||
ratio_2: The second part of the desired aspect ratio.
|
||||
orientation: Either "landscape" or "portrait" (used for clarity, not calculation)
|
||||
|
||||
Returns:
|
||||
A tuple containing the new width and height of the image.
|
||||
"""
|
||||
|
||||
total_pixels = original_width * original_height
|
||||
side_1 = math.sqrt(total_pixels * ratio_1 / ratio_2)
|
||||
side_2 = side_1 * ratio_2 / ratio_1
|
||||
|
||||
bigger_side = max(side_1, side_2)
|
||||
smaller_side = min(side_1, side_2)
|
||||
|
||||
if orientation == 'landscape':
|
||||
return int(round(bigger_side)), int(round(smaller_side))
|
||||
else:
|
||||
return int(round(smaller_side)), int(round(bigger_side))
|
||||
@@ -0,0 +1,40 @@
|
||||
class ShowText:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"forceInput": True}),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
},
|
||||
}
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "notify"
|
||||
OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
CATEGORY = "GR85/Logging"
|
||||
|
||||
def notify(self, text, unique_id=None, extra_pnginfo=None):
|
||||
if unique_id is not None and extra_pnginfo is not None:
|
||||
if not isinstance(extra_pnginfo, list):
|
||||
print("Error: extra_pnginfo is not a list")
|
||||
elif (
|
||||
not isinstance(extra_pnginfo[0], dict)
|
||||
or "workflow" not in extra_pnginfo[0]
|
||||
):
|
||||
print("Error: extra_pnginfo[0] is not a dict or missing 'workflow' key")
|
||||
else:
|
||||
workflow = extra_pnginfo[0]["workflow"]
|
||||
node = next(
|
||||
(x for x in workflow["nodes"] if str(x["id"]) == str(unique_id[0])),
|
||||
None,
|
||||
)
|
||||
if node:
|
||||
node["widgets_values"] = [text]
|
||||
|
||||
return {"ui": {"text": text}, "result": (text,)}
|
||||
@@ -0,0 +1,30 @@
|
||||
class ElementConcatenator:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"title_character_elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"random_elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("concatenated_elements",)
|
||||
|
||||
FUNCTION = "concatenate_elements"
|
||||
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def concatenate_elements(self, title_character_elements, random_elements):
|
||||
return (f"{title_character_elements}, {random_elements}",)
|
||||
@@ -0,0 +1,74 @@
|
||||
class InsertCharacter:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"title_character": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True,
|
||||
}),
|
||||
"elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"facial_features": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"append": ("STRING", {
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"anime_template": ("STRING", {
|
||||
"default": "1girl, {title_character}, anime, {elements}, {append}",
|
||||
"multiline": True
|
||||
}),
|
||||
"realistic_template": ("STRING", {
|
||||
"default": "realistic, 1girl, {title_character}, {facial_features}, {elements}, {append}",
|
||||
"multiline": True
|
||||
}),
|
||||
"face_detailer_template": ("STRING", {
|
||||
"default": "realistic, 1girl, {title_character}, {facial_features}, cute eyes, natural beauty, attractive face, {append}",
|
||||
"multiline": True
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
||||
RETURN_NAMES = ("anime_string", "realistic_string", "face_detailer_string")
|
||||
|
||||
FUNCTION = "insert_character"
|
||||
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def insert_character(self, title_character, elements, facial_features, append, anime_template, realistic_template,
|
||||
face_detailer_template):
|
||||
"""
|
||||
This function takes the output of the RandomTitleCharacter node and inserts it into three different templates.
|
||||
The templates are strings that contain placeholders for the different elements of the character.
|
||||
The placeholders are:
|
||||
- {title_character}
|
||||
- {elements}
|
||||
- {facial_features}
|
||||
- {append}
|
||||
|
||||
The function will replace these placeholders with the corresponding elements and return the resulting strings.
|
||||
"""
|
||||
anime = anime_template.format(title_character=title_character, elements=elements,
|
||||
facial_features=facial_features, append=append)
|
||||
realistic = realistic_template.format(title_character=title_character, elements=elements,
|
||||
facial_features=facial_features, append=append)
|
||||
face_detailer = face_detailer_template.format(title_character=title_character, elements=elements,
|
||||
facial_features=facial_features, append=append)
|
||||
|
||||
print("Anime:", anime)
|
||||
print("Realistic:", realistic)
|
||||
print("Face Detailer:", face_detailer)
|
||||
|
||||
return anime, realistic, face_detailer
|
||||
@@ -0,0 +1,57 @@
|
||||
import openai
|
||||
|
||||
|
||||
class LlmEnhancer:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"title_character": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"system_prompt": ("STRING", {
|
||||
"default": "Enhance the following elements into a short and simple movie still that is coherent with the character.",
|
||||
"multiline": True
|
||||
}),
|
||||
"user_prompt": ("STRING", {
|
||||
"default": "Character in scene: {title_character}, Props: {elements}",
|
||||
"multiline": True
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("enhanced_string",)
|
||||
FUNCTION = "enhance_string"
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def enhance_string(self, title_character, elements, system_prompt, user_prompt):
|
||||
"""
|
||||
This makes a call to: http://localhost:7875/v1/chat/completions
|
||||
This service is a ChatGPT model that has been fine-tuned to respond with an enhanced version of the input text.
|
||||
"""
|
||||
client = openai.OpenAI(base_url="http://localhost:7875/v1", api_key="lm-studio")
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="Undi95/Xwin-MLewd-13B-V0.2-GGUF",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt.format(title_character=title_character, elements=elements)},
|
||||
],
|
||||
temperature=0.7,
|
||||
)
|
||||
|
||||
try:
|
||||
return (completion.choices[0].message.content,)
|
||||
except Exception as e:
|
||||
return (str(e),)
|
||||
@@ -0,0 +1,45 @@
|
||||
import yaml
|
||||
import random
|
||||
|
||||
|
||||
class RandomElements:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {"forceInput": True, "default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"elements_amount": ("INT", {
|
||||
"default": 4,
|
||||
"min": 1,
|
||||
"max": 100,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("random_elements",)
|
||||
|
||||
FUNCTION = "choose_random_elements"
|
||||
|
||||
CATEGORY = "GR85/Prompt/Wildcard"
|
||||
|
||||
def choose_random_elements(self, seed, elements_amount):
|
||||
with open("ComfyUI/custom_nodes/GR85/wildcards/elements.txt", "r") as file:
|
||||
elements = file.readlines()
|
||||
result = []
|
||||
for element in elements:
|
||||
parts = element.split(':')
|
||||
if len(parts) == 2: # Check if we have two parts
|
||||
amount_str, name = parts
|
||||
amount = int(amount_str)
|
||||
result.extend([name.strip()] * amount)
|
||||
else:
|
||||
result.append(element.strip()) # Handle elements without ':'
|
||||
random.seed(seed)
|
||||
chosen_elements = random.sample(result, elements_amount)
|
||||
return (", ".join(chosen_elements),)
|
||||
@@ -0,0 +1,93 @@
|
||||
import random
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
class RandomTitleCharacter:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {
|
||||
"forceInput": True, "default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"title_elements_amount": ("INT", {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"character_elements_amount": ("INT", {
|
||||
"default": 2,
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"facial_features_amount": ("INT", {
|
||||
"default": 2,
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
||||
RETURN_NAMES = ("random_character", "elements", "facial_features")
|
||||
|
||||
FUNCTION = "choose_random_title_characters"
|
||||
|
||||
CATEGORY = "GR85/Prompt/Wildcard"
|
||||
|
||||
def choose_random_title_characters(self,
|
||||
seed,
|
||||
title_elements_amount,
|
||||
character_elements_amount,
|
||||
facial_features_amount):
|
||||
"""
|
||||
There is a YAML file in the same directory as this file named: characters.yaml
|
||||
It contains a list of characters in the following format:
|
||||
- Movie/TV Show/Video Game Title:
|
||||
Title_Elements:
|
||||
- Title Element
|
||||
Characters:
|
||||
- Character Name:
|
||||
Elements:
|
||||
- Character element
|
||||
Facial Features:
|
||||
- Facial feature
|
||||
|
||||
This function will:
|
||||
- Choose a random title
|
||||
- Choose a random character from that title
|
||||
- Choose 3 random elements from that character
|
||||
- Choose a random element from that title
|
||||
|
||||
Returns a string in the following format:
|
||||
"Character Name, Title Name, Character element 1, Character element 2, Title Element 1"
|
||||
"""
|
||||
with open("characters.yaml", "r") as file:
|
||||
titles = yaml.load(file, Loader=yaml.FullLoader)
|
||||
|
||||
# Use the seed to generate a random number
|
||||
random.seed(seed)
|
||||
|
||||
title = random.choice(titles)
|
||||
title_name = list(title.keys())[0]
|
||||
title_elements = random.sample(title["Title_Elements"], title_elements_amount)
|
||||
|
||||
title_character = random.choice(title["Characters"])
|
||||
title_character_name = list(title_character.keys())[0]
|
||||
title_character_elements = random.sample(title_character["Elements"], character_elements_amount)
|
||||
|
||||
facial_features = random.sample(title_character["Facial Features"], facial_features_amount)
|
||||
|
||||
return (f"{title_character_name}, {title_name}",
|
||||
f"{', '.join(title_character_elements)}, {', '.join(title_elements)}",
|
||||
f"{', '.join(facial_features)}")
|
||||
@@ -1,305 +0,0 @@
|
||||
import random
|
||||
|
||||
import openai
|
||||
import yaml
|
||||
|
||||
|
||||
class RandomTitleCharacter:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {
|
||||
"forceInput": True, "default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"title_elements_amount": ("INT", {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"character_elements_amount": ("INT", {
|
||||
"default": 2,
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"facial_features_amount": ("INT", {
|
||||
"default": 2,
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
||||
RETURN_NAMES = ("random_character", "elements", "facial_features")
|
||||
|
||||
FUNCTION = "choose_random_title_characters"
|
||||
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def choose_random_title_characters(self,
|
||||
seed,
|
||||
title_elements_amount,
|
||||
character_elements_amount,
|
||||
facial_features_amount):
|
||||
"""
|
||||
There is a YAML file in the same directory as this file named: characters.yaml
|
||||
It contains a list of characters in the following format:
|
||||
- Movie/TV Show/Video Game Title:
|
||||
Title_Elements:
|
||||
- Title Element
|
||||
Characters:
|
||||
- Character Name:
|
||||
Elements:
|
||||
- Character element
|
||||
Facial Features:
|
||||
- Facial feature
|
||||
|
||||
This function will:
|
||||
- Choose a random title
|
||||
- Choose a random character from that title
|
||||
- Choose 3 random elements from that character
|
||||
- Choose a random element from that title
|
||||
|
||||
Returns a string in the following format:
|
||||
"Character Name, Title Name, Character element 1, Character element 2, Title Element 1"
|
||||
"""
|
||||
with open("characters.yaml", "r") as file:
|
||||
titles = yaml.load(file, Loader=yaml.FullLoader)
|
||||
|
||||
# Use the seed to generate a random number
|
||||
random.seed(seed)
|
||||
|
||||
title = random.choice(titles)
|
||||
title_name = list(title.keys())[0]
|
||||
title_elements = random.sample(title["Title_Elements"], title_elements_amount)
|
||||
|
||||
title_character = random.choice(title["Characters"])
|
||||
title_character_name = list(title_character.keys())[0]
|
||||
title_character_elements = random.sample(title_character["Elements"], character_elements_amount)
|
||||
|
||||
facial_features = random.sample(title_character["Facial Features"], facial_features_amount)
|
||||
|
||||
return (f"{title_character_name}, {title_name}",
|
||||
f"{', '.join(title_character_elements)}, {', '.join(title_elements)}",
|
||||
f"{', '.join(facial_features)}")
|
||||
|
||||
|
||||
class InsertCharacter:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"title_character": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True,
|
||||
}),
|
||||
"elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"facial_features": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"append": ("STRING", {
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"anime_template": ("STRING", {
|
||||
"default": "1girl, {title_character}, anime, {elements}, {append}",
|
||||
"multiline": True
|
||||
}),
|
||||
"realistic_template": ("STRING", {
|
||||
"default": "realistic, 1girl, {title_character}, {facial_features}, {elements}, {append}",
|
||||
"multiline": True
|
||||
}),
|
||||
"face_detailer_template": ("STRING", {
|
||||
"default": "realistic, 1girl, {title_character}, {facial_features}, cute eyes, natural beauty, attractive face, {append}",
|
||||
"multiline": True
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
||||
RETURN_NAMES = ("anime_string", "realistic_string", "face_detailer_string")
|
||||
|
||||
FUNCTION = "insert_character"
|
||||
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def insert_character(self, title_character, elements, facial_features, append, anime_template, realistic_template,
|
||||
face_detailer_template):
|
||||
"""
|
||||
This function takes the output of the RandomTitleCharacter node and inserts it into three different templates.
|
||||
The templates are strings that contain placeholders for the different elements of the character.
|
||||
The placeholders are:
|
||||
- {title_character}
|
||||
- {elements}
|
||||
- {facial_features}
|
||||
- {append}
|
||||
|
||||
The function will replace these placeholders with the corresponding elements and return the resulting strings.
|
||||
"""
|
||||
anime = anime_template.format(title_character=title_character, elements=elements,
|
||||
facial_features=facial_features, append=append)
|
||||
realistic = realistic_template.format(title_character=title_character, elements=elements,
|
||||
facial_features=facial_features, append=append)
|
||||
face_detailer = face_detailer_template.format(title_character=title_character, elements=elements,
|
||||
facial_features=facial_features, append=append)
|
||||
|
||||
print("Anime:", anime)
|
||||
print("Realistic:", realistic)
|
||||
print("Face Detailer:", face_detailer)
|
||||
|
||||
return anime, realistic, face_detailer
|
||||
|
||||
|
||||
class RandomElements:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {"forceInput": True, "default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"elements_amount": ("INT", {
|
||||
"default": 4,
|
||||
"min": 1,
|
||||
"max": 100,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("random_elements",)
|
||||
|
||||
FUNCTION = "choose_random_elements"
|
||||
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def choose_random_elements(self, seed, elements_amount):
|
||||
with open("elements.yaml", "r") as file:
|
||||
elements = yaml.load(file, Loader=yaml.FullLoader)
|
||||
|
||||
result = []
|
||||
for element in elements:
|
||||
parts = element.split(':')
|
||||
if len(parts) == 2: # Check if we have two parts
|
||||
amount_str, name = parts
|
||||
amount = int(amount_str)
|
||||
result.extend([name] * amount)
|
||||
else:
|
||||
result.append(element) # Handle elements without ':'
|
||||
|
||||
random.seed(seed)
|
||||
|
||||
chosen_elements = random.sample(result, elements_amount)
|
||||
|
||||
return (", ".join(chosen_elements),)
|
||||
|
||||
|
||||
class ElementsConcatenator:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"title_character_elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"random_elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("concatenated_elements",)
|
||||
|
||||
FUNCTION = "concatenate_elements"
|
||||
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def concatenate_elements(self, title_character_elements, random_elements):
|
||||
return (f"{title_character_elements}, {random_elements}",)
|
||||
|
||||
|
||||
class LlmEnhancer:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"title_character": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"elements": ("STRING", {
|
||||
"forceInput": True,
|
||||
"default": "",
|
||||
"multiline": True
|
||||
}),
|
||||
"system_prompt": ("STRING", {
|
||||
"default": "Enhance the following elements into a short and simple movie still that is coherent with the character.",
|
||||
"multiline": True
|
||||
}),
|
||||
"user_prompt": ("STRING", {
|
||||
"default": "Character in scene: {title_character}, Props: {elements}",
|
||||
"multiline": True
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("enhanced_string",)
|
||||
FUNCTION = "enhance_string"
|
||||
CATEGORY = "GR85/Prompt"
|
||||
|
||||
def enhance_string(self, title_character, elements, system_prompt, user_prompt):
|
||||
"""
|
||||
This makes a call to: http://localhost:7875/v1/chat/completions
|
||||
This service is a ChatGPT model that has been fine-tuned to respond with an enhanced version of the input text.
|
||||
"""
|
||||
client = openai.OpenAI(base_url="http://localhost:7875/v1", api_key="lm-studio")
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="Undi95/Xwin-MLewd-13B-V0.2-GGUF",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt.format(title_character=title_character, elements=elements)},
|
||||
],
|
||||
temperature=0.7,
|
||||
)
|
||||
|
||||
try:
|
||||
return (completion.choices[0].message.content,)
|
||||
except Exception as e:
|
||||
return (str(e),)
|
||||
Reference in New Issue
Block a user