feat: Added config node
This commit is contained in:
@@ -4,6 +4,7 @@ import importlib
|
||||
node_list = [
|
||||
"api_node",
|
||||
"finetune",
|
||||
"config_node",
|
||||
]
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
|
||||
+48
-36
@@ -14,11 +14,12 @@ class FluxPro11(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, width, height, prompt_upsampling, safety_tolerance, output_format, seed):
|
||||
def generate_image(self, prompt, width, height, prompt_upsampling, safety_tolerance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"width": width,
|
||||
@@ -29,7 +30,7 @@ class FluxPro11(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-pro-1.1", arguments)
|
||||
return super().generate_image("flux-pro-1.1", arguments, config)
|
||||
|
||||
|
||||
class FluxDev(BaseFlux):
|
||||
@@ -47,11 +48,12 @@ class FluxDev(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, width, height, steps, prompt_upsampling, safety_tolerance, guidance, output_format, seed):
|
||||
def generate_image(self, prompt, width, height, steps, prompt_upsampling, safety_tolerance, guidance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"width": width,
|
||||
@@ -64,7 +66,7 @@ class FluxDev(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-dev", arguments)
|
||||
return super().generate_image("flux-dev", arguments, config)
|
||||
|
||||
class FluxDevRedux(BaseFlux):
|
||||
@classmethod
|
||||
@@ -82,11 +84,12 @@ class FluxDevRedux(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, image_prompt, width, height, steps, prompt_upsampling, safety_tolerance, guidance, output_format, seed):
|
||||
def generate_image(self, prompt, image_prompt, width, height, steps, prompt_upsampling, safety_tolerance, guidance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"image_prompt": image_prompt,
|
||||
@@ -100,7 +103,7 @@ class FluxDevRedux(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-dev", arguments)
|
||||
return super().generate_image("flux-dev", arguments, config)
|
||||
|
||||
class FluxPro(BaseFlux):
|
||||
@classmethod
|
||||
@@ -118,11 +121,12 @@ class FluxPro(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, width, height, steps, prompt_upsampling, safety_tolerance, guidance, interval, output_format, seed):
|
||||
def generate_image(self, prompt, width, height, steps, prompt_upsampling, safety_tolerance, guidance, interval, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"width": width,
|
||||
@@ -136,7 +140,7 @@ class FluxPro(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-pro", arguments)
|
||||
return super().generate_image("flux-pro", arguments, config)
|
||||
|
||||
|
||||
class FluxPro11Ultra(BaseFlux):
|
||||
@@ -151,11 +155,12 @@ class FluxPro11Ultra(BaseFlux):
|
||||
"raw": ("BOOLEAN", {"default": False})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, aspect_ratio, safety_tolerance, output_format, raw, seed):
|
||||
def generate_image(self, prompt, aspect_ratio, safety_tolerance, output_format, raw, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
@@ -165,7 +170,7 @@ class FluxPro11Ultra(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-pro-1.1-ultra", arguments)
|
||||
return super().generate_image("flux-pro-1.1-ultra", arguments, config)
|
||||
|
||||
class FluxProFill(BaseFlux):
|
||||
@classmethod
|
||||
@@ -182,12 +187,13 @@ class FluxProFill(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, image, mask=None, prompt=None, steps=50, prompt_upsampling=False, guidance=60.0,
|
||||
safety_tolerance=2, output_format="jpeg", seed=-1):
|
||||
safety_tolerance=2, output_format="jpeg", seed=-1, config=None):
|
||||
arguments = {
|
||||
"image": image,
|
||||
"steps": steps,
|
||||
@@ -209,7 +215,7 @@ class FluxProFill(BaseFlux):
|
||||
if prompt is not None:
|
||||
arguments["prompt"] = prompt
|
||||
|
||||
return super().generate_image("flux-pro-1.0-fill", arguments)
|
||||
return super().generate_image("flux-pro-1.0-fill", arguments, config)
|
||||
|
||||
class FluxProCanny(BaseFlux):
|
||||
@classmethod
|
||||
@@ -225,11 +231,12 @@ class FluxProCanny(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, control_image, prompt_upsampling, steps, guidance, safety_tolerance, output_format, seed):
|
||||
def generate_image(self, prompt, control_image, prompt_upsampling, steps, guidance, safety_tolerance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"control_image": control_image,
|
||||
@@ -241,7 +248,7 @@ class FluxProCanny(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-pro-1.0-canny", arguments)
|
||||
return super().generate_image("flux-pro-1.0-canny", arguments, config)
|
||||
|
||||
|
||||
class FluxProDepth(BaseFlux):
|
||||
@@ -258,11 +265,12 @@ class FluxProDepth(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, control_image, prompt_upsampling, steps, guidance, safety_tolerance, output_format, seed):
|
||||
def generate_image(self, prompt, control_image, prompt_upsampling, steps, guidance, safety_tolerance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"control_image": control_image,
|
||||
@@ -274,7 +282,7 @@ class FluxProDepth(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-pro-1.0-depth", arguments)
|
||||
return super().generate_image("flux-pro-1.0-depth", arguments, config)
|
||||
|
||||
class FluxPro11Redux(BaseFlux):
|
||||
@classmethod
|
||||
@@ -290,11 +298,12 @@ class FluxPro11Redux(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, image_prompt, width, height, prompt_upsampling, safety_tolerance, output_format, seed):
|
||||
def generate_image(self, prompt, image_prompt, width, height, prompt_upsampling, safety_tolerance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"image_prompt": image_prompt,
|
||||
@@ -306,7 +315,7 @@ class FluxPro11Redux(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-pro-1.1", arguments)
|
||||
return super().generate_image("flux-pro-1.1", arguments, config)
|
||||
|
||||
class FluxPro11UltraRedux(BaseFlux):
|
||||
@classmethod
|
||||
@@ -322,11 +331,12 @@ class FluxPro11UltraRedux(BaseFlux):
|
||||
"raw": ("BOOLEAN", {"default": False})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, image_prompt, image_prompt_strength, aspect_ratio, safety_tolerance, output_format, raw, seed):
|
||||
def generate_image(self, prompt, image_prompt, image_prompt_strength, aspect_ratio, safety_tolerance, output_format, raw, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"image_prompt": image_prompt,
|
||||
@@ -338,7 +348,7 @@ class FluxPro11UltraRedux(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-pro-1.1-ultra", arguments)
|
||||
return super().generate_image("flux-pro-1.1-ultra", arguments, config)
|
||||
|
||||
class FluxKontextPro(BaseFlux):
|
||||
@classmethod
|
||||
@@ -353,11 +363,12 @@ class FluxKontextPro(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, input_image, aspect_ratio, prompt_upsampling, safety_tolerance, output_format, seed):
|
||||
def generate_image(self, prompt, input_image, aspect_ratio, prompt_upsampling, safety_tolerance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"input_image": input_image,
|
||||
@@ -368,7 +379,7 @@ class FluxKontextPro(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-kontext-pro", arguments)
|
||||
return super().generate_image("flux-kontext-pro", arguments, config)
|
||||
|
||||
class FluxKontextMax(BaseFlux):
|
||||
@classmethod
|
||||
@@ -383,11 +394,12 @@ class FluxKontextMax(BaseFlux):
|
||||
"output_format": (["jpeg", "png"], {"default": "jpeg"})
|
||||
},
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1})
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, prompt, input_image, aspect_ratio, prompt_upsampling, safety_tolerance, output_format, seed):
|
||||
def generate_image(self, prompt, input_image, aspect_ratio, prompt_upsampling, safety_tolerance, output_format, seed=-1, config=None):
|
||||
arguments = {
|
||||
"prompt": prompt,
|
||||
"input_image": input_image,
|
||||
@@ -398,7 +410,7 @@ class FluxKontextMax(BaseFlux):
|
||||
}
|
||||
if seed != -1:
|
||||
arguments["seed"] = seed
|
||||
return super().generate_image("flux-kontext-max", arguments)
|
||||
return super().generate_image("flux-kontext-max", arguments, config)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"FluxPro_BFL": FluxPro,
|
||||
|
||||
+17
-9
@@ -7,6 +7,7 @@ import os
|
||||
import time
|
||||
from .status import Status
|
||||
from .config import config_loader
|
||||
from .config_node import get_config_loader
|
||||
|
||||
class BaseFlux:
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
@@ -44,9 +45,13 @@ class BaseFlux:
|
||||
if width % 32 != 0 or height % 32 != 0:
|
||||
raise ValueError(f"Width {width} and height {height} must be multiples of 32.")
|
||||
|
||||
def post_request(self, url_path, arguments):
|
||||
post_url = config_loader.create_url(url_path)
|
||||
headers = {"x-key": os.environ["X_KEY"]}
|
||||
def post_request(self, url_path, arguments, config_override=None):
|
||||
# Use ConfigLoader with optional config override
|
||||
config_loader_instance = get_config_loader(config_override)
|
||||
config_loader_instance.set_x_key() # Ensure X_KEY is set in environment
|
||||
|
||||
post_url = config_loader_instance.create_url(url_path)
|
||||
headers = {"x-key": config_loader_instance.get_x_key()}
|
||||
response = requests.post(post_url, json=arguments, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
@@ -55,9 +60,12 @@ class BaseFlux:
|
||||
print(f"Error initiating request: {response.status_code}, {response.text}")
|
||||
return None
|
||||
|
||||
def get_result(self, task_id, output_format="jpeg", max_attempts=20):
|
||||
headers = {"x-key": os.environ["X_KEY"]}
|
||||
get_url = config_loader.create_url(f"get_result?id={task_id}")
|
||||
def get_result(self, task_id, output_format="jpeg", max_attempts=20, config_override=None):
|
||||
# Use ConfigLoader with optional config override
|
||||
config_loader_instance = get_config_loader(config_override)
|
||||
|
||||
headers = {"x-key": config_loader_instance.get_x_key()}
|
||||
get_url = config_loader_instance.create_url(f"get_result?id={task_id}")
|
||||
attempt = 1
|
||||
|
||||
while attempt <= max_attempts:
|
||||
@@ -104,15 +112,15 @@ class BaseFlux:
|
||||
print(f"All attempts exhausted for task_id {task_id}. Returning blank image.")
|
||||
return self.create_blank_image()
|
||||
|
||||
def generate_image(self, url_path, arguments):
|
||||
def generate_image(self, url_path, arguments, config_override=None):
|
||||
if "width" in arguments and "height" in arguments:
|
||||
self.check_multiple_of_32(arguments["width"], arguments["height"])
|
||||
|
||||
try:
|
||||
task_id = self.post_request(url_path, arguments)
|
||||
task_id = self.post_request(url_path, arguments, config_override)
|
||||
if task_id:
|
||||
print(f"Task ID '{task_id}'")
|
||||
return self.get_result(task_id, output_format=arguments.get("output_format", "jpeg"))
|
||||
return self.get_result(task_id, output_format=arguments.get("output_format", "jpeg"), config_override=config_override)
|
||||
return self.create_blank_image()
|
||||
except Exception as e:
|
||||
print(f"Error generating image: {str(e)}")
|
||||
|
||||
+28
-1
@@ -3,7 +3,16 @@ import configparser
|
||||
from urllib.parse import urljoin
|
||||
|
||||
class ConfigLoader:
|
||||
def __init__(self):
|
||||
def __init__(self, config_override=None):
|
||||
"""
|
||||
Initialize ConfigLoader with optional config override.
|
||||
|
||||
Args:
|
||||
config_override: Optional dict with config values to override file-based config
|
||||
"""
|
||||
self.config_override = config_override
|
||||
|
||||
# Load file-based config
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
parent_dir = os.path.dirname(current_dir)
|
||||
config_path = os.path.join(parent_dir, "config.ini")
|
||||
@@ -18,6 +27,15 @@ class ConfigLoader:
|
||||
}
|
||||
|
||||
def get_key(self, section, key):
|
||||
"""Get a config value, preferring override over file config."""
|
||||
# Check for override values first
|
||||
if self.config_override:
|
||||
if section == 'API' and key == 'X_KEY' and self.config_override.get('x_key'):
|
||||
return self.config_override['x_key']
|
||||
elif section == 'API' and key == 'BASE_URL' and self.config_override.get('base_url'):
|
||||
return self.config_override['base_url']
|
||||
|
||||
# Fall back to file config
|
||||
try:
|
||||
return self.config[section][key]
|
||||
except KeyError:
|
||||
@@ -33,6 +51,10 @@ class ConfigLoader:
|
||||
If provided, uses regional endpoint instead of global
|
||||
"""
|
||||
try:
|
||||
# Use region from config override if available and region not explicitly provided
|
||||
if not region and self.config_override and self.config_override.get("default_region"):
|
||||
region = self.config_override["default_region"]
|
||||
|
||||
if region and region in self.regional_endpoints:
|
||||
base_url = self.regional_endpoints[region]
|
||||
return urljoin(base_url, f"/v1/{path}")
|
||||
@@ -49,11 +71,16 @@ class ConfigLoader:
|
||||
return self.regional_endpoints[region]
|
||||
|
||||
def set_x_key(self):
|
||||
"""Set the X_KEY environment variable from config or override."""
|
||||
try:
|
||||
x_key = self.get_key('API', 'X_KEY')
|
||||
os.environ["X_KEY"] = x_key
|
||||
except KeyError as e:
|
||||
print(f"Error: {str(e)}")
|
||||
|
||||
def get_x_key(self):
|
||||
"""Get the API key directly without setting environment variable."""
|
||||
return self.get_key('API', 'X_KEY')
|
||||
|
||||
# Create a singleton instance to be shared across modules
|
||||
config_loader = ConfigLoader()
|
||||
@@ -0,0 +1,77 @@
|
||||
from .config import ConfigLoader
|
||||
|
||||
class FluxConfig_BFL:
|
||||
"""
|
||||
Configuration node for BFL API settings.
|
||||
Provides optional configuration that can be connected to other nodes.
|
||||
If not connected, nodes will use the default file-based configuration.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"x_key": ("STRING", {
|
||||
"default": "",
|
||||
"multiline": False,
|
||||
"placeholder": "Enter your BFL API key"
|
||||
}),
|
||||
"base_url": ("STRING", {
|
||||
"default": "https://api.bfl.ml/v1/",
|
||||
"multiline": False,
|
||||
"placeholder": "Base API URL"
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"region": (["none", "us", "eu"], {
|
||||
"default": "none",
|
||||
"tooltip": "Regional endpoint for finetuning operations"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BFL_CONFIG",)
|
||||
RETURN_NAMES = ("config",)
|
||||
FUNCTION = "create_config"
|
||||
CATEGORY = "BFL/Config"
|
||||
|
||||
def create_config(self, x_key, base_url, region="none"):
|
||||
"""Create a configuration object with the provided settings."""
|
||||
|
||||
# Regional endpoints for finetuning (required by BFL API)
|
||||
regional_endpoints = {
|
||||
"us": "https://api.us1.bfl.ai",
|
||||
"eu": "https://api.eu1.bfl.ai"
|
||||
}
|
||||
|
||||
config = {
|
||||
"x_key": x_key.strip() if x_key.strip() else None,
|
||||
"base_url": base_url.strip() if base_url.strip() else "https://api.bfl.ml/v1/",
|
||||
"regional_endpoints": regional_endpoints,
|
||||
"default_region": region if region != "none" else None
|
||||
}
|
||||
|
||||
return (config,)
|
||||
|
||||
|
||||
def get_config_loader(config_override=None):
|
||||
"""
|
||||
Get a ConfigLoader instance with optional config override.
|
||||
|
||||
Args:
|
||||
config_override: Optional config dict from FluxConfig_BFL node
|
||||
|
||||
Returns:
|
||||
ConfigLoader instance
|
||||
"""
|
||||
return ConfigLoader(config_override)
|
||||
|
||||
|
||||
# Node mappings for ComfyUI
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"FluxConfig_BFL": FluxConfig_BFL,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"FluxConfig_BFL": "Flux Config (BFL)",
|
||||
}
|
||||
+37
-24
@@ -4,6 +4,7 @@ import json
|
||||
import base64
|
||||
from .base import BaseFinetuneFlux
|
||||
from .config import config_loader
|
||||
from .config_node import get_config_loader
|
||||
|
||||
class FluxFinetune:
|
||||
RETURN_TYPES = ("STRING",)
|
||||
@@ -310,13 +311,14 @@ class FluxProFinetune(BaseFinetuneFlux):
|
||||
"image_prompt": ("STRING", {"default": ""}),
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"webhook_url": ("STRING", {"default": ""}),
|
||||
"webhook_secret": ("STRING", {"default": ""})
|
||||
"webhook_secret": ("STRING", {"default": ""}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, finetune_id, region, prompt, finetune_strength, steps, guidance, width, height,
|
||||
prompt_upsampling, safety_tolerance, output_format, image_prompt="", seed=-1,
|
||||
webhook_url="", webhook_secret=""):
|
||||
webhook_url="", webhook_secret="", config=None):
|
||||
arguments = {
|
||||
"finetune_id": finetune_id,
|
||||
"prompt": prompt,
|
||||
@@ -339,23 +341,27 @@ class FluxProFinetune(BaseFinetuneFlux):
|
||||
if webhook_secret:
|
||||
arguments["webhook_secret"] = webhook_secret
|
||||
|
||||
return self.generate_regional_image("flux-pro-finetuned", arguments, region)
|
||||
return self.generate_regional_image("flux-pro-finetuned", arguments, region, config)
|
||||
|
||||
def generate_regional_image(self, endpoint, arguments, region):
|
||||
def generate_regional_image(self, endpoint, arguments, region, config_override=None):
|
||||
if "width" in arguments and "height" in arguments:
|
||||
self.check_multiple_of_32(arguments["width"], arguments["height"])
|
||||
|
||||
try:
|
||||
post_url = config_loader.create_url(endpoint, region=region)
|
||||
headers = {"x-key": os.environ["X_KEY"]}
|
||||
# Use ConfigLoader with optional config override
|
||||
config_loader_instance = get_config_loader(config_override)
|
||||
config_loader_instance.set_x_key() # Ensure X_KEY is set in environment
|
||||
|
||||
post_url = config_loader_instance.create_url(endpoint, region=region)
|
||||
headers = {"x-key": config_loader_instance.get_x_key()}
|
||||
response = requests.post(post_url, json=arguments, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
task_id = response.json().get("id")
|
||||
if task_id:
|
||||
regional_endpoint = config_loader.get_regional_endpoint(region)
|
||||
regional_endpoint = config_loader_instance.get_regional_endpoint(region)
|
||||
print(f"Finetune Task ID '{task_id}' (Region: {region.upper()} - {regional_endpoint})")
|
||||
return self.get_result(task_id, output_format=arguments.get("output_format", "jpeg"))
|
||||
return self.get_result(task_id, output_format=arguments.get("output_format", "jpeg"), config_override=config_override)
|
||||
else:
|
||||
print(f"Error initiating regional finetune request: {response.status_code}, {response.text}")
|
||||
return self.create_blank_image()
|
||||
@@ -381,12 +387,13 @@ class FluxProDepthFinetune(BaseFinetuneFlux):
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"webhook_url": ("STRING", {"default": ""}),
|
||||
"webhook_secret": ("STRING", {"default": ""})
|
||||
"webhook_secret": ("STRING", {"default": ""}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, finetune_id, prompt, control_image, finetune_strength, prompt_upsampling,
|
||||
steps, output_format, guidance, safety_tolerance, seed=-1, webhook_url="", webhook_secret=""):
|
||||
steps, output_format, guidance, safety_tolerance, seed=-1, webhook_url="", webhook_secret="", config=None):
|
||||
arguments = {
|
||||
"finetune_id": finetune_id,
|
||||
"prompt": prompt,
|
||||
@@ -406,7 +413,7 @@ class FluxProDepthFinetune(BaseFinetuneFlux):
|
||||
if webhook_secret:
|
||||
arguments["webhook_secret"] = webhook_secret
|
||||
|
||||
return super().generate_image("flux-pro-1.0-depth-finetuned", arguments)
|
||||
return super().generate_image("flux-pro-1.0-depth-finetuned", arguments, config)
|
||||
|
||||
class FluxProCannyFinetune(BaseFinetuneFlux):
|
||||
@classmethod
|
||||
@@ -428,13 +435,14 @@ class FluxProCannyFinetune(BaseFinetuneFlux):
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"webhook_url": ("STRING", {"default": ""}),
|
||||
"webhook_secret": ("STRING", {"default": ""})
|
||||
"webhook_secret": ("STRING", {"default": ""}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, finetune_id, prompt, control_image, finetune_strength, canny_low_threshold,
|
||||
canny_high_threshold, prompt_upsampling, steps, output_format, guidance, safety_tolerance,
|
||||
seed=-1, webhook_url="", webhook_secret=""):
|
||||
seed=-1, webhook_url="", webhook_secret="", config=None):
|
||||
arguments = {
|
||||
"finetune_id": finetune_id,
|
||||
"prompt": prompt,
|
||||
@@ -456,7 +464,7 @@ class FluxProCannyFinetune(BaseFinetuneFlux):
|
||||
if webhook_secret:
|
||||
arguments["webhook_secret"] = webhook_secret
|
||||
|
||||
return super().generate_image("flux-pro-1.0-canny-finetuned", arguments)
|
||||
return super().generate_image("flux-pro-1.0-canny-finetuned", arguments, config)
|
||||
|
||||
class FluxProFillFinetune(BaseFinetuneFlux):
|
||||
@classmethod
|
||||
@@ -482,7 +490,7 @@ class FluxProFillFinetune(BaseFinetuneFlux):
|
||||
}
|
||||
|
||||
def generate_image(self, finetune_id, image, finetune_strength, steps, prompt_upsampling, guidance,
|
||||
safety_tolerance, output_format, mask="", prompt="", seed=-1, webhook_url="", webhook_secret=""):
|
||||
safety_tolerance, output_format, mask="", prompt="", seed=-1, webhook_url="", webhook_secret="", config=None):
|
||||
arguments = {
|
||||
"finetune_id": finetune_id,
|
||||
"image": image,
|
||||
@@ -505,7 +513,7 @@ class FluxProFillFinetune(BaseFinetuneFlux):
|
||||
if webhook_secret:
|
||||
arguments["webhook_secret"] = webhook_secret
|
||||
|
||||
return super().generate_image("flux-pro-1.0-fill-finetuned", arguments)
|
||||
return super().generate_image("flux-pro-1.0-fill-finetuned", arguments, config)
|
||||
|
||||
class FluxPro11UltraFinetune(BaseFinetuneFlux):
|
||||
@classmethod
|
||||
@@ -524,12 +532,13 @@ class FluxPro11UltraFinetune(BaseFinetuneFlux):
|
||||
"optional": {
|
||||
"seed": ("INT", {"default": -1}),
|
||||
"webhook_url": ("STRING", {"default": ""}),
|
||||
"webhook_secret": ("STRING", {"default": ""})
|
||||
"webhook_secret": ("STRING", {"default": ""}),
|
||||
"config": ("BFL_CONFIG",)
|
||||
}
|
||||
}
|
||||
|
||||
def generate_image(self, finetune_id, region, prompt, finetune_strength, aspect_ratio, safety_tolerance,
|
||||
output_format, raw, seed=-1, webhook_url="", webhook_secret=""):
|
||||
output_format, raw, seed=-1, webhook_url="", webhook_secret="", config=None):
|
||||
arguments = {
|
||||
"finetune_id": finetune_id,
|
||||
"prompt": prompt,
|
||||
@@ -547,20 +556,24 @@ class FluxPro11UltraFinetune(BaseFinetuneFlux):
|
||||
if webhook_secret:
|
||||
arguments["webhook_secret"] = webhook_secret
|
||||
|
||||
return self.generate_regional_image("flux-pro-1.1-ultra-finetuned", arguments, region)
|
||||
return self.generate_regional_image("flux-pro-1.1-ultra-finetuned", arguments, region, config)
|
||||
|
||||
def generate_regional_image(self, endpoint, arguments, region):
|
||||
def generate_regional_image(self, endpoint, arguments, region, config_override=None):
|
||||
try:
|
||||
post_url = config_loader.create_url(endpoint, region=region)
|
||||
headers = {"x-key": os.environ["X_KEY"]}
|
||||
# Use ConfigLoader with optional config override
|
||||
config_loader_instance = get_config_loader(config_override)
|
||||
config_loader_instance.set_x_key() # Ensure X_KEY is set in environment
|
||||
|
||||
post_url = config_loader_instance.create_url(endpoint, region=region)
|
||||
headers = {"x-key": config_loader_instance.get_x_key()}
|
||||
response = requests.post(post_url, json=arguments, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
task_id = response.json().get("id")
|
||||
if task_id:
|
||||
regional_endpoint = config_loader.get_regional_endpoint(region)
|
||||
regional_endpoint = config_loader_instance.get_regional_endpoint(region)
|
||||
print(f"Finetune Task ID '{task_id}' (Region: {region.upper()} - {regional_endpoint})")
|
||||
return self.get_result(task_id, output_format=arguments.get("output_format", "jpeg"))
|
||||
return self.get_result(task_id, output_format=arguments.get("output_format", "jpeg"), config_override=config_override)
|
||||
else:
|
||||
print(f"Error initiating regional finetune request: {response.status_code}, {response.text}")
|
||||
return self.create_blank_image()
|
||||
|
||||
+212
-211
@@ -1,21 +1,23 @@
|
||||
{
|
||||
"last_node_id": 19,
|
||||
"last_link_id": 10,
|
||||
"id": "da553514-59db-4d0f-ab66-b010a0ba3a55",
|
||||
"revision": 0,
|
||||
"last_node_id": 20,
|
||||
"last_link_id": 13,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 8,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1331.552978515625,
|
||||
"1": 215.39434814453125
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"pos": [
|
||||
1331.552978515625,
|
||||
215.39434814453125
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 2,
|
||||
"order": 4,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
@@ -25,6 +27,8 @@
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.49",
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
@@ -32,27 +36,33 @@
|
||||
{
|
||||
"id": 7,
|
||||
"type": "FluxPro11_BFL",
|
||||
"pos": {
|
||||
"0": 711.2532958984375,
|
||||
"1": 215.6094970703125
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 244
|
||||
},
|
||||
"pos": [
|
||||
711.2532958984375,
|
||||
215.6094970703125
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
256
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 2,
|
||||
"inputs": [],
|
||||
"order": 0,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "config",
|
||||
"shape": 7,
|
||||
"type": "BFL_CONFIG",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
5
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -65,72 +75,40 @@
|
||||
false,
|
||||
2,
|
||||
"png",
|
||||
1259,
|
||||
"randomize"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"type": "FluxPro11Ultra_BFL",
|
||||
"pos": {
|
||||
"0": 719.71240234375,
|
||||
"1": -135.97286987304688
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 220
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
10
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FluxPro11Ultra_BFL"
|
||||
},
|
||||
"widgets_values": [
|
||||
"istanbul hagia sophia",
|
||||
"16:9",
|
||||
2,
|
||||
"jpeg",
|
||||
true,
|
||||
675,
|
||||
955,
|
||||
"randomize"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "FluxPro_BFL",
|
||||
"pos": {
|
||||
"0": 710.218994140625,
|
||||
"1": 561.643798828125
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 316
|
||||
},
|
||||
"pos": [
|
||||
710.218994140625,
|
||||
561.643798828125
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
328
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 2,
|
||||
"inputs": [],
|
||||
"order": 1,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "config",
|
||||
"shape": 7,
|
||||
"type": "BFL_CONFIG",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
7
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -153,27 +131,33 @@
|
||||
{
|
||||
"id": 9,
|
||||
"type": "FluxDev_BFL",
|
||||
"pos": {
|
||||
"0": 710.954833984375,
|
||||
"1": 976.184814453125
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 292
|
||||
},
|
||||
"pos": [
|
||||
710.954833984375,
|
||||
976.184814453125
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
304
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 2,
|
||||
"inputs": [],
|
||||
"order": 2,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "config",
|
||||
"shape": 7,
|
||||
"type": "BFL_CONFIG",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
6
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -188,24 +172,24 @@
|
||||
"2",
|
||||
3,
|
||||
884,
|
||||
42,
|
||||
737,
|
||||
"randomize"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1335,
|
||||
"1": 977
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"pos": [
|
||||
1335,
|
||||
977
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 2,
|
||||
"order": 6,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
@@ -215,6 +199,8 @@
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.49",
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
@@ -222,17 +208,17 @@
|
||||
{
|
||||
"id": 12,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1333,
|
||||
"1": 562
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"pos": [
|
||||
1333,
|
||||
562
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 2,
|
||||
"order": 5,
|
||||
"mode": 4,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
@@ -242,24 +228,104 @@
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.49",
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1333,
|
||||
"1": -137.97286987304688
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
"id": 20,
|
||||
"type": "FluxConfig_BFL",
|
||||
"pos": [
|
||||
717.29052734375,
|
||||
-134.6406707763672
|
||||
],
|
||||
"size": [
|
||||
270,
|
||||
106
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "config",
|
||||
"type": "BFL_CONFIG",
|
||||
"links": [
|
||||
13
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FluxConfig_BFL"
|
||||
},
|
||||
"widgets_values": [
|
||||
"x-key",
|
||||
"https://api.bfl.ml/v1/",
|
||||
"none"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"type": "FluxPro11Ultra_BFL",
|
||||
"pos": [
|
||||
1012.481689453125,
|
||||
-136.26632690429688
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
232
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "config",
|
||||
"shape": 7,
|
||||
"type": "BFL_CONFIG",
|
||||
"link": 13
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
10
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FluxPro11Ultra_BFL"
|
||||
},
|
||||
"widgets_values": [
|
||||
"istanbul hagia sophia",
|
||||
"16:9",
|
||||
2,
|
||||
"jpeg",
|
||||
true,
|
||||
980,
|
||||
"randomize"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1434.077392578125,
|
||||
-139.47886657714844
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
@@ -269,6 +335,8 @@
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.49",
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
@@ -306,22 +374,32 @@
|
||||
14,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
13,
|
||||
20,
|
||||
0,
|
||||
19,
|
||||
0,
|
||||
"BFL_CONFIG"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Flux Pro 1.1 Ultra",
|
||||
"bounding": [
|
||||
702,
|
||||
-227,
|
||||
857,
|
||||
354
|
||||
707.29052734375,
|
||||
-213.07887268066406,
|
||||
946.786865234375,
|
||||
329.6000061035156
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"title": "Flux Pro",
|
||||
"bounding": [
|
||||
700,
|
||||
@@ -334,6 +412,7 @@
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "Flux Dev",
|
||||
"bounding": [
|
||||
702,
|
||||
@@ -346,6 +425,7 @@
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"title": "Flux Pro 1.1",
|
||||
"bounding": [
|
||||
701,
|
||||
@@ -361,93 +441,14 @@
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.6727499949325623,
|
||||
"scale": 0.6727499949325617,
|
||||
"offset": [
|
||||
95.41390143669695,
|
||||
214.70408909184692
|
||||
401.3714466821817,
|
||||
784.6786514326493
|
||||
]
|
||||
},
|
||||
"groupNodes": {
|
||||
"Flux Pro 1.1 Ultra": {
|
||||
"nodes": [
|
||||
{
|
||||
"id": -1,
|
||||
"type": "FluxPro11Ultra_BFL",
|
||||
"pos": {
|
||||
"0": 715,
|
||||
"1": -85
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 220
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FluxPro11Ultra_BFL"
|
||||
},
|
||||
"widgets_values": [
|
||||
"",
|
||||
"16:9",
|
||||
2,
|
||||
"jpeg",
|
||||
false,
|
||||
42,
|
||||
"randomize"
|
||||
],
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"id": -1,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1360,
|
||||
"1": -80
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 26
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"index": 1
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
13,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"external": []
|
||||
}
|
||||
}
|
||||
"groupNodes": {},
|
||||
"frontendVersion": "1.24.4"
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user