From aeba0b3a268eeb66c3a12cae6fd97f7c2d28f36f Mon Sep 17 00:00:00 2001 From: Jedrzej Kosinski Date: Thu, 29 May 2025 14:14:27 -0700 Subject: [PATCH] Reduce code duplication for [pro] and [max], rename Pro and Max to [pro] and [max] to be consistent with other BFL nodes, make default seed for Kontext nodes be 1234. since 0 is interpreted by API as 'choose random seed' (#8337) --- comfy_api_nodes/apis/bfl_api.py | 16 ---- comfy_api_nodes/nodes_bfl.py | 153 +++----------------------------- 2 files changed, 12 insertions(+), 157 deletions(-) diff --git a/comfy_api_nodes/apis/bfl_api.py b/comfy_api_nodes/apis/bfl_api.py index 504e507e1..0e90aef7c 100644 --- a/comfy_api_nodes/apis/bfl_api.py +++ b/comfy_api_nodes/apis/bfl_api.py @@ -125,22 +125,6 @@ class BFLFluxKontextProGenerateRequest(BaseModel): None, description='Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation.' ) -class BFLFluxKontextMaxGenerateRequest(BaseModel): - prompt: str = Field(..., description='The text prompt for what you wannt to edit.') - input_image: Optional[str] = Field(None, description='Image to edit in base64 format') - seed: Optional[int] = Field(None, description='The seed value for reproducibility.') - guidance: confloat(ge=0.1, le=99.0) = Field(..., description='Guidance strength for the image generation process') - steps: conint(ge=1, le=150) = Field(..., description='Number of steps for the image generation process') - safety_tolerance: Optional[conint(ge=0, le=2)] = Field( - 2, description='Tolerance level for input and output moderation. Between 0 and 2, 0 being most strict, 6 being least strict. Defaults to 2.' - ) - output_format: Optional[BFLOutputFormat] = Field( - BFLOutputFormat.png, description="Output format for the generated image. Can be 'jpeg' or 'png'.", examples=['png'] - ) - aspect_ratio: Optional[str] = Field(None, description='Aspect ratio of the image between 21:9 and 9:21.') - prompt_upsampling: Optional[bool] = Field( - None, description='Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation.' - ) class BFLFluxProUltraGenerateRequest(BaseModel): prompt: str = Field(..., description='The text prompt for image generation.') diff --git a/comfy_api_nodes/nodes_bfl.py b/comfy_api_nodes/nodes_bfl.py index a762472e6..010564704 100644 --- a/comfy_api_nodes/nodes_bfl.py +++ b/comfy_api_nodes/nodes_bfl.py @@ -272,7 +272,7 @@ class FluxProUltraImageNode(ComfyNodeABC): class FluxKontextProImageNode(ComfyNodeABC): """ - Edits images using Flux.1 Kontext Pro via api based on prompt and resolution. + Edits images using Flux.1 Kontext [pro] via api based on prompt and aspect ratio. """ MINIMUM_RATIO = 1 / 4 @@ -321,7 +321,7 @@ class FluxKontextProImageNode(ComfyNodeABC): "seed": ( IO.INT, { - "default": 0, + "default": 1234, "min": 0, "max": 0xFFFFFFFFFFFFFFFF, "control_after_generate": True, @@ -366,6 +366,8 @@ class FluxKontextProImageNode(ComfyNodeABC): API_NODE = True CATEGORY = "api node/image/BFL" + BFL_PATH = "/proxy/bfl/flux-kontext-pro/generate" + def api_call( self, prompt: str, @@ -382,7 +384,7 @@ class FluxKontextProImageNode(ComfyNodeABC): validate_string(prompt, strip_whitespace=False) operation = SynchronousOperation( endpoint=ApiEndpoint( - path="/proxy/bfl/flux-kontext-pro/generate", + path=self.BFL_PATH, method=HttpMethod.POST, request_model=BFLFluxKontextProGenerateRequest, response_model=BFLFluxProGenerateResponse, @@ -411,146 +413,15 @@ class FluxKontextProImageNode(ComfyNodeABC): output_image = handle_bfl_synchronous_operation(operation, node_id=unique_id) return (output_image,) -class FluxKontextMaxImageNode(ComfyNodeABC): + +class FluxKontextMaxImageNode(FluxKontextProImageNode): """ - Edits images using Flux.1 Kontext Max via api based on prompt and resolution. + Edits images using Flux.1 Kontext [max] via api based on prompt and aspect ratio. """ - MINIMUM_RATIO = 1 / 4 - MAXIMUM_RATIO = 4 / 1 - MINIMUM_RATIO_STR = "1:4" - MAXIMUM_RATIO_STR = "4:1" + DESCRIPTION = cleandoc(__doc__ or "") + BFL_PATH = "/proxy/bfl/flux-kontext-max/generate" - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "prompt": ( - IO.STRING, - { - "multiline": True, - "default": "", - "tooltip": "Prompt for the image generation - specify what and how to edit.", - }, - ), - "aspect_ratio": ( - IO.STRING, - { - "default": "16:9", - "tooltip": "Aspect ratio of image; must be between 1:4 and 4:1.", - }, - ), - "guidance": ( - IO.FLOAT, - { - "default": 3.0, - "min": 0.1, - "max": 99.0, - "step": 0.1, - "tooltip": "Guidance strength for the image generation process" - }, - ), - "steps": ( - IO.INT, - { - "default": 50, - "min": 1, - "max": 150, - "tooltip": "Number of steps for the image generation process" - }, - ), - "seed": ( - IO.INT, - { - "default": 0, - "min": 0, - "max": 0xFFFFFFFFFFFFFFFF, - "control_after_generate": True, - "tooltip": "The random seed used for creating the noise.", - }, - ), - "prompt_upsampling": ( - IO.BOOLEAN, - { - "default": False, - "tooltip": "Whether to perform upsampling on the prompt. If active, automatically modifies the prompt for more creative generation, but results are nondeterministic (same seed will not produce exactly the same result).", - }, - ), - }, - "optional": { - "input_image": (IO.IMAGE,), - }, - "hidden": { - "auth_token": "AUTH_TOKEN_COMFY_ORG", - "comfy_api_key": "API_KEY_COMFY_ORG", - "unique_id": "UNIQUE_ID", - }, - } - - @classmethod - def VALIDATE_INPUTS(cls, aspect_ratio: str): - try: - validate_aspect_ratio( - aspect_ratio, - minimum_ratio=cls.MINIMUM_RATIO, - maximum_ratio=cls.MAXIMUM_RATIO, - minimum_ratio_str=cls.MINIMUM_RATIO_STR, - maximum_ratio_str=cls.MAXIMUM_RATIO_STR, - ) - except Exception as e: - return str(e) - return True - - RETURN_TYPES = (IO.IMAGE,) - DESCRIPTION = cleandoc(__doc__ or "") # Handle potential None value - FUNCTION = "api_call" - API_NODE = True - CATEGORY = "api node/image/BFL" - - def api_call( - self, - prompt: str, - aspect_ratio: str, - guidance: float, - steps: int, - input_image: Optional[torch.Tensor]=None, - seed=0, - prompt_upsampling=False, - unique_id: Union[str, None] = None, - **kwargs, - ): - if input_image is None: - validate_string(prompt, strip_whitespace=False) - operation = SynchronousOperation( - endpoint=ApiEndpoint( - path="/proxy/bfl/flux-kontext-max/generate", - method=HttpMethod.POST, - request_model=BFLFluxKontextProGenerateRequest, - response_model=BFLFluxProGenerateResponse, - ), - request=BFLFluxKontextProGenerateRequest( - prompt=prompt, - prompt_upsampling=prompt_upsampling, - guidance=round(guidance, 1), - steps=steps, - seed=seed, - aspect_ratio=validate_aspect_ratio( - aspect_ratio, - minimum_ratio=self.MINIMUM_RATIO, - maximum_ratio=self.MAXIMUM_RATIO, - minimum_ratio_str=self.MINIMUM_RATIO_STR, - maximum_ratio_str=self.MAXIMUM_RATIO_STR, - ), - input_image=( - input_image - if input_image is None - else convert_image_to_base64(input_image) - ) - ), - auth_kwargs=kwargs, - ) - output_image = handle_bfl_synchronous_operation(operation, node_id=unique_id) - return (output_image,) class FluxProImageNode(ComfyNodeABC): """ @@ -1208,8 +1079,8 @@ NODE_CLASS_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = { "FluxProUltraImageNode": "Flux 1.1 [pro] Ultra Image", # "FluxProImageNode": "Flux 1.1 [pro] Image", - "FluxKontextProImageNode": "Flux.1 Kontext Pro Image", - "FluxKontextMaxImageNode": "Flux.1 Kontext Max Image", + "FluxKontextProImageNode": "Flux.1 Kontext [pro] Image", + "FluxKontextMaxImageNode": "Flux.1 Kontext [max] Image", "FluxProExpandNode": "Flux.1 Expand Image", "FluxProFillNode": "Flux.1 Fill Image", "FluxProCannyNode": "Flux.1 Canny Control Image",