diff --git a/__init__.py b/__init__.py index 7b890ae..d2bded5 100644 --- a/__init__.py +++ b/__init__.py @@ -4,6 +4,7 @@ import importlib node_list = [ "api_node", "finetune", +"config_node", ] NODE_CLASS_MAPPINGS = {} diff --git a/nodes/api_node.py b/nodes/api_node.py index 54e6a11..1c64a38 100644 --- a/nodes/api_node.py +++ b/nodes/api_node.py @@ -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, diff --git a/nodes/base.py b/nodes/base.py index 80b8d8a..ed00698 100644 --- a/nodes/base.py +++ b/nodes/base.py @@ -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)}") diff --git a/nodes/config.py b/nodes/config.py index 0711cf5..7f83d0a 100644 --- a/nodes/config.py +++ b/nodes/config.py @@ -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() \ No newline at end of file diff --git a/nodes/config_node.py b/nodes/config_node.py new file mode 100644 index 0000000..2d20c4b --- /dev/null +++ b/nodes/config_node.py @@ -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)", +} diff --git a/nodes/finetune.py b/nodes/finetune.py index 61573f5..d586a57 100644 --- a/nodes/finetune.py +++ b/nodes/finetune.py @@ -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() diff --git a/workflows/BFL-API-workflow.json b/workflows/BFL-API-workflow.json index bc170f6..6f0606f 100644 --- a/workflows/BFL-API-workflow.json +++ b/workflows/BFL-API-workflow.json @@ -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 } \ No newline at end of file