support flux and ideogram nodes

This commit is contained in:
Jimmy Wong
2024-10-27 15:11:15 +08:00
parent 6713c40ff0
commit 2eea88b841
5 changed files with 442 additions and 3 deletions
+12 -2
View File
@@ -19,6 +19,10 @@ from .nodes_omost import (
from .nodes_json import FlowyPreviewJSON, FlowyExtractJSON, ComflowyLoadJSON
from .nodes_http import FlowyHttpRequest
from .nodes_llm import FlowyLLM
from .nodes_upscale import FlowyUpscale
from .nodes_flux import ComflowyFlux
from .nodes_ideogram import FlowyIdeogram
API_KEY_FILE = os.path.join(os.path.dirname(__file__), "api_key.json")
@@ -65,7 +69,10 @@ NODE_CLASS_MAPPINGS = {
"Comflowy_Omost_Preview": ComflowyOmostPreviewNode,
"Comflowy_Omost_Load_Canvas_Python_Code": ComflowyOmostLoadCanvasPythonCodeNode,
"Comflowy_Omost_Load_Canvas_Conditioning": ComflowyOmostLoadCanvasConditioningNode,
"Comflowy_Set_API_Key": ComflowySetAPIKey
"Comflowy_Set_API_Key": ComflowySetAPIKey,
"Comflowy_Upscale": FlowyUpscale,
"Comflowy_Ideogram": FlowyIdeogram,
"Comflowy_Flux": ComflowyFlux,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -79,5 +86,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Comflowy_Omost_Preview": "Comflowy Omost Preview",
"Comflowy_Omost_Load_Canvas_Python_Code": "Comflowy Omost Load Canvas Python Code",
"Comflowy_Omost_Load_Canvas_Conditioning": "Comflowy Omost Load Canvas Conditioning",
"Comflowy_Set_API_Key": "Comflowy Set API Key"
"Comflowy_Set_API_Key": "Comflowy Set API Key",
"Comflowy_Upscale": "Comflowy Upscale",
"Comflowy_Ideogram": "Comflowy Ideogram",
"Comflowy_Flux": "Comflowy Flux",
}
+149
View File
@@ -0,0 +1,149 @@
import time
import requests
import base64
import io
from PIL import Image
import torch
import numpy as np
import logging
import json
from .types import STRING, INT, API_HOST
from .utils import logger, get_nested_value
from .api_key_manager import load_api_key
logger = logging.getLogger(__name__)
class ComflowyFlux:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"prompt": ("STRING", {"multiline": True}),
"version": (["flux-1.1-pro", "flux-pro"],),
"aspect_ratio": ([
"custom",
"1:1",
"16:9",
"2:3",
"3:2",
"4:5",
"5:4",
"9:16",
"3:4",
"4:3"
],),
"height": ("INT", {"default": 256, "min": 256, "max": 1440}),
"width": ("INT", {"default": 256, "min": 256, "max": 1440}),
"prompt_upsampling": (["Off", "On"],),
"safety_tolerance": ([
"1",
"2",
"3",
"4",
"5"
]),
"seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "generate_image_with_flux"
CATEGORY = "Comflowy"
DESCRIPTION = """
Nodes from https://comflowy.com:
- Description: A service to generate images using Flux AI.
- How to use:
- Provide a prompt to generate an image.
- Choose version, aspect ratio, height, width, and seed.
- Height and width are only used when aspect_ratio=custom. Must be a multiple of 32 (if it's not, it will be rounded to nearest multiple of 32).
- Prompt Upsampling: Automatically modify the prompt for more creative generation.
- Safety tolerance, 1 is most strict and 5 is most permissive.
- Make sure to set your API Key using the 'Comflowy Set API Key' node before using this node.
- Output: Returns the generated image.
"""
def generate_image_with_flux(self, prompt, version, aspect_ratio, height, width, seed, prompt_upsampling, safety_tolerance):
api_key = load_api_key()
if not api_key:
error_msg = "API Key is not set. Please use the 'Comflowy Set API Key' node to set a global API Key before using this node."
logger.error(error_msg)
raise ValueError(error_msg)
logger.info(f"开始处理 Flux 图像生成请求。prompt: {prompt}, version: {version}, aspect_ratio: {aspect_ratio}, height: {height}, width: {width}, seed: {seed}, prompt_upsampling: {prompt_upsampling}, safety_tolerance: {safety_tolerance}")
try:
response = requests.post(
f"{API_HOST}/api/open/v0/flux",
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json={
"prompt": prompt,
"version": version,
"aspect_ratio": aspect_ratio,
"height": height,
"width": width,
"prompt_upsampling": prompt_upsampling,
"safety_tolerance": safety_tolerance,
"seed": seed,
}
)
response.raise_for_status()
result = response.json()
logger.info(f"API 请求完成。状态码: {response.status_code}")
logger.debug(f"API 响应内容: {json.dumps(result, indent=2)}")
if not result.get('success'):
logger.error(f"API 请求失败。响应内容: {json.dumps(result, indent=2)}")
raise Exception(f"API 请求失败。响应内容: {json.dumps(result, indent=2)}")
output_url = result.get('data', {}).get('output')
if not output_url or not isinstance(output_url, str):
logger.error(f"完整的 API 响应: {json.dumps(result, indent=2)}")
raise Exception(f"无法获取有效的输出图像 URL。API 响应中没有预期的数据结构。完整响应: {json.dumps(result, indent=2)}")
logger.info(f"获取到的输出 URL: {output_url}")
# 验证 URL 是否可访问
try:
url_check = requests.head(output_url)
url_check.raise_for_status()
except requests.RequestException as e:
logger.error(f"无法访问输出 URL: {str(e)}")
raise Exception(f"无法访问输出 URL: {str(e)}")
# 添加延迟,等待 Replicate 处理完成
time.sleep(10)
img_response = requests.get(output_url, stream=True)
img_response.raise_for_status()
# 将图像数据转换为 PIL Image
img = Image.open(img_response.raw)
# 转换为 numpy 数组
img_np = np.array(img)
# 确保图像是 3 通道 RGB
if len(img_np.shape) == 2: # 灰度图像
img_np = np.stack([img_np] * 3, axis=-1)
elif img_np.shape[-1] == 4: # RGBA 图像
img_np = img_np[:, :, :3]
# 转换为 float32 并归一化到 0-1 范围
img_np = img_np.astype(np.float32) / 255.0
# 转换为 torch tensor,确保形状为 [B,H,W,C]
img_tensor = torch.from_numpy(img_np).unsqueeze(0) # 添加批次维度
logger.info(f"图像处理完成。输出张量形状: {img_tensor.shape}")
return (img_tensor,)
except Exception as e:
error_msg = f"图像生成过程中出错: {str(e)}"
logger.error(error_msg)
logger.exception("详细错误信息:")
# 返回一个错误标记图像,确保形状为 [B,H,W,C]
error_image = torch.zeros((1, 100, 400, 3), dtype=torch.float32)
return (error_image,)
+131
View File
@@ -0,0 +1,131 @@
import time
import requests
import base64
import io
from PIL import Image
import torch
import numpy as np
import logging
import json
from .types import STRING, INT, API_HOST
from .utils import logger, get_nested_value
from .api_key_manager import load_api_key
logger = logging.getLogger(__name__)
class FlowyIdeogram:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"prompt": ("STRING", {"multiline": True}),
"negative_prompt": ("STRING", {"multiline": True}),
"version": (["ideogram-v2-turbo", "ideogram-v2"],),
"resolution": (["None", "512x1536", "576x1408", "576x1472", "576x1536", "640x1024", "640x1344", "640x1408", "640x1472", "640x1536", "704x1152", "704x1216", "704x1280", "704x1344", "704x1408", "704x1472", "720x1280", "736x1312", "768x1024", "768x1088", "768x1152", "768x1216", "768x1232", "768x1280", "768x1344", "832x960", "832x1024", "832x1088", "832x1152", "832x1216", "832x1248", "864x1152", "896x960", "896x1024", "896x1088", "896x1120", "896x1152", "960x832", "960x896", "960x1024", "960x1088", "1024x640", "1024x768", "1024x832", "1024x896", "1024x960", "1024x1024", "1088x768", "1088x832", "1088x896", "1088x960", "1120x896", "1152x704", "1152x768", "1152x832", "1152x864", "1152x896", "1216x704", "1216x768", "1216x832", "1232x768", "1248x832", "1280x704", "1280x720", "1280x768", "1280x800", "1312x736", "1344x640", "1344x704", "1344x768", "1408x576", "1408x640", "1408x704", "1472x576", "1472x640", "1472x704", "1536x512", "1536x576", "1536x640"],),
"style_type": (["None", "Auto", "Realistic", "Design", "Anime", "Render 3D"],),
"aspect_ratio": (["1:1", "4:3", "3:4", "16:9", "9:16", "3:2", "2:3", "16:10", "10:16", "3:1", "1:3"],),
"magic_prompt_option": (["On", "Off"],),
"seed": ("INT", {"default": 0, "min": 0, "max": 2147483647}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "generate_image_with_ideogram"
CATEGORY = "Comflowy"
DESCRIPTION = """
Nodes from https://comflowy.com:
- Description: A service to generate images using Ideogram AI.
- How to use:
- Provide a prompt to generate an image.
- Choose resolution, style type, aspect ratio, and magic prompt option.
- Resolution overrides aspect ratio.
- Magic Prompt will interpret your prompt and optimize it to maximize variety and quality of the images generated. You can also use it to write prompts in different languages.
- Make sure to set your API Key using the 'Comflowy Set API Key' node before using this node.
- Output: Returns the generated image.
"""
def generate_image_with_ideogram(self, prompt, negative_prompt, version, resolution, style_type, aspect_ratio, magic_prompt_option, seed):
api_key = load_api_key()
if not api_key:
error_msg = "API Key is not set. Please use the 'Comflowy Set API Key' node to set a global API Key before using this node."
logger.error(error_msg)
raise ValueError(error_msg)
logger.info(f"开始处理 Ideogram 图像生成请求。prompt: {prompt}, negative_prompt: {negative_prompt}, resolution: {resolution}, style_type: {style_type}, aspect_ratio: {aspect_ratio}, magic_prompt_option: {magic_prompt_option}, seed: {seed}")
try:
response = requests.post(
f"{API_HOST}/api/open/v0/ideogram",
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json={
"prompt": prompt,
"negative_prompt": negative_prompt,
"version": version,
"resolution": resolution if resolution != "None" else None,
"style_type": style_type if style_type != "None" else None,
"aspect_ratio": aspect_ratio,
"magic_prompt_option": magic_prompt_option,
"seed": seed,
}
)
response.raise_for_status()
result = response.json()
logger.info(f"API 请求完成。状态码: {response.status_code}")
logger.debug(f"API 响应内容: {json.dumps(result, indent=2)}")
if not result.get('success'):
logger.error(f"API 请求失败。响应内容: {json.dumps(result, indent=2)}")
raise Exception(f"API 请求失败。响应内容: {json.dumps(result, indent=2)}")
output_url = result.get('data', {}).get('output')
if not output_url or not isinstance(output_url, str):
logger.error(f"完整的 API 响应: {json.dumps(result, indent=2)}")
raise Exception(f"无法获取有效的输出图像 URL。API 响应中没有预期的数据结构。完整响应: {json.dumps(result, indent=2)}")
logger.info(f"获取到的输出 URL: {output_url}")
# 验证 URL 是否可访问
try:
url_check = requests.head(output_url)
url_check.raise_for_status()
except requests.RequestException as e:
logger.error(f"无法访问输出 URL: {str(e)}")
raise Exception(f"无法访问输出 URL: {str(e)}")
# 添加延迟,等待 Replicate 处理完成
time.sleep(10)
img_response = requests.get(output_url, stream=True)
img_response.raise_for_status()
# 将图像数据转换为 PIL Image
img = Image.open(img_response.raw)
# 转换为 numpy 数组
img_np = np.array(img)
# 确保图像是 3 通道 RGB
if len(img_np.shape) == 2: # 灰度图像
img_np = np.stack([img_np] * 3, axis=-1)
elif img_np.shape[-1] == 4: # RGBA 图像
img_np = img_np[:, :, :3]
# 转换为 float32 并归一化到 0-1 范围
img_np = img_np.astype(np.float32) / 255.0
# 转换为 torch tensor,确保形状为 [B,H,W,C]
img_tensor = torch.from_numpy(img_np).unsqueeze(0) # 添加批次维度
logger.info(f"图像处理完成。输出张量形状: {img_tensor.shape}")
return (img_tensor,)
except Exception as e:
error_msg = f"图像生成过程中出错: {str(e)}"
logger.error(error_msg)
logger.exception("详细错误信息:")
# 返回一个错误标记图像,确保形状为 [B,H,W,C]
error_image = torch.zeros((1, 100, 400, 3), dtype=torch.float32)
return (error_image,)
+148
View File
@@ -0,0 +1,148 @@
import time
import requests
import base64
import io
from PIL import Image
import torch
import numpy as np
import logging
import json
from .types import STRING, INT, API_HOST
from .utils import logger, get_nested_value
from .api_key_manager import load_api_key
logger = logging.getLogger(__name__)
class FlowyUpscale:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"scale_factor": ("INT", {"default": 2, "min": 1, "max": 4, "step": 1}),
"model": (["clarity-upscaler"],),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "upscale"
CATEGORY = "Comflowy"
DESCRIPTION = """
Nodes from https://comflowy.com:
- Description: A service to upscale images using AI models.
- How to use:
- Provide an image to upscale.
- Choose the scale factor and the upscaling model.
- Make sure to set your API Key using the 'Comflowy Set API Key' node before using this node.
- Output: Returns the upscaled image.
"""
def upscale(self, image, scale_factor, model):
api_key = load_api_key()
if not api_key:
error_msg = "API Key is not set. Please use the 'Comflowy Set API Key' node to set a global API Key before using this node."
logger.error(error_msg)
raise ValueError(error_msg)
logger.info(f"开始处理图像放大请求。scale_factor: {scale_factor}, model: {model}")
# 处理输入图像
if isinstance(image, torch.Tensor):
if image.dim() == 4:
image = image.squeeze(0) # 移除批次维度
if image.shape[-1] == 3:
image = (image.cpu().numpy() * 255).astype(np.uint8)
elif image.shape[0] == 3:
image = (image.permute(1, 2, 0).cpu().numpy() * 255).astype(np.uint8)
else:
raise ValueError(f"Unsupported image shape: {image.shape}")
elif isinstance(image, np.ndarray):
if image.ndim == 2:
image = np.stack([image] * 3, axis=-1)
elif image.shape[-1] == 1:
image = np.repeat(image, 3, axis=-1)
elif image.shape[-1] != 3:
raise ValueError(f"Unsupported number of channels: {image.shape[-1]}")
image = (image * 255).astype(np.uint8)
else:
raise ValueError(f"Unsupported image type: {type(image)}")
# 将输入图像转换为 JPEG 格式并压缩
buffered = io.BytesIO()
Image.fromarray(image).save(buffered, format="JPEG", quality=85)
img_str = base64.b64encode(buffered.getvalue()).decode()
try:
# 使用 API_HOST 构建 API 请求的 URL
response = requests.post(
f"{API_HOST}/api/open/v0/upscale",
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json={
"image": f"data:image/jpeg;base64,{img_str}",
"scale_factor": scale_factor,
"model": model
}
)
response.raise_for_status()
result = response.json()
logger.info(f"API 请求完成。状态码: {response.status_code}")
logger.debug(f"API 响应内容: {json.dumps(result, indent=2)}")
if not result.get('success'):
logger.error(f"API 请求失败。响应内容: {json.dumps(result, indent=2)}")
raise Exception(f"API 请求失败。响应内容: {json.dumps(result, indent=2)}")
output_url = result.get('data', {}).get('output', [None])[0]
if not output_url:
logger.error(f"完整的 API 响应: {json.dumps(result, indent=2)}")
raise Exception(f"无法获取输出图像 URL。API 响应中没有预期的数据结构。完整响应: {json.dumps(result, indent=2)}")
logger.info(f"获取到的输出 URL: {output_url}")
# 验证 URL 是否可访问
try:
url_check = requests.head(output_url)
url_check.raise_for_status()
except requests.RequestException as e:
logger.error(f"无法访问输出 URL: {str(e)}")
raise Exception(f"无法访问输出 URL: {str(e)}")
# 添加延迟,等待 Replicate 处理完成
time.sleep(10)
img_response = requests.get(output_url, stream=True)
img_response.raise_for_status()
# 将图像数据转换为 PIL Image
img = Image.open(img_response.raw)
# 转换为 numpy 数组
img_np = np.array(img)
# 确保图像是 3 通道 RGB
if len(img_np.shape) == 2: # 灰度图像
img_np = np.stack([img_np] * 3, axis=-1)
elif img_np.shape[-1] == 4: # RGBA 图像
img_np = img_np[:, :, :3]
# 转换为 float32 并归一化到 0-1 范围
img_np = img_np.astype(np.float32) / 255.0
# 转换为 torch tensor,确保形状为 [B,H,W,C]
img_tensor = torch.from_numpy(img_np).unsqueeze(0) # 添加批次维度
logger.info(f"图像处理完成。输出张量形状: {img_tensor.shape}")
logger.info(f"API 请求完成。状态码: {response.status_code}")
logger.debug(f"API 响应内容: {response.text}")
return (img_tensor,)
except Exception as e:
error_msg = f"放大过程中出错: {str(e)}"
logger.error(error_msg)
logger.exception("详细错误信息:")
# 返回一个错误标记图像,确保形状为 [B,H,W,C]
error_image = torch.zeros((1, 100, 400, 3), dtype=torch.float32)
return (error_image,)
+2 -1
View File
@@ -1,6 +1,7 @@
import sys
API_HOST = "https://app.comflowy.com" # "http://127.0.0.1:3000" #
# API_HOST = "https://app.comflowy.com"
API_HOST = "http://127.0.0.1:3000"
FLOAT = (
"FLOAT",
{"default": 1, "min": -sys.float_info.max, "max": sys.float_info.max, "step": 0.01},