From 19650f6ada297d5766e4a89d9eb21f33898c6160 Mon Sep 17 00:00:00 2001
From: CypherNaught-0x <9931495+CypherNaught-0x@users.noreply.github.com>
Date: Wed, 4 Feb 2026 16:57:59 +0100
Subject: [PATCH 01/44] feat: initial external model support
---
docs/contributing/EXTERNAL_PROVIDERS.md | 57 +++
docs/contributing/index.md | 4 +
invokeai/app/api/dependencies.py | 10 +
invokeai/app/api/routers/app_info.py | 138 ++++++-
invokeai/app/api/routers/model_manager.py | 16 +-
.../invocations/external_image_generation.py | 148 ++++++++
.../app/services/config/config_default.py | 10 +
.../services/external_generation/__init__.py | 23 ++
.../services/external_generation/errors.py | 18 +
.../external_generation_base.py | 40 ++
.../external_generation_common.py | 55 +++
.../external_generation_default.py | 291 +++++++++++++++
.../external_generation/image_utils.py | 19 +
.../external_generation/providers/__init__.py | 4 +
.../external_generation/providers/gemini.py | 249 +++++++++++++
.../external_generation/providers/openai.py | 105 ++++++
invokeai/app/services/invocation_services.py | 3 +
.../model_install/model_install_common.py | 17 +-
.../model_install/model_install_default.py | 72 +++-
.../model_records/model_records_base.py | 19 +-
.../app/services/shared/invocation_context.py | 8 +
.../model_manager/configs/external_api.py | 80 ++++
.../backend/model_manager/configs/factory.py | 2 +
.../backend/model_manager/starter_models.py | 112 ++++++
invokeai/backend/model_manager/taxonomy.py | 4 +
invokeai/frontend/web/public/locales/en.json | 38 +-
.../components/StagingArea/context.tsx | 11 +-
.../components/StagingArea/shared.test.ts | 27 ++
.../components/StagingArea/shared.ts | 5 +-
.../controlLayers/store/paramsSlice.test.ts | 61 +++
.../controlLayers/store/paramsSlice.ts | 74 ++--
.../controlLayers/store/validators.ts | 12 +-
.../web/src/features/modelManagerV2/models.ts | 13 +-
.../store/installModelsStore.ts | 7 +-
.../ExternalProvidersForm.tsx | 281 ++++++++++++++
.../LaunchpadForm/LaunchpadForm.tsx | 12 +-
.../subpanels/InstallModels.tsx | 21 +-
.../ModelManagerPanel/ModelFormatBadge.tsx | 2 +
.../ModelPanel/Fields/BaseModelSelect.tsx | 4 +-
.../ModelPanel/Fields/ModelFormatSelect.tsx | 4 +-
.../ModelPanel/Fields/ModelTypeSelect.tsx | 4 +-
.../ModelPanel/Fields/ModelVariantSelect.tsx | 4 +-
.../Fields/PredictionTypeSelect.tsx | 4 +-
.../subpanels/ModelPanel/ModelEdit.tsx | 192 +++++++++-
.../subpanels/ModelPanel/ModelView.tsx | 23 +-
.../web/src/features/nodes/types/common.ts | 3 +
.../generation/buildExternalGraph.test.ts | 154 ++++++++
.../graph/generation/buildExternalGraph.ts | 129 +++++++
.../Bbox/BboxAspectRatioSelect.test.tsx | 44 +++
.../components/Bbox/BboxAspectRatioSelect.tsx | 5 +-
.../DimensionsAspectRatioSelect.test.tsx | 44 +++
.../DimensionsAspectRatioSelect.tsx | 10 +-
.../MainModel/mainModelPickerUtils.test.ts | 61 +++
.../MainModel/mainModelPickerUtils.ts | 14 +
.../parameters/components/ModelPicker.tsx | 24 +-
.../features/queue/hooks/useEnqueueCanvas.ts | 3 +
.../queue/hooks/useEnqueueGenerate.ts | 3 +
.../web/src/features/queue/store/readiness.ts | 23 +-
.../MainModelPicker.tsx | 11 +-
.../ExternalProviderStatusList.tsx | 39 ++
.../SettingsModal/SettingsModal.tsx | 8 +-
.../externalProviderStatusUtils.test.ts | 38 ++
.../externalProviderStatusUtils.ts | 26 ++
.../layouts/InitialStateMainModelPicker.tsx | 11 +-
.../web/src/services/api/endpoints/appInfo.ts | 40 +-
.../src/services/api/hooks/modelsByType.ts | 11 +-
.../frontend/web/src/services/api/schema.ts | 10 +-
.../frontend/web/src/services/api/types.ts | 51 +++
.../test_external_image_generation.py | 120 ++++++
tests/app/routers/test_app_info.py | 93 +++++
tests/app/routers/test_model_manager.py | 71 ++++
.../test_external_generation_service.py | 243 ++++++++++++
.../test_external_provider_adapters.py | 346 ++++++++++++++++++
.../model_install/test_model_install.py | 16 +
.../app/services/model_load/test_load_api.py | 23 ++
.../model_manager/test_external_api_config.py | 54 +++
tests/conftest.py | 2 +
77 files changed, 3923 insertions(+), 110 deletions(-)
create mode 100644 docs/contributing/EXTERNAL_PROVIDERS.md
create mode 100644 invokeai/app/invocations/external_image_generation.py
create mode 100644 invokeai/app/services/external_generation/__init__.py
create mode 100644 invokeai/app/services/external_generation/errors.py
create mode 100644 invokeai/app/services/external_generation/external_generation_base.py
create mode 100644 invokeai/app/services/external_generation/external_generation_common.py
create mode 100644 invokeai/app/services/external_generation/external_generation_default.py
create mode 100644 invokeai/app/services/external_generation/image_utils.py
create mode 100644 invokeai/app/services/external_generation/providers/__init__.py
create mode 100644 invokeai/app/services/external_generation/providers/gemini.py
create mode 100644 invokeai/app/services/external_generation/providers/openai.py
create mode 100644 invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts
create mode 100644 invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx
create mode 100644 invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts
create mode 100644 invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
create mode 100644 invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx
create mode 100644 invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx
create mode 100644 invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts
create mode 100644 invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.ts
create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx
create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts
create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts
create mode 100644 tests/app/invocations/test_external_image_generation.py
create mode 100644 tests/app/routers/test_app_info.py
create mode 100644 tests/app/routers/test_model_manager.py
create mode 100644 tests/app/services/external_generation/test_external_generation_service.py
create mode 100644 tests/app/services/external_generation/test_external_provider_adapters.py
create mode 100644 tests/backend/model_manager/test_external_api_config.py
diff --git a/docs/contributing/EXTERNAL_PROVIDERS.md b/docs/contributing/EXTERNAL_PROVIDERS.md
new file mode 100644
index 00000000000..c2371971db9
--- /dev/null
+++ b/docs/contributing/EXTERNAL_PROVIDERS.md
@@ -0,0 +1,57 @@
+# External Provider Integration
+
+This guide covers how to add new external image generation providers and model configs.
+
+## Provider Adapter Steps
+
+1) Create a provider adapter in `invokeai/app/services/external_generation/providers/` that inherits from `ExternalProvider`.
+2) Implement `is_configured()` using `InvokeAIAppConfig` fields, and `generate()` to map `ExternalGenerationRequest` to the provider API.
+3) Use helpers from `invokeai/app/services/external_generation/image_utils.py` for image encoding/decoding.
+4) Raise `ExternalProviderRequestError` on non-200 responses or empty payloads.
+5) Register the provider in `invokeai/app/api/dependencies.py` when building the `ExternalGenerationService` registry.
+
+## Config + Env Vars
+
+Add provider API keys to `InvokeAIAppConfig` with the `INVOKEAI_` prefix:
+
+- `INVOKEAI_EXTERNAL_GEMINI_API_KEY`
+- `INVOKEAI_EXTERNAL_OPENAI_API_KEY`
+
+These can also be set in `invokeai.yaml` under `external_gemini_api_key` and `external_openai_api_key`.
+
+## Example External Model Config
+
+External models are stored in the model manager like any other config. This example can be used as the `config` payload
+for `POST /api/v2/models/install?source=external://openai/gpt-image-1`:
+
+```json
+{
+ "key": "openai_gpt_image_1",
+ "name": "OpenAI GPT-Image-1",
+ "base": "external",
+ "type": "external_image_generator",
+ "format": "external_api",
+ "provider_id": "openai",
+ "provider_model_id": "gpt-image-1",
+ "capabilities": {
+ "modes": ["txt2img", "img2img", "inpaint"],
+ "supports_negative_prompt": true,
+ "supports_seed": true,
+ "supports_guidance": true,
+ "supports_reference_images": false,
+ "max_images_per_request": 1
+ },
+ "default_settings": {
+ "width": 1024,
+ "height": 1024,
+ "steps": 30
+ },
+ "tags": ["external", "openai"],
+ "is_default": false
+}
+```
+
+Notes:
+
+- `path`, `source`, and `hash` will auto-populate if omitted.
+- Set `capabilities` conservatively; the external generation service enforces them at runtime.
diff --git a/docs/contributing/index.md b/docs/contributing/index.md
index 79c1082746d..b8002a18024 100644
--- a/docs/contributing/index.md
+++ b/docs/contributing/index.md
@@ -8,6 +8,10 @@ We welcome contributions, whether features, bug fixes, code cleanup, testing, co
If you’d like to help with development, please see our [development guide](contribution_guides/development.md).
+## External Providers
+
+If you are adding external image generation providers or configs, see our [external provider integration guide](EXTERNAL_PROVIDERS.md).
+
**New Contributors:** If you’re unfamiliar with contributing to open source projects, take a look at our [new contributor guide](contribution_guides/newContributorChecklist.md).
## Nodes
diff --git a/invokeai/app/api/dependencies.py b/invokeai/app/api/dependencies.py
index 339a0ceadb4..ccb9388abcc 100644
--- a/invokeai/app/api/dependencies.py
+++ b/invokeai/app/api/dependencies.py
@@ -15,6 +15,8 @@
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import ClientStatePersistenceSqlite
from invokeai.app.services.config.config_default import InvokeAIAppConfig
from invokeai.app.services.download.download_default import DownloadQueueService
+from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
+from invokeai.app.services.external_generation.providers import GeminiProvider, OpenAIProvider
from invokeai.app.services.events.events_fastapievents import FastAPIEventService
from invokeai.app.services.image_files.image_files_disk import DiskImageFileStorage
from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
@@ -145,6 +147,13 @@ def initialize(
),
)
download_queue_service = DownloadQueueService(app_config=configuration, event_bus=events)
+ external_generation = ExternalGenerationService(
+ providers={
+ GeminiProvider.provider_id: GeminiProvider(app_config=configuration, logger=logger),
+ OpenAIProvider.provider_id: OpenAIProvider(app_config=configuration, logger=logger),
+ },
+ logger=logger,
+ )
model_images_service = ModelImageFileStorageDisk(model_images_folder / "model_images")
model_manager = ModelManagerService.build_model_manager(
app_config=configuration,
@@ -184,6 +193,7 @@ def initialize(
model_relationships=model_relationships,
model_relationship_records=model_relationship_records,
download_queue=download_queue_service,
+ external_generation=external_generation,
names=names,
performance_statistics=performance_statistics,
session_processor=session_processor,
diff --git a/invokeai/app/api/routers/app_info.py b/invokeai/app/api/routers/app_info.py
index d8f3bb2f807..cbb3e6fb47d 100644
--- a/invokeai/app/api/routers/app_info.py
+++ b/invokeai/app/api/routers/app_info.py
@@ -2,12 +2,18 @@
from importlib.metadata import distributions
import torch
-from fastapi import Body
+from fastapi import Body, HTTPException, Path
from fastapi.routing import APIRouter
from pydantic import BaseModel, Field
from invokeai.app.api.dependencies import ApiDependencies
-from invokeai.app.services.config.config_default import InvokeAIAppConfig, get_config
+from invokeai.app.services.config.config_default import (
+ DefaultInvokeAIAppConfig,
+ InvokeAIAppConfig,
+ get_config,
+ load_and_migrate_config,
+)
+from invokeai.app.services.external_generation.external_generation_common import ExternalProviderStatus
from invokeai.app.services.invocation_cache.invocation_cache_common import InvocationCacheStatus
from invokeai.backend.image_util.infill_methods.patchmatch import PatchMatch
from invokeai.backend.util.logging import logging
@@ -41,7 +47,7 @@ async def get_version() -> AppVersion:
async def get_app_deps() -> dict[str, str]:
deps: dict[str, str] = {dist.metadata["Name"]: dist.version for dist in distributions()}
try:
- cuda = torch.version.cuda or "N/A"
+ cuda = getattr(getattr(torch, "version", None), "cuda", None) or "N/A" # pyright: ignore[reportAttributeAccessIssue]
except Exception:
cuda = "N/A"
@@ -64,6 +70,29 @@ class InvokeAIAppConfigWithSetFields(BaseModel):
config: InvokeAIAppConfig = Field(description="The InvokeAI App Config")
+class ExternalProviderStatusModel(BaseModel):
+ provider_id: str = Field(description="The external provider identifier")
+ configured: bool = Field(description="Whether credentials are configured for the provider")
+ message: str | None = Field(default=None, description="Optional provider status detail")
+
+
+class ExternalProviderConfigUpdate(BaseModel):
+ api_key: str | None = Field(default=None, description="API key for the external provider")
+ base_url: str | None = Field(default=None, description="Optional base URL override for the provider")
+
+
+class ExternalProviderConfigModel(BaseModel):
+ provider_id: str = Field(description="The external provider identifier")
+ api_key_configured: bool = Field(description="Whether an API key is configured")
+ base_url: str | None = Field(default=None, description="Optional base URL override")
+
+
+EXTERNAL_PROVIDER_FIELDS: dict[str, tuple[str, str]] = {
+ "gemini": ("external_gemini_api_key", "external_gemini_base_url"),
+ "openai": ("external_openai_api_key", "external_openai_base_url"),
+}
+
+
@app_router.get(
"/runtime_config", operation_id="get_runtime_config", status_code=200, response_model=InvokeAIAppConfigWithSetFields
)
@@ -72,6 +101,109 @@ async def get_runtime_config() -> InvokeAIAppConfigWithSetFields:
return InvokeAIAppConfigWithSetFields(set_fields=config.model_fields_set, config=config)
+@app_router.get(
+ "/external_providers/status",
+ operation_id="get_external_provider_statuses",
+ status_code=200,
+ response_model=list[ExternalProviderStatusModel],
+)
+async def get_external_provider_statuses() -> list[ExternalProviderStatusModel]:
+ statuses = ApiDependencies.invoker.services.external_generation.get_provider_statuses()
+ return [status_to_model(status) for status in statuses.values()]
+
+
+@app_router.get(
+ "/external_providers/config",
+ operation_id="get_external_provider_configs",
+ status_code=200,
+ response_model=list[ExternalProviderConfigModel],
+)
+async def get_external_provider_configs() -> list[ExternalProviderConfigModel]:
+ config = get_config()
+ return [_build_external_provider_config(provider_id, config) for provider_id in EXTERNAL_PROVIDER_FIELDS]
+
+
+@app_router.post(
+ "/external_providers/config/{provider_id}",
+ operation_id="set_external_provider_config",
+ status_code=200,
+ response_model=ExternalProviderConfigModel,
+)
+async def set_external_provider_config(
+ provider_id: str = Path(description="The external provider identifier"),
+ update: ExternalProviderConfigUpdate = Body(description="External provider configuration settings"),
+) -> ExternalProviderConfigModel:
+ api_key_field, base_url_field = _get_external_provider_fields(provider_id)
+ updates: dict[str, str | None] = {}
+
+ if update.api_key is not None:
+ api_key = update.api_key.strip()
+ updates[api_key_field] = api_key or None
+ if update.base_url is not None:
+ base_url = update.base_url.strip()
+ updates[base_url_field] = base_url or None
+
+ if not updates:
+ raise HTTPException(status_code=400, detail="No external provider config fields provided")
+
+ _apply_external_provider_update(updates)
+ return _build_external_provider_config(provider_id, get_config())
+
+
+@app_router.delete(
+ "/external_providers/config/{provider_id}",
+ operation_id="reset_external_provider_config",
+ status_code=200,
+ response_model=ExternalProviderConfigModel,
+)
+async def reset_external_provider_config(
+ provider_id: str = Path(description="The external provider identifier"),
+) -> ExternalProviderConfigModel:
+ api_key_field, base_url_field = _get_external_provider_fields(provider_id)
+ _apply_external_provider_update({api_key_field: None, base_url_field: None})
+ return _build_external_provider_config(provider_id, get_config())
+
+
+def status_to_model(status: ExternalProviderStatus) -> ExternalProviderStatusModel:
+ return ExternalProviderStatusModel(
+ provider_id=status.provider_id,
+ configured=status.configured,
+ message=status.message,
+ )
+
+
+def _get_external_provider_fields(provider_id: str) -> tuple[str, str]:
+ if provider_id not in EXTERNAL_PROVIDER_FIELDS:
+ raise HTTPException(status_code=404, detail=f"Unknown external provider '{provider_id}'")
+ return EXTERNAL_PROVIDER_FIELDS[provider_id]
+
+
+def _apply_external_provider_update(updates: dict[str, str | None]) -> None:
+ runtime_config = get_config()
+ config_path = runtime_config.config_file_path
+ if config_path.exists():
+ file_config = load_and_migrate_config(config_path)
+ else:
+ file_config = DefaultInvokeAIAppConfig()
+
+ for config in (runtime_config, file_config):
+ config.update_config(updates)
+ for field_name, value in updates.items():
+ if value is None:
+ config.model_fields_set.discard(field_name)
+
+ file_config.write_file(config_path, as_example=False)
+
+
+def _build_external_provider_config(provider_id: str, config: InvokeAIAppConfig) -> ExternalProviderConfigModel:
+ api_key_field, base_url_field = _get_external_provider_fields(provider_id)
+ return ExternalProviderConfigModel(
+ provider_id=provider_id,
+ api_key_configured=bool(getattr(config, api_key_field)),
+ base_url=getattr(config, base_url_field),
+ )
+
+
@app_router.get(
"/logging",
operation_id="get_log_level",
diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py
index a1f6b3a744a..4e1b14d18c1 100644
--- a/invokeai/app/api/routers/model_manager.py
+++ b/invokeai/app/api/routers/model_manager.py
@@ -30,6 +30,7 @@
)
from invokeai.app.services.orphaned_models import OrphanedModelInfo
from invokeai.app.util.suppress_output import SuppressOutput
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig
from invokeai.backend.model_manager.configs.factory import AnyModelConfig, ModelConfigFactory
from invokeai.backend.model_manager.configs.main import (
Main_Checkpoint_SD1_Config,
@@ -145,8 +146,16 @@ async def list_model_records(
found_models.extend(
record_store.search_by_attr(model_type=model_type, model_name=model_name, model_format=model_format)
)
- for model in found_models:
+ for index, model in enumerate(found_models):
model = add_cover_image_to_model_config(model, ApiDependencies)
+ if isinstance(model, ExternalApiModelConfig):
+ starter_match = next((starter for starter in STARTER_MODELS if starter.source == model.source), None)
+ if starter_match is not None:
+ if starter_match.capabilities is not None:
+ setattr(model, "capabilities", starter_match.capabilities)
+ if starter_match.default_settings is not None:
+ setattr(model, "default_settings", starter_match.default_settings)
+ found_models[index] = model
return ModelsList(models=found_models)
@@ -166,6 +175,8 @@ async def list_missing_models() -> ModelsList:
missing_models: list[AnyModelConfig] = []
for model_config in record_store.all_models():
+ if model_config.base == BaseModelType.External or model_config.format == ModelFormat.ExternalApi:
+ continue
if not (models_path / model_config.path).resolve().exists():
missing_models.append(model_config)
@@ -250,7 +261,8 @@ async def reidentify_model(
result.config.name = config.name
result.config.description = config.description
result.config.cover_image = config.cover_image
- result.config.trigger_phrases = config.trigger_phrases
+ if hasattr(result.config, "trigger_phrases") and hasattr(config, "trigger_phrases"):
+ setattr(result.config, "trigger_phrases", getattr(config, "trigger_phrases"))
result.config.source = config.source
result.config.source_type = config.source_type
diff --git a/invokeai/app/invocations/external_image_generation.py b/invokeai/app/invocations/external_image_generation.py
new file mode 100644
index 00000000000..c70ecb40795
--- /dev/null
+++ b/invokeai/app/invocations/external_image_generation.py
@@ -0,0 +1,148 @@
+from typing import Any
+
+from invokeai.app.invocations.baseinvocation import BaseInvocation, invocation
+from invokeai.app.invocations.fields import (
+ FieldDescriptions,
+ ImageField,
+ InputField,
+ MetadataField,
+ WithBoard,
+ WithMetadata,
+)
+from invokeai.app.invocations.model import ModelIdentifierField
+from invokeai.app.invocations.primitives import ImageCollectionOutput
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+ ExternalReferenceImage,
+)
+from invokeai.app.services.shared.invocation_context import InvocationContext
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig, ExternalGenerationMode
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType
+
+
+@invocation(
+ "external_image_generation",
+ title="External Image Generation",
+ tags=["external", "generation"],
+ category="image",
+ version="1.0.0",
+)
+class ExternalImageGenerationInvocation(BaseInvocation, WithMetadata, WithBoard):
+ """Generate images using an external provider."""
+
+ model: ModelIdentifierField = InputField(
+ description=FieldDescriptions.main_model,
+ ui_model_base=[BaseModelType.External],
+ ui_model_type=[ModelType.ExternalImageGenerator],
+ ui_model_format=[ModelFormat.ExternalApi],
+ )
+ mode: ExternalGenerationMode = InputField(default="txt2img", description="Generation mode")
+ prompt: str = InputField(description="Prompt")
+ negative_prompt: str | None = InputField(default=None, description="Negative prompt")
+ seed: int | None = InputField(default=None, description=FieldDescriptions.seed)
+ num_images: int = InputField(default=1, gt=0, description="Number of images to generate")
+ width: int = InputField(default=1024, gt=0, description=FieldDescriptions.width)
+ height: int = InputField(default=1024, gt=0, description=FieldDescriptions.height)
+ steps: int | None = InputField(default=None, gt=0, description=FieldDescriptions.steps)
+ guidance: float | None = InputField(default=None, ge=0, description="Guidance strength")
+ init_image: ImageField | None = InputField(default=None, description="Init image for img2img/inpaint")
+ mask_image: ImageField | None = InputField(default=None, description="Mask image for inpaint")
+ reference_images: list[ImageField] = InputField(default=[], description="Reference images")
+ reference_image_weights: list[float] | None = InputField(default=None, description="Reference image weights")
+ reference_image_modes: list[str] | None = InputField(default=None, description="Reference image modes")
+
+ def invoke(self, context: InvocationContext) -> ImageCollectionOutput:
+ model_config = context.models.get_config(self.model)
+ if not isinstance(model_config, ExternalApiModelConfig):
+ raise ValueError("Selected model is not an external API model")
+
+ init_image = None
+ if self.init_image is not None:
+ init_image = context.images.get_pil(self.init_image.image_name, mode="RGB")
+
+ mask_image = None
+ if self.mask_image is not None:
+ mask_image = context.images.get_pil(self.mask_image.image_name, mode="L")
+
+ if self.reference_image_weights is not None and len(self.reference_image_weights) != len(self.reference_images):
+ raise ValueError("reference_image_weights must match reference_images length")
+
+ if self.reference_image_modes is not None and len(self.reference_image_modes) != len(self.reference_images):
+ raise ValueError("reference_image_modes must match reference_images length")
+
+ reference_images: list[ExternalReferenceImage] = []
+ for index, image_field in enumerate(self.reference_images):
+ reference_image = context.images.get_pil(image_field.image_name, mode="RGB")
+ weight = None
+ mode = None
+ if self.reference_image_weights is not None:
+ weight = self.reference_image_weights[index]
+ if self.reference_image_modes is not None:
+ mode = self.reference_image_modes[index]
+ reference_images.append(ExternalReferenceImage(image=reference_image, weight=weight, mode=mode))
+
+ request = ExternalGenerationRequest(
+ model=model_config,
+ mode=self.mode,
+ prompt=self.prompt,
+ negative_prompt=self.negative_prompt,
+ seed=self.seed,
+ num_images=self.num_images,
+ width=self.width,
+ height=self.height,
+ steps=self.steps,
+ guidance=self.guidance,
+ init_image=init_image,
+ mask_image=mask_image,
+ reference_images=reference_images,
+ metadata=self._build_request_metadata(),
+ )
+
+ result = context._services.external_generation.generate(request)
+
+ outputs: list[ImageField] = []
+ for generated in result.images:
+ metadata = self._build_output_metadata(model_config, result, generated.seed)
+ image_dto = context.images.save(image=generated.image, metadata=metadata)
+ outputs.append(ImageField(image_name=image_dto.image_name))
+
+ return ImageCollectionOutput(collection=outputs)
+
+ def _build_request_metadata(self) -> dict[str, Any] | None:
+ if self.metadata is None:
+ return None
+ return self.metadata.root
+
+ def _build_output_metadata(
+ self,
+ model_config: ExternalApiModelConfig,
+ result: ExternalGenerationResult,
+ image_seed: int | None,
+ ) -> MetadataField | None:
+ metadata: dict[str, Any] = {}
+
+ if self.metadata is not None:
+ metadata.update(self.metadata.root)
+
+ metadata.update(
+ {
+ "external_provider": model_config.provider_id,
+ "external_model_id": model_config.provider_model_id,
+ }
+ )
+
+ provider_request_id = getattr(result, "provider_request_id", None)
+ if provider_request_id:
+ metadata["external_request_id"] = provider_request_id
+
+ provider_metadata = getattr(result, "provider_metadata", None)
+ if provider_metadata:
+ metadata["external_provider_metadata"] = provider_metadata
+
+ if image_seed is not None:
+ metadata["external_seed"] = image_seed
+
+ if not metadata:
+ return None
+ return MetadataField(root=metadata)
diff --git a/invokeai/app/services/config/config_default.py b/invokeai/app/services/config/config_default.py
index 2cc2aaf273c..2fc2e9710ae 100644
--- a/invokeai/app/services/config/config_default.py
+++ b/invokeai/app/services/config/config_default.py
@@ -207,6 +207,16 @@ class InvokeAIAppConfig(BaseSettings):
# MULTIUSER
multiuser: bool = Field(default=False, description="Enable multiuser support. When disabled, the application runs in single-user mode using a default system account with administrator privileges. When enabled, requires user authentication and authorization.")
+ # EXTERNAL PROVIDERS
+ external_gemini_api_key: Optional[str] = Field(default=None, description="API key for Gemini image generation.")
+ external_openai_api_key: Optional[str] = Field(default=None, description="API key for OpenAI image generation.")
+ external_gemini_base_url: Optional[str] = Field(
+ default=None, description="Base URL override for Gemini image generation."
+ )
+ external_openai_base_url: Optional[str] = Field(
+ default=None, description="Base URL override for OpenAI image generation."
+ )
+
# fmt: on
model_config = SettingsConfigDict(env_prefix="INVOKEAI_", env_ignore_empty=True)
diff --git a/invokeai/app/services/external_generation/__init__.py b/invokeai/app/services/external_generation/__init__.py
new file mode 100644
index 00000000000..692da64643a
--- /dev/null
+++ b/invokeai/app/services/external_generation/__init__.py
@@ -0,0 +1,23 @@
+from invokeai.app.services.external_generation.external_generation_base import (
+ ExternalGenerationServiceBase,
+ ExternalProvider,
+)
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+ ExternalGeneratedImage,
+ ExternalProviderStatus,
+ ExternalReferenceImage,
+)
+from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
+
+__all__ = [
+ "ExternalGenerationRequest",
+ "ExternalGenerationResult",
+ "ExternalGeneratedImage",
+ "ExternalGenerationService",
+ "ExternalGenerationServiceBase",
+ "ExternalProvider",
+ "ExternalProviderStatus",
+ "ExternalReferenceImage",
+]
diff --git a/invokeai/app/services/external_generation/errors.py b/invokeai/app/services/external_generation/errors.py
new file mode 100644
index 00000000000..9980b39bc43
--- /dev/null
+++ b/invokeai/app/services/external_generation/errors.py
@@ -0,0 +1,18 @@
+class ExternalGenerationError(Exception):
+ """Base error for external generation."""
+
+
+class ExternalProviderNotFoundError(ExternalGenerationError):
+ """Raised when no provider is registered for a model."""
+
+
+class ExternalProviderNotConfiguredError(ExternalGenerationError):
+ """Raised when a provider is missing required credentials."""
+
+
+class ExternalProviderCapabilityError(ExternalGenerationError):
+ """Raised when a request is not supported by provider capabilities."""
+
+
+class ExternalProviderRequestError(ExternalGenerationError):
+ """Raised when a provider rejects the request or returns an error."""
diff --git a/invokeai/app/services/external_generation/external_generation_base.py b/invokeai/app/services/external_generation/external_generation_base.py
new file mode 100644
index 00000000000..2145ff5ca42
--- /dev/null
+++ b/invokeai/app/services/external_generation/external_generation_base.py
@@ -0,0 +1,40 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from logging import Logger
+
+from invokeai.app.services.config import InvokeAIAppConfig
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+ ExternalProviderStatus,
+)
+
+
+class ExternalProvider(ABC):
+ provider_id: str
+
+ def __init__(self, app_config: InvokeAIAppConfig, logger: Logger) -> None:
+ self._app_config = app_config
+ self._logger = logger
+
+ @abstractmethod
+ def is_configured(self) -> bool:
+ raise NotImplementedError
+
+ @abstractmethod
+ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
+ raise NotImplementedError
+
+ def get_status(self) -> ExternalProviderStatus:
+ return ExternalProviderStatus(provider_id=self.provider_id, configured=self.is_configured())
+
+
+class ExternalGenerationServiceBase(ABC):
+ @abstractmethod
+ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
+ raise NotImplementedError
+
+ @abstractmethod
+ def get_provider_statuses(self) -> dict[str, ExternalProviderStatus]:
+ raise NotImplementedError
diff --git a/invokeai/app/services/external_generation/external_generation_common.py b/invokeai/app/services/external_generation/external_generation_common.py
new file mode 100644
index 00000000000..c1e2f4706f5
--- /dev/null
+++ b/invokeai/app/services/external_generation/external_generation_common.py
@@ -0,0 +1,55 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import Any
+
+from PIL.Image import Image as PILImageType
+
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig, ExternalGenerationMode
+
+
+@dataclass(frozen=True)
+class ExternalReferenceImage:
+ image: PILImageType
+ weight: float | None = None
+ mode: str | None = None
+
+
+@dataclass(frozen=True)
+class ExternalGenerationRequest:
+ model: ExternalApiModelConfig
+ mode: ExternalGenerationMode
+ prompt: str
+ negative_prompt: str | None
+ seed: int | None
+ num_images: int
+ width: int
+ height: int
+ steps: int | None
+ guidance: float | None
+ init_image: PILImageType | None
+ mask_image: PILImageType | None
+ reference_images: list[ExternalReferenceImage]
+ metadata: dict[str, Any] | None
+
+
+@dataclass(frozen=True)
+class ExternalGeneratedImage:
+ image: PILImageType
+ seed: int | None = None
+
+
+@dataclass(frozen=True)
+class ExternalGenerationResult:
+ images: list[ExternalGeneratedImage]
+ seed_used: int | None = None
+ provider_request_id: str | None = None
+ provider_metadata: dict[str, Any] | None = None
+ content_filters: dict[str, str] | None = None
+
+
+@dataclass(frozen=True)
+class ExternalProviderStatus:
+ provider_id: str
+ configured: bool
+ message: str | None = None
diff --git a/invokeai/app/services/external_generation/external_generation_default.py b/invokeai/app/services/external_generation/external_generation_default.py
new file mode 100644
index 00000000000..c72e16cde8d
--- /dev/null
+++ b/invokeai/app/services/external_generation/external_generation_default.py
@@ -0,0 +1,291 @@
+from __future__ import annotations
+
+from logging import Logger
+
+from PIL import Image
+from PIL.Image import Image as PILImageType
+
+from invokeai.app.services.external_generation.errors import (
+ ExternalProviderCapabilityError,
+ ExternalProviderNotConfiguredError,
+ ExternalProviderNotFoundError,
+)
+from invokeai.app.services.external_generation.external_generation_base import (
+ ExternalGenerationServiceBase,
+ ExternalProvider,
+)
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+ ExternalProviderStatus,
+)
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig, ExternalImageSize
+from invokeai.backend.model_manager.starter_models import STARTER_MODELS
+
+
+class ExternalGenerationService(ExternalGenerationServiceBase):
+ def __init__(self, providers: dict[str, ExternalProvider], logger: Logger) -> None:
+ self._providers = providers
+ self._logger = logger
+
+ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
+ provider = self._providers.get(request.model.provider_id)
+ if provider is None:
+ raise ExternalProviderNotFoundError(f"No external provider registered for '{request.model.provider_id}'")
+
+ if not provider.is_configured():
+ raise ExternalProviderNotConfiguredError(f"Provider '{request.model.provider_id}' is missing credentials")
+
+ request = self._refresh_model_capabilities(request)
+ request = self._bucket_request(request)
+
+ self._validate_request(request)
+ return provider.generate(request)
+
+ def get_provider_statuses(self) -> dict[str, ExternalProviderStatus]:
+ return {provider_id: provider.get_status() for provider_id, provider in self._providers.items()}
+
+ def _validate_request(self, request: ExternalGenerationRequest) -> None:
+ capabilities = request.model.capabilities
+
+ self._logger.debug(
+ "Validating external request provider=%s model=%s mode=%s supported=%s",
+ request.model.provider_id,
+ request.model.provider_model_id,
+ request.mode,
+ capabilities.modes,
+ )
+
+ if request.mode not in capabilities.modes:
+ raise ExternalProviderCapabilityError(f"Mode '{request.mode}' is not supported by {request.model.name}")
+
+ if request.negative_prompt and not capabilities.supports_negative_prompt:
+ raise ExternalProviderCapabilityError(f"Negative prompts are not supported by {request.model.name}")
+
+ if request.seed is not None and not capabilities.supports_seed:
+ raise ExternalProviderCapabilityError(f"Seed control is not supported by {request.model.name}")
+
+ if request.guidance is not None and not capabilities.supports_guidance:
+ raise ExternalProviderCapabilityError(f"Guidance is not supported by {request.model.name}")
+
+ if request.reference_images and not capabilities.supports_reference_images:
+ raise ExternalProviderCapabilityError(f"Reference images are not supported by {request.model.name}")
+
+ if capabilities.max_reference_images is not None:
+ if len(request.reference_images) > capabilities.max_reference_images:
+ raise ExternalProviderCapabilityError(
+ f"{request.model.name} supports at most {capabilities.max_reference_images} reference images"
+ )
+
+ if capabilities.max_images_per_request is not None and request.num_images > capabilities.max_images_per_request:
+ raise ExternalProviderCapabilityError(
+ f"{request.model.name} supports at most {capabilities.max_images_per_request} images per request"
+ )
+
+ if capabilities.max_image_size is not None:
+ if request.width > capabilities.max_image_size.width or request.height > capabilities.max_image_size.height:
+ raise ExternalProviderCapabilityError(
+ f"{request.model.name} supports a maximum size of {capabilities.max_image_size.width}x{capabilities.max_image_size.height}"
+ )
+
+ if capabilities.allowed_aspect_ratios:
+ aspect_ratio = _format_aspect_ratio(request.width, request.height)
+ if aspect_ratio not in capabilities.allowed_aspect_ratios:
+ size_ratio = None
+ if capabilities.aspect_ratio_sizes:
+ size_ratio = _ratio_for_size(request.width, request.height, capabilities.aspect_ratio_sizes)
+ if size_ratio is None or size_ratio not in capabilities.allowed_aspect_ratios:
+ ratio_label = size_ratio or aspect_ratio
+ raise ExternalProviderCapabilityError(
+ f"{request.model.name} does not support aspect ratio {ratio_label}"
+ )
+
+ required_modes = capabilities.input_image_required_for or ["img2img", "inpaint"]
+ if request.mode in required_modes and request.init_image is None:
+ raise ExternalProviderCapabilityError(
+ f"Mode '{request.mode}' requires an init image for {request.model.name}"
+ )
+
+ if request.mode == "inpaint" and request.mask_image is None:
+ raise ExternalProviderCapabilityError(
+ f"Mode '{request.mode}' requires a mask image for {request.model.name}"
+ )
+
+ def _refresh_model_capabilities(self, request: ExternalGenerationRequest) -> ExternalGenerationRequest:
+ try:
+ from invokeai.app.api.dependencies import ApiDependencies
+
+ record = ApiDependencies.invoker.services.model_manager.store.get_model(request.model.key)
+ except Exception:
+ record = None
+
+ if not isinstance(record, ExternalApiModelConfig):
+ return request
+
+ if record.key != request.model.key:
+ return request
+
+ if record.provider_id != request.model.provider_id:
+ return request
+
+ if record.provider_model_id != request.model.provider_model_id:
+ return request
+
+ record = _apply_starter_overrides(record)
+
+ if record == request.model:
+ return request
+
+ return ExternalGenerationRequest(
+ model=record,
+ mode=request.mode,
+ prompt=request.prompt,
+ negative_prompt=request.negative_prompt,
+ seed=request.seed,
+ num_images=request.num_images,
+ width=request.width,
+ height=request.height,
+ steps=request.steps,
+ guidance=request.guidance,
+ init_image=request.init_image,
+ mask_image=request.mask_image,
+ reference_images=request.reference_images,
+ metadata=request.metadata,
+ )
+
+ def _bucket_request(self, request: ExternalGenerationRequest) -> ExternalGenerationRequest:
+ capabilities = request.model.capabilities
+ if not capabilities.allowed_aspect_ratios:
+ return request
+
+ aspect_ratio = _format_aspect_ratio(request.width, request.height)
+ size = None
+ if capabilities.aspect_ratio_sizes:
+ size = capabilities.aspect_ratio_sizes.get(aspect_ratio)
+
+ if size is not None:
+ if request.width == size.width and request.height == size.height:
+ return request
+ return self._bucket_to_size(request, size.width, size.height, aspect_ratio)
+
+ if aspect_ratio in capabilities.allowed_aspect_ratios:
+ return request
+
+ if not capabilities.aspect_ratio_sizes:
+ return request
+
+ closest = _select_closest_ratio(
+ request.width,
+ request.height,
+ capabilities.allowed_aspect_ratios,
+ )
+ if closest is None:
+ return request
+
+ size = capabilities.aspect_ratio_sizes.get(closest)
+ if size is None:
+ return request
+
+ return self._bucket_to_size(request, size.width, size.height, closest)
+
+ def _bucket_to_size(
+ self,
+ request: ExternalGenerationRequest,
+ width: int,
+ height: int,
+ ratio: str,
+ ) -> ExternalGenerationRequest:
+ self._logger.info(
+ "Bucketing external request provider=%s model=%s %sx%s -> %sx%s (ratio %s)",
+ request.model.provider_id,
+ request.model.provider_model_id,
+ request.width,
+ request.height,
+ width,
+ height,
+ ratio,
+ )
+
+ return ExternalGenerationRequest(
+ model=request.model,
+ mode=request.mode,
+ prompt=request.prompt,
+ negative_prompt=request.negative_prompt,
+ seed=request.seed,
+ num_images=request.num_images,
+ width=width,
+ height=height,
+ steps=request.steps,
+ guidance=request.guidance,
+ init_image=_resize_image(request.init_image, width, height, "RGB"),
+ mask_image=_resize_image(request.mask_image, width, height, "L"),
+ reference_images=request.reference_images,
+ metadata=request.metadata,
+ )
+
+
+def _format_aspect_ratio(width: int, height: int) -> str:
+ divisor = _gcd(width, height)
+ return f"{width // divisor}:{height // divisor}"
+
+
+def _select_closest_ratio(width: int, height: int, ratios: list[str]) -> str | None:
+ ratio = width / height
+ parsed: list[tuple[str, float]] = []
+ for value in ratios:
+ parsed_ratio = _parse_ratio(value)
+ if parsed_ratio is not None:
+ parsed.append((value, parsed_ratio))
+ if not parsed:
+ return None
+ return min(parsed, key=lambda item: abs(item[1] - ratio))[0]
+
+
+def _ratio_for_size(width: int, height: int, sizes: dict[str, ExternalImageSize]) -> str | None:
+ for ratio, size in sizes.items():
+ if size.width == width and size.height == height:
+ return ratio
+ return None
+
+
+def _parse_ratio(value: str) -> float | None:
+ if ":" not in value:
+ return None
+ left, right = value.split(":", 1)
+ try:
+ numerator = float(left)
+ denominator = float(right)
+ except ValueError:
+ return None
+ if denominator == 0:
+ return None
+ return numerator / denominator
+
+
+def _gcd(a: int, b: int) -> int:
+ while b:
+ a, b = b, a % b
+ return a
+
+
+def _resize_image(image: PILImageType | None, width: int, height: int, mode: str) -> PILImageType | None:
+ if image is None:
+ return None
+ if image.width == width and image.height == height:
+ return image
+ return image.convert(mode).resize((width, height), Image.Resampling.LANCZOS)
+
+
+def _apply_starter_overrides(model: ExternalApiModelConfig) -> ExternalApiModelConfig:
+ source = model.source or f"external://{model.provider_id}/{model.provider_model_id}"
+ starter_match = next((starter for starter in STARTER_MODELS if starter.source == source), None)
+ if starter_match is None:
+ return model
+ updates: dict[str, object] = {}
+ if starter_match.capabilities is not None:
+ updates["capabilities"] = starter_match.capabilities
+ if starter_match.default_settings is not None:
+ updates["default_settings"] = starter_match.default_settings
+ if not updates:
+ return model
+ return model.model_copy(update=updates)
diff --git a/invokeai/app/services/external_generation/image_utils.py b/invokeai/app/services/external_generation/image_utils.py
new file mode 100644
index 00000000000..a23c1f11d66
--- /dev/null
+++ b/invokeai/app/services/external_generation/image_utils.py
@@ -0,0 +1,19 @@
+from __future__ import annotations
+
+import base64
+import io
+
+from PIL import Image
+from PIL.Image import Image as PILImageType
+
+
+def encode_image_base64(image: PILImageType, format: str = "PNG") -> str:
+ buffer = io.BytesIO()
+ image.save(buffer, format=format)
+ return base64.b64encode(buffer.getvalue()).decode("ascii")
+
+
+def decode_image_base64(encoded: str) -> PILImageType:
+ data = base64.b64decode(encoded)
+ image = Image.open(io.BytesIO(data))
+ return image.convert("RGB")
diff --git a/invokeai/app/services/external_generation/providers/__init__.py b/invokeai/app/services/external_generation/providers/__init__.py
new file mode 100644
index 00000000000..9e380fca1e1
--- /dev/null
+++ b/invokeai/app/services/external_generation/providers/__init__.py
@@ -0,0 +1,4 @@
+from invokeai.app.services.external_generation.providers.gemini import GeminiProvider
+from invokeai.app.services.external_generation.providers.openai import OpenAIProvider
+
+__all__ = ["GeminiProvider", "OpenAIProvider"]
diff --git a/invokeai/app/services/external_generation/providers/gemini.py b/invokeai/app/services/external_generation/providers/gemini.py
new file mode 100644
index 00000000000..4d43431a14a
--- /dev/null
+++ b/invokeai/app/services/external_generation/providers/gemini.py
@@ -0,0 +1,249 @@
+from __future__ import annotations
+
+import json
+import uuid
+
+import requests
+from PIL.Image import Image as PILImageType
+
+from invokeai.app.services.external_generation.errors import ExternalProviderRequestError
+from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGeneratedImage,
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+)
+from invokeai.app.services.external_generation.image_utils import decode_image_base64, encode_image_base64
+
+
+class GeminiProvider(ExternalProvider):
+ provider_id = "gemini"
+ _SYSTEM_INSTRUCTION = (
+ "You are an image generation model. Always respond with an image based on the user's prompt. "
+ "Do not return text-only responses. If the user input is not an edit instruction, "
+ "interpret it as a request to create a new image."
+ )
+
+ def is_configured(self) -> bool:
+ return bool(self._app_config.external_gemini_api_key)
+
+ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
+ api_key = self._app_config.external_gemini_api_key
+ if not api_key:
+ raise ExternalProviderRequestError("Gemini API key is not configured")
+
+ base_url = (self._app_config.external_gemini_base_url or "https://generativelanguage.googleapis.com").rstrip(
+ "/"
+ )
+ if not base_url.endswith("/v1") and not base_url.endswith("/v1beta"):
+ base_url = f"{base_url}/v1beta"
+ model_id = request.model.provider_model_id.removeprefix("models/")
+ endpoint = f"{base_url}/models/{model_id}:generateContent"
+
+ request_parts: list[dict[str, object]] = []
+
+ if request.init_image is not None:
+ request_parts.append(
+ {
+ "inlineData": {
+ "mimeType": "image/png",
+ "data": encode_image_base64(request.init_image),
+ }
+ }
+ )
+
+ request_parts.append({"text": request.prompt})
+
+ for reference in request.reference_images:
+ request_parts.append(
+ {
+ "inlineData": {
+ "mimeType": "image/png",
+ "data": encode_image_base64(reference.image),
+ }
+ }
+ )
+
+ generation_config: dict[str, object] = {
+ "candidateCount": request.num_images,
+ "responseModalities": ["IMAGE"],
+ }
+ aspect_ratio = _select_aspect_ratio(
+ request.width,
+ request.height,
+ request.model.capabilities.allowed_aspect_ratios,
+ )
+ system_instruction = self._SYSTEM_INSTRUCTION
+ if request.init_image is not None:
+ system_instruction = (
+ f"{system_instruction} An input image is provided. "
+ "Treat the prompt as an edit instruction and modify the image accordingly. "
+ "Do not return the original image unchanged."
+ )
+ if aspect_ratio is not None:
+ system_instruction = f"{system_instruction} Use an aspect ratio of {aspect_ratio}."
+
+ payload: dict[str, object] = {
+ "systemInstruction": {"parts": [{"text": system_instruction}]},
+ "contents": [{"role": "user", "parts": request_parts}],
+ "generationConfig": generation_config,
+ }
+
+ self._dump_debug_payload("request", payload)
+
+ response = requests.post(
+ endpoint,
+ params={"key": api_key},
+ json=payload,
+ timeout=120,
+ )
+
+ if not response.ok:
+ raise ExternalProviderRequestError(
+ f"Gemini request failed with status {response.status_code} for model '{model_id}': {response.text}"
+ )
+
+ data = response.json()
+ self._dump_debug_payload("response", data)
+ if not isinstance(data, dict):
+ raise ExternalProviderRequestError("Gemini response payload was not a JSON object")
+ images: list[ExternalGeneratedImage] = []
+ text_parts: list[str] = []
+ finish_messages: list[str] = []
+ candidates = data.get("candidates")
+ if not isinstance(candidates, list):
+ raise ExternalProviderRequestError("Gemini response payload missing candidates")
+ for candidate in candidates:
+ if not isinstance(candidate, dict):
+ continue
+ finish_message = candidate.get("finishMessage")
+ finish_reason = candidate.get("finishReason")
+ if isinstance(finish_message, str):
+ finish_messages.append(finish_message)
+ elif isinstance(finish_reason, str):
+ finish_messages.append(f"Finish reason: {finish_reason}")
+ for part in _iter_response_parts(candidate):
+ inline_data = part.get("inline_data") or part.get("inlineData")
+ if isinstance(inline_data, dict):
+ encoded = inline_data.get("data")
+ if encoded:
+ image = decode_image_base64(encoded)
+ images.append(ExternalGeneratedImage(image=image, seed=request.seed))
+ self._dump_debug_image(image)
+ continue
+ file_data = part.get("fileData") or part.get("file_data")
+ if isinstance(file_data, dict):
+ file_uri = file_data.get("fileUri") or file_data.get("file_uri")
+ if isinstance(file_uri, str) and file_uri:
+ raise ExternalProviderRequestError(
+ f"Gemini returned fileUri instead of inline image data: {file_uri}"
+ )
+ text = part.get("text")
+ if isinstance(text, str):
+ text_parts.append(text)
+
+ if not images:
+ self._logger.error("Gemini response contained no images: %s", data)
+ detail = ""
+ if finish_messages:
+ combined = " ".join(message.strip() for message in finish_messages if message.strip())
+ if combined:
+ detail = f" Response status: {combined[:500]}"
+ elif text_parts:
+ combined = " ".join(text_parts).strip()
+ if combined:
+ detail = f" Response text: {combined[:500]}"
+ raise ExternalProviderRequestError(f"Gemini response contained no images.{detail}")
+
+ return ExternalGenerationResult(
+ images=images,
+ seed_used=request.seed,
+ provider_metadata={"model": request.model.provider_model_id},
+ )
+
+ def _dump_debug_payload(self, label: str, payload: object) -> None:
+ """TODO: remove debug payload dump once Gemini is stable."""
+ try:
+ outputs_path = self._app_config.outputs_path
+ if outputs_path is None:
+ return
+ debug_dir = outputs_path / "external_debug" / "gemini"
+ debug_dir.mkdir(parents=True, exist_ok=True)
+ path = debug_dir / f"{label}_{uuid.uuid4().hex}.json"
+ path.write_text(json.dumps(payload, indent=2, default=str), encoding="utf-8")
+ except Exception as exc:
+ self._logger.debug("Failed to write Gemini debug payload: %s", exc)
+
+ def _dump_debug_image(self, image: "PILImageType") -> None:
+ """TODO: remove debug image dump once Gemini is stable."""
+ try:
+ outputs_path = self._app_config.outputs_path
+ if outputs_path is None:
+ return
+ debug_dir = outputs_path / "external_debug" / "gemini"
+ debug_dir.mkdir(parents=True, exist_ok=True)
+ path = debug_dir / f"decoded_{uuid.uuid4().hex}.png"
+ image.save(path, format="PNG")
+ except Exception as exc:
+ self._logger.debug("Failed to write Gemini debug image: %s", exc)
+
+
+def _iter_response_parts(candidate: dict[str, object]) -> list[dict[str, object]]:
+ content = candidate.get("content")
+ if isinstance(content, dict):
+ content_parts = content.get("parts")
+ if isinstance(content_parts, list):
+ return [part for part in content_parts if isinstance(part, dict)]
+ contents = candidate.get("contents")
+ if isinstance(contents, list):
+ parts: list[dict[str, object]] = []
+ for item in contents:
+ if not isinstance(item, dict):
+ continue
+ item_parts = item.get("parts")
+ if isinstance(item_parts, list):
+ parts.extend([part for part in item_parts if isinstance(part, dict)])
+ if parts:
+ return parts
+ return []
+
+
+def _select_aspect_ratio(width: int, height: int, allowed: list[str] | None) -> str | None:
+ if width <= 0 or height <= 0:
+ return None
+ ratio = width / height
+ default_ratio = _format_aspect_ratio(width, height)
+ if not allowed:
+ return default_ratio
+ parsed = [(value, _parse_ratio(value)) for value in allowed]
+ filtered = [(value, parsed_ratio) for value, parsed_ratio in parsed if parsed_ratio is not None]
+ if not filtered:
+ return default_ratio
+ return min(filtered, key=lambda item: abs(item[1] - ratio))[0]
+
+
+def _format_aspect_ratio(width: int, height: int) -> str | None:
+ if width <= 0 or height <= 0:
+ return None
+ divisor = _gcd(width, height)
+ return f"{width // divisor}:{height // divisor}"
+
+
+def _parse_ratio(value: str) -> float | None:
+ if ":" not in value:
+ return None
+ left, right = value.split(":", 1)
+ try:
+ numerator = float(left)
+ denominator = float(right)
+ except ValueError:
+ return None
+ if denominator == 0:
+ return None
+ return numerator / denominator
+
+
+def _gcd(a: int, b: int) -> int:
+ while b:
+ a, b = b, a % b
+ return a
diff --git a/invokeai/app/services/external_generation/providers/openai.py b/invokeai/app/services/external_generation/providers/openai.py
new file mode 100644
index 00000000000..e31a493b7a1
--- /dev/null
+++ b/invokeai/app/services/external_generation/providers/openai.py
@@ -0,0 +1,105 @@
+from __future__ import annotations
+
+import io
+
+import requests
+
+from invokeai.app.services.external_generation.errors import ExternalProviderRequestError
+from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGeneratedImage,
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+)
+from invokeai.app.services.external_generation.image_utils import decode_image_base64
+
+
+class OpenAIProvider(ExternalProvider):
+ provider_id = "openai"
+
+ def is_configured(self) -> bool:
+ return bool(self._app_config.external_openai_api_key)
+
+ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
+ api_key = self._app_config.external_openai_api_key
+ if not api_key:
+ raise ExternalProviderRequestError("OpenAI API key is not configured")
+
+ size = f"{request.width}x{request.height}"
+ base_url = (self._app_config.external_openai_base_url or "https://api.openai.com").rstrip("/")
+ headers = {"Authorization": f"Bearer {api_key}"}
+
+ if request.mode == "txt2img":
+ payload: dict[str, object] = {
+ "prompt": request.prompt,
+ "n": request.num_images,
+ "size": size,
+ "response_format": "b64_json",
+ }
+ if request.seed is not None:
+ payload["seed"] = request.seed
+ response = requests.post(
+ f"{base_url}/v1/images/generations",
+ headers=headers,
+ json=payload,
+ timeout=120,
+ )
+ else:
+ files: dict[str, tuple[str, io.BytesIO, str]] = {}
+ if request.init_image is None:
+ raise ExternalProviderRequestError("OpenAI img2img/inpaint requires an init image")
+
+ image_buffer = io.BytesIO()
+ request.init_image.save(image_buffer, format="PNG")
+ image_buffer.seek(0)
+ files["image"] = ("image.png", image_buffer, "image/png")
+
+ if request.mask_image is not None:
+ mask_buffer = io.BytesIO()
+ request.mask_image.save(mask_buffer, format="PNG")
+ mask_buffer.seek(0)
+ files["mask"] = ("mask.png", mask_buffer, "image/png")
+
+ data: dict[str, object] = {
+ "prompt": request.prompt,
+ "n": request.num_images,
+ "size": size,
+ "response_format": "b64_json",
+ }
+ response = requests.post(
+ f"{base_url}/v1/images/edits",
+ headers=headers,
+ data=data,
+ files=files,
+ timeout=120,
+ )
+
+ if not response.ok:
+ raise ExternalProviderRequestError(
+ f"OpenAI request failed with status {response.status_code}: {response.text}"
+ )
+
+ payload = response.json()
+ if not isinstance(payload, dict):
+ raise ExternalProviderRequestError("OpenAI response payload was not a JSON object")
+ images: list[ExternalGeneratedImage] = []
+ data_items = payload.get("data")
+ if not isinstance(data_items, list):
+ raise ExternalProviderRequestError("OpenAI response payload missing image data")
+ for item in data_items:
+ if not isinstance(item, dict):
+ continue
+ encoded = item.get("b64_json")
+ if not encoded:
+ continue
+ images.append(ExternalGeneratedImage(image=decode_image_base64(encoded), seed=request.seed))
+
+ if not images:
+ raise ExternalProviderRequestError("OpenAI response contained no images")
+
+ return ExternalGenerationResult(
+ images=images,
+ seed_used=request.seed,
+ provider_request_id=response.headers.get("x-request-id"),
+ provider_metadata={"model": request.model.provider_model_id},
+ )
diff --git a/invokeai/app/services/invocation_services.py b/invokeai/app/services/invocation_services.py
index 7a33f49940c..2c95f87b41d 100644
--- a/invokeai/app/services/invocation_services.py
+++ b/invokeai/app/services/invocation_services.py
@@ -21,6 +21,7 @@
from invokeai.app.services.config import InvokeAIAppConfig
from invokeai.app.services.download import DownloadQueueServiceBase
from invokeai.app.services.events.events_base import EventServiceBase
+ from invokeai.app.services.external_generation.external_generation_base import ExternalGenerationServiceBase
from invokeai.app.services.image_files.image_files_base import ImageFileStorageBase
from invokeai.app.services.image_records.image_records_base import ImageRecordStorageBase
from invokeai.app.services.images.images_base import ImageServiceABC
@@ -63,6 +64,7 @@ def __init__(
model_relationships: "ModelRelationshipsServiceABC",
model_relationship_records: "ModelRelationshipRecordStorageBase",
download_queue: "DownloadQueueServiceBase",
+ external_generation: "ExternalGenerationServiceBase",
performance_statistics: "InvocationStatsServiceBase",
session_queue: "SessionQueueBase",
session_processor: "SessionProcessorBase",
@@ -94,6 +96,7 @@ def __init__(
self.model_relationships = model_relationships
self.model_relationship_records = model_relationship_records
self.download_queue = download_queue
+ self.external_generation = external_generation
self.performance_statistics = performance_statistics
self.session_queue = session_queue
self.session_processor = session_processor
diff --git a/invokeai/app/services/model_install/model_install_common.py b/invokeai/app/services/model_install/model_install_common.py
index 1006135a95e..11b7cd1f9dc 100644
--- a/invokeai/app/services/model_install/model_install_common.py
+++ b/invokeai/app/services/model_install/model_install_common.py
@@ -139,12 +139,27 @@ def __str__(self) -> str:
return str(self.url)
-ModelSource = Annotated[Union[LocalModelSource, HFModelSource, URLModelSource], Field(discriminator="type")]
+class ExternalModelSource(StringLikeSource):
+ """An external provider model identifier."""
+
+ provider_id: str
+ provider_model_id: str
+ type: Literal["external"] = "external"
+
+ def __str__(self) -> str:
+ return f"external://{self.provider_id}/{self.provider_model_id}"
+
+
+ModelSource = Annotated[
+ Union[LocalModelSource, HFModelSource, URLModelSource, ExternalModelSource],
+ Field(discriminator="type"),
+]
MODEL_SOURCE_TO_TYPE_MAP = {
URLModelSource: ModelSourceType.Url,
HFModelSource: ModelSourceType.HFRepoID,
LocalModelSource: ModelSourceType.Path,
+ ExternalModelSource: ModelSourceType.Url,
}
diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py
index c47267eab5f..2176ba926a9 100644
--- a/invokeai/app/services/model_install/model_install_default.py
+++ b/invokeai/app/services/model_install/model_install_default.py
@@ -28,6 +28,7 @@
from invokeai.app.services.model_install.model_install_base import ModelInstallServiceBase
from invokeai.app.services.model_install.model_install_common import (
MODEL_SOURCE_TO_TYPE_MAP,
+ ExternalModelSource,
HFModelSource,
InstallStatus,
InvalidModelConfigException,
@@ -45,6 +46,11 @@
AnyModelConfig,
ModelConfigFactory,
)
+from invokeai.backend.model_manager.configs.external_api import (
+ ExternalApiModelConfig,
+ ExternalApiModelDefaultSettings,
+ ExternalModelCapabilities,
+)
from invokeai.backend.model_manager.configs.unknown import Unknown_Config
from invokeai.backend.model_manager.metadata import (
AnyModelRepoMetadata,
@@ -55,7 +61,7 @@
)
from invokeai.backend.model_manager.metadata.metadata_base import HuggingFaceMetadata
from invokeai.backend.model_manager.search import ModelSearch
-from invokeai.backend.model_manager.taxonomy import ModelRepoVariant, ModelSourceType
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelRepoVariant, ModelSourceType
from invokeai.backend.model_manager.util.lora_metadata_extractor import apply_lora_metadata
from invokeai.backend.util import InvokeAILogger
from invokeai.backend.util.catch_sigint import catch_sigint
@@ -451,6 +457,9 @@ def import_model(self, source: ModelSource, config: Optional[ModelRecordChanges]
install_job = self._import_from_hf(source, config)
elif isinstance(source, URLModelSource):
install_job = self._import_from_url(source, config)
+ elif isinstance(source, ExternalModelSource):
+ install_job = self._import_external_model(source, config)
+ self._put_in_queue(install_job)
else:
raise ValueError(f"Unsupported model source: '{type(source)}'")
@@ -748,7 +757,13 @@ def _guess_source(self, source: str) -> ModelSource:
source_obj: Optional[StringLikeSource] = None
source_stripped = source.strip('"')
- if Path(source_stripped).exists(): # A local file or directory
+ if source_stripped.startswith("external://"):
+ external_id = source_stripped.removeprefix("external://")
+ provider_id, _, provider_model_id = external_id.partition("/")
+ if not provider_id or not provider_model_id:
+ raise ValueError(f"Invalid external model source: '{source_stripped}'")
+ source_obj = ExternalModelSource(provider_id=provider_id, provider_model_id=provider_model_id)
+ elif Path(source_stripped).exists(): # A local file or directory
source_obj = LocalModelSource(path=Path(source_stripped))
elif match := re.match(hf_repoid_re, source):
source_obj = HFModelSource(
@@ -840,6 +855,9 @@ def _install_next_item(self) -> None:
self._logger.info(f"Installer thread {threading.get_ident()} exiting")
def _register_or_install(self, job: ModelInstallJob) -> None:
+ if isinstance(job.source, ExternalModelSource):
+ self._register_external_model(job)
+ return
# local jobs will be in waiting state, remote jobs will be downloading state
job.total_bytes = self._stat_size(job.local_path)
job.bytes = job.total_bytes
@@ -860,6 +878,41 @@ def _register_or_install(self, job: ModelInstallJob) -> None:
job.config_out = self.record_store.get_model(key)
self._signal_job_completed(job)
+ def _register_external_model(self, job: ModelInstallJob) -> None:
+ job.total_bytes = 0
+ job.bytes = 0
+ self._signal_job_running(job)
+ job.config_in.source = str(job.source)
+ job.config_in.source_type = MODEL_SOURCE_TO_TYPE_MAP[job.source.__class__]
+
+ provider_id = job.source.provider_id
+ provider_model_id = job.source.provider_model_id
+ capabilities = job.config_in.capabilities or ExternalModelCapabilities()
+ default_settings = (
+ job.config_in.default_settings
+ if isinstance(job.config_in.default_settings, ExternalApiModelDefaultSettings)
+ else None
+ )
+ name = job.config_in.name or f"{provider_id} {provider_model_id}"
+
+ config = ExternalApiModelConfig(
+ key=job.config_in.key or slugify(f"{provider_id}-{provider_model_id}"),
+ name=name,
+ description=job.config_in.description,
+ provider_id=provider_id,
+ provider_model_id=provider_model_id,
+ capabilities=capabilities,
+ default_settings=default_settings,
+ source=str(job.source),
+ source_type=MODEL_SOURCE_TO_TYPE_MAP[job.source.__class__],
+ path="",
+ hash="",
+ file_size=0,
+ )
+ self.record_store.add_model(config)
+ job.config_out = self.record_store.get_model(config.key)
+ self._signal_job_completed(job)
+
def _set_error(self, install_job: ModelInstallJob, excp: Exception) -> None:
multifile_download_job = install_job._multifile_job
if multifile_download_job and any(
@@ -895,6 +948,8 @@ def _scan_for_missing_models(self) -> list[AnyModelConfig]:
"""Scan the models directory for missing models and return a list of them."""
missing_models: list[AnyModelConfig] = []
for model_config in self.record_store.all_models():
+ if model_config.base == BaseModelType.External or model_config.format == ModelFormat.ExternalApi:
+ continue
if not (self.app_config.models_path / model_config.path).resolve().exists():
missing_models.append(model_config)
return missing_models
@@ -1036,6 +1091,19 @@ def _import_from_url(
remote_files=remote_files,
)
+ def _import_external_model(
+ self,
+ source: ExternalModelSource,
+ config: Optional[ModelRecordChanges] = None,
+ ) -> ModelInstallJob:
+ return ModelInstallJob(
+ id=self._next_id(),
+ source=source,
+ config_in=config or ModelRecordChanges(),
+ local_path=self._app_config.models_path,
+ inplace=True,
+ )
+
def _import_remote_model(
self,
source: HFModelSource | URLModelSource,
diff --git a/invokeai/app/services/model_records/model_records_base.py b/invokeai/app/services/model_records/model_records_base.py
index 96e12d3b0a3..318ebb000e6 100644
--- a/invokeai/app/services/model_records/model_records_base.py
+++ b/invokeai/app/services/model_records/model_records_base.py
@@ -13,6 +13,10 @@
from invokeai.app.services.shared.pagination import PaginatedResults
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
from invokeai.backend.model_manager.configs.controlnet import ControlAdapterDefaultSettings
+from invokeai.backend.model_manager.configs.external_api import (
+ ExternalApiModelDefaultSettings,
+ ExternalModelCapabilities,
+)
from invokeai.backend.model_manager.configs.factory import AnyModelConfig
from invokeai.backend.model_manager.configs.lora import LoraModelDefaultSettings
from invokeai.backend.model_manager.configs.main import MainModelDefaultSettings
@@ -86,8 +90,19 @@ class ModelRecordChanges(BaseModelExcludeNull):
file_size: Optional[int] = Field(description="Size of model file", default=None)
format: Optional[str] = Field(description="format of model file", default=None)
trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None)
- default_settings: Optional[MainModelDefaultSettings | LoraModelDefaultSettings | ControlAdapterDefaultSettings] = (
- Field(description="Default settings for this model", default=None)
+ default_settings: Optional[
+ MainModelDefaultSettings
+ | LoraModelDefaultSettings
+ | ControlAdapterDefaultSettings
+ | ExternalApiModelDefaultSettings
+ ] = Field(description="Default settings for this model", default=None)
+
+ # External API model changes
+ provider_id: Optional[str] = Field(description="External provider identifier", default=None)
+ provider_model_id: Optional[str] = Field(description="External provider model identifier", default=None)
+ capabilities: Optional[ExternalModelCapabilities] = Field(
+ description="External model capabilities",
+ default=None,
)
cpu_only: Optional[bool] = Field(description="Whether this model should run on CPU only", default=None)
diff --git a/invokeai/app/services/shared/invocation_context.py b/invokeai/app/services/shared/invocation_context.py
index 67e3c99f1ad..e38766d5ba2 100644
--- a/invokeai/app/services/shared/invocation_context.py
+++ b/invokeai/app/services/shared/invocation_context.py
@@ -388,6 +388,8 @@ def load(
submodel_type = submodel_type or identifier.submodel_type
model = self._services.model_manager.store.get_model(identifier.key)
+ self._raise_if_external(model)
+
message = f"Loading model {model.name}"
if submodel_type:
message += f" ({submodel_type.value})"
@@ -417,12 +419,18 @@ def load_by_attrs(
if len(configs) > 1:
raise ValueError(f"More than one model found with name {name}, base {base}, and type {type}")
+ self._raise_if_external(configs[0])
message = f"Loading model {name}"
if submodel_type:
message += f" ({submodel_type.value})"
self._util.signal_progress(message)
return self._services.model_manager.load.load_model(configs[0], submodel_type)
+ @staticmethod
+ def _raise_if_external(model: AnyModelConfig) -> None:
+ if model.base == BaseModelType.External or model.format == ModelFormat.ExternalApi:
+ raise ValueError("External API models cannot be loaded from disk")
+
def get_config(self, identifier: Union[str, "ModelIdentifierField"]) -> AnyModelConfig:
"""Get a model's config.
diff --git a/invokeai/backend/model_manager/configs/external_api.py b/invokeai/backend/model_manager/configs/external_api.py
index e69de29bb2d..f57b4404e00 100644
--- a/invokeai/backend/model_manager/configs/external_api.py
+++ b/invokeai/backend/model_manager/configs/external_api.py
@@ -0,0 +1,80 @@
+from typing import Literal, Self
+
+from pydantic import BaseModel, ConfigDict, Field, model_validator
+
+from invokeai.backend.model_manager.configs.base import Config_Base
+from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
+from invokeai.backend.model_manager.model_on_disk import ModelOnDisk
+from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelSourceType, ModelType
+
+ExternalGenerationMode = Literal["txt2img", "img2img", "inpaint"]
+ExternalMaskFormat = Literal["alpha", "binary", "none"]
+
+
+class ExternalImageSize(BaseModel):
+ width: int = Field(gt=0)
+ height: int = Field(gt=0)
+
+ model_config = ConfigDict(extra="forbid")
+
+
+class ExternalModelCapabilities(BaseModel):
+ modes: list[ExternalGenerationMode] = Field(default_factory=lambda: ["txt2img"])
+ supports_reference_images: bool = Field(default=False)
+ supports_negative_prompt: bool = Field(default=True)
+ supports_seed: bool = Field(default=False)
+ supports_guidance: bool = Field(default=False)
+ max_images_per_request: int | None = Field(default=None, gt=0)
+ max_image_size: ExternalImageSize | None = Field(default=None)
+ allowed_aspect_ratios: list[str] | None = Field(default=None)
+ aspect_ratio_sizes: dict[str, ExternalImageSize] | None = Field(default=None)
+ max_reference_images: int | None = Field(default=None, gt=0)
+ mask_format: ExternalMaskFormat = Field(default="none")
+ input_image_required_for: list[ExternalGenerationMode] | None = Field(default=None)
+
+ model_config = ConfigDict(extra="forbid")
+
+
+class ExternalApiModelDefaultSettings(BaseModel):
+ width: int | None = Field(default=None, gt=0)
+ height: int | None = Field(default=None, gt=0)
+ steps: int | None = Field(default=None, gt=0)
+ guidance: float | None = Field(default=None, gt=0)
+ num_images: int | None = Field(default=None, gt=0)
+
+ model_config = ConfigDict(extra="forbid")
+
+
+class ExternalApiModelConfig(Config_Base):
+ base: Literal[BaseModelType.External] = Field(default=BaseModelType.External)
+ type: Literal[ModelType.ExternalImageGenerator] = Field(default=ModelType.ExternalImageGenerator)
+ format: Literal[ModelFormat.ExternalApi] = Field(default=ModelFormat.ExternalApi)
+
+ provider_id: str = Field(min_length=1, description="External provider ID")
+ provider_model_id: str = Field(min_length=1, description="Provider-specific model ID")
+ capabilities: ExternalModelCapabilities = Field(description="Provider capability matrix")
+ default_settings: ExternalApiModelDefaultSettings | None = Field(default=None)
+ tags: list[str] | None = Field(default=None)
+ is_default: bool = Field(default=False)
+
+ source_type: ModelSourceType = Field(default=ModelSourceType.Url)
+ path: str = Field(default="")
+ source: str = Field(default="")
+ hash: str = Field(default="")
+ file_size: int = Field(default=0, ge=0)
+
+ model_config = ConfigDict(extra="forbid")
+
+ @model_validator(mode="after")
+ def _populate_external_fields(self) -> "ExternalApiModelConfig":
+ if not self.path:
+ self.path = f"external://{self.provider_id}/{self.provider_model_id}"
+ if not self.source:
+ self.source = self.path
+ if not self.hash:
+ self.hash = f"external:{self.provider_id}:{self.provider_model_id}"
+ return self
+
+ @classmethod
+ def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, object]) -> Self:
+ raise NotAMatchError("external API models are not probed from disk")
diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py
index 7702d4a5d9b..81464a1a971 100644
--- a/invokeai/backend/model_manager/configs/factory.py
+++ b/invokeai/backend/model_manager/configs/factory.py
@@ -26,6 +26,7 @@
ControlNet_Diffusers_SD2_Config,
ControlNet_Diffusers_SDXL_Config,
)
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig
from invokeai.backend.model_manager.configs.flux_redux import FLUXRedux_Checkpoint_Config
from invokeai.backend.model_manager.configs.identification_utils import NotAMatchError
from invokeai.backend.model_manager.configs.ip_adapter import (
@@ -256,6 +257,7 @@
Annotated[SigLIP_Diffusers_Config, SigLIP_Diffusers_Config.get_tag()],
Annotated[FLUXRedux_Checkpoint_Config, FLUXRedux_Checkpoint_Config.get_tag()],
Annotated[LlavaOnevision_Diffusers_Config, LlavaOnevision_Diffusers_Config.get_tag()],
+ Annotated[ExternalApiModelConfig, ExternalApiModelConfig.get_tag()],
# Unknown model (fallback)
Annotated[Unknown_Config, Unknown_Config.get_tag()],
],
diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py
index ef7cd80cd29..59d7ceba205 100644
--- a/invokeai/backend/model_manager/starter_models.py
+++ b/invokeai/backend/model_manager/starter_models.py
@@ -2,6 +2,11 @@
from pydantic import BaseModel
+from invokeai.backend.model_manager.configs.external_api import (
+ ExternalApiModelDefaultSettings,
+ ExternalImageSize,
+ ExternalModelCapabilities,
+)
from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType
@@ -13,6 +18,8 @@ class StarterModelWithoutDependencies(BaseModel):
type: ModelType
format: Optional[ModelFormat] = None
is_installed: bool = False
+ capabilities: ExternalModelCapabilities | None = None
+ default_settings: ExternalApiModelDefaultSettings | None = None
# allows us to track what models a user has installed across name changes within starter models
# if you update a starter model name, please add the old one to this list for that starter model
previous_names: list[str] = []
@@ -862,6 +869,108 @@ class StarterModelBundle(BaseModel):
)
# endregion
+# region External API
+gemini_flash_image = StarterModel(
+ name="Gemini 2.5 Flash Image",
+ base=BaseModelType.External,
+ source="external://gemini/gemini-2.5-flash-image",
+ description="Google Gemini 2.5 Flash image generation model (external API).",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img", "img2img", "inpaint"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ supports_guidance=True,
+ supports_reference_images=True,
+ max_images_per_request=1,
+ allowed_aspect_ratios=[
+ "1:1",
+ "2:3",
+ "3:2",
+ "3:4",
+ "4:3",
+ "4:5",
+ "5:4",
+ "9:16",
+ "16:9",
+ "21:9",
+ ],
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=1024, height=1024),
+ "2:3": ExternalImageSize(width=832, height=1248),
+ "3:2": ExternalImageSize(width=1248, height=832),
+ "3:4": ExternalImageSize(width=864, height=1184),
+ "4:3": ExternalImageSize(width=1184, height=864),
+ "4:5": ExternalImageSize(width=896, height=1152),
+ "5:4": ExternalImageSize(width=1152, height=896),
+ "9:16": ExternalImageSize(width=768, height=1344),
+ "16:9": ExternalImageSize(width=1344, height=768),
+ "21:9": ExternalImageSize(width=1536, height=672),
+ },
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=1024, height=1024, num_images=1),
+)
+gemini_pro_image_preview = StarterModel(
+ name="Gemini 3 Pro Image Preview",
+ base=BaseModelType.External,
+ source="external://gemini/gemini-3-pro-image-preview",
+ description="Google Gemini 3 Pro image generation preview model (external API).",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img", "img2img", "inpaint"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ supports_guidance=True,
+ supports_reference_images=True,
+ max_images_per_request=1,
+ allowed_aspect_ratios=[
+ "1:1",
+ "2:3",
+ "3:2",
+ "3:4",
+ "4:3",
+ "4:5",
+ "5:4",
+ "9:16",
+ "16:9",
+ "21:9",
+ ],
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=1024, height=1024),
+ "2:3": ExternalImageSize(width=832, height=1248),
+ "3:2": ExternalImageSize(width=1248, height=832),
+ "3:4": ExternalImageSize(width=864, height=1184),
+ "4:3": ExternalImageSize(width=1184, height=864),
+ "4:5": ExternalImageSize(width=896, height=1152),
+ "5:4": ExternalImageSize(width=1152, height=896),
+ "9:16": ExternalImageSize(width=768, height=1344),
+ "16:9": ExternalImageSize(width=1344, height=768),
+ "21:9": ExternalImageSize(width=1536, height=672),
+ },
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=1024, height=1024, num_images=1),
+)
+openai_gpt_image_1 = StarterModel(
+ name="ChatGPT Image",
+ base=BaseModelType.External,
+ source="external://openai/gpt-image-1",
+ description="OpenAI GPT-Image-1 image generation model (external API).",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img", "img2img", "inpaint"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ supports_guidance=True,
+ supports_reference_images=False,
+ max_images_per_request=1,
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=1024, height=1024, num_images=1),
+)
+# endregion
+
# List of starter models, displayed on the frontend.
# The order/sort of this list is not changed by the frontend - set it how you want it here.
STARTER_MODELS: list[StarterModel] = [
@@ -957,6 +1066,9 @@ class StarterModelBundle(BaseModel):
z_image_qwen3_encoder_quantized,
z_image_controlnet_union,
z_image_controlnet_tile,
+ gemini_flash_image,
+ gemini_pro_image_preview,
+ openai_gpt_image_1,
]
sd1_bundle: list[StarterModel] = [
diff --git a/invokeai/backend/model_manager/taxonomy.py b/invokeai/backend/model_manager/taxonomy.py
index c002418a6bd..855a39441d9 100644
--- a/invokeai/backend/model_manager/taxonomy.py
+++ b/invokeai/backend/model_manager/taxonomy.py
@@ -52,6 +52,8 @@ class BaseModelType(str, Enum):
"""Indicates the model is associated with CogView 4 model architecture."""
ZImage = "z-image"
"""Indicates the model is associated with Z-Image model architecture, including Z-Image-Turbo."""
+ External = "external"
+ """Indicates the model is hosted by an external provider."""
Unknown = "unknown"
"""Indicates the model's base architecture is unknown."""
@@ -76,6 +78,7 @@ class ModelType(str, Enum):
SigLIP = "siglip"
FluxRedux = "flux_redux"
LlavaOnevision = "llava_onevision"
+ ExternalImageGenerator = "external_image_generator"
Unknown = "unknown"
@@ -170,6 +173,7 @@ class ModelFormat(str, Enum):
BnbQuantizedLlmInt8b = "bnb_quantized_int8b"
BnbQuantizednf4b = "bnb_quantized_nf4b"
GGUFQuantized = "gguf_quantized"
+ ExternalApi = "external_api"
Unknown = "unknown"
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index c28df6ee383..9c726b6c938 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -993,6 +993,22 @@
"fileSize": "File Size",
"filterModels": "Filter models",
"fluxRedux": "FLUX Redux",
+ "externalImageGenerator": "External Image Generator",
+ "externalProviders": "External Providers",
+ "externalSetupTitle": "External Providers Setup",
+ "externalSetupDescription": "Connect an API key to enable external image generation and optionally install curated external models.",
+ "externalInstallDefaults": "Auto-install starter models",
+ "externalProvidersUnavailable": "External providers are not available in this build.",
+ "externalSetupFooter": "External providers use remote APIs; usage may incur provider-side costs.",
+ "externalProviderCardDescription": "Configure {{providerId}} credentials for external image generation.",
+ "externalApiKey": "API Key",
+ "externalApiKeyPlaceholder": "Paste your API key",
+ "externalApiKeyPlaceholderSet": "API key configured",
+ "externalApiKeyHelper": "Stored in your InvokeAI config file.",
+ "externalBaseUrl": "Base URL (optional)",
+ "externalBaseUrlPlaceholder": "https://...",
+ "externalBaseUrlHelper": "Override the default API base URL if needed.",
+ "externalResetHelper": "Clear API key and base URL.",
"height": "Height",
"huggingFace": "HuggingFace",
"huggingFacePlaceholder": "owner/model-name",
@@ -1060,6 +1076,21 @@
"modelUpdated": "Model Updated",
"modelUpdateFailed": "Model Update Failed",
"name": "Name",
+ "externalProvider": "External Provider",
+ "externalCapabilities": "External Capabilities",
+ "externalDefaults": "External Defaults",
+ "providerId": "Provider ID",
+ "providerModelId": "Provider Model ID",
+ "supportedModes": "Supported Modes",
+ "supportsNegativePrompt": "Supports Negative Prompt",
+ "supportsReferenceImages": "Supports Reference Images",
+ "supportsSeed": "Supports Seed",
+ "supportsGuidance": "Supports Guidance",
+ "maxImagesPerRequest": "Max Images Per Request",
+ "maxReferenceImages": "Max Reference Images",
+ "maxImageWidth": "Max Image Width",
+ "maxImageHeight": "Max Image Height",
+ "numImages": "Num Images",
"modelPickerFallbackNoModelsInstalled": "No models installed.",
"modelPickerFallbackNoModelsInstalled2": "Visit the Model Manager to install models.",
"noModelsInstalledDesc1": "Install models with the",
@@ -1102,6 +1133,7 @@
"urlDescription": "Install models from a URL or local file path. Perfect for specific models you want to add.",
"huggingFaceDescription": "Browse and install models directly from HuggingFace repositories.",
"scanFolderDescription": "Scan a local folder to automatically detect and install models.",
+ "externalDescription": "Connect to Gemini or OpenAI to generate images with external APIs.",
"recommendedModels": "Recommended Models",
"exploreStarter": "Or browse all available starter models",
"quickStart": "Quick Start Bundles",
@@ -1575,7 +1607,11 @@
"intermediatesCleared_one": "Cleared {{count}} Intermediate",
"intermediatesCleared_other": "Cleared {{count}} Intermediates",
"intermediatesClearedFailed": "Problem Clearing Intermediates",
- "reloadingIn": "Reloading in"
+ "reloadingIn": "Reloading in",
+ "externalProviders": "External Providers",
+ "externalProviderConfigured": "Configured",
+ "externalProviderNotConfigured": "API Key Required",
+ "externalProviderNotConfiguredHint": "Add your API key in Model Manager or the server config to enable this provider."
},
"toast": {
"addedToBoard": "Added to board {{name}}'s assets",
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/context.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/context.tsx
index 6b8da8dc4da..c8333600c56 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/context.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/context.tsx
@@ -72,10 +72,17 @@ export const StagingAreaContextProvider = memo(({ children, sessionId }: PropsWi
onAccept: (item, imageDTO) => {
const bboxRect = selectBboxRect(store.getState());
const { x, y } = bboxRect;
- const imageObject = imageDTOToImageObject(imageDTO);
+ const imageObject = imageDTOToImageObject(imageDTO, { usePixelBbox: false });
+ const scale = Math.min(bboxRect.width / imageDTO.width, bboxRect.height / imageDTO.height);
+ const scaledWidth = Math.round(imageDTO.width * scale);
+ const scaledHeight = Math.round(imageDTO.height * scale);
+ const position = {
+ x: x + Math.round((bboxRect.width - scaledWidth) / 2),
+ y: y + Math.round((bboxRect.height - scaledHeight) / 2),
+ };
const selectedEntityIdentifier = selectSelectedEntityIdentifier(store.getState());
const overrides: Partial = {
- position: { x, y },
+ position,
objects: [imageObject],
};
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts
index f16b9023164..9268fc7570f 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts
@@ -181,6 +181,33 @@ describe('StagingAreaApi Utility Functions', () => {
expect(result).toBe('first-image.png');
});
+ it('should return first image from image collections', () => {
+ const queueItem: S['SessionQueueItem'] = {
+ item_id: 1,
+ status: 'completed',
+ priority: 0,
+ destination: 'test-session',
+ created_at: '2024-01-01T00:00:00Z',
+ updated_at: '2024-01-01T00:00:00Z',
+ started_at: '2024-01-01T00:00:01Z',
+ completed_at: '2024-01-01T00:01:00Z',
+ error: null,
+ session: {
+ id: 'test-session',
+ source_prepared_mapping: {
+ canvas_output: ['output-node-id'],
+ },
+ results: {
+ 'output-node-id': {
+ images: [{ image_name: 'first.png' }, { image_name: 'second.png' }],
+ },
+ },
+ },
+ } as unknown as S['SessionQueueItem'];
+
+ expect(getOutputImageName(queueItem)).toBe('first.png');
+ });
+
it('should handle empty session mapping', () => {
const queueItem: S['SessionQueueItem'] = {
item_id: 1,
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts
index fe98408df58..1fe461e9993 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts
@@ -1,4 +1,4 @@
-import { isImageField } from 'features/nodes/types/common';
+import { isImageField, isImageFieldCollection } from 'features/nodes/types/common';
import { isCanvasOutputNodeId } from 'features/nodes/util/graph/graphBuilderUtils';
import type { S } from 'services/api/types';
import { formatProgressMessage } from 'services/events/stores';
@@ -29,6 +29,9 @@ export const getOutputImageName = (item: S['SessionQueueItem']) => {
if (isImageField(value)) {
return value.image_name;
}
+ if (isImageFieldCollection(value)) {
+ return value[0]?.image_name ?? null;
+ }
}
return null;
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts
new file mode 100644
index 00000000000..03de58908f0
--- /dev/null
+++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.test.ts
@@ -0,0 +1,61 @@
+import { zModelIdentifierField } from 'features/nodes/types/common';
+import type {
+ ExternalApiModelConfig,
+ ExternalApiModelDefaultSettings,
+ ExternalImageSize,
+ ExternalModelCapabilities,
+} from 'services/api/types';
+import { describe, expect, it } from 'vitest';
+
+import { selectModelSupportsNegativePrompt, selectModelSupportsRefImages } from './paramsSlice';
+
+const createExternalConfig = (capabilities: ExternalModelCapabilities): ExternalApiModelConfig => {
+ const maxImageSize: ExternalImageSize = { width: 1024, height: 1024 };
+ const defaultSettings: ExternalApiModelDefaultSettings = { width: 1024, height: 1024, steps: 30 };
+
+ return {
+ key: 'external-test',
+ hash: 'external:openai:gpt-image-1',
+ path: 'external://openai/gpt-image-1',
+ file_size: 0,
+ name: 'External Test',
+ description: null,
+ source: 'external://openai/gpt-image-1',
+ source_type: 'url',
+ source_api_response: null,
+ cover_image: null,
+ base: 'external',
+ type: 'external_image_generator',
+ format: 'external_api',
+ provider_id: 'openai',
+ provider_model_id: 'gpt-image-1',
+ capabilities: { ...capabilities, max_image_size: maxImageSize },
+ default_settings: defaultSettings,
+ tags: ['external'],
+ is_default: false,
+ };
+};
+
+describe('paramsSlice selectors for external models', () => {
+ it('uses external capabilities for negative prompt support', () => {
+ const config = createExternalConfig({
+ modes: ['txt2img'],
+ supports_negative_prompt: true,
+ supports_reference_images: false,
+ });
+ const model = zModelIdentifierField.parse(config);
+
+ expect(selectModelSupportsNegativePrompt.resultFunc(model, config)).toBe(true);
+ });
+
+ it('uses external capabilities for ref image support', () => {
+ const config = createExternalConfig({
+ modes: ['txt2img'],
+ supports_negative_prompt: false,
+ supports_reference_images: false,
+ });
+ const model = zModelIdentifierField.parse(config);
+
+ expect(selectModelSupportsRefImages.resultFunc(model, config)).toBe(false);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
index 8dcd93cc5de..41afb2ac19c 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
@@ -43,7 +43,7 @@ import type {
} from 'features/parameters/types/parameterSchemas';
import { getGridSize, getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models';
-import { isNonRefinerMainModelConfig } from 'services/api/types';
+import { isExternalApiModelConfig, isNonRefinerMainModelConfig } from 'services/api/types';
import { assert } from 'tsafe';
const slice = createSlice({
@@ -638,15 +638,42 @@ export const selectOptimizedDenoisingEnabled = createParamsSelector((params) =>
export const selectPositivePrompt = createParamsSelector((params) => params.positivePrompt);
export const selectNegativePrompt = createParamsSelector((params) => params.negativePrompt);
export const selectNegativePromptWithFallback = createParamsSelector((params) => params.negativePrompt ?? '');
+export const selectModelConfig = createSelector(
+ selectModelConfigsQuery,
+ selectParamsSlice,
+ (modelConfigs, { model }) => {
+ if (!modelConfigs.data) {
+ return null;
+ }
+ if (!model) {
+ return null;
+ }
+ return modelConfigsAdapterSelectors.selectById(modelConfigs.data, model.key) ?? null;
+ }
+);
export const selectHasNegativePrompt = createParamsSelector((params) => params.negativePrompt !== null);
export const selectModelSupportsNegativePrompt = createSelector(
selectModel,
- (model) => !!model && SUPPORTS_NEGATIVE_PROMPT_BASE_MODELS.includes(model.base)
-);
-export const selectModelSupportsRefImages = createSelector(
- selectModel,
- (model) => !!model && SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base)
+ selectModelConfig,
+ (model, modelConfig) => {
+ if (!model) {
+ return false;
+ }
+ if (modelConfig && isExternalApiModelConfig(modelConfig)) {
+ return modelConfig.capabilities.supports_negative_prompt ?? false;
+ }
+ return SUPPORTS_NEGATIVE_PROMPT_BASE_MODELS.includes(model.base);
+ }
);
+export const selectModelSupportsRefImages = createSelector(selectModel, selectModelConfig, (model, modelConfig) => {
+ if (!model) {
+ return false;
+ }
+ if (modelConfig && isExternalApiModelConfig(modelConfig)) {
+ return modelConfig.capabilities.supports_reference_images ?? false;
+ }
+ return SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base);
+});
export const selectModelSupportsOptimizedDenoising = createSelector(
selectModel,
(model) => !!model && SUPPORTS_OPTIMIZED_DENOISING_BASE_MODELS.includes(model.base)
@@ -693,24 +720,23 @@ export const selectHeight = createParamsSelector((params) => params.dimensions.h
export const selectAspectRatioID = createParamsSelector((params) => params.dimensions.aspectRatio.id);
export const selectAspectRatioValue = createParamsSelector((params) => params.dimensions.aspectRatio.value);
export const selectAspectRatioIsLocked = createParamsSelector((params) => params.dimensions.aspectRatio.isLocked);
+export const selectAllowedAspectRatioIDs = createSelector(selectModelConfig, (modelConfig) => {
+ if (!modelConfig || !isExternalApiModelConfig(modelConfig)) {
+ return null;
+ }
+ const allowed = modelConfig.capabilities.allowed_aspect_ratios;
+ return allowed?.length ? allowed : null;
+});
-export const selectMainModelConfig = createSelector(
- selectModelConfigsQuery,
- selectParamsSlice,
- (modelConfigs, { model }) => {
- if (!modelConfigs.data) {
- return null;
- }
- if (!model) {
- return null;
- }
- const modelConfig = modelConfigsAdapterSelectors.selectById(modelConfigs.data, model.key);
- if (!modelConfig) {
- return null;
- }
- if (!isNonRefinerMainModelConfig(modelConfig)) {
- return null;
- }
+export const selectMainModelConfig = createSelector(selectModelConfig, (modelConfig) => {
+ if (!modelConfig) {
+ return null;
+ }
+ if (isExternalApiModelConfig(modelConfig)) {
return modelConfig;
}
-);
+ if (!isNonRefinerMainModelConfig(modelConfig)) {
+ return null;
+ }
+ return modelConfig;
+});
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts b/invokeai/frontend/web/src/features/controlLayers/store/validators.ts
index 3406e9e7ee6..e431c10558f 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/validators.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/validators.ts
@@ -6,7 +6,7 @@ import type {
RefImageState,
} from 'features/controlLayers/store/types';
import type { ModelIdentifierField } from 'features/nodes/types/common';
-import type { AnyModelConfig, MainModelConfig } from 'services/api/types';
+import type { AnyModelConfig, MainOrExternalModelConfig } from 'services/api/types';
const WARNINGS = {
UNSUPPORTED_MODEL: 'controlLayers.warnings.unsupportedModel',
@@ -28,7 +28,7 @@ type WarningTKey = (typeof WARNINGS)[keyof typeof WARNINGS];
export const getRegionalGuidanceWarnings = (
entity: CanvasRegionalGuidanceState,
- model: MainModelConfig | null | undefined
+ model: MainOrExternalModelConfig | null | undefined
): WarningTKey[] => {
const warnings: WarningTKey[] = [];
@@ -112,7 +112,7 @@ export const areBasesCompatibleForRefImage = (
export const getGlobalReferenceImageWarnings = (
entity: RefImageState,
- model: MainModelConfig | null | undefined
+ model: MainOrExternalModelConfig | null | undefined
): WarningTKey[] => {
const warnings: WarningTKey[] = [];
@@ -147,7 +147,7 @@ export const getGlobalReferenceImageWarnings = (
export const getControlLayerWarnings = (
entity: CanvasControlLayerState,
- model: MainModelConfig | null | undefined
+ model: MainOrExternalModelConfig | null | undefined
): WarningTKey[] => {
const warnings: WarningTKey[] = [];
@@ -181,7 +181,7 @@ export const getControlLayerWarnings = (
export const getRasterLayerWarnings = (
_entity: CanvasRasterLayerState,
- _model: MainModelConfig | null | undefined
+ _model: MainOrExternalModelConfig | null | undefined
): WarningTKey[] => {
const warnings: WarningTKey[] = [];
@@ -192,7 +192,7 @@ export const getRasterLayerWarnings = (
export const getInpaintMaskWarnings = (
_entity: CanvasInpaintMaskState,
- _model: MainModelConfig | null | undefined
+ _model: MainOrExternalModelConfig | null | undefined
): WarningTKey[] => {
const warnings: WarningTKey[] = [];
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/models.ts b/invokeai/frontend/web/src/features/modelManagerV2/models.ts
index 7b5a08adfe2..99cd2e8a573 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/models.ts
+++ b/invokeai/frontend/web/src/features/modelManagerV2/models.ts
@@ -1,10 +1,11 @@
import type { AnyModelVariant, BaseModelType, ModelFormat, ModelType } from 'features/nodes/types/common';
-import type { AnyModelConfig } from 'services/api/types';
import {
+ type AnyModelConfig,
isCLIPEmbedModelConfig,
isCLIPVisionModelConfig,
isControlLoRAModelConfig,
isControlNetModelConfig,
+ isExternalApiModelConfig,
isFluxReduxModelConfig,
isIPAdapterModelConfig,
isLLaVAModelConfig,
@@ -121,6 +122,11 @@ export const MODEL_CATEGORIES: Record = {
i18nKey: 'modelManager.llavaOnevision',
filter: isLLaVAModelConfig,
},
+ external_image_generator: {
+ category: 'external_image_generator',
+ i18nKey: 'modelManager.externalImageGenerator',
+ filter: isExternalApiModelConfig,
+ },
};
export const MODEL_CATEGORIES_AS_LIST = objectEntries(MODEL_CATEGORIES).map(([category, { i18nKey, filter }]) => ({
@@ -143,6 +149,7 @@ export const MODEL_BASE_TO_COLOR: Record = {
flux2: 'gold',
cogview4: 'red',
'z-image': 'cyan',
+ external: 'orange',
unknown: 'red',
};
@@ -167,6 +174,7 @@ export const MODEL_TYPE_TO_LONG_NAME: Record = {
clip_embed: 'CLIP Embed',
siglip: 'SigLIP',
flux_redux: 'FLUX Redux',
+ external_image_generator: 'External Image Generator',
unknown: 'Unknown',
};
@@ -184,6 +192,7 @@ export const MODEL_BASE_TO_LONG_NAME: Record = {
flux2: 'FLUX.2',
cogview4: 'CogView4',
'z-image': 'Z-Image',
+ external: 'External',
unknown: 'Unknown',
};
@@ -201,6 +210,7 @@ export const MODEL_BASE_TO_SHORT_NAME: Record = {
flux2: 'FLUX.2',
cogview4: 'CogView4',
'z-image': 'Z-Image',
+ external: 'External',
unknown: 'Unknown',
};
@@ -228,6 +238,7 @@ export const MODEL_FORMAT_TO_LONG_NAME: Record = {
checkpoint: 'Checkpoint',
lycoris: 'LyCORIS',
onnx: 'ONNX',
+ external_api: 'External API',
olive: 'Olive',
embedding_file: 'Embedding (file)',
embedding_folder: 'Embedding (folder)',
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/store/installModelsStore.ts b/invokeai/frontend/web/src/features/modelManagerV2/store/installModelsStore.ts
index b99a1212fec..79b7bfe31a7 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/store/installModelsStore.ts
+++ b/invokeai/frontend/web/src/features/modelManagerV2/store/installModelsStore.ts
@@ -1,13 +1,14 @@
import { atom } from 'nanostores';
-type InstallModelsTabName = 'launchpad' | 'urlOrLocal' | 'huggingface' | 'scanFolder' | 'starterModels';
+type InstallModelsTabName = 'launchpad' | 'urlOrLocal' | 'huggingface' | 'external' | 'scanFolder' | 'starterModels';
const TAB_TO_INDEX_MAP: Record = {
launchpad: 0,
urlOrLocal: 1,
huggingface: 2,
- scanFolder: 3,
- starterModels: 4,
+ external: 3,
+ scanFolder: 4,
+ starterModels: 5,
};
export const setInstallModelsTabByName = (tab: InstallModelsTabName) => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx
new file mode 100644
index 00000000000..26820cb0e29
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx
@@ -0,0 +1,281 @@
+import {
+ Badge,
+ Button,
+ Card,
+ Flex,
+ FormControl,
+ FormHelperText,
+ FormLabel,
+ Heading,
+ Input,
+ Switch,
+ Text,
+ Tooltip,
+} from '@invoke-ai/ui-library';
+import { useStore } from '@nanostores/react';
+import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
+import { useBuildModelInstallArg } from 'features/modelManagerV2/hooks/useBuildModelsToInstall';
+import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
+import { $installModelsTabIndex } from 'features/modelManagerV2/store/installModelsStore';
+import type { ChangeEvent } from 'react';
+import { memo, useCallback, useEffect, useMemo, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiCheckBold, PiWarningBold } from 'react-icons/pi';
+import {
+ useGetExternalProviderConfigsQuery,
+ useResetExternalProviderConfigMutation,
+ useSetExternalProviderConfigMutation,
+} from 'services/api/endpoints/appInfo';
+import { useGetStarterModelsQuery } from 'services/api/endpoints/models';
+import type { ExternalProviderConfig, StarterModel } from 'services/api/types';
+
+const PROVIDER_SORT_ORDER = ['gemini', 'openai'];
+
+type ProviderCardProps = {
+ provider: ExternalProviderConfig;
+ onInstallModels: (providerId: string) => void;
+};
+
+type UpdatePayload = {
+ provider_id: string;
+ api_key?: string;
+ base_url?: string;
+};
+
+export const ExternalProvidersForm = memo(() => {
+ const { t } = useTranslation();
+ const { data, isLoading } = useGetExternalProviderConfigsQuery();
+ const { data: starterModels } = useGetStarterModelsQuery();
+ const [installModel] = useInstallModel();
+ const { getIsInstalled, buildModelInstallArg } = useBuildModelInstallArg();
+ const [installDefaults, setInstallDefaults] = useState(true);
+ const tabIndex = useStore($installModelsTabIndex);
+
+ const toggleInstallDefaults = useCallback((event: ChangeEvent) => {
+ setInstallDefaults(event.target.checked);
+ }, []);
+
+ const externalModelsByProvider = useMemo(() => {
+ const groups = new Map();
+ for (const model of starterModels?.starter_models ?? []) {
+ if (!model.source.startsWith('external://')) {
+ continue;
+ }
+ const providerId = model.source.replace('external://', '').split('/')[0];
+ if (!providerId) {
+ continue;
+ }
+ const models = groups.get(providerId) ?? [];
+ models.push(model);
+ groups.set(providerId, models);
+ }
+
+ for (const [providerId, models] of groups.entries()) {
+ models.sort((a, b) => a.name.localeCompare(b.name));
+ groups.set(providerId, models);
+ }
+
+ return groups;
+ }, [starterModels]);
+
+ const handleInstallProviderModels = useCallback(
+ (providerId: string) => {
+ if (!installDefaults) {
+ return;
+ }
+ const models = externalModelsByProvider.get(providerId);
+ if (!models?.length) {
+ return;
+ }
+ const modelsToInstall = models.filter((model) => !getIsInstalled(model)).map(buildModelInstallArg);
+ modelsToInstall.forEach((model) => installModel(model));
+ },
+ [buildModelInstallArg, externalModelsByProvider, getIsInstalled, installDefaults, installModel]
+ );
+
+ const sortedProviders = useMemo(() => {
+ if (!data) {
+ return [];
+ }
+ return [...data].sort((a, b) => {
+ const aIndex = PROVIDER_SORT_ORDER.indexOf(a.provider_id);
+ const bIndex = PROVIDER_SORT_ORDER.indexOf(b.provider_id);
+ if (aIndex === -1 && bIndex === -1) {
+ return a.provider_id.localeCompare(b.provider_id);
+ }
+ if (aIndex === -1) {
+ return 1;
+ }
+ if (bIndex === -1) {
+ return -1;
+ }
+ return aIndex - bIndex;
+ });
+ }, [data]);
+
+ return (
+
+
+
+ {t('modelManager.externalSetupTitle')}
+ {t('modelManager.externalSetupDescription')}
+
+
+ {t('modelManager.externalInstallDefaults')}
+
+
+
+
+
+ {isLoading && {t('common.loading')}}
+ {!isLoading && sortedProviders.length === 0 && (
+ {t('modelManager.externalProvidersUnavailable')}
+ )}
+ {sortedProviders.map((provider) => (
+
+ ))}
+
+
+ {tabIndex === 3 && (
+
+ {t('modelManager.externalSetupFooter')}
+
+ )}
+
+ );
+});
+
+ExternalProvidersForm.displayName = 'ExternalProvidersForm';
+
+const ProviderCard = memo(({ provider, onInstallModels }: ProviderCardProps) => {
+ const { t } = useTranslation();
+ const [apiKey, setApiKey] = useState('');
+ const [baseUrl, setBaseUrl] = useState(provider.base_url ?? '');
+ const [saveConfig, { isLoading }] = useSetExternalProviderConfigMutation();
+ const [resetConfig, { isLoading: isResetting }] = useResetExternalProviderConfigMutation();
+
+ useEffect(() => {
+ setBaseUrl(provider.base_url ?? '');
+ }, [provider.base_url]);
+
+ const handleSave = useCallback(() => {
+ const trimmedApiKey = apiKey.trim();
+ const trimmedBaseUrl = baseUrl.trim();
+ const updatePayload: UpdatePayload = {
+ provider_id: provider.provider_id,
+ };
+ if (trimmedApiKey) {
+ updatePayload.api_key = trimmedApiKey;
+ }
+ if (trimmedBaseUrl !== (provider.base_url ?? '')) {
+ updatePayload.base_url = trimmedBaseUrl;
+ }
+
+ if (!updatePayload.api_key && updatePayload.base_url === undefined) {
+ return;
+ }
+
+ saveConfig(updatePayload)
+ .unwrap()
+ .then((result) => {
+ if (result.api_key_configured) {
+ setApiKey('');
+ onInstallModels(provider.provider_id);
+ }
+ if (result.base_url !== undefined) {
+ setBaseUrl(result.base_url ?? '');
+ }
+ });
+ }, [apiKey, baseUrl, onInstallModels, provider.base_url, provider.provider_id, saveConfig]);
+
+ const handleReset = useCallback(() => {
+ resetConfig(provider.provider_id)
+ .unwrap()
+ .then((result) => {
+ setApiKey('');
+ setBaseUrl(result.base_url ?? '');
+ });
+ }, [provider.provider_id, resetConfig]);
+
+ const handleApiKeyChange = useCallback((event: ChangeEvent) => {
+ setApiKey(event.target.value);
+ }, []);
+
+ const handleBaseUrlChange = useCallback((event: ChangeEvent) => {
+ setBaseUrl(event.target.value);
+ }, []);
+
+ const statusBadge = provider.api_key_configured ? (
+
+
+ {t('settings.externalProviderConfigured')}
+
+ ) : (
+
+
+ {t('settings.externalProviderNotConfigured')}
+
+ );
+
+ return (
+
+
+
+
+ {provider.provider_id}
+
+
+ {t('modelManager.externalProviderCardDescription', { providerId: provider.provider_id })}
+
+
+ {statusBadge}
+
+
+
+ {t('modelManager.externalApiKey')}
+
+ {t('modelManager.externalApiKeyHelper')}
+
+
+ {t('modelManager.externalBaseUrl')}
+
+ {t('modelManager.externalBaseUrlHelper')}
+
+
+
+
+
+
+
+
+
+ );
+});
+
+ProviderCard.displayName = 'ProviderCard';
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx
index fc99bcec7bf..591c61a4b23 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm.tsx
@@ -6,7 +6,7 @@ import { StarterBundleButton } from 'features/modelManagerV2/subpanels/AddModelP
import { StarterBundleTooltipContentCompact } from 'features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterBundleTooltipContentCompact';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiFolderOpenBold, PiLinkBold, PiStarBold } from 'react-icons/pi';
+import { PiFolderOpenBold, PiLinkBold, PiPlugBold, PiStarBold } from 'react-icons/pi';
import { SiHuggingface } from 'react-icons/si';
import { useGetStarterModelsQuery } from 'services/api/endpoints/models';
@@ -28,6 +28,10 @@ export const LaunchpadForm = memo(() => {
setInstallModelsTabByName('scanFolder');
}, []);
+ const navigateToExternalTab = useCallback(() => {
+ setInstallModelsTabByName('external');
+ }, []);
+
const navigateToStarterModelsTab = useCallback(() => {
setInstallModelsTabByName('starterModels');
}, []);
@@ -63,6 +67,12 @@ export const LaunchpadForm = memo(() => {
title={t('modelManager.scanFolder')}
description={t('modelManager.launchpad.scanFolderDescription')}
/>
+
{/* Recommended Section */}
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx
index 9039c0f85f4..5bc4c9713fc 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/InstallModels.tsx
@@ -2,18 +2,18 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library';
import { Box, Flex, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { $installModelsTabIndex } from 'features/modelManagerV2/store/installModelsStore';
+import { ExternalProvidersForm } from 'features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm';
+import { HuggingFaceForm } from 'features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm';
+import { InstallModelForm } from 'features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm';
+import { LaunchpadForm } from 'features/modelManagerV2/subpanels/AddModelPanel/LaunchpadForm/LaunchpadForm';
+import { ModelInstallQueue } from 'features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue';
+import { ScanModelsForm } from 'features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderForm';
import { StarterModelsForm } from 'features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsForm';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
-import { PiCubeBold, PiFolderOpenBold, PiLinkSimpleBold, PiShootingStarBold } from 'react-icons/pi';
+import { PiCubeBold, PiFolderOpenBold, PiLinkSimpleBold, PiPlugBold, PiShootingStarBold } from 'react-icons/pi';
import { SiHuggingface } from 'react-icons/si';
-import { HuggingFaceForm } from './AddModelPanel/HuggingFaceFolder/HuggingFaceForm';
-import { InstallModelForm } from './AddModelPanel/InstallModelForm';
-import { LaunchpadForm } from './AddModelPanel/LaunchpadForm/LaunchpadForm';
-import { ModelInstallQueue } from './AddModelPanel/ModelInstallQueue/ModelInstallQueue';
-import { ScanModelsForm } from './AddModelPanel/ScanFolder/ScanFolderForm';
-
const installModelsTabSx: SystemStyleObject = {
display: 'flex',
gap: 2,
@@ -61,6 +61,10 @@ export const InstallModels = memo(() => {
{t('modelManager.huggingFace')}
+
+
+ {t('modelManager.externalProviders')}
+
{t('modelManager.scanFolder')}
@@ -80,6 +84,9 @@ export const InstallModels = memo(() => {
+
+
+
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx
index 7d44ee54637..ff4dbe88fc8 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge.tsx
@@ -19,6 +19,7 @@ const FORMAT_NAME_MAP: Record = {
bnb_quantized_nf4b: 'quantized',
gguf_quantized: 'gguf',
omi: 'omi',
+ external_api: 'external_api',
unknown: 'unknown',
olive: 'olive',
onnx: 'onnx',
@@ -40,6 +41,7 @@ const FORMAT_COLOR_MAP: Record = {
unknown: 'red',
olive: 'base',
onnx: 'base',
+ external_api: 'base',
};
const ModelFormatBadge = ({ format }: Props) => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx
index 8235d26efef..e4c8752e569 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/BaseModelSelect.tsx
@@ -5,7 +5,7 @@ import { MODEL_BASE_TO_LONG_NAME } from 'features/modelManagerV2/models';
import { useCallback, useMemo } from 'react';
import type { Control } from 'react-hook-form';
import { useController } from 'react-hook-form';
-import type { UpdateModelArg } from 'services/api/endpoints/models';
+import type { UpdateModelBody } from 'services/api/types';
import { objectEntries } from 'tsafe';
const options: ComboboxOption[] = objectEntries(MODEL_BASE_TO_LONG_NAME).map(([value, label]) => ({
@@ -14,7 +14,7 @@ const options: ComboboxOption[] = objectEntries(MODEL_BASE_TO_LONG_NAME).map(([v
}));
type Props = {
- control: Control;
+ control: Control;
};
const BaseModelSelect = ({ control }: Props) => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx
index 1057ab7784c..2bd3eb954e5 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelFormatSelect.tsx
@@ -5,7 +5,7 @@ import { MODEL_FORMAT_TO_LONG_NAME } from 'features/modelManagerV2/models';
import { useCallback, useMemo } from 'react';
import type { Control } from 'react-hook-form';
import { useController } from 'react-hook-form';
-import type { UpdateModelArg } from 'services/api/endpoints/models';
+import type { UpdateModelBody } from 'services/api/types';
import { objectEntries } from 'tsafe';
const options: ComboboxOption[] = objectEntries(MODEL_FORMAT_TO_LONG_NAME).map(([value, label]) => ({
@@ -14,7 +14,7 @@ const options: ComboboxOption[] = objectEntries(MODEL_FORMAT_TO_LONG_NAME).map((
}));
type Props = {
- control: Control;
+ control: Control;
};
const ModelFormatSelect = ({ control }: Props) => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx
index 44b41f01518..b35ce7f96df 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelTypeSelect.tsx
@@ -5,7 +5,7 @@ import { MODEL_TYPE_TO_LONG_NAME } from 'features/modelManagerV2/models';
import { useCallback, useMemo } from 'react';
import type { Control } from 'react-hook-form';
import { useController } from 'react-hook-form';
-import type { UpdateModelArg } from 'services/api/endpoints/models';
+import type { UpdateModelBody } from 'services/api/types';
import { objectEntries } from 'tsafe';
const options: ComboboxOption[] = objectEntries(MODEL_TYPE_TO_LONG_NAME).map(([value, label]) => ({
@@ -14,7 +14,7 @@ const options: ComboboxOption[] = objectEntries(MODEL_TYPE_TO_LONG_NAME).map(([v
}));
type Props = {
- control: Control;
+ control: Control;
};
const ModelTypeSelect = ({ control }: Props) => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx
index 52eb2a4749d..d8e8c6a5b8a 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelVariantSelect.tsx
@@ -5,13 +5,13 @@ import { MODEL_VARIANT_TO_LONG_NAME } from 'features/modelManagerV2/models';
import { useCallback, useMemo } from 'react';
import type { Control } from 'react-hook-form';
import { useController } from 'react-hook-form';
-import type { UpdateModelArg } from 'services/api/endpoints/models';
+import type { UpdateModelBody } from 'services/api/types';
import { objectEntries } from 'tsafe';
const options: ComboboxOption[] = objectEntries(MODEL_VARIANT_TO_LONG_NAME).map(([value, label]) => ({ label, value }));
type Props = {
- control: Control;
+ control: Control;
};
const ModelVariantSelect = ({ control }: Props) => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx
index dcef95b4243..593bc4c4136 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/PredictionTypeSelect.tsx
@@ -4,7 +4,7 @@ import { typedMemo } from 'common/util/typedMemo';
import { useCallback, useMemo } from 'react';
import type { Control } from 'react-hook-form';
import { useController } from 'react-hook-form';
-import type { UpdateModelArg } from 'services/api/endpoints/models';
+import type { UpdateModelBody } from 'services/api/types';
const options: ComboboxOption[] = [
{ value: 'none', label: '-' },
@@ -14,7 +14,7 @@ const options: ComboboxOption[] = [
];
type Props = {
- control: Control;
+ control: Control;
};
const PredictionTypeSelect = ({ control }: Props) => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx
index d845eca3eec..7cde65bf072 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelEdit.tsx
@@ -15,12 +15,17 @@ import { useAppDispatch } from 'app/store/storeHooks';
import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { ModelHeader } from 'features/modelManagerV2/subpanels/ModelPanel/ModelHeader';
import { toast } from 'features/toast/toast';
-import { memo, useCallback } from 'react';
-import { type SubmitHandler, useForm } from 'react-hook-form';
+import { memo, useCallback, useMemo } from 'react';
+import { type SubmitHandler, useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { PiCheckBold, PiXBold } from 'react-icons/pi';
import { type UpdateModelArg, useUpdateModelMutation } from 'services/api/endpoints/models';
-import type { AnyModelConfig } from 'services/api/types';
+import {
+ type AnyModelConfig,
+ type ExternalModelCapabilities,
+ isExternalApiModelConfig,
+ type UpdateModelBody,
+} from 'services/api/types';
import BaseModelSelect from './Fields/BaseModelSelect';
import ModelFormatSelect from './Fields/ModelFormatSelect';
@@ -33,6 +38,8 @@ type Props = {
modelConfig: AnyModelConfig;
};
+type ModelEditFormValues = UpdateModelBody;
+
const stringFieldOptions = {
validate: (value?: string | null) => (value && value.trim().length > 3) || 'Must be at least 3 characters',
};
@@ -41,19 +48,54 @@ export const ModelEdit = memo(({ modelConfig }: Props) => {
const { t } = useTranslation();
const [updateModel, { isLoading: isSubmitting }] = useUpdateModelMutation();
const dispatch = useAppDispatch();
+ const isExternal = useMemo(() => isExternalApiModelConfig(modelConfig), [modelConfig]);
- const form = useForm({
+ const form = useForm({
defaultValues: modelConfig,
mode: 'onChange',
});
- const onSubmit = useCallback>(
+ const externalModes = useWatch({
+ control: form.control,
+ name: 'capabilities.modes',
+ }) as ExternalModelCapabilities['modes'] | undefined;
+
+ const modeSet = useMemo(() => new Set(externalModes ?? []), [externalModes]);
+
+ const toggleMode = useCallback(
+ (mode: ExternalModelCapabilities['modes'][number]) => {
+ const nextModes = modeSet.has(mode)
+ ? externalModes?.filter((value) => value !== mode)
+ : [...(externalModes ?? []), mode];
+ form.setValue('capabilities.modes', nextModes ?? [], { shouldDirty: true, shouldValidate: true });
+ },
+ [externalModes, form, modeSet]
+ );
+
+ const handleToggleTxt2Img = useCallback(() => toggleMode('txt2img'), [toggleMode]);
+ const handleToggleImg2Img = useCallback(() => toggleMode('img2img'), [toggleMode]);
+ const handleToggleInpaint = useCallback(() => toggleMode('inpaint'), [toggleMode]);
+
+ const parseOptionalNumber = useCallback((value: string | null | undefined) => {
+ if (value === null || value === undefined || value === '') {
+ return null;
+ }
+ if (typeof value !== 'string') {
+ return Number.isNaN(Number(value)) ? null : Number(value);
+ }
+ if (value.trim() === '') {
+ return null;
+ }
+ const parsed = Number(value);
+ return Number.isNaN(parsed) ? null : parsed;
+ }, []);
+
+ const onSubmit = useCallback>(
(values) => {
const responseBody: UpdateModelArg = {
key: modelConfig.key,
body: values,
};
-
updateModel(responseBody)
.unwrap()
.then((payload) => {
@@ -160,6 +202,144 @@ export const ModelEdit = memo(({ modelConfig }: Props) => {
+ {isExternal && (
+ <>
+
+ {t('modelManager.externalProvider')}
+
+
+
+ {t('modelManager.providerId')}
+
+
+
+ {t('modelManager.providerModelId')}
+
+
+
+
+ {t('modelManager.externalCapabilities')}
+
+
+
+ {t('modelManager.supportedModes')}
+
+
+ txt2img
+
+
+ img2img
+
+
+ inpaint
+
+
+
+
+ {t('modelManager.supportsNegativePrompt')}
+
+
+
+ {t('modelManager.supportsReferenceImages')}
+
+
+
+ {t('modelManager.supportsSeed')}
+
+
+
+ {t('modelManager.supportsGuidance')}
+
+
+
+ {t('modelManager.maxImagesPerRequest')}
+
+
+
+ {t('modelManager.maxReferenceImages')}
+
+
+
+ {t('modelManager.maxImageWidth')}
+
+
+
+ {t('modelManager.maxImageHeight')}
+
+
+
+
+ {t('modelManager.externalDefaults')}
+
+
+
+ {t('modelManager.width')}
+
+
+
+ {t('modelManager.height')}
+
+
+
+ {t('parameters.steps')}
+
+
+
+ {t('parameters.guidance')}
+
+
+
+ {t('modelManager.numImages')}
+
+
+
+ >
+ )}
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
index 6e114bb252d..c54523e0fad 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
@@ -10,14 +10,15 @@ import { TriggerPhrases } from 'features/modelManagerV2/subpanels/ModelPanel/Tri
import { filesize } from 'filesize';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import type {
- AnyModelConfig,
- CLIPEmbedModelConfig,
- CLIPVisionModelConfig,
- LlavaOnevisionModelConfig,
- Qwen3EncoderModelConfig,
- SigLIPModelConfig,
- T5EncoderModelConfig,
+import {
+ isExternalApiModelConfig,
+ type AnyModelConfig,
+ type CLIPEmbedModelConfig,
+ type CLIPVisionModelConfig,
+ type LlavaOnevisionModelConfig,
+ type Qwen3EncoderModelConfig,
+ type SigLIPModelConfig,
+ type T5EncoderModelConfig,
} from 'services/api/types';
import { isExternalModel } from './isExternalModel';
@@ -100,6 +101,12 @@ export const ModelView = memo(({ modelConfig }: Props) => {
+ {isExternalApiModelConfig(modelConfig) && (
+ <>
+
+
+ >
+ )}
{'variant' in modelConfig && modelConfig.variant && (
)}
diff --git a/invokeai/frontend/web/src/features/nodes/types/common.ts b/invokeai/frontend/web/src/features/nodes/types/common.ts
index 36805c022d8..e5a4e32701d 100644
--- a/invokeai/frontend/web/src/features/nodes/types/common.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/common.ts
@@ -94,6 +94,7 @@ export const zBaseModelType = z.enum([
'flux2',
'cogview4',
'z-image',
+ 'external',
'unknown',
]);
export type BaseModelType = z.infer;
@@ -118,6 +119,7 @@ export const zModelType = z.enum([
'clip_embed',
'siglip',
'flux_redux',
+ 'external_image_generator',
'unknown',
]);
export type ModelType = z.infer;
@@ -167,6 +169,7 @@ export const zModelFormat = z.enum([
'bnb_quantized_int8b',
'bnb_quantized_nf4b',
'gguf_quantized',
+ 'external_api',
'unknown',
]);
export type ModelFormat = z.infer;
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts
new file mode 100644
index 00000000000..fd787456381
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts
@@ -0,0 +1,154 @@
+import type { RootState } from 'app/store/store';
+import type { ParamsState, RefImagesState } from 'features/controlLayers/store/types';
+import { imageDTOToCroppableImage, initialIPAdapter } from 'features/controlLayers/store/util';
+import type {
+ ExternalApiModelConfig,
+ ExternalApiModelDefaultSettings,
+ ExternalImageSize,
+ ExternalModelCapabilities,
+ ImageDTO,
+ Invocation,
+} from 'services/api/types';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { buildExternalGraph } from './buildExternalGraph';
+
+const createExternalModel = (overrides: Partial = {}): ExternalApiModelConfig => {
+ const maxImageSize: ExternalImageSize = { width: 1024, height: 1024 };
+ const defaultSettings: ExternalApiModelDefaultSettings = { width: 1024, height: 1024, steps: 30 };
+ const capabilities: ExternalModelCapabilities = {
+ modes: ['txt2img'],
+ supports_negative_prompt: true,
+ supports_reference_images: true,
+ supports_seed: true,
+ supports_guidance: true,
+ max_image_size: maxImageSize,
+ };
+
+ return {
+ key: 'external-test',
+ hash: 'external:openai:gpt-image-1',
+ path: 'external://openai/gpt-image-1',
+ file_size: 0,
+ name: 'External Test',
+ description: null,
+ source: 'external://openai/gpt-image-1',
+ source_type: 'url',
+ source_api_response: null,
+ cover_image: null,
+ base: 'external',
+ type: 'external_image_generator',
+ format: 'external_api',
+ provider_id: 'openai',
+ provider_model_id: 'gpt-image-1',
+ capabilities,
+ default_settings: defaultSettings,
+ tags: ['external'],
+ is_default: false,
+ ...overrides,
+ };
+};
+
+let mockModelConfig: ExternalApiModelConfig | null = null;
+let mockParams: ParamsState;
+let mockRefImages: RefImagesState;
+let mockPrompts: { positive: string; negative: string };
+let mockSizes: { scaledSize: { width: number; height: number } };
+
+const mockOutputFields = {
+ id: 'external_output',
+ use_cache: false,
+ is_intermediate: false,
+ board: undefined,
+};
+
+vi.mock('features/controlLayers/store/paramsSlice', () => ({
+ selectModelConfig: () => mockModelConfig,
+ selectParamsSlice: () => mockParams,
+}));
+
+vi.mock('features/controlLayers/store/refImagesSlice', () => ({
+ selectRefImagesSlice: () => mockRefImages,
+}));
+
+vi.mock('features/nodes/util/graph/graphBuilderUtils', () => ({
+ getOriginalAndScaledSizesForTextToImage: () => mockSizes,
+ getOriginalAndScaledSizesForOtherModes: () => ({
+ scaledSize: { width: 512, height: 512 },
+ rect: { x: 0, y: 0, width: 512, height: 512 },
+ }),
+ selectCanvasOutputFields: () => mockOutputFields,
+ selectPresetModifiedPrompts: () => mockPrompts,
+}));
+
+beforeEach(() => {
+ mockParams = {
+ steps: 20,
+ guidance: 4.5,
+ } as ParamsState;
+ mockPrompts = { positive: 'a test prompt', negative: 'bad prompt' };
+ mockSizes = { scaledSize: { width: 768, height: 512 } };
+
+ const imageDTO = { image_name: 'ref.png', width: 64, height: 64 } as ImageDTO;
+ mockRefImages = {
+ selectedEntityId: null,
+ isPanelOpen: false,
+ entities: [
+ {
+ id: 'ref-image-1',
+ isEnabled: true,
+ config: {
+ ...initialIPAdapter,
+ weight: 0.5,
+ image: imageDTOToCroppableImage(imageDTO),
+ },
+ },
+ ],
+ };
+});
+
+describe('buildExternalGraph', () => {
+ it('builds txt2img graph with reference images and seed', async () => {
+ const modelConfig = createExternalModel();
+ mockModelConfig = modelConfig;
+
+ const { g } = await buildExternalGraph({
+ generationMode: 'txt2img',
+ state: {} as RootState,
+ manager: null,
+ });
+ const graph = g.getGraph();
+ const externalNode = Object.values(graph.nodes).find(
+ (node) => node.type === 'external_image_generation'
+ ) as Invocation<'external_image_generation'>;
+
+ expect(externalNode).toBeDefined();
+ expect(externalNode.mode).toBe('txt2img');
+ expect(externalNode.width).toBe(768);
+ expect(externalNode.height).toBe(512);
+ expect(externalNode.negative_prompt).toBe('bad prompt');
+ expect(externalNode.guidance).toBe(4.5);
+ expect(externalNode.reference_images?.[0]).toEqual({ image_name: 'ref.png' });
+ expect(externalNode.reference_image_weights).toEqual([0.5]);
+
+ const seedEdge = graph.edges.find((edge) => edge.destination.field === 'seed');
+ expect(seedEdge).toBeDefined();
+ });
+
+ it('throws when mode is unsupported', async () => {
+ const modelConfig = createExternalModel({
+ capabilities: {
+ modes: ['img2img'],
+ },
+ });
+ mockModelConfig = modelConfig;
+
+ await expect(
+ buildExternalGraph({
+ generationMode: 'txt2img',
+ state: {} as RootState,
+ manager: null,
+ })
+ ).rejects.toThrow('does not support txt2img');
+ });
+});
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
new file mode 100644
index 00000000000..02c030aa3b8
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
@@ -0,0 +1,129 @@
+import { getPrefixedId } from 'features/controlLayers/konva/util';
+import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
+import { selectModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
+import { selectRefImagesSlice } from 'features/controlLayers/store/refImagesSlice';
+import { zImageField } from 'features/nodes/types/common';
+import { Graph } from 'features/nodes/util/graph/generation/Graph';
+import {
+ getOriginalAndScaledSizesForOtherModes,
+ getOriginalAndScaledSizesForTextToImage,
+ selectCanvasOutputFields,
+ selectPresetModifiedPrompts,
+} from 'features/nodes/util/graph/graphBuilderUtils';
+import {
+ type GraphBuilderArg,
+ type GraphBuilderReturn,
+ UnsupportedGenerationModeError,
+} from 'features/nodes/util/graph/types';
+import { type Invocation, isExternalApiModelConfig } from 'services/api/types';
+import { assert } from 'tsafe';
+
+export const buildExternalGraph = async (arg: GraphBuilderArg): Promise => {
+ const { generationMode, state, manager } = arg;
+
+ const model = selectModelConfig(state);
+ assert(model, 'No model selected');
+ assert(isExternalApiModelConfig(model), 'Selected model is not an external API model');
+
+ const requestedMode = generationMode === 'outpaint' ? 'inpaint' : generationMode;
+ if (!model.capabilities.modes.includes(requestedMode)) {
+ throw new UnsupportedGenerationModeError(`${model.name} does not support ${requestedMode} mode`);
+ }
+
+ const params = selectParamsSlice(state);
+ const refImages = selectRefImagesSlice(state);
+ const prompts = selectPresetModifiedPrompts(state);
+
+ const g = new Graph(getPrefixedId('external_graph'));
+
+ const seed = model.capabilities.supports_seed
+ ? g.addNode({
+ id: getPrefixedId('seed'),
+ type: 'integer',
+ })
+ : null;
+
+ const positivePrompt = g.addNode({
+ id: getPrefixedId('positive_prompt'),
+ type: 'string',
+ });
+
+ const externalNode = g.addNode({
+ id: getPrefixedId('external_image_generation'),
+ type: 'external_image_generation',
+ model,
+ mode: requestedMode,
+ negative_prompt: model.capabilities.supports_negative_prompt ? prompts.negative : null,
+ steps: params.steps,
+ guidance: model.capabilities.supports_guidance ? params.guidance : null,
+ num_images: 1,
+ });
+
+ if (seed) {
+ g.addEdge(seed, 'value', externalNode, 'seed');
+ }
+ g.addEdge(positivePrompt, 'value', externalNode, 'prompt');
+
+ if (model.capabilities.supports_reference_images) {
+ const referenceImages = refImages.entities
+ .filter((entity) => entity.isEnabled)
+ .map((entity) => entity.config)
+ .filter((config) => config.image)
+ .map((config) => zImageField.parse(config.image?.crop?.image ?? config.image?.original.image));
+
+ const referenceWeights = refImages.entities
+ .filter((entity) => entity.isEnabled)
+ .map((entity) => entity.config)
+ .filter((config) => config.image)
+ .map((config) => (config.type === 'ip_adapter' ? config.weight : null));
+
+ if (referenceImages.length > 0) {
+ externalNode.reference_images = referenceImages;
+ if (referenceWeights.every((weight): weight is number => weight !== null)) {
+ externalNode.reference_image_weights = referenceWeights;
+ }
+ }
+ }
+
+ if (generationMode === 'txt2img') {
+ const { scaledSize } = getOriginalAndScaledSizesForTextToImage(state);
+ externalNode.width = scaledSize.width;
+ externalNode.height = scaledSize.height;
+ } else {
+ assert(manager, 'Canvas manager is required for img2img/inpaint');
+ const canvasSettings = selectCanvasSettingsSlice(state);
+ const { scaledSize, rect } = getOriginalAndScaledSizesForOtherModes(state);
+ externalNode.width = scaledSize.width;
+ externalNode.height = scaledSize.height;
+
+ const rasterAdapters = manager.compositor.getVisibleAdaptersOfType('raster_layer');
+ const initImage = await manager.compositor.getCompositeImageDTO(rasterAdapters, rect, {
+ is_intermediate: true,
+ silent: true,
+ });
+ externalNode.init_image = { image_name: initImage.image_name };
+
+ if (generationMode === 'inpaint' || generationMode === 'outpaint') {
+ const inpaintMaskAdapters = manager.compositor.getVisibleAdaptersOfType('inpaint_mask');
+ const maskImage = await manager.compositor.getGrayscaleMaskCompositeImageDTO(
+ inpaintMaskAdapters,
+ rect,
+ 'denoiseLimit',
+ canvasSettings.preserveMask,
+ {
+ is_intermediate: true,
+ silent: true,
+ }
+ );
+ externalNode.mask_image = { image_name: maskImage.image_name };
+ }
+ }
+
+ g.updateNode(externalNode, selectCanvasOutputFields(state));
+
+ return {
+ g,
+ seed: seed ?? undefined,
+ positivePrompt: positivePrompt as Invocation<'string'>,
+ };
+};
diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx
new file mode 100644
index 00000000000..1ae1dcdc3a8
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx
@@ -0,0 +1,44 @@
+import type { ExternalApiModelConfig } from 'services/api/types';
+import { describe, expect, test } from 'vitest';
+
+const createExternalModel = (overrides: Partial = {}): ExternalApiModelConfig => ({
+ key: 'external-test',
+ name: 'External Test',
+ base: 'external',
+ type: 'external_image_generator',
+ format: 'external_api',
+ provider_id: 'gemini',
+ provider_model_id: 'gemini-2.5-flash-image',
+ description: 'Test model',
+ source: 'external://gemini/gemini-2.5-flash-image',
+ source_type: 'url',
+ source_api_response: null,
+ path: '',
+ file_size: 0,
+ hash: 'external:gemini:gemini-2.5-flash-image',
+ cover_image: null,
+ is_default: false,
+ tags: ['external'],
+ capabilities: {
+ modes: ['txt2img'],
+ supports_reference_images: false,
+ supports_negative_prompt: true,
+ supports_seed: true,
+ supports_guidance: true,
+ max_images_per_request: 1,
+ max_image_size: null,
+ allowed_aspect_ratios: ['1:1', '16:9'],
+ max_reference_images: null,
+ mask_format: 'none',
+ input_image_required_for: null,
+ },
+ default_settings: null,
+ ...overrides,
+});
+
+describe('external model aspect ratios (bbox)', () => {
+ test('uses allowed aspect ratios for external models', () => {
+ const model = createExternalModel();
+ expect(model.capabilities.allowed_aspect_ratios).toEqual(['1:1', '16:9']);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx
index a237896c676..28dcb54cd7b 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.tsx
@@ -3,6 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import { bboxAspectRatioIdChanged } from 'features/controlLayers/store/canvasSlice';
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
+import { selectAllowedAspectRatioIDs } from 'features/controlLayers/store/paramsSlice';
import { selectAspectRatioID } from 'features/controlLayers/store/selectors';
import { isAspectRatioID, zAspectRatioID } from 'features/controlLayers/store/types';
import type { ChangeEventHandler } from 'react';
@@ -15,6 +16,8 @@ export const BboxAspectRatioSelect = memo(() => {
const dispatch = useAppDispatch();
const id = useAppSelector(selectAspectRatioID);
const isStaging = useCanvasIsStaging();
+ const allowedAspectRatios = useAppSelector(selectAllowedAspectRatioIDs);
+ const options = allowedAspectRatios ?? zAspectRatioID.options;
const onChange = useCallback>(
(e) => {
@@ -32,7 +35,7 @@ export const BboxAspectRatioSelect = memo(() => {
{t('parameters.aspect')}
}>
- {zAspectRatioID.options.map((ratio) => (
+ {options.map((ratio) => (
diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx b/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx
new file mode 100644
index 00000000000..636260d1d25
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx
@@ -0,0 +1,44 @@
+import type { ExternalApiModelConfig } from 'services/api/types';
+import { describe, expect, test } from 'vitest';
+
+const createExternalModel = (overrides: Partial = {}): ExternalApiModelConfig => ({
+ key: 'external-test',
+ name: 'External Test',
+ base: 'external',
+ type: 'external_image_generator',
+ format: 'external_api',
+ provider_id: 'gemini',
+ provider_model_id: 'gemini-2.5-flash-image',
+ description: 'Test model',
+ source: 'external://gemini/gemini-2.5-flash-image',
+ source_type: 'url',
+ source_api_response: null,
+ path: '',
+ file_size: 0,
+ hash: 'external:gemini:gemini-2.5-flash-image',
+ cover_image: null,
+ is_default: false,
+ tags: ['external'],
+ capabilities: {
+ modes: ['txt2img'],
+ supports_reference_images: false,
+ supports_negative_prompt: true,
+ supports_seed: true,
+ supports_guidance: true,
+ max_images_per_request: 1,
+ max_image_size: null,
+ allowed_aspect_ratios: ['1:1', '16:9'],
+ max_reference_images: null,
+ mask_format: 'none',
+ input_image_required_for: null,
+ },
+ default_settings: null,
+ ...overrides,
+});
+
+describe('external model aspect ratios', () => {
+ test('uses allowed aspect ratios for external models', () => {
+ const model = createExternalModel();
+ expect(model.capabilities.allowed_aspect_ratios).toEqual(['1:1', '16:9']);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx
index 4d3edc6e4bd..5e2952552c9 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.tsx
@@ -1,7 +1,11 @@
import { FormControl, FormLabel, Select } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
-import { aspectRatioIdChanged, selectAspectRatioID } from 'features/controlLayers/store/paramsSlice';
+import {
+ aspectRatioIdChanged,
+ selectAllowedAspectRatioIDs,
+ selectAspectRatioID,
+} from 'features/controlLayers/store/paramsSlice';
import { isAspectRatioID, zAspectRatioID } from 'features/controlLayers/store/types';
import type { ChangeEventHandler } from 'react';
import { memo, useCallback } from 'react';
@@ -12,6 +16,8 @@ export const DimensionsAspectRatioSelect = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const id = useAppSelector(selectAspectRatioID);
+ const allowedAspectRatios = useAppSelector(selectAllowedAspectRatioIDs);
+ const options = allowedAspectRatios ?? zAspectRatioID.options;
const onChange = useCallback>(
(e) => {
@@ -29,7 +35,7 @@ export const DimensionsAspectRatioSelect = memo(() => {
{t('parameters.aspect')}
}>
- {zAspectRatioID.options.map((ratio) => (
+ {options.map((ratio) => (
diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts b/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts
new file mode 100644
index 00000000000..b908efa096e
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts
@@ -0,0 +1,61 @@
+import type {
+ ExternalApiModelConfig,
+ ExternalApiModelDefaultSettings,
+ ExternalImageSize,
+ ExternalModelCapabilities,
+} from 'services/api/types';
+import { describe, expect, it } from 'vitest';
+
+import { isExternalModelUnsupportedForTab } from './mainModelPickerUtils';
+
+const createExternalConfig = (modes: ExternalModelCapabilities['modes']): ExternalApiModelConfig => {
+ const maxImageSize: ExternalImageSize = { width: 1024, height: 1024 };
+ const defaultSettings: ExternalApiModelDefaultSettings = { width: 1024, height: 1024, steps: 30 };
+
+ return {
+ key: 'external-test',
+ hash: 'external:openai:gpt-image-1',
+ path: 'external://openai/gpt-image-1',
+ file_size: 0,
+ name: 'External Test',
+ description: null,
+ source: 'external://openai/gpt-image-1',
+ source_type: 'url',
+ source_api_response: null,
+ cover_image: null,
+ base: 'external',
+ type: 'external_image_generator',
+ format: 'external_api',
+ provider_id: 'openai',
+ provider_model_id: 'gpt-image-1',
+ capabilities: {
+ modes,
+ supports_negative_prompt: true,
+ supports_reference_images: false,
+ max_image_size: maxImageSize,
+ },
+ default_settings: defaultSettings,
+ tags: ['external'],
+ is_default: false,
+ };
+};
+
+describe('isExternalModelUnsupportedForTab', () => {
+ it('disables external models without txt2img for generate', () => {
+ const model = createExternalConfig(['img2img', 'inpaint']);
+
+ expect(isExternalModelUnsupportedForTab(model, 'generate')).toBe(true);
+ });
+
+ it('allows external models with txt2img for generate', () => {
+ const model = createExternalConfig(['txt2img']);
+
+ expect(isExternalModelUnsupportedForTab(model, 'generate')).toBe(false);
+ });
+
+ it('allows external models on canvas', () => {
+ const model = createExternalConfig(['inpaint']);
+
+ expect(isExternalModelUnsupportedForTab(model, 'canvas')).toBe(false);
+ });
+});
diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.ts b/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.ts
new file mode 100644
index 00000000000..bc20c1a1184
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.ts
@@ -0,0 +1,14 @@
+import type { TabName } from 'features/ui/store/uiTypes';
+import { type AnyModelConfig, isExternalApiModelConfig } from 'services/api/types';
+
+export const isExternalModelUnsupportedForTab = (model: AnyModelConfig, tab: TabName): boolean => {
+ if (!isExternalApiModelConfig(model)) {
+ return false;
+ }
+
+ if (tab === 'generate') {
+ return !model.capabilities.modes.includes('txt2img');
+ }
+
+ return false;
+};
diff --git a/invokeai/frontend/web/src/features/parameters/components/ModelPicker.tsx b/invokeai/frontend/web/src/features/parameters/components/ModelPicker.tsx
index c5397791b84..b8631f5f742 100644
--- a/invokeai/frontend/web/src/features/parameters/components/ModelPicker.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/ModelPicker.tsx
@@ -1,5 +1,6 @@
import type { BoxProps, ButtonProps, SystemStyleObject } from '@invoke-ai/ui-library';
import {
+ Badge,
Button,
Flex,
Icon,
@@ -33,7 +34,7 @@ import { memo, useCallback, useMemo, useRef } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { PiCaretDownBold, PiLinkSimple } from 'react-icons/pi';
import { useGetRelatedModelIdsBatchQuery } from 'services/api/endpoints/modelRelationships';
-import type { AnyModelConfig } from 'services/api/types';
+import { type AnyModelConfig, type ExternalApiModelConfig, isExternalApiModelConfig } from 'services/api/types';
const selectSelectedModelKeys = createMemoizedSelector(selectParamsSlice, selectLoRAsSlice, (params, loras) => {
const keys: string[] = [];
@@ -94,9 +95,7 @@ const NoOptionsFallback = memo(({ noOptionsText }: { noOptionsText?: string }) =
});
NoOptionsFallback.displayName = 'NoOptionsFallback';
-const getGroupIDFromModelConfig = (modelConfig: AnyModelConfig): string => {
- return modelConfig.base;
-};
+const getGroupIDFromModelConfig = (modelConfig: AnyModelConfig): string => modelConfig.base;
const getGroupNameFromModelConfig = (modelConfig: AnyModelConfig): string => {
return MODEL_BASE_TO_LONG_NAME[modelConfig.base];
@@ -387,6 +386,10 @@ const optionNameSx: SystemStyleObject = {
const PickerOptionComponent = typedMemo(
({ option, ...rest }: { option: WithStarred } & BoxProps) => {
const { isCompactView } = usePickerContext>();
+ const externalOption = isExternalApiModelConfig(option as AnyModelConfig)
+ ? (option as ExternalApiModelConfig)
+ : null;
+ const providerLabel = externalOption ? externalOption.provider_id.toUpperCase() : null;
return (
@@ -397,6 +400,15 @@ const PickerOptionComponent = typedMemo(
{option.name}
+ {!isCompactView && externalOption && (
+
+ {providerLabel}
+
+ )}
{option.file_size > 0 && (
(model: WithStarred, searchTerm: string) => {
const regex = getRegex(searchTerm);
const bases = BASE_KEYWORDS[model.base] ?? [model.base];
+ const externalModel = isExternalApiModelConfig(model as AnyModelConfig) ? (model as ExternalApiModelConfig) : null;
+ const externalSearch = externalModel ? ` ${externalModel.provider_id} ${externalModel.provider_model_id}` : '';
const testString =
- `${model.name} ${bases.join(' ')} ${model.type} ${model.description ?? ''} ${model.format}`.toLowerCase();
+ `${model.name} ${bases.join(' ')} ${model.type} ${model.description ?? ''} ${model.format}${externalSearch}`.toLowerCase();
if (testString.includes(searchTerm) || regex.test(testString)) {
return true;
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts
index 652cf4c5b24..5bfc31d10fd 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueCanvas.ts
@@ -9,6 +9,7 @@ import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { positivePromptAddedToHistory, selectPositivePrompt } from 'features/controlLayers/store/paramsSlice';
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
import { buildCogView4Graph } from 'features/nodes/util/graph/generation/buildCogView4Graph';
+import { buildExternalGraph } from 'features/nodes/util/graph/generation/buildExternalGraph';
import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph';
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph';
@@ -59,6 +60,8 @@ const enqueueCanvas = async (store: AppStore, canvasManager: CanvasManager, prep
return await buildCogView4Graph(graphBuilderArg);
case 'z-image':
return await buildZImageGraph(graphBuilderArg);
+ case 'external':
+ return await buildExternalGraph(graphBuilderArg);
default:
assert(false, `No graph builders for base ${base}`);
}
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts
index cf00a12ee5f..c50f833ba85 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useEnqueueGenerate.ts
@@ -7,6 +7,7 @@ import { withResult, withResultAsync } from 'common/util/result';
import { positivePromptAddedToHistory, selectPositivePrompt } from 'features/controlLayers/store/paramsSlice';
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
import { buildCogView4Graph } from 'features/nodes/util/graph/generation/buildCogView4Graph';
+import { buildExternalGraph } from 'features/nodes/util/graph/generation/buildExternalGraph';
import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph';
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
import { buildSD3Graph } from 'features/nodes/util/graph/generation/buildSD3Graph';
@@ -52,6 +53,8 @@ const enqueueGenerate = async (store: AppStore, prepend: boolean) => {
return await buildCogView4Graph(graphBuilderArg);
case 'z-image':
return await buildZImageGraph(graphBuilderArg);
+ case 'external':
+ return await buildExternalGraph(graphBuilderArg);
default:
assert(false, `No graph builders for base ${base}`);
}
diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.ts b/invokeai/frontend/web/src/features/queue/store/readiness.ts
index 8fa97eff4a9..61955e82c92 100644
--- a/invokeai/frontend/web/src/features/queue/store/readiness.ts
+++ b/invokeai/frontend/web/src/features/queue/store/readiness.ts
@@ -39,7 +39,8 @@ import type { TabName } from 'features/ui/store/uiTypes';
import i18n from 'i18next';
import { atom, computed } from 'nanostores';
import { useEffect } from 'react';
-import type { MainModelConfig } from 'services/api/types';
+import type { MainOrExternalModelConfig } from 'services/api/types';
+import { isExternalApiModelConfig } from 'services/api/types';
import { $isConnected } from 'services/events/stores';
/**
@@ -221,7 +222,7 @@ const disconnectedReason = (t: typeof i18n.t) => ({ content: t('parameters.invok
const getReasonsWhyCannotEnqueueGenerateTab = (arg: {
isConnected: boolean;
- model: MainModelConfig | null | undefined;
+ model: MainOrExternalModelConfig | null | undefined;
params: ParamsState;
refImages: RefImagesState;
loras: LoRA[];
@@ -243,7 +244,11 @@ const getReasonsWhyCannotEnqueueGenerateTab = (arg: {
reasons.push({ content: i18n.t('parameters.invoke.noModelSelected') });
}
- if (model?.base === 'flux') {
+ if (!model) {
+ // nothing else to validate
+ } else if (isExternalApiModelConfig(model)) {
+ // external models don't require local sub-models
+ } else if (model.base === 'flux') {
if (!params.t5EncoderModel) {
reasons.push({ content: i18n.t('parameters.invoke.noT5EncoderModelSelected') });
}
@@ -280,7 +285,7 @@ const getReasonsWhyCannotEnqueueGenerateTab = (arg: {
}
}
- if (model && SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base)) {
+ if (model && !isExternalApiModelConfig(model) && SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base)) {
const enabledRefImages = refImages.entities.filter(({ isEnabled }) => isEnabled);
enabledRefImages.forEach((entity, i) => {
@@ -431,7 +436,7 @@ const getReasonsWhyCannotEnqueueUpscaleTab = (arg: {
const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
isConnected: boolean;
- model: MainModelConfig | null | undefined;
+ model: MainOrExternalModelConfig | null | undefined;
canvas: CanvasState;
params: ParamsState;
refImages: RefImagesState;
@@ -488,7 +493,11 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
reasons.push({ content: i18n.t('parameters.invoke.noModelSelected') });
}
- if (model?.base === 'flux') {
+ if (!model) {
+ // nothing else to validate
+ } else if (isExternalApiModelConfig(model)) {
+ // external models don't require local sub-models
+ } else if (model.base === 'flux') {
if (!params.t5EncoderModel) {
reasons.push({ content: i18n.t('parameters.invoke.noT5EncoderModelSelected') });
}
@@ -682,7 +691,7 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
}
});
- if (model && SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base)) {
+ if (model && !isExternalApiModelConfig(model) && SUPPORTS_REF_IMAGES_BASE_MODELS.includes(model.base)) {
const enabledRefImages = refImages.entities.filter(({ isEnabled }) => isEnabled);
enabledRefImages.forEach((entity, i) => {
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
index 773b67e39bb..91f5f1efd0a 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
@@ -1,9 +1,11 @@
import { Flex, FormLabel, Icon } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
+import { isExternalModelUnsupportedForTab } from 'features/parameters/components/MainModel/mainModelPickerUtils';
import { UseDefaultSettingsButton } from 'features/parameters/components/MainModel/UseDefaultSettingsButton';
import { ModelPicker } from 'features/parameters/components/ModelPicker';
import { modelSelected } from 'features/parameters/store/actions';
+import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { MdMoneyOff } from 'react-icons/md';
@@ -14,6 +16,7 @@ import { type AnyModelConfig, isNonCommercialMainModelConfig } from 'services/ap
export const MainModelPicker = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
+ const activeTab = useAppSelector(selectActiveTab);
const [modelConfigs] = useMainModels();
const selectedModelConfig = useSelectedModelConfig();
const onChange = useCallback(
@@ -28,6 +31,11 @@ export const MainModelPicker = memo(() => {
[selectedModelConfig]
);
+ const getIsOptionDisabled = useCallback(
+ (modelConfig: AnyModelConfig) => isExternalModelUnsupportedForTab(modelConfig, activeTab),
+ [activeTab]
+ );
+
return (
@@ -46,6 +54,7 @@ export const MainModelPicker = memo(() => {
selectedModelConfig={selectedModelConfig}
onChange={onChange}
grouped
+ getIsOptionDisabled={getIsOptionDisabled}
/>
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx
new file mode 100644
index 00000000000..ea36cf4c65a
--- /dev/null
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/ExternalProviderStatusList.tsx
@@ -0,0 +1,39 @@
+import { Badge, Flex, FormControl, FormLabel, Text, Tooltip } from '@invoke-ai/ui-library';
+import { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useGetExternalProviderStatusesQuery } from 'services/api/endpoints/appInfo';
+
+import { getExternalProviderStatusBadgeInfo } from './externalProviderStatusUtils';
+
+export const ExternalProviderStatusList = memo(() => {
+ const { t } = useTranslation();
+ const { data } = useGetExternalProviderStatusesQuery();
+
+ if (!data || data.length === 0) {
+ return null;
+ }
+
+ const sortedProviders = [...data].sort((a, b) => a.provider_id.localeCompare(b.provider_id));
+
+ return (
+
+ {t('settings.externalProviders')}
+
+ {sortedProviders.map((status) => {
+ const badgeInfo = getExternalProviderStatusBadgeInfo(status);
+ const tooltip = badgeInfo.tooltipMessage ?? (badgeInfo.tooltipKey ? t(badgeInfo.tooltipKey) : null);
+ return (
+
+ {status.provider_id}
+
+ {t(badgeInfo.labelKey)}
+
+
+ );
+ })}
+
+
+ );
+});
+
+ExternalProviderStatusList.displayName = 'ExternalProviderStatusList';
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
index 6c7ebada8f0..b94669e92f0 100644
--- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
@@ -20,6 +20,7 @@ import { InformationalPopover } from 'common/components/InformationalPopover/Inf
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import { buildUseBoolean } from 'common/hooks/useBoolean';
import { selectShouldUseCPUNoise, shouldUseCpuNoiseChanged } from 'features/controlLayers/store/paramsSlice';
+import { ExternalProviderStatusList } from 'features/system/components/SettingsModal/ExternalProviderStatusList';
import { useRefreshAfterResetModal } from 'features/system/components/SettingsModal/RefreshAfterResetModal';
import { SettingsDeveloperLogIsEnabled } from 'features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled';
import { SettingsDeveloperLogLevel } from 'features/system/components/SettingsModal/SettingsDeveloperLogLevel';
@@ -48,8 +49,7 @@ import {
} from 'features/system/store/systemSlice';
import { selectShouldShowProgressInViewer } from 'features/ui/store/uiSelectors';
import { setShouldShowProgressInViewer } from 'features/ui/store/uiSlice';
-import type { ChangeEvent, ReactElement } from 'react';
-import { cloneElement, memo, useCallback, useEffect } from 'react';
+import { type ChangeEvent, cloneElement, memo, type ReactElement, useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { SettingsLanguageSelect } from './SettingsLanguageSelect';
@@ -198,6 +198,10 @@ const SettingsModal = (props: { children: ReactElement }) => {
+
+
+
+
{t('settings.showProgressInViewer')}
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts b/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts
new file mode 100644
index 00000000000..98ae63004c3
--- /dev/null
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.test.ts
@@ -0,0 +1,38 @@
+import type { ExternalProviderStatus } from 'services/api/types';
+import { describe, expect, it } from 'vitest';
+
+import { getExternalProviderStatusBadgeInfo } from './externalProviderStatusUtils';
+
+const buildStatus = (overrides: Partial = {}): ExternalProviderStatus => ({
+ provider_id: 'openai',
+ configured: false,
+ message: null,
+ ...overrides,
+});
+
+describe('getExternalProviderStatusBadgeInfo', () => {
+ it('marks configured providers as configured', () => {
+ const badgeInfo = getExternalProviderStatusBadgeInfo(buildStatus({ configured: true }));
+
+ expect(badgeInfo.labelKey).toBe('settings.externalProviderConfigured');
+ expect(badgeInfo.tooltipKey).toBeNull();
+ expect(badgeInfo.tooltipMessage).toBeNull();
+ expect(badgeInfo.colorScheme).toBe('green');
+ });
+
+ it('adds hint when provider is not configured', () => {
+ const badgeInfo = getExternalProviderStatusBadgeInfo(buildStatus());
+
+ expect(badgeInfo.labelKey).toBe('settings.externalProviderNotConfigured');
+ expect(badgeInfo.tooltipKey).toBe('settings.externalProviderNotConfiguredHint');
+ expect(badgeInfo.tooltipMessage).toBeNull();
+ expect(badgeInfo.colorScheme).toBe('warning');
+ });
+
+ it('prefers status messages when present', () => {
+ const badgeInfo = getExternalProviderStatusBadgeInfo(buildStatus({ message: 'Missing key' }));
+
+ expect(badgeInfo.tooltipKey).toBeNull();
+ expect(badgeInfo.tooltipMessage).toBe('Missing key');
+ });
+});
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts b/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts
new file mode 100644
index 00000000000..fb1f764e2a4
--- /dev/null
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/externalProviderStatusUtils.ts
@@ -0,0 +1,26 @@
+import type { ExternalProviderStatus } from 'services/api/types';
+
+type ExternalProviderStatusBadgeInfo = {
+ labelKey: 'settings.externalProviderConfigured' | 'settings.externalProviderNotConfigured';
+ tooltipKey: 'settings.externalProviderNotConfiguredHint' | null;
+ tooltipMessage: string | null;
+ colorScheme: 'green' | 'warning';
+};
+
+export const getExternalProviderStatusBadgeInfo = (status: ExternalProviderStatus): ExternalProviderStatusBadgeInfo => {
+ if (status.configured) {
+ return {
+ labelKey: 'settings.externalProviderConfigured',
+ tooltipKey: null,
+ tooltipMessage: status.message ?? null,
+ colorScheme: 'green',
+ };
+ }
+
+ return {
+ labelKey: 'settings.externalProviderNotConfigured',
+ tooltipKey: status.message ? null : 'settings.externalProviderNotConfiguredHint',
+ tooltipMessage: status.message ?? null,
+ colorScheme: 'warning',
+ };
+};
diff --git a/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx b/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx
index 0d71b621734..07da9e6e18b 100644
--- a/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx
+++ b/invokeai/frontend/web/src/features/ui/layouts/InitialStateMainModelPicker.tsx
@@ -1,8 +1,10 @@
import { Flex, FormControl, FormLabel, Icon } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
+import { isExternalModelUnsupportedForTab } from 'features/parameters/components/MainModel/mainModelPickerUtils';
import { ModelPicker } from 'features/parameters/components/ModelPicker';
import { modelSelected } from 'features/parameters/store/actions';
+import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { MdMoneyOff } from 'react-icons/md';
@@ -13,6 +15,7 @@ import { type AnyModelConfig, isNonCommercialMainModelConfig } from 'services/ap
export const InitialStateMainModelPicker = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
+ const activeTab = useAppSelector(selectActiveTab);
const [modelConfigs] = useMainModels();
const selectedModelConfig = useSelectedModelConfig();
const onChange = useCallback(
@@ -27,6 +30,11 @@ export const InitialStateMainModelPicker = memo(() => {
[selectedModelConfig]
);
+ const getIsOptionDisabled = useCallback(
+ (modelConfig: AnyModelConfig) => isExternalModelUnsupportedForTab(modelConfig, activeTab),
+ [activeTab]
+ );
+
return (
@@ -45,6 +53,7 @@ export const InitialStateMainModelPicker = memo(() => {
selectedModelConfig={selectedModelConfig}
onChange={onChange}
grouped
+ getIsOptionDisabled={getIsOptionDisabled}
/>
);
diff --git a/invokeai/frontend/web/src/services/api/endpoints/appInfo.ts b/invokeai/frontend/web/src/services/api/endpoints/appInfo.ts
index 8fe85125e6a..9f01c717108 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/appInfo.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/appInfo.ts
@@ -1,7 +1,7 @@
import type { OpenAPIV3_1 } from 'openapi-types';
import type { stringify } from 'querystring';
import type { paths } from 'services/api/schema';
-import type { AppVersion } from 'services/api/types';
+import type { AppVersion, ExternalProviderConfig, ExternalProviderStatus } from 'services/api/types';
import { api, buildV1Url } from '..';
@@ -52,6 +52,35 @@ export const appInfoApi = api.injectEndpoints({
method: 'GET',
}),
}),
+ getExternalProviderStatuses: build.query({
+ query: () => ({
+ url: buildAppInfoUrl('external_providers/status'),
+ method: 'GET',
+ }),
+ providesTags: ['FetchOnReconnect'],
+ }),
+ getExternalProviderConfigs: build.query({
+ query: () => ({
+ url: buildAppInfoUrl('external_providers/config'),
+ method: 'GET',
+ }),
+ providesTags: ['AppConfig', 'FetchOnReconnect'],
+ }),
+ setExternalProviderConfig: build.mutation({
+ query: ({ provider_id, ...body }) => ({
+ url: buildAppInfoUrl(`external_providers/config/${provider_id}`),
+ method: 'POST',
+ body,
+ }),
+ invalidatesTags: ['AppConfig', 'FetchOnReconnect'],
+ }),
+ resetExternalProviderConfig: build.mutation({
+ query: (provider_id) => ({
+ url: buildAppInfoUrl(`external_providers/config/${provider_id}`),
+ method: 'DELETE',
+ }),
+ invalidatesTags: ['AppConfig', 'FetchOnReconnect'],
+ }),
getInvocationCacheStatus: build.query<
paths['/api/v1/app/invocation_cache/status']['get']['responses']['200']['content']['application/json'],
void
@@ -95,6 +124,10 @@ export const {
useGetAppDepsQuery,
useGetPatchmatchStatusQuery,
useGetRuntimeConfigQuery,
+ useGetExternalProviderStatusesQuery,
+ useGetExternalProviderConfigsQuery,
+ useSetExternalProviderConfigMutation,
+ useResetExternalProviderConfigMutation,
useClearInvocationCacheMutation,
useDisableInvocationCacheMutation,
useEnableInvocationCacheMutation,
@@ -102,3 +135,8 @@ export const {
useGetOpenAPISchemaQuery,
useLazyGetOpenAPISchemaQuery,
} = appInfoApi;
+
+type SetExternalProviderConfigArg =
+ paths['/api/v1/app/external_providers/config/{provider_id}']['post']['requestBody']['content']['application/json'] & {
+ provider_id: paths['/api/v1/app/external_providers/config/{provider_id}']['post']['parameters']['path']['provider_id'];
+ };
diff --git a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts b/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts
index 98d7dd1e8df..fa3218d400e 100644
--- a/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts
+++ b/invokeai/frontend/web/src/services/api/hooks/modelsByType.ts
@@ -9,11 +9,12 @@ import {
useGetMissingModelsQuery,
useGetModelConfigsQuery,
} from 'services/api/endpoints/models';
-import type { AnyModelConfig } from 'services/api/types';
+import type { AnyModelConfig, MainOrExternalModelConfig } from 'services/api/types';
import {
isCLIPEmbedModelConfigOrSubmodel,
isControlLayerModelConfig,
isControlNetModelConfig,
+ isExternalApiModelConfig,
isFlux1VAEModelConfig,
isFlux2VAEModelConfig,
isFluxKontextModelConfig,
@@ -21,7 +22,7 @@ import {
isFluxVAEModelConfig,
isIPAdapterModelConfig,
isLoRAModelConfig,
- isNonRefinerMainModelConfig,
+ isMainOrExternalModelConfig,
isQwen3EncoderModelConfig,
isRefinerMainModelModelConfig,
isSpandrelImageToImageModelConfig,
@@ -50,13 +51,13 @@ const buildModelsHook =
return modelConfigsAdapterSelectors
.selectAll(result.data)
.filter((config) => typeGuard(config))
- .filter((config) => !missingModelKeys.has(config.key))
+ .filter((config) => !missingModelKeys.has(config.key) || isExternalApiModelConfig(config))
.filter(filter);
}, [filter, result.data, missingModelsData]);
return [modelConfigs, result] as const;
};
-export const useMainModels = buildModelsHook(isNonRefinerMainModelConfig);
+export const useMainModels = buildModelsHook(isMainOrExternalModelConfig);
export const useRefinerModels = buildModelsHook(isRefinerMainModelModelConfig);
export const useLoRAModels = buildModelsHook(isLoRAModelConfig);
export const useControlLayerModels = buildModelsHook(isControlLayerModelConfig);
@@ -94,7 +95,7 @@ const buildModelsSelector =
return modelConfigsAdapterSelectors
.selectAll(result.data)
.filter(typeGuard)
- .filter((config) => !missingModelKeys.has(config.key));
+ .filter((config) => !missingModelKeys.has(config.key) || isExternalApiModelConfig(config));
};
export const selectIPAdapterModels = buildModelsSelector(isIPAdapterModelConfig);
export const selectGlobalRefImageModels = buildModelsSelector(
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index b605413787b..392723bef19 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -10434,7 +10434,7 @@ export type components = {
* @description The nodes in this graph
*/
nodes?: {
- [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
};
/**
* Edges
@@ -13656,7 +13656,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -13720,7 +13720,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -14026,7 +14026,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -14101,7 +14101,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts
index 5d56c346f87..0c06e04dcd6 100644
--- a/invokeai/frontend/web/src/services/api/types.ts
+++ b/invokeai/frontend/web/src/services/api/types.ts
@@ -43,6 +43,9 @@ export type InvocationJSONSchemaExtra = S['UIConfigBase'];
// App Info
export type AppVersion = S['AppVersion'];
+export type ExternalProviderStatus = S['ExternalProviderStatusModel'];
+export type ExternalProviderConfig = S['ExternalProviderConfigModel'];
+export type UpdateModelBody = paths['/api/v2/models/i/{key}']['patch']['requestBody']['content']['application/json'];
const zResourceOrigin = z.enum(['internal', 'external']);
type ResourceOrigin = z.infer;
@@ -110,6 +113,46 @@ export type ChatGPT4oModelConfig = ApiModelConfig;
export type Gemini2_5ModelConfig = ApiModelConfig;
type SubmodelDefinition = S['SubmodelDefinition'];
+export type ExternalImageSize = {
+ width: number;
+ height: number;
+};
+
+export type ExternalModelCapabilities = {
+ modes: ('txt2img' | 'img2img' | 'inpaint')[];
+ supports_reference_images?: boolean;
+ supports_negative_prompt?: boolean;
+ supports_seed?: boolean;
+ supports_guidance?: boolean;
+ max_images_per_request?: number | null;
+ max_image_size?: ExternalImageSize | null;
+ allowed_aspect_ratios?: string[] | null;
+ max_reference_images?: number | null;
+ mask_format?: 'alpha' | 'binary' | 'none';
+ input_image_required_for?: ('txt2img' | 'img2img' | 'inpaint')[] | null;
+};
+
+export type ExternalApiModelDefaultSettings = {
+ width?: number | null;
+ height?: number | null;
+ steps?: number | null;
+ guidance?: number | null;
+ num_images?: number | null;
+};
+
+export type ExternalApiModelConfig = AnyModelConfig & {
+ base: 'external';
+ type: 'external_image_generator';
+ format: 'external_api';
+ provider_id: string;
+ provider_model_id: string;
+ capabilities: ExternalModelCapabilities;
+ default_settings?: ExternalApiModelDefaultSettings | null;
+ tags?: string[] | null;
+ is_default?: boolean;
+};
+export type MainOrExternalModelConfig = MainModelConfig | ExternalApiModelConfig;
+
/**
* Checks if a list of submodels contains any that match a given variant or type
* @param submodels The list of submodels to check
@@ -290,6 +333,10 @@ export const isFluxReduxModelConfig = (config: AnyModelConfig): config is FLUXRe
return config.type === 'flux_redux';
};
+export const isExternalApiModelConfig = (config: AnyModelConfig): config is ExternalApiModelConfig => {
+ return (config as { format?: string }).format === 'external_api';
+};
+
export const isUnknownModelConfig = (config: AnyModelConfig): config is UnknownModelConfig => {
return config.type === 'unknown';
};
@@ -302,6 +349,10 @@ export const isNonRefinerMainModelConfig = (config: AnyModelConfig): config is M
return config.type === 'main' && config.base !== 'sdxl-refiner';
};
+export const isMainOrExternalModelConfig = (config: AnyModelConfig): config is MainOrExternalModelConfig => {
+ return isNonRefinerMainModelConfig(config) || isExternalApiModelConfig(config);
+};
+
export const isRefinerMainModelModelConfig = (config: AnyModelConfig): config is MainModelConfig => {
return config.type === 'main' && config.base === 'sdxl-refiner';
};
diff --git a/tests/app/invocations/test_external_image_generation.py b/tests/app/invocations/test_external_image_generation.py
new file mode 100644
index 00000000000..3ede7aef421
--- /dev/null
+++ b/tests/app/invocations/test_external_image_generation.py
@@ -0,0 +1,120 @@
+from types import SimpleNamespace
+from unittest.mock import MagicMock
+
+import pytest
+from PIL import Image
+
+from invokeai.app.invocations.external_image_generation import ExternalImageGenerationInvocation
+from invokeai.app.invocations.fields import ImageField
+from invokeai.app.invocations.model import ModelIdentifierField
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGeneratedImage,
+ ExternalGenerationResult,
+)
+from invokeai.app.services.shared.graph import Graph, GraphExecutionState
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig, ExternalModelCapabilities
+
+
+def _build_model() -> ExternalApiModelConfig:
+ return ExternalApiModelConfig(
+ key="external_test",
+ name="External Test",
+ provider_id="openai",
+ provider_model_id="gpt-image-1",
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img"],
+ supports_reference_images=True,
+ supports_negative_prompt=True,
+ supports_seed=True,
+ ),
+ )
+
+
+def _build_context(model_config: ExternalApiModelConfig, generated_image: Image.Image) -> MagicMock:
+ context = MagicMock()
+ context.models.get_config.return_value = model_config
+ context.images.get_pil.return_value = generated_image
+ context.images.save.return_value = SimpleNamespace(image_name="result.png")
+ context._services.external_generation.generate.return_value = ExternalGenerationResult(
+ images=[ExternalGeneratedImage(image=generated_image, seed=42)],
+ provider_request_id="req-123",
+ provider_metadata={"model": model_config.provider_model_id},
+ )
+ return context
+
+
+def test_external_invocation_builds_request_and_outputs() -> None:
+ model_config = _build_model()
+ model_field = ModelIdentifierField.from_config(model_config)
+ generated_image = Image.new("RGB", (16, 16), color="black")
+ context = _build_context(model_config, generated_image)
+
+ invocation = ExternalImageGenerationInvocation(
+ id="external_node",
+ model=model_field,
+ mode="txt2img",
+ prompt="A prompt",
+ negative_prompt="bad",
+ seed=123,
+ num_images=1,
+ width=512,
+ height=512,
+ steps=10,
+ guidance=4.5,
+ reference_images=[ImageField(image_name="ref.png")],
+ reference_image_weights=[0.6],
+ )
+
+ output = invocation.invoke(context)
+
+ request = context._services.external_generation.generate.call_args[0][0]
+ assert request.prompt == "A prompt"
+ assert request.negative_prompt == "bad"
+ assert request.seed == 123
+ assert len(request.reference_images) == 1
+ assert request.reference_images[0].weight == 0.6
+ assert output.collection[0].image_name == "result.png"
+
+
+def test_external_invocation_rejects_mismatched_reference_weights() -> None:
+ model_config = _build_model()
+ model_field = ModelIdentifierField.from_config(model_config)
+ generated_image = Image.new("RGB", (16, 16), color="black")
+ context = _build_context(model_config, generated_image)
+
+ invocation = ExternalImageGenerationInvocation(
+ id="external_node",
+ model=model_field,
+ mode="txt2img",
+ prompt="A prompt",
+ reference_images=[ImageField(image_name="ref.png")],
+ reference_image_weights=[0.1, 0.2],
+ )
+
+ with pytest.raises(ValueError, match="reference_image_weights"):
+ invocation.invoke(context)
+
+
+def test_external_graph_execution_state_runs_node() -> None:
+ model_config = _build_model()
+ model_field = ModelIdentifierField.from_config(model_config)
+ generated_image = Image.new("RGB", (16, 16), color="black")
+ context = _build_context(model_config, generated_image)
+
+ invocation = ExternalImageGenerationInvocation(
+ id="external_node",
+ model=model_field,
+ mode="txt2img",
+ prompt="A prompt",
+ )
+
+ graph = Graph()
+ graph.add_node(invocation)
+
+ session = GraphExecutionState(graph=graph)
+ node = session.next()
+ assert node is not None
+ output = node.invoke(context)
+ session.complete(node.id, output)
+
+ assert session.results[node.id] == output
diff --git a/tests/app/routers/test_app_info.py b/tests/app/routers/test_app_info.py
new file mode 100644
index 00000000000..12201249ef4
--- /dev/null
+++ b/tests/app/routers/test_app_info.py
@@ -0,0 +1,93 @@
+import os
+import os
+from pathlib import Path
+from typing import Any
+
+import pytest
+from fastapi.testclient import TestClient
+
+from invokeai.app.api.dependencies import ApiDependencies
+from invokeai.app.api_app import app
+from invokeai.app.services.config.config_default import get_config, load_and_migrate_config
+from invokeai.app.services.external_generation.external_generation_common import ExternalProviderStatus
+from invokeai.app.services.invoker import Invoker
+
+
+@pytest.fixture(autouse=True, scope="module")
+def client(invokeai_root_dir: Path) -> TestClient:
+ os.environ["INVOKEAI_ROOT"] = invokeai_root_dir.as_posix()
+ return TestClient(app)
+
+
+class MockApiDependencies(ApiDependencies):
+ invoker: Invoker
+
+ def __init__(self, invoker: Invoker) -> None:
+ self.invoker = invoker
+
+
+def test_get_external_provider_statuses(monkeypatch: Any, mock_invoker: Invoker, client: TestClient) -> None:
+ statuses = {
+ "gemini": ExternalProviderStatus(provider_id="gemini", configured=True, message=None),
+ "openai": ExternalProviderStatus(provider_id="openai", configured=False, message="Missing key"),
+ }
+
+ monkeypatch.setattr("invokeai.app.api.routers.app_info.ApiDependencies", MockApiDependencies(mock_invoker))
+ monkeypatch.setattr(mock_invoker.services.external_generation, "get_provider_statuses", lambda: statuses)
+
+ response = client.get("/api/v1/app/external_providers/status")
+
+ assert response.status_code == 200
+ payload = sorted(response.json(), key=lambda item: item["provider_id"])
+ assert payload == [
+ {"provider_id": "gemini", "configured": True, "message": None},
+ {"provider_id": "openai", "configured": False, "message": "Missing key"},
+ ]
+
+
+def test_external_provider_config_update_and_reset(client: TestClient) -> None:
+ for provider_id in ("gemini", "openai"):
+ response = client.delete(f"/api/v1/app/external_providers/config/{provider_id}")
+ assert response.status_code == 200
+
+ response = client.get("/api/v1/app/external_providers/config")
+ assert response.status_code == 200
+ payload = response.json()
+ openai_config = _get_provider_config(payload, "openai")
+ assert openai_config["api_key_configured"] is False
+ assert openai_config["base_url"] is None
+
+ response = client.post(
+ "/api/v1/app/external_providers/config/openai",
+ json={"api_key": "openai-key", "base_url": "https://api.openai.test"},
+ )
+ assert response.status_code == 200
+ payload = response.json()
+ assert payload["api_key_configured"] is True
+ assert payload["base_url"] == "https://api.openai.test"
+
+ response = client.get("/api/v1/app/external_providers/config")
+ assert response.status_code == 200
+ payload = response.json()
+ openai_config = _get_provider_config(payload, "openai")
+ assert openai_config["api_key_configured"] is True
+ assert openai_config["base_url"] == "https://api.openai.test"
+
+ config_path = get_config().config_file_path
+ file_config = load_and_migrate_config(config_path)
+ assert file_config.external_openai_api_key == "openai-key"
+ assert file_config.external_openai_base_url == "https://api.openai.test"
+
+ response = client.delete("/api/v1/app/external_providers/config/openai")
+ assert response.status_code == 200
+ payload = response.json()
+ assert payload["api_key_configured"] is False
+ assert payload["base_url"] is None
+
+ file_config = load_and_migrate_config(config_path)
+ assert file_config.external_openai_api_key is None
+ assert file_config.external_openai_base_url is None
+
+
+def _get_provider_config(payload: list[dict[str, Any]], provider_id: str) -> dict[str, Any]:
+ return next(item for item in payload if item["provider_id"] == provider_id)
diff --git a/tests/app/routers/test_model_manager.py b/tests/app/routers/test_model_manager.py
new file mode 100644
index 00000000000..8f69ffce371
--- /dev/null
+++ b/tests/app/routers/test_model_manager.py
@@ -0,0 +1,71 @@
+import os
+from pathlib import Path
+from typing import Any
+
+import pytest
+from fastapi.testclient import TestClient
+
+from invokeai.app.api.dependencies import ApiDependencies
+from invokeai.app.api_app import app
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig, ExternalModelCapabilities
+from invokeai.backend.model_manager.taxonomy import ModelType
+
+
+@pytest.fixture(autouse=True, scope="module")
+def client(invokeai_root_dir: Path) -> TestClient:
+ os.environ["INVOKEAI_ROOT"] = invokeai_root_dir.as_posix()
+ return TestClient(app)
+
+
+class DummyModelImages:
+ def get_url(self, key: str) -> str:
+ return f"https://example.com/models/{key}.png"
+
+
+class DummyInvoker:
+ def __init__(self, services: Any) -> None:
+ self.services = services
+
+
+class MockApiDependencies(ApiDependencies):
+ invoker: DummyInvoker
+
+ def __init__(self, invoker: DummyInvoker) -> None:
+ self.invoker = invoker
+
+
+def test_model_manager_external_config_round_trip(
+ monkeypatch: Any, client: TestClient, mm2_model_manager: Any, mm2_app_config: Any
+) -> None:
+ config = ExternalApiModelConfig(
+ key="external_test",
+ name="External Test",
+ provider_id="openai",
+ provider_model_id="gpt-image-1",
+ capabilities=ExternalModelCapabilities(modes=["txt2img"]),
+ )
+ mm2_model_manager.store.add_model(config)
+
+ services = type("Services", (), {})()
+ services.model_manager = mm2_model_manager
+ services.model_images = DummyModelImages()
+ services.configuration = mm2_app_config
+
+ invoker = DummyInvoker(services)
+ monkeypatch.setattr("invokeai.app.api.routers.model_manager.ApiDependencies", MockApiDependencies(invoker))
+
+ response = client.get("/api/v2/models/", params={"model_type": ModelType.ExternalImageGenerator.value})
+
+ assert response.status_code == 200
+ payload = response.json()
+ assert len(payload["models"]) == 1
+ assert payload["models"][0]["key"] == "external_test"
+ assert payload["models"][0]["provider_id"] == "openai"
+ assert payload["models"][0]["cover_image"] == "https://example.com/models/external_test.png"
+
+ get_response = client.get("/api/v2/models/i/external_test")
+
+ assert get_response.status_code == 200
+ model_payload = get_response.json()
+ assert model_payload["provider_model_id"] == "gpt-image-1"
+ assert model_payload["cover_image"] == "https://example.com/models/external_test.png"
diff --git a/tests/app/services/external_generation/test_external_generation_service.py b/tests/app/services/external_generation/test_external_generation_service.py
new file mode 100644
index 00000000000..8379b8b754c
--- /dev/null
+++ b/tests/app/services/external_generation/test_external_generation_service.py
@@ -0,0 +1,243 @@
+import logging
+
+import pytest
+from PIL import Image
+
+from invokeai.app.services.external_generation.errors import (
+ ExternalProviderCapabilityError,
+ ExternalProviderNotConfiguredError,
+ ExternalProviderNotFoundError,
+)
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGeneratedImage,
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+ ExternalReferenceImage,
+)
+from invokeai.app.services.config.config_default import InvokeAIAppConfig
+from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
+from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
+from invokeai.backend.model_manager.configs.external_api import (
+ ExternalApiModelConfig,
+ ExternalImageSize,
+ ExternalModelCapabilities,
+)
+
+
+class DummyProvider(ExternalProvider):
+ def __init__(self, provider_id: str, configured: bool, result: ExternalGenerationResult | None = None) -> None:
+ super().__init__(InvokeAIAppConfig(), logging.getLogger("test"))
+ self.provider_id = provider_id
+ self._configured = configured
+ self._result = result
+ self.last_request: ExternalGenerationRequest | None = None
+
+ def is_configured(self) -> bool:
+ return self._configured
+
+ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
+ self.last_request = request
+ assert self._result is not None
+ return self._result
+
+
+def _build_model(capabilities: ExternalModelCapabilities) -> ExternalApiModelConfig:
+ return ExternalApiModelConfig(
+ key="external_test",
+ name="External Test",
+ provider_id="openai",
+ provider_model_id="gpt-image-1",
+ capabilities=capabilities,
+ )
+
+
+def _build_request(
+ *,
+ model: ExternalApiModelConfig,
+ mode: str = "txt2img",
+ negative_prompt: str | None = None,
+ seed: int | None = None,
+ num_images: int = 1,
+ guidance: float | None = None,
+ width: int = 64,
+ height: int = 64,
+ init_image: Image.Image | None = None,
+ mask_image: Image.Image | None = None,
+ reference_images: list[ExternalReferenceImage] | None = None,
+) -> ExternalGenerationRequest:
+ return ExternalGenerationRequest(
+ model=model,
+ mode=mode, # type: ignore[arg-type]
+ prompt="A test prompt",
+ negative_prompt=negative_prompt,
+ seed=seed,
+ num_images=num_images,
+ width=width,
+ height=height,
+ steps=10,
+ guidance=guidance,
+ init_image=init_image,
+ mask_image=mask_image,
+ reference_images=reference_images or [],
+ metadata=None,
+ )
+
+
+def _make_image() -> Image.Image:
+ return Image.new("RGB", (64, 64), color="black")
+
+
+def test_generate_requires_registered_provider() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["txt2img"]))
+ request = _build_request(model=model)
+ service = ExternalGenerationService({}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderNotFoundError):
+ service.generate(request)
+
+
+def test_generate_requires_configured_provider() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["txt2img"]))
+ request = _build_request(model=model)
+ provider = DummyProvider("openai", configured=False)
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderNotConfiguredError):
+ service.generate(request)
+
+
+def test_generate_validates_mode_support() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["txt2img"]))
+ request = _build_request(model=model, mode="img2img", init_image=_make_image())
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderCapabilityError, match="Mode 'img2img'"):
+ service.generate(request)
+
+
+def test_generate_validates_negative_prompt_support() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["txt2img"], supports_negative_prompt=False))
+ request = _build_request(model=model, negative_prompt="bad")
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderCapabilityError, match="Negative prompts"):
+ service.generate(request)
+
+
+def test_generate_requires_init_image_for_img2img() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["img2img"]))
+ request = _build_request(model=model, mode="img2img")
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderCapabilityError, match="requires an init image"):
+ service.generate(request)
+
+
+def test_generate_requires_mask_for_inpaint() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["inpaint"]))
+ request = _build_request(model=model, mode="inpaint", init_image=_make_image())
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderCapabilityError, match="requires a mask"):
+ service.generate(request)
+
+
+def test_generate_validates_reference_images() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["txt2img"], supports_reference_images=False))
+ request = _build_request(
+ model=model,
+ reference_images=[ExternalReferenceImage(image=_make_image(), weight=0.8)],
+ )
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderCapabilityError, match="Reference images"):
+ service.generate(request)
+
+
+def test_generate_validates_limits() -> None:
+ model = _build_model(
+ ExternalModelCapabilities(
+ modes=["txt2img"],
+ supports_reference_images=True,
+ max_reference_images=1,
+ max_images_per_request=1,
+ )
+ )
+ request = _build_request(
+ model=model,
+ num_images=2,
+ reference_images=[
+ ExternalReferenceImage(image=_make_image()),
+ ExternalReferenceImage(image=_make_image()),
+ ],
+ )
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ with pytest.raises(ExternalProviderCapabilityError, match="supports at most"):
+ service.generate(request)
+
+
+def test_generate_validates_allowed_aspect_ratios() -> None:
+ model = _build_model(
+ ExternalModelCapabilities(
+ modes=["txt2img"],
+ allowed_aspect_ratios=["1:1", "16:9"],
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=1024, height=1024),
+ "16:9": ExternalImageSize(width=1344, height=768),
+ },
+ )
+ )
+ request = _build_request(model=model)
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ response = service.generate(request)
+ assert response.images == []
+ assert provider.last_request is not None
+ assert provider.last_request.width == 1024
+ assert provider.last_request.height == 1024
+
+
+def test_generate_validates_allowed_aspect_ratios_with_bucket_sizes() -> None:
+ model = _build_model(
+ ExternalModelCapabilities(
+ modes=["txt2img"],
+ allowed_aspect_ratios=["1:1", "16:9"],
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=1024, height=1024),
+ "16:9": ExternalImageSize(width=1344, height=768),
+ },
+ )
+ )
+ request = _build_request(model=model, width=160, height=90)
+ provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ response = service.generate(request)
+
+ assert response.images == []
+ assert provider.last_request is not None
+ assert provider.last_request.width == 1344
+ assert provider.last_request.height == 768
+
+
+def test_generate_happy_path() -> None:
+ model = _build_model(
+ ExternalModelCapabilities(modes=["txt2img"], supports_negative_prompt=True, supports_seed=True)
+ )
+ request = _build_request(model=model, negative_prompt="", seed=42)
+ result = ExternalGenerationResult(images=[ExternalGeneratedImage(image=_make_image(), seed=42)])
+ provider = DummyProvider("openai", configured=True, result=result)
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ response = service.generate(request)
+
+ assert response is result
+ assert provider.last_request == request
diff --git a/tests/app/services/external_generation/test_external_provider_adapters.py b/tests/app/services/external_generation/test_external_provider_adapters.py
new file mode 100644
index 00000000000..38f4c9e3d52
--- /dev/null
+++ b/tests/app/services/external_generation/test_external_provider_adapters.py
@@ -0,0 +1,346 @@
+import io
+import logging
+
+import pytest
+from PIL import Image
+
+from invokeai.app.services.config.config_default import InvokeAIAppConfig
+from invokeai.app.services.external_generation.errors import ExternalProviderRequestError
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGenerationRequest,
+ ExternalReferenceImage,
+)
+from invokeai.app.services.external_generation.image_utils import decode_image_base64, encode_image_base64
+from invokeai.app.services.external_generation.providers.gemini import GeminiProvider
+from invokeai.app.services.external_generation.providers.openai import OpenAIProvider
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig, ExternalModelCapabilities
+
+
+class DummyResponse:
+ def __init__(self, ok: bool, status_code: int = 200, json_data: dict | None = None, text: str = "") -> None:
+ self.ok = ok
+ self.status_code = status_code
+ self._json_data = json_data or {}
+ self.text = text
+ self.headers: dict[str, str] = {}
+
+ def json(self) -> dict:
+ return self._json_data
+
+
+def _make_image(color: str = "black") -> Image.Image:
+ return Image.new("RGB", (32, 32), color=color)
+
+
+def _build_model(provider_id: str, provider_model_id: str) -> ExternalApiModelConfig:
+ return ExternalApiModelConfig(
+ key=f"{provider_id}_test",
+ name=f"{provider_id.title()} Test",
+ provider_id=provider_id,
+ provider_model_id=provider_model_id,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img", "img2img", "inpaint"],
+ supports_negative_prompt=True,
+ supports_reference_images=True,
+ supports_seed=True,
+ supports_guidance=True,
+ ),
+ )
+
+
+def _build_request(
+ model: ExternalApiModelConfig,
+ mode: str = "txt2img",
+ init_image: Image.Image | None = None,
+ mask_image: Image.Image | None = None,
+ reference_images: list[ExternalReferenceImage] | None = None,
+) -> ExternalGenerationRequest:
+ return ExternalGenerationRequest(
+ model=model,
+ mode=mode, # type: ignore[arg-type]
+ prompt="A test prompt",
+ negative_prompt="",
+ seed=123,
+ num_images=1,
+ width=256,
+ height=256,
+ steps=20,
+ guidance=5.5,
+ init_image=init_image,
+ mask_image=mask_image,
+ reference_images=reference_images or [],
+ metadata=None,
+ )
+
+
+def test_gemini_generate_success(monkeypatch: pytest.MonkeyPatch) -> None:
+ api_key = "gemini-key"
+ config = InvokeAIAppConfig(external_gemini_api_key=api_key)
+ provider = GeminiProvider(config, logging.getLogger("test"))
+ model = _build_model("gemini", "gemini-2.5-flash-image")
+ init_image = _make_image("blue")
+ ref_image = _make_image("red")
+ request = _build_request(
+ model,
+ init_image=init_image,
+ reference_images=[ExternalReferenceImage(image=ref_image, weight=0.6)],
+ )
+ encoded = encode_image_base64(_make_image("green"))
+ captured: dict[str, object] = {}
+
+ def fake_post(url: str, params: dict, json: dict, timeout: int) -> DummyResponse:
+ captured["url"] = url
+ captured["params"] = params
+ captured["json"] = json
+ captured["timeout"] = timeout
+ return DummyResponse(
+ ok=True,
+ json_data={
+ "candidates": [
+ {"content": {"parts": [{"inlineData": {"data": encoded}}]}},
+ ]
+ },
+ )
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ result = provider.generate(request)
+
+ assert (
+ captured["url"]
+ == "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent"
+ )
+ assert captured["params"] == {"key": api_key}
+ payload = captured["json"]
+ assert isinstance(payload, dict)
+ system_instruction = payload.get("systemInstruction")
+ assert isinstance(system_instruction, dict)
+ system_parts = system_instruction.get("parts")
+ assert isinstance(system_parts, list)
+ system_text = str(system_parts[0]).lower()
+ assert "image" in system_text
+ generation_config = payload.get("generationConfig")
+ assert isinstance(generation_config, dict)
+ assert generation_config["candidateCount"] == 1
+ assert generation_config["responseModalities"] == ["IMAGE"]
+ contents = payload.get("contents")
+ assert isinstance(contents, list)
+ first_content = contents[0]
+ assert isinstance(first_content, dict)
+ parts = first_content.get("parts")
+ assert isinstance(parts, list)
+ assert len(parts) >= 3
+ part0 = parts[0]
+ part1 = parts[1]
+ part2 = parts[2]
+ assert isinstance(part0, dict)
+ assert isinstance(part1, dict)
+ assert isinstance(part2, dict)
+ inline0 = part0.get("inlineData")
+ assert isinstance(inline0, dict)
+ assert part1["text"] == request.prompt
+ inline1 = part2.get("inlineData")
+ assert isinstance(inline1, dict)
+ assert inline0["data"] == encode_image_base64(init_image)
+ assert inline1["data"] == encode_image_base64(ref_image)
+ assert result.images[0].seed == request.seed
+ assert result.provider_metadata == {"model": request.model.provider_model_id}
+
+
+def test_gemini_generate_error_response(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(external_gemini_api_key="gemini-key")
+ provider = GeminiProvider(config, logging.getLogger("test"))
+ model = _build_model("gemini", "gemini-2.5-flash-image")
+ request = _build_request(model)
+
+ def fake_post(url: str, params: dict, json: dict, timeout: int) -> DummyResponse:
+ return DummyResponse(ok=False, status_code=400, text="bad request")
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ with pytest.raises(ExternalProviderRequestError, match="Gemini request failed"):
+ provider.generate(request)
+
+
+def test_gemini_generate_uses_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(
+ external_gemini_api_key="gemini-key",
+ external_gemini_base_url="https://proxy.gemini",
+ )
+ provider = GeminiProvider(config, logging.getLogger("test"))
+ model = _build_model("gemini", "gemini-2.5-flash-image")
+ request = _build_request(model)
+ encoded = encode_image_base64(_make_image("green"))
+ captured: dict[str, object] = {}
+
+ def fake_post(url: str, params: dict, json: dict, timeout: int) -> DummyResponse:
+ captured["url"] = url
+ return DummyResponse(
+ ok=True,
+ json_data={"candidates": [{"content": {"parts": [{"inlineData": {"data": encoded}}]}}]},
+ )
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ provider.generate(request)
+
+ assert captured["url"] == "https://proxy.gemini/v1beta/models/gemini-2.5-flash-image:generateContent"
+
+
+def test_gemini_generate_keeps_base_url_version(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(
+ external_gemini_api_key="gemini-key",
+ external_gemini_base_url="https://proxy.gemini/v1",
+ )
+ provider = GeminiProvider(config, logging.getLogger("test"))
+ model = _build_model("gemini", "gemini-2.5-flash-image")
+ request = _build_request(model)
+ encoded = encode_image_base64(_make_image("green"))
+ captured: dict[str, object] = {}
+
+ def fake_post(url: str, params: dict, json: dict, timeout: int) -> DummyResponse:
+ captured["url"] = url
+ return DummyResponse(
+ ok=True,
+ json_data={"candidates": [{"content": {"parts": [{"inlineData": {"data": encoded}}]}}]},
+ )
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ provider.generate(request)
+
+ assert captured["url"] == "https://proxy.gemini/v1/models/gemini-2.5-flash-image:generateContent"
+
+
+def test_gemini_generate_strips_models_prefix(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(external_gemini_api_key="gemini-key")
+ provider = GeminiProvider(config, logging.getLogger("test"))
+ model = _build_model("gemini", "models/gemini-2.5-flash-image")
+ request = _build_request(model)
+ encoded = encode_image_base64(_make_image("green"))
+ captured: dict[str, object] = {}
+
+ def fake_post(url: str, params: dict, json: dict, timeout: int) -> DummyResponse:
+ captured["url"] = url
+ return DummyResponse(
+ ok=True,
+ json_data={"candidates": [{"content": {"parts": [{"inlineData": {"data": encoded}}]}}]},
+ )
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ provider.generate(request)
+
+ assert (
+ captured["url"]
+ == "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent"
+ )
+
+
+def test_openai_generate_txt2img_success(monkeypatch: pytest.MonkeyPatch) -> None:
+ api_key = "openai-key"
+ config = InvokeAIAppConfig(external_openai_api_key=api_key)
+ provider = OpenAIProvider(config, logging.getLogger("test"))
+ model = _build_model("openai", "gpt-image-1")
+ request = _build_request(model)
+ encoded = encode_image_base64(_make_image("purple"))
+ captured: dict[str, object] = {}
+
+ def fake_post(url: str, headers: dict, json: dict, timeout: int) -> DummyResponse:
+ captured["url"] = url
+ captured["headers"] = headers
+ captured["json"] = json
+ response = DummyResponse(ok=True, json_data={"data": [{"b64_json": encoded}]})
+ response.headers["x-request-id"] = "req-123"
+ return response
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ result = provider.generate(request)
+
+ assert captured["url"] == "https://api.openai.com/v1/images/generations"
+ headers = captured["headers"]
+ assert isinstance(headers, dict)
+ assert headers["Authorization"] == f"Bearer {api_key}"
+ json_payload = captured["json"]
+ assert isinstance(json_payload, dict)
+ assert json_payload["prompt"] == request.prompt
+ assert result.provider_request_id == "req-123"
+ assert result.images[0].seed == request.seed
+ assert decode_image_base64(encoded).size == result.images[0].image.size
+
+
+def test_openai_generate_uses_base_url(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(
+ external_openai_api_key="openai-key",
+ external_openai_base_url="https://proxy.openai/",
+ )
+ provider = OpenAIProvider(config, logging.getLogger("test"))
+ model = _build_model("openai", "gpt-image-1")
+ request = _build_request(model)
+ encoded = encode_image_base64(_make_image("purple"))
+ captured: dict[str, object] = {}
+
+ def fake_post(url: str, headers: dict, json: dict, timeout: int) -> DummyResponse:
+ captured["url"] = url
+ return DummyResponse(ok=True, json_data={"data": [{"b64_json": encoded}]})
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ provider.generate(request)
+
+ assert captured["url"] == "https://proxy.openai/v1/images/generations"
+
+
+def test_openai_generate_txt2img_error_response(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(external_openai_api_key="openai-key")
+ provider = OpenAIProvider(config, logging.getLogger("test"))
+ model = _build_model("openai", "gpt-image-1")
+ request = _build_request(model)
+
+ def fake_post(url: str, headers: dict, json: dict, timeout: int) -> DummyResponse:
+ return DummyResponse(ok=False, status_code=500, text="server error")
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ with pytest.raises(ExternalProviderRequestError, match="OpenAI request failed"):
+ provider.generate(request)
+
+
+def test_openai_generate_inpaint_uses_edit_endpoint(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(external_openai_api_key="openai-key")
+ provider = OpenAIProvider(config, logging.getLogger("test"))
+ model = _build_model("openai", "gpt-image-1")
+ request = _build_request(
+ model,
+ mode="inpaint",
+ init_image=_make_image("white"),
+ mask_image=_make_image("black"),
+ )
+ encoded = encode_image_base64(_make_image("orange"))
+ captured: dict[str, object] = {}
+
+ def fake_post(url: str, headers: dict, data: dict, files: dict, timeout: int) -> DummyResponse:
+ captured["url"] = url
+ captured["data"] = data
+ captured["files"] = files
+ response = DummyResponse(ok=True, json_data={"data": [{"b64_json": encoded}]})
+ return response
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ result = provider.generate(request)
+
+ assert captured["url"] == "https://api.openai.com/v1/images/edits"
+ data_payload = captured["data"]
+ assert isinstance(data_payload, dict)
+ assert data_payload["prompt"] == request.prompt
+ files = captured["files"]
+ assert isinstance(files, dict)
+ assert "image" in files
+ assert "mask" in files
+ image_tuple = files["image"]
+ assert isinstance(image_tuple, tuple)
+ assert image_tuple[0] == "image.png"
+ assert isinstance(image_tuple[1], io.BytesIO)
+ assert result.images
diff --git a/tests/app/services/model_install/test_model_install.py b/tests/app/services/model_install/test_model_install.py
index d19eb95a8c2..c3d5d18e06c 100644
--- a/tests/app/services/model_install/test_model_install.py
+++ b/tests/app/services/model_install/test_model_install.py
@@ -33,6 +33,7 @@
URLModelSource,
)
from invokeai.app.services.model_records import ModelRecordChanges, UnknownModelException
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig
from invokeai.backend.model_manager.taxonomy import (
BaseModelType,
ModelFormat,
@@ -213,6 +214,21 @@ def test_inplace_install(
assert Path(job.config_out.path).exists()
+def test_external_install(mm2_installer: ModelInstallServiceBase) -> None:
+ config = ModelRecordChanges(name="ChatGPT Image", description="External model", key="chatgpt_image")
+ job = mm2_installer.heuristic_import("external://openai/gpt-image-1", config=config)
+
+ mm2_installer.wait_for_installs()
+
+ assert job.status == InstallStatus.COMPLETED
+ assert job.config_out is not None
+ assert isinstance(job.config_out, ExternalApiModelConfig)
+ assert job.config_out.provider_id == "openai"
+ assert job.config_out.provider_model_id == "gpt-image-1"
+ assert job.config_out.base == BaseModelType.External
+ assert job.config_out.type == ModelType.ExternalImageGenerator
+
+
def test_delete_install(
mm2_installer: ModelInstallServiceBase, embedding_file: Path, mm2_app_config: InvokeAIAppConfig
) -> None:
diff --git a/tests/app/services/model_load/test_load_api.py b/tests/app/services/model_load/test_load_api.py
index c0760cd3cad..8f7f8449723 100644
--- a/tests/app/services/model_load/test_load_api.py
+++ b/tests/app/services/model_load/test_load_api.py
@@ -4,6 +4,7 @@
import torch
from diffusers import AutoencoderTiny
+from invokeai.app.invocations.model import ModelIdentifierField
from invokeai.app.services.invocation_services import InvocationServices
from invokeai.app.services.model_manager import ModelManagerServiceBase
from invokeai.app.services.shared.invocation_context import (
@@ -11,6 +12,7 @@
InvocationContextData,
build_invocation_context,
)
+from invokeai.backend.model_manager.configs.external_api import ExternalApiModelConfig, ExternalModelCapabilities
from invokeai.backend.model_manager.load.load_base import LoadedModelWithoutConfig
from tests.backend.model_manager.model_manager_fixtures import * # noqa F403
@@ -78,6 +80,27 @@ def test_download_and_load(mock_context: InvocationContext) -> None:
assert loaded_model_1.model is loaded_model_2.model # should be cached copy
+def test_external_model_load_raises(
+ mock_context: InvocationContext, mm2_model_manager: ModelManagerServiceBase
+) -> None:
+ config = ExternalApiModelConfig(
+ key="external_test",
+ name="External Test",
+ provider_id="openai",
+ provider_model_id="gpt-image-1",
+ capabilities=ExternalModelCapabilities(modes=["txt2img"]),
+ )
+ mm2_model_manager.store.add_model(config)
+
+ model_field = ModelIdentifierField.from_config(config)
+
+ with pytest.raises(ValueError, match="External API models"):
+ mock_context.models.load(model_field)
+
+ with pytest.raises(ValueError, match="External API models"):
+ mock_context.models.load_by_attrs(name=config.name, base=config.base, type=config.type)
+
+
def test_download_diffusers(mock_context: InvocationContext) -> None:
model_path = mock_context.models.download_and_cache_model("stabilityai/sdxl-turbo")
assert (model_path / "model_index.json").exists()
diff --git a/tests/backend/model_manager/test_external_api_config.py b/tests/backend/model_manager/test_external_api_config.py
new file mode 100644
index 00000000000..943a5a79918
--- /dev/null
+++ b/tests/backend/model_manager/test_external_api_config.py
@@ -0,0 +1,54 @@
+import pytest
+from pydantic import ValidationError
+
+from invokeai.backend.model_manager.configs.external_api import (
+ ExternalApiModelConfig,
+ ExternalApiModelDefaultSettings,
+ ExternalImageSize,
+ ExternalModelCapabilities,
+)
+
+
+def test_external_api_model_config_defaults() -> None:
+ capabilities = ExternalModelCapabilities(modes=["txt2img"], supports_seed=True)
+
+ config = ExternalApiModelConfig(
+ name="Test External",
+ provider_id="openai",
+ provider_model_id="gpt-image-1",
+ capabilities=capabilities,
+ )
+
+ assert config.path == "external://openai/gpt-image-1"
+ assert config.source == "external://openai/gpt-image-1"
+ assert config.hash == "external:openai:gpt-image-1"
+ assert config.file_size == 0
+ assert config.default_settings is None
+ assert config.capabilities.supports_seed is True
+
+
+def test_external_api_model_capabilities_allows_aspect_ratio_sizes() -> None:
+ capabilities = ExternalModelCapabilities(
+ modes=["txt2img"],
+ allowed_aspect_ratios=["1:1"],
+ aspect_ratio_sizes={"1:1": ExternalImageSize(width=1024, height=1024)},
+ )
+
+ assert capabilities.aspect_ratio_sizes is not None
+ assert capabilities.aspect_ratio_sizes["1:1"].width == 1024
+
+
+def test_external_api_model_config_rejects_extra_fields() -> None:
+ with pytest.raises(ValidationError):
+ ExternalModelCapabilities(modes=["txt2img"], supports_seed=True, extra_field=True) # type: ignore
+
+ with pytest.raises(ValidationError):
+ ExternalApiModelDefaultSettings(width=512, extra_field=True) # type: ignore
+
+
+def test_external_api_model_config_validates_limits() -> None:
+ with pytest.raises(ValidationError):
+ ExternalModelCapabilities(modes=["txt2img"], max_images_per_request=0)
+
+ with pytest.raises(ValidationError):
+ ExternalApiModelDefaultSettings(width=0)
diff --git a/tests/conftest.py b/tests/conftest.py
index 980a99611ab..bfd9f070df4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -17,6 +17,7 @@
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import ClientStatePersistenceSqlite
from invokeai.app.services.config.config_default import InvokeAIAppConfig
from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
+from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
from invokeai.app.services.images.images_default import ImageService
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
from invokeai.app.services.invocation_services import InvocationServices
@@ -52,6 +53,7 @@ def mock_services() -> InvocationServices:
model_images=None, # type: ignore
model_manager=None, # type: ignore
download_queue=None, # type: ignore
+ external_generation=ExternalGenerationService({}, logger),
names=None, # type: ignore
performance_statistics=InvocationStatsService(),
session_processor=None, # type: ignore
From 74ecc461b979fb9c212586a307f2c164b4573e15 Mon Sep 17 00:00:00 2001
From: CypherNaught-0x <9931495+CypherNaught-0x@users.noreply.github.com>
Date: Tue, 17 Feb 2026 12:13:50 +0100
Subject: [PATCH 02/44] feat: support reference images for external models
---
.../external_generation_default.py | 35 ++++++++++-
.../external_generation/providers/openai.py | 31 ++++++---
.../backend/model_manager/starter_models.py | 2 +-
.../components/RefImage/RefImagePreview.tsx | 12 ++--
.../components/RefImage/RefImageSettings.tsx | 13 ++--
.../controlLayers/store/validators.ts | 10 ++-
.../test_external_generation_service.py | 23 +++++++
.../test_external_provider_adapters.py | 63 +++++++++++++++++--
8 files changed, 160 insertions(+), 29 deletions(-)
diff --git a/invokeai/app/services/external_generation/external_generation_default.py b/invokeai/app/services/external_generation/external_generation_default.py
index c72e16cde8d..c96b5af711e 100644
--- a/invokeai/app/services/external_generation/external_generation_default.py
+++ b/invokeai/app/services/external_generation/external_generation_default.py
@@ -15,6 +15,7 @@
ExternalProvider,
)
from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGeneratedImage,
ExternalGenerationRequest,
ExternalGenerationResult,
ExternalProviderStatus,
@@ -37,10 +38,17 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
raise ExternalProviderNotConfiguredError(f"Provider '{request.model.provider_id}' is missing credentials")
request = self._refresh_model_capabilities(request)
+ resize_to_original_inpaint_size = _get_resize_target_for_inpaint(request)
request = self._bucket_request(request)
self._validate_request(request)
- return provider.generate(request)
+ result = provider.generate(request)
+
+ if resize_to_original_inpaint_size is None:
+ return result
+
+ width, height = resize_to_original_inpaint_size
+ return _resize_result_images(result, width, height)
def get_provider_statuses(self) -> dict[str, ExternalProviderStatus]:
return {provider_id: provider.get_status() for provider_id, provider in self._providers.items()}
@@ -276,6 +284,31 @@ def _resize_image(image: PILImageType | None, width: int, height: int, mode: str
return image.convert(mode).resize((width, height), Image.Resampling.LANCZOS)
+def _get_resize_target_for_inpaint(request: ExternalGenerationRequest) -> tuple[int, int] | None:
+ if request.mode != "inpaint" or request.init_image is None:
+ return None
+ return request.init_image.width, request.init_image.height
+
+
+def _resize_result_images(result: ExternalGenerationResult, width: int, height: int) -> ExternalGenerationResult:
+ resized_images = [
+ ExternalGeneratedImage(
+ image=generated.image
+ if generated.image.width == width and generated.image.height == height
+ else generated.image.resize((width, height), Image.Resampling.LANCZOS),
+ seed=generated.seed,
+ )
+ for generated in result.images
+ ]
+ return ExternalGenerationResult(
+ images=resized_images,
+ seed_used=result.seed_used,
+ provider_request_id=result.provider_request_id,
+ provider_metadata=result.provider_metadata,
+ content_filters=result.content_filters,
+ )
+
+
def _apply_starter_overrides(model: ExternalApiModelConfig) -> ExternalApiModelConfig:
source = model.source or f"external://{model.provider_id}/{model.provider_model_id}"
starter_match = next((starter for starter in STARTER_MODELS if starter.source == source), None)
diff --git a/invokeai/app/services/external_generation/providers/openai.py b/invokeai/app/services/external_generation/providers/openai.py
index e31a493b7a1..f06491a225b 100644
--- a/invokeai/app/services/external_generation/providers/openai.py
+++ b/invokeai/app/services/external_generation/providers/openai.py
@@ -3,6 +3,7 @@
import io
import requests
+from PIL.Image import Image as PILImageType
from invokeai.app.services.external_generation.errors import ExternalProviderRequestError
from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
@@ -29,7 +30,9 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
base_url = (self._app_config.external_openai_base_url or "https://api.openai.com").rstrip("/")
headers = {"Authorization": f"Bearer {api_key}"}
- if request.mode == "txt2img":
+ use_edits_endpoint = request.mode != "txt2img" or bool(request.reference_images)
+
+ if not use_edits_endpoint:
payload: dict[str, object] = {
"prompt": request.prompt,
"n": request.num_images,
@@ -45,20 +48,28 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
timeout=120,
)
else:
- files: dict[str, tuple[str, io.BytesIO, str]] = {}
- if request.init_image is None:
- raise ExternalProviderRequestError("OpenAI img2img/inpaint requires an init image")
-
- image_buffer = io.BytesIO()
- request.init_image.save(image_buffer, format="PNG")
- image_buffer.seek(0)
- files["image"] = ("image.png", image_buffer, "image/png")
+ images: list[PILImageType] = []
+ if request.init_image is not None:
+ images.append(request.init_image)
+ images.extend(reference.image for reference in request.reference_images)
+ if not images:
+ raise ExternalProviderRequestError(
+ "OpenAI image edits require at least one image (init image or reference image)"
+ )
+
+ files: list[tuple[str, tuple[str, io.BytesIO, str]]] = []
+ image_field_name = "image" if len(images) == 1 else "image[]"
+ for index, image in enumerate(images):
+ image_buffer = io.BytesIO()
+ image.save(image_buffer, format="PNG")
+ image_buffer.seek(0)
+ files.append((image_field_name, (f"image_{index}.png", image_buffer, "image/png")))
if request.mask_image is not None:
mask_buffer = io.BytesIO()
request.mask_image.save(mask_buffer, format="PNG")
mask_buffer.seek(0)
- files["mask"] = ("mask.png", mask_buffer, "image/png")
+ files.append(("mask", ("mask.png", mask_buffer, "image/png")))
data: dict[str, object] = {
"prompt": request.prompt,
diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py
index 59d7ceba205..183edc04ba7 100644
--- a/invokeai/backend/model_manager/starter_models.py
+++ b/invokeai/backend/model_manager/starter_models.py
@@ -964,7 +964,7 @@ class StarterModelBundle(BaseModel):
supports_negative_prompt=True,
supports_seed=True,
supports_guidance=True,
- supports_reference_images=False,
+ supports_reference_images=True,
max_images_per_request=1,
),
default_settings=ExternalApiModelDefaultSettings(width=1024, height=1024, num_images=1),
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx
index 84c1b2fc37b..ddbdb8b131c 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx
@@ -15,6 +15,7 @@ import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/va
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { PiExclamationMarkBold, PiEyeSlashBold, PiImageBold } from 'react-icons/pi';
import { useImageDTOFromCroppableImage } from 'services/api/endpoints/images';
+import { isExternalApiModelConfig } from 'services/api/types';
import { RefImageWarningTooltipContent } from './RefImageWarningTooltipContent';
@@ -71,18 +72,19 @@ export const RefImagePreview = memo(() => {
const selectedEntityId = useAppSelector(selectSelectedRefEntityId);
const isPanelOpen = useAppSelector(selectIsRefImagePanelOpen);
const [showWeightDisplay, setShowWeightDisplay] = useState(false);
+ const isExternalModel = !!mainModelConfig && isExternalApiModelConfig(mainModelConfig);
const imageDTO = useImageDTOFromCroppableImage(entity.config.image);
const sx = useMemo(() => {
- if (!isIPAdapterConfig(entity.config)) {
+ if (!isIPAdapterConfig(entity.config) || isExternalModel) {
return baseSx;
}
return getImageSxWithWeight(entity.config.weight);
- }, [entity.config]);
+ }, [entity.config, isExternalModel]);
useEffect(() => {
- if (!isIPAdapterConfig(entity.config)) {
+ if (!isIPAdapterConfig(entity.config) || isExternalModel) {
return;
}
setShowWeightDisplay(true);
@@ -92,7 +94,7 @@ export const RefImagePreview = memo(() => {
return () => {
window.clearTimeout(timeout);
};
- }, [entity.config]);
+ }, [entity.config, isExternalModel]);
const warnings = useMemo(() => {
return getGlobalReferenceImageWarnings(entity, mainModelConfig);
@@ -154,7 +156,7 @@ export const RefImagePreview = memo(() => {
) : (
)}
- {isIPAdapterConfig(entity.config) && (
+ {isIPAdapterConfig(entity.config) && !isExternalModel && (
{
const selectConfig = useMemo(() => buildSelectConfig(id), [id]);
const config = useAppSelector(selectConfig);
const tab = useAppSelector(selectActiveTab);
+ const mainModelConfig = useAppSelector(selectMainModelConfig);
const onChangeBeginEndStepPct = useCallback(
(beginEndStepPct: [number, number]) => {
@@ -120,9 +122,10 @@ const RefImageSettingsContent = memo(() => {
);
const isFLUX = useAppSelector(selectIsFLUX);
+ const isExternalModel = !!mainModelConfig && isExternalApiModelConfig(mainModelConfig);
- // FLUX.2 Klein has built-in reference image support - no model selector needed
- const showModelSelector = !isFlux2ReferenceImageConfig(config);
+ // FLUX.2 Klein and external API models do not require a ref image model selection.
+ const showModelSelector = !isFlux2ReferenceImageConfig(config) && !isExternalModel;
return (
@@ -150,14 +153,14 @@ const RefImageSettingsContent = memo(() => {
)}
- {isIPAdapterConfig(config) && (
+ {isIPAdapterConfig(config) && !isExternalModel && (
{!isFLUX && }
)}
- {isFLUXReduxConfig(config) && (
+ {isFLUXReduxConfig(config) && !isExternalModel && (
None:
assert response is result
assert provider.last_request == request
+
+
+def test_generate_resizes_inpaint_result_to_original_init_size() -> None:
+ model = _build_model(ExternalModelCapabilities(modes=["inpaint"]))
+ request = _build_request(
+ model=model,
+ mode="inpaint",
+ width=128,
+ height=128,
+ init_image=_make_image(),
+ mask_image=_make_image(),
+ )
+ generated_large = Image.new("RGB", (128, 128), color="black")
+ result = ExternalGenerationResult(images=[ExternalGeneratedImage(image=generated_large, seed=1)])
+ provider = DummyProvider("openai", configured=True, result=result)
+ service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
+
+ response = service.generate(request)
+
+ assert request.init_image is not None
+ assert response.images[0].image.width == request.init_image.width
+ assert response.images[0].image.height == request.init_image.height
+ assert response.images[0].seed == 1
diff --git a/tests/app/services/external_generation/test_external_provider_adapters.py b/tests/app/services/external_generation/test_external_provider_adapters.py
index 38f4c9e3d52..c4da4c913be 100644
--- a/tests/app/services/external_generation/test_external_provider_adapters.py
+++ b/tests/app/services/external_generation/test_external_provider_adapters.py
@@ -320,7 +320,13 @@ def test_openai_generate_inpaint_uses_edit_endpoint(monkeypatch: pytest.MonkeyPa
encoded = encode_image_base64(_make_image("orange"))
captured: dict[str, object] = {}
- def fake_post(url: str, headers: dict, data: dict, files: dict, timeout: int) -> DummyResponse:
+ def fake_post(
+ url: str,
+ headers: dict,
+ data: dict,
+ files: list[tuple[str, tuple[str, io.BytesIO, str]]],
+ timeout: int,
+ ) -> DummyResponse:
captured["url"] = url
captured["data"] = data
captured["files"] = files
@@ -336,11 +342,56 @@ def fake_post(url: str, headers: dict, data: dict, files: dict, timeout: int) ->
assert isinstance(data_payload, dict)
assert data_payload["prompt"] == request.prompt
files = captured["files"]
- assert isinstance(files, dict)
- assert "image" in files
- assert "mask" in files
- image_tuple = files["image"]
+ assert isinstance(files, list)
+ image_file = next((file for file in files if file[0] == "image"), None)
+ mask_file = next((file for file in files if file[0] == "mask"), None)
+ assert image_file is not None
+ assert mask_file is not None
+ image_tuple = image_file[1]
assert isinstance(image_tuple, tuple)
- assert image_tuple[0] == "image.png"
+ assert image_tuple[0] == "image_0.png"
assert isinstance(image_tuple[1], io.BytesIO)
assert result.images
+
+
+def test_openai_generate_txt2img_with_references_uses_edit_endpoint(monkeypatch: pytest.MonkeyPatch) -> None:
+ config = InvokeAIAppConfig(external_openai_api_key="openai-key")
+ provider = OpenAIProvider(config, logging.getLogger("test"))
+ model = _build_model("openai", "gpt-image-1")
+ request = _build_request(
+ model,
+ reference_images=[
+ ExternalReferenceImage(image=_make_image("red")),
+ ExternalReferenceImage(image=_make_image("blue")),
+ ],
+ )
+ encoded = encode_image_base64(_make_image("orange"))
+ captured: dict[str, object] = {}
+
+ def fake_post(
+ url: str,
+ headers: dict,
+ data: dict,
+ files: list[tuple[str, tuple[str, io.BytesIO, str]]],
+ timeout: int,
+ ) -> DummyResponse:
+ captured["url"] = url
+ captured["data"] = data
+ captured["files"] = files
+ return DummyResponse(ok=True, json_data={"data": [{"b64_json": encoded}]})
+
+ monkeypatch.setattr("requests.post", fake_post)
+
+ result = provider.generate(request)
+
+ assert captured["url"] == "https://api.openai.com/v1/images/edits"
+ data_payload = captured["data"]
+ assert isinstance(data_payload, dict)
+ assert data_payload["prompt"] == request.prompt
+ files = captured["files"]
+ assert isinstance(files, list)
+ image_files = [file for file in files if file[0] == "image[]"]
+ assert len(image_files) == 2
+ assert image_files[0][1][0] == "image_0.png"
+ assert image_files[1][1][0] == "image_1.png"
+ assert result.images
From a9d3b4e17c2440c89b629d1089872fe3ed0edb61 Mon Sep 17 00:00:00 2001
From: CypherNaught-0x <9931495+CypherNaught-0x@users.noreply.github.com>
Date: Tue, 17 Feb 2026 12:23:16 +0100
Subject: [PATCH 03/44] fix: sorting lint error
---
.../features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
index c54523e0fad..a35d865e752 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
@@ -11,10 +11,10 @@ import { filesize } from 'filesize';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import {
- isExternalApiModelConfig,
type AnyModelConfig,
type CLIPEmbedModelConfig,
type CLIPVisionModelConfig,
+ isExternalApiModelConfig,
type LlavaOnevisionModelConfig,
type Qwen3EncoderModelConfig,
type SigLIPModelConfig,
From 1b43769b951b1244604d391df198ce04e5b5a238 Mon Sep 17 00:00:00 2001
From: CypherNaught-0x <9931495+CypherNaught-0x@users.noreply.github.com>
Date: Fri, 27 Feb 2026 09:12:35 +0100
Subject: [PATCH 04/44] chore: hide Reidentify button for external models
---
.../subpanels/ModelPanel/ModelReidentifyButton.tsx | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx
index 31334c0510d..8ea5df310a7 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelReidentifyButton.tsx
@@ -4,7 +4,9 @@ import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiSparkleFill } from 'react-icons/pi';
import { useReidentifyModelMutation } from 'services/api/endpoints/models';
-import type { AnyModelConfig } from 'services/api/types';
+import { type AnyModelConfig, isExternalApiModelConfig } from 'services/api/types';
+
+import { isExternalModel } from './isExternalModel';
interface Props {
modelConfig: AnyModelConfig;
@@ -13,6 +15,7 @@ interface Props {
export const ModelReidentifyButton = memo(({ modelConfig }: Props) => {
const { t } = useTranslation();
const [reidentifyModel, { isLoading }] = useReidentifyModelMutation();
+ const isExternal = isExternalApiModelConfig(modelConfig) || isExternalModel(modelConfig.path);
const onClick = useCallback(() => {
reidentifyModel({ key: modelConfig.key })
@@ -40,6 +43,10 @@ export const ModelReidentifyButton = memo(({ modelConfig }: Props) => {
});
}, [modelConfig.key, reidentifyModel, t]);
+ if (isExternal) {
+ return null;
+ }
+
return (
-
- {t('modelManager.supportsNegativePrompt')}
-
-
{t('modelManager.supportsReferenceImages')}
@@ -253,10 +249,6 @@ export const ModelEdit = memo(({ modelConfig }: Props) => {
{t('modelManager.supportsSeed')}
-
- {t('modelManager.supportsGuidance')}
-
-
{t('modelManager.maxImagesPerRequest')}
= {}): ExternalApiModelConfig => {
const maxImageSize: ExternalImageSize = { width: 1024, height: 1024 };
- const defaultSettings: ExternalApiModelDefaultSettings = { width: 1024, height: 1024, steps: 30 };
+ const defaultSettings: ExternalApiModelDefaultSettings = { width: 1024, height: 1024 };
const capabilities: ExternalModelCapabilities = {
modes: ['txt2img'],
- supports_negative_prompt: true,
supports_reference_images: true,
supports_seed: true,
- supports_guidance: true,
- supports_steps: true,
max_image_size: maxImageSize,
};
@@ -54,7 +51,6 @@ const createExternalModel = (overrides: Partial = {}): E
let mockModelConfig: ExternalApiModelConfig | null = null;
let mockParams: ParamsState;
let mockRefImages: RefImagesState;
-let mockPrompts: { positive: string; negative: string };
let mockSizes: { scaledSize: { width: number; height: number } };
const mockOutputFields = {
@@ -80,7 +76,6 @@ vi.mock('features/nodes/util/graph/graphBuilderUtils', () => ({
rect: { x: 0, y: 0, width: 512, height: 512 },
}),
selectCanvasOutputFields: () => mockOutputFields,
- selectPresetModifiedPrompts: () => mockPrompts,
}));
beforeEach(() => {
@@ -88,7 +83,6 @@ beforeEach(() => {
steps: 20,
guidance: 4.5,
} as ParamsState;
- mockPrompts = { positive: 'a test prompt', negative: 'bad prompt' };
mockSizes = { scaledSize: { width: 768, height: 512 } };
const imageDTO = { image_name: 'ref.png', width: 64, height: 64 } as ImageDTO;
@@ -129,44 +123,14 @@ describe('buildExternalGraph', () => {
expect(externalNode?.mode).toBe('txt2img');
expect(externalNode?.width).toBe(768);
expect(externalNode?.height).toBe(512);
- expect(externalNode?.negative_prompt).toBe('bad prompt');
- expect(externalNode?.steps).toBe(20);
- expect(externalNode?.guidance).toBe(4.5);
expect((externalNode?.reference_images as Array<{ image_name: string }> | undefined)?.[0]).toEqual({
image_name: 'ref.png',
});
- expect(externalNode?.reference_image_weights).toEqual([0.5]);
const seedEdge = graph.edges.find((edge) => edge.destination.field === 'seed');
expect(seedEdge).toBeDefined();
});
- it('does not include steps when model does not support them', async () => {
- const modelConfig = createExternalModel({
- capabilities: {
- modes: ['txt2img'],
- supports_negative_prompt: true,
- supports_reference_images: true,
- supports_seed: true,
- supports_guidance: true,
- supports_steps: false,
- },
- });
- mockModelConfig = modelConfig;
-
- const { g } = await buildExternalGraph({
- generationMode: 'txt2img',
- state: {} as RootState,
- manager: null,
- });
- const graph = g.getGraph();
- const externalNode = Object.values(graph.nodes).find((node) => node.type === 'openai_image_generation') as
- | Record
- | undefined;
-
- expect(externalNode?.steps).toBeNull();
- });
-
it('prefers panel schema over capabilities when building node inputs', async () => {
const panelSchema: ExternalModelPanelSchema = {
prompts: [{ name: 'reference_images' }],
@@ -187,9 +151,6 @@ describe('buildExternalGraph', () => {
| Record
| undefined;
- expect(externalNode?.negative_prompt).toBeNull();
- expect(externalNode?.steps).toBeNull();
- expect(externalNode?.guidance).toBeNull();
expect((externalNode?.reference_images as Array<{ image_name: string }> | undefined)?.[0]).toEqual({
image_name: 'ref.png',
});
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
index 4f686a59868..0ba82234a66 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
@@ -8,7 +8,6 @@ import {
getOriginalAndScaledSizesForOtherModes,
getOriginalAndScaledSizesForTextToImage,
selectCanvasOutputFields,
- selectPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import {
type GraphBuilderArg,
@@ -44,13 +43,9 @@ export const buildExternalGraph = async (arg: GraphBuilderArg): Promise config.image)
.map((config) => zImageField.parse(config.image?.crop?.image ?? config.image?.original.image));
- const referenceWeights = refImages.entities
- .filter((entity) => entity.isEnabled)
- .map((entity) => entity.config)
- .filter((config) => config.image)
- .map((config) => (config.type === 'ip_adapter' ? config.weight : null));
-
if (referenceImages.length > 0) {
externalNode.reference_images = referenceImages;
- if (referenceWeights.every((weight): weight is number => weight !== null)) {
- externalNode.reference_image_weights = referenceWeights;
- }
}
}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx
index 1ae1dcdc3a8..8fb4e81b59d 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxAspectRatioSelect.test.tsx
@@ -22,9 +22,7 @@ const createExternalModel = (overrides: Partial = {}): E
capabilities: {
modes: ['txt2img'],
supports_reference_images: false,
- supports_negative_prompt: true,
supports_seed: true,
- supports_guidance: true,
max_images_per_request: 1,
max_image_size: null,
allowed_aspect_ratios: ['1:1', '16:9'],
diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamGuidance.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamGuidance.tsx
index 80e9d188b30..62fc5aa826c 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamGuidance.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamGuidance.tsx
@@ -1,8 +1,8 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
-import { selectGuidance, selectGuidanceControl, setGuidance } from 'features/controlLayers/store/paramsSlice';
-import { memo, useCallback, useMemo } from 'react';
+import { selectGuidance, setGuidance } from 'features/controlLayers/store/paramsSlice';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const CONSTRAINTS = {
@@ -23,22 +23,10 @@ export const MARKS = [
const ParamGuidance = () => {
const guidance = useAppSelector(selectGuidance);
- const externalControl = useAppSelector(selectGuidanceControl);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const onChange = useCallback((v: number) => dispatch(setGuidance(v)), [dispatch]);
- const sliderMin = externalControl?.slider_min ?? CONSTRAINTS.sliderMin;
- const sliderMax = externalControl?.slider_max ?? CONSTRAINTS.sliderMax;
- const numberInputMin = externalControl?.number_input_min ?? CONSTRAINTS.numberInputMin;
- const numberInputMax = externalControl?.number_input_max ?? CONSTRAINTS.numberInputMax;
- const fineStep = externalControl?.fine_step ?? CONSTRAINTS.fineStep;
- const coarseStep = externalControl?.coarse_step ?? CONSTRAINTS.coarseStep;
- const marks = useMemo(
- () => externalControl?.marks ?? [sliderMin, Math.floor(sliderMax - (sliderMax - sliderMin) / 2), sliderMax],
- [externalControl?.marks, sliderMin, sliderMax]
- );
-
return (
@@ -47,20 +35,20 @@ const ParamGuidance = () => {
diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx
index b6d810dd2e2..31efe5d0a6f 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx
@@ -1,8 +1,8 @@
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
-import { selectSteps, selectStepsControl, setSteps } from 'features/controlLayers/store/paramsSlice';
-import { memo, useCallback, useMemo } from 'react';
+import { selectSteps, setSteps } from 'features/controlLayers/store/paramsSlice';
+import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const CONSTRAINTS = {
@@ -19,7 +19,6 @@ export const MARKS = [CONSTRAINTS.sliderMin, Math.floor(CONSTRAINTS.sliderMax /
const ParamSteps = () => {
const steps = useAppSelector(selectSteps);
- const externalControl = useAppSelector(selectStepsControl);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const onChange = useCallback(
@@ -29,17 +28,6 @@ const ParamSteps = () => {
[dispatch]
);
- const sliderMin = externalControl?.slider_min ?? CONSTRAINTS.sliderMin;
- const sliderMax = externalControl?.slider_max ?? CONSTRAINTS.sliderMax;
- const numberInputMin = externalControl?.number_input_min ?? CONSTRAINTS.numberInputMin;
- const numberInputMax = externalControl?.number_input_max ?? CONSTRAINTS.numberInputMax;
- const fineStep = externalControl?.fine_step ?? CONSTRAINTS.fineStep;
- const coarseStep = externalControl?.coarse_step ?? CONSTRAINTS.coarseStep;
- const marks = useMemo(
- () => externalControl?.marks ?? [sliderMin, Math.floor(sliderMax / 2), sliderMax],
- [externalControl?.marks, sliderMin, sliderMax]
- );
-
return (
@@ -48,20 +36,20 @@ const ParamSteps = () => {
diff --git a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx b/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx
index 636260d1d25..0651c47863f 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Dimensions/DimensionsAspectRatioSelect.test.tsx
@@ -22,9 +22,7 @@ const createExternalModel = (overrides: Partial = {}): E
capabilities: {
modes: ['txt2img'],
supports_reference_images: false,
- supports_negative_prompt: true,
supports_seed: true,
- supports_guidance: true,
max_images_per_request: 1,
max_image_size: null,
allowed_aspect_ratios: ['1:1', '16:9'],
diff --git a/invokeai/frontend/web/src/features/parameters/components/External/GeminiProviderOptions.tsx b/invokeai/frontend/web/src/features/parameters/components/External/GeminiProviderOptions.tsx
new file mode 100644
index 00000000000..1ec27ef809d
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/External/GeminiProviderOptions.tsx
@@ -0,0 +1,74 @@
+import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel, Select } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import {
+ geminiTemperatureChanged,
+ geminiThinkingLevelChanged,
+ selectGeminiTemperature,
+ selectGeminiThinkingLevel,
+} from 'features/controlLayers/store/paramsSlice';
+import type { ChangeEventHandler } from 'react';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiCaretDownBold } from 'react-icons/pi';
+
+const TEMPERATURE_MARKS = [0, 1, 2];
+
+export const GeminiProviderOptions = memo(() => {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const temperature = useAppSelector(selectGeminiTemperature);
+ const thinkingLevel = useAppSelector(selectGeminiThinkingLevel);
+
+ const onTemperatureChange = useCallback((v: number) => dispatch(geminiTemperatureChanged(v)), [dispatch]);
+
+ const onThinkingLevelChange = useCallback>(
+ (e) => {
+ const value = e.target.value;
+ dispatch(geminiThinkingLevelChanged(value === '' ? null : (value as 'minimal' | 'high')));
+ },
+ [dispatch]
+ );
+
+ return (
+ <>
+
+ {t('parameters.temperature', 'Temperature')}
+
+
+
+
+ {t('parameters.thinkingLevel', 'Thinking Level')}
+ }
+ iconSize="0.75rem"
+ >
+
+
+
+
+
+ >
+ );
+});
+
+GeminiProviderOptions.displayName = 'GeminiProviderOptions';
diff --git a/invokeai/frontend/web/src/features/parameters/components/External/OpenAIProviderOptions.tsx b/invokeai/frontend/web/src/features/parameters/components/External/OpenAIProviderOptions.tsx
new file mode 100644
index 00000000000..f4eba05d84b
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/External/OpenAIProviderOptions.tsx
@@ -0,0 +1,84 @@
+import { FormControl, FormLabel, Select } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import {
+ openaiBackgroundChanged,
+ openaiInputFidelityChanged,
+ openaiQualityChanged,
+ selectOpenaiBackground,
+ selectOpenaiInputFidelity,
+ selectOpenaiQuality,
+} from 'features/controlLayers/store/paramsSlice';
+import type { ChangeEventHandler } from 'react';
+import { memo, useCallback } from 'react';
+import { useTranslation } from 'react-i18next';
+import { PiCaretDownBold } from 'react-icons/pi';
+
+export const OpenAIProviderOptions = memo(() => {
+ const { t } = useTranslation();
+ const dispatch = useAppDispatch();
+ const quality = useAppSelector(selectOpenaiQuality);
+ const background = useAppSelector(selectOpenaiBackground);
+ const inputFidelity = useAppSelector(selectOpenaiInputFidelity);
+
+ const onQualityChange = useCallback>(
+ (e) => dispatch(openaiQualityChanged(e.target.value as 'auto' | 'high' | 'medium' | 'low')),
+ [dispatch]
+ );
+
+ const onBackgroundChange = useCallback>(
+ (e) => dispatch(openaiBackgroundChanged(e.target.value as 'auto' | 'transparent' | 'opaque')),
+ [dispatch]
+ );
+
+ const onInputFidelityChange = useCallback>(
+ (e) => {
+ const value = e.target.value;
+ dispatch(openaiInputFidelityChanged(value === '' ? null : (value as 'low' | 'high')));
+ },
+ [dispatch]
+ );
+
+ return (
+ <>
+
+ {t('parameters.quality', 'Quality')}
+ } iconSize="0.75rem">
+
+
+
+
+
+
+
+ {t('parameters.background', 'Background')}
+ }
+ iconSize="0.75rem"
+ >
+
+
+
+
+
+
+ {t('parameters.inputFidelity', 'Input Fidelity')}
+ }
+ iconSize="0.75rem"
+ >
+
+
+
+
+
+ >
+ );
+});
+
+OpenAIProviderOptions.displayName = 'OpenAIProviderOptions';
diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts b/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts
index b908efa096e..7d72b529c2c 100644
--- a/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts
+++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/mainModelPickerUtils.test.ts
@@ -30,7 +30,6 @@ const createExternalConfig = (modes: ExternalModelCapabilities['modes']): Extern
provider_model_id: 'gpt-image-1',
capabilities: {
modes,
- supports_negative_prompt: true,
supports_reference_images: false,
max_image_size: maxImageSize,
},
diff --git a/invokeai/frontend/web/src/features/parameters/util/externalPanelSchema.ts b/invokeai/frontend/web/src/features/parameters/util/externalPanelSchema.ts
index 9c1296b1a54..c49190b225b 100644
--- a/invokeai/frontend/web/src/features/parameters/util/externalPanelSchema.ts
+++ b/invokeai/frontend/web/src/features/parameters/util/externalPanelSchema.ts
@@ -11,15 +11,9 @@ type ExternalPanelName = keyof ExternalModelPanelSchema;
const buildExternalPanelSchemaFromCapabilities = (
capabilities: ExternalModelCapabilities
): ExternalModelPanelSchema => ({
- prompts: [
- ...(capabilities.supports_negative_prompt ? [{ name: 'negative_prompt' as const }] : []),
- ...(capabilities.supports_reference_images ? [{ name: 'reference_images' as const }] : []),
- ],
+ prompts: [...(capabilities.supports_reference_images ? [{ name: 'reference_images' as const }] : [])],
image: [{ name: 'dimensions' }, ...(capabilities.supports_seed ? [{ name: 'seed' as const }] : [])],
- generation: [
- ...(capabilities.supports_steps ? [{ name: 'steps' as const }] : []),
- ...(capabilities.supports_guidance ? [{ name: 'guidance' as const }] : []),
- ],
+ generation: [],
});
const getExternalPanelSchema = (modelConfig: ExternalApiModelConfig): ExternalModelPanelSchema =>
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx
index deb3ea2c83f..2988acbd198 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/ExternalSettingsAccordion/ExternalSettingsAccordion.tsx
@@ -1,9 +1,11 @@
import type { FormLabelProps } from '@invoke-ai/ui-library';
import { Flex, FormControlGroup, StandaloneAccordion } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
-import { selectIsExternal } from 'features/controlLayers/store/paramsSlice';
+import { selectExternalProviderId, selectIsExternal } from 'features/controlLayers/store/paramsSlice';
import { ExternalModelImageSizeSelect } from 'features/parameters/components/Dimensions/ExternalModelImageSizeSelect';
import { ExternalModelResolutionSelect } from 'features/parameters/components/Dimensions/ExternalModelResolutionSelect';
+import { GeminiProviderOptions } from 'features/parameters/components/External/GeminiProviderOptions';
+import { OpenAIProviderOptions } from 'features/parameters/components/External/OpenAIProviderOptions';
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -15,6 +17,7 @@ const formLabelProps: FormLabelProps = {
export const ExternalSettingsAccordion = memo(() => {
const { t } = useTranslation();
const isExternal = useAppSelector(selectIsExternal);
+ const providerId = useAppSelector(selectExternalProviderId);
const { isOpen, onToggle } = useStandaloneAccordionToggle({
id: 'external-settings',
defaultIsOpen: true,
@@ -35,6 +38,8 @@ export const ExternalSettingsAccordion = memo(() => {
+ {providerId === 'openai' && }
+ {providerId === 'gemini' && }
diff --git a/tests/app/invocations/test_external_image_generation.py b/tests/app/invocations/test_external_image_generation.py
index 34a2d921625..9cd2919d46d 100644
--- a/tests/app/invocations/test_external_image_generation.py
+++ b/tests/app/invocations/test_external_image_generation.py
@@ -27,7 +27,6 @@ def _build_model() -> ExternalApiModelConfig:
capabilities=ExternalModelCapabilities(
modes=["txt2img"],
supports_reference_images=True,
- supports_negative_prompt=True,
supports_seed=True,
),
)
@@ -57,47 +56,22 @@ def test_external_invocation_builds_request_and_outputs() -> None:
model=model_field,
mode="txt2img",
prompt="A prompt",
- negative_prompt="bad",
seed=123,
num_images=1,
width=512,
height=512,
- steps=10,
- guidance=4.5,
reference_images=[ImageField(image_name="ref.png")],
- reference_image_weights=[0.6],
)
output = invocation.invoke(context)
request = context._services.external_generation.generate.call_args[0][0]
assert request.prompt == "A prompt"
- assert request.negative_prompt == "bad"
assert request.seed == 123
assert len(request.reference_images) == 1
- assert request.reference_images[0].weight == 0.6
assert output.collection[0].image_name == "result.png"
-def test_external_invocation_rejects_mismatched_reference_weights() -> None:
- model_config = _build_model()
- model_field = ModelIdentifierField.from_config(model_config)
- generated_image = Image.new("RGB", (16, 16), color="black")
- context = _build_context(model_config, generated_image)
-
- invocation = ExternalImageGenerationInvocation(
- id="external_node",
- model=model_field,
- mode="txt2img",
- prompt="A prompt",
- reference_images=[ImageField(image_name="ref.png")],
- reference_image_weights=[0.1, 0.2],
- )
-
- with pytest.raises(ValueError, match="reference_image_weights"):
- invocation.invoke(context)
-
-
def test_provider_specific_external_invocation_rejects_wrong_provider() -> None:
model_config = _build_model().model_copy(update={"provider_id": "gemini"})
model_field = ModelIdentifierField.from_config(model_config)
diff --git a/tests/app/routers/test_model_manager.py b/tests/app/routers/test_model_manager.py
index 771790ee9ca..3260b539bb3 100644
--- a/tests/app/routers/test_model_manager.py
+++ b/tests/app/routers/test_model_manager.py
@@ -83,9 +83,9 @@ def test_model_manager_external_config_preserves_custom_panel_schema(
name="External Custom Schema",
provider_id="custom",
provider_model_id="custom-model",
- capabilities=ExternalModelCapabilities(modes=["txt2img"], supports_negative_prompt=True),
+ capabilities=ExternalModelCapabilities(modes=["txt2img"]),
panel_schema=ExternalModelPanelSchema(
- prompts=[{"name": "negative_prompt"}],
+ prompts=[{"name": "reference_images"}],
image=[{"name": "dimensions"}],
),
source="external://custom/custom-model",
@@ -104,7 +104,7 @@ def test_model_manager_external_config_preserves_custom_panel_schema(
assert response.status_code == 200
payload = response.json()
- assert [control["name"] for control in payload["panel_schema"]["prompts"]] == ["negative_prompt"]
+ assert [control["name"] for control in payload["panel_schema"]["prompts"]] == ["reference_images"]
assert [control["name"] for control in payload["panel_schema"]["image"]] == ["dimensions"]
@@ -118,10 +118,7 @@ def test_model_manager_external_starter_model_applies_panel_schema_overrides(
provider_model_id="gpt-image-1",
capabilities=ExternalModelCapabilities(
modes=["txt2img"],
- supports_negative_prompt=True,
supports_reference_images=False,
- supports_guidance=True,
- supports_steps=True,
),
)
mm2_model_manager.store.add_model(config)
diff --git a/tests/app/services/external_generation/test_external_generation_service.py b/tests/app/services/external_generation/test_external_generation_service.py
index 4ad0899c9c1..927f60b6eef 100644
--- a/tests/app/services/external_generation/test_external_generation_service.py
+++ b/tests/app/services/external_generation/test_external_generation_service.py
@@ -55,10 +55,8 @@ def _build_request(
*,
model: ExternalApiModelConfig,
mode: str = "txt2img",
- negative_prompt: str | None = None,
seed: int | None = None,
num_images: int = 1,
- guidance: float | None = None,
width: int = 64,
height: int = 64,
init_image: Image.Image | None = None,
@@ -69,14 +67,11 @@ def _build_request(
model=model,
mode=mode, # type: ignore[arg-type]
prompt="A test prompt",
- negative_prompt=negative_prompt,
seed=seed,
num_images=num_images,
width=width,
height=height,
image_size=None,
- steps=10,
- guidance=guidance,
init_image=init_image,
mask_image=mask_image,
reference_images=reference_images or [],
@@ -117,16 +112,6 @@ def test_generate_validates_mode_support() -> None:
service.generate(request)
-def test_generate_validates_negative_prompt_support() -> None:
- model = _build_model(ExternalModelCapabilities(modes=["txt2img"], supports_negative_prompt=False))
- request = _build_request(model=model, negative_prompt="bad")
- provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
- service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
-
- with pytest.raises(ExternalProviderCapabilityError, match="Negative prompts"):
- service.generate(request)
-
-
def test_generate_requires_init_image_for_img2img() -> None:
model = _build_model(ExternalModelCapabilities(modes=["img2img"]))
request = _build_request(model=model, mode="img2img")
@@ -151,7 +136,7 @@ def test_generate_validates_reference_images() -> None:
model = _build_model(ExternalModelCapabilities(modes=["txt2img"], supports_reference_images=False))
request = _build_request(
model=model,
- reference_images=[ExternalReferenceImage(image=_make_image(), weight=0.8)],
+ reference_images=[ExternalReferenceImage(image=_make_image())],
)
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
@@ -231,9 +216,9 @@ def test_generate_validates_allowed_aspect_ratios_with_bucket_sizes() -> None:
def test_generate_happy_path() -> None:
model = _build_model(
- ExternalModelCapabilities(modes=["txt2img"], supports_negative_prompt=True, supports_seed=True)
+ ExternalModelCapabilities(modes=["txt2img"], supports_seed=True)
)
- request = _build_request(model=model, negative_prompt="", seed=42)
+ request = _build_request(model=model, seed=42)
result = ExternalGenerationResult(images=[ExternalGeneratedImage(image=_make_image(), seed=42)])
provider = DummyProvider("openai", configured=True, result=result)
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
diff --git a/tests/app/services/external_generation/test_external_provider_adapters.py b/tests/app/services/external_generation/test_external_provider_adapters.py
index a7493ec0567..a64f197b4dd 100644
--- a/tests/app/services/external_generation/test_external_provider_adapters.py
+++ b/tests/app/services/external_generation/test_external_provider_adapters.py
@@ -40,10 +40,8 @@ def _build_model(provider_id: str, provider_model_id: str) -> ExternalApiModelCo
provider_model_id=provider_model_id,
capabilities=ExternalModelCapabilities(
modes=["txt2img", "img2img", "inpaint"],
- supports_negative_prompt=True,
supports_reference_images=True,
supports_seed=True,
- supports_guidance=True,
),
)
@@ -59,14 +57,11 @@ def _build_request(
model=model,
mode=mode, # type: ignore[arg-type]
prompt="A test prompt",
- negative_prompt="",
seed=123,
num_images=1,
width=256,
height=256,
image_size=None,
- steps=20,
- guidance=5.5,
init_image=init_image,
mask_image=mask_image,
reference_images=reference_images or [],
@@ -84,7 +79,7 @@ def test_gemini_generate_success(monkeypatch: pytest.MonkeyPatch) -> None:
request = _build_request(
model,
init_image=init_image,
- reference_images=[ExternalReferenceImage(image=ref_image, weight=0.6)],
+ reference_images=[ExternalReferenceImage(image=ref_image)],
)
encoded = encode_image_base64(_make_image("green"))
captured: dict[str, object] = {}
From a1eef791a14a1151f22750a5c105d046bd13dc0d Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Fri, 20 Mar 2026 10:01:49 +0100
Subject: [PATCH 24/44] feat: add Alibaba Cloud DashScope external image
generation provider
Add AlibabaCloudProvider supporting Qwen Image and Wan model families
via the DashScope API. Includes sync (multimodal-generation) and async
(image-generation with task polling) request modes, five starter models
(Qwen Image 2.0 Pro, 2.0, Max, Wan 2.6 T2I, Qwen Image Edit Max),
config fields for API key and base URL, and frontend registration.
---
invokeai/app/api/dependencies.py | 3 +-
invokeai/app/api/routers/app_info.py | 1 +
.../app/services/config/config_default.py | 8 +-
.../external_generation/providers/__init__.py | 3 +-
.../providers/alibabacloud.py | 309 ++++++++++++++++++
.../backend/model_manager/starter_models.py | 129 ++++++++
.../ExternalProvidersForm.tsx | 2 +-
7 files changed, 451 insertions(+), 4 deletions(-)
create mode 100644 invokeai/app/services/external_generation/providers/alibabacloud.py
diff --git a/invokeai/app/api/dependencies.py b/invokeai/app/api/dependencies.py
index 65171bf52ff..856dcb7d1a0 100644
--- a/invokeai/app/api/dependencies.py
+++ b/invokeai/app/api/dependencies.py
@@ -17,7 +17,7 @@
from invokeai.app.services.download.download_default import DownloadQueueService
from invokeai.app.services.events.events_fastapievents import FastAPIEventService
from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
-from invokeai.app.services.external_generation.providers import GeminiProvider, OpenAIProvider
+from invokeai.app.services.external_generation.providers import AlibabaCloudProvider, GeminiProvider, OpenAIProvider
from invokeai.app.services.external_generation.startup import sync_configured_external_starter_models
from invokeai.app.services.image_files.image_files_disk import DiskImageFileStorage
from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
@@ -157,6 +157,7 @@ def initialize(
)
external_generation = ExternalGenerationService(
providers={
+ AlibabaCloudProvider.provider_id: AlibabaCloudProvider(app_config=configuration, logger=logger),
GeminiProvider.provider_id: GeminiProvider(app_config=configuration, logger=logger),
OpenAIProvider.provider_id: OpenAIProvider(app_config=configuration, logger=logger),
},
diff --git a/invokeai/app/api/routers/app_info.py b/invokeai/app/api/routers/app_info.py
index 1d68bcc3b8d..ea3ca480b9a 100644
--- a/invokeai/app/api/routers/app_info.py
+++ b/invokeai/app/api/routers/app_info.py
@@ -96,6 +96,7 @@ class ExternalProviderConfigModel(BaseModel):
EXTERNAL_PROVIDER_FIELDS: dict[str, tuple[str, str]] = {
+ "alibabacloud": ("external_alibabacloud_api_key", "external_alibabacloud_base_url"),
"gemini": ("external_gemini_api_key", "external_gemini_base_url"),
"openai": ("external_openai_api_key", "external_openai_base_url"),
}
diff --git a/invokeai/app/services/config/config_default.py b/invokeai/app/services/config/config_default.py
index 704dccb7c13..1351a94a7bc 100644
--- a/invokeai/app/services/config/config_default.py
+++ b/invokeai/app/services/config/config_default.py
@@ -32,9 +32,11 @@
LOG_LEVEL = Literal["debug", "info", "warning", "error", "critical"]
CONFIG_SCHEMA_VERSION = "4.0.2"
EXTERNAL_PROVIDER_CONFIG_FIELDS = (
+ "external_alibabacloud_api_key",
+ "external_alibabacloud_base_url",
"external_gemini_api_key",
- "external_openai_api_key",
"external_gemini_base_url",
+ "external_openai_api_key",
"external_openai_base_url",
)
@@ -221,6 +223,10 @@ class InvokeAIAppConfig(BaseSettings):
strict_password_checking: bool = Field(default=False, description="Enforce strict password requirements. When True, passwords must contain uppercase, lowercase, and numbers. When False (default), any password is accepted but its strength (weak/moderate/strong) is reported to the user.")
# EXTERNAL PROVIDERS
+ external_alibabacloud_api_key: Optional[str] = Field(default=None, description="API key for Alibaba Cloud DashScope image generation.")
+ external_alibabacloud_base_url: Optional[str] = Field(
+ default=None, description="Base URL override for Alibaba Cloud DashScope image generation."
+ )
external_gemini_api_key: Optional[str] = Field(default=None, description="API key for Gemini image generation.")
external_openai_api_key: Optional[str] = Field(default=None, description="API key for OpenAI image generation.")
external_gemini_base_url: Optional[str] = Field(
diff --git a/invokeai/app/services/external_generation/providers/__init__.py b/invokeai/app/services/external_generation/providers/__init__.py
index 9e380fca1e1..065d60fbcec 100644
--- a/invokeai/app/services/external_generation/providers/__init__.py
+++ b/invokeai/app/services/external_generation/providers/__init__.py
@@ -1,4 +1,5 @@
+from invokeai.app.services.external_generation.providers.alibabacloud import AlibabaCloudProvider
from invokeai.app.services.external_generation.providers.gemini import GeminiProvider
from invokeai.app.services.external_generation.providers.openai import OpenAIProvider
-__all__ = ["GeminiProvider", "OpenAIProvider"]
+__all__ = ["AlibabaCloudProvider", "GeminiProvider", "OpenAIProvider"]
diff --git a/invokeai/app/services/external_generation/providers/alibabacloud.py b/invokeai/app/services/external_generation/providers/alibabacloud.py
new file mode 100644
index 00000000000..b8e928ce1fe
--- /dev/null
+++ b/invokeai/app/services/external_generation/providers/alibabacloud.py
@@ -0,0 +1,309 @@
+from __future__ import annotations
+
+import io
+import time
+
+import requests
+from PIL import Image
+from PIL.Image import Image as PILImageType
+
+from invokeai.app.services.external_generation.errors import ExternalProviderRequestError
+from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
+from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGeneratedImage,
+ ExternalGenerationRequest,
+ ExternalGenerationResult,
+)
+from invokeai.app.services.external_generation.image_utils import decode_image_base64, encode_image_base64
+
+# Models that support the synchronous multimodal-generation endpoint with messages format
+_SYNC_MODELS = {
+ "qwen-image-2.0-pro",
+ "qwen-image-2.0",
+ "qwen-image-max",
+ "wan2.6-t2i",
+ "wan2.6-image",
+ "qwen-image-edit-max",
+}
+
+# Models that use the async image-generation endpoint with flat prompt format
+_ASYNC_MODELS = {
+ "qwen-image-plus",
+ "qwen-image",
+ "qwen-image-edit-plus",
+ "qwen-image-edit",
+ "wan2.5-t2i-preview",
+ "wan2.2-t2i-flash",
+ "wanx2.0-t2i-turbo",
+}
+
+# Models that support image editing (accept input images)
+_EDIT_MODELS = {
+ "wan2.6-image",
+ "qwen-image-edit-max",
+ "qwen-image-edit-plus",
+ "qwen-image-edit",
+}
+
+_TASK_POLL_INTERVAL = 5 # seconds
+_TASK_POLL_TIMEOUT = 300 # seconds
+
+
+class AlibabaCloudProvider(ExternalProvider):
+ provider_id = "alibabacloud"
+
+ def is_configured(self) -> bool:
+ return bool(self._app_config.external_alibabacloud_api_key)
+
+ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
+ api_key = self._app_config.external_alibabacloud_api_key
+ if not api_key:
+ raise ExternalProviderRequestError("Alibaba Cloud DashScope API key is not configured")
+
+ base_url = (
+ self._app_config.external_alibabacloud_base_url or "https://dashscope-intl.aliyuncs.com"
+ ).rstrip("/")
+ model_id = request.model.provider_model_id
+ headers = {
+ "Content-Type": "application/json",
+ "Authorization": f"Bearer {api_key}",
+ }
+ size = f"{request.width}*{request.height}"
+
+ if model_id in _SYNC_MODELS or model_id not in _ASYNC_MODELS:
+ return self._generate_sync(request, base_url, headers, model_id, size)
+ else:
+ return self._generate_async(request, base_url, headers, model_id, size)
+
+ def _generate_sync(
+ self,
+ request: ExternalGenerationRequest,
+ base_url: str,
+ headers: dict[str, str],
+ model_id: str,
+ size: str,
+ ) -> ExternalGenerationResult:
+ """Use the synchronous multimodal-generation endpoint (messages format)."""
+ endpoint = f"{base_url}/api/v1/services/aigc/multimodal-generation/generation"
+
+ content: list[dict[str, str]] = []
+
+ # Add init image for editing
+ if request.init_image is not None and model_id in _EDIT_MODELS:
+ content.append({"image": f"data:image/png;base64,{encode_image_base64(request.init_image)}"})
+
+ # Add reference images
+ for ref in request.reference_images:
+ content.append({"image": f"data:image/png;base64,{encode_image_base64(ref.image)}"})
+
+ content.append({"text": request.prompt})
+
+ parameters: dict[str, object] = {
+ "size": size,
+ "n": request.num_images,
+ "prompt_extend": False,
+ "watermark": False,
+ }
+ if request.negative_prompt:
+ parameters["negative_prompt"] = request.negative_prompt
+ if request.seed is not None:
+ parameters["seed"] = request.seed
+
+ payload: dict[str, object] = {
+ "model": model_id,
+ "input": {
+ "messages": [
+ {
+ "role": "user",
+ "content": content,
+ }
+ ]
+ },
+ "parameters": parameters,
+ }
+
+ response = requests.post(endpoint, headers=headers, json=payload, timeout=120)
+
+ if not response.ok:
+ raise ExternalProviderRequestError(
+ f"DashScope request failed with status {response.status_code} for model '{model_id}': {response.text}"
+ )
+
+ data = response.json()
+ request_id = data.get("request_id")
+ return self._parse_sync_response(data, request, request_id)
+
+ def _generate_async(
+ self,
+ request: ExternalGenerationRequest,
+ base_url: str,
+ headers: dict[str, str],
+ model_id: str,
+ size: str,
+ ) -> ExternalGenerationResult:
+ """Use the async image-generation endpoint (flat prompt format) with task polling."""
+ endpoint = f"{base_url}/api/v1/services/aigc/image-generation/generation"
+ async_headers = {**headers, "X-DashScope-Async": "enable"}
+
+ parameters: dict[str, object] = {
+ "size": size,
+ "n": request.num_images,
+ "prompt_extend": False,
+ "watermark": False,
+ }
+ if request.negative_prompt:
+ parameters["negative_prompt"] = request.negative_prompt
+ if request.seed is not None:
+ parameters["seed"] = request.seed
+
+ input_data: dict[str, object] = {"prompt": request.prompt}
+ if request.negative_prompt:
+ input_data["negative_prompt"] = request.negative_prompt
+
+ payload: dict[str, object] = {
+ "model": model_id,
+ "input": input_data,
+ "parameters": parameters,
+ }
+
+ response = requests.post(endpoint, headers=async_headers, json=payload, timeout=60)
+
+ if not response.ok:
+ raise ExternalProviderRequestError(
+ f"DashScope async request failed with status {response.status_code} for model '{model_id}': {response.text}"
+ )
+
+ data = response.json()
+ request_id = data.get("request_id")
+ output = data.get("output", {})
+ task_id = output.get("task_id")
+
+ if not task_id:
+ raise ExternalProviderRequestError(f"DashScope async response missing task_id: {data}")
+
+ return self._poll_task(base_url, headers, task_id, request, request_id)
+
+ def _poll_task(
+ self,
+ base_url: str,
+ headers: dict[str, str],
+ task_id: str,
+ request: ExternalGenerationRequest,
+ request_id: str | None,
+ ) -> ExternalGenerationResult:
+ """Poll an async task until completion."""
+ task_url = f"{base_url}/api/v1/tasks/{task_id}"
+ start_time = time.monotonic()
+
+ while True:
+ elapsed = time.monotonic() - start_time
+ if elapsed > _TASK_POLL_TIMEOUT:
+ raise ExternalProviderRequestError(
+ f"DashScope task {task_id} timed out after {_TASK_POLL_TIMEOUT}s"
+ )
+
+ time.sleep(_TASK_POLL_INTERVAL)
+
+ response = requests.get(task_url, headers={"Authorization": headers["Authorization"]}, timeout=30)
+ if not response.ok:
+ raise ExternalProviderRequestError(
+ f"DashScope task poll failed with status {response.status_code}: {response.text}"
+ )
+
+ data = response.json()
+ output = data.get("output", {})
+ status = output.get("task_status")
+
+ if status == "SUCCEEDED":
+ return self._parse_async_response(output, request, request_id)
+ elif status in ("FAILED", "UNKNOWN"):
+ message = output.get("message", "Unknown error")
+ raise ExternalProviderRequestError(f"DashScope task {task_id} failed: {message}")
+
+ self._logger.debug("DashScope task %s status: %s (%.0fs elapsed)", task_id, status, elapsed)
+
+ def _parse_sync_response(
+ self,
+ data: dict[str, object],
+ request: ExternalGenerationRequest,
+ request_id: str | None,
+ ) -> ExternalGenerationResult:
+ """Parse the synchronous multimodal-generation response."""
+ output = data.get("output")
+ if not isinstance(output, dict):
+ raise ExternalProviderRequestError(f"DashScope response missing output: {data}")
+
+ choices = output.get("choices")
+ if not isinstance(choices, list):
+ raise ExternalProviderRequestError(f"DashScope response missing choices: {data}")
+
+ images: list[ExternalGeneratedImage] = []
+ for choice in choices:
+ if not isinstance(choice, dict):
+ continue
+ message = choice.get("message")
+ if not isinstance(message, dict):
+ continue
+ content = message.get("content")
+ if not isinstance(content, list):
+ continue
+ for part in content:
+ if not isinstance(part, dict):
+ continue
+ image_url = part.get("image")
+ if isinstance(image_url, str) and image_url:
+ pil_image = self._download_image(image_url)
+ images.append(ExternalGeneratedImage(image=pil_image, seed=request.seed))
+
+ if not images:
+ raise ExternalProviderRequestError(f"DashScope response contained no images: {data}")
+
+ return ExternalGenerationResult(
+ images=images,
+ seed_used=request.seed,
+ provider_request_id=request_id,
+ provider_metadata={"model": request.model.provider_model_id},
+ )
+
+ def _parse_async_response(
+ self,
+ output: dict[str, object],
+ request: ExternalGenerationRequest,
+ request_id: str | None,
+ ) -> ExternalGenerationResult:
+ """Parse the async task completion response."""
+ results = output.get("results")
+ if not isinstance(results, list):
+ raise ExternalProviderRequestError(f"DashScope async response missing results: {output}")
+
+ images: list[ExternalGeneratedImage] = []
+ for result in results:
+ if not isinstance(result, dict):
+ continue
+ url = result.get("url")
+ if isinstance(url, str) and url:
+ pil_image = self._download_image(url)
+ images.append(ExternalGeneratedImage(image=pil_image, seed=request.seed))
+ b64_image = result.get("b64_image")
+ if isinstance(b64_image, str) and b64_image:
+ pil_image = decode_image_base64(b64_image)
+ images.append(ExternalGeneratedImage(image=pil_image, seed=request.seed))
+
+ if not images:
+ raise ExternalProviderRequestError(f"DashScope async response contained no images: {output}")
+
+ return ExternalGenerationResult(
+ images=images,
+ seed_used=request.seed,
+ provider_request_id=request_id,
+ provider_metadata={"model": request.model.provider_model_id},
+ )
+
+ def _download_image(self, url: str) -> PILImageType:
+ """Download an image from a URL and return it as a PIL Image."""
+ response = requests.get(url, timeout=60)
+ if not response.ok:
+ raise ExternalProviderRequestError(
+ f"Failed to download image from DashScope (status {response.status_code})"
+ )
+ return Image.open(io.BytesIO(response.content)).convert("RGB")
diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py
index 0d6671cf7ed..952a14bbddf 100644
--- a/invokeai/backend/model_manager/starter_models.py
+++ b/invokeai/backend/model_manager/starter_models.py
@@ -974,6 +974,130 @@ class StarterModelBundle(BaseModel):
default_settings=ExternalApiModelDefaultSettings(width=1024, height=1024, num_images=1),
panel_schema=ExternalModelPanelSchema(prompts=[{"name": "reference_images"}], image=[{"name": "dimensions"}]),
)
+QWEN_IMAGE_2_ALLOWED_ASPECT_RATIOS = ["1:1", "4:3", "3:4", "16:9", "9:16"]
+QWEN_IMAGE_MAX_ALLOWED_ASPECT_RATIOS = ["1:1", "4:3", "3:4", "16:9", "9:16"]
+WAN_V2_ALLOWED_ASPECT_RATIOS = ["1:1", "4:3", "3:4", "16:9", "9:16"]
+
+alibabacloud_qwen_image_2_pro = StarterModel(
+ name="Qwen Image 2.0 Pro",
+ base=BaseModelType.External,
+ source="external://alibabacloud/qwen-image-2.0-pro",
+ description="Alibaba Cloud Qwen Image 2.0 Pro model (external API). Best quality text-to-image with excellent bilingual text rendering. Requires a configured Alibaba Cloud DashScope API key and may incur provider usage costs.",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ max_images_per_request=4,
+ allowed_aspect_ratios=QWEN_IMAGE_2_ALLOWED_ASPECT_RATIOS,
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=2048, height=2048),
+ "4:3": ExternalImageSize(width=2368, height=1728),
+ "3:4": ExternalImageSize(width=1728, height=2368),
+ "16:9": ExternalImageSize(width=2688, height=1536),
+ "9:16": ExternalImageSize(width=1536, height=2688),
+ },
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=2048, height=2048, num_images=1),
+ panel_schema=ExternalModelPanelSchema(image=[{"name": "dimensions"}]),
+)
+alibabacloud_qwen_image_2 = StarterModel(
+ name="Qwen Image 2.0",
+ base=BaseModelType.External,
+ source="external://alibabacloud/qwen-image-2.0",
+ description="Alibaba Cloud Qwen Image 2.0 model (external API). Fast text-to-image with good bilingual text rendering. Requires a configured Alibaba Cloud DashScope API key and may incur provider usage costs.",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ max_images_per_request=4,
+ allowed_aspect_ratios=QWEN_IMAGE_2_ALLOWED_ASPECT_RATIOS,
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=2048, height=2048),
+ "4:3": ExternalImageSize(width=2368, height=1728),
+ "3:4": ExternalImageSize(width=1728, height=2368),
+ "16:9": ExternalImageSize(width=2688, height=1536),
+ "9:16": ExternalImageSize(width=1536, height=2688),
+ },
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=2048, height=2048, num_images=1),
+ panel_schema=ExternalModelPanelSchema(image=[{"name": "dimensions"}]),
+)
+alibabacloud_qwen_image_max = StarterModel(
+ name="Qwen Image Max",
+ base=BaseModelType.External,
+ source="external://alibabacloud/qwen-image-max",
+ description="Alibaba Cloud Qwen Image Max model (external API). High quality text-to-image generation. Requires a configured Alibaba Cloud DashScope API key and may incur provider usage costs.",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ max_images_per_request=4,
+ allowed_aspect_ratios=QWEN_IMAGE_MAX_ALLOWED_ASPECT_RATIOS,
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=1328, height=1328),
+ "4:3": ExternalImageSize(width=1472, height=1104),
+ "3:4": ExternalImageSize(width=1104, height=1472),
+ "16:9": ExternalImageSize(width=1664, height=928),
+ "9:16": ExternalImageSize(width=928, height=1664),
+ },
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=1328, height=1328, num_images=1),
+ panel_schema=ExternalModelPanelSchema(image=[{"name": "dimensions"}]),
+)
+alibabacloud_wan26_t2i = StarterModel(
+ name="Wan 2.6 Text-to-Image",
+ base=BaseModelType.External,
+ source="external://alibabacloud/wan2.6-t2i",
+ description="Alibaba Cloud Wan 2.6 text-to-image model (external API). Photorealistic image generation. Requires a configured Alibaba Cloud DashScope API key and may incur provider usage costs.",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["txt2img"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ max_images_per_request=4,
+ allowed_aspect_ratios=WAN_V2_ALLOWED_ASPECT_RATIOS,
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=1024, height=1024),
+ "4:3": ExternalImageSize(width=1440, height=1080),
+ "3:4": ExternalImageSize(width=1080, height=1440),
+ "16:9": ExternalImageSize(width=1440, height=810),
+ "9:16": ExternalImageSize(width=810, height=1440),
+ },
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=1024, height=1024, num_images=1),
+ panel_schema=ExternalModelPanelSchema(image=[{"name": "dimensions"}]),
+)
+alibabacloud_qwen_image_edit_max = StarterModel(
+ name="Qwen Image Edit Max",
+ base=BaseModelType.External,
+ source="external://alibabacloud/qwen-image-edit-max",
+ description="Alibaba Cloud Qwen Image Edit Max model (external API). Image editing with industrial design and geometric reasoning. Requires a configured Alibaba Cloud DashScope API key and may incur provider usage costs.",
+ type=ModelType.ExternalImageGenerator,
+ format=ModelFormat.ExternalApi,
+ capabilities=ExternalModelCapabilities(
+ modes=["img2img"],
+ supports_negative_prompt=True,
+ supports_seed=True,
+ max_images_per_request=4,
+ allowed_aspect_ratios=QWEN_IMAGE_2_ALLOWED_ASPECT_RATIOS,
+ aspect_ratio_sizes={
+ "1:1": ExternalImageSize(width=2048, height=2048),
+ "4:3": ExternalImageSize(width=2368, height=1728),
+ "3:4": ExternalImageSize(width=1728, height=2368),
+ "16:9": ExternalImageSize(width=2688, height=1536),
+ "9:16": ExternalImageSize(width=1536, height=2688),
+ },
+ ),
+ default_settings=ExternalApiModelDefaultSettings(width=2048, height=2048, num_images=1),
+ panel_schema=ExternalModelPanelSchema(image=[{"name": "dimensions"}]),
+)
openai_gpt_image_1 = StarterModel(
name="ChatGPT Image",
base=BaseModelType.External,
@@ -1093,6 +1217,11 @@ class StarterModelBundle(BaseModel):
gemini_pro_image_preview,
gemini_3_1_flash_image_preview,
openai_gpt_image_1,
+ alibabacloud_qwen_image_2_pro,
+ alibabacloud_qwen_image_2,
+ alibabacloud_qwen_image_max,
+ alibabacloud_wan26_t2i,
+ alibabacloud_qwen_image_edit_max,
]
sd1_bundle: list[StarterModel] = [
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx
index 59fb868a50d..3e5c5d77e79 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ExternalProviders/ExternalProvidersForm.tsx
@@ -28,7 +28,7 @@ import {
import { useGetStarterModelsQuery } from 'services/api/endpoints/models';
import type { ExternalProviderConfig, StarterModel } from 'services/api/types';
-const PROVIDER_SORT_ORDER = ['gemini', 'openai'];
+const PROVIDER_SORT_ORDER = ['gemini', 'openai', 'alibabacloud'];
type ProviderCardProps = {
provider: ExternalProviderConfig;
From 18315db7f0173b50f3886773797ad6ae892bede2 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Sat, 28 Mar 2026 14:50:57 +0100
Subject: [PATCH 25/44] Chore Ruff check & format
---
invokeai/app/api/routers/app_info.py | 4 +-
.../services/external_generation/__init__.py | 2 +-
.../external_generation/providers/gemini.py | 5 +-
.../external_generation/providers/openai.py | 5 +-
.../image_util/controlnet_processor.py | 90 +++++++++++--------
.../backend/model_manager/starter_models.py | 6 --
.../test_external_generation_service.py | 8 +-
tests/conftest.py | 2 +-
8 files changed, 69 insertions(+), 53 deletions(-)
diff --git a/invokeai/app/api/routers/app_info.py b/invokeai/app/api/routers/app_info.py
index 1d68bcc3b8d..ef1932c22cb 100644
--- a/invokeai/app/api/routers/app_info.py
+++ b/invokeai/app/api/routers/app_info.py
@@ -12,12 +12,12 @@
from invokeai.app.api.dependencies import ApiDependencies
from invokeai.app.services.config.config_default import (
- DefaultInvokeAIAppConfig,
EXTERNAL_PROVIDER_CONFIG_FIELDS,
+ DefaultInvokeAIAppConfig,
InvokeAIAppConfig,
get_config,
- load_external_api_keys,
load_and_migrate_config,
+ load_external_api_keys,
)
from invokeai.app.services.external_generation.external_generation_common import ExternalProviderStatus
from invokeai.app.services.invocation_cache.invocation_cache_common import InvocationCacheStatus
diff --git a/invokeai/app/services/external_generation/__init__.py b/invokeai/app/services/external_generation/__init__.py
index 692da64643a..b933811d293 100644
--- a/invokeai/app/services/external_generation/__init__.py
+++ b/invokeai/app/services/external_generation/__init__.py
@@ -3,9 +3,9 @@
ExternalProvider,
)
from invokeai.app.services.external_generation.external_generation_common import (
+ ExternalGeneratedImage,
ExternalGenerationRequest,
ExternalGenerationResult,
- ExternalGeneratedImage,
ExternalProviderStatus,
ExternalReferenceImage,
)
diff --git a/invokeai/app/services/external_generation/providers/gemini.py b/invokeai/app/services/external_generation/providers/gemini.py
index 7f289cced76..70cf6a09659 100644
--- a/invokeai/app/services/external_generation/providers/gemini.py
+++ b/invokeai/app/services/external_generation/providers/gemini.py
@@ -6,7 +6,10 @@
import requests
from PIL.Image import Image as PILImageType
-from invokeai.app.services.external_generation.errors import ExternalProviderRateLimitError, ExternalProviderRequestError
+from invokeai.app.services.external_generation.errors import (
+ ExternalProviderRateLimitError,
+ ExternalProviderRequestError,
+)
from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
from invokeai.app.services.external_generation.external_generation_common import (
ExternalGeneratedImage,
diff --git a/invokeai/app/services/external_generation/providers/openai.py b/invokeai/app/services/external_generation/providers/openai.py
index 5051cf9cf18..033f6cd4a60 100644
--- a/invokeai/app/services/external_generation/providers/openai.py
+++ b/invokeai/app/services/external_generation/providers/openai.py
@@ -5,7 +5,10 @@
import requests
from PIL.Image import Image as PILImageType
-from invokeai.app.services.external_generation.errors import ExternalProviderRateLimitError, ExternalProviderRequestError
+from invokeai.app.services.external_generation.errors import (
+ ExternalProviderRateLimitError,
+ ExternalProviderRequestError,
+)
from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
from invokeai.app.services.external_generation.external_generation_common import (
ExternalGeneratedImage,
diff --git a/invokeai/backend/image_util/controlnet_processor.py b/invokeai/backend/image_util/controlnet_processor.py
index 87739f69e1c..81eed420977 100644
--- a/invokeai/backend/image_util/controlnet_processor.py
+++ b/invokeai/backend/image_util/controlnet_processor.py
@@ -14,43 +14,61 @@ def _get_processor_invocation_class(processor_type: str):
"""Get the invocation class for a processor type."""
# Import processor invocation classes on demand
processor_class_map = {
- "canny_image_processor": lambda: __import__(
- "invokeai.app.invocations.canny", fromlist=["CannyEdgeDetectionInvocation"]
- ).CannyEdgeDetectionInvocation,
- "hed_image_processor": lambda: __import__(
- "invokeai.app.invocations.hed", fromlist=["HEDEdgeDetectionInvocation"]
- ).HEDEdgeDetectionInvocation,
- "mlsd_image_processor": lambda: __import__(
- "invokeai.app.invocations.mlsd", fromlist=["MLSDDetectionInvocation"]
- ).MLSDDetectionInvocation,
- "depth_anything_image_processor": lambda: __import__(
- "invokeai.app.invocations.depth_anything", fromlist=["DepthAnythingDepthEstimationInvocation"]
- ).DepthAnythingDepthEstimationInvocation,
- "normalbae_image_processor": lambda: __import__(
- "invokeai.app.invocations.normal_bae", fromlist=["NormalMapInvocation"]
- ).NormalMapInvocation,
- "pidi_image_processor": lambda: __import__(
- "invokeai.app.invocations.pidi", fromlist=["PiDiNetEdgeDetectionInvocation"]
- ).PiDiNetEdgeDetectionInvocation,
- "lineart_image_processor": lambda: __import__(
- "invokeai.app.invocations.lineart", fromlist=["LineartEdgeDetectionInvocation"]
- ).LineartEdgeDetectionInvocation,
- "lineart_anime_image_processor": lambda: __import__(
- "invokeai.app.invocations.lineart_anime", fromlist=["LineartAnimeEdgeDetectionInvocation"]
- ).LineartAnimeEdgeDetectionInvocation,
- "content_shuffle_image_processor": lambda: __import__(
- "invokeai.app.invocations.content_shuffle", fromlist=["ContentShuffleInvocation"]
- ).ContentShuffleInvocation,
- "dw_openpose_image_processor": lambda: __import__(
- "invokeai.app.invocations.dw_openpose", fromlist=["DWOpenposeDetectionInvocation"]
- ).DWOpenposeDetectionInvocation,
- "mediapipe_face_processor": lambda: __import__(
- "invokeai.app.invocations.mediapipe_face", fromlist=["MediaPipeFaceDetectionInvocation"]
- ).MediaPipeFaceDetectionInvocation,
+ "canny_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.canny", fromlist=["CannyEdgeDetectionInvocation"]
+ ).CannyEdgeDetectionInvocation
+ ),
+ "hed_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.hed", fromlist=["HEDEdgeDetectionInvocation"]
+ ).HEDEdgeDetectionInvocation
+ ),
+ "mlsd_image_processor": lambda: (
+ __import__("invokeai.app.invocations.mlsd", fromlist=["MLSDDetectionInvocation"]).MLSDDetectionInvocation
+ ),
+ "depth_anything_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.depth_anything", fromlist=["DepthAnythingDepthEstimationInvocation"]
+ ).DepthAnythingDepthEstimationInvocation
+ ),
+ "normalbae_image_processor": lambda: (
+ __import__("invokeai.app.invocations.normal_bae", fromlist=["NormalMapInvocation"]).NormalMapInvocation
+ ),
+ "pidi_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.pidi", fromlist=["PiDiNetEdgeDetectionInvocation"]
+ ).PiDiNetEdgeDetectionInvocation
+ ),
+ "lineart_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.lineart", fromlist=["LineartEdgeDetectionInvocation"]
+ ).LineartEdgeDetectionInvocation
+ ),
+ "lineart_anime_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.lineart_anime", fromlist=["LineartAnimeEdgeDetectionInvocation"]
+ ).LineartAnimeEdgeDetectionInvocation
+ ),
+ "content_shuffle_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.content_shuffle", fromlist=["ContentShuffleInvocation"]
+ ).ContentShuffleInvocation
+ ),
+ "dw_openpose_image_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.dw_openpose", fromlist=["DWOpenposeDetectionInvocation"]
+ ).DWOpenposeDetectionInvocation
+ ),
+ "mediapipe_face_processor": lambda: (
+ __import__(
+ "invokeai.app.invocations.mediapipe_face", fromlist=["MediaPipeFaceDetectionInvocation"]
+ ).MediaPipeFaceDetectionInvocation
+ ),
# Note: zoe_depth_image_processor doesn't have a processor invocation implementation
- "color_map_image_processor": lambda: __import__(
- "invokeai.app.invocations.color_map", fromlist=["ColorMapInvocation"]
- ).ColorMapInvocation,
+ "color_map_image_processor": lambda: (
+ __import__("invokeai.app.invocations.color_map", fromlist=["ColorMapInvocation"]).ColorMapInvocation
+ ),
}
if processor_type in processor_class_map:
diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py
index f047dc48e81..7fe50d91fc4 100644
--- a/invokeai/backend/model_manager/starter_models.py
+++ b/invokeai/backend/model_manager/starter_models.py
@@ -939,9 +939,7 @@ def _gemini_3_resolution_presets(
format=ModelFormat.ExternalApi,
capabilities=ExternalModelCapabilities(
modes=["txt2img", "img2img", "inpaint"],
-
supports_seed=True,
-
supports_reference_images=True,
max_images_per_request=1,
allowed_aspect_ratios=[
@@ -981,9 +979,7 @@ def _gemini_3_resolution_presets(
format=ModelFormat.ExternalApi,
capabilities=ExternalModelCapabilities(
modes=["txt2img", "img2img", "inpaint"],
-
supports_seed=True,
-
supports_reference_images=True,
max_reference_images=14,
max_images_per_request=1,
@@ -1003,9 +999,7 @@ def _gemini_3_resolution_presets(
format=ModelFormat.ExternalApi,
capabilities=ExternalModelCapabilities(
modes=["txt2img", "img2img", "inpaint"],
-
supports_seed=True,
-
supports_reference_images=True,
max_reference_images=14,
max_images_per_request=1,
diff --git a/tests/app/services/external_generation/test_external_generation_service.py b/tests/app/services/external_generation/test_external_generation_service.py
index 927f60b6eef..89c1a6db315 100644
--- a/tests/app/services/external_generation/test_external_generation_service.py
+++ b/tests/app/services/external_generation/test_external_generation_service.py
@@ -3,19 +3,19 @@
import pytest
from PIL import Image
+from invokeai.app.services.config.config_default import InvokeAIAppConfig
from invokeai.app.services.external_generation.errors import (
ExternalProviderCapabilityError,
ExternalProviderNotConfiguredError,
ExternalProviderNotFoundError,
)
+from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
from invokeai.app.services.external_generation.external_generation_common import (
ExternalGeneratedImage,
ExternalGenerationRequest,
ExternalGenerationResult,
ExternalReferenceImage,
)
-from invokeai.app.services.config.config_default import InvokeAIAppConfig
-from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
from invokeai.backend.model_manager.configs.external_api import (
ExternalApiModelConfig,
@@ -215,9 +215,7 @@ def test_generate_validates_allowed_aspect_ratios_with_bucket_sizes() -> None:
def test_generate_happy_path() -> None:
- model = _build_model(
- ExternalModelCapabilities(modes=["txt2img"], supports_seed=True)
- )
+ model = _build_model(ExternalModelCapabilities(modes=["txt2img"], supports_seed=True))
request = _build_request(model=model, seed=42)
result = ExternalGenerationResult(images=[ExternalGeneratedImage(image=_make_image(), seed=42)])
provider = DummyProvider("openai", configured=True, result=result)
diff --git a/tests/conftest.py b/tests/conftest.py
index bfd9f070df4..92f42375ed4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -16,8 +16,8 @@
from invokeai.app.services.bulk_download.bulk_download_default import BulkDownloadService
from invokeai.app.services.client_state_persistence.client_state_persistence_sqlite import ClientStatePersistenceSqlite
from invokeai.app.services.config.config_default import InvokeAIAppConfig
-from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
+from invokeai.app.services.image_records.image_records_sqlite import SqliteImageRecordStorage
from invokeai.app.services.images.images_default import ImageService
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
from invokeai.app.services.invocation_services import InvocationServices
From 813a5e2c2ed81235c5747d983c395219c25282e0 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Sat, 28 Mar 2026 14:59:51 +0100
Subject: [PATCH 26/44] Chore typegen
---
.../frontend/web/src/services/api/schema.ts | 130 ++++++++----------
1 file changed, 55 insertions(+), 75 deletions(-)
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 67e1c312218..bfba81695a2 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -8069,10 +8069,6 @@ export type components = {
width?: number | null;
/** Height */
height?: number | null;
- /** Steps */
- steps?: number | null;
- /** Guidance */
- guidance?: number | null;
/** Num Images */
num_images?: number | null;
};
@@ -8126,12 +8122,6 @@ export type components = {
* @default null
*/
prompt?: string | null;
- /**
- * Negative Prompt
- * @description Negative prompt
- * @default null
- */
- negative_prompt?: string | null;
/**
* Seed
* @description Seed for random number generation
@@ -8157,17 +8147,11 @@ export type components = {
*/
height?: number;
/**
- * Steps
- * @description Number of steps to run
- * @default null
- */
- steps?: number | null;
- /**
- * Guidance
- * @description Guidance strength
+ * Image Size
+ * @description Image size preset (e.g. 1K, 2K, 4K)
* @default null
*/
- guidance?: number | null;
+ image_size?: string | null;
/**
* @description Init image for img2img/inpaint
* @default null
@@ -8184,18 +8168,6 @@ export type components = {
* @default []
*/
reference_images?: components["schemas"]["ImageField"][];
- /**
- * Reference Image Weights
- * @description Reference image weights
- * @default null
- */
- reference_image_weights?: number[] | null;
- /**
- * Reference Image Modes
- * @description Reference image modes
- * @default null
- */
- reference_image_modes?: string[] | null;
/**
* type
* @default external_image_generation
@@ -8248,6 +8220,8 @@ export type components = {
aspect_ratio_sizes?: {
[key: string]: components["schemas"]["ExternalImageSize"];
} | null;
+ /** Resolution Presets */
+ resolution_presets?: components["schemas"]["ExternalResolutionPreset"][] | null;
/** Max Reference Images */
max_reference_images?: number | null;
/**
@@ -8265,7 +8239,7 @@ export type components = {
* Name
* @enum {string}
*/
- name: "negative_prompt" | "reference_images" | "dimensions" | "seed" | "steps" | "guidance";
+ name: "reference_images" | "dimensions" | "seed";
/** Slider Min */
slider_min?: number | null;
/** Slider Max */
@@ -8354,6 +8328,28 @@ export type components = {
*/
message?: string | null;
};
+ /** ExternalResolutionPreset */
+ ExternalResolutionPreset: {
+ /**
+ * Label
+ * @description Display label, e.g. '1:1 (1K)'
+ */
+ label: string;
+ /**
+ * Aspect Ratio
+ * @description Aspect ratio string, e.g. '1:1'
+ */
+ aspect_ratio: string;
+ /**
+ * Image Size
+ * @description Image size preset, e.g. '1K'
+ */
+ image_size: string;
+ /** Width */
+ width: number;
+ /** Height */
+ height: number;
+ };
/**
* Apply LoRA Collection - FLUX
* @description Applies a collection of LoRAs to a FLUX transformer.
@@ -11043,12 +11039,6 @@ export type components = {
* @default null
*/
prompt?: string | null;
- /**
- * Negative Prompt
- * @description Negative prompt
- * @default null
- */
- negative_prompt?: string | null;
/**
* Seed
* @description Seed for random number generation
@@ -11074,17 +11064,11 @@ export type components = {
*/
height?: number;
/**
- * Steps
- * @description Number of steps to run
- * @default null
- */
- steps?: number | null;
- /**
- * Guidance
- * @description Guidance strength
+ * Image Size
+ * @description Image size preset (e.g. 1K, 2K, 4K)
* @default null
*/
- guidance?: number | null;
+ image_size?: string | null;
/**
* @description Init image for img2img/inpaint
* @default null
@@ -11102,17 +11086,17 @@ export type components = {
*/
reference_images?: components["schemas"]["ImageField"][];
/**
- * Reference Image Weights
- * @description Reference image weights
+ * Temperature
+ * @description Sampling temperature
* @default null
*/
- reference_image_weights?: number[] | null;
+ temperature?: number | null;
/**
- * Reference Image Modes
- * @description Reference image modes
+ * Thinking Level
+ * @description Thinking level for image generation
* @default null
*/
- reference_image_modes?: string[] | null;
+ thinking_level?: ("minimal" | "high") | null;
/**
* type
* @default gemini_image_generation
@@ -22220,12 +22204,6 @@ export type components = {
* @default null
*/
prompt?: string | null;
- /**
- * Negative Prompt
- * @description Negative prompt
- * @default null
- */
- negative_prompt?: string | null;
/**
* Seed
* @description Seed for random number generation
@@ -22251,17 +22229,11 @@ export type components = {
*/
height?: number;
/**
- * Steps
- * @description Number of steps to run
+ * Image Size
+ * @description Image size preset (e.g. 1K, 2K, 4K)
* @default null
*/
- steps?: number | null;
- /**
- * Guidance
- * @description Guidance strength
- * @default null
- */
- guidance?: number | null;
+ image_size?: string | null;
/**
* @description Init image for img2img/inpaint
* @default null
@@ -22279,17 +22251,25 @@ export type components = {
*/
reference_images?: components["schemas"]["ImageField"][];
/**
- * Reference Image Weights
- * @description Reference image weights
- * @default null
+ * Quality
+ * @description Output image quality
+ * @default auto
+ * @enum {string}
+ */
+ quality?: "auto" | "high" | "medium" | "low";
+ /**
+ * Background
+ * @description Background transparency handling
+ * @default auto
+ * @enum {string}
*/
- reference_image_weights?: number[] | null;
+ background?: "auto" | "transparent" | "opaque";
/**
- * Reference Image Modes
- * @description Reference image modes
+ * Input Fidelity
+ * @description Fidelity to source images (edits only)
* @default null
*/
- reference_image_modes?: string[] | null;
+ input_fidelity?: ("low" | "high") | null;
/**
* type
* @default openai_image_generation
From c2016bcfb7d4edebbd72424365a656bcfb9d285a Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Mon, 6 Apr 2026 23:13:10 +0200
Subject: [PATCH 27/44] feat: full canvas workflow integration for external
models
- Add missing aspect ratios (4:5, 5:4, 8:1, 4:1, 1:4, 1:8) to type
system for external model support
- Sync canvas bbox when external model resolution preset is selected
- Use params preset dimensions in buildExternalGraph to prevent
"unsupported aspect ratio" errors
- Lock all bbox controls (resize handles, aspect ratio select,
width/height sliders, swap/optimal buttons) for external models
with fixed dimension presets
- Disable denoise strength slider for external models (not applicable)
- Sync bbox aspect ratio changes back to paramsSlice for external models
- Initialize bbox dimensions when switching to an external model
---
invokeai/frontend/web/public/locales/en.json | 1 +
.../listeners/modelSelected.ts | 34 ++++++++++-
.../components/ParamDenoisingStrength.tsx | 13 ++++-
.../konva/CanvasTool/CanvasBboxToolModule.ts | 9 ++-
.../controlLayers/store/canvasSlice.ts | 56 +++++++++++++++----
.../src/features/controlLayers/store/types.ts | 25 ++++++++-
.../graph/generation/buildExternalGraph.ts | 16 +++---
.../components/Bbox/BboxAspectRatioSelect.tsx | 24 ++++++--
.../Bbox/BboxSwapDimensionsButton.tsx | 6 +-
.../Bbox/use-is-bbox-size-locked.ts | 5 +-
10 files changed, 153 insertions(+), 36 deletions(-)
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 4686ad070e4..c13020c608b 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -1490,6 +1490,7 @@
"copyImage": "Copy Image",
"denoisingStrength": "Denoising Strength",
"disabledNoRasterContent": "Disabled (No Raster Content)",
+ "disabledNotSupported": "Not supported by model",
"downloadImage": "Download Image",
"general": "General",
"guidance": "Guidance",
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
index ed2c67d5292..25bad13f4b3 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
@@ -4,9 +4,11 @@ import { bboxSyncedToOptimalDimension, rgRefImageModelChanged } from 'features/c
import { buildSelectIsStaging, selectCanvasSessionId } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { loraIsEnabledChanged } from 'features/controlLayers/store/lorasSlice';
import {
+ aspectRatioIdChanged,
kleinQwen3EncoderModelSelected,
kleinVaeModelSelected,
modelChanged,
+ resolutionPresetSelected,
setZImageScheduler,
syncedToOptimalDimension,
vaeSelected,
@@ -24,7 +26,7 @@ import {
selectBboxModelBase,
selectCanvasSlice,
} from 'features/controlLayers/store/selectors';
-import { getEntityIdentifier, isFlux2ReferenceImageConfig } from 'features/controlLayers/store/types';
+import { getEntityIdentifier, isAspectRatioID, isFlux2ReferenceImageConfig } from 'features/controlLayers/store/types';
import {
initialFlux2ReferenceImage,
initialFluxKontextReferenceImage,
@@ -46,7 +48,7 @@ import {
selectZImageDiffusersModels,
} from 'services/api/hooks/modelsByType';
import type { FLUXKontextModelConfig, FLUXReduxModelConfig, IPAdapterModelConfig } from 'services/api/types';
-import { isFluxKontextModelConfig, isFluxReduxModelConfig } from 'services/api/types';
+import { isExternalApiModelConfig, isFluxKontextModelConfig, isFluxReduxModelConfig } from 'services/api/types';
const log = logger('models');
@@ -352,6 +354,34 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
dispatch(bboxSyncedToOptimalDimension());
}
}
+
+ // When switching to an external model, sync bbox to the model's first preset dimensions
+ if (newBase === 'external') {
+ const modelConfigsResult = selectModelConfigsQuery(getState());
+ if (modelConfigsResult.data) {
+ const newModelConfig = modelConfigsAdapterSelectors.selectById(modelConfigsResult.data, newModel.key);
+ if (newModelConfig && isExternalApiModelConfig(newModelConfig)) {
+ const { aspect_ratio_sizes, resolution_presets } = newModelConfig.capabilities;
+ if (resolution_presets && resolution_presets.length > 0) {
+ const firstPreset = resolution_presets[0]!;
+ dispatch(
+ resolutionPresetSelected({
+ imageSize: firstPreset.image_size,
+ aspectRatio: firstPreset.aspect_ratio,
+ width: firstPreset.width,
+ height: firstPreset.height,
+ })
+ );
+ } else if (aspect_ratio_sizes) {
+ const firstRatio = Object.keys(aspect_ratio_sizes)[0];
+ const firstSize = firstRatio ? aspect_ratio_sizes[firstRatio] : undefined;
+ if (firstRatio && firstSize && isAspectRatioID(firstRatio)) {
+ dispatch(aspectRatioIdChanged({ id: firstRatio, fixedSize: firstSize }));
+ }
+ }
+ }
+ }
+ }
},
});
};
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx
index 34fb96f063d..658fb8b7458 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/ParamDenoisingStrength.tsx
@@ -11,7 +11,7 @@ import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
import WavyLine from 'common/components/WavyLine';
-import { selectImg2imgStrength, setImg2imgStrength } from 'features/controlLayers/store/paramsSlice';
+import { selectImg2imgStrength, selectIsExternal, setImg2imgStrength } from 'features/controlLayers/store/paramsSlice';
import { selectActiveRasterLayerEntities } from 'features/controlLayers/store/selectors';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -37,6 +37,7 @@ export const ParamDenoisingStrength = memo(() => {
const img2imgStrength = useAppSelector(selectImg2imgStrength);
const dispatch = useAppDispatch();
const hasRasterLayersWithContent = useAppSelector(selectHasRasterLayersWithContent);
+ const isExternal = useAppSelector(selectIsExternal);
const selectedModelConfig = useSelectedModelConfig();
const onChange = useCallback(
@@ -55,12 +56,16 @@ export const ParamDenoisingStrength = memo(() => {
// Denoising strength does nothing if there are no raster layers w/ content
return true;
}
+ if (isExternal) {
+ // External models don't support denoise strength - they handle img2img via prompt
+ return true;
+ }
if (selectedModelConfig && isFluxFillMainModelModelConfig(selectedModelConfig)) {
// Denoising strength is ignored by FLUX Fill, which is indicated by the variant being 'inpaint'
return true;
}
return false;
- }, [hasRasterLayersWithContent, selectedModelConfig]);
+ }, [hasRasterLayersWithContent, isExternal, selectedModelConfig]);
return (
@@ -96,7 +101,9 @@ export const ParamDenoisingStrength = memo(() => {
>
) : (
- {t('parameters.disabledNoRasterContent')}
+
+ {isExternal ? t('parameters.disabledNotSupported') : t('parameters.disabledNoRasterContent')}
+
)}
diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts
index ecf9a5d1c7c..2ab2d1f281f 100644
--- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasBboxToolModule.ts
@@ -10,7 +10,7 @@ import {
getPrefixedId,
} from 'features/controlLayers/konva/util';
import { selectBboxOverlay } from 'features/controlLayers/store/canvasSettingsSlice';
-import { selectModel } from 'features/controlLayers/store/paramsSlice';
+import { selectHasFixedDimensionSizes, selectModel } from 'features/controlLayers/store/paramsSlice';
import { selectBbox } from 'features/controlLayers/store/selectors';
import type { Coordinate, Rect, Tool } from 'features/controlLayers/store/types';
import Konva from 'konva';
@@ -191,6 +191,9 @@ export class CanvasBboxToolModule extends CanvasModuleBase {
// Listen for the model changing - some model types constraint the bbox to a certain size or aspect ratio.
this.subscriptions.add(this.manager.stateApi.createStoreSubscription(selectModel, this.render));
+ // Listen for fixed dimension sizes changes - external models may lock bbox resizing
+ this.subscriptions.add(this.manager.stateApi.createStoreSubscription(selectHasFixedDimensionSizes, this.render));
+
// Update on busy state changes
this.subscriptions.add(this.manager.$isBusy.listen(this.render));
@@ -246,6 +249,10 @@ export class CanvasBboxToolModule extends CanvasModuleBase {
if (tool !== 'bbox') {
return NO_ANCHORS;
}
+ // External models with fixed dimension presets don't allow free bbox resizing
+ if (this.manager.stateApi.runSelector(selectHasFixedDimensionSizes)) {
+ return NO_ANCHORS;
+ }
return ALL_ANCHORS;
};
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
index 79d3963d122..9c283f188f0 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
@@ -7,7 +7,7 @@ import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMul
import { merge } from 'es-toolkit/compat';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { canvasReset } from 'features/controlLayers/store/actions';
-import { modelChanged } from 'features/controlLayers/store/paramsSlice';
+import { aspectRatioIdChanged, modelChanged, resolutionPresetSelected } from 'features/controlLayers/store/paramsSlice';
import {
selectAllEntities,
selectAllEntitiesOfType,
@@ -31,6 +31,7 @@ import type {
RgbColor,
SimpleAdjustmentsConfig,
} from 'features/controlLayers/store/types';
+import { isAspectRatioID } from 'features/controlLayers/store/types';
import {
calculateNewSize,
getScaledBoundingBoxDimensions,
@@ -1279,21 +1280,31 @@ const slice = createSlice({
state.bbox.aspectRatio.isLocked = !state.bbox.aspectRatio.isLocked;
syncScaledSize(state);
},
- bboxAspectRatioIdChanged: (state, action: PayloadAction<{ id: AspectRatioID }>) => {
- const { id } = action.payload;
+ bboxAspectRatioIdChanged: (
+ state,
+ action: PayloadAction<{ id: AspectRatioID; fixedSize?: { width: number; height: number } }>
+ ) => {
+ const { id, fixedSize } = action.payload;
state.bbox.aspectRatio.id = id;
if (id === 'Free') {
state.bbox.aspectRatio.isLocked = false;
} else {
state.bbox.aspectRatio.isLocked = true;
- state.bbox.aspectRatio.value = ASPECT_RATIO_MAP[id].ratio;
- const { width, height } = calculateNewSize(
- state.bbox.aspectRatio.value,
- state.bbox.rect.width * state.bbox.rect.height,
- state.bbox.modelBase
- );
- state.bbox.rect.width = width;
- state.bbox.rect.height = height;
+ if (fixedSize) {
+ // External models provide fixed dimensions for each aspect ratio
+ state.bbox.aspectRatio.value = fixedSize.width / fixedSize.height;
+ state.bbox.rect.width = fixedSize.width;
+ state.bbox.rect.height = fixedSize.height;
+ } else {
+ state.bbox.aspectRatio.value = ASPECT_RATIO_MAP[id].ratio;
+ const { width, height } = calculateNewSize(
+ state.bbox.aspectRatio.value,
+ state.bbox.rect.width * state.bbox.rect.height,
+ state.bbox.modelBase
+ );
+ state.bbox.rect.width = width;
+ state.bbox.rect.height = height;
+ }
}
syncScaledSize(state);
@@ -1744,6 +1755,29 @@ const slice = createSlice({
syncScaledSize(state);
}
});
+ // Sync bbox when external model resolution preset is selected (aspect_ratio_sizes)
+ builder.addCase(aspectRatioIdChanged, (state, action) => {
+ const { id, fixedSize } = action.payload;
+ // Only sync when fixedSize is provided (external models with aspect_ratio_sizes)
+ if (fixedSize) {
+ state.bbox.rect.width = fixedSize.width;
+ state.bbox.rect.height = fixedSize.height;
+ state.bbox.aspectRatio.value = fixedSize.width / fixedSize.height;
+ state.bbox.aspectRatio.id = id;
+ state.bbox.aspectRatio.isLocked = true;
+ syncScaledSize(state);
+ }
+ });
+ // Sync bbox when external model resolution preset is selected (resolution_presets)
+ builder.addCase(resolutionPresetSelected, (state, action) => {
+ const { width, height, aspectRatio } = action.payload;
+ state.bbox.rect.width = width;
+ state.bbox.rect.height = height;
+ state.bbox.aspectRatio.value = width / height;
+ state.bbox.aspectRatio.id = isAspectRatioID(aspectRatio) ? aspectRatio : 'Free';
+ state.bbox.aspectRatio.isLocked = true;
+ syncScaledSize(state);
+ });
},
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
index 77ad6619db6..2b09b4b8edc 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts
@@ -636,19 +636,42 @@ export const zLoRA = z.object({
});
export type LoRA = z.infer;
-export const zAspectRatioID = z.enum(['Free', '21:9', '16:9', '3:2', '4:3', '1:1', '3:4', '2:3', '9:16', '9:21']);
+export const zAspectRatioID = z.enum([
+ 'Free',
+ '8:1',
+ '4:1',
+ '21:9',
+ '16:9',
+ '3:2',
+ '5:4',
+ '4:3',
+ '1:1',
+ '3:4',
+ '4:5',
+ '2:3',
+ '9:16',
+ '1:4',
+ '9:21',
+ '1:8',
+]);
export type AspectRatioID = z.infer;
export const isAspectRatioID = (v: unknown): v is AspectRatioID => zAspectRatioID.safeParse(v).success;
export const ASPECT_RATIO_MAP: Record, { ratio: number; inverseID: AspectRatioID }> = {
+ '8:1': { ratio: 8 / 1, inverseID: '1:8' },
+ '4:1': { ratio: 4 / 1, inverseID: '1:4' },
'21:9': { ratio: 21 / 9, inverseID: '9:21' },
'16:9': { ratio: 16 / 9, inverseID: '9:16' },
'3:2': { ratio: 3 / 2, inverseID: '2:3' },
+ '5:4': { ratio: 5 / 4, inverseID: '4:5' },
'4:3': { ratio: 4 / 3, inverseID: '4:3' },
'1:1': { ratio: 1, inverseID: '1:1' },
'3:4': { ratio: 3 / 4, inverseID: '4:3' },
+ '4:5': { ratio: 4 / 5, inverseID: '5:4' },
'2:3': { ratio: 2 / 3, inverseID: '3:2' },
'9:16': { ratio: 9 / 16, inverseID: '16:9' },
+ '1:4': { ratio: 1 / 4, inverseID: '4:1' },
'9:21': { ratio: 9 / 21, inverseID: '21:9' },
+ '1:8': { ratio: 1 / 8, inverseID: '8:1' },
};
const zAspectRatioConfig = z.object({
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
index 0ba82234a66..2d7ee198974 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
@@ -6,7 +6,6 @@ import { type ModelIdentifierField, zImageField } from 'features/nodes/types/com
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import {
getOriginalAndScaledSizesForOtherModes,
- getOriginalAndScaledSizesForTextToImage,
selectCanvasOutputFields,
} from 'features/nodes/util/graph/graphBuilderUtils';
import {
@@ -110,16 +109,15 @@ export const buildExternalGraph = async (arg: GraphBuilderArg): Promise {
const id = useAppSelector(selectAspectRatioID);
const isStaging = useCanvasIsStaging();
const allowedAspectRatios = useAppSelector(selectAllowedAspectRatioIDs);
- const options = allowedAspectRatios ?? zAspectRatioID.options;
+ const aspectRatioSizes = useAppSelector(selectAspectRatioSizes);
+ const hasFixedSizes = useAppSelector(selectHasFixedDimensionSizes);
+ const options = useMemo(() => allowedAspectRatios ?? zAspectRatioID.options, [allowedAspectRatios]);
const onChange = useCallback>(
(e) => {
if (!isAspectRatioID(e.target.value)) {
return;
}
- dispatch(bboxAspectRatioIdChanged({ id: e.target.value }));
+ const fixedSize = aspectRatioSizes?.[e.target.value] ?? undefined;
+ dispatch(bboxAspectRatioIdChanged({ id: e.target.value, fixedSize }));
+ // For external models with fixed sizes, also sync to params so buildExternalGraph uses correct dimensions
+ if (fixedSize) {
+ dispatch(aspectRatioIdChanged({ id: e.target.value, fixedSize }));
+ }
},
- [dispatch]
+ [dispatch, aspectRatioSizes]
);
return (
-
+
{t('parameters.aspect')}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx
index 54614419a57..372f8187ea5 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Bbox/BboxSwapDimensionsButton.tsx
@@ -1,7 +1,8 @@
import { IconButton } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { bboxDimensionsSwapped } from 'features/controlLayers/store/canvasSlice';
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
+import { selectHasFixedDimensionSizes } from 'features/controlLayers/store/paramsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiArrowsDownUpBold } from 'react-icons/pi';
@@ -10,6 +11,7 @@ export const BboxSwapDimensionsButton = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const isStaging = useCanvasIsStaging();
+ const hasFixedSizes = useAppSelector(selectHasFixedDimensionSizes);
const onClick = useCallback(() => {
dispatch(bboxDimensionsSwapped());
}, [dispatch]);
@@ -21,7 +23,7 @@ export const BboxSwapDimensionsButton = memo(() => {
variant="ghost"
size="sm"
icon={}
- isDisabled={isStaging}
+ isDisabled={isStaging || hasFixedSizes}
/>
);
});
diff --git a/invokeai/frontend/web/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts b/invokeai/frontend/web/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts
index eaf13811088..18f453708ef 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts
+++ b/invokeai/frontend/web/src/features/parameters/components/Bbox/use-is-bbox-size-locked.ts
@@ -1,6 +1,9 @@
+import { useAppSelector } from 'app/store/storeHooks';
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
+import { selectHasFixedDimensionSizes } from 'features/controlLayers/store/paramsSlice';
export const useIsBboxSizeLocked = () => {
const isStaging = useCanvasIsStaging();
- return isStaging;
+ const hasFixedSizes = useAppSelector(selectHasFixedDimensionSizes);
+ return isStaging || hasFixedSizes;
};
From 089e2db402effa338bfe6e9b918851bcb967f83d Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Mon, 6 Apr 2026 23:21:45 +0200
Subject: [PATCH 28/44] Chore typegen Linux seperator
---
invokeai/frontend/web/src/services/api/schema.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 2729d86f708..6981dde09b7 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -15188,14 +15188,14 @@ export type components = {
* Convert Cache Dir
* Format: path
* @description Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions).
- * @default models\.convert_cache
+ * @default models/.convert_cache
*/
convert_cache_dir?: string;
/**
* Download Cache Dir
* Format: path
* @description Path to the directory that contains dynamically downloaded models.
- * @default models\.download_cache
+ * @default models/.download_cache
*/
download_cache_dir?: string;
/**
From 3e9e052d5db60329f5f3448920e0e855db69b0fc Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Mon, 6 Apr 2026 23:32:10 +0200
Subject: [PATCH 29/44] feat: full canvas workflow integration for external
models - Update buildExternalGraph test to include dimensions in mock params
---
.../nodes/util/graph/generation/buildExternalGraph.test.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts
index 561453fc4ed..f1fa54b4e0b 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.test.ts
@@ -82,6 +82,7 @@ beforeEach(() => {
mockParams = {
steps: 20,
guidance: 4.5,
+ dimensions: { width: 768, height: 512, aspectRatio: { id: '3:2', value: 1.5, isLocked: true } },
} as ParamsState;
mockSizes = { scaledSize: { width: 768, height: 512 } };
From ec90b2fbe9c7bc8be54b1e99eda9659617eb5d20 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Sun, 12 Apr 2026 04:29:17 +0200
Subject: [PATCH 30/44] Merge remote-tracking branch 'upstream/main' into
external-models
---
invokeai/backend/model_manager/starter_models.py | 1 +
.../listenerMiddleware/listeners/modelSelected.ts | 2 +-
.../controlLayers/components/StagingArea/shared.test.ts | 2 +-
.../controlLayers/components/StagingArea/shared.ts | 8 +++++---
.../web/src/features/parameters/types/parameterSchemas.ts | 2 +-
.../AdvancedSettingsAccordion.tsx | 2 +-
.../GenerationSettingsAccordion.tsx | 4 +++-
7 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/invokeai/backend/model_manager/starter_models.py b/invokeai/backend/model_manager/starter_models.py
index 8536319cf29..edcac321f1a 100644
--- a/invokeai/backend/model_manager/starter_models.py
+++ b/invokeai/backend/model_manager/starter_models.py
@@ -1108,6 +1108,7 @@ def _gemini_3_resolution_presets(
),
default_settings=ExternalApiModelDefaultSettings(width=1024, height=1024, num_images=1),
panel_schema=ExternalModelPanelSchema(image=[{"name": "dimensions"}]),
+)
# region Anima
anima_qwen3_encoder = StarterModel(
name="Anima Qwen3 0.6B Text Encoder",
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
index da8a2d28ce2..251403ed046 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
@@ -4,10 +4,10 @@ import { bboxSyncedToOptimalDimension, rgRefImageModelChanged } from 'features/c
import { buildSelectIsStaging, selectCanvasSessionId } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { loraIsEnabledChanged } from 'features/controlLayers/store/lorasSlice';
import {
- aspectRatioIdChanged,
animaQwen3EncoderModelSelected,
animaT5EncoderModelSelected,
animaVaeModelSelected,
+ aspectRatioIdChanged,
kleinQwen3EncoderModelSelected,
kleinVaeModelSelected,
modelChanged,
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts
index 8b3f47ffcb2..d0ce87edfc0 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.test.ts
@@ -207,7 +207,7 @@ describe('StagingAreaApi Utility Functions', () => {
},
} as unknown as S['SessionQueueItem'];
- expect(getOutputImageName(queueItem)).toBe('first.png');
+ expect(getOutputImageNames(queueItem)).toEqual(['first.png', 'second.png']);
});
it('should handle empty session mapping', () => {
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts
index b8e0d96f2b5..bc13ee16f81 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/shared.ts
@@ -32,9 +32,11 @@ export const getOutputImageNames = (item: S['SessionQueueItem']): string[] => {
if (isImageField(value)) {
imageNames.push(value.image_name);
}
- }
- if (isImageFieldCollection(value)) {
- return value[0]?.image_name ?? null;
+ if (isImageFieldCollection(value)) {
+ for (const img of value) {
+ imageNames.push(img.image_name);
+ }
+ }
}
}
diff --git a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
index 7f5ae3b0892..eb2f1e6c15b 100644
--- a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
+++ b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts
@@ -2,8 +2,8 @@ import { NUMPY_RAND_MAX } from 'app/constants';
import { roundToMultiple } from 'common/util/roundDownToMultiple';
import { buildZodTypeGuard } from 'common/util/zodUtils';
import {
- zExternalModelIdentifierField,
zAnimaSchedulerField,
+ zExternalModelIdentifierField,
zFluxDypeExponentField,
zFluxDypePresetField,
zFluxDypeScaleField,
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
index d0e9d734997..b96b4c5e619 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx
@@ -4,8 +4,8 @@ import { skipToken } from '@reduxjs/toolkit/query';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
import {
- selectIsExternal,
selectIsAnima,
+ selectIsExternal,
selectIsFLUX,
selectIsFlux2,
selectIsSD3,
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
index ef9560df747..9d53331a2d4 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
@@ -94,7 +94,9 @@ export const GenerationSettingsAccordion = memo(() => {
- {!isExternal && !isFLUX && !isFlux2 && !isSD3 && !isCogView4 && !isZImage && !isAnima && }
+ {!isExternal && !isFLUX && !isFlux2 && !isSD3 && !isCogView4 && !isZImage && !isAnima && (
+
+ )}
{!isExternal && isFLUX && }
{!isExternal && isZImage && }
{!isExternal && isAnima && }
From 8f00759af0f900b876d8d4a01ed29948e4df2417 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Tue, 14 Apr 2026 01:07:35 +0200
Subject: [PATCH 31/44] Chore pnpm fix
---
.../GenerationSettingsAccordion.tsx | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
index 99bb544ccdd..7e07c9f5648 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx
@@ -97,9 +97,14 @@ export const GenerationSettingsAccordion = memo(() => {
- {!isExternal && !isFLUX && !isFlux2 && !isSD3 && !isCogView4 && !isZImage && !isQwenImage && !isAnima && (
-
- )}
+ {!isExternal &&
+ !isFLUX &&
+ !isFlux2 &&
+ !isSD3 &&
+ !isCogView4 &&
+ !isZImage &&
+ !isQwenImage &&
+ !isAnima && }
{!isExternal && isFLUX && }
{!isExternal && isZImage && }
{!isExternal && isAnima && }
From ec4b87b949aa1c33565dccf41741316a30442592 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Tue, 14 Apr 2026 01:39:04 +0200
Subject: [PATCH 32/44] add missing parameter
---
tests/app/routers/test_multiuser_authorization.py | 1 +
tests/app/routers/test_workflows_multiuser.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/tests/app/routers/test_multiuser_authorization.py b/tests/app/routers/test_multiuser_authorization.py
index e9efae7034d..85354c6a577 100644
--- a/tests/app/routers/test_multiuser_authorization.py
+++ b/tests/app/routers/test_multiuser_authorization.py
@@ -115,6 +115,7 @@ def mock_services() -> InvocationServices:
model_relationships=None, # type: ignore
client_state_persistence=ClientStatePersistenceSqlite(db=db),
users=UserService(db),
+ external_generation=None, # type: ignore
)
diff --git a/tests/app/routers/test_workflows_multiuser.py b/tests/app/routers/test_workflows_multiuser.py
index 28b301e18e3..5ec97fbb7c8 100644
--- a/tests/app/routers/test_workflows_multiuser.py
+++ b/tests/app/routers/test_workflows_multiuser.py
@@ -106,6 +106,7 @@ def mock_services() -> InvocationServices:
model_relationships=None, # type: ignore
client_state_persistence=ClientStatePersistenceSqlite(db=db),
users=UserService(db),
+ external_generation=None, # type: ignore
)
From b739af18b51be0dfe4481374a7c169dd2e5458d8 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Tue, 14 Apr 2026 23:17:04 +0200
Subject: [PATCH 33/44] docs: add External Models guide with Gemini and OpenAI
provider pages
---
docs/features/external-models/gemini.md | 46 ++++++++++++++++++++
docs/features/external-models/index.md | 58 +++++++++++++++++++++++++
docs/features/external-models/openai.md | 56 ++++++++++++++++++++++++
mkdocs.yml | 4 ++
4 files changed, 164 insertions(+)
create mode 100644 docs/features/external-models/gemini.md
create mode 100644 docs/features/external-models/index.md
create mode 100644 docs/features/external-models/openai.md
diff --git a/docs/features/external-models/gemini.md b/docs/features/external-models/gemini.md
new file mode 100644
index 00000000000..d3e0622e97f
--- /dev/null
+++ b/docs/features/external-models/gemini.md
@@ -0,0 +1,46 @@
+---
+title: Google Gemini
+---
+
+# :material-google: Google Gemini
+
+Invoke supports Google's Gemini image generation models through the Gemini API. This provider is a good fit if you want high-quality text-to-image and reference-based image edits without running a local model.
+
+## Getting an API Key
+
+1. Open [Google AI Studio](https://aistudio.google.com/) and sign in with your Google account.
+2. Generate a new API key.
+3. Note the key — it will only be shown once.
+
+## Configuration
+
+Add your key to `api_keys.yaml` in your Invoke root directory:
+
+```yaml
+external_gemini_api_key: "your-gemini-api-key"
+
+# Optional — only set this if you need to route requests through a different endpoint
+external_gemini_base_url: "https://generativelanguage.googleapis.com"
+```
+
+Restart Invoke for the change to take effect.
+
+## Available Models
+
+| Model | Modes | Reference Images | Notes |
+| --- | --- | --- | --- |
+| **Gemini 2.5 Flash Image** | txt2img, img2img, inpaint | Yes | 10 aspect ratios, fixed per-ratio resolutions. |
+| **Gemini 3 Pro Image Preview** | txt2img, img2img, inpaint | Up to 14 (6 object + 5 character) | 1K / 2K / 4K resolution presets. |
+| **Gemini 3.1 Flash Image Preview** | txt2img, img2img, inpaint | Up to 14 (10 object + 4 character) | 512 / 1K / 2K / 4K resolution presets. |
+
+All Gemini models are single-image-per-request — batch size is fixed at 1. To generate multiple variations, queue multiple invocations.
+
+## Provider-Specific Options
+
+Gemini exposes a **temperature** control in the parameters panel. Lower values make outputs more deterministic, higher values increase variability.
+
+## Tips
+
+- **Reference images** are sent directly to the API as inlined PNG data. Large references increase request latency and cost — crop tightly where possible.
+- **Aspect ratios** are mapped to the closest Gemini-supported ratio. For Gemini 3 models, use the resolution presets to stay at the provider's native output sizes and avoid unnecessary rescaling.
+- **Pricing** varies by model and region. Check Google's documentation before running large batches.
diff --git a/docs/features/external-models/index.md b/docs/features/external-models/index.md
new file mode 100644
index 00000000000..52e02d6958d
--- /dev/null
+++ b/docs/features/external-models/index.md
@@ -0,0 +1,58 @@
+---
+title: External Models
+---
+
+# :material-cloud-outline: External Models
+
+External models let you generate images in Invoke by calling third-party image generation APIs instead of running a model locally. This is useful when:
+
+- You don't have the GPU or VRAM to run a model locally.
+- You want access to closed-source models (e.g. GPT Image, Gemini).
+- You need a specific provider capability (very high resolutions, fast batches, bilingual text rendering, etc.).
+
+External models appear in the model picker alongside locally installed models. Generations are routed to the provider's API, billed against your provider account, and the resulting images are imported back into Invoke like any other generation.
+
+## Supported Providers
+
+- [Google Gemini](gemini.md) — Gemini 2.5 Flash Image, Gemini 3 Pro Image Preview, Gemini 3.1 Flash Image Preview
+- [OpenAI](openai.md) — GPT Image 1 / 1.5 / 1-mini, DALL·E 3, DALL·E 2
+
+## Configuring API Keys
+
+External provider credentials are stored in a dedicated `api_keys.yaml` file alongside `invokeai.yaml` in your Invoke root directory.
+
+```yaml
+# api_keys.yaml
+external_gemini_api_key: "your-gemini-api-key"
+external_openai_api_key: "your-openai-api-key"
+
+# Optional: override the provider base URL (e.g. for a compatible proxy or regional endpoint)
+external_gemini_base_url: "https://generativelanguage.googleapis.com"
+external_openai_base_url: "https://api.openai.com"
+```
+
+Restart Invoke after editing `api_keys.yaml` so the new values are picked up.
+
+!!! warning "Keep your keys private"
+ `api_keys.yaml` contains secrets. Do not commit it to version control and do not share it with other users of your machine.
+
+## Installing External Models
+
+External models are listed in the starter models dialog under their provider. Install them like any other starter model — Invoke records a model reference but does not download weights (there are no weights to download).
+
+Once installed, external models show up everywhere a model can be selected. Choose one, set the usual parameters (prompt, dimensions, num images, etc.), and invoke as normal.
+
+## Capabilities and Settings Visibility
+
+Each external model declares its own **capabilities** — for example:
+
+- Which generation modes it supports (`txt2img`, `img2img`, `inpaint`).
+- Whether it accepts reference images, and how many.
+- Which aspect ratios and resolutions it allows.
+- Whether it supports a negative prompt, seed, or batch size > 1.
+
+Invoke uses these capabilities to drive the UI: only the settings a given model actually supports will be shown in the parameters panel. If a field you expect is missing, it's because the selected model does not support it.
+
+## Costs and Rate Limits
+
+External providers charge for each request. Check the provider's pricing page before running large batches. Rate-limit errors from the provider are surfaced in Invoke as generation failures — wait a moment and try again, or lower your concurrent batch size.
diff --git a/docs/features/external-models/openai.md b/docs/features/external-models/openai.md
new file mode 100644
index 00000000000..dff0c948890
--- /dev/null
+++ b/docs/features/external-models/openai.md
@@ -0,0 +1,56 @@
+---
+title: OpenAI
+---
+
+# :material-alpha-o-circle-outline: OpenAI
+
+Invoke supports OpenAI's image generation models — both the GPT Image family and the older DALL·E models — through the OpenAI API.
+
+## Getting an API Key
+
+1. Open the [OpenAI API Platform](https://platform.openai.com/api-keys) and sign in.
+2. Create a new secret API key.
+3. Make sure your account has billing set up — image endpoints are paid per request.
+
+## Configuration
+
+Add your key to `api_keys.yaml` in your Invoke root directory:
+
+```yaml
+external_openai_api_key: "sk-..."
+
+# Optional — use this to point at a compatible proxy or Azure OpenAI deployment
+external_openai_base_url: "https://api.openai.com"
+```
+
+Restart Invoke for the change to take effect.
+
+## Available Models
+
+| Model | Modes | Aspect Ratios | Batch | Notes |
+| --- | --- | --- | --- | --- |
+| **GPT Image 1.5** | txt2img, img2img, inpaint | 1:1, 3:2, 2:3 | up to 10 | Fastest and cheapest GPT Image model. |
+| **GPT Image 1** | txt2img, img2img, inpaint | 1:1, 3:2, 2:3 | up to 10 | Highest quality of the GPT Image family. |
+| **GPT Image 1 Mini** | txt2img, img2img, inpaint | 1:1, 3:2, 2:3 | up to 10 | ~80% cheaper than GPT Image 1. |
+| **DALL·E 3** | txt2img only | 1:1, 7:4, 4:7 | 1 | No reference-image / edit support. |
+| **DALL·E 2** | txt2img, img2img, inpaint | 1:1 | up to 10 | Square only. |
+
+## Provider-Specific Options
+
+For **GPT Image** models, Invoke surfaces two provider-specific options in the parameters panel:
+
+- **Quality** — `low`, `medium`, `high`, or `auto`. Higher quality costs more and takes longer.
+- **Background** — `auto`, `transparent`, or `opaque`. Use `transparent` for PNG output with an alpha channel.
+
+DALL·E 2 and DALL·E 3 do not expose these options.
+
+## How Requests Are Routed
+
+- Pure text-to-image requests hit `/v1/images/generations`.
+- Any request with an init image or reference images is sent to `/v1/images/edits` instead. This is done transparently — you don't need to pick an endpoint.
+
+## Tips
+
+- **Batching** on GPT Image and DALL·E 2 tops out at 10 per request. Larger batches are split into multiple API calls.
+- **Costs** can climb quickly with high-quality GPT Image generations. Start with GPT Image 1 Mini when iterating on prompts.
+- **Rate limits** from OpenAI surface as failed invocations — retry after a short wait.
diff --git a/mkdocs.yml b/mkdocs.yml
index 4c6d3039cf2..0b29300c0ff 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -141,6 +141,10 @@ nav:
- Database: 'features/database.md'
- Gallery: 'features/gallery.md'
- Hot Keys: 'features/hotkeys.md'
+ - External Models:
+ - Overview: 'features/external-models/index.md'
+ - Google Gemini: 'features/external-models/gemini.md'
+ - OpenAI: 'features/external-models/openai.md'
- Multi-User Mode:
- User Guide: 'multiuser/user_guide.md'
- Administrator Guide: 'multiuser/admin_guide.md'
From 872b70f6d238164f5c788781d0833f6cb0674afd Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Tue, 14 Apr 2026 23:22:07 +0200
Subject: [PATCH 34/44] docs: add Alibaba Cloud DashScope provider page to
External Models guide
---
docs/features/external-models/alibabacloud.md | 49 +++++++++++++++++++
mkdocs.yml | 1 +
2 files changed, 50 insertions(+)
create mode 100644 docs/features/external-models/alibabacloud.md
diff --git a/docs/features/external-models/alibabacloud.md b/docs/features/external-models/alibabacloud.md
new file mode 100644
index 00000000000..e4fa4ca35ba
--- /dev/null
+++ b/docs/features/external-models/alibabacloud.md
@@ -0,0 +1,49 @@
+---
+title: Alibaba Cloud DashScope
+---
+
+# :material-cloud-outline: Alibaba Cloud DashScope
+
+Invoke supports Alibaba Cloud's **DashScope** image generation service, giving access to the **Qwen Image** family and **Wan 2.6** text-to-image. Qwen Image is particularly strong at bilingual (Chinese / English) text rendering.
+
+## Getting an API Key
+
+1. Sign in to [Alibaba Cloud Model Studio](https://www.alibabacloud.com/en/product/modelstudio) (the international DashScope portal).
+2. Enable **DashScope** and activate the image generation models you plan to use.
+3. Create an API key from the **API Keys** section of the console.
+
+## Configuration
+
+Add your key to `api_keys.yaml` in your Invoke root directory:
+
+```yaml
+external_alibabacloud_api_key: "your-dashscope-api-key"
+
+# Optional — default is the international endpoint. Use the China endpoint if your account lives there:
+# https://dashscope.aliyuncs.com
+external_alibabacloud_base_url: "https://dashscope-intl.aliyuncs.com"
+```
+
+Restart Invoke for the change to take effect.
+
+!!! info "International vs. China endpoints"
+ DashScope has separate international (`dashscope-intl.aliyuncs.com`) and China (`dashscope.aliyuncs.com`) deployments. Your API key only works on the deployment it was issued on — if you get authentication errors, check that `external_alibabacloud_base_url` matches.
+
+## Available Models
+
+| Model | Modes | Aspect Ratios | Batch | Notes |
+| --- | --- | --- | --- | --- |
+| **Qwen Image 2.0 Pro** | txt2img | 1:1, 4:3, 3:4, 16:9, 9:16 | up to 4 | Best quality, 2K output, excellent bilingual text. |
+| **Qwen Image 2.0** | txt2img | 1:1, 4:3, 3:4, 16:9, 9:16 | up to 4 | Faster / cheaper 2K sibling of 2.0 Pro. |
+| **Qwen Image Max** | txt2img | 1:1, 4:3, 3:4, 16:9, 9:16 | up to 4 | High quality at ~1.3K native size. |
+| **Qwen Image Edit Max** | img2img | 1:1, 4:3, 3:4, 16:9, 9:16 | up to 4 | Image editing with industrial / geometric reasoning. |
+| **Wan 2.6 Text-to-Image** | txt2img | 1:1, 4:3, 3:4, 16:9, 9:16 | up to 4 | Photorealistic T2I at 1K. |
+
+All models support **negative prompt** and **seed**.
+
+## Tips
+
+- **Bilingual prompts.** Qwen Image is unusually good at rendering Chinese text and mixed-language prompts — it's a strong choice when your prompt or desired output contains non-Latin script.
+- **Editing** is only supported by Qwen Image Edit Max. Use it for image-to-image workflows; the other Qwen Image models are text-to-image only.
+- **Batching** is capped at 4 images per request. Larger batches are split across multiple API calls.
+- **Costs** vary per model — Qwen Image 2.0 Pro is the most expensive, Qwen Image 2.0 the cheapest of the 2.0 family. Check Alibaba Cloud's pricing page before running large batches.
diff --git a/mkdocs.yml b/mkdocs.yml
index 0b29300c0ff..f09ad8d8fb9 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -145,6 +145,7 @@ nav:
- Overview: 'features/external-models/index.md'
- Google Gemini: 'features/external-models/gemini.md'
- OpenAI: 'features/external-models/openai.md'
+ - Alibaba Cloud DashScope: 'features/external-models/alibabacloud.md'
- Multi-User Mode:
- User Guide: 'multiuser/user_guide.md'
- Administrator Guide: 'multiuser/admin_guide.md'
From d42cc671afa446e4d188d56c151ebd11952f24da Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Tue, 14 Apr 2026 23:25:06 +0200
Subject: [PATCH 35/44] chore: ruff format alibabacloud provider
---
.../external_generation/providers/alibabacloud.py | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/invokeai/app/services/external_generation/providers/alibabacloud.py b/invokeai/app/services/external_generation/providers/alibabacloud.py
index b8e928ce1fe..1e3ddc5821d 100644
--- a/invokeai/app/services/external_generation/providers/alibabacloud.py
+++ b/invokeai/app/services/external_generation/providers/alibabacloud.py
@@ -60,9 +60,9 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
if not api_key:
raise ExternalProviderRequestError("Alibaba Cloud DashScope API key is not configured")
- base_url = (
- self._app_config.external_alibabacloud_base_url or "https://dashscope-intl.aliyuncs.com"
- ).rstrip("/")
+ base_url = (self._app_config.external_alibabacloud_base_url or "https://dashscope-intl.aliyuncs.com").rstrip(
+ "/"
+ )
model_id = request.model.provider_model_id
headers = {
"Content-Type": "application/json",
@@ -198,9 +198,7 @@ def _poll_task(
while True:
elapsed = time.monotonic() - start_time
if elapsed > _TASK_POLL_TIMEOUT:
- raise ExternalProviderRequestError(
- f"DashScope task {task_id} timed out after {_TASK_POLL_TIMEOUT}s"
- )
+ raise ExternalProviderRequestError(f"DashScope task {task_id} timed out after {_TASK_POLL_TIMEOUT}s")
time.sleep(_TASK_POLL_INTERVAL)
From 66f35dde2c52fda9f1aefd65b13eb91cd7d1428d Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Tue, 14 Apr 2026 23:36:19 +0200
Subject: [PATCH 36/44] chore(frontend): regenerate schema.ts to include
Alibaba Cloud DashScope config fields
---
invokeai/frontend/web/src/services/api/schema.ts | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 1a9c5312518..7d5323cea84 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -16146,6 +16146,16 @@ export type components = {
* @default false
*/
strict_password_checking?: boolean;
+ /**
+ * External Alibabacloud Api Key
+ * @description API key for Alibaba Cloud DashScope image generation.
+ */
+ external_alibabacloud_api_key?: string | null;
+ /**
+ * External Alibabacloud Base Url
+ * @description Base URL override for Alibaba Cloud DashScope image generation.
+ */
+ external_alibabacloud_base_url?: string | null;
/**
* External Gemini Api Key
* @description API key for Gemini image generation.
From a2077fa6f65c5677571488654e729da43b6890dc Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Wed, 15 Apr 2026 00:08:30 +0200
Subject: [PATCH 37/44] fix: update InvokeAIAppConfig docstring with Alibaba
Cloud DashScope config fields
---
invokeai/app/services/config/config_default.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/invokeai/app/services/config/config_default.py b/invokeai/app/services/config/config_default.py
index b391f92020b..aeaeb8b3ed4 100644
--- a/invokeai/app/services/config/config_default.py
+++ b/invokeai/app/services/config/config_default.py
@@ -122,6 +122,8 @@ class InvokeAIAppConfig(BaseSettings):
allow_unknown_models: Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation.
multiuser: Enable multiuser support. When disabled, the application runs in single-user mode using a default system account with administrator privileges. When enabled, requires user authentication and authorization.
strict_password_checking: Enforce strict password requirements. When True, passwords must contain uppercase, lowercase, and numbers. When False (default), any password is accepted but its strength (weak/moderate/strong) is reported to the user.
+ external_alibabacloud_api_key: API key for Alibaba Cloud DashScope image generation.
+ external_alibabacloud_base_url: Base URL override for Alibaba Cloud DashScope image generation.
external_gemini_api_key: API key for Gemini image generation.
external_openai_api_key: API key for OpenAI image generation.
external_gemini_base_url: Base URL override for Gemini image generation.
From aebff3bf589919e04b527965ce00d7166c08ace4 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Wed, 15 Apr 2026 00:13:43 +0200
Subject: [PATCH 38/44] chore(frontend): regenerate schema.ts after docstring
update
---
invokeai/frontend/web/src/services/api/schema.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 7d5323cea84..3e7cad04fd9 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -15758,6 +15758,8 @@ export type components = {
* allow_unknown_models: Allow installation of models that we are unable to identify. If enabled, models will be marked as `unknown` in the database, and will not have any metadata associated with them. If disabled, unknown models will be rejected during installation.
* multiuser: Enable multiuser support. When disabled, the application runs in single-user mode using a default system account with administrator privileges. When enabled, requires user authentication and authorization.
* strict_password_checking: Enforce strict password requirements. When True, passwords must contain uppercase, lowercase, and numbers. When False (default), any password is accepted but its strength (weak/moderate/strong) is reported to the user.
+ * external_alibabacloud_api_key: API key for Alibaba Cloud DashScope image generation.
+ * external_alibabacloud_base_url: Base URL override for Alibaba Cloud DashScope image generation.
* external_gemini_api_key: API key for Gemini image generation.
* external_openai_api_key: API key for OpenAI image generation.
* external_gemini_base_url: Base URL override for Gemini image generation.
From 73b15a2e9fa7937d09dc87eddb23ad7a04ca5fc8 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Wed, 15 Apr 2026 02:16:49 +0200
Subject: [PATCH 39/44] fix(external-models): address PR review feedback
- Gemini recall: write temperature, thinking_level, image_size to image metadata;
wire external graph as metadata receiver; add recall handlers.
- Canvas: gate regional guidance, inpaint mask, and control layer for external models.
- Canvas: throw a clear error on outpainting for external models (was falling back to
inpaint and hitting an API-side mask/image size mismatch).
- Workflow editor: add ui_model_provider_id filter so OpenAI and Gemini nodes only
list their own provider's models.
- Workflow editor: silently drop seed when the selected model does not support it
instead of raising a capability error.
- Remove the legacy external_image_generation invocation and the graph-builder
fallback; providers must register a dedicated node.
- Regenerate schema.ts.
- remove Gemini debug dumps to outputs/external_debug
---
.../invocations/external_image_generation.py | 59 +++++++--
invokeai/app/invocations/fields.py | 12 ++
.../external_generation_default.py | 22 +++-
.../external_generation/providers/gemini.py | 34 -----
invokeai/frontend/web/public/locales/en.json | 3 +
.../hooks/useIsEntityTypeEnabled.ts | 16 ++-
.../web/src/features/metadata/parsing.tsx | 61 +++++++++
.../ModelIdentifierFieldInputComponent.tsx | 12 +-
.../web/src/features/nodes/types/field.ts | 1 +
.../graph/generation/buildExternalGraph.ts | 26 +++-
.../util/schema/buildFieldInputTemplate.ts | 2 +
.../frontend/web/src/services/api/schema.ts | 123 ++----------------
.../test_external_image_generation.py | 9 +-
13 files changed, 202 insertions(+), 178 deletions(-)
diff --git a/invokeai/app/invocations/external_image_generation.py b/invokeai/app/invocations/external_image_generation.py
index b66affe9b0a..61455db6b2a 100644
--- a/invokeai/app/invocations/external_image_generation.py
+++ b/invokeai/app/invocations/external_image_generation.py
@@ -1,6 +1,6 @@
from typing import Any, ClassVar, Literal
-from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, invocation
+from invokeai.app.invocations.baseinvocation import BaseInvocation, invocation
from invokeai.app.invocations.fields import (
FieldDescriptions,
ImageField,
@@ -32,7 +32,10 @@ class BaseExternalImageGenerationInvocation(BaseInvocation, WithMetadata, WithBo
ui_model_type=[ModelType.ExternalImageGenerator],
ui_model_format=[ModelFormat.ExternalApi],
)
- mode: ExternalGenerationMode = InputField(default="txt2img", description="Generation mode")
+ mode: ExternalGenerationMode = InputField(
+ default="txt2img",
+ description="Generation mode. Not all modes are supported by every model; unsupported modes raise at runtime.",
+ )
prompt: str = InputField(description="Prompt")
seed: int | None = InputField(default=None, description=FieldDescriptions.seed)
num_images: int = InputField(default=1, gt=0, description="Number of images to generate")
@@ -119,6 +122,9 @@ def _build_output_metadata(
}
)
+ if self.image_size is not None:
+ metadata["image_size"] = self.image_size
+
provider_request_id = getattr(result, "provider_request_id", None)
if provider_request_id:
metadata["external_request_id"] = provider_request_id
@@ -130,21 +136,15 @@ def _build_output_metadata(
if image_seed is not None:
metadata["external_seed"] = image_seed
+ metadata.update(self._build_output_provider_metadata())
+
if not metadata:
return None
return MetadataField(root=metadata)
-
-@invocation(
- "external_image_generation",
- title="External Image Generation (Legacy)",
- tags=["external", "generation"],
- category="image",
- version="1.1.0",
- classification=Classification.Internal,
-)
-class ExternalImageGenerationInvocation(BaseExternalImageGenerationInvocation):
- """Legacy external image generation node kept for backward compatibility."""
+ def _build_output_provider_metadata(self) -> dict[str, Any]:
+ """Override in provider-specific subclasses to add recall-relevant fields to the image metadata."""
+ return {}
@invocation(
@@ -159,6 +159,14 @@ class OpenAIImageGenerationInvocation(BaseExternalImageGenerationInvocation):
provider_id = "openai"
+ model: ModelIdentifierField = InputField(
+ description=FieldDescriptions.main_model,
+ ui_model_base=[BaseModelType.External],
+ ui_model_type=[ModelType.ExternalImageGenerator],
+ ui_model_format=[ModelFormat.ExternalApi],
+ ui_model_provider_id=["openai"],
+ )
+
quality: Literal["auto", "high", "medium", "low"] = InputField(default="auto", description="Output image quality")
background: Literal["auto", "transparent", "opaque"] = InputField(
default="auto", description="Background transparency handling"
@@ -176,6 +184,15 @@ def _build_provider_options(self) -> dict[str, Any]:
options["input_fidelity"] = self.input_fidelity
return options
+ def _build_output_provider_metadata(self) -> dict[str, Any]:
+ metadata: dict[str, Any] = {
+ "openai_quality": self.quality,
+ "openai_background": self.background,
+ }
+ if self.input_fidelity is not None:
+ metadata["openai_input_fidelity"] = self.input_fidelity
+ return metadata
+
@invocation(
"gemini_image_generation",
@@ -189,6 +206,14 @@ class GeminiImageGenerationInvocation(BaseExternalImageGenerationInvocation):
provider_id = "gemini"
+ model: ModelIdentifierField = InputField(
+ description=FieldDescriptions.main_model,
+ ui_model_base=[BaseModelType.External],
+ ui_model_type=[ModelType.ExternalImageGenerator],
+ ui_model_format=[ModelFormat.ExternalApi],
+ ui_model_provider_id=["gemini"],
+ )
+
temperature: float | None = InputField(default=None, ge=0.0, le=2.0, description="Sampling temperature")
thinking_level: Literal["minimal", "high"] | None = InputField(
default=None, description="Thinking level for image generation"
@@ -201,3 +226,11 @@ def _build_provider_options(self) -> dict[str, Any] | None:
if self.thinking_level is not None:
options["thinking_level"] = self.thinking_level
return options or None
+
+ def _build_output_provider_metadata(self) -> dict[str, Any]:
+ metadata: dict[str, Any] = {}
+ if self.temperature is not None:
+ metadata["gemini_temperature"] = self.temperature
+ if self.thinking_level is not None:
+ metadata["gemini_thinking_level"] = self.thinking_level
+ return metadata
diff --git a/invokeai/app/invocations/fields.py b/invokeai/app/invocations/fields.py
index fbe0e9a6153..2fc5fd5a3c0 100644
--- a/invokeai/app/invocations/fields.py
+++ b/invokeai/app/invocations/fields.py
@@ -455,6 +455,7 @@ class InputFieldJSONSchemaExtra(BaseModel):
ui_model_type: Optional[list[ModelType]] = None
ui_model_variant: Optional[list[ClipVariantType | ModelVariantType]] = None
ui_model_format: Optional[list[ModelFormat]] = None
+ ui_model_provider_id: Optional[list[str]] = None
model_config = ConfigDict(
validate_assignment=True,
@@ -636,6 +637,7 @@ def InputField(
ui_model_type: Optional[ModelType | list[ModelType]] = None,
ui_model_variant: Optional[ClipVariantType | ModelVariantType | list[ClipVariantType | ModelVariantType]] = None,
ui_model_format: Optional[ModelFormat | list[ModelFormat]] = None,
+ ui_model_provider_id: Optional[str | list[str]] = None,
) -> Any:
"""
Creates an input field for an invocation.
@@ -685,6 +687,11 @@ def InputField(
`ui_model_format=ModelFormat.Diffusers` will show only models in the diffusers format. This arg is only valid
if this Input field is annotated as a `ModelIdentifierField`.
+ ui_model_provider_id: Specifies the external provider id(s) to filter the model list by in the Workflow Editor.
+ For example, `ui_model_provider_id="openai"` will show only models registered under the OpenAI external provider.
+ This arg is only valid if this Input field is annotated as a `ModelIdentifierField` and the target models are
+ external API models.
+
ui_choice_labels: Specifies the labels to use for the choices in an enum field. If omitted, the enum values
will be used. This arg is only valid if the field is annotated with as a `Literal`. For example,
`Literal["choice1", "choice2", "choice3"]` with `ui_choice_labels={"choice1": "Choice 1", "choice2": "Choice 2",
@@ -724,6 +731,11 @@ def InputField(
json_schema_extra_.ui_model_format = ui_model_format
else:
json_schema_extra_.ui_model_format = [ui_model_format]
+ if ui_model_provider_id is not None:
+ if isinstance(ui_model_provider_id, list):
+ json_schema_extra_.ui_model_provider_id = ui_model_provider_id
+ else:
+ json_schema_extra_.ui_model_provider_id = [ui_model_provider_id]
if ui_type is not None:
json_schema_extra_.ui_type = ui_type
diff --git a/invokeai/app/services/external_generation/external_generation_default.py b/invokeai/app/services/external_generation/external_generation_default.py
index 2622aa9b1cb..d6a266753b3 100644
--- a/invokeai/app/services/external_generation/external_generation_default.py
+++ b/invokeai/app/services/external_generation/external_generation_default.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import dataclasses
import time
from logging import Logger
from typing import TYPE_CHECKING
@@ -52,6 +53,7 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
request = self._refresh_model_capabilities(request)
resize_to_original_inpaint_size = _get_resize_target_for_inpaint(request)
request = self._bucket_request(request)
+ request = self._drop_unsupported_capabilities(request)
self._validate_request(request)
result = self._generate_with_retry(provider, request)
@@ -103,9 +105,6 @@ def _validate_request(self, request: ExternalGenerationRequest) -> None:
if request.mode not in capabilities.modes:
raise ExternalProviderCapabilityError(f"Mode '{request.mode}' is not supported by {request.model.name}")
- if request.seed is not None and not capabilities.supports_seed:
- raise ExternalProviderCapabilityError(f"Seed control is not supported by {request.model.name}")
-
if request.reference_images and not capabilities.supports_reference_images:
raise ExternalProviderCapabilityError(f"Reference images are not supported by {request.model.name}")
@@ -149,6 +148,23 @@ def _validate_request(self, request: ExternalGenerationRequest) -> None:
f"Mode '{request.mode}' requires a mask image for {request.model.name}"
)
+ def _drop_unsupported_capabilities(self, request: ExternalGenerationRequest) -> ExternalGenerationRequest:
+ """Silently drop request fields the selected model does not support so workflow-editor runs don't fail
+ when users wire them in regardless."""
+ capabilities = request.model.capabilities
+ updates: dict[str, object] = {}
+
+ if request.seed is not None and not capabilities.supports_seed:
+ self._logger.debug(
+ "Dropping seed for %s: model does not support seed control",
+ request.model.name,
+ )
+ updates["seed"] = None
+
+ if updates:
+ return dataclasses.replace(request, **updates)
+ return request
+
def _refresh_model_capabilities(self, request: ExternalGenerationRequest) -> ExternalGenerationRequest:
if self._record_store is None:
return request
diff --git a/invokeai/app/services/external_generation/providers/gemini.py b/invokeai/app/services/external_generation/providers/gemini.py
index 70cf6a09659..de2cf0e85a7 100644
--- a/invokeai/app/services/external_generation/providers/gemini.py
+++ b/invokeai/app/services/external_generation/providers/gemini.py
@@ -1,10 +1,6 @@
from __future__ import annotations
-import json
-import uuid
-
import requests
-from PIL.Image import Image as PILImageType
from invokeai.app.services.external_generation.errors import (
ExternalProviderRateLimitError,
@@ -107,8 +103,6 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
if "thinking_level" in opts:
payload["thinkingConfig"] = {"thinkingLevel": opts["thinking_level"].upper()}
- self._dump_debug_payload("request", payload)
-
response = requests.post(
endpoint,
params={"key": api_key},
@@ -128,7 +122,6 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
)
data = response.json()
- self._dump_debug_payload("response", data)
if not isinstance(data, dict):
raise ExternalProviderRequestError("Gemini response payload was not a JSON object")
images: list[ExternalGeneratedImage] = []
@@ -153,7 +146,6 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
if encoded:
image = decode_image_base64(encoded)
images.append(ExternalGeneratedImage(image=image, seed=request.seed))
- self._dump_debug_image(image)
continue
file_data = part.get("fileData") or part.get("file_data")
if isinstance(file_data, dict):
@@ -185,32 +177,6 @@ def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResu
provider_metadata={"model": request.model.provider_model_id},
)
- def _dump_debug_payload(self, label: str, payload: object) -> None:
- """TODO: remove debug payload dump once Gemini is stable."""
- try:
- outputs_path = self._app_config.outputs_path
- if outputs_path is None:
- return
- debug_dir = outputs_path / "external_debug" / "gemini"
- debug_dir.mkdir(parents=True, exist_ok=True)
- path = debug_dir / f"{label}_{uuid.uuid4().hex}.json"
- path.write_text(json.dumps(payload, indent=2, default=str), encoding="utf-8")
- except Exception as exc:
- self._logger.debug("Failed to write Gemini debug payload: %s", exc)
-
- def _dump_debug_image(self, image: "PILImageType") -> None:
- """TODO: remove debug image dump once Gemini is stable."""
- try:
- outputs_path = self._app_config.outputs_path
- if outputs_path is None:
- return
- debug_dir = outputs_path / "external_debug" / "gemini"
- debug_dir.mkdir(parents=True, exist_ok=True)
- path = debug_dir / f"decoded_{uuid.uuid4().hex}.png"
- image.save(path, format="PNG")
- except Exception as exc:
- self._logger.debug("Failed to write Gemini debug image: %s", exc)
-
def _iter_response_parts(candidate: dict[str, object]) -> list[dict[str, object]]:
content = candidate.get("content")
diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index a0bf23843e6..7136204cded 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -994,10 +994,13 @@
"dypeScale": "$t(parameters.dypeScale)",
"dypeExponent": "$t(parameters.dypeExponent)",
"generationMode": "Generation Mode",
+ "geminiTemperature": "Gemini Temperature",
+ "geminiThinkingLevel": "Gemini Thinking Level",
"guidance": "Guidance",
"height": "Height",
"imageDetails": "Image Details",
"imageDimensions": "Image Dimensions",
+ "imageSize": "Image Size",
"metadata": "Metadata",
"model": "Model",
"negativePrompt": "Negative Prompt",
diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts
index b852d119149..4c4617f1b81 100644
--- a/invokeai/frontend/web/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useIsEntityTypeEnabled.ts
@@ -1,5 +1,10 @@
import { useAppSelector } from 'app/store/storeHooks';
-import { selectIsCogView4, selectIsFluxKontext, selectIsSD3 } from 'features/controlLayers/store/paramsSlice';
+import {
+ selectIsCogView4,
+ selectIsExternal,
+ selectIsFluxKontext,
+ selectIsSD3,
+} from 'features/controlLayers/store/paramsSlice';
import type { CanvasEntityType } from 'features/controlLayers/store/types';
import { useMemo } from 'react';
import type { Equals } from 'tsafe';
@@ -9,23 +14,24 @@ export const useIsEntityTypeEnabled = (entityType: CanvasEntityType) => {
const isSD3 = useAppSelector(selectIsSD3);
const isCogView4 = useAppSelector(selectIsCogView4);
const isFluxKontext = useAppSelector(selectIsFluxKontext);
+ const isExternal = useAppSelector(selectIsExternal);
// TODO(psyche): consider using a constant to define which entity types are supported by which model,
// see invokeai/frontend/web/src/features/modelManagerV2/models.ts for ref
const isEntityTypeEnabled = useMemo(() => {
switch (entityType) {
case 'regional_guidance':
- return !isSD3 && !isCogView4 && !isFluxKontext;
+ return !isSD3 && !isCogView4 && !isFluxKontext && !isExternal;
case 'control_layer':
- return !isSD3 && !isCogView4 && !isFluxKontext;
+ return !isSD3 && !isCogView4 && !isFluxKontext && !isExternal;
case 'inpaint_mask':
- return !isFluxKontext;
+ return !isFluxKontext && !isExternal;
case 'raster_layer':
return !isFluxKontext;
default:
assert>(false);
}
- }, [entityType, isSD3, isCogView4, isFluxKontext]);
+ }, [entityType, isSD3, isCogView4, isFluxKontext, isExternal]);
return isEntityTypeEnabled;
};
diff --git a/invokeai/frontend/web/src/features/metadata/parsing.tsx b/invokeai/frontend/web/src/features/metadata/parsing.tsx
index 4f643123beb..1cad8591460 100644
--- a/invokeai/frontend/web/src/features/metadata/parsing.tsx
+++ b/invokeai/frontend/web/src/features/metadata/parsing.tsx
@@ -11,7 +11,10 @@ import {
animaQwen3EncoderModelSelected,
animaT5EncoderModelSelected,
animaVaeModelSelected,
+ geminiTemperatureChanged,
+ geminiThinkingLevelChanged,
heightChanged,
+ imageSizeChanged,
kleinQwen3EncoderModelSelected,
kleinVaeModelSelected,
negativePromptChanged,
@@ -1373,6 +1376,61 @@ const RefImages: CollectionMetadataHandler = {
};
//#endregion RefImages
+//#region External Image Size
+const ImageSize: SingleMetadataHandler = {
+ [SingleMetadataKey]: true,
+ type: 'ImageSize',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'image_size');
+ const parsed = z.string().min(1).parse(raw);
+ return Promise.resolve(parsed);
+ },
+ recall: (value, store) => {
+ store.dispatch(imageSizeChanged(value));
+ },
+ i18nKey: 'metadata.imageSize',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps) => ,
+};
+//#endregion External Image Size
+
+//#region Gemini Temperature
+const GeminiTemperature: SingleMetadataHandler = {
+ [SingleMetadataKey]: true,
+ type: 'GeminiTemperature',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'gemini_temperature');
+ const parsed = z.number().min(0).max(2).parse(raw);
+ return Promise.resolve(parsed);
+ },
+ recall: (value, store) => {
+ store.dispatch(geminiTemperatureChanged(value));
+ },
+ i18nKey: 'metadata.geminiTemperature',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps) => ,
+};
+//#endregion Gemini Temperature
+
+//#region Gemini Thinking Level
+const zGeminiThinkingLevel = z.enum(['minimal', 'high']);
+const GeminiThinkingLevel: SingleMetadataHandler<'minimal' | 'high'> = {
+ [SingleMetadataKey]: true,
+ type: 'GeminiThinkingLevel',
+ parse: (metadata, _store) => {
+ const raw = getProperty(metadata, 'gemini_thinking_level');
+ const parsed = zGeminiThinkingLevel.parse(raw);
+ return Promise.resolve(parsed);
+ },
+ recall: (value, store) => {
+ store.dispatch(geminiThinkingLevelChanged(value));
+ },
+ i18nKey: 'metadata.geminiThinkingLevel',
+ LabelComponent: MetadataLabel,
+ ValueComponent: ({ value }: SingleMetadataValueProps<'minimal' | 'high'>) => ,
+};
+//#endregion Gemini Thinking Level
+
export const ImageMetadataHandlers = {
CreatedBy,
GenerationMode,
@@ -1421,6 +1479,9 @@ export const ImageMetadataHandlers = {
LoRAs,
CanvasLayers,
RefImages,
+ ImageSize,
+ GeminiTemperature,
+ GeminiThinkingLevel,
// TODO: These had parsers in the prev implementation, but they were never actually used?
// controlNet: parseControlNet,
// controlNets: parseAllControlNets,
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx
index ca215d07258..9e28fae9883 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx
@@ -36,7 +36,11 @@ const ModelIdentifierFieldInputComponent = (props: Props) => {
return EMPTY_ARRAY;
}
- if (!fieldTemplate.ui_model_base && !fieldTemplate.ui_model_type) {
+ if (
+ !fieldTemplate.ui_model_base &&
+ !fieldTemplate.ui_model_type &&
+ !fieldTemplate.ui_model_provider_id
+ ) {
return modelConfigsAdapterSelectors.selectAll(data);
}
@@ -58,6 +62,12 @@ const ModelIdentifierFieldInputComponent = (props: Props) => {
if (fieldTemplate.ui_model_format && !fieldTemplate.ui_model_format.includes(config.format)) {
return false;
}
+ if (
+ fieldTemplate.ui_model_provider_id &&
+ (!('provider_id' in config) || !fieldTemplate.ui_model_provider_id.includes(config.provider_id as string))
+ ) {
+ return false;
+ }
return true;
});
}, [data, fieldTemplate]);
diff --git a/invokeai/frontend/web/src/features/nodes/types/field.ts b/invokeai/frontend/web/src/features/nodes/types/field.ts
index 98b20912ab2..ffd87ae3984 100644
--- a/invokeai/frontend/web/src/features/nodes/types/field.ts
+++ b/invokeai/frontend/web/src/features/nodes/types/field.ts
@@ -75,6 +75,7 @@ const zFieldInputTemplateBase = zFieldTemplateBase.extend({
ui_model_type: z.array(zModelType).nullish(),
ui_model_variant: z.array(zAnyModelVariant).nullish(),
ui_model_format: z.array(zModelFormat).nullish(),
+ ui_model_provider_id: z.array(z.string()).nullish(),
});
const zFieldOutputTemplateBase = zFieldTemplateBase.extend({
fieldKind: z.literal('output'),
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
index 2d7ee198974..6dbbd573b79 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
@@ -35,7 +35,10 @@ export const buildExternalGraph = async (arg: GraphBuilderArg): Promise = {
- id: getPrefixedId('external_image_generation'),
- type: externalNodeType ?? 'external_image_generation',
+ id: getPrefixedId(externalNodeType),
+ type: externalNodeType,
model: model as unknown as ModelIdentifierField,
mode: requestedMode,
image_size: params.imageSize ?? null,
@@ -84,7 +88,7 @@ export const buildExternalGraph = async (arg: GraphBuilderArg): Promise, 'value', 'positive_prompt');
+ if (seed) {
+ g.addEdgeToMetadata(seed, 'value', 'seed');
+ }
+
+ g.setMetadataReceivingNode(externalInvocation);
+
return {
g,
seed: seed ?? undefined,
diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts b/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts
index 27a0b21a7c9..adaa3f413ce 100644
--- a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts
@@ -499,6 +499,7 @@ export const buildFieldInputTemplate = (
ui_model_type,
ui_model_variant,
ui_model_format,
+ ui_model_provider_id,
} = fieldSchema;
// This is the base field template that is common to all fields. The builder function will add all other
@@ -519,6 +520,7 @@ export const buildFieldInputTemplate = (
ui_model_type,
ui_model_variant,
ui_model_format,
+ ui_model_provider_id,
};
if (isStatefulFieldType(fieldType)) {
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 1a9c5312518..c763c1b92f1 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -8804,109 +8804,6 @@ export type components = {
/** Num Images */
num_images?: number | null;
};
- /**
- * External Image Generation (Legacy)
- * @description Legacy external image generation node kept for backward compatibility.
- */
- ExternalImageGenerationInvocation: {
- /**
- * @description The board to save the image to
- * @default null
- */
- board?: components["schemas"]["BoardField"] | null;
- /**
- * @description Optional metadata to be saved with the image
- * @default null
- */
- metadata?: components["schemas"]["MetadataField"] | null;
- /**
- * Id
- * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
- */
- id: string;
- /**
- * Is Intermediate
- * @description Whether or not this is an intermediate invocation.
- * @default false
- */
- is_intermediate?: boolean;
- /**
- * Use Cache
- * @description Whether or not to use the cache
- * @default true
- */
- use_cache?: boolean;
- /**
- * @description Main model (UNet, VAE, CLIP) to load
- * @default null
- */
- model?: components["schemas"]["ModelIdentifierField"] | null;
- /**
- * Mode
- * @description Generation mode
- * @default txt2img
- * @enum {string}
- */
- mode?: "txt2img" | "img2img" | "inpaint";
- /**
- * Prompt
- * @description Prompt
- * @default null
- */
- prompt?: string | null;
- /**
- * Seed
- * @description Seed for random number generation
- * @default null
- */
- seed?: number | null;
- /**
- * Num Images
- * @description Number of images to generate
- * @default 1
- */
- num_images?: number;
- /**
- * Width
- * @description Width of output (px)
- * @default 1024
- */
- width?: number;
- /**
- * Height
- * @description Height of output (px)
- * @default 1024
- */
- height?: number;
- /**
- * Image Size
- * @description Image size preset (e.g. 1K, 2K, 4K)
- * @default null
- */
- image_size?: string | null;
- /**
- * @description Init image for img2img/inpaint
- * @default null
- */
- init_image?: components["schemas"]["ImageField"] | null;
- /**
- * @description Mask image for inpaint
- * @default null
- */
- mask_image?: components["schemas"]["ImageField"] | null;
- /**
- * Reference Images
- * @description Reference images
- * @default []
- */
- reference_images?: components["schemas"]["ImageField"][];
- /**
- * type
- * @default external_image_generation
- * @constant
- */
- type: "external_image_generation";
- };
/** ExternalImageSize */
ExternalImageSize: {
/** Width */
@@ -11760,7 +11657,7 @@ export type components = {
model?: components["schemas"]["ModelIdentifierField"] | null;
/**
* Mode
- * @description Generation mode
+ * @description Generation mode. Not all modes are supported by every model; unsupported modes raise at runtime.
* @default txt2img
* @enum {string}
*/
@@ -11935,7 +11832,7 @@ export type components = {
* @description The nodes in this graph
*/
nodes?: {
- [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
};
/**
* Edges
@@ -14854,6 +14751,11 @@ export type components = {
* @default null
*/
ui_model_format: components["schemas"]["ModelFormat"][] | null;
+ /**
+ * Ui Model Provider Id
+ * @default null
+ */
+ ui_model_provider_id: string[] | null;
};
/**
* InstallStatus
@@ -15219,7 +15121,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -15283,7 +15185,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -15358,7 +15260,6 @@ export type components = {
dynamic_prompt: components["schemas"]["StringCollectionOutput"];
esrgan: components["schemas"]["ImageOutput"];
expand_mask_with_fade: components["schemas"]["ImageOutput"];
- external_image_generation: components["schemas"]["ImageCollectionOutput"];
face_identifier: components["schemas"]["ImageOutput"];
face_mask_detection: components["schemas"]["FaceMaskOutput"];
face_off: components["schemas"]["FaceOffOutput"];
@@ -15609,7 +15510,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -15684,7 +15585,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExternalImageGenerationInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -23420,7 +23321,7 @@ export type components = {
model?: components["schemas"]["ModelIdentifierField"] | null;
/**
* Mode
- * @description Generation mode
+ * @description Generation mode. Not all modes are supported by every model; unsupported modes raise at runtime.
* @default txt2img
* @enum {string}
*/
diff --git a/tests/app/invocations/test_external_image_generation.py b/tests/app/invocations/test_external_image_generation.py
index 9cd2919d46d..4247a366871 100644
--- a/tests/app/invocations/test_external_image_generation.py
+++ b/tests/app/invocations/test_external_image_generation.py
@@ -4,10 +4,7 @@
import pytest
from PIL import Image
-from invokeai.app.invocations.external_image_generation import (
- ExternalImageGenerationInvocation,
- OpenAIImageGenerationInvocation,
-)
+from invokeai.app.invocations.external_image_generation import OpenAIImageGenerationInvocation
from invokeai.app.invocations.fields import ImageField
from invokeai.app.invocations.model import ModelIdentifierField
from invokeai.app.services.external_generation.external_generation_common import (
@@ -51,7 +48,7 @@ def test_external_invocation_builds_request_and_outputs() -> None:
generated_image = Image.new("RGB", (16, 16), color="black")
context = _build_context(model_config, generated_image)
- invocation = ExternalImageGenerationInvocation(
+ invocation = OpenAIImageGenerationInvocation(
id="external_node",
model=model_field,
mode="txt2img",
@@ -95,7 +92,7 @@ def test_external_graph_execution_state_runs_node() -> None:
generated_image = Image.new("RGB", (16, 16), color="black")
context = _build_context(model_config, generated_image)
- invocation = ExternalImageGenerationInvocation(
+ invocation = OpenAIImageGenerationInvocation(
id="external_node",
model=model_field,
mode="txt2img",
From 887f110f0120d90c77d5e880ec1275268465f93a Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Wed, 15 Apr 2026 02:32:24 +0200
Subject: [PATCH 40/44] feat: add AlibabaCloudImageGenerationInvocation node
for DashScope provider
---
.../invocations/external_image_generation.py | 21 ++++
.../graph/generation/buildExternalGraph.ts | 1 +
.../frontend/web/src/services/api/schema.ts | 114 +++++++++++++++++-
3 files changed, 131 insertions(+), 5 deletions(-)
diff --git a/invokeai/app/invocations/external_image_generation.py b/invokeai/app/invocations/external_image_generation.py
index 61455db6b2a..07a74ffddee 100644
--- a/invokeai/app/invocations/external_image_generation.py
+++ b/invokeai/app/invocations/external_image_generation.py
@@ -234,3 +234,24 @@ def _build_output_provider_metadata(self) -> dict[str, Any]:
if self.thinking_level is not None:
metadata["gemini_thinking_level"] = self.thinking_level
return metadata
+
+
+@invocation(
+ "alibabacloud_image_generation",
+ title="Alibaba Cloud DashScope Image Generation",
+ tags=["external", "generation", "alibabacloud", "dashscope"],
+ category="image",
+ version="1.0.0",
+)
+class AlibabaCloudImageGenerationInvocation(BaseExternalImageGenerationInvocation):
+ """Generate images using an Alibaba Cloud DashScope external model."""
+
+ provider_id = "alibabacloud"
+
+ model: ModelIdentifierField = InputField(
+ description=FieldDescriptions.main_model,
+ ui_model_base=[BaseModelType.External],
+ ui_model_type=[ModelType.ExternalImageGenerator],
+ ui_model_format=[ModelFormat.ExternalApi],
+ ui_model_provider_id=["alibabacloud"],
+ )
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
index 6dbbd573b79..aed1cf67003 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
@@ -23,6 +23,7 @@ import {
import { assert } from 'tsafe';
const EXTERNAL_PROVIDER_NODE_TYPES = {
+ alibabacloud: 'alibabacloud_image_generation',
gemini: 'gemini_image_generation',
openai: 'openai_image_generation',
} as const;
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 4407dfdba91..a2dc5f04e52 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -2686,6 +2686,109 @@ export type components = {
*/
is_active?: boolean | null;
};
+ /**
+ * Alibaba Cloud DashScope Image Generation
+ * @description Generate images using an Alibaba Cloud DashScope external model.
+ */
+ AlibabaCloudImageGenerationInvocation: {
+ /**
+ * @description The board to save the image to
+ * @default null
+ */
+ board?: components["schemas"]["BoardField"] | null;
+ /**
+ * @description Optional metadata to be saved with the image
+ * @default null
+ */
+ metadata?: components["schemas"]["MetadataField"] | null;
+ /**
+ * Id
+ * @description The id of this instance of an invocation. Must be unique among all instances of invocations.
+ */
+ id: string;
+ /**
+ * Is Intermediate
+ * @description Whether or not this is an intermediate invocation.
+ * @default false
+ */
+ is_intermediate?: boolean;
+ /**
+ * Use Cache
+ * @description Whether or not to use the cache
+ * @default true
+ */
+ use_cache?: boolean;
+ /**
+ * @description Main model (UNet, VAE, CLIP) to load
+ * @default null
+ */
+ model?: components["schemas"]["ModelIdentifierField"] | null;
+ /**
+ * Mode
+ * @description Generation mode. Not all modes are supported by every model; unsupported modes raise at runtime.
+ * @default txt2img
+ * @enum {string}
+ */
+ mode?: "txt2img" | "img2img" | "inpaint";
+ /**
+ * Prompt
+ * @description Prompt
+ * @default null
+ */
+ prompt?: string | null;
+ /**
+ * Seed
+ * @description Seed for random number generation
+ * @default null
+ */
+ seed?: number | null;
+ /**
+ * Num Images
+ * @description Number of images to generate
+ * @default 1
+ */
+ num_images?: number;
+ /**
+ * Width
+ * @description Width of output (px)
+ * @default 1024
+ */
+ width?: number;
+ /**
+ * Height
+ * @description Height of output (px)
+ * @default 1024
+ */
+ height?: number;
+ /**
+ * Image Size
+ * @description Image size preset (e.g. 1K, 2K, 4K)
+ * @default null
+ */
+ image_size?: string | null;
+ /**
+ * @description Init image for img2img/inpaint
+ * @default null
+ */
+ init_image?: components["schemas"]["ImageField"] | null;
+ /**
+ * @description Mask image for inpaint
+ * @default null
+ */
+ mask_image?: components["schemas"]["ImageField"] | null;
+ /**
+ * Reference Images
+ * @description Reference images
+ * @default []
+ */
+ reference_images?: components["schemas"]["ImageField"][];
+ /**
+ * type
+ * @default alibabacloud_image_generation
+ * @constant
+ */
+ type: "alibabacloud_image_generation";
+ };
/**
* Alpha Mask to Tensor
* @description Convert a mask image to a tensor. Opaque regions are 1 and transparent regions are 0.
@@ -11832,7 +11935,7 @@ export type components = {
* @description The nodes in this graph
*/
nodes?: {
- [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
};
/**
* Edges
@@ -15121,7 +15224,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -15185,7 +15288,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -15209,6 +15312,7 @@ export type components = {
};
InvocationOutputMap: {
add: components["schemas"]["IntegerOutput"];
+ alibabacloud_image_generation: components["schemas"]["ImageCollectionOutput"];
alpha_mask_to_tensor: components["schemas"]["MaskOutput"];
anima_denoise: components["schemas"]["LatentsOutput"];
anima_i2l: components["schemas"]["LatentsOutput"];
@@ -15510,7 +15614,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
@@ -15585,7 +15689,7 @@ export type components = {
* Invocation
* @description The ID of the invocation
*/
- invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
+ invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"];
/**
* Invocation Source Id
* @description The ID of the prepared invocation's source node
From 20254f338db21cec1b2298faa1e09ce44c562a5b Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Wed, 15 Apr 2026 02:49:49 +0200
Subject: [PATCH 41/44] fix(external-models): resolve TSC errors in metadata
parsing and external graph
- Export imageSizeChanged from paramsSlice (required by the new ImageSize
recall handler).
- Emit the external graph's metadata model entry via zModelIdentifierField
since ExternalApiModelConfig is not part of the AnyModelConfig union.
---
.../web/src/features/controlLayers/store/paramsSlice.ts | 1 +
.../nodes/util/graph/generation/buildExternalGraph.ts | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
index 02cb040fb98..a319b7ae630 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts
@@ -663,6 +663,7 @@ export const {
syncedToOptimalDimension,
resolutionPresetSelected,
+ imageSizeChanged,
paramsReset,
openaiQualityChanged,
openaiBackgroundChanged,
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
index 6dbbd573b79..1ca454ca9d7 100644
--- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildExternalGraph.ts
@@ -2,7 +2,7 @@ import { getPrefixedId } from 'features/controlLayers/konva/util';
import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
import { selectModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import { selectRefImagesSlice } from 'features/controlLayers/store/refImagesSlice';
-import { type ModelIdentifierField, zImageField } from 'features/nodes/types/common';
+import { type ModelIdentifierField, zImageField, zModelIdentifierField } from 'features/nodes/types/common';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import {
getOriginalAndScaledSizesForOtherModes,
@@ -149,7 +149,7 @@ export const buildExternalGraph = async (arg: GraphBuilderArg): Promise
Date: Wed, 15 Apr 2026 02:55:04 +0200
Subject: [PATCH 42/44] chore: prettier format
ModelIdentifierFieldInputComponent
---
.../fields/inputs/ModelIdentifierFieldInputComponent.tsx | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx
index 9e28fae9883..0b1d2fa88c9 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelIdentifierFieldInputComponent.tsx
@@ -36,11 +36,7 @@ const ModelIdentifierFieldInputComponent = (props: Props) => {
return EMPTY_ARRAY;
}
- if (
- !fieldTemplate.ui_model_base &&
- !fieldTemplate.ui_model_type &&
- !fieldTemplate.ui_model_provider_id
- ) {
+ if (!fieldTemplate.ui_model_base && !fieldTemplate.ui_model_type && !fieldTemplate.ui_model_provider_id) {
return modelConfigsAdapterSelectors.selectAll(data);
}
From 13e891078d94e51a62508af32ed9d58d8fd93c88 Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Wed, 22 Apr 2026 14:56:29 +0200
Subject: [PATCH 43/44] Chore Fix typegen
---
invokeai/frontend/web/src/services/api/schema.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 6c5d6b6ba06..1b2ab625d6f 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -15855,14 +15855,14 @@ export type components = {
* Convert Cache Dir
* Format: path
* @description Path to the converted models cache directory (DEPRECATED, but do not delete because it is needed for migration from previous versions).
- * @default models\.convert_cache
+ * @default models/.convert_cache
*/
convert_cache_dir?: string;
/**
* Download Cache Dir
* Format: path
* @description Path to the directory that contains dynamically downloaded models.
- * @default models\.download_cache
+ * @default models/.download_cache
*/
download_cache_dir?: string;
/**
From 7f4d704992679915e4d9af26705ddaf8c2a796db Mon Sep 17 00:00:00 2001
From: Alexander Eichhorn
Date: Wed, 22 Apr 2026 15:06:48 +0200
Subject: [PATCH 44/44] Chore Docs
---
docs/src/generated/settings.json | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/docs/src/generated/settings.json b/docs/src/generated/settings.json
index 32140da667a..99bb5f2f172 100644
--- a/docs/src/generated/settings.json
+++ b/docs/src/generated/settings.json
@@ -724,6 +724,28 @@
"type": "",
"validation": {}
},
+ {
+ "category": "EXTERNAL PROVIDERS",
+ "default": null,
+ "description": "API key for Alibaba Cloud DashScope image generation.",
+ "env_var": "INVOKEAI_EXTERNAL_ALIBABACLOUD_API_KEY",
+ "literal_values": [],
+ "name": "external_alibabacloud_api_key",
+ "required": false,
+ "type": "typing.Optional[str]",
+ "validation": {}
+ },
+ {
+ "category": "EXTERNAL PROVIDERS",
+ "default": null,
+ "description": "Base URL override for Alibaba Cloud DashScope image generation.",
+ "env_var": "INVOKEAI_EXTERNAL_ALIBABACLOUD_BASE_URL",
+ "literal_values": [],
+ "name": "external_alibabacloud_base_url",
+ "required": false,
+ "type": "typing.Optional[str]",
+ "validation": {}
+ },
{
"category": "EXTERNAL PROVIDERS",
"default": null,