support flux and ideogram nodes
This commit is contained in:
+12
-2
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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,)
|
||||
@@ -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,)
|
||||
@@ -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
@@ -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},
|
||||
|
||||
Reference in New Issue
Block a user