From e24b9abe2707564b0a8a4288d06d3e7e7e3cfebf Mon Sep 17 00:00:00 2001 From: nofunstudio Date: Sun, 25 Jan 2026 15:02:32 -0700 Subject: [PATCH] add time generation duration output to api nodes --- fal_flux2_klein_edit.py | 18 ++++++++++++++---- fal_flux2_lora_edit.py | 18 ++++++++++++++---- fal_flux2_turbo_edit.py | 18 ++++++++++++++---- fal_kling_video.py | 18 ++++++++++++++---- fal_ltx2_video.py | 18 ++++++++++++++---- fal_qwen_edit.py | 18 ++++++++++++++---- fal_recraft_upscale.py | 18 ++++++++++++++---- fal_seedance_video.py | 18 ++++++++++++++---- nano_banana_pro.py | 21 ++++++++++++++++----- wavespeed_ai_image.py | 18 ++++++++++++++---- wavespeed_flux2_lora_edit.py | 18 ++++++++++++++---- 11 files changed, 156 insertions(+), 45 deletions(-) diff --git a/fal_flux2_klein_edit.py b/fal_flux2_klein_edit.py index ce80df5..273516f 100644 --- a/fal_flux2_klein_edit.py +++ b/fal_flux2_klein_edit.py @@ -94,8 +94,8 @@ class FalAPI_Flux2KleinEdit: } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING",) - RETURN_NAMES = ("image", "image_path", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("image", "image_path", "generation_info", "generation_time",) FUNCTION = "generate_image" CATEGORY = "FAL" @@ -165,12 +165,19 @@ class FalAPI_Flux2KleinEdit: mean_val = tensor.mean().item() return mean_val < 0.01 + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_image(self, api_key, model_variant, prompt, image1, width, height, image2=None, image3=None, negative_prompt="", num_inference_steps=28, guidance_scale=5.0, lora_url="", lora_scale=1.0, seed=-1, output_format="png", enable_safety_checker=True, acceleration="none"): print(f"[Flux2KleinEdit] Starting image generation process with {model_variant} model...") + start_time = time.time() if not api_key: raise ValueError("A FAL API key is required.") @@ -283,7 +290,9 @@ class FalAPI_Flux2KleinEdit: else: batch_tensor = final_tensors[0] - return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[Flux2KleinEdit] Generation completed in {generation_time} seconds") + return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2), generation_time) except Exception as e: print(f"[Flux2KleinEdit] Error: {str(e)}") @@ -296,7 +305,8 @@ class FalAPI_Flux2KleinEdit: "error": f"Flux2KleinEdit generation failed: {str(e)}", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), } - return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2), generation_time) finally: # Cleanup temp inputs diff --git a/fal_flux2_lora_edit.py b/fal_flux2_lora_edit.py index 4fe7133..44bfa12 100644 --- a/fal_flux2_lora_edit.py +++ b/fal_flux2_lora_edit.py @@ -89,8 +89,8 @@ class FalAPI_Flux2LoraEdit: } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING",) - RETURN_NAMES = ("image", "image_path", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("image", "image_path", "generation_info", "generation_time",) FUNCTION = "generate_image" CATEGORY = "FAL" @@ -153,12 +153,19 @@ class FalAPI_Flux2LoraEdit: img_array = np.array(pil_img).astype(np.float32) / 255.0 return torch.from_numpy(img_array)[None,] + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_image(self, api_key, prompt, image1, width, height, image2=None, image3=None, num_inference_steps=28, guidance_scale=2.5, lora_url="", lora_scale=1.0, seed=-1, output_format="png", enable_safety_checker=True, enable_prompt_expansion=False, acceleration="regular"): print("[Flux2LoraEdit] Starting image generation process...") + start_time = time.time() if not api_key: raise ValueError("A FAL API key is required.") @@ -265,7 +272,9 @@ class FalAPI_Flux2LoraEdit: else: batch_tensor = final_tensors[0] - return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[Flux2LoraEdit] Generation completed in {generation_time} seconds") + return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2), generation_time) except Exception as e: print(f"[Flux2LoraEdit] Error: {str(e)}") @@ -278,7 +287,8 @@ class FalAPI_Flux2LoraEdit: "error": f"Flux2LoraEdit generation failed: {str(e)}", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), } - return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2), generation_time) finally: # Cleanup temp inputs diff --git a/fal_flux2_turbo_edit.py b/fal_flux2_turbo_edit.py index d70c87a..edfb28d 100644 --- a/fal_flux2_turbo_edit.py +++ b/fal_flux2_turbo_edit.py @@ -62,8 +62,8 @@ class FalAPI_Flux2TurboEdit: } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING",) - RETURN_NAMES = ("image", "image_path", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("image", "image_path", "generation_info", "generation_time",) FUNCTION = "generate_image" CATEGORY = "FAL" @@ -126,11 +126,18 @@ class FalAPI_Flux2TurboEdit: img_array = np.array(pil_img).astype(np.float32) / 255.0 return torch.from_numpy(img_array)[None,] + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_image(self, api_key, prompt, image1, width, height, image2=None, image3=None, guidance_scale=2.5, seed=-1, output_format="png", enable_safety_checker=True): print("[Flux2TurboEdit] Starting image generation process...") + start_time = time.time() if not api_key: raise ValueError("A FAL API key is required.") @@ -229,7 +236,9 @@ class FalAPI_Flux2TurboEdit: else: batch_tensor = final_tensors[0] - return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[Flux2TurboEdit] Generation completed in {generation_time} seconds") + return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2), generation_time) except Exception as e: print(f"[Flux2TurboEdit] Error: {str(e)}") @@ -242,7 +251,8 @@ class FalAPI_Flux2TurboEdit: "error": f"Flux2TurboEdit generation failed: {str(e)}", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), } - return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2), generation_time) finally: # Cleanup temp inputs diff --git a/fal_kling_video.py b/fal_kling_video.py index e59fadd..53f7d34 100644 --- a/fal_kling_video.py +++ b/fal_kling_video.py @@ -34,8 +34,8 @@ class FalAPI_kling_video: } } - RETURN_TYPES = ("STRING", "STRING",) - RETURN_NAMES = ("video_path", "generation_info",) + RETURN_TYPES = ("STRING", "STRING", "STRING",) + RETURN_NAMES = ("video_path", "generation_info", "generation_time",) FUNCTION = "generate_video" CATEGORY = "FAL" @@ -107,11 +107,18 @@ class FalAPI_kling_video: except Exception as e: print(f"[Kling Video] Error in queue update: {e}") + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + def generate_video(self, api_token, image, prompt): """ Generate video using fal-ai/kling-video/v2.1/standard/image-to-video """ print(f"[Kling Video] Starting video generation process...") + start_time = time.time() # For errors, return empty strings empty_path = "" @@ -231,7 +238,9 @@ class FalAPI_kling_video: print(f"[Kling Video] Saved metadata -> {metadata_path}") # 10) Return the video path & metadata JSON string - return (video_path, json.dumps(generation_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[Kling Video] Generation completed in {generation_time} seconds") + return (video_path, json.dumps(generation_info, indent=2), generation_time) except Exception as e: # Return empty path and an error message in JSON @@ -241,7 +250,8 @@ class FalAPI_kling_video: "model": "fal-ai/kling-video/v2.1/standard/image-to-video" } print(f"[Kling Video] Error: {str(e)}") - return (empty_path, json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (empty_path, json.dumps(error_info, indent=2), generation_time) def tensor_to_tempfile(self, tensor): """ diff --git a/fal_ltx2_video.py b/fal_ltx2_video.py index ffdfe23..444c8e1 100644 --- a/fal_ltx2_video.py +++ b/fal_ltx2_video.py @@ -160,8 +160,8 @@ class FalAPI_ltx2_video: } } - RETURN_TYPES = ("VIDEO", "STRING", "STRING",) - RETURN_NAMES = ("video", "video_path", "generation_info",) + RETURN_TYPES = ("VIDEO", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("video", "video_path", "generation_info", "generation_time",) FUNCTION = "generate_video" CATEGORY = "FAL" @@ -230,6 +230,12 @@ class FalAPI_ltx2_video: arr = (arr * 255).clip(0, 255).astype("uint8") return Image.fromarray(arr) + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_video(self, api_token, model_variant, image, prompt, num_frames, width, height, fps, image_strength, negative_prompt="", guidance_scale=3.0, num_inference_steps=40, use_multiscale=True, generate_audio=False, enable_safety_checker=True, acceleration="none", @@ -239,6 +245,7 @@ class FalAPI_ltx2_video: """Generate video using fal-ai/ltx-2-19b/image-to-video/lora (Async execution)""" print(f"[LTX-2 Video] Starting video generation with {model_variant} model...") + start_time = time.time() empty_path = "" @@ -397,7 +404,9 @@ class FalAPI_ltx2_video: # Create VideoFromFile object for ComfyUI's VIDEO type video_output = VideoFromFile(video_path) - return (video_output, video_path, json.dumps(generation_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[LTX-2 Video] Generation completed in {generation_time} seconds") + return (video_output, video_path, json.dumps(generation_info, indent=2), generation_time) except Exception as e: # Clean up temp file on error @@ -413,7 +422,8 @@ class FalAPI_ltx2_video: "model": f"fal-ai/ltx-2-19b/{model_variant}/image-to-video/lora" } print(f"[LTX-2 Video] Error: {str(e)}") - return (None, empty_path, json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (None, empty_path, json.dumps(error_info, indent=2), generation_time) @classmethod def IS_CHANGED(cls, **kwargs): diff --git a/fal_qwen_edit.py b/fal_qwen_edit.py index e4216d4..0d54d66 100644 --- a/fal_qwen_edit.py +++ b/fal_qwen_edit.py @@ -85,8 +85,8 @@ class FalAPI_QwenEditPlus: } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING",) - RETURN_NAMES = ("image", "image_path", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("image", "image_path", "generation_info", "generation_time",) FUNCTION = "generate_image" CATEGORY = "FAL" @@ -149,12 +149,19 @@ class FalAPI_QwenEditPlus: img_array = np.array(pil_img).astype(np.float32) / 255.0 return torch.from_numpy(img_array)[None,] + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_image(self, api_key, prompt, image1, width, height, image2=None, image3=None, num_inference_steps=28, guidance_scale=4.0, lora_url="", lora_scale=0.84, seed=-1, output_format="png", enable_safety_checker=True, acceleration="regular"): print("[QwenEditPlus] Starting image generation process...") + start_time = time.time() if not api_key: raise ValueError("A FAL API key is required.") @@ -260,7 +267,9 @@ class FalAPI_QwenEditPlus: else: batch_tensor = final_tensors[0] - return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[QwenEditPlus] Generation completed in {generation_time} seconds") + return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2), generation_time) except Exception as e: print(f"[QwenEditPlus] Error: {str(e)}") @@ -273,7 +282,8 @@ class FalAPI_QwenEditPlus: "error": f"QwenEditPlus generation failed: {str(e)}", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), } - return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2), generation_time) finally: # Cleanup temp inputs diff --git a/fal_recraft_upscale.py b/fal_recraft_upscale.py index 1f5b292..97c4425 100644 --- a/fal_recraft_upscale.py +++ b/fal_recraft_upscale.py @@ -33,8 +33,8 @@ class FalAPI_recraft_upscale: } } - RETURN_TYPES = ("IMAGE", "STRING",) - RETURN_NAMES = ("upscaled_image", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING",) + RETURN_NAMES = ("upscaled_image", "generation_info", "generation_time",) FUNCTION = "upscale" CATEGORY = "FAL" @@ -105,11 +105,18 @@ class FalAPI_recraft_upscale: except Exception as e: print(f"[Recraft Upscale] Error in queue update: {e}") + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + def upscale(self, api_token, image, enable_safety_checker): """ Upscale image using fal-ai/recraft/upscale/crisp """ print(f"[Recraft Upscale] Starting upscale process...") + start_time = time.time() # For errors, return an empty 1024x1024 image empty_image = torch.zeros((1, 1024, 1024, 3)) @@ -241,7 +248,9 @@ class FalAPI_recraft_upscale: print(f"[Recraft Upscale] Final tensor shape: {img_tensor.shape}") # 11) Return the image tensor & metadata JSON string - return (img_tensor, json.dumps(generation_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[Recraft Upscale] Upscale completed in {generation_time} seconds") + return (img_tensor, json.dumps(generation_info, indent=2), generation_time) except Exception as e: # Return an empty image and an error message in JSON @@ -251,7 +260,8 @@ class FalAPI_recraft_upscale: "model": "fal-ai/recraft/upscale/crisp" } print(f"[Recraft Upscale] Error: {str(e)}") - return (empty_image, json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (empty_image, json.dumps(error_info, indent=2), generation_time) def tensor_to_tempfile(self, tensor): """ diff --git a/fal_seedance_video.py b/fal_seedance_video.py index d83815e..dbf154b 100644 --- a/fal_seedance_video.py +++ b/fal_seedance_video.py @@ -92,8 +92,8 @@ class FalAPI_seedance_video: } } - RETURN_TYPES = ("VIDEO", "STRING", "STRING",) - RETURN_NAMES = ("video", "video_path", "generation_info",) + RETURN_TYPES = ("VIDEO", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("video", "video_path", "generation_info", "generation_time",) FUNCTION = "generate_video" CATEGORY = "FAL" @@ -140,6 +140,12 @@ class FalAPI_seedance_video: except Exception as e: print(f"[Seedance Video] Error in queue update: {e}") + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_video(self, api_token, image, prompt, aspect_ratio, resolution, duration, camera_fixed, generate_audio, enable_safety_checker, end_image=None, seed=-1): """ @@ -147,6 +153,7 @@ class FalAPI_seedance_video: (Async execution for parallel processing) """ print(f"[Seedance Video] Starting video generation process...") + start_time = time.time() # For errors, return empty strings empty_path = "" @@ -316,7 +323,9 @@ class FalAPI_seedance_video: video_output = VideoFromFile(video_path) # 11) Return the video object, video path string & metadata JSON string - return (video_output, video_path, json.dumps(generation_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[Seedance Video] Generation completed in {generation_time} seconds") + return (video_output, video_path, json.dumps(generation_info, indent=2), generation_time) except Exception as e: # Return None for video, empty path and an error message in JSON @@ -326,7 +335,8 @@ class FalAPI_seedance_video: "model": "fal-ai/bytedance/seedance/v1.5/pro/image-to-video" } print(f"[Seedance Video] Error: {str(e)}") - return (None, empty_path, json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (None, empty_path, json.dumps(error_info, indent=2), generation_time) def tensor_to_tempfile(self, tensor): """ diff --git a/nano_banana_pro.py b/nano_banana_pro.py index f0d0927..698c4a4 100644 --- a/nano_banana_pro.py +++ b/nano_banana_pro.py @@ -72,8 +72,8 @@ class FalAPI_NanoBananaPro: } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING",) - RETURN_NAMES = ("images", "image_paths", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("images", "image_paths", "generation_info", "generation_time",) FUNCTION = "generate_image" CATEGORY = "FAL" @@ -132,9 +132,16 @@ class FalAPI_NanoBananaPro: arr = (arr * 255).clip(0, 255).astype("uint8") return Image.fromarray(arr) + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_image(self, api_key, prompt, num_images, image1, aspect_ratio, resolution, image2=None, image3=None, image4=None, seed=-1): print("[NanoBananaPro] Starting generation process...") + start_time = time.time() if not api_key: raise ValueError("A FAL API Key is required.") @@ -238,7 +245,8 @@ class FalAPI_NanoBananaPro: path, _ = await asyncio.to_thread(self.save_image_and_metadata, black_pil, timeout_info, number) saved_paths.append(path) - return (black_tensor, ";".join(saved_paths), json.dumps(timeout_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (black_tensor, ";".join(saved_paths), json.dumps(timeout_info, indent=2), generation_time) # Check status try: @@ -326,7 +334,9 @@ class FalAPI_NanoBananaPro: else: batch_tensor = final_tensors[0] - return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[NanoBananaPro] Generation completed in {generation_time} seconds") + return (batch_tensor, ";".join(saved_paths), json.dumps(result, indent=2), generation_time) except Exception as e: print(f"[NanoBananaPro] Error: {e}") @@ -337,7 +347,8 @@ class FalAPI_NanoBananaPro: # Return empty/error empty = torch.zeros((1, 64, 64, 3)) - return (empty, "", str(e)) + generation_time = self.format_generation_time(time.time() - start_time) + return (empty, "", str(e), generation_time) finally: # Cleanup temp inputs diff --git a/wavespeed_ai_image.py b/wavespeed_ai_image.py index bdcff38..c95ff94 100644 --- a/wavespeed_ai_image.py +++ b/wavespeed_ai_image.py @@ -63,8 +63,8 @@ class WaveSpeedAI_Image: } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING",) - RETURN_NAMES = ("image", "image_path", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("image", "image_path", "generation_info", "generation_time",) FUNCTION = "generate_image" CATEGORY = "WaveSpeedAI" @@ -93,6 +93,7 @@ class WaveSpeedAI_Image: image2=None, image3=None, lora_url="", lora_scale=0.84, seed=-1, output_format="jpeg"): print("[WaveSpeedAI] Starting image generation process...") + start_time = time.time() if not api_key: raise ValueError("A WaveSpeedAI API key is required.") @@ -183,7 +184,9 @@ class WaveSpeedAI_Image: pil_image = Image.open(BytesIO(resp.content)) output_tensor = self.pil_to_tensor(pil_image) - return (output_tensor, image_path, json.dumps(generation_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[WaveSpeedAI] Generation completed in {generation_time} seconds") + return (output_tensor, image_path, json.dumps(generation_info, indent=2), generation_time) except Exception as e: error_info = { @@ -191,7 +194,8 @@ class WaveSpeedAI_Image: "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), } print(f"[WaveSpeedAI] Error: {str(e)}") - return (None, "", json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (None, "", json.dumps(error_info, indent=2), generation_time) def tensor_to_data_uri(self, tensor, image_format="PNG"): pil_img = self.tensor_to_pil(tensor) @@ -210,6 +214,12 @@ class WaveSpeedAI_Image: img_array = np.array(pil_img).astype(np.float32) / 255.0 return torch.from_numpy(img_array)[None,] + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + @classmethod def IS_CHANGED(cls, **kwargs): return float("NaN") diff --git a/wavespeed_flux2_lora_edit.py b/wavespeed_flux2_lora_edit.py index 51e7098..766547b 100644 --- a/wavespeed_flux2_lora_edit.py +++ b/wavespeed_flux2_lora_edit.py @@ -57,8 +57,8 @@ class WaveSpeedAI_Flux2LoraEdit: } } - RETURN_TYPES = ("IMAGE", "STRING", "STRING",) - RETURN_NAMES = ("image", "image_path", "generation_info",) + RETURN_TYPES = ("IMAGE", "STRING", "STRING", "STRING",) + RETURN_NAMES = ("image", "image_path", "generation_info", "generation_time",) FUNCTION = "generate_image" CATEGORY = "WaveSpeedAI" @@ -100,10 +100,17 @@ class WaveSpeedAI_Flux2LoraEdit: img_array = np.array(pil_img).astype(np.float32) / 255.0 return torch.from_numpy(img_array)[None,] + def format_generation_time(self, elapsed_seconds): + """Format elapsed time as 'seconds-centiseconds' (e.g., '14-50' for 14.50 seconds)""" + seconds = int(elapsed_seconds) + centiseconds = int((elapsed_seconds - seconds) * 100) + return f"{seconds}-{centiseconds:02d}" + async def generate_image(self, api_key, prompt, image1, width, height, image2=None, image3=None, lora_url="", lora_scale=1.0, seed=-1): print("[WaveSpeedAI Flux2] Starting image generation process...") + start_time = time.time() if not api_key: raise ValueError("A WaveSpeedAI API key is required.") @@ -215,7 +222,9 @@ class WaveSpeedAI_Flux2LoraEdit: pil_image = Image.open(BytesIO(resp.content)).convert("RGB") output_tensor = self.pil_to_tensor(pil_image) - return (output_tensor, image_path, json.dumps(generation_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + print(f"[WaveSpeedAI Flux2] Generation completed in {generation_time} seconds") + return (output_tensor, image_path, json.dumps(generation_info, indent=2), generation_time) except Exception as e: print(f"[WaveSpeedAI Flux2] Error: {str(e)}") @@ -223,7 +232,8 @@ class WaveSpeedAI_Flux2LoraEdit: "error": f"WaveSpeedAI Flux2 generation failed: {str(e)}", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), } - return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2)) + generation_time = self.format_generation_time(time.time() - start_time) + return (torch.zeros((1, 64, 64, 3)), "", json.dumps(error_info, indent=2), generation_time) @classmethod def IS_CHANGED(cls, **kwargs):