Add .env support for API keys, add input tooltips

This commit is contained in:
Aryan
2026-02-06 15:38:38 +05:30
parent 16fa00fa2a
commit 2a899302a6
21 changed files with 115 additions and 205 deletions
+7
View File
@@ -0,0 +1,7 @@
# Copy this file to .env and fill in your API keys
# You can reference these variable names directly in the nodes
GEMINI_API_KEY="your_gemini_api_key"
OPENAI_API_KEY="your_openai_api_key"
XI_API_KEY="your_elevenlabs_api_key"
REPLICATE_API_TOKEN="your_replicate_api_token"
+64 -166
View File
@@ -51,251 +51,149 @@ All nodes in this collection require API keys to function.
* **ElevenLabs TTS Node:** You will need an [ElevenLabs API Key](https://elevenlabs.io/).
* **Vertex AI Nodes (Imagen Edit, Veo Vertex AI):** You will need a Google Cloud Project ID, a service account with appropriate permissions, and the location for the resources.
You can paste your key directly into the `api_key` field on the corresponding node. For Vertex AI nodes, you will need to provide the project ID, location, and path to your service account JSON file.
You can paste your key directly into the `api_key` field on the corresponding node. For Vertex AI nodes, you will need to provide the project ID, location, and paste your service account JSON content.
### Using Environment Variables (.env file)
Instead of pasting API keys directly into nodes, you can store them in a `.env` file:
1. Copy `.env.example` to `.env` in the node directory
2. Fill in your API keys:
```
GEMINI_API_KEY=your_gemini_key_here
OPENAI_API_KEY=your_openai_key_here
XI_API_KEY=your_elevenlabs_key_here
REPLICATE_API_TOKEN=your_replicate_token_here
```
3. In the node's `api_key` field, enter the variable name (e.g., `GEMINI_API_KEY`) instead of the actual key
This keeps your API keys secure and makes it easy to switch between different keys or projects.
---
## 📚 Node Guide
> **Note:** All nodes include standard inputs like `api_key`, `prompt`, `model`, `temperature`, and `seed` where applicable. Only unique or notable inputs are listed below.
### Flux Kontext Pro / Max
These nodes allow you to transform an input image based on a text prompt. They are ideal for applying artistic styles or making significant conceptual changes to an existing image.
Transform images based on text prompts. Ideal for applying artistic styles or making conceptual changes.
* **Category:** `image/edit`
* **Inputs:**
* `image`: The source image to transform.
* `prompt`: A text description of the desired output (e.g., "A vibrant Van Gogh painting", "Make this a 90s cartoon").
* `replicate_api_token`: Your API token from Replicate.
* `aspect_ratio`: The desired output aspect ratio. `match_input_image` is highly recommended to preserve the original composition.
* `output_format`: `jpg` or `png`.
* `safety_tolerance`: Adjust the content safety filter level.
* **Output:**
* `image`: The generated image.
* **Key Inputs:** `image`, `aspect_ratio` (use `match_input_image` to preserve composition)
* **Output:** `image`
### Flux.2 (Replicate)
Generate images using Black Forest Labs' FLUX.2 models via the Replicate API.
Generate images using FLUX.2 models (Pro, Max, Dev) via Replicate.
* **Category:** `image/generation`
* **Inputs:**
* `prompt`: The text prompt for image generation.
* `api_key`: Your Replicate API token.
* `model`: Choose between `flux-2-max`, `flux-2-pro`, or `flux-2-dev`.
* `aspect_ratio`: The desired aspect ratio for the generated image.
* `output_format`: `webp`, `jpg`, or `png`.
* `output_quality`: Quality of the output image (0-100).
* `image_1` to `image_5` (Optional): Input images for image-to-image or control tasks.
* **Output:**
* `image`: The generated image.
* **Key Inputs:** `image_1` to `image_5` (optional, for image-to-image tasks)
* **Output:** `image`
### Gemini Chat
A versatile node for text generation and image/audio analysis. Use it to understand an image's content, analyze audio, or to generate creative text for other nodes.
Multimodal text generation with image and audio analysis capabilities.
* **Category:** `text/generation`
* **Inputs:**
* `prompt`: The text prompt or question you want to ask the model.
* `model`: The Gemini model to use (e.g., `gemini-2.5-pro`, `gemini-2.5-flash`).
* `temperature`: Controls the creativity of the output.
* `thinking`: Enables the model's thinking/reasoning process.
* `seed`: Seed for reproducibility.
* `api_key`: Your API key from Google AI Studio.
* `system_instruction` (Optional): Provide context or rules for how the model should behave.
* `thinking_budget` (Optional): Token budget for thinking.
* `image` (Optional): An input image for the model to analyze.
* `audio` (Optional): An input audio for the model to analyze.
* **Output:**
* `response`: The text generated by the Gemini model.
* **Key Inputs:** `thinking`, `thinking_budget`, `system_instruction`, `image` (optional), `audio` (optional)
* **Output:** `response` (text)
### Gemini Segmentation
This node uses a Gemini model to generate segmentation masks for specified objects within an image.
Generate segmentation masks for objects in an image.
* **Category:** `image/generation`
* **Inputs:**
* `image`: The source image for segmentation.
* `segment_prompt`: A text description of the objects to segment (e.g., "the car", "all people").
* `model`: The Gemini model to use.
* `temperature`: Controls randomness.
* `thinking`: Enable thinking process.
* `seed`: Seed for reproducibility.
* `api_key`: Your API key from Google AI Studio.
* `thinking_budget` (Optional): Token budget for thinking.
* **Output:**
* `mask`: A black and white mask of the segmented objects.
* **Key Inputs:** `image`, `segment_prompt` (e.g., "the car", "all people"), `thinking`, `thinking_budget`
* **Output:** `mask`
### Gemini Speaker Diarization
Separate audio into different speaker tracks using Gemini.
Separate audio into different speaker tracks.
* **Category:** `audio/diarise`
* **Inputs:**
* `audio`: The input audio to process.
* `num_speakers`: The expected number of speakers.
* `model`: The Gemini model to use.
* `api_key`: Your API key from Google AI Studio.
* `seed`: Seed for reproducibility.
* `temperature`: Controls randomness.
* `thinking` (Optional): Enable thinking process.
* `thinking_budget` (Optional): Token budget for thinking.
* **Output:**
* `speaker_1` to `speaker_4`: Audio tracks for up to 4 separated speakers.
* **Key Inputs:** `audio`, `num_speakers`, `thinking`, `thinking_budget`
* **Output:** `speaker_1` to `speaker_4` (audio tracks)
### GPT Image Edit
This node uses OpenAI's API to perform powerful, prompt-based inpainting and editing.
Prompt-based inpainting and image editing using OpenAI's API.
* **Category:** `image/edit`
* **Inputs:**
* `image`: The source image to edit.
* `mask` (Optional): A black and white mask. The model will edit the **white area** of the mask.
* `prompt`: A description of the edit to perform.
* `api_key`: Your API key from OpenAI.
* `...other_params`: Various quality and formatting options for the OpenAI API.
* **Output:**
* `image`: The edited image.
* **Key Inputs:** `image_1` to `image_5`, `mask` (optional, white area = edit region), `background`, `quality`, `size`
* **Output:** `image`
### OpenAI LLM
Access OpenAI's powerful language models for text generation and reasoning.
Access OpenAI language models (GPT-4, GPT-5, o1, etc.) for text generation.
* **Category:** `text/generation`
* **Inputs:**
* `prompt`: The text prompt.
* `model`: The OpenAI model to use (e.g., `gpt-4.1`, `o1`, `gpt-5`).
* `temperature`: Controls randomness.
* `reasoning_effort`: Effort level for reasoning models.
* `api_key`: Your OpenAI API key.
* `max_output_tokens`: Maximum number of tokens to generate.
* `system_instruction` (Optional): System level instructions.
* `image` (Optional): Input image for multimodal models.
* **Output:**
* `response`: The generated text response.
* **Key Inputs:** `reasoning_effort` (low/medium/high), `max_output_tokens`, `system_instruction`, `image` (optional)
* **Output:** `response` (text)
### OpenAI Text-to-Speech
Generate high-quality speech from text using OpenAI's TTS models.
Generate speech using OpenAI's TTS models.
* **Category:** `audio/generation`
* **Inputs:**
* `text`: The text to convert to speech.
* `model`: The TTS model to use (e.g., `gpt-4o-mini-tts`, `tts-1`).
* `voice`: The voice to use (e.g., `alloy`, `echo`).
* `response_format`: Output audio format.
* `speed`: Speaking speed.
* `api_key`: Your OpenAI API key.
* `instructions` (Optional): Instructions for the model (supported by some models).
* **Output:**
* `audio`: The generated audio.
* **Key Inputs:** `text`, `voice` (alloy, echo, etc.), `response_format`, `speed`, `instructions` (optional)
* **Output:** `audio`
### Google Imagen Generator
Generate images from a text prompt using Google's Imagen models.
Generate images from text using Google's Imagen models.
* **Category:** `image/generation`
* **Inputs:**
* `prompt`: A text description of the image to generate.
* `api_key`: Your API key from Google AI Studio.
* `model`: The Imagen model to use.
* `...other_params`: Options for number of images, aspect ratio, and image size.
* **Output:**
* `images`: The generated image(s).
* **Key Inputs:** `number_of_images`, `aspect_ratio`, `image_size`, `guidance_scale`, `negative_prompt`
* **Output:** `images`
### Google Imagen Edit (Vertex AI only)
Perform advanced image editing, inpainting, outpainting, and background swapping using Imagen on Google's Vertex AI platform.
Advanced image editing with inpainting, outpainting, and background swapping.
* **Category:** `image/edit`
* **Inputs:**
* `image`: The source image to edit.
* `mask`: A mask defining the area to edit.
* `prompt`: A description of the desired edit.
* `project_id`: Your Google Cloud Project ID.
* `location`: The Google Cloud location for the model.
* `service_account`: Path to your Google Cloud service account JSON file.
* `edit_mode`: The type of edit to perform (e.g., inpainting, outpainting).
* `...other_params`: Controls for negative prompt, seed, and steps.
* **Output:**
* `edited_images`: The edited image(s).
* **Key Inputs:** `image`, `mask`, `project_id`, `location`, `service_account` (JSON content), `edit_mode`
* **Output:** `edited_images`
### Nano Banana
A creative image generation node that can take a combination of text and up to five images as input.
Creative image generation using a specialized Gemini model.
* **Category:** `image/generation`
* **Inputs:**
* `api_key`: Your API key from Google AI Studio.
* `prompt` (Optional): A text prompt.
* `image_1` to `image_5` (Optional): Up to five source images.
* `...other_params`: Controls for aspect ratio, temperature, top_p, and seed.
* **Output:**
* `image`: The generated image.
* **Key Inputs:** `image_1` to `image_5` (optional), `aspect_ratio`, `resolution`, `top_p`
* **Output:** `image`
### Veo Video Generator (Vertex AI)
Generate short, high-quality video clips from a text description using Google's Veo model on Vertex AI.
Generate video clips using Google's Veo model on Vertex AI.
* **Category:** `video/generation`
* **Inputs:**
* `prompt`: A text description of the video to generate.
* `project_id`: Your Google Cloud Project ID.
* `location`: The Google Cloud location for the model.
* `service_account`: Path to your Google Cloud service account JSON file.
* `...other_params`: Controls for negative prompt, aspect ratio, audio generation, and seed.
* **Output:**
* `frames`: The generated video frames, output as an image batch.
* **Key Inputs:** `project_id`, `location`, `service_account` (JSON content), `resolution`, `aspect_ratio`, `duration_seconds`, `generate_audio`, `first_frame`/`last_frame` (optional)
* **Output:** `frames`, `audio`
### Veo Video Generator (Gemini API)
Generate videos using Google's Veo 2.0 model via the Gemini API. Supports text-to-video and image-to-video.
Generate videos using Veo 2.0 via the Gemini API. Supports text-to-video and image-to-video.
* **Category:** `video/generation`
* **Inputs:**
* `prompt`: A text description of the video.
* `image` (Optional): An input image for image-to-video generation.
* `api_key`: Your API key from Google AI Studio.
* `model`: The Veo model to use (e.g., `veo-2.0-generate-001`).
* `aspect_ratio`: Desired aspect ratio (16:9 or 9:16).
* `duration_seconds`: Duration of the video (e.g., 5-8 seconds).
* `...other_params`: Controls for negative prompt and seed.
* **Output:**
* `frames`: The generated video frames.
* **Key Inputs:** `aspect_ratio`, `duration_seconds`, `negative_prompt`, `image` (optional, for image-to-video)
* **Output:** `frames`
### ElevenLabs TTS
Generate speech from text using the ElevenLabs API.
Generate speech using ElevenLabs' diverse voices and models.
* **Category:** `audio/generation`
* **Inputs:**
* `text`: The text to convert to speech.
* `api_key`: Your API key from ElevenLabs.
* `voice_id`: The ID of the voice to use for generation.
* `model_id`: The ElevenLabs model to use.
* `output_format`: The desired output audio format.
* `stability`: Controls the stability and variability of the generated speech.
* `similarity_boost`: Enhances the similarity of the generated speech to the chosen voice.
* `speed`: Adjusts the speaking rate.
* `style`: Controls the expressiveness of the speech.
* `use_speaker_boost`: A boolean to enable or disable speaker boost.
* `seed`: A seed for ensuring reproducible results.
* **Output:**
* `audio`: The generated audio waveform and sample rate.
* **Key Inputs:** `text`, `voice_id`, `model_id`, `stability`, `similarity_boost`, `speed`, `style`, `use_speaker_boost`
* **Output:** `audio`
### Gemini TTS
Generate speech from text using Google's Gemini TTS models.
Generate speech using Google's Gemini TTS models.
* **Category:** `audio/generation`
* **Inputs:**
* `text`: The text to be converted into speech.
* `api_key`: Your API key from Google AI Studio.
* `model`: The specific Gemini model to use for generation.
* `voice_id`: The prebuilt voice to use for the output.
* `temperature`: Controls the randomness and creativity of the output.
* `seed`: A seed for ensuring reproducible results.
* `system_prompt` (Optional): A system-level instruction to guide the model's behavior.
* **Output:**
* `audio`: The generated audio waveform and sample rate.
* **Key Inputs:** `text`, `voice_id`, `system_prompt` (optional)
* **Output:** `audio`
## Acknowledgements
+3
View File
@@ -1,3 +1,6 @@
from dotenv import load_dotenv
load_dotenv()
from .flux_kontext_replicate import NODE_CLASS_MAPPINGS as PRO_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as PRO_DISPLAY
from .gemini_node import NODE_CLASS_MAPPINGS as GEMINI_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as GEMINI_DISPLAY
from .gemini_diarisation import NODE_CLASS_MAPPINGS as GEMINI_DIAR_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as GEMINI_DIAR_DISPLAY
+2 -2
View File
@@ -48,7 +48,7 @@ class ElevenLabsTTSNode:
"style": ("FLOAT", {"default": 0.50, "min": 0.0, "max": 1.0, "step": 0.01}),
"use_speaker_boost": ("BOOLEAN", {"default": True}),
"seed": ("INT", {"default": 40, "min": 0, "max": 4294967294}),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put ElevenLabs API key or .env variable name (XI_API_KEY)"}),
},
"optional": {
"previous_text": ("STRING", {"multiline": True, "default": ""}),
@@ -68,7 +68,7 @@ class ElevenLabsTTSNode:
if not text.strip():
raise ValueError("Text input cannot be empty.")
key = api_key.strip() or os.environ.get("XI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("XI_API_KEY")
if not key:
raise ValueError("No API key provided.")
+3 -3
View File
@@ -12,7 +12,7 @@ class Flux2Replicate:
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": "A beautiful landscape"}),
"api_key": ("STRING", {"default": ""}),
"api_key": ("STRING", {"default": "", "tooltip": "Directly put Replicate API token or .env variable name (REPLICATE_API_TOKEN)"}),
"model": (["flux-2-max", "flux-2-pro", "flux-2-dev"], {"default": "flux-2-max"}),
"aspect_ratio": (["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "5:4", "4:5", "21:9", "9:21", "2:1", "1:2"], {"default": "1:1"}),
"output_format": (["webp", "jpg", "png"], {"default": "webp"}),
@@ -49,7 +49,7 @@ class Flux2Replicate:
def generate_image(self, prompt, api_key, model, aspect_ratio, output_format, output_quality,
image_1=None, image_2=None, image_3=None, image_4=None, image_5=None):
try:
os.environ["REPLICATE_API_TOKEN"] = api_key
os.environ["REPLICATE_API_TOKEN"] = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("REPLICATE_API_TOKEN", "")
input_images = []
for img in [image_1, image_2, image_3, image_4, image_5]:
@@ -68,7 +68,7 @@ class Flux2Replicate:
# Add safety_tolerance for models that support it (flux-2-max and flux-2-pro)
if model in ["flux-2-max", "flux-2-pro"]:
replicate_input["safety_tolerance"] = 0
replicate_input["safety_tolerance"] = 1
# Run Replicate model
output = replicate.run(
+2 -2
View File
@@ -13,7 +13,7 @@ class FluxKontextReplicate:
"required": {
"image": ("IMAGE",),
"prompt": ("STRING", {"multiline": True, "default": "Make this a 90s cartoon"}),
"api_key": ("STRING", {"default": ""}),
"api_key": ("STRING", {"default": "", "tooltip": "Directly put Replicate API token or .env variable name (REPLICATE_API_TOKEN)"}),
"model": (["flux-kontext-dev", "flux-kontext-max", "flux-kontext-pro"], {"default": "flux-kontext-dev"}),
"aspect_ratio": (["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "5:4", "4:5", "21:9", "9:21", "2:1", "1:2", "match_input_image"], {"default": "match_input_image"}),
"output_format": (["jpg", "png"], {"default": "jpg"}),
@@ -30,7 +30,7 @@ class FluxKontextReplicate:
def generate_image(self, image, prompt, api_key, model, aspect_ratio, output_format, safety_tolerance, seed, prompt_upsampling):
try:
os.environ["REPLICATE_API_TOKEN"] = api_key
os.environ["REPLICATE_API_TOKEN"] = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("REPLICATE_API_TOKEN", "")
# Convert tensor to PIL and save to buffer
tensor = image.squeeze(0) if len(image.shape) == 4 else image
+5 -6
View File
@@ -16,18 +16,18 @@ class GeminiDiarisationAPI:
"audio": ("AUDIO",),
"num_speakers": ("INT", {"default": 2, "min": 1, "max": 10, "step": 1}),
"model": ("STRING", {"default": "gemini-2.5-flash", "multiline": False}),
"api_key": ("STRING", {"default": "", "multiline": False}),
"api_key": ("STRING", {"default": "", "multiline": False, "tooltip": "Directly put Gemini API key or .env variable name (GEMINI_API_KEY)"}),
"seed": ("INT", {"default": 69, "min": 0, "max": 2147483646, "step": 1}),
"temperature": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 2.0, "step": 0.1})
},
"optional": {
"thinking": ("BOOLEAN", {"default": False}),
"thinking_budget": ("INT", {"default": 1024, "min": 0, "max": 24576, "step": 1}),
"thinking_budget": ("INT", {"default": 1024, "min": 0, "max": 24576, "step": 1, "tooltip": "-1 = auto, 0 = disabled, 1+ = token budget"}),
}
}
RETURN_TYPES = ("AUDIO", "AUDIO", "AUDIO", "AUDIO", "STRING")
RETURN_NAMES = ("speaker_1", "speaker_2", "speaker_3", "speaker_4", "json")
RETURN_TYPES = ("AUDIO", "AUDIO", "AUDIO", "AUDIO")
RETURN_NAMES = ("speaker_1", "speaker_2", "speaker_3", "speaker_4")
FUNCTION = "diarise"
CATEGORY = "audio/diarise"
@@ -64,7 +64,7 @@ class GeminiDiarisationAPI:
w.writeframes((audio_np * 32767).astype(np.int16).tobytes())
# 2. Setup Client
key = api_key.strip() or os.environ.get("GEMINI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("GEMINI_API_KEY")
if not key: raise ValueError("API Key missing")
client = genai.Client(api_key=key, http_options={'api_version': 'v1beta'})
@@ -149,7 +149,6 @@ class GeminiDiarisationAPI:
tensor = torch.from_numpy(track).float().unsqueeze(0).unsqueeze(0)
outputs.append({"waveform": tensor, "sample_rate": sr})
outputs.append(json.dumps(result, indent=2))
return tuple(outputs)
NODE_CLASS_MAPPINGS = {"GeminiDiarisationAPI": GeminiDiarisationAPI}
+3 -3
View File
@@ -17,11 +17,11 @@ class GeminiChatNode:
"temperature": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 2.0, "step": 0.1}),
"thinking": ("BOOLEAN", {"default": False}),
"seed": ("INT", {"default": 69, "min": -1, "max": 2147483646, "step": 1}),
"api_key": ("STRING", {"default": "", "multiline": False})
"api_key": ("STRING", {"default": "", "multiline": False, "tooltip": "Directly put Gemini API key or .env variable name (GEMINI_API_KEY)"})
},
"optional": {
"system_instruction": ("STRING", {"multiline": True, "default": ""}),
"thinking_budget": ("INT", {"default": 0, "min": -1, "max": 24576, "step": 1}),
"thinking_budget": ("INT", {"default": 0, "min": -1, "max": 24576, "step": 1, "tooltip": "-1 = auto, 0 = disabled"}),
"image": ("IMAGE",),
"audio": ("AUDIO",),
}
@@ -35,7 +35,7 @@ class GeminiChatNode:
def generate(self, prompt, model, temperature, thinking, seed, api_key,
system_instruction=None, thinking_budget=-1, image=None, audio=None):
key = api_key.strip() or os.environ.get("GEMINI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("GEMINI_API_KEY")
if not key: raise ValueError("Error: No API key provided.")
client = genai.Client(api_key=key, http_options={'api_version': 'v1beta'})
+3 -3
View File
@@ -20,10 +20,10 @@ class GeminiSegmentationNode:
"temperature": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 2.0, "step": 0.1}),
"thinking": ("BOOLEAN", {"default": False}),
"seed": ("INT", {"default": 69, "min": -1, "max": 2147483646, "step": 1}),
"api_key": ("STRING", {"default": "", "multiline": False})
"api_key": ("STRING", {"default": "", "multiline": False, "tooltip": "Directly put Gemini API key or .env variable name (GEMINI_API_KEY)"})
},
"optional": {
"thinking_budget": ("INT", {"default": 0, "min": -1, "max": 24576, "step": 1}),
"thinking_budget": ("INT", {"default": 0, "min": -1, "max": 24576, "step": 1, "tooltip": "-1 = auto, 0 = disabled"}),
}
}
@@ -33,7 +33,7 @@ class GeminiSegmentationNode:
CATEGORY = "image/generation"
def generate_segmentation(self, image, segment_prompt, model, temperature, thinking, seed, api_key, thinking_budget=0):
key = api_key.strip() or os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY")
if not key: raise ValueError("API Key missing")
client = genai.Client(api_key=key, http_options={'api_version': 'v1beta'})
+2 -2
View File
@@ -9,7 +9,7 @@ class GeminiTTSNode:
return {
"required": {
"text": ("STRING", {"multiline": True, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put Gemini API key or .env variable name (GEMINI_API_KEY)"}),
"model": (["gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts"],),
"voice_id": (["Zephyr", "Puck", "Charon", "Kore", "Fenrir", "Leda", "Orus", "Aoede", "Callirrhoe", "Autonoe", "Enceladus", "Iapetus", "Umbriel", "Algieba", "Despina", "Erinome", "Achernar", "Laomedeia", "Rasalgethi", "Algenib", "Achird", "Pulcherrima", "Gacrux", "Schedar", "Alnilam", "Sulafat", "Sadaltager", "Sadachbia", "Vindemiatrix", "Zubenelgenubi"],),
"seed": ("INT", {"default": 69, "min": -1, "max": 2147483646, "step": 1}),
@@ -30,7 +30,7 @@ class GeminiTTSNode:
if not text.strip():
raise ValueError("Text input cannot be empty.")
key = api_key.strip() or os.environ.get("GEMINI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("GEMINI_API_KEY")
if not key:
raise ValueError("No API key provided.")
+2 -2
View File
@@ -15,7 +15,7 @@ class GPTImageEditNode:
"image_1": ("IMAGE",),
"model": (["gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"],),
"prompt": ("STRING", {"multiline": True, "default": "Edit this image"}),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put OpenAI API key or .env variable name (OPENAI_API_KEY)"}),
"background": (["auto", "transparent", "opaque"], {"default": "auto"}),
"quality": (["auto", "high", "medium", "low"], {"default": "auto"}),
"size": (["auto", "1024x1024", "1536x1024", "1024x1536"], {"default": "auto"}),
@@ -56,7 +56,7 @@ class GPTImageEditNode:
def edit_image(self, image_1, model, prompt, api_key, background, quality, size,
output_format, output_compression, n_images, mask=None, **kwargs):
key = api_key.strip() or os.environ.get("OPENAI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("OPENAI_API_KEY")
if not key:
raise ValueError("GPT Image Edit: No API Key provided.")
+2 -2
View File
@@ -12,7 +12,7 @@ class GoogleImagenNode:
return {
"required": {
"prompt": ("STRING", {"multiline": True}),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put Gemini API key or .env variable name (GEMINI_API_KEY)"}),
"model": (["models/imagen-4.0-ultra-generate-001", "models/imagen-4.0-generate-001", "models/imagen-4.0-fast-generate-001", "models/imagen-3.0-generate-002"],),
"number_of_images": ("INT", {"default": 1, "min": 1, "max": 4, "step": 1}),
"aspect_ratio": (["1:1", "9:16", "16:9", "4:3", "3:4"],),
@@ -31,7 +31,7 @@ class GoogleImagenNode:
CATEGORY = "image/generation"
def generate_images(self, prompt, api_key, model, number_of_images, aspect_ratio, image_size, seed, guidance_scale, negative_prompt=""):
key = api_key.strip() or os.environ.get("GEMINI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("GEMINI_API_KEY")
if not key: raise ValueError("No API key provided.")
client = genai.Client(api_key=key)
+1 -1
View File
@@ -19,7 +19,7 @@ class GoogleImagenEditNode:
"prompt": ("STRING", {"multiline": True, "default": "Edit this image"}),
"project_id": ("STRING", {"multiline": False, "default": ""}),
"location": (["global", "us-central1", "us-east1", "us-east4", "us-east5", "us-south1", "us-west1", "us-west2", "us-west3", "us-west4", "northamerica-northeast1", "northamerica-northeast2", "southamerica-east1", "southamerica-west1", "africa-south1", "europe-west1", "europe-north1", "europe-west2", "europe-west3", "europe-west4", "europe-west6", "europe-west8", "europe-west9", "europe-west12", "europe-southwest1", "europe-central2", "asia-east1", "asia-east2", "asia-northeast1", "asia-northeast2", "asia-northeast3", "asia-south1", "asia-south2", "asia-southeast1", "asia-southeast2", "australia-southeast1", "australia-southeast2", "me-central1", "me-central2", "me-west1"], {"default": "us-central1"}),
"service_account": ("STRING", {"multiline": True, "default": ""}),
"service_account": ("STRING", {"multiline": True, "default": "", "tooltip": "Paste service account JSON content"}),
"edit_mode": (["EDIT_MODE_INPAINT_INSERTION", "EDIT_MODE_INPAINT_REMOVAL", "EDIT_MODE_OUTPAINT", "EDIT_MODE_BGSWAP"], {"default": "EDIT_MODE_INPAINT_INSERTION"}),
"number_of_images": ("INT", {"default": 1, "min": 1, "max": 4, "step": 1}),
"seed": ("INT", {"default": 69, "min": 1, "max": 2147483646, "step": 1}),
+2 -2
View File
@@ -13,7 +13,7 @@ class NanoBananaNode:
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put Gemini API key or .env variable name (GEMINI_API_KEY)"}),
"model": (["gemini-3-pro-image-preview", "gemini-2.5-flash-image"],),
"aspect_ratio": (["1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9", "21:9"],),
"resolution": (["1K", "2K", "4K"], {"default": "1K"}),
@@ -48,7 +48,7 @@ class NanoBananaNode:
def generate(self, api_key, model, aspect_ratio, resolution, temperature, top_p, seed,
prompt="", system_instruction="", **kwargs):
key = api_key.strip() or os.environ.get("GEMINI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("GEMINI_API_KEY")
if not key:
raise ValueError("No API key provided.")
+2 -2
View File
@@ -16,7 +16,7 @@ class OpenAILLMNode:
"model": (["gpt-4.1","gpt-4.1-mini","gpt-5","gpt-5.2","gpt-5-mini","gpt-5-nano","gpt-5.2-pro","o1","o3-mini"],),
"temperature": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.01}),
"reasoning_effort": (["low", "medium", "high"],),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put OpenAI API key or .env variable name (OPENAI_API_KEY)"}),
"max_output_tokens": ("INT", {"default": 16384, "min": 1, "max": 32768, "step": 1})
},
"optional": {
@@ -36,7 +36,7 @@ class OpenAILLMNode:
if not prompt.strip():
raise ValueError("Prompt cannot be empty.")
key = api_key.strip() or os.environ.get("OPENAI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("OPENAI_API_KEY")
if not key:
raise ValueError("No API key provided.")
+2 -2
View File
@@ -36,7 +36,7 @@ class OpenAITTSNode:
"pcm"
],),
"speed": ("FLOAT", {"default": 1.0, "min": 0.25, "max": 4.0, "step": 0.01}),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put OpenAI API key or .env variable name (OPENAI_API_KEY)"}),
},
"optional": {
"instructions": ("STRING", {"multiline": True, "default": ""}),
@@ -53,7 +53,7 @@ class OpenAITTSNode:
if not text.strip():
raise ValueError("Text input cannot be empty.")
key = api_key.strip() or os.environ.get("OPENAI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("OPENAI_API_KEY")
if not key:
raise ValueError("No API key provided. Set OPENAI_API_KEY environment variable or provide it in the node.")
+2 -2
View File
@@ -1,7 +1,7 @@
[project]
name = "externalapi-helpers"
description = "Various ComfyUI nodes for Gemini, Replicate and OpenAI"
version = "1.0.6"
version = "1.0.7"
license = {file = "LICENSE"}
# classifiers = [
# # For OS-independent nodes (works on all operating systems)
@@ -20,7 +20,7 @@ license = {file = "LICENSE"}
# "Environment :: GPU :: Apple Metal", # Apple Metal support
# ]
dependencies = ["replicate", "pillow", "numpy", "torch", "google-genai", "opencv-python", "soundfile", "openai", "av"]
dependencies = ["replicate", "pillow", "numpy", "torch", "google-genai", "opencv-python", "soundfile", "openai", "av", "python-dotenv"]
[project.urls]
Repository = "https://github.com/Aryan185/ComfyUI-ExternalAPI-Helpers"
+1
View File
@@ -1,3 +1,4 @@
python-dotenv
replicate
pillow
numpy
+4 -2
View File
@@ -12,7 +12,7 @@ class SoraGen:
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": "A calico cat playing a piano on stage"}),
"api_key": ("STRING", {"multiline": False, "default": ""}),
"api_key": ("STRING", {"multiline": False, "default": "", "tooltip": "Directly put OpenAI API key or .env variable name (OPENAI_API_KEY)"}),
"model": (["sora-2", "sora-2-pro"], {"default": "sora-2"}),
"size": (["720x1280", "1280x720", "1024x1792", "1792x1024"], {"default": "1280x720"}),
"duration": (["4", "8", "12"], {"default": "4"}),
@@ -28,7 +28,9 @@ class SoraGen:
OUTPUT_IS_LIST = (True, False)
def generate_video(self, prompt, api_key, model, size, duration, seed, input_image=None):
client = OpenAI(api_key=api_key)
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("OPENAI_API_KEY")
if not key: raise ValueError("No API key provided.")
client = OpenAI(api_key=key)
api_args = {"prompt": prompt, "model": model, "size": size, "seconds": duration}
img_buf = None
+1 -1
View File
@@ -29,7 +29,7 @@ class VeoVertexVideoGenerator:
"asia-southeast2", "australia-southeast1", "australia-southeast2",
"me-central1", "me-central2", "me-west1"
], {"default": "us-central1"}),
"service_account": ("STRING", {"multiline": True, "default": ""}),
"service_account": ("STRING", {"multiline": True, "default": "", "tooltip": "Paste service account JSON content"}),
"model": ([
"veo-2.0-generate-001", "veo-2.0-generate-exp", "veo-2.0-generate-preview",
"veo-3.0-generate-001", "veo-3.0-fast-generate-001",
+2 -2
View File
@@ -17,7 +17,7 @@ class VeoGeminiVideoGenerator:
"model": (["veo-2.0-generate-001"], {"default": "veo-2.0-generate-001"}),
"aspect_ratio": (["16:9", "9:16"], {"default": "16:9"}),
"duration_seconds": ("INT", {"default": 8, "min": 5, "max": 8, "step": 1}),
"api_key": ("STRING", {"default": "", "multiline": False}),
"api_key": ("STRING", {"default": "", "multiline": False, "tooltip": "Directly put Gemini API key or .env variable name (GEMINI_API_KEY)"}),
"seed": ("INT", {"default": 69, "min": -1, "max": 2147483646, "step": 1}),
},
"optional": {
@@ -36,7 +36,7 @@ class VeoGeminiVideoGenerator:
negative_prompt=None, image=None):
# 1. Setup Client
key = api_key.strip() or os.environ.get("GEMINI_API_KEY")
key = os.environ.get(api_key.strip(), api_key.strip()) or os.environ.get("GEMINI_API_KEY")
if not key: raise ValueError("API Key required")
client = genai.Client(http_options={"api_version": "v1beta"}, api_key=key)