diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py index f67393f2ea2..d2e6f6096e9 100644 --- a/invokeai/app/api/routers/model_manager.py +++ b/invokeai/app/api/routers/model_manager.py @@ -765,6 +765,7 @@ async def list_model_installs() -> List[ModelInstallJob]: * "waiting" -- Job is waiting in the queue to run * "downloading" -- Model file(s) are downloading * "running" -- Model has downloaded and the model probing and registration process is running + * "paused" -- Job is paused and can be resumed * "completed" -- Installation completed successfully * "error" -- An error occurred. Details will be in the "error_type" and "error" fields. * "cancelled" -- Job was cancelled before completion. @@ -818,6 +819,89 @@ async def cancel_model_install_job(id: int = Path(description="Model install job installer.cancel_job(job) +@model_manager_router.post( + "/install/{id}/pause", + operation_id="pause_model_install_job", + responses={ + 201: {"description": "The job was paused successfully"}, + 415: {"description": "No such job"}, + }, + status_code=201, +) +async def pause_model_install_job(id: int = Path(description="Model install job ID")) -> ModelInstallJob: + """Pause the model install job corresponding to the given job ID.""" + installer = ApiDependencies.invoker.services.model_manager.install + try: + job = installer.get_job_by_id(id) + except ValueError as e: + raise HTTPException(status_code=415, detail=str(e)) + installer.pause_job(job) + return job + + +@model_manager_router.post( + "/install/{id}/resume", + operation_id="resume_model_install_job", + responses={ + 201: {"description": "The job was resumed successfully"}, + 415: {"description": "No such job"}, + }, + status_code=201, +) +async def resume_model_install_job(id: int = Path(description="Model install job ID")) -> ModelInstallJob: + """Resume a paused model install job corresponding to the given job ID.""" + installer = ApiDependencies.invoker.services.model_manager.install + try: + job = installer.get_job_by_id(id) + except ValueError as e: + raise HTTPException(status_code=415, detail=str(e)) + installer.resume_job(job) + return job + + +@model_manager_router.post( + "/install/{id}/restart_failed", + operation_id="restart_failed_model_install_job", + responses={ + 201: {"description": "Failed files restarted successfully"}, + 415: {"description": "No such job"}, + }, + status_code=201, +) +async def restart_failed_model_install_job(id: int = Path(description="Model install job ID")) -> ModelInstallJob: + """Restart failed or non-resumable file downloads for the given job.""" + installer = ApiDependencies.invoker.services.model_manager.install + try: + job = installer.get_job_by_id(id) + except ValueError as e: + raise HTTPException(status_code=415, detail=str(e)) + installer.restart_failed(job) + return job + + +@model_manager_router.post( + "/install/{id}/restart_file", + operation_id="restart_model_install_file", + responses={ + 201: {"description": "File restarted successfully"}, + 415: {"description": "No such job"}, + }, + status_code=201, +) +async def restart_model_install_file( + id: int = Path(description="Model install job ID"), + file_source: AnyHttpUrl = Body(description="File download URL to restart"), +) -> ModelInstallJob: + """Restart a specific file download for the given job.""" + installer = ApiDependencies.invoker.services.model_manager.install + try: + job = installer.get_job_by_id(id) + except ValueError as e: + raise HTTPException(status_code=415, detail=str(e)) + installer.restart_file(job, str(file_source)) + return job + + @model_manager_router.delete( "/install", operation_id="prune_model_install_jobs", diff --git a/invokeai/app/invocations/flux2_denoise.py b/invokeai/app/invocations/flux2_denoise.py index 645f376eed3..c387a72790e 100644 --- a/invokeai/app/invocations/flux2_denoise.py +++ b/invokeai/app/invocations/flux2_denoise.py @@ -38,6 +38,9 @@ ) from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelFormat, ModelType from invokeai.backend.patches.layer_patcher import LayerPatcher +from invokeai.backend.patches.lora_conversions.flux_bfl_peft_lora_conversion_utils import ( + convert_bfl_lora_patch_to_diffusers, +) from invokeai.backend.patches.lora_conversions.flux_lora_constants import FLUX_LORA_TRANSFORMER_PREFIX from invokeai.backend.patches.model_patch_raw import ModelPatchRaw from invokeai.backend.rectified_flow.rectified_flow_inpaint_extension import RectifiedFlowInpaintExtension @@ -503,11 +506,17 @@ def _prep_inpaint_mask(self, context: InvocationContext, latents: torch.Tensor) return mask.expand_as(latents) def _lora_iterator(self, context: InvocationContext) -> Iterator[Tuple[ModelPatchRaw, float]]: - """Iterate over LoRA models to apply.""" + """Iterate over LoRA models to apply. + + Converts BFL-format LoRA keys to diffusers format if needed, since FLUX.2 Klein + uses Flux2Transformer2DModel (diffusers naming) but LoRAs may have been loaded + with BFL naming (e.g. when a Klein 4B LoRA is misidentified as FLUX.1). + """ for lora in self.transformer.loras: lora_info = context.models.load(lora.lora) assert isinstance(lora_info.model, ModelPatchRaw) - yield (lora_info.model, lora.weight) + converted = convert_bfl_lora_patch_to_diffusers(lora_info.model) + yield (converted, lora.weight) del lora_info def _build_step_callback(self, context: InvocationContext) -> Callable[[PipelineIntermediateState], None]: diff --git a/invokeai/app/invocations/flux2_klein_lora_loader.py b/invokeai/app/invocations/flux2_klein_lora_loader.py new file mode 100644 index 00000000000..b7d55b6b134 --- /dev/null +++ b/invokeai/app/invocations/flux2_klein_lora_loader.py @@ -0,0 +1,182 @@ +"""FLUX.2 Klein LoRA Loader Invocation. + +Applies LoRA models to a FLUX.2 Klein transformer and/or Qwen3 text encoder. +Unlike standard FLUX which uses CLIP+T5, Klein uses only Qwen3 for text encoding. +""" + +from typing import Optional + +from invokeai.app.invocations.baseinvocation import ( + BaseInvocation, + BaseInvocationOutput, + Classification, + invocation, + invocation_output, +) +from invokeai.app.invocations.fields import FieldDescriptions, Input, InputField, OutputField +from invokeai.app.invocations.model import LoRAField, ModelIdentifierField, Qwen3EncoderField, TransformerField +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType + + +@invocation_output("flux2_klein_lora_loader_output") +class Flux2KleinLoRALoaderOutput(BaseInvocationOutput): + """FLUX.2 Klein LoRA Loader Output""" + + transformer: Optional[TransformerField] = OutputField( + default=None, description=FieldDescriptions.transformer, title="Transformer" + ) + qwen3_encoder: Optional[Qwen3EncoderField] = OutputField( + default=None, description=FieldDescriptions.qwen3_encoder, title="Qwen3 Encoder" + ) + + +@invocation( + "flux2_klein_lora_loader", + title="Apply LoRA - Flux2 Klein", + tags=["lora", "model", "flux", "klein", "flux2"], + category="model", + version="1.0.0", + classification=Classification.Prototype, +) +class Flux2KleinLoRALoaderInvocation(BaseInvocation): + """Apply a LoRA model to a FLUX.2 Klein transformer and/or Qwen3 text encoder.""" + + lora: ModelIdentifierField = InputField( + description=FieldDescriptions.lora_model, + title="LoRA", + ui_model_base=BaseModelType.Flux2, + ui_model_type=ModelType.LoRA, + ) + weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight) + transformer: TransformerField | None = InputField( + default=None, + description=FieldDescriptions.transformer, + input=Input.Connection, + title="Transformer", + ) + qwen3_encoder: Qwen3EncoderField | None = InputField( + default=None, + title="Qwen3 Encoder", + description=FieldDescriptions.qwen3_encoder, + input=Input.Connection, + ) + + def invoke(self, context: InvocationContext) -> Flux2KleinLoRALoaderOutput: + lora_key = self.lora.key + + if not context.models.exists(lora_key): + raise ValueError(f"Unknown lora: {lora_key}!") + + # Warn if LoRA variant doesn't match transformer variant + lora_config = context.models.get_config(lora_key) + lora_variant = getattr(lora_config, "variant", None) + if lora_variant and self.transformer is not None: + transformer_config = context.models.get_config(self.transformer.transformer.key) + transformer_variant = getattr(transformer_config, "variant", None) + if transformer_variant and lora_variant != transformer_variant: + context.logger.warning( + f"LoRA variant mismatch: LoRA '{lora_config.name}' is for {lora_variant.value} " + f"but transformer is {transformer_variant.value}. This may cause shape errors." + ) + + # Check for existing LoRAs with the same key. + if self.transformer and any(lora.lora.key == lora_key for lora in self.transformer.loras): + raise ValueError(f'LoRA "{lora_key}" already applied to transformer.') + if self.qwen3_encoder and any(lora.lora.key == lora_key for lora in self.qwen3_encoder.loras): + raise ValueError(f'LoRA "{lora_key}" already applied to Qwen3 encoder.') + + output = Flux2KleinLoRALoaderOutput() + + # Attach LoRA layers to the models. + if self.transformer is not None: + output.transformer = self.transformer.model_copy(deep=True) + output.transformer.loras.append( + LoRAField( + lora=self.lora, + weight=self.weight, + ) + ) + if self.qwen3_encoder is not None: + output.qwen3_encoder = self.qwen3_encoder.model_copy(deep=True) + output.qwen3_encoder.loras.append( + LoRAField( + lora=self.lora, + weight=self.weight, + ) + ) + + return output + + +@invocation( + "flux2_klein_lora_collection_loader", + title="Apply LoRA Collection - Flux2 Klein", + tags=["lora", "model", "flux", "klein", "flux2"], + category="model", + version="1.0.0", + classification=Classification.Prototype, +) +class Flux2KleinLoRACollectionLoader(BaseInvocation): + """Applies a collection of LoRAs to a FLUX.2 Klein transformer and/or Qwen3 text encoder.""" + + loras: Optional[LoRAField | list[LoRAField]] = InputField( + default=None, description="LoRA models and weights. May be a single LoRA or collection.", title="LoRAs" + ) + + transformer: Optional[TransformerField] = InputField( + default=None, + description=FieldDescriptions.transformer, + input=Input.Connection, + title="Transformer", + ) + qwen3_encoder: Qwen3EncoderField | None = InputField( + default=None, + title="Qwen3 Encoder", + description=FieldDescriptions.qwen3_encoder, + input=Input.Connection, + ) + + def invoke(self, context: InvocationContext) -> Flux2KleinLoRALoaderOutput: + output = Flux2KleinLoRALoaderOutput() + loras = self.loras if isinstance(self.loras, list) else [self.loras] + added_loras: list[str] = [] + + if self.transformer is not None: + output.transformer = self.transformer.model_copy(deep=True) + + if self.qwen3_encoder is not None: + output.qwen3_encoder = self.qwen3_encoder.model_copy(deep=True) + + for lora in loras: + if lora is None: + continue + if lora.lora.key in added_loras: + continue + + if not context.models.exists(lora.lora.key): + raise Exception(f"Unknown lora: {lora.lora.key}!") + + assert lora.lora.base in (BaseModelType.Flux, BaseModelType.Flux2) + + # Warn if LoRA variant doesn't match transformer variant + lora_config = context.models.get_config(lora.lora.key) + lora_variant = getattr(lora_config, "variant", None) + if lora_variant and self.transformer is not None: + transformer_config = context.models.get_config(self.transformer.transformer.key) + transformer_variant = getattr(transformer_config, "variant", None) + if transformer_variant and lora_variant != transformer_variant: + context.logger.warning( + f"LoRA variant mismatch: LoRA '{lora_config.name}' is for {lora_variant.value} " + f"but transformer is {transformer_variant.value}. This may cause shape errors." + ) + + added_loras.append(lora.lora.key) + + if self.transformer is not None and output.transformer is not None: + output.transformer.loras.append(lora) + + if self.qwen3_encoder is not None and output.qwen3_encoder is not None: + output.qwen3_encoder.loras.append(lora) + + return output diff --git a/invokeai/app/services/boards/boards_default.py b/invokeai/app/services/boards/boards_default.py index 6efeaa1fea8..3ba0e7445f6 100644 --- a/invokeai/app/services/boards/boards_default.py +++ b/invokeai/app/services/boards/boards_default.py @@ -17,7 +17,7 @@ def create( board_name: str, ) -> BoardDTO: board_record = self.__invoker.services.board_records.save(board_name) - return board_record_to_dto(board_record, None, 0, 0, 0) + return board_record_to_dto(board_record, None, 0, 0) def get_dto(self, board_id: str) -> BoardDTO: board_record = self.__invoker.services.board_records.get(board_id) diff --git a/invokeai/app/services/download/download_base.py b/invokeai/app/services/download/download_base.py index 4880ab98b89..1798fd69df5 100644 --- a/invokeai/app/services/download/download_base.py +++ b/invokeai/app/services/download/download_base.py @@ -18,6 +18,7 @@ class DownloadJobStatus(str, Enum): WAITING = "waiting" # not enqueued, will not run RUNNING = "running" # actively downloading + PAUSED = "paused" # paused, can be resumed COMPLETED = "completed" # finished running CANCELLED = "cancelled" # user cancelled ERROR = "error" # terminated with an error message @@ -61,6 +62,7 @@ class DownloadJobBase(BaseModel): # internal flag _cancelled: bool = PrivateAttr(default=False) + _paused: bool = PrivateAttr(default=False) # optional event handlers passed in on creation _on_start: Optional[DownloadEventHandler] = PrivateAttr(default=None) @@ -72,6 +74,12 @@ class DownloadJobBase(BaseModel): def cancel(self) -> None: """Call to cancel the job.""" self._cancelled = True + self._paused = False + + def pause(self) -> None: + """Pause the job, preserving partial downloads.""" + self._paused = True + self._cancelled = True # cancelled and the callbacks are private attributes in order to prevent # them from being serialized and/or used in the Json Schema @@ -80,6 +88,11 @@ def cancelled(self) -> bool: """Call to cancel the job.""" return self._cancelled + @property + def paused(self) -> bool: + """Return true if job is paused.""" + return self._paused + @property def complete(self) -> bool: """Return true if job completed without errors.""" @@ -161,6 +174,17 @@ class DownloadJob(DownloadJobBase): default=None, description="Timestamp for when the download job ende1d (completed or errored)" ) content_type: Optional[str] = Field(default=None, description="Content type of downloaded file") + canonical_url: Optional[str] = Field(default=None, description="Canonical URL to request on resume") + etag: Optional[str] = Field(default=None, description="ETag from the remote server, if available") + last_modified: Optional[str] = Field(default=None, description="Last-Modified from the remote server, if available") + final_url: Optional[str] = Field(default=None, description="Final resolved URL after redirects, if available") + expected_total_bytes: Optional[int] = Field(default=None, description="Expected total size of the download") + resume_required: bool = Field(default=False, description="True if server refused resume; restart required") + resume_message: Optional[str] = Field(default=None, description="Message explaining why resume is required") + resume_from_scratch: bool = Field( + default=False, + description="True if resume metadata existed but the partial file was missing and the download restarted from the beginning", + ) def __hash__(self) -> int: """Return hash of the string representation of this object, for indexing.""" @@ -321,6 +345,10 @@ def cancel_job(self, job: DownloadJobBase) -> None: """Cancel the job, clearing partial downloads and putting it into ERROR state.""" pass + def pause_job(self, job: DownloadJobBase) -> None: # noqa D401 + """Pause the job, preserving partial downloads.""" + raise NotImplementedError + @abstractmethod def join(self) -> None: """Wait until all jobs are off the queue.""" diff --git a/invokeai/app/services/download/download_default.py b/invokeai/app/services/download/download_default.py index 0db9075ddeb..f65f00e2aee 100644 --- a/invokeai/app/services/download/download_default.py +++ b/invokeai/app/services/download/download_default.py @@ -10,6 +10,7 @@ from queue import Empty, PriorityQueue from shutil import disk_usage from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Set +from urllib.parse import urlparse import requests from pydantic.networks import AnyHttpUrl @@ -60,6 +61,8 @@ def __init__( self._app_config = app_config or get_config() self._jobs: Dict[int, DownloadJob] = {} self._download_part2parent: Dict[AnyHttpUrl, MultiFileDownloadJob] = {} + self._mfd_pending: Dict[int, list[DownloadJob]] = {} + self._mfd_active: Dict[int, DownloadJob] = {} self._next_job_id = 0 self._queue: PriorityQueue[DownloadJob] = PriorityQueue() self._stop_event = threading.Event() @@ -126,6 +129,11 @@ def submit_download_job( self._jobs[job.id] = job self._queue.put(job) + def pause_job(self, job: DownloadJobBase) -> None: + """Pause the indicated job, preserving partial downloads.""" + if job.status in [DownloadJobStatus.WAITING, DownloadJobStatus.RUNNING]: + job.pause() + def download( self, source: AnyHttpUrl, @@ -189,6 +197,10 @@ def multifile_download( dest=path, access_token=access_token or self._lookup_access_token(url), ) + if part.size and part.size > 0: + job.total_bytes = part.size + job.expected_total_bytes = part.size + job.canonical_url = str(url) mfdj.download_parts.add(job) self._download_part2parent[job.source] = mfdj if submit_job: @@ -196,15 +208,27 @@ def multifile_download( return mfdj def submit_multifile_download(self, job: MultiFileDownloadJob) -> None: - for download_job in job.download_parts: - self.submit_download_job( - download_job, - on_start=self._mfd_started, - on_progress=self._mfd_progress, - on_complete=self._mfd_complete, - on_cancelled=self._mfd_cancelled, - on_error=self._mfd_error, - ) + pending = sorted(job.download_parts, key=lambda j: str(j.source)) + self._mfd_pending[job.id] = list(pending) + self._mfd_active.pop(job.id, None) + self._submit_next_mfd_part(job) + + def _submit_next_mfd_part(self, job: MultiFileDownloadJob) -> None: + pending = self._mfd_pending.get(job.id, []) + if not pending: + return + if self._mfd_active.get(job.id) is not None: + return + download_job = pending.pop(0) + self._mfd_active[job.id] = download_job + self.submit_download_job( + download_job, + on_start=self._mfd_started, + on_progress=self._mfd_progress, + on_complete=self._mfd_complete, + on_cancelled=self._mfd_cancelled, + on_error=self._mfd_error, + ) def join(self) -> None: """Wait for all jobs to complete.""" @@ -284,12 +308,18 @@ def _download_next_item(self) -> None: except Empty: continue try: + if job.cancelled: + raise DownloadJobCancelledException("Job was cancelled before start") job.job_started = get_iso_timestamp() self._do_download(job) - self._signal_job_complete(job) + if job.status != DownloadJobStatus.COMPLETED: + self._signal_job_complete(job) except DownloadJobCancelledException: - self._signal_job_cancelled(job) - self._cleanup_cancelled_job(job) + if job.paused: + self._signal_job_paused(job) + else: + self._signal_job_cancelled(job) + self._cleanup_cancelled_job(job) except Exception as excp: job.error_type = excp.__class__.__name__ + f"({str(excp)})" job.error = traceback.format_exc() @@ -298,7 +328,6 @@ def _download_next_item(self) -> None: job.job_ended = get_iso_timestamp() self._job_terminated_event.set() # signal a change to terminal state self._download_part2parent.pop(job.source, None) # if this is a subpart of a multipart job, remove it - self._job_terminated_event.set() self._queue.task_done() self._logger.debug(f"Download queue worker thread {threading.current_thread().name} exiting.") @@ -306,22 +335,125 @@ def _download_next_item(self) -> None: def _do_download(self, job: DownloadJob) -> None: """Do the actual download.""" - url = job.source + url = job.canonical_url or str(job.source) header = {"Authorization": f"Bearer {job.access_token}"} if job.access_token else {} + had_resume_metadata = bool(job.etag or job.last_modified) open_mode = "wb" + resume_from = 0 + + if not job.dest.is_dir(): + job.download_path = job.dest + in_progress_path = self._in_progress_path(job.download_path) + if in_progress_path.exists(): + resume_from = in_progress_path.stat().st_size + job.bytes = resume_from + self._logger.debug( + f"Resume check: in-progress file found at {in_progress_path} size={resume_from} bytes" + ) + if resume_from > 0: + if job.etag: + header["If-Range"] = job.etag + elif job.last_modified: + header["If-Range"] = job.last_modified + header["Range"] = f"bytes={resume_from}-" + open_mode = "ab" + else: + self._logger.debug(f"Resume check: no in-progress file at {in_progress_path}") + elif job.download_path: + # Resume for directory downloads when we already know the filename. + in_progress_path = self._in_progress_path(job.download_path) + if in_progress_path.exists(): + resume_from = in_progress_path.stat().st_size + job.bytes = resume_from + self._logger.debug( + f"Resume check (dir): in-progress file found at {in_progress_path} size={resume_from} bytes" + ) + if resume_from > 0: + if job.etag: + header["If-Range"] = job.etag + elif job.last_modified: + header["If-Range"] = job.last_modified + header["Range"] = f"bytes={resume_from}-" + open_mode = "ab" + else: + self._logger.debug(f"Resume check (dir): no in-progress file at {in_progress_path}") + elif job.dest.is_dir(): + # Attempt to infer a single in-progress file from disk for directory downloads. + try: + candidates = sorted(job.dest.glob("*.downloading")) + except OSError: + candidates = [] + if len(candidates) == 1: + inferred = candidates[0].with_name(candidates[0].name.removesuffix(".downloading")) + job.download_path = inferred + resume_from = candidates[0].stat().st_size + job.bytes = resume_from + self._logger.debug( + f"Resume check (dir): inferred in-progress file path={candidates[0]} size={resume_from} bytes" + ) + if resume_from > 0: + if job.etag: + header["If-Range"] = job.etag + elif job.last_modified: + header["If-Range"] = job.last_modified + header["Range"] = f"bytes={resume_from}-" + open_mode = "ab" + else: + self._logger.debug( + "Resume check (dir): no prior download_path available; cannot resume from disk " + f"(candidates={len(candidates)})" + ) + + if resume_from == 0: + job.bytes = 0 + if had_resume_metadata: + job.resume_from_scratch = True + job.resume_message = "Partial file missing. Restarted download from the beginning." # Make a streaming request. This will retrieve headers including # content-length and content-disposition, but not fetch any content itself resp = self._requests.get(str(url), headers=header, stream=True) + job.final_url = str(resp.url) if resp.url else None + self._logger.debug( + "Resume response: " + f"status={resp.status_code} " + f"content_length={resp.headers.get('content-length')} " + f"content_range={resp.headers.get('Content-Range')} " + f"etag={resp.headers.get('ETag')} " + f"last_modified={resp.headers.get('Last-Modified')}" + ) + if resp.status_code == 416 and resume_from > 0: + # Range not satisfiable - local partial is already complete + expected = job.expected_total_bytes or job.total_bytes or resume_from + if resume_from == expected: + job.total_bytes = expected + job.bytes = resume_from + job.download_path = job.download_path or job.dest + self._signal_job_started(job) + self._signal_job_complete(job) + return + job.resume_required = True + job.resume_message = "Resume refused by server. Restart required." + job.pause() + raise DownloadJobCancelledException("Resume refused by server. Restart required.") if not resp.ok: - raise HTTPError(resp.reason) + host = urlparse(str(resp.url or url)).netloc + status = resp.status_code + reason = resp.reason + if status >= 500: + self._logger.error(f"Remote server error from {host}: HTTP {status} {reason}") + raise HTTPError(reason) + self._logger.error(f"Download failed from {host}: HTTP {status} {reason}") + raise HTTPError(reason) job.content_type = resp.headers.get("Content-Type") + job.etag = resp.headers.get("ETag") or job.etag + job.last_modified = resp.headers.get("Last-Modified") or job.last_modified content_length = int(resp.headers.get("content-length", 0)) - job.total_bytes = content_length if job.dest.is_dir(): - file_name = os.path.basename(str(url.path)) # default is to use the last bit of the URL + parsed_url = urlparse(str(url)) + file_name = os.path.basename(parsed_url.path) # default is to use the last bit of the URL if match := re.search('filename="(.+)"', resp.headers.get("Content-Disposition", "")): remote_name = match.group(1) @@ -336,39 +468,83 @@ def _do_download(self, job: DownloadJob) -> None: assert job.download_path + in_progress_path = self._in_progress_path(job.download_path) + + if resume_from > 0 and resp.status_code == 200: + # Server ignored Range. Restart download from scratch. + job.resume_required = True + job.resume_message = "Resume refused by server. Restart required." + job.pause() + raise DownloadJobCancelledException("Resume refused by server. Restart required.") + + if resume_from > 0 and resp.status_code == 206: + content_range = resp.headers.get("Content-Range", "") + total_from_range = None + if match := re.match(r"bytes\s+\d+-\d+/(\d+)", content_range): + total_from_range = int(match.group(1)) + if total_from_range is not None: + job.total_bytes = total_from_range + else: + job.total_bytes = resume_from + content_length + job.bytes = resume_from + job.expected_total_bytes = job.total_bytes + else: + job.total_bytes = content_length + job.expected_total_bytes = content_length + + if job.download_path.exists() and resume_from == 0: + existing_size = job.download_path.stat().st_size + if job.total_bytes > 0 and existing_size == job.total_bytes: + job.bytes = existing_size + self._signal_job_started(job) + self._signal_job_complete(job) + return + # Existing file does not match expected size; treat as corrupt and restart. + self._logger.debug( + "Resume check: existing file size mismatch; deleting and restarting " + f"path={job.download_path} existing_size={existing_size} expected={job.total_bytes}" + ) + job.download_path.unlink() + free_space = disk_usage(job.download_path.parent).free GB = 2**30 - self._logger.debug(f"Download is {job.total_bytes / GB:.2f} GB of {free_space / GB:.2f} GB free.") - if free_space < job.total_bytes: + remaining_bytes = max(job.total_bytes - job.bytes, 0) + if free_space < remaining_bytes: raise RuntimeError( - f"Free disk space {free_space / GB:.2f} GB is not enough for download of {job.total_bytes / GB:.2f} GB." + f"Free disk space {free_space / GB:.2f} GB is not enough for download of {remaining_bytes / GB:.2f} GB." ) # Don't clobber an existing file. See commit 82c2c85202f88c6d24ff84710f297cfc6ae174af # for code that instead resumes an interrupted download. - if job.download_path.exists(): + if job.download_path.exists() and resume_from == 0: raise OSError(f"[Errno 17] File {job.download_path} exists") # append ".downloading" to the path - in_progress_path = self._in_progress_path(job.download_path) - # signal caller that the download is starting. At this point, key fields such as # download_path and total_bytes will be populated. We call it here because the might # discover that the local file is already complete and generate a COMPLETED status. self._signal_job_started(job) + expected_total = job.total_bytes or job.expected_total_bytes or content_length # "range not satisfiable" - local file is at least as large as the remote file - if resp.status_code == 416 or (content_length > 0 and job.bytes >= content_length): - self._logger.warning(f"{job.download_path}: complete file found. Skipping.") + if resp.status_code == 416 or (expected_total > 0 and job.bytes >= expected_total): + self._logger.info(f"{job.download_path}: complete file found. Skipping.") return # "partial content" - local file is smaller than remote file elif resp.status_code == 206 or job.bytes > 0: - self._logger.warning(f"{job.download_path}: partial file found. Resuming") + self._logger.info(f"{job.download_path}: partial file found. Resuming") # some other error elif resp.status_code != 200: - raise HTTPError(resp.reason) + host = urlparse(str(resp.url or url)).netloc + status = resp.status_code + reason = resp.reason + if status >= 500: + self._logger.error(f"Remote server error from {host}: HTTP {status} {reason}") + raise HTTPError(reason) + self._logger.error(f"Download failed from {host}: HTTP {status} {reason}") + raise HTTPError(reason) self._logger.debug(f"{job.source}: Downloading {job.download_path}") report_delta = job.total_bytes / 100 # report every 1% change @@ -385,6 +561,12 @@ def _do_download(self, job: DownloadJob) -> None: last_report_bytes = job.bytes self._signal_job_progress(job) + if job.total_bytes > 0 and job.bytes < job.total_bytes: + job.resume_required = True + job.resume_message = "Download interrupted. Resume required." + job.pause() + raise DownloadJobCancelledException("Download interrupted. Resume required.") + # if we get here we are done and can rename the file to the original dest self._logger.debug(f"{job.source}: saved to {job.download_path} (bytes={job.bytes})") in_progress_path.rename(job.download_path) @@ -445,6 +627,23 @@ def _signal_job_cancelled(self, job: DownloadJob) -> None: parent_job.status = DownloadJobStatus.CANCELLED self._execute_cb(parent_job, "on_cancelled") + def _signal_job_paused(self, job: DownloadJob) -> None: + if job.status not in [DownloadJobStatus.RUNNING, DownloadJobStatus.WAITING]: + return + if job.download_path: + in_progress_path = self._in_progress_path(job.download_path) + if in_progress_path.exists(): + job.bytes = in_progress_path.stat().st_size + job.status = DownloadJobStatus.PAUSED + self._execute_cb(job, "on_cancelled") + if self._event_bus: + self._event_bus.emit_download_paused(job) + + if parent_job := self._download_part2parent.get(job.source, None): + if not parent_job.in_terminal_state: + parent_job.status = DownloadJobStatus.PAUSED + self._execute_cb(parent_job, "on_cancelled") + def _signal_job_error(self, job: DownloadJob, excp: Optional[Exception] = None) -> None: job.status = DownloadJobStatus.ERROR self._logger.error(f"{str(job.source)}: {traceback.format_exception(excp)}") @@ -454,6 +653,8 @@ def _signal_job_error(self, job: DownloadJob, excp: Optional[Exception] = None) self._event_bus.emit_download_error(job) def _cleanup_cancelled_job(self, job: DownloadJob) -> None: + if job.paused: + return self._logger.debug(f"Cleaning up leftover files from cancelled download job {job.download_path}") try: if job.download_path: @@ -487,38 +688,56 @@ def _mfd_progress(self, download_job: DownloadJob) -> None: self.cancel_job(part) elif mf_job.running: mf_job.total_bytes = sum(x.total_bytes for x in mf_job.download_parts) - mf_job.bytes = sum(x.total_bytes for x in mf_job.download_parts) + mf_job.bytes = sum(x.bytes for x in mf_job.download_parts) self._execute_cb(mf_job, "on_progress") def _mfd_complete(self, download_job: DownloadJob) -> None: self._logger.info(f"Download complete: {download_job.source}") + submit_next = False + mf_job: Optional[MultiFileDownloadJob] = None with self._lock: mf_job = self._download_part2parent[download_job.source] + self._mfd_active.pop(mf_job.id, None) + mf_job.total_bytes = sum(x.total_bytes for x in mf_job.download_parts) + mf_job.bytes = sum(x.bytes for x in mf_job.download_parts) # are there any more active jobs left in this task? - if mf_job.running and all(x.complete for x in mf_job.download_parts): + if all(x.complete for x in mf_job.download_parts): mf_job.status = DownloadJobStatus.COMPLETED self._execute_cb(mf_job, "on_complete") + elif not mf_job.in_terminal_state and not mf_job.paused: + submit_next = True # we're done with this sub-job self._job_terminated_event.set() + if submit_next and mf_job is not None: + self._submit_next_mfd_part(mf_job) def _mfd_cancelled(self, download_job: DownloadJob) -> None: with self._lock: mf_job = self._download_part2parent[download_job.source] assert mf_job is not None + self._mfd_active.pop(mf_job.id, None) if not mf_job.in_terminal_state: - self._logger.warning(f"Download cancelled: {download_job.source}") - mf_job.cancel() - + if download_job.paused: + self._logger.warning(f"Download paused: {download_job.source}") + mf_job.pause() + else: + self._logger.warning(f"Download cancelled: {download_job.source}") + mf_job.cancel() + + if download_job.paused: + return for s in mf_job.download_parts: self.cancel_job(s) + self._mfd_pending.pop(mf_job.id, None) def _mfd_error(self, download_job: DownloadJob, excp: Optional[Exception] = None) -> None: with self._lock: mf_job = self._download_part2parent[download_job.source] assert mf_job is not None + self._mfd_active.pop(mf_job.id, None) if not mf_job.in_terminal_state: mf_job.status = download_job.status mf_job.error = download_job.error @@ -530,6 +749,7 @@ def _mfd_error(self, download_job: DownloadJob, excp: Optional[Exception] = None for s in [x for x in mf_job.download_parts if x.running]: self.cancel_job(s) self._download_part2parent.pop(download_job.source) + self._mfd_pending.pop(mf_job.id, None) self._job_terminated_event.set() def _execute_cb( diff --git a/invokeai/app/services/events/events_base.py b/invokeai/app/services/events/events_base.py index 4c2d3c5c4cb..aa1cbb5e0ee 100644 --- a/invokeai/app/services/events/events_base.py +++ b/invokeai/app/services/events/events_base.py @@ -11,6 +11,7 @@ DownloadCancelledEvent, DownloadCompleteEvent, DownloadErrorEvent, + DownloadPausedEvent, DownloadProgressEvent, DownloadStartedEvent, EventBase, @@ -135,6 +136,10 @@ def emit_download_cancelled(self, job: "DownloadJob") -> None: """Emitted when a download is cancelled""" self.dispatch(DownloadCancelledEvent.build(job)) + def emit_download_paused(self, job: "DownloadJob") -> None: + """Emitted when a download is paused""" + self.dispatch(DownloadPausedEvent.build(job)) + def emit_download_error(self, job: "DownloadJob") -> None: """Emitted when a download encounters an error""" self.dispatch(DownloadErrorEvent.build(job)) diff --git a/invokeai/app/services/events/events_common.py b/invokeai/app/services/events/events_common.py index 082eb8a6b40..cab5861b11f 100644 --- a/invokeai/app/services/events/events_common.py +++ b/invokeai/app/services/events/events_common.py @@ -382,6 +382,17 @@ def build(cls, job: "DownloadJob") -> "DownloadCancelledEvent": return cls(source=str(job.source)) +@payload_schema.register +class DownloadPausedEvent(DownloadEventBase): + """Event model for download_paused""" + + __event_name__ = "download_paused" + + @classmethod + def build(cls, job: "DownloadJob") -> "DownloadPausedEvent": + return cls(source=str(job.source)) + + @payload_schema.register class DownloadErrorEvent(DownloadEventBase): """Event model for download_error""" diff --git a/invokeai/app/services/model_install/model_install_base.py b/invokeai/app/services/model_install/model_install_base.py index 39981071c18..96e1c351415 100644 --- a/invokeai/app/services/model_install/model_install_base.py +++ b/invokeai/app/services/model_install/model_install_base.py @@ -205,6 +205,22 @@ def prune_jobs(self) -> None: def cancel_job(self, job: ModelInstallJob) -> None: """Cancel the indicated job.""" + @abstractmethod + def pause_job(self, job: ModelInstallJob) -> None: + """Pause the indicated job, preserving partial downloads.""" + + @abstractmethod + def resume_job(self, job: ModelInstallJob) -> None: + """Resume a previously paused job.""" + + @abstractmethod + def restart_failed(self, job: ModelInstallJob) -> None: + """Restart failed or non-resumable downloads for a job.""" + + @abstractmethod + def restart_file(self, job: ModelInstallJob, file_source: str) -> None: + """Restart a specific file download for a job.""" + @abstractmethod def wait_for_job(self, job: ModelInstallJob, timeout: int = 0) -> ModelInstallJob: """Wait for the indicated job to reach a terminal state. diff --git a/invokeai/app/services/model_install/model_install_common.py b/invokeai/app/services/model_install/model_install_common.py index aa0f6d6d2ff..1006135a95e 100644 --- a/invokeai/app/services/model_install/model_install_common.py +++ b/invokeai/app/services/model_install/model_install_common.py @@ -28,6 +28,7 @@ class InstallStatus(str, Enum): DOWNLOADING = "downloading" # downloading of model files in process DOWNLOADS_DONE = "downloads_done" # downloading done, waiting to run RUNNING = "running" # being processed + PAUSED = "paused" # paused, can be resumed COMPLETED = "completed" # finished running ERROR = "error" # terminated with an error message CANCELLED = "cancelled" # terminated with an error message @@ -185,6 +186,7 @@ class ModelInstallJob(BaseModel): _install_tmpdir: Optional[Path] = PrivateAttr(default=None) _multifile_job: Optional[MultiFileDownloadJob] = PrivateAttr(default=None) _exception: Optional[Exception] = PrivateAttr(default=None) + _resume_metadata: Optional[dict] = PrivateAttr(default=None) def set_error(self, e: Exception) -> None: """Record the error and traceback from an exception.""" @@ -232,6 +234,11 @@ def downloads_done(self) -> bool: """Return true if job's downloads ae done.""" return self.status == InstallStatus.DOWNLOADS_DONE + @property + def paused(self) -> bool: + """Return true if job is paused.""" + return self.status == InstallStatus.PAUSED + @property def running(self) -> bool: """Return true if job is running.""" diff --git a/invokeai/app/services/model_install/model_install_default.py b/invokeai/app/services/model_install/model_install_default.py index 77dc3dfa70a..c1fb0c651f4 100644 --- a/invokeai/app/services/model_install/model_install_default.py +++ b/invokeai/app/services/model_install/model_install_default.py @@ -1,6 +1,7 @@ """Model installation class.""" import gc +import json import locale import os import re @@ -38,6 +39,7 @@ ) from invokeai.app.services.model_records import DuplicateModelException, ModelRecordServiceBase from invokeai.app.services.model_records.model_records_base import ModelRecordChanges +from invokeai.app.util.misc import get_iso_timestamp from invokeai.backend.model_manager.configs.base import Checkpoint_Config_Base from invokeai.backend.model_manager.configs.factory import ( AnyModelConfig, @@ -65,6 +67,9 @@ TMPDIR_PREFIX = "tmpinstall_" +# Marker file used to resume or pause remote model installs across restarts. +INSTALL_MARKER_FILENAME = ".invokeai_install.json" +INSTALL_MARKER_VERSION = 1 class ModelInstallService(ModelInstallServiceBase): @@ -102,6 +107,171 @@ def __init__( self._install_thread: Optional[threading.Thread] = None self._next_job_id = 0 + def _marker_path(self, tmpdir: Path) -> Path: + return tmpdir / INSTALL_MARKER_FILENAME + + def _write_install_marker(self, job: ModelInstallJob, status: Optional[InstallStatus] = None) -> None: + if job._install_tmpdir is None: + return + files: list[dict] = [] + if job.download_parts: + for part in job.download_parts: + files.append( + { + "url": str(part.source), + "canonical_url": part.canonical_url, + "etag": part.etag, + "last_modified": part.last_modified, + "expected_total_bytes": part.expected_total_bytes, + "final_url": part.final_url, + "download_path": part.download_path.as_posix() if part.download_path else None, + "resume_required": part.resume_required, + "resume_message": part.resume_message, + } + ) + marker = { + "version": INSTALL_MARKER_VERSION, + "source": str(job.source), + "config_in": job.config_in.model_dump(), + "status": (status or job.status).value, + "updated_at": get_iso_timestamp(), + "files": files, + } + path = self._marker_path(job._install_tmpdir) + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "wt", encoding="utf-8") as f: + json.dump(marker, f) + + def _read_install_marker(self, tmpdir: Path) -> Optional[dict]: + path = self._marker_path(tmpdir) + if not path.exists(): + return None + try: + with open(path, "rt", encoding="utf-8") as f: + marker = json.load(f) + if marker.get("version") != INSTALL_MARKER_VERSION: + return None + return marker + except Exception as e: + self._logger.warning(f"Invalid install marker in {tmpdir}: {e}") + return None + + def _delete_install_marker(self, tmpdir: Path) -> None: + path = self._marker_path(tmpdir) + if path.exists(): + try: + path.unlink() + except Exception as e: + self._logger.warning(f"Failed to remove install marker {path}: {e}") + + def _find_reusable_tmpdir(self, source: ModelSource) -> Optional[Path]: + path = self._app_config.models_path + source_str = str(source) + candidates: list[tuple[str, Path]] = [] + for tmpdir in path.glob(f"{TMPDIR_PREFIX}*"): + marker = self._read_install_marker(tmpdir) + if not marker: + continue + if marker.get("source") != source_str: + continue + status = marker.get("status") + if status in {InstallStatus.COMPLETED.value, InstallStatus.ERROR.value, InstallStatus.CANCELLED.value}: + continue + candidates.append((marker.get("updated_at", ""), tmpdir)) + if not candidates: + return None + candidates.sort(key=lambda item: item[0], reverse=True) + return candidates[0][1] + + def _restore_incomplete_installs(self) -> None: + path = self._app_config.models_path + seen_sources: set[str] = set() + for tmpdir in path.glob(f"{TMPDIR_PREFIX}*"): + marker = self._read_install_marker(tmpdir) + if not marker: + continue + status = marker.get("status") + if status in {InstallStatus.COMPLETED.value, InstallStatus.ERROR.value, InstallStatus.CANCELLED.value}: + continue + + try: + source_str = marker["source"] + if source_str in seen_sources: + self._logger.info(f"Removing duplicate temporary directory {tmpdir}") + self._safe_rmtree(tmpdir, self._logger) + continue + seen_sources.add(source_str) + source = self._guess_source(source_str) + except Exception as e: + self._logger.warning(f"Skipping install marker in {tmpdir}: {e}") + continue + + config_in = ModelRecordChanges(**(marker.get("config_in") or {})) + job = ModelInstallJob( + id=self._next_id(), + source=source, + config_in=config_in, + local_path=tmpdir, + ) + job._install_tmpdir = tmpdir + files_meta = marker.get("files") or [] + if files_meta: + job._resume_metadata = {f.get("url"): f for f in files_meta if f.get("url")} + job.status = InstallStatus(status) if status else InstallStatus.WAITING + self._install_jobs.append(job) + + if job.paused: + continue + + if job.status in [InstallStatus.DOWNLOADS_DONE, InstallStatus.RUNNING]: + job.status = InstallStatus.DOWNLOADS_DONE + self._put_in_queue(job) + else: + try: + self._resume_remote_download(job) + except Exception as e: + self._set_error(job, e) + if job._install_tmpdir is not None: + self._safe_rmtree(job._install_tmpdir, self._logger) + + def _restore_incomplete_installs_async(self) -> None: + def _run() -> None: + try: + self._logger.info("Restoring incomplete installs") + self._restore_incomplete_installs() + self._logger.info("Finished restoring incomplete installs") + except Exception as e: + self._logger.error(f"Failed to restore incomplete installs: {e}") + + threading.Thread(target=_run, daemon=True).start() + + def _resume_remote_download(self, job: ModelInstallJob) -> None: + job.status = InstallStatus.WAITING + if job.download_parts: + for part in job.download_parts: + if part.complete or part.bytes <= 0: + continue + if not part.download_path: + continue + in_progress_path = part.download_path.with_name(part.download_path.name + ".downloading") + if not in_progress_path.exists(): + part.bytes = 0 + part.resume_from_scratch = True + part.resume_message = "Partial file missing. Restarted download from the beginning." + job.bytes = sum(p.bytes for p in job.download_parts) + remote_files, metadata = self._remote_files_from_source(job.source) + subfolders = job.source.subfolders if isinstance(job.source, HFModelSource) else [] + self._enqueue_remote_download( + job=job, + source=job.source, + remote_files=remote_files, + metadata=metadata, + destdir=job._install_tmpdir or job.local_path, + subfolder=job.source.subfolder if isinstance(job.source, HFModelSource) and len(subfolders) <= 1 else None, + subfolders=subfolders if len(subfolders) > 1 else None, + resume_metadata=job._resume_metadata, + ) + @property def app_config(self) -> InvokeAIAppConfig: # noqa D102 return self._app_config @@ -138,6 +308,7 @@ def start(self, invoker: Optional[Invoker] = None) -> None: self._logger.warning(f"Missing model file: {model.name} at {model.path}") self._write_invoke_managed_models_dir_readme() + self._restore_incomplete_installs_async() def stop(self, invoker: Optional[Invoker] = None) -> None: """Stop the installer thread; after this the object can be deleted and garbage collected.""" @@ -162,8 +333,12 @@ def _write_invoke_managed_models_dir_readme(self) -> None: def _clear_pending_jobs(self) -> None: for job in self.list_jobs(): if not job.in_terminal_state: - self._logger.warning(f"Cancelling job {job.id}") - self.cancel_job(job) + if job._multifile_job is not None: + self._logger.warning(f"Pausing job {job.id}") + self.pause_job(job) + else: + self._logger.warning(f"Cancelling job {job.id}") + self.cancel_job(job) while True: try: job = self._install_queue.get(block=False) @@ -315,6 +490,76 @@ def cancel_job(self, job: ModelInstallJob) -> None: self._logger.warning(f"Cancelling {job.source}") if dj := job._multifile_job: self._download_queue.cancel_job(dj) + if job._install_tmpdir is not None: + # Mark cancelled before cleanup so we don't reuse the folder if deletion fails. + self._write_install_marker(job, status=InstallStatus.CANCELLED) + self._delete_install_marker(job._install_tmpdir) + self._safe_rmtree(job._install_tmpdir, self._logger) + + def pause_job(self, job: ModelInstallJob) -> None: + """Pause the indicated job, preserving partial downloads.""" + if job.in_terminal_state: + return + job.status = InstallStatus.PAUSED + self._logger.warning(f"Pausing {job.source}") + if dj := job._multifile_job: + for part in dj.download_parts: + self._download_queue.pause_job(part) + self._write_install_marker(job, status=InstallStatus.PAUSED) + + def resume_job(self, job: ModelInstallJob) -> None: + """Resume a previously paused job.""" + if not job.paused: + return + self._logger.info(f"Resuming {job.source}") + self._resume_remote_download(job) + + def restart_failed(self, job: ModelInstallJob) -> None: + """Restart failed or non-resumable downloads for a job.""" + if not isinstance(job.source, (HFModelSource, URLModelSource)): + return + if not job.download_parts: + return + if not any(part.resume_required or part.errored for part in job.download_parts): + return + sources_to_restart = {str(part.source) for part in job.download_parts if not part.complete} + if not sources_to_restart: + return + job.status = InstallStatus.WAITING + remote_files, metadata = self._remote_files_from_source(job.source) + remote_files = [rf for rf in remote_files if str(rf.url) in sources_to_restart] + subfolders = job.source.subfolders if isinstance(job.source, HFModelSource) else [] + self._enqueue_remote_download( + job=job, + source=job.source, + remote_files=remote_files, + metadata=metadata, + destdir=job._install_tmpdir or job.local_path, + subfolder=job.source.subfolder if isinstance(job.source, HFModelSource) and len(subfolders) <= 1 else None, + subfolders=subfolders if len(subfolders) > 1 else None, + clear_partials=True, + ) + + def restart_file(self, job: ModelInstallJob, file_source: str) -> None: + """Restart a specific file download for a job.""" + if not isinstance(job.source, (HFModelSource, URLModelSource)): + return + job.status = InstallStatus.WAITING + remote_files, metadata = self._remote_files_from_source(job.source) + remote_files = [rf for rf in remote_files if str(rf.url) == file_source] + if not remote_files: + return + subfolders = job.source.subfolders if isinstance(job.source, HFModelSource) else [] + self._enqueue_remote_download( + job=job, + source=job.source, + remote_files=remote_files, + metadata=metadata, + destdir=job._install_tmpdir or job.local_path, + subfolder=job.source.subfolder if isinstance(job.source, HFModelSource) and len(subfolders) <= 1 else None, + subfolders=subfolders if len(subfolders) > 1 else None, + clear_partials=True, + ) def prune_jobs(self) -> None: """Prune all completed and errored jobs.""" @@ -596,6 +841,9 @@ def _register_or_install(self, job: ModelInstallJob) -> None: if isinstance(job.source_metadata, (HuggingFaceMetadata)): job.config_in.source_api_response = job.source_metadata.api_response + if job._install_tmpdir is not None: + self._delete_install_marker(job._install_tmpdir) + if job.inplace: key = self.register_path(job.local_path, job.config_in) else: @@ -624,8 +872,15 @@ def _remove_dangling_install_dirs(self) -> None: """Remove leftover tmpdirs from aborted installs.""" path = self._app_config.models_path for tmpdir in path.glob(f"{TMPDIR_PREFIX}*"): - self._logger.info(f"Removing dangling temporary directory {tmpdir}") - self._safe_rmtree(tmpdir, self._logger) + marker = self._read_install_marker(tmpdir) + if marker is None: + self._logger.info(f"Removing dangling temporary directory {tmpdir}") + self._safe_rmtree(tmpdir, self._logger) + continue + status = marker.get("status") + if status in {InstallStatus.COMPLETED.value, InstallStatus.ERROR.value, InstallStatus.CANCELLED.value}: + self._logger.info(f"Removing completed/errored temporary directory {tmpdir}") + self._safe_rmtree(tmpdir, self._logger) def _scan_for_missing_models(self) -> list[AnyModelConfig]: """Scan the models directory for missing models and return a list of them.""" @@ -781,12 +1036,14 @@ def _import_remote_model( ) -> ModelInstallJob: if len(remote_files) == 0: raise ValueError(f"{source}: No downloadable files found") - destdir = Path( - mkdtemp( - dir=self._app_config.models_path, - prefix=TMPDIR_PREFIX, + destdir = self._find_reusable_tmpdir(source) + if destdir is None: + destdir = Path( + mkdtemp( + dir=self._app_config.models_path, + prefix=TMPDIR_PREFIX, + ) ) - ) install_job = ModelInstallJob( id=self._next_id(), source=source, @@ -798,26 +1055,81 @@ def _import_remote_model( ) # remember the temporary directory for later removal install_job._install_tmpdir = destdir - install_job.total_bytes = sum((x.size or 0) for x in remote_files) # Handle multiple subfolders for HFModelSource subfolders = source.subfolders if isinstance(source, HFModelSource) else [] - multifile_job = self._multifile_download( + return self._enqueue_remote_download( + job=install_job, + source=source, remote_files=remote_files, - dest=destdir, + metadata=metadata, + destdir=destdir, subfolder=source.subfolder if isinstance(source, HFModelSource) and len(subfolders) <= 1 else None, subfolders=subfolders if len(subfolders) > 1 else None, + ) + + def _enqueue_remote_download( + self, + job: ModelInstallJob, + source: HFModelSource | URLModelSource, + remote_files: List[RemoteModelFile], + metadata: Optional[AnyModelRepoMetadata], + destdir: Path, + subfolder: Optional[Path] = None, + subfolders: Optional[List[Path]] = None, + resume_metadata: Optional[dict] = None, + clear_partials: bool = False, + ) -> ModelInstallJob: + job.source_metadata = metadata + job.local_path = destdir + job._install_tmpdir = destdir + job.total_bytes = sum((x.size or 0) for x in remote_files) + + multifile_job = self._multifile_download( + remote_files=remote_files, + dest=destdir, + subfolder=subfolder, + subfolders=subfolders, access_token=source.access_token, submit_job=False, # Important! Don't submit the job until we have set our _download_cache dict ) - self._download_cache[multifile_job.id] = install_job - install_job._multifile_job = multifile_job - + if clear_partials: + for part in multifile_job.download_parts: + target_path = part.dest + if target_path.exists(): + try: + self._logger.info(f"Deleting partial file before restart: {target_path}") + target_path.unlink() + except Exception: + pass + in_progress_path = target_path.with_name(target_path.name + ".downloading") + if in_progress_path.exists(): + try: + self._logger.info(f"Deleting partial file before restart: {in_progress_path}") + in_progress_path.unlink() + except Exception: + pass + if resume_metadata: + for part in multifile_job.download_parts: + meta = resume_metadata.get(str(part.source)) + if not meta: + continue + part.canonical_url = meta.get("canonical_url") or part.canonical_url + part.etag = meta.get("etag") or part.etag + part.last_modified = meta.get("last_modified") or part.last_modified + part.expected_total_bytes = meta.get("expected_total_bytes") or part.expected_total_bytes + part.final_url = meta.get("final_url") or part.final_url + if meta.get("download_path"): + part.download_path = Path(meta.get("download_path")) + self._download_cache[multifile_job.id] = job + job._multifile_job = multifile_job + + self._write_install_marker(job, status=InstallStatus.WAITING) files_string = "file" if len(remote_files) == 1 else "files" self._logger.info(f"Queueing model install: {source} ({len(remote_files)} {files_string})") self._logger.debug(f"remote_files={remote_files}") self._download_queue.submit_multifile_download(multifile_job) - return install_job + return job def _stat_size(self, path: Path) -> int: size = 0 @@ -922,7 +1234,9 @@ def _download_started_callback(self, download_job: MultiFileDownloadJob) -> None install_job.local_path = download_job.download_path install_job.download_parts = download_job.download_parts install_job.bytes = sum(x.bytes for x in download_job.download_parts) - install_job.total_bytes = download_job.total_bytes + total_parts = sum(x.total_bytes for x in download_job.download_parts) + if total_parts > 0: + install_job.total_bytes = max(install_job.total_bytes or 0, total_parts) self._signal_job_download_started(install_job) def _download_progress_callback(self, download_job: MultiFileDownloadJob) -> None: @@ -933,7 +1247,9 @@ def _download_progress_callback(self, download_job: MultiFileDownloadJob) -> Non else: # update sizes install_job.bytes = sum(x.bytes for x in download_job.download_parts) - install_job.total_bytes = sum(x.total_bytes for x in download_job.download_parts) + total_parts = sum(x.total_bytes for x in download_job.download_parts) + if total_parts > 0: + install_job.total_bytes = max(install_job.total_bytes or 0, total_parts) self._signal_job_downloading(install_job) def _download_complete_callback(self, download_job: MultiFileDownloadJob) -> None: @@ -951,6 +1267,8 @@ def _download_error_callback(self, download_job: MultiFileDownloadJob, excp: Opt assert excp is not None self._set_error(install_job, excp) self._download_queue.cancel_job(download_job) + if install_job._install_tmpdir is not None: + self._safe_rmtree(install_job._install_tmpdir, self._logger) # Let other threads know that the number of downloads has changed self._downloads_changed_event.set() @@ -959,9 +1277,19 @@ def _download_cancelled_callback(self, download_job: MultiFileDownloadJob) -> No with self._lock: if install_job := self._download_cache.pop(download_job.id, None): self._downloads_changed_event.set() + if any(part.resume_required for part in download_job.download_parts): + install_job.status = InstallStatus.PAUSED + self._write_install_marker(install_job, status=InstallStatus.PAUSED) + self._downloads_changed_event.set() + return # if install job has already registered an error, then do not replace its status with cancelled - if not install_job.errored: + if not install_job.errored and not install_job.paused: install_job.cancel() + if install_job._install_tmpdir is not None: + # Mark cancelled before cleanup so we don't reuse the folder if deletion fails. + self._write_install_marker(install_job, status=InstallStatus.CANCELLED) + self._delete_install_marker(install_job._install_tmpdir) + self._safe_rmtree(install_job._install_tmpdir, self._logger) # Let other threads know that the number of downloads has changed self._downloads_changed_event.set() @@ -972,6 +1300,7 @@ def _download_cancelled_callback(self, download_job: MultiFileDownloadJob) -> No def _signal_job_running(self, job: ModelInstallJob) -> None: job.status = InstallStatus.RUNNING self._logger.info(f"Model install started: {job.source}") + self._write_install_marker(job, status=InstallStatus.RUNNING) if self._event_bus: self._event_bus.emit_model_install_started(job) @@ -981,6 +1310,7 @@ def _signal_job_download_started(self, job: ModelInstallJob) -> None: assert job.bytes is not None assert job.total_bytes is not None self._event_bus.emit_model_install_download_started(job) + self._write_install_marker(job, status=InstallStatus.DOWNLOADING) def _signal_job_downloading(self, job: ModelInstallJob) -> None: if self._event_bus: @@ -992,6 +1322,7 @@ def _signal_job_downloading(self, job: ModelInstallJob) -> None: def _signal_job_downloads_done(self, job: ModelInstallJob) -> None: job.status = InstallStatus.DOWNLOADS_DONE self._logger.info(f"Model download complete: {job.source}") + self._write_install_marker(job, status=InstallStatus.DOWNLOADS_DONE) if self._event_bus: self._event_bus.emit_model_install_downloads_complete(job) @@ -1000,6 +1331,8 @@ def _signal_job_completed(self, job: ModelInstallJob) -> None: assert job.config_out self._logger.info(f"Model install complete: {job.source}") self._logger.debug(f"{job.local_path} registered key {job.config_out.key}") + if job._install_tmpdir is not None: + self._delete_install_marker(job._install_tmpdir) if self._event_bus: assert job.local_path is not None assert job.config_out is not None @@ -1007,6 +1340,8 @@ def _signal_job_completed(self, job: ModelInstallJob) -> None: def _signal_job_errored(self, job: ModelInstallJob) -> None: self._logger.error(f"Model install error: {job.source}\n{job.error_type}: {job.error}") + if job._install_tmpdir is not None: + self._delete_install_marker(job._install_tmpdir) if self._event_bus: assert job.error_type is not None assert job.error is not None @@ -1014,6 +1349,8 @@ def _signal_job_errored(self, job: ModelInstallJob) -> None: def _signal_job_cancelled(self, job: ModelInstallJob) -> None: self._logger.info(f"Model install canceled: {job.source}") + if job._install_tmpdir is not None: + self._delete_install_marker(job._install_tmpdir) if self._event_bus: self._event_bus.emit_model_install_cancelled(job) diff --git a/invokeai/app/services/shared/sqlite/sqlite_database.py b/invokeai/app/services/shared/sqlite/sqlite_database.py index d14d8039701..e67aab0ea58 100644 --- a/invokeai/app/services/shared/sqlite/sqlite_database.py +++ b/invokeai/app/services/shared/sqlite/sqlite_database.py @@ -86,7 +86,7 @@ def transaction(self) -> Generator[sqlite3.Cursor, None, None]: try: yield cursor self._conn.commit() - except: + except Exception: self._conn.rollback() raise finally: diff --git a/invokeai/backend/image_util/imwatermark/vendor.py b/invokeai/backend/image_util/imwatermark/vendor.py index ef06274ff73..bb072307d64 100644 --- a/invokeai/backend/image_util/imwatermark/vendor.py +++ b/invokeai/backend/image_util/imwatermark/vendor.py @@ -50,7 +50,9 @@ def set_by_b16(self, b16): self.set_by_bytes(content) self._wmType = "b16" - def set_by_bits(self, bits=[]): + def set_by_bits(self, bits=None): + if bits is None: + bits = [] self._watermarks = [int(bit) % 2 for bit in bits] self._wmLen = len(self._watermarks) self._wmType = "bits" @@ -177,7 +179,11 @@ def decode(self, cv2Image, method="dwtDct", **configs): class EmbedMaxDct(object): - def __init__(self, watermarks=[], wmLen=8, scales=[0, 36, 36], block=4): + def __init__(self, watermarks=None, wmLen=8, scales=None, block=4): + if watermarks is None: + watermarks = [] + if scales is None: + scales = [0, 36, 36] self._watermarks = watermarks self._wmLen = wmLen self._scales = scales diff --git a/invokeai/backend/image_util/mlsd/utils.py b/invokeai/backend/image_util/mlsd/utils.py index dbadce01a4f..ee51e0f615d 100644 --- a/invokeai/backend/image_util/mlsd/utils.py +++ b/invokeai/backend/image_util/mlsd/utils.py @@ -569,13 +569,13 @@ def check_outside_inside(segments_info, connect_idx): new_segments[:, 1] = new_segments[:, 1] * 2 / input_shape[0] * original_shape[0] new_segments[:, 2] = new_segments[:, 2] * 2 / input_shape[1] * original_shape[1] new_segments[:, 3] = new_segments[:, 3] * 2 / input_shape[0] * original_shape[0] - except: + except Exception: new_segments = [] try: squares[:, :, 0] = squares[:, :, 0] * 2 / input_shape[1] * original_shape[1] squares[:, :, 1] = squares[:, :, 1] * 2 / input_shape[0] * original_shape[0] - except: + except Exception: squares = [] score_array = [] @@ -583,7 +583,7 @@ def check_outside_inside(segments_info, connect_idx): inter_points = np.array(inter_points) inter_points[:, 0] = inter_points[:, 0] * 2 / input_shape[1] * original_shape[1] inter_points[:, 1] = inter_points[:, 1] * 2 / input_shape[0] * original_shape[0] - except: + except Exception: inter_points = [] return new_segments, squares, score_array, inter_points diff --git a/invokeai/backend/model_manager/configs/factory.py b/invokeai/backend/model_manager/configs/factory.py index 021752d47bd..7702d4a5d9b 100644 --- a/invokeai/backend/model_manager/configs/factory.py +++ b/invokeai/backend/model_manager/configs/factory.py @@ -40,11 +40,13 @@ from invokeai.backend.model_manager.configs.llava_onevision import LlavaOnevision_Diffusers_Config from invokeai.backend.model_manager.configs.lora import ( ControlLoRA_LyCORIS_FLUX_Config, + LoRA_Diffusers_Flux2_Config, LoRA_Diffusers_FLUX_Config, LoRA_Diffusers_SD1_Config, LoRA_Diffusers_SD2_Config, LoRA_Diffusers_SDXL_Config, LoRA_Diffusers_ZImage_Config, + LoRA_LyCORIS_Flux2_Config, LoRA_LyCORIS_FLUX_Config, LoRA_LyCORIS_SD1_Config, LoRA_LyCORIS_SD2_Config, @@ -197,18 +199,24 @@ Annotated[ControlNet_Diffusers_SDXL_Config, ControlNet_Diffusers_SDXL_Config.get_tag()], Annotated[ControlNet_Diffusers_FLUX_Config, ControlNet_Diffusers_FLUX_Config.get_tag()], # LoRA - LyCORIS format + # IMPORTANT: FLUX.2 must be checked BEFORE FLUX.1 because FLUX.2 has specific validation + # that will reject FLUX.1 models, but FLUX.1 validation may incorrectly match FLUX.2 models Annotated[LoRA_LyCORIS_SD1_Config, LoRA_LyCORIS_SD1_Config.get_tag()], Annotated[LoRA_LyCORIS_SD2_Config, LoRA_LyCORIS_SD2_Config.get_tag()], Annotated[LoRA_LyCORIS_SDXL_Config, LoRA_LyCORIS_SDXL_Config.get_tag()], + Annotated[LoRA_LyCORIS_Flux2_Config, LoRA_LyCORIS_Flux2_Config.get_tag()], Annotated[LoRA_LyCORIS_FLUX_Config, LoRA_LyCORIS_FLUX_Config.get_tag()], Annotated[LoRA_LyCORIS_ZImage_Config, LoRA_LyCORIS_ZImage_Config.get_tag()], # LoRA - OMI format Annotated[LoRA_OMI_SDXL_Config, LoRA_OMI_SDXL_Config.get_tag()], Annotated[LoRA_OMI_FLUX_Config, LoRA_OMI_FLUX_Config.get_tag()], # LoRA - diffusers format + # IMPORTANT: FLUX.2 must be checked BEFORE FLUX.1 because FLUX.2 has specific validation + # that will reject FLUX.1 models, but FLUX.1 validation may incorrectly match FLUX.2 models Annotated[LoRA_Diffusers_SD1_Config, LoRA_Diffusers_SD1_Config.get_tag()], Annotated[LoRA_Diffusers_SD2_Config, LoRA_Diffusers_SD2_Config.get_tag()], Annotated[LoRA_Diffusers_SDXL_Config, LoRA_Diffusers_SDXL_Config.get_tag()], + Annotated[LoRA_Diffusers_Flux2_Config, LoRA_Diffusers_Flux2_Config.get_tag()], Annotated[LoRA_Diffusers_FLUX_Config, LoRA_Diffusers_FLUX_Config.get_tag()], Annotated[LoRA_Diffusers_ZImage_Config, LoRA_Diffusers_ZImage_Config.get_tag()], # ControlLoRA - diffusers format diff --git a/invokeai/backend/model_manager/configs/lora.py b/invokeai/backend/model_manager/configs/lora.py index 6d7040b9c1f..f5b80a72f00 100644 --- a/invokeai/backend/model_manager/configs/lora.py +++ b/invokeai/backend/model_manager/configs/lora.py @@ -24,6 +24,7 @@ from invokeai.backend.model_manager.omi import flux_dev_1_lora, stable_diffusion_xl_1_lora from invokeai.backend.model_manager.taxonomy import ( BaseModelType, + Flux2VariantType, FluxLoRAFormat, ModelFormat, ModelType, @@ -61,6 +62,247 @@ def _get_flux_lora_format(mod: ModelOnDisk) -> FluxLoRAFormat | None: return value +# FLUX.2 Klein context_in_dim values: 3 * Qwen3 hidden_size +# Klein 4B: 3 * 2560 = 7680, Klein 9B: 3 * 4096 = 12288 +_FLUX2_CONTEXT_IN_DIMS = {7680, 12288} + +# FLUX.2 Klein vec_in_dim values: Qwen3 hidden_size +# Klein 4B: 2560 (Qwen3-4B), Klein 9B: 4096 (Qwen3-8B) +_FLUX2_VEC_IN_DIMS = {2560, 4096} + +# FLUX.1 hidden_size is 3072. Klein 9B uses hidden_size=4096. +# Klein 4B also uses 3072, so hidden_size alone can't distinguish Klein 4B from FLUX.1. +_FLUX1_HIDDEN_SIZE = 3072 + +# FLUX.1 uses mlp_ratio=4 (ffn_dim=12288 for hidden_size=3072). +# Klein 4B uses mlp_ratio=6 (ffn_dim=18432 for hidden_size=3072). +_FLUX1_MLP_RATIO = 4 + + +def _is_flux2_lora(mod: ModelOnDisk) -> bool: + """Check if a FLUX-format LoRA is specifically for FLUX.2 (Klein) rather than FLUX.1. + + Detection is based on: + 1. Tensor shapes of embedding layers (context_embedder, vector_in) that differ between FLUX.1 and FLUX.2 + 2. Hidden size of attention layers (3072 for FLUX.1/Klein 4B, 4096 for Klein 9B) + + Returns False for ambiguous LoRAs (e.g. Klein 4B transformer-only LoRAs with no distinguishing layers). + """ + state_dict = mod.load_state_dict() + return _is_flux2_lora_state_dict(state_dict) + + +def _is_flux2_lora_state_dict(state_dict: dict[str | int, Any]) -> bool: + """Check state dict tensor shapes for FLUX.2 Klein-specific dimensions.""" + # Check diffusers/PEFT format keys (with various prefixes). + # This covers both Flux.1 diffusers naming AND Flux2 Klein diffusers naming. + for prefix in ["transformer.", "base_model.model.", ""]: + # Check context_embedder (txt_in) dimensions + # FLUX.1: context_in_dim=4096, FLUX.2 Klein 4B: 7680, Klein 9B: 12288 + ctx_key_a = f"{prefix}context_embedder.lora_A.weight" + if ctx_key_a in state_dict: + return state_dict[ctx_key_a].shape[1] in _FLUX2_CONTEXT_IN_DIMS + + # Check vector_in (time_text_embed.text_embedder) dimensions + # FLUX.1: vec_in_dim=768, FLUX.2 Klein 4B: 2560, Klein 9B: 4096 + vec_key_a = f"{prefix}time_text_embed.text_embedder.linear_1.lora_A.weight" + if vec_key_a in state_dict: + return state_dict[vec_key_a].shape[1] in _FLUX2_VEC_IN_DIMS + + # Check Flux2 Klein diffusers naming: fused QKV+MLP in single blocks. + # This key only exists in Flux2 models (Flux.1 uses separate to_q/to_k/to_v + proj_mlp). + fused_key_a = f"{prefix}single_transformer_blocks.0.attn.to_qkv_mlp_proj.lora_A.weight" + if fused_key_a in state_dict: + return True + + # Check Flux2 Klein diffusers naming: ff.linear_in (Flux.1 uses ff.net.0.proj). + ff_key_a = f"{prefix}transformer_blocks.0.ff.linear_in.lora_A.weight" + if ff_key_a in state_dict: + return True + + # Check BFL PEFT format (diffusion_model.* or base_model.model.* prefix with BFL layer names). + # Klein 9B has hidden_size=4096 (vs 3072 for FLUX.1 and Klein 4B). + # Klein 4B has same hidden_size as FLUX.1 (3072) but different mlp_ratio (6 vs 4), + # and different txt_in/vector_in dimensions. + _bfl_prefixes = ("diffusion_model.", "base_model.model.") + bfl_hidden_size: int | None = None + for key in state_dict: + if not isinstance(key, str): + continue + if not key.startswith(_bfl_prefixes): + continue + + # BFL PEFT: attention projection → check hidden_size + if key.endswith(".img_attn.proj.lora_A.weight"): + bfl_hidden_size = state_dict[key].shape[1] + if bfl_hidden_size != _FLUX1_HIDDEN_SIZE: + return True + # hidden_size=3072 is ambiguous (could be Klein 4B or FLUX.1), keep checking + + # BFL PEFT: context_embedder/txt_in + elif "txt_in" in key and key.endswith("lora_A.weight"): + return state_dict[key].shape[1] in _FLUX2_CONTEXT_IN_DIMS + + # BFL PEFT: vector_in + elif "vector_in" in key and key.endswith("lora_A.weight"): + return state_dict[key].shape[1] in _FLUX2_VEC_IN_DIMS + + # BFL PEFT: hidden_size matches FLUX.1. Check MLP ratio to distinguish Klein 4B. + # Klein 4B uses mlp_ratio=6 (ffn_dim=18432), FLUX.1 uses mlp_ratio=4 (ffn_dim=12288). + if bfl_hidden_size == _FLUX1_HIDDEN_SIZE: + for key in state_dict: + if not isinstance(key, str): + continue + if key.startswith(_bfl_prefixes) and key.endswith(".img_mlp.0.lora_B.weight"): + ffn_dim = state_dict[key].shape[0] + if ffn_dim != bfl_hidden_size * _FLUX1_MLP_RATIO: + return True + break + + # Check kohya format: look for context_embedder or vector_in keys + # Kohya format uses lora_unet_ prefix with underscores instead of dots + for key in state_dict: + if not isinstance(key, str): + continue + if key.startswith("lora_unet_txt_in.") or key.startswith("lora_unet_context_embedder."): + if key.endswith("lora_down.weight"): + return state_dict[key].shape[1] in _FLUX2_CONTEXT_IN_DIMS + if key.startswith("lora_unet_vector_in.") or key.startswith("lora_unet_time_text_embed_text_embedder_"): + if key.endswith("lora_down.weight"): + return state_dict[key].shape[1] in _FLUX2_VEC_IN_DIMS + + return False + + +def _get_flux2_lora_variant(state_dict: dict[str | int, Any]) -> Flux2VariantType | None: + """Determine FLUX.2 Klein variant (4B vs 9B) from a LoRA state dict. + + Detection is based on tensor dimensions that differ between Klein 4B and Klein 9B: + - hidden_size from attention projection: 3072 = Klein 4B, 4096 = Klein 9B + - context_in_dim from context embedder: 7680 = Klein 4B, 12288 = Klein 9B + - vec_in_dim from vector embedder: 2560 = Klein 4B, 4096 = Klein 9B + + Returns None if the variant cannot be determined (e.g. LoRA only targets layers + with identical dimensions across variants). + """ + KLEIN_4B_CONTEXT_DIM = 7680 # 3 * 2560 + KLEIN_9B_CONTEXT_DIM = 12288 # 3 * 4096 + KLEIN_4B_VEC_DIM = 2560 + KLEIN_9B_VEC_DIM = 4096 + KLEIN_4B_HIDDEN_SIZE = 3072 + KLEIN_9B_HIDDEN_SIZE = 4096 + + # Check diffusers/PEFT format keys + for prefix in ["transformer.", "base_model.model.", ""]: + # Context embedder (txt_in) dimensions + ctx_key_a = f"{prefix}context_embedder.lora_A.weight" + if ctx_key_a in state_dict: + dim = state_dict[ctx_key_a].shape[1] + if dim == KLEIN_4B_CONTEXT_DIM: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_CONTEXT_DIM: + return Flux2VariantType.Klein9B + return None + + # Vector embedder dimensions + vec_key_a = f"{prefix}time_text_embed.text_embedder.linear_1.lora_A.weight" + if vec_key_a in state_dict: + dim = state_dict[vec_key_a].shape[1] + if dim == KLEIN_4B_VEC_DIM: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_VEC_DIM: + return Flux2VariantType.Klein9B + return None + + # Attention projection hidden_size (Flux.1 diffusers naming) + attn_key_a = f"{prefix}transformer_blocks.0.attn.to_out.0.lora_A.weight" + if attn_key_a in state_dict: + dim = state_dict[attn_key_a].shape[1] + if dim == KLEIN_4B_HIDDEN_SIZE: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_HIDDEN_SIZE: + return Flux2VariantType.Klein9B + return None + + # Attention projection hidden_size (Flux2 Klein diffusers naming) + attn_key_a2 = f"{prefix}transformer_blocks.0.attn.to_add_out.lora_A.weight" + if attn_key_a2 in state_dict: + dim = state_dict[attn_key_a2].shape[1] + if dim == KLEIN_4B_HIDDEN_SIZE: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_HIDDEN_SIZE: + return Flux2VariantType.Klein9B + return None + + # Fused QKV+MLP hidden_size (Flux2 Klein diffusers naming) + fused_key_a = f"{prefix}single_transformer_blocks.0.attn.to_qkv_mlp_proj.lora_A.weight" + if fused_key_a in state_dict: + dim = state_dict[fused_key_a].shape[1] + if dim == KLEIN_4B_HIDDEN_SIZE: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_HIDDEN_SIZE: + return Flux2VariantType.Klein9B + return None + + # Check BFL PEFT format (diffusion_model.* or base_model.model.* prefix with BFL names) + _bfl_prefixes = ("diffusion_model.", "base_model.model.") + for key in state_dict: + if not isinstance(key, str): + continue + if not key.startswith(_bfl_prefixes): + continue + + # BFL PEFT: context embedder (txt_in) + if "txt_in" in key and key.endswith("lora_A.weight"): + dim = state_dict[key].shape[1] + if dim == KLEIN_4B_CONTEXT_DIM: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_CONTEXT_DIM: + return Flux2VariantType.Klein9B + return None + + # BFL PEFT: vector embedder (vector_in) + if "vector_in" in key and key.endswith("lora_A.weight"): + dim = state_dict[key].shape[1] + if dim == KLEIN_4B_VEC_DIM: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_VEC_DIM: + return Flux2VariantType.Klein9B + return None + + # BFL PEFT: attention projection + if key.endswith(".img_attn.proj.lora_A.weight"): + dim = state_dict[key].shape[1] + if dim == KLEIN_4B_HIDDEN_SIZE: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_HIDDEN_SIZE: + return Flux2VariantType.Klein9B + return None + + # Check kohya format + for key in state_dict: + if not isinstance(key, str): + continue + if key.startswith("lora_unet_txt_in.") or key.startswith("lora_unet_context_embedder."): + if key.endswith("lora_down.weight"): + dim = state_dict[key].shape[1] + if dim == KLEIN_4B_CONTEXT_DIM: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_CONTEXT_DIM: + return Flux2VariantType.Klein9B + return None + if key.startswith("lora_unet_vector_in.") or key.startswith("lora_unet_time_text_embed_text_embedder_"): + if key.endswith("lora_down.weight"): + dim = state_dict[key].shape[1] + if dim == KLEIN_4B_VEC_DIM: + return Flux2VariantType.Klein4B + if dim == KLEIN_9B_VEC_DIM: + return Flux2VariantType.Klein9B + return None + + return None + + class LoRA_OMI_Config_Base(LoRA_Config_Base): format: Literal[ModelFormat.OMI] = Field(default=ModelFormat.OMI) @@ -190,6 +432,8 @@ def _validate_looks_like_lora(cls, mod: ModelOnDisk) -> None: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: if _get_flux_lora_format(mod): + if _is_flux2_lora(mod): + return BaseModelType.Flux2 return BaseModelType.Flux state_dict = mod.load_state_dict() @@ -223,6 +467,28 @@ class LoRA_LyCORIS_FLUX_Config(LoRA_LyCORIS_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) +class LoRA_LyCORIS_Flux2_Config(LoRA_LyCORIS_Config_Base, Config_Base): + """Model config for FLUX.2 (Klein) LoRA models in LyCORIS format.""" + + base: Literal[BaseModelType.Flux2] = Field(default=BaseModelType.Flux2) + variant: Flux2VariantType | None = Field(default=None) + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_file(mod) + raise_for_override_fields(cls, override_fields) + cls._validate_looks_like_lora(mod) + cls._validate_base(mod) + override_fields.setdefault("variant", _get_flux2_lora_variant(mod.load_state_dict())) + return cls(**override_fields) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + if _get_flux_lora_format(mod) and _is_flux2_lora(mod): + return BaseModelType.Flux2 + raise NotAMatchError("model is not a FLUX.2 LoRA") + + class LoRA_LyCORIS_ZImage_Config(LoRA_LyCORIS_Config_Base, Config_Base): """Model config for Z-Image LoRA models in LyCORIS format.""" @@ -351,6 +617,8 @@ def _validate_base(cls, mod: ModelOnDisk) -> None: @classmethod def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: if _get_flux_lora_format(mod): + if _is_flux2_lora(mod): + return BaseModelType.Flux2 return BaseModelType.Flux # If we've gotten here, we assume that the LoRA is a Stable Diffusion LoRA @@ -396,6 +664,31 @@ class LoRA_Diffusers_FLUX_Config(LoRA_Diffusers_Config_Base, Config_Base): base: Literal[BaseModelType.Flux] = Field(default=BaseModelType.Flux) +class LoRA_Diffusers_Flux2_Config(LoRA_Diffusers_Config_Base, Config_Base): + """Model config for FLUX.2 (Klein) LoRA models in Diffusers format.""" + + base: Literal[BaseModelType.Flux2] = Field(default=BaseModelType.Flux2) + variant: Flux2VariantType | None = Field(default=None) + + @classmethod + def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self: + raise_if_not_dir(mod) + raise_for_override_fields(cls, override_fields) + cls._validate_base(mod) + path_to_weight_file = cls._get_weight_file_or_raise(mod) + state_dict = mod.load_state_dict(path_to_weight_file) + override_fields.setdefault("variant", _get_flux2_lora_variant(state_dict)) + return cls(**override_fields) + + @classmethod + def _get_base_or_raise(cls, mod: ModelOnDisk) -> BaseModelType: + path_to_weight_file = cls._get_weight_file_or_raise(mod) + state_dict = mod.load_state_dict(path_to_weight_file) + if _is_flux2_lora_state_dict(state_dict): + return BaseModelType.Flux2 + raise NotAMatchError("model is not a FLUX.2 Diffusers LoRA") + + class LoRA_Diffusers_ZImage_Config(LoRA_Diffusers_Config_Base, Config_Base): """Model config for Z-Image LoRA models in Diffusers format.""" diff --git a/invokeai/backend/model_manager/load/model_loaders/lora.py b/invokeai/backend/model_manager/load/model_loaders/lora.py index 2b22221151c..d39982456af 100644 --- a/invokeai/backend/model_manager/load/model_loaders/lora.py +++ b/invokeai/backend/model_manager/load/model_loaders/lora.py @@ -25,12 +25,19 @@ is_state_dict_likely_in_flux_aitoolkit_format, lora_model_from_flux_aitoolkit_state_dict, ) +from invokeai.backend.patches.lora_conversions.flux_bfl_peft_lora_conversion_utils import ( + is_state_dict_likely_in_flux_bfl_peft_format, + lora_model_from_flux2_bfl_peft_state_dict, + lora_model_from_flux_bfl_peft_state_dict, +) from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import ( is_state_dict_likely_flux_control, lora_model_from_flux_control_state_dict, ) from invokeai.backend.patches.lora_conversions.flux_diffusers_lora_conversion_utils import ( + is_state_dict_flux2_diffusers_format, is_state_dict_likely_in_flux_diffusers_format, + lora_model_from_flux2_diffusers_state_dict, lora_model_from_flux_diffusers_state_dict, ) from invokeai.backend.patches.lora_conversions.flux_kohya_lora_conversion_utils import ( @@ -101,7 +108,7 @@ def _load_model( if self._model_base == BaseModelType.StableDiffusionXL: state_dict = convert_sdxl_keys_to_diffusers_format(state_dict) model = lora_model_from_sd_state_dict(state_dict=state_dict) - elif self._model_base == BaseModelType.Flux: + elif self._model_base in (BaseModelType.Flux, BaseModelType.Flux2): if config.format is ModelFormat.OMI: # HACK(ryand): We set alpha=None for diffusers PEFT format models. These models are typically # distributed as a single file without the associated metadata containing the alpha value. We chose @@ -113,7 +120,12 @@ def _load_model( model = lora_model_from_flux_diffusers_state_dict(state_dict=state_dict, alpha=None) elif config.format is ModelFormat.LyCORIS: if is_state_dict_likely_in_flux_diffusers_format(state_dict=state_dict): - model = lora_model_from_flux_diffusers_state_dict(state_dict=state_dict, alpha=None) + if is_state_dict_flux2_diffusers_format(state_dict=state_dict): + # Flux2 Klein native diffusers naming (to_qkv_mlp_proj, ff.linear_in, etc.) + model = lora_model_from_flux2_diffusers_state_dict(state_dict=state_dict, alpha=None) + else: + # Flux.1 diffusers naming (to_q/to_k/to_v, ff.net.0.proj, etc.) + model = lora_model_from_flux_diffusers_state_dict(state_dict=state_dict, alpha=None) elif is_state_dict_likely_in_flux_kohya_format(state_dict=state_dict): model = lora_model_from_flux_kohya_state_dict(state_dict=state_dict) elif is_state_dict_likely_in_flux_onetrainer_format(state_dict=state_dict): @@ -124,6 +136,14 @@ def _load_model( model = lora_model_from_flux_aitoolkit_state_dict(state_dict=state_dict) elif is_state_dict_likely_in_flux_xlabs_format(state_dict=state_dict): model = lora_model_from_flux_xlabs_state_dict(state_dict=state_dict) + elif is_state_dict_likely_in_flux_bfl_peft_format(state_dict=state_dict): + if self._model_base == BaseModelType.Flux2: + # FLUX.2 Klein uses Flux2Transformer2DModel (diffusers naming), + # so we need to convert BFL keys to diffusers naming. + model = lora_model_from_flux2_bfl_peft_state_dict(state_dict=state_dict, alpha=None) + else: + # FLUX.1 uses BFL Flux class, so BFL keys work directly. + model = lora_model_from_flux_bfl_peft_state_dict(state_dict=state_dict, alpha=None) else: raise ValueError("LoRA model is in unsupported FLUX format") else: diff --git a/invokeai/backend/model_manager/taxonomy.py b/invokeai/backend/model_manager/taxonomy.py index eec3ac14a48..c002418a6bd 100644 --- a/invokeai/backend/model_manager/taxonomy.py +++ b/invokeai/backend/model_manager/taxonomy.py @@ -209,6 +209,7 @@ class FluxLoRAFormat(str, Enum): Control = "flux.control" AIToolkit = "flux.aitoolkit" XLabs = "flux.xlabs" + BflPeft = "flux.bfl_peft" AnyVariant: TypeAlias = Union[ diff --git a/invokeai/backend/patches/lora_conversions/flux_bfl_peft_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/flux_bfl_peft_lora_conversion_utils.py new file mode 100644 index 00000000000..faef5fb200a --- /dev/null +++ b/invokeai/backend/patches/lora_conversions/flux_bfl_peft_lora_conversion_utils.py @@ -0,0 +1,376 @@ +"""Utilities for detecting and converting FLUX LoRAs in BFL PEFT format. + +This format uses BFL internal key names (double_blocks, single_blocks, etc.) with a +'diffusion_model.' prefix and PEFT-style LoRA suffixes (lora_A.weight, lora_B.weight). + +Example keys: + diffusion_model.double_blocks.0.img_attn.proj.lora_A.weight + diffusion_model.double_blocks.0.img_attn.qkv.lora_B.weight + diffusion_model.single_blocks.0.linear1.lora_A.weight + +This format is used by some training tools (e.g. SimpleTuner, ComfyUI-based trainers) +and is common for FLUX.2 Klein LoRAs. +""" + +import re +from typing import Dict + +import torch + +from invokeai.backend.patches.layers.base_layer_patch import BaseLayerPatch +from invokeai.backend.patches.layers.lora_layer import LoRALayer +from invokeai.backend.patches.layers.utils import any_lora_layer_from_state_dict +from invokeai.backend.patches.lora_conversions.flux_lora_constants import FLUX_LORA_TRANSFORMER_PREFIX +from invokeai.backend.patches.model_patch_raw import ModelPatchRaw + +# The prefixes used in BFL PEFT format LoRAs. +# Most commonly "diffusion_model.", but some PEFT-wrapped variants use "base_model.model.". +_BFL_PEFT_PREFIX = "diffusion_model." +_PEFT_BASE_MODEL_PREFIX = "base_model.model." +_BFL_PEFT_PREFIXES = (_BFL_PEFT_PREFIX, _PEFT_BASE_MODEL_PREFIX) + +# Key patterns that identify FLUX architecture in BFL format +_BFL_FLUX_BLOCK_PREFIXES = ( + f"{_BFL_PEFT_PREFIX}double_blocks.", + f"{_BFL_PEFT_PREFIX}single_blocks.", + f"{_PEFT_BASE_MODEL_PREFIX}double_blocks.", + f"{_PEFT_BASE_MODEL_PREFIX}single_blocks.", +) + +# Regex patterns for converting BFL layer names to diffusers naming (for FLUX.2 Klein). +# BFL uses fused QKV, diffusers uses separate Q/K/V for double blocks. +_DOUBLE_BLOCK_RE = re.compile(r"^double_blocks\.(\d+)\.(.+)$") +_SINGLE_BLOCK_RE = re.compile(r"^single_blocks\.(\d+)\.(.+)$") + +# Mapping of BFL double block layer suffixes to diffusers equivalents (simple renames). +_DOUBLE_BLOCK_RENAMES: dict[str, str] = { + "img_attn.proj": "attn.to_out.0", + "txt_attn.proj": "attn.to_add_out", + "img_mlp.0": "ff.linear_in", + "img_mlp.2": "ff.linear_out", + "txt_mlp.0": "ff_context.linear_in", + "txt_mlp.2": "ff_context.linear_out", +} + +# Mapping of BFL single block layer suffixes to diffusers equivalents. +_SINGLE_BLOCK_RENAMES: dict[str, str] = { + "linear1": "attn.to_qkv_mlp_proj", + "linear2": "attn.to_out", +} + + +def is_state_dict_likely_in_flux_bfl_peft_format(state_dict: dict[str | int, torch.Tensor]) -> bool: + """Checks if the provided state dict is likely in the BFL PEFT FLUX LoRA format. + + This format uses BFL key names (double_blocks, single_blocks, img_attn, etc.) with PEFT LoRA + suffixes (lora_A.weight, lora_B.weight). The keys may be prefixed with either 'diffusion_model.' + (common for ComfyUI/SimpleTuner) or 'base_model.model.' (PEFT-wrapped variant). + """ + str_keys = [k for k in state_dict.keys() if isinstance(k, str)] + if not str_keys: + return False + + # All keys must be in PEFT format (lora_A.weight / lora_B.weight) + all_peft = all(k.endswith(("lora_A.weight", "lora_B.weight")) for k in str_keys) + if not all_peft: + return False + + # Must have at least some keys with FLUX block structure (double_blocks/single_blocks) + has_flux_blocks = any(k.startswith(_BFL_FLUX_BLOCK_PREFIXES) for k in str_keys) + if not has_flux_blocks: + return False + + # All keys should share the same recognized prefix + all_have_prefix = all(k.startswith(_BFL_PEFT_PREFIXES) for k in str_keys) + + return all_have_prefix + + +def _strip_bfl_peft_prefix(key: str) -> str: + """Strip the BFL PEFT prefix ('diffusion_model.' or 'base_model.model.') from a key.""" + for prefix in _BFL_PEFT_PREFIXES: + if key.startswith(prefix): + return key[len(prefix) :] + return key + + +def lora_model_from_flux_bfl_peft_state_dict( + state_dict: Dict[str, torch.Tensor], alpha: float | None = None +) -> ModelPatchRaw: + """Convert a BFL PEFT format FLUX LoRA state dict to a ModelPatchRaw. + + The conversion is straightforward: strip the prefix ('diffusion_model.' or 'base_model.model.') + to get the BFL internal key names, which are already the format used by InvokeAI internally. + """ + # Group keys by layer + grouped_state_dict: dict[str, dict[str, torch.Tensor]] = {} + for key, value in state_dict.items(): + # Strip the prefix + if isinstance(key, str): + key = _strip_bfl_peft_prefix(key) + + # Split off the lora_A.weight / lora_B.weight suffix + parts = key.rsplit(".", maxsplit=2) + layer_name = parts[0] + suffix = ".".join(parts[1:]) + + if layer_name not in grouped_state_dict: + grouped_state_dict[layer_name] = {} + + # Convert PEFT naming to InvokeAI naming + if suffix == "lora_A.weight": + grouped_state_dict[layer_name]["lora_down.weight"] = value + elif suffix == "lora_B.weight": + grouped_state_dict[layer_name]["lora_up.weight"] = value + else: + grouped_state_dict[layer_name][suffix] = value + + # Add alpha if provided + if alpha is not None: + for layer_state_dict in grouped_state_dict.values(): + layer_state_dict["alpha"] = torch.tensor(alpha) + + # Build LoRA layers with the transformer prefix + layers = {} + for layer_key, layer_state_dict in grouped_state_dict.items(): + layers[f"{FLUX_LORA_TRANSFORMER_PREFIX}{layer_key}"] = any_lora_layer_from_state_dict(layer_state_dict) + + return ModelPatchRaw(layers=layers) + + +def lora_model_from_flux2_bfl_peft_state_dict( + state_dict: Dict[str, torch.Tensor], alpha: float | None = None +) -> ModelPatchRaw: + """Convert a BFL PEFT format FLUX LoRA state dict for use with FLUX.2 Klein (diffusers model). + + FLUX.2 Klein models are loaded as Flux2Transformer2DModel (diffusers), which uses different + layer naming than BFL's internal format: + - double_blocks.{i} → transformer_blocks.{i} + - single_blocks.{i} → single_transformer_blocks.{i} + - Fused QKV (img_attn.qkv) → separate Q/K/V (attn.to_q, attn.to_k, attn.to_v) + + This function converts BFL PEFT keys to diffusers naming and splits fused QKV LoRAs + into separate Q/K/V LoRA layers. + """ + # First, strip the prefix and group by BFL layer name with PEFT→InvokeAI naming. + grouped_state_dict: dict[str, dict[str, torch.Tensor]] = {} + for key, value in state_dict.items(): + if isinstance(key, str): + key = _strip_bfl_peft_prefix(key) + + parts = key.rsplit(".", maxsplit=2) + layer_name = parts[0] + suffix = ".".join(parts[1:]) + + if layer_name not in grouped_state_dict: + grouped_state_dict[layer_name] = {} + + if suffix == "lora_A.weight": + grouped_state_dict[layer_name]["lora_down.weight"] = value + elif suffix == "lora_B.weight": + grouped_state_dict[layer_name]["lora_up.weight"] = value + else: + grouped_state_dict[layer_name][suffix] = value + + if alpha is not None: + for layer_state_dict in grouped_state_dict.values(): + layer_state_dict["alpha"] = torch.tensor(alpha) + + # Now convert BFL layer names to diffusers naming, splitting fused QKV as needed. + layers: dict[str, any] = {} + for bfl_key, layer_sd in grouped_state_dict.items(): + diffusers_layers = _convert_bfl_layer_to_diffusers(bfl_key, layer_sd) + for diff_key, diff_sd in diffusers_layers: + layers[f"{FLUX_LORA_TRANSFORMER_PREFIX}{diff_key}"] = any_lora_layer_from_state_dict(diff_sd) + + return ModelPatchRaw(layers=layers) + + +def _convert_bfl_layer_to_diffusers( + bfl_key: str, layer_sd: dict[str, torch.Tensor] +) -> list[tuple[str, dict[str, torch.Tensor]]]: + """Convert a single BFL-named LoRA layer to one or more diffusers-named layers. + + Returns a list of (diffusers_key, layer_state_dict) tuples. Most layers produce one entry, + but fused QKV layers are split into three separate Q/K/V entries. + """ + # Double blocks + m = _DOUBLE_BLOCK_RE.match(bfl_key) + if m: + idx, rest = m.group(1), m.group(2) + prefix = f"transformer_blocks.{idx}" + + # Fused image QKV → split into separate Q, K, V + if rest == "img_attn.qkv": + return _split_qkv_lora( + layer_sd, + q_key=f"{prefix}.attn.to_q", + k_key=f"{prefix}.attn.to_k", + v_key=f"{prefix}.attn.to_v", + ) + # Fused text QKV → split into separate Q, K, V + if rest == "txt_attn.qkv": + return _split_qkv_lora( + layer_sd, + q_key=f"{prefix}.attn.add_q_proj", + k_key=f"{prefix}.attn.add_k_proj", + v_key=f"{prefix}.attn.add_v_proj", + ) + # Simple renames + if rest in _DOUBLE_BLOCK_RENAMES: + return [(f"{prefix}.{_DOUBLE_BLOCK_RENAMES[rest]}", layer_sd)] + + # Fallback: keep as-is under the new prefix + return [(f"{prefix}.{rest}", layer_sd)] + + # Single blocks + m = _SINGLE_BLOCK_RE.match(bfl_key) + if m: + idx, rest = m.group(1), m.group(2) + prefix = f"single_transformer_blocks.{idx}" + + if rest in _SINGLE_BLOCK_RENAMES: + return [(f"{prefix}.{_SINGLE_BLOCK_RENAMES[rest]}", layer_sd)] + + return [(f"{prefix}.{rest}", layer_sd)] + + # Non-block keys (embedders, etc.) - pass through unchanged + return [(bfl_key, layer_sd)] + + +def _split_qkv_lora( + layer_sd: dict[str, torch.Tensor], + q_key: str, + k_key: str, + v_key: str, +) -> list[tuple[str, dict[str, torch.Tensor]]]: + """Split a fused QKV LoRA layer into separate Q, K, V LoRA layers. + + BFL uses fused QKV: lora_down [rank, hidden], lora_up [3*hidden, rank]. + Diffusers uses separate layers: each gets lora_down (shared/cloned) and a third of lora_up. + """ + lora_down = layer_sd["lora_down.weight"] # [rank, hidden] + lora_up = layer_sd["lora_up.weight"] # [3*hidden, rank] + alpha = layer_sd.get("alpha") + + # Split lora_up into 3 equal parts along dim 0 + up_q, up_k, up_v = lora_up.chunk(3, dim=0) + + result = [] + for key, up_part in [(q_key, up_q), (k_key, up_k), (v_key, up_v)]: + sd: dict[str, torch.Tensor] = { + "lora_down.weight": lora_down.clone(), + "lora_up.weight": up_part, + } + if alpha is not None: + sd["alpha"] = alpha + result.append((key, sd)) + + return result + + +def convert_bfl_lora_patch_to_diffusers(patch: ModelPatchRaw) -> ModelPatchRaw: + """Convert a ModelPatchRaw with BFL-format layer keys to diffusers-format keys. + + This handles LoRAs that were loaded with the FLUX.1 BFL PEFT converter (which keeps BFL keys) + but need to be applied to a FLUX.2 Klein model (which uses diffusers module names). + + If the patch doesn't contain BFL-format keys, it is returned unchanged. + """ + prefix = FLUX_LORA_TRANSFORMER_PREFIX + prefix_len = len(prefix) + + # Check if any layer keys are in BFL format (contain double_blocks or single_blocks) + has_bfl_keys = any( + k.startswith(prefix) + and (k[prefix_len:].startswith("double_blocks.") or k[prefix_len:].startswith("single_blocks.")) + for k in patch.layers + ) + if not has_bfl_keys: + return patch + + new_layers: dict[str, BaseLayerPatch] = {} + for layer_key, layer in patch.layers.items(): + if not layer_key.startswith(prefix): + new_layers[layer_key] = layer + continue + + bfl_key = layer_key[prefix_len:] + converted = _convert_bfl_layer_patch_to_diffusers(bfl_key, layer) + for diff_key, diff_layer in converted: + new_layers[f"{prefix}{diff_key}"] = diff_layer + + return ModelPatchRaw(layers=new_layers) + + +def _convert_bfl_layer_patch_to_diffusers(bfl_key: str, layer: BaseLayerPatch) -> list[tuple[str, BaseLayerPatch]]: + """Convert a single BFL-named LoRA layer patch to one or more diffusers-named patches. + + For simple renames, the layer object is reused. For QKV splits, new LoRALayer objects + are created with split up-weights and cloned down-weights. + """ + # Double blocks + m = _DOUBLE_BLOCK_RE.match(bfl_key) + if m: + idx, rest = m.group(1), m.group(2) + diff_prefix = f"transformer_blocks.{idx}" + + # Fused QKV → split into separate Q, K, V + if rest == "img_attn.qkv" and isinstance(layer, LoRALayer): + return _split_qkv_lora_layer( + layer, + q_key=f"{diff_prefix}.attn.to_q", + k_key=f"{diff_prefix}.attn.to_k", + v_key=f"{diff_prefix}.attn.to_v", + ) + if rest == "txt_attn.qkv" and isinstance(layer, LoRALayer): + return _split_qkv_lora_layer( + layer, + q_key=f"{diff_prefix}.attn.add_q_proj", + k_key=f"{diff_prefix}.attn.add_k_proj", + v_key=f"{diff_prefix}.attn.add_v_proj", + ) + # Simple renames + if rest in _DOUBLE_BLOCK_RENAMES: + return [(f"{diff_prefix}.{_DOUBLE_BLOCK_RENAMES[rest]}", layer)] + return [(f"{diff_prefix}.{rest}", layer)] + + # Single blocks + m = _SINGLE_BLOCK_RE.match(bfl_key) + if m: + idx, rest = m.group(1), m.group(2) + diff_prefix = f"single_transformer_blocks.{idx}" + + if rest in _SINGLE_BLOCK_RENAMES: + return [(f"{diff_prefix}.{_SINGLE_BLOCK_RENAMES[rest]}", layer)] + return [(f"{diff_prefix}.{rest}", layer)] + + # Non-block keys - pass through + return [(bfl_key, layer)] + + +def _split_qkv_lora_layer( + layer: LoRALayer, + q_key: str, + k_key: str, + v_key: str, +) -> list[tuple[str, LoRALayer]]: + """Split a fused QKV LoRALayer into separate Q, K, V LoRALayers. + + The up weight [3*hidden, rank] is split into 3 parts. + The down weight [rank, hidden] is cloned for each. + """ + up_q, up_k, up_v = layer.up.chunk(3, dim=0) + + result = [] + for key, up_part in [(q_key, up_q), (k_key, up_k), (v_key, up_v)]: + split_layer = LoRALayer( + up=up_part, + mid=None, + down=layer.down.clone(), + alpha=layer._alpha, + bias=None, + ) + result.append((key, split_layer)) + + return result diff --git a/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py b/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py index f5b4bc66847..e691071a397 100644 --- a/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py +++ b/invokeai/backend/patches/lora_conversions/flux_diffusers_lora_conversion_utils.py @@ -12,33 +12,71 @@ def is_state_dict_likely_in_flux_diffusers_format(state_dict: dict[str | int, torch.Tensor]) -> bool: """Checks if the provided state dict is likely in the Diffusers FLUX LoRA format. + This detects both Flux.1 diffusers format (separate to_q/to_k/to_v, ff.net.0.proj) and + Flux2 Klein diffusers format (fused to_qkv_mlp_proj, ff.linear_in). + This is intended to be a reasonably high-precision detector, but it is not guaranteed to have perfect precision. (A perfect-precision detector would require checking all keys against a whitelist and verifying tensor shapes.) """ - # First, check that all keys end in "lora_A.weight" or "lora_B.weight" (i.e. are in PEFT format). - all_keys_in_peft_format = all( - k.endswith(("lora_A.weight", "lora_B.weight")) for k in state_dict.keys() if isinstance(k, str) - ) - + # Check that all keys are LoRA weight keys (either PEFT or standard format). + # Some LoRAs use a mix of formats (PEFT for some layers, standard for others). + _LORA_SUFFIXES = ("lora_A.weight", "lora_B.weight", "lora.down.weight", "lora.up.weight") + all_keys_are_lora = all(k.endswith(_LORA_SUFFIXES) for k in state_dict.keys() if isinstance(k, str)) + if not all_keys_are_lora: + return False + + # --- Flux.1 diffusers key patterns (separate Q/K/V, ff.net.0.proj) --- # Check if keys use transformer prefix - transformer_prefix_keys = [ + flux1_transformer_keys = [ "transformer.single_transformer_blocks.0.attn.to_q.lora_A.weight", "transformer.single_transformer_blocks.0.attn.to_q.lora_B.weight", "transformer.transformer_blocks.0.attn.add_q_proj.lora_A.weight", "transformer.transformer_blocks.0.attn.add_q_proj.lora_B.weight", ] - transformer_keys_present = all(k in state_dict for k in transformer_prefix_keys) + flux1_transformer_present = all(k in state_dict for k in flux1_transformer_keys) # Check if keys use base_model.model prefix - base_model_prefix_keys = [ + flux1_base_model_keys = [ "base_model.model.single_transformer_blocks.0.attn.to_q.lora_A.weight", "base_model.model.single_transformer_blocks.0.attn.to_q.lora_B.weight", "base_model.model.transformer_blocks.0.attn.add_q_proj.lora_A.weight", "base_model.model.transformer_blocks.0.attn.add_q_proj.lora_B.weight", ] - base_model_keys_present = all(k in state_dict for k in base_model_prefix_keys) + flux1_base_model_present = all(k in state_dict for k in flux1_base_model_keys) + + if flux1_transformer_present or flux1_base_model_present: + return True - return all_keys_in_peft_format and (transformer_keys_present or base_model_keys_present) + # --- Flux2 Klein diffusers key patterns (fused QKV+MLP, ff.linear_in) --- + # These use Flux2Transformer2DModel naming which differs from Flux.1. + for prefix in ["transformer.", "base_model.model."]: + has_single = any( + k.startswith(f"{prefix}single_transformer_blocks.") and "to_qkv_mlp_proj" in k for k in state_dict + ) + has_double = any(k.startswith(f"{prefix}transformer_blocks.") for k in state_dict if isinstance(k, str)) + if has_single or has_double: + # Verify it's actually Flux2 naming by checking for a Flux2-specific key pattern. + # Flux2 uses ff.linear_in (not ff.net.0.proj) and attn.to_add_out (not attn.to_add_out in Flux.1 too, + # but fused to_qkv_mlp_proj is unique to Flux2). + has_flux2_keys = any( + ("to_qkv_mlp_proj" in k or "ff.linear_in" in k or "ff_context.linear_in" in k) + for k in state_dict + if isinstance(k, str) + ) + if has_flux2_keys: + return True + + return False + + +def is_state_dict_flux2_diffusers_format(state_dict: dict[str | int, torch.Tensor]) -> bool: + """Checks if the state dict uses Flux2 Klein native diffusers naming (not Flux.1 diffusers naming). + + Returns True only for Flux2 Klein diffusers format (to_qkv_mlp_proj, ff.linear_in, etc.), + NOT for Flux.1 diffusers format (to_q/to_k/to_v, ff.net.0.proj). + """ + str_keys = [k for k in state_dict.keys() if isinstance(k, str)] + return any("to_qkv_mlp_proj" in k or "ff.linear_in" in k or "ff_context.linear_in" in k for k in str_keys) def lora_model_from_flux_diffusers_state_dict( @@ -251,6 +289,84 @@ def add_qkv_lora_layer_if_present( return layers_with_prefix +def lora_model_from_flux2_diffusers_state_dict( + state_dict: Dict[str, torch.Tensor], alpha: float | None +) -> ModelPatchRaw: + """Convert a Flux2 Klein native diffusers format LoRA state dict to a ModelPatchRaw. + + Flux2 Klein diffusers LoRAs use key names that match Flux2Transformer2DModel directly + (e.g. transformer_blocks.0.attn.to_add_out, single_transformer_blocks.0.attn.to_qkv_mlp_proj). + The conversion strips the model prefix (transformer. or base_model.model.) and adds + the InvokeAI prefix. + + Some LoRAs use a mix of PEFT format (lora_A.weight/lora_B.weight) and standard format + (lora.down.weight/lora.up.weight) for different layers. Both are handled here. + """ + grouped_state_dict = _group_by_layer_mixed_format(state_dict) + + # Determine and strip prefix + has_base_model_prefix = any(k.startswith("base_model.model.") for k in grouped_state_dict.keys()) + if has_base_model_prefix: + grouped_state_dict = {k.replace("base_model.model.", "", 1): v for k, v in grouped_state_dict.items()} + else: + grouped_state_dict = {k.replace("transformer.", "", 1): v for k, v in grouped_state_dict.items()} + + layers: dict[str, BaseLayerPatch] = {} + for layer_key, src_layer_dict in grouped_state_dict.items(): + # Normalize to InvokeAI naming (lora_down.weight / lora_up.weight) + values: dict[str, torch.Tensor] = {} + if "lora_A.weight" in src_layer_dict: + values["lora_down.weight"] = src_layer_dict["lora_A.weight"] + values["lora_up.weight"] = src_layer_dict["lora_B.weight"] + elif "lora.down.weight" in src_layer_dict: + values["lora_down.weight"] = src_layer_dict["lora.down.weight"] + values["lora_up.weight"] = src_layer_dict["lora.up.weight"] + else: + values = src_layer_dict + + if alpha is not None and "alpha" not in values: + values["alpha"] = torch.tensor(alpha) + + layers[f"{FLUX_LORA_TRANSFORMER_PREFIX}{layer_key}"] = any_lora_layer_from_state_dict(values) + + return ModelPatchRaw(layers=layers) + + +def _group_by_layer_mixed_format(state_dict: Dict[str, torch.Tensor]) -> dict[str, dict[str, torch.Tensor]]: + """Groups keys by layer, handling both PEFT and standard LoRA suffixes. + + PEFT format: layer_name.lora_A.weight → layer=layer_name, suffix=lora_A.weight + Standard format: layer_name.lora.down.weight → layer=layer_name, suffix=lora.down.weight + """ + layer_dict: dict[str, dict[str, torch.Tensor]] = {} + for key in state_dict: + if not isinstance(key, str): + continue + + # Determine suffix length based on the key ending + if key.endswith((".lora_A.weight", ".lora_B.weight")): + # PEFT format: split off 2 parts (lora_A + weight) + parts = key.rsplit(".", maxsplit=2) + layer_name = parts[0] + suffix = ".".join(parts[1:]) + elif key.endswith((".lora.down.weight", ".lora.up.weight")): + # Standard format: split off 3 parts (lora + down/up + weight) + parts = key.rsplit(".", maxsplit=3) + layer_name = parts[0] + suffix = ".".join(parts[1:]) + else: + # Unknown format, use 2-part split as fallback + parts = key.rsplit(".", maxsplit=2) + layer_name = parts[0] + suffix = ".".join(parts[1:]) + + if layer_name not in layer_dict: + layer_dict[layer_name] = {} + layer_dict[layer_name][suffix] = state_dict[key] + + return layer_dict + + def _group_by_layer(state_dict: Dict[str, torch.Tensor]) -> dict[str, dict[str, torch.Tensor]]: """Groups the keys in the state dict by layer.""" layer_dict: dict[str, dict[str, torch.Tensor]] = {} diff --git a/invokeai/backend/patches/lora_conversions/formats.py b/invokeai/backend/patches/lora_conversions/formats.py index ae2e1b14596..0b316602fcd 100644 --- a/invokeai/backend/patches/lora_conversions/formats.py +++ b/invokeai/backend/patches/lora_conversions/formats.py @@ -4,6 +4,9 @@ from invokeai.backend.patches.lora_conversions.flux_aitoolkit_lora_conversion_utils import ( is_state_dict_likely_in_flux_aitoolkit_format, ) +from invokeai.backend.patches.lora_conversions.flux_bfl_peft_lora_conversion_utils import ( + is_state_dict_likely_in_flux_bfl_peft_format, +) from invokeai.backend.patches.lora_conversions.flux_control_lora_utils import is_state_dict_likely_flux_control from invokeai.backend.patches.lora_conversions.flux_diffusers_lora_conversion_utils import ( is_state_dict_likely_in_flux_diffusers_format, @@ -35,5 +38,7 @@ def flux_format_from_state_dict( return FluxLoRAFormat.AIToolkit elif is_state_dict_likely_in_flux_xlabs_format(state_dict): return FluxLoRAFormat.XLabs + elif is_state_dict_likely_in_flux_bfl_peft_format(state_dict): + return FluxLoRAFormat.BflPeft else: return None diff --git a/invokeai/frontend/web/openapi.json b/invokeai/frontend/web/openapi.json index 2878975eda4..f9adaa28ef8 100644 --- a/invokeai/frontend/web/openapi.json +++ b/invokeai/frontend/web/openapi.json @@ -153,6 +153,26 @@ } } }, + "/api/v2/models/missing": { + "get": { + "tags": ["model_manager"], + "summary": "List Missing Models", + "description": "Get models whose files are missing from disk.\n\nThese are models that have database entries but their corresponding\nweight files have been deleted externally (not via Model Manager).", + "operationId": "list_missing_models", + "responses": { + "200": { + "description": "List of models with missing files", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelsList" + } + } + } + } + } + } + }, "/api/v2/models/get_by_attrs": { "get": { "tags": ["model_manager"], @@ -1799,7 +1819,7 @@ "get": { "tags": ["model_manager"], "summary": "List Model Installs", - "description": "Return the list of model install jobs.\n\nInstall jobs have a numeric `id`, a `status`, and other fields that provide information on\nthe nature of the job and its progress. The `status` is one of:\n\n* \"waiting\" -- Job is waiting in the queue to run\n* \"downloading\" -- Model file(s) are downloading\n* \"running\" -- Model has downloaded and the model probing and registration process is running\n* \"completed\" -- Installation completed successfully\n* \"error\" -- An error occurred. Details will be in the \"error_type\" and \"error\" fields.\n* \"cancelled\" -- Job was cancelled before completion.\n\nOnce completed, information about the model such as its size, base\nmodel and type can be retrieved from the `config_out` field. For multi-file models such as diffusers,\ninformation on individual files can be retrieved from `download_parts`.\n\nSee the example and schema below for more information.", + "description": "Return the list of model install jobs.\n\nInstall jobs have a numeric `id`, a `status`, and other fields that provide information on\nthe nature of the job and its progress. The `status` is one of:\n\n* \"waiting\" -- Job is waiting in the queue to run\n* \"downloading\" -- Model file(s) are downloading\n* \"running\" -- Model has downloaded and the model probing and registration process is running\n* \"paused\" -- Job is paused and can be resumed\n* \"completed\" -- Installation completed successfully\n* \"error\" -- An error occurred. Details will be in the \"error_type\" and \"error\" fields.\n* \"cancelled\" -- Job was cancelled before completion.\n\nOnce completed, information about the model such as its size, base\nmodel and type can be retrieved from the `config_out` field. For multi-file models such as diffusers,\ninformation on individual files can be retrieved from `download_parts`.\n\nSee the example and schema below for more information.", "operationId": "list_model_installs", "responses": { "200": { @@ -1978,6 +1998,204 @@ } } }, + "/api/v2/models/install/{id}/pause": { + "post": { + "tags": ["model_manager"], + "summary": "Pause Model Install Job", + "description": "Pause the model install job corresponding to the given job ID.", + "operationId": "pause_model_install_job", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "description": "Model install job ID", + "title": "Id" + }, + "description": "Model install job ID" + } + ], + "responses": { + "201": { + "description": "The job was paused successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelInstallJob" + } + } + } + }, + "415": { + "description": "No such job" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v2/models/install/{id}/resume": { + "post": { + "tags": ["model_manager"], + "summary": "Resume Model Install Job", + "description": "Resume a paused model install job corresponding to the given job ID.", + "operationId": "resume_model_install_job", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "description": "Model install job ID", + "title": "Id" + }, + "description": "Model install job ID" + } + ], + "responses": { + "201": { + "description": "The job was resumed successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelInstallJob" + } + } + } + }, + "415": { + "description": "No such job" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v2/models/install/{id}/restart_failed": { + "post": { + "tags": ["model_manager"], + "summary": "Restart Failed Model Install Job", + "description": "Restart failed or non-resumable file downloads for the given job.", + "operationId": "restart_failed_model_install_job", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "description": "Model install job ID", + "title": "Id" + }, + "description": "Model install job ID" + } + ], + "responses": { + "201": { + "description": "Failed files restarted successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelInstallJob" + } + } + } + }, + "415": { + "description": "No such job" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v2/models/install/{id}/restart_file": { + "post": { + "tags": ["model_manager"], + "summary": "Restart Model Install File", + "description": "Restart a specific file download for the given job.", + "operationId": "restart_model_install_file", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "description": "Model install job ID", + "title": "Id" + }, + "description": "Model install job ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "string", + "format": "uri", + "minLength": 1, + "description": "File download URL to restart", + "title": "File Source" + } + } + } + }, + "responses": { + "201": { + "description": "File restarted successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelInstallJob" + } + } + } + }, + "415": { + "description": "No such job" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, "/api/v2/models/convert/{key}": { "put": { "tags": ["model_manager"], @@ -2422,6 +2640,68 @@ } } }, + "/api/v2/models/sync/orphaned": { + "get": { + "tags": ["model_manager"], + "summary": "Get Orphaned Models", + "description": "Find orphaned model directories.\n\nOrphaned models are directories in the models folder that contain model files\nbut are not referenced in the database. This can happen when models are deleted\nfrom the database but the files remain on disk.\n\nReturns:\n List of orphaned model directory information", + "operationId": "get_orphaned_models", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/OrphanedModelInfo" + }, + "type": "array", + "title": "Response Get Orphaned Models" + } + } + } + } + } + }, + "delete": { + "tags": ["model_manager"], + "summary": "Delete Orphaned Models", + "description": "Delete specified orphaned model directories.\n\nArgs:\n request: Request containing list of relative paths to delete\n\nReturns:\n Response indicating which paths were deleted and which had errors", + "operationId": "delete_orphaned_models", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteOrphanedModelsRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteOrphanedModelsResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, "/api/v1/download_queue/": { "get": { "tags": ["download_queue"], @@ -15677,6 +15957,46 @@ "required": ["affected_boards", "deleted_images"], "title": "DeleteImagesResult" }, + "DeleteOrphanedModelsRequest": { + "properties": { + "paths": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Paths", + "description": "List of relative paths to delete" + } + }, + "type": "object", + "required": ["paths"], + "title": "DeleteOrphanedModelsRequest", + "description": "Request to delete specific orphaned model directories." + }, + "DeleteOrphanedModelsResponse": { + "properties": { + "deleted": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Deleted", + "description": "Paths that were successfully deleted" + }, + "errors": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "title": "Errors", + "description": "Paths that had errors, with error messages" + } + }, + "type": "object", + "required": ["deleted", "errors"], + "title": "DeleteOrphanedModelsResponse", + "description": "Response from deleting orphaned models." + }, "DenoiseLatentsInvocation": { "category": "latents", "class": "invocation", @@ -16808,6 +17128,90 @@ ], "title": "Content Type", "description": "Content type of downloaded file" + }, + "canonical_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Canonical Url", + "description": "Canonical URL to request on resume" + }, + "etag": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Etag", + "description": "ETag from the remote server, if available" + }, + "last_modified": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Modified", + "description": "Last-Modified from the remote server, if available" + }, + "final_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Final Url", + "description": "Final resolved URL after redirects, if available" + }, + "expected_total_bytes": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Expected Total Bytes", + "description": "Expected total size of the download" + }, + "resume_required": { + "type": "boolean", + "title": "Resume Required", + "description": "True if server refused resume; restart required", + "default": false + }, + "resume_message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Resume Message", + "description": "Message explaining why resume is required" + }, + "resume_from_scratch": { + "type": "boolean", + "title": "Resume From Scratch", + "description": "True if resume metadata existed but the partial file was missing and the download restarted from the beginning", + "default": false } }, "type": "object", @@ -16817,10 +17221,28 @@ }, "DownloadJobStatus": { "type": "string", - "enum": ["waiting", "running", "completed", "cancelled", "error"], + "enum": ["waiting", "running", "paused", "completed", "cancelled", "error"], "title": "DownloadJobStatus", "description": "State of a download job." }, + "DownloadPausedEvent": { + "description": "Event model for download_paused", + "properties": { + "timestamp": { + "description": "The timestamp of the event", + "title": "Timestamp", + "type": "integer" + }, + "source": { + "description": "The source of the download", + "title": "Source", + "type": "string" + } + }, + "required": ["timestamp", "source"], + "title": "DownloadPausedEvent", + "type": "object" + }, "DownloadProgressEvent": { "description": "Event model for download_progress", "properties": { @@ -16877,12 +17299,6 @@ "title": "DownloadStartedEvent", "type": "object" }, - "DyPEPreset": { - "description": "Predefined DyPE configurations.", - "enum": ["off", "auto", "area", "4k"], - "title": "DyPEPreset", - "type": "string" - }, "DynamicPromptInvocation": { "category": "prompt", "class": "invocation", @@ -20222,13 +20638,23 @@ "title": "Kontext Conditioning" }, "dype_preset": { - "$ref": "#/components/schemas/DyPEPreset", "default": "off", - "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. '4k' uses optimized settings for 4K output.", + "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. 'area' enables automatically based on image area. '4k' uses optimized settings for 4K output.", + "enum": ["off", "manual", "auto", "area", "4k"], "field_kind": "input", "input": "any", "orig_default": "off", - "orig_required": false + "orig_required": false, + "title": "Dype Preset", + "type": "string", + "ui_choice_labels": { + "4k": "4K Optimized", + "area": "Area (auto)", + "auto": "Auto (>1536px)", + "manual": "Manual", + "off": "Off" + }, + "ui_order": 100 }, "dype_scale": { "anyOf": [ @@ -20247,7 +20673,8 @@ "input": "any", "orig_default": null, "orig_required": false, - "title": "Dype Scale" + "title": "Dype Scale", + "ui_order": 101 }, "dype_exponent": { "anyOf": [ @@ -20266,7 +20693,8 @@ "input": "any", "orig_default": null, "orig_required": false, - "title": "Dype Exponent" + "title": "Dype Exponent", + "ui_order": 102 }, "type": { "const": "flux_denoise", @@ -20280,7 +20708,7 @@ "tags": ["image", "flux"], "title": "FLUX Denoise", "type": "object", - "version": "4.3.0", + "version": "4.5.1", "output": { "$ref": "#/components/schemas/LatentsOutput" } @@ -20709,13 +21137,23 @@ "title": "Kontext Conditioning" }, "dype_preset": { - "$ref": "#/components/schemas/DyPEPreset", "default": "off", - "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. '4k' uses optimized settings for 4K output.", + "description": "DyPE preset for high-resolution generation. 'auto' enables automatically for resolutions > 1536px. 'area' enables automatically based on image area. '4k' uses optimized settings for 4K output.", + "enum": ["off", "manual", "auto", "area", "4k"], "field_kind": "input", "input": "any", "orig_default": "off", - "orig_required": false + "orig_required": false, + "title": "Dype Preset", + "type": "string", + "ui_choice_labels": { + "4k": "4K Optimized", + "area": "Area (auto)", + "auto": "Auto (>1536px)", + "manual": "Manual", + "off": "Off" + }, + "ui_order": 100 }, "dype_scale": { "anyOf": [ @@ -20734,7 +21172,8 @@ "input": "any", "orig_default": null, "orig_required": false, - "title": "Dype Scale" + "title": "Dype Scale", + "ui_order": 101 }, "dype_exponent": { "anyOf": [ @@ -20753,7 +21192,8 @@ "input": "any", "orig_default": null, "orig_required": false, - "title": "Dype Exponent" + "title": "Dype Exponent", + "ui_order": 102 }, "type": { "const": "flux_denoise_meta", @@ -21454,28 +21894,52 @@ "type": "boolean" }, "model": { - "$ref": "#/components/schemas/ModelIdentifierField", + "anyOf": [ + { + "$ref": "#/components/schemas/ModelIdentifierField" + }, + { + "type": "null" + } + ], + "default": null, "description": "Flux model (Transformer) to load", "field_kind": "input", - "input": "direct", + "input": "any", "orig_required": true, "ui_model_base": ["flux"], "ui_model_type": ["main"] }, "t5_encoder_model": { - "$ref": "#/components/schemas/ModelIdentifierField", + "anyOf": [ + { + "$ref": "#/components/schemas/ModelIdentifierField" + }, + { + "type": "null" + } + ], + "default": null, "description": "T5 tokenizer and text encoder", "field_kind": "input", - "input": "direct", + "input": "any", "orig_required": true, "title": "T5 Encoder", "ui_model_type": ["t5_encoder"] }, "clip_embed_model": { - "$ref": "#/components/schemas/ModelIdentifierField", + "anyOf": [ + { + "$ref": "#/components/schemas/ModelIdentifierField" + }, + { + "type": "null" + } + ], + "default": null, "description": "CLIP Embed loader", "field_kind": "input", - "input": "direct", + "input": "any", "orig_required": true, "title": "CLIP Embed", "ui_model_type": ["clip_embed"] @@ -21506,11 +21970,11 @@ "type": "string" } }, - "required": ["model", "t5_encoder_model", "clip_embed_model", "type", "id"], + "required": ["type", "id"], "tags": ["model", "flux"], "title": "Main Model - FLUX", "type": "object", - "version": "1.0.6", + "version": "1.0.7", "output": { "$ref": "#/components/schemas/FluxModelLoaderOutput" } @@ -28743,7 +29207,7 @@ }, "InstallStatus": { "type": "string", - "enum": ["waiting", "downloading", "downloads_done", "running", "completed", "error", "cancelled"], + "enum": ["waiting", "downloading", "downloads_done", "running", "paused", "completed", "error", "cancelled"], "title": "InstallStatus", "description": "State of an install job running in the background." }, @@ -31765,234 +32229,234 @@ } }, "required": [ + "integer_batch", + "img_ilerp", + "image_panel_layout", + "t2i_adapter", + "metadata_to_model", + "flux2_klein_model_loader", + "bounding_box", "integer_generator", - "llava_onevision_vllm", - "depth_anything_depth_estimation", + "flux_text_encoder", + "img_resize", + "z_image_seed_variance_enhancer", + "metadata_to_integer", + "apply_tensor_mask_to_image", + "metadata_to_bool_collection", + "vae_loader", + "float_batch", + "paste_image_into_bounding_box", + "unsharp_mask", + "invokeai_ealightness", + "flux_denoise", + "color_map", + "face_identifier", + "float_math", + "round_float", + "z_image_denoise_meta", + "flux_controlnet", + "metadata_to_float", + "cogview4_text_encoder", + "calculate_image_tiles_even_split", + "lineart_edge_detection", + "img_noise", + "color", + "i2l", + "metadata_item", + "metadata_to_string_collection", + "integer", + "clip_skip", + "cogview4_i2l", + "image_mask_to_tensor", + "dw_openpose_detection", + "crop_latents", + "img_blur", "invokeai_img_blend", - "metadata_field_extractor", - "collect", - "integer_collection", - "noise", - "main_model_loader", - "float", + "grounding_dino", + "core_metadata", + "metadata_to_t2i_adapters", + "z_image_lora_loader", "sd3_denoise", - "tensor_mask_to_image", - "cogview4_text_encoder", - "mask_combine", - "image", - "denoise_latents", - "flux2_vae_encode", - "img_channel_multiply", - "invokeai_img_val_thresholds", - "alpha_mask_to_tensor", - "string", - "metadata_to_controlnets", + "lineart_anime_edge_detection", "lblend", - "prompt_template", - "core_metadata", - "lineart_edge_detection", - "pair_tile_image", - "compel", - "z_image_i2l", - "flux_kontext_image_prep", + "image_collection", + "metadata_to_loras", + "flux_fill", + "cogview4_l2i", + "sdxl_refiner_model_loader", + "mediapipe_face_detection", + "infill_tile", + "alpha_mask_to_tensor", + "create_denoise_mask", + "noise", + "collect", + "calculate_image_tiles_min_overlap", + "string_join", "div", - "metadata_to_float", - "dw_openpose_detection", + "invert_tensor_mask", + "crop_image_to_bounding_box", + "latents", "z_image_text_encoder", - "mask_edge", - "spandrel_image_to_image", - "image_batch", - "img_hue_adjust", - "metadata_to_integer", - "save_image", - "cogview4_l2i", - "canvas_v2_mask_and_crop", - "spandrel_image_to_image_autoscale", - "flux_lora_loader", - "flux_vae_decode", - "metadata_to_bool_collection", - "img_conv", - "hed_edge_detection", - "flux_kontext", - "tile_to_properties", - "expand_mask_with_fade", - "flux_controlnet", - "esrgan", - "sdxl_lora_loader", - "heuristic_resize", - "range_of_size", - "controlnet", - "apply_mask_to_image", - "img_watermark", + "metadata", + "flux2_klein_text_encoder", "img_channel_offset", - "infill_rgba", "metadata_to_scheduler", - "pidi_edge_detection", - "color_correct", - "conditioning", - "integer_math", - "invokeai_img_composite", - "paste_image_into_bounding_box", - "metadata", - "img_noise", - "t2i_adapter", - "sdxl_lora_collection_loader", + "dynamic_prompt", + "seamless", + "spandrel_image_to_image", + "string_join_three", + "depth_anything_depth_estimation", + "cv_inpaint", + "float_collection", "invokeai_img_dilate_erode", - "img_pad_crop", - "flux_denoise", - "invokeai_img_hue_adjust_plus", - "infill_tile", - "latents", - "float_to_int", - "sd3_text_encoder", - "flux2_klein_model_loader", - "tomask", - "ip_adapter", - "img_ilerp", - "face_mask_detection", + "lresize", + "iterate", "flux2_vae_decode", - "get_image_mask_bounding_box", - "denoise_latents_meta", - "lscale", - "img_crop", - "rand_int", - "metadata_to_bool", - "invokeai_img_enhance", + "metadata_to_sdxl_model", + "apply_mask_to_image", + "img_chan", + "esrgan", "string_split", - "lresize", - "apply_tensor_mask_to_image", - "sdxl_refiner_model_loader", + "lscale", + "image_generator", + "img_channel_multiply", + "img_pad_crop", + "show_image", "range", - "z_image_lora_loader", - "string_batch", - "clip_skip", - "string_collection", - "lora_selector", - "metadata_to_model", - "model_identifier", - "ideal_size", - "bounding_box", - "rectangle_mask", - "img_chan", - "flux_fill", - "z_image_denoise_meta", - "seamless", - "image_panel_layout", - "pbr_maps", - "float_generator", - "metadata_from_image", - "flux_ip_adapter", - "boolean", + "calculate_image_tiles", + "get_image_mask_bounding_box", + "sdxl_model_loader", + "metadata_to_integer_collection", "prompt_from_file", - "float_range", - "metadata_to_t2i_adapters", - "add", - "invokeai_ealightness", + "rectangle_mask", + "canny_edge_detection", + "heuristic_resize", + "blank_image", + "latents_collection", + "flux_model_loader", + "denoise_latents_meta", + "z_image_model_loader", + "rand_float", + "l2i", + "img_conv", + "controlnet", + "flux_redux", + "boolean_collection", + "flux_kontext", + "metadata_to_ip_adapters", + "metadata_to_string", "cogview4_model_loader", - "calculate_image_tiles_even_split", - "tiled_multi_diffusion_denoise_latents", - "face_off", - "random_range", - "flux_text_encoder", - "cv_inpaint", - "color", - "metadata_to_lora_collection", - "round_float", - "merge_metadata", - "img_blur", - "lora_loader", - "calculate_image_tiles", - "image_generator", - "integer_batch", - "freeu", - "metadata_to_float_collection", - "sd3_l2i", - "crop_image_to_bounding_box", - "float_collection", - "img_scale", + "z_image_l2i", + "add", + "lora_collection_loader", + "string_batch", "img_nsfw", - "boolean_collection", - "flux_denoise_meta", - "sd3_i2l", - "vae_loader", - "metadata_to_loras", - "create_gradient_mask", - "scheduler", - "metadata_to_integer_collection", - "metadata_item", - "float_math", - "z_image_seed_variance_enhancer", - "image_collection", - "sd3_model_loader", - "unsharp_mask", - "metadata_item_linked", - "mediapipe_face_detection", - "metadata_to_sdxl_model", - "flux2_klein_text_encoder", - "sdxl_compel_prompt", - "z_image_denoise", - "metadata_to_vae", - "cogview4_denoise", - "image_mask_to_tensor", - "sub", - "segment_anything", - "canvas_paste_back", "content_shuffle", - "merge_tiles_to_image", - "integer", - "normal_map", - "z_image_control", - "blank_image", - "iterate", + "conditioning_collection", + "string", + "metadata_to_float_collection", + "random_range", + "flux2_denoise", + "tile_to_properties", + "mask_edge", + "ideal_size", "sdxl_refiner_compel_prompt", "string_split_neg", - "infill_cv2", - "sdxl_model_loader", - "img_lerp", + "sub", + "sd3_i2l", "string_generator", - "dynamic_prompt", - "z_image_lora_collection_loader", - "string_join", - "lora_collection_loader", - "flux_lora_collection_loader", - "flux_vae_encode", - "flux2_denoise", - "flux_control_lora_loader", - "z_image_model_loader", - "l2i", - "color_map", - "lineart_anime_edge_detection", - "flux_model_loader", - "invert_tensor_mask", - "face_identifier", - "img_paste", - "img_resize", - "infill_lama", - "canny_edge_detection", - "infill_patchmatch", - "string_join_three", - "float_batch", - "show_image", - "z_image_l2i", "img_mul", - "mul", - "conditioning_collection", - "calculate_image_tiles_min_overlap", + "invokeai_img_val_thresholds", + "pidi_edge_detection", + "flux_lora_loader", + "metadata_to_lora_collection", + "canvas_paste_back", + "tomask", + "string_collection", + "metadata_to_sdlx_loras", + "compel", + "sd3_model_loader", + "denoise_latents", + "flux_kontext_image_prep", "mlsd_detection", - "rand_float", + "pair_tile_image", + "flux2_vae_encode", + "flux_denoise_meta", + "float_to_int", + "img_lerp", + "flux_vae_encode", + "mul", + "z_image_denoise", + "img_crop", + "invokeai_img_composite", + "prompt_template", + "sdxl_compel_prompt", + "metadata_to_bool", + "sdxl_lora_loader", + "lora_loader", + "metadata_from_image", "string_replace", - "grounding_dino", - "metadata_to_sdlx_loras", - "crop_latents", - "metadata_to_ip_adapters", - "latents_collection", - "i2l", - "metadata_to_string_collection", - "create_denoise_mask", - "cogview4_i2l", - "metadata_to_string", + "infill_lama", + "img_paste", + "invokeai_img_enhance", + "hed_edge_detection", + "merge_metadata", + "float_range", + "range_of_size", + "integer_math", + "sd3_text_encoder", + "model_identifier", + "infill_patchmatch", + "img_watermark", + "scheduler", + "flux_control_lora_loader", + "spandrel_image_to_image_autoscale", + "image_batch", + "image", + "flux_vae_decode", + "float", + "cogview4_denoise", + "segment_anything", "mask_from_id", - "flux_redux" + "img_hue_adjust", + "normal_map", + "flux_lora_collection_loader", + "z_image_lora_collection_loader", + "metadata_item_linked", + "freeu", + "face_off", + "z_image_control", + "lora_selector", + "llava_onevision_vllm", + "metadata_field_extractor", + "metadata_to_vae", + "mask_combine", + "infill_rgba", + "img_scale", + "conditioning", + "boolean", + "rand_int", + "sd3_l2i", + "expand_mask_with_fade", + "create_gradient_mask", + "color_correct", + "metadata_to_controlnets", + "float_generator", + "save_image", + "invokeai_img_hue_adjust_plus", + "flux_ip_adapter", + "tiled_multi_diffusion_denoise_latents", + "sdxl_lora_collection_loader", + "z_image_i2l", + "infill_cv2", + "ip_adapter", + "canvas_v2_mask_and_crop", + "pbr_maps", + "integer_collection", + "merge_tiles_to_image", + "face_mask_detection", + "tensor_mask_to_image", + "main_model_loader" ] }, "InvocationProgressEvent": { @@ -48054,6 +48518,37 @@ "required": ["limit", "offset", "total", "items"], "title": "OffsetPaginatedResults[ImageDTO]" }, + "OrphanedModelInfo": { + "properties": { + "path": { + "type": "string", + "title": "Path", + "description": "Relative path to the orphaned directory from models root" + }, + "absolute_path": { + "type": "string", + "title": "Absolute Path", + "description": "Absolute path to the orphaned directory" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Files", + "description": "List of model files in this directory" + }, + "size_bytes": { + "type": "integer", + "title": "Size Bytes", + "description": "Total size of all files in bytes" + } + }, + "type": "object", + "required": ["path", "absolute_path", "files", "size_bytes"], + "title": "OrphanedModelInfo", + "description": "Information about an orphaned model directory." + }, "OutputFieldJSONSchemaExtra": { "description": "Extra attributes to be added to input fields and their OpenAPI schema. Used by the workflow editor\nduring schema parsing and UI rendering.", "properties": { diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index a4027002c89..99685323030 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -26,6 +26,12 @@ "selectedForAutoAdd": "Selected for Auto-Add", "bottomMessage": "Deleting images will reset any features currently using them.", "cancel": "Cancel", + "pause": "Pause", + "resume": "Resume", + "restartFailed": "Restart failed", + "restartFile": "Restart file", + "restartRequired": "Restart required", + "resumeRefused": "Resume refused by server. Restart required.", "changeBoard": "Change Board", "clearSearch": "Clear Search", "deleteBoard": "Delete Board", @@ -292,6 +298,7 @@ "credits": "Credits", "pending": "Pending", "in_progress": "In Progress", + "paused": "Paused", "completed": "Completed", "failed": "Failed", "canceled": "Canceled", @@ -875,6 +882,16 @@ "modelManager": { "active": "active", "actions": "Bulk Actions", + "pause": "Pause", + "pauseAll": "Pause All", + "pauseAllTooltip": "Pause all active downloads", + "resume": "Resume", + "resumeAll": "Resume All", + "resumeAllTooltip": "Resume all paused downloads", + "restartFailed": "Restart failed", + "restartFile": "Restart file", + "restartRequired": "Restart required", + "resumeRefused": "Resume refused by server. Restart required.", "addModel": "Add Model", "addModels": "Add Models", "advanced": "Advanced", @@ -882,7 +899,10 @@ "alpha": "Alpha", "availableModels": "Available Models", "baseModel": "Base Model", + "backendDisconnected": "Backend disconnected", "cancel": "Cancel", + "cancelAll": "Cancel All", + "cancelAllTooltip": "Cancel all active downloads", "clipEmbed": "CLIP Embed", "clipLEmbed": "CLIP-L Embed", "clipGEmbed": "CLIP-G Embed", @@ -1517,6 +1537,11 @@ "addedToBoard": "Added to board {{name}}'s assets", "addedToUncategorized": "Added to board $t(boards.uncategorized)'s assets", "baseModelChanged": "Base Model Changed", + "modelDownloadPaused": "Model download paused", + "modelDownloadResumed": "Resuming download", + "modelDownloadRestartFailed": "Restart failed downloads", + "modelDownloadRestartFile": "Restarting file download", + "modelDownloadRestartedFromScratch": "Partial file missing. Restarted download from the beginning.", "baseModelChangedCleared_one": "Updated, cleared or disabled {{count}} incompatible submodel", "baseModelChangedCleared_other": "Updated, cleared or disabled {{count}} incompatible submodels", "kleinEncoderCleared": "Qwen3 Encoder Cleared", diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts index f8f7d1659a1..d89f89104ab 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts @@ -19,8 +19,10 @@ export const addSocketConnectedEventListener = (startAppListening: AppStartListe /** * The rest of this listener has recovery logic for when the socket disconnects and reconnects. * - * We need to re-fetch if something has changed while we were disconnected. In practice, the only - * thing that could change while disconnected is a queue item finishes processing. + * We need to re-fetch if something has changed while we were disconnected. + * + * Session queue status is one proxy for disconnected changes. Model installs need explicit recovery + * as well because they can transition to paused during backend shutdown while the socket is down. * * The queue status is a proxy for this - if the queue status has changed, we need to re-fetch * the queries that may have changed while we were disconnected. @@ -40,6 +42,14 @@ export const addSocketConnectedEventListener = (startAppListening: AppStartListe dispatch(api.util.resetApiState()); } + // Always re-sync model installs on reconnect. + dispatch( + modelsApi.endpoints.listModelInstalls.initiate(undefined, { + forceRefetch: true, + subscribe: false, + }) + ); + // Else, we need to compare the last-known queue status with the current queue status, re-fetching // everything if it has changed. const prevQueueStatusData = selectQueueStatus(getState()).data; diff --git a/invokeai/frontend/web/src/common/components/IAITooltip.tsx b/invokeai/frontend/web/src/common/components/IAITooltip.tsx new file mode 100644 index 00000000000..578d30deffc --- /dev/null +++ b/invokeai/frontend/web/src/common/components/IAITooltip.tsx @@ -0,0 +1,22 @@ +import type { TooltipProps } from '@invoke-ai/ui-library'; +import { Tooltip } from '@invoke-ai/ui-library'; +import { useAppSelector } from 'app/store/storeHooks'; +import { selectSystemShouldEnableInformationalPopovers } from 'features/system/store/systemSlice'; +import type { PropsWithChildren } from 'react'; +import { memo } from 'react'; + +/** + * A Tooltip component that respects the user's "Enable Informational Popovers" setting. + * When the setting is disabled, this component returns its children without a tooltip wrapper. + */ +export const IAITooltip = memo(({ children, ...rest }: PropsWithChildren) => { + const shouldEnableInformationalPopovers = useAppSelector(selectSystemShouldEnableInformationalPopovers); + + if (!shouldEnableInformationalPopovers) { + return children; + } + + return {children}; +}); + +IAITooltip.displayName = 'IAITooltip'; diff --git a/invokeai/frontend/web/src/common/components/IconMenuItem.tsx b/invokeai/frontend/web/src/common/components/IconMenuItem.tsx index 6b58d5a6112..8b83448b038 100644 --- a/invokeai/frontend/web/src/common/components/IconMenuItem.tsx +++ b/invokeai/frontend/web/src/common/components/IconMenuItem.tsx @@ -1,5 +1,6 @@ import type { MenuItemProps } from '@invoke-ai/ui-library'; -import { Flex, MenuItem, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, MenuItem } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import type { ReactNode } from 'react'; type Props = MenuItemProps & { @@ -9,7 +10,7 @@ type Props = MenuItemProps & { export const IconMenuItem = ({ tooltip, icon, ...props }: Props) => { return ( - + { > {icon} - + ); }; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListGlobalActionBarAddLayerMenu.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListGlobalActionBarAddLayerMenu.tsx index 4a7e6fa3750..07633d6d77f 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListGlobalActionBarAddLayerMenu.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListGlobalActionBarAddLayerMenu.tsx @@ -1,4 +1,5 @@ import { IconButton, Menu, MenuButton, MenuGroup, MenuItem, MenuList } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useAddControlLayer, useAddInpaintMask, @@ -26,17 +27,18 @@ export const EntityListGlobalActionBarAddLayerMenu = memo(() => { return ( - } - data-testid="control-layers-add-layer-menu-button" - isDisabled={isBusy} - /> + + } + data-testid="control-layers-add-layer-menu-button" + isDisabled={isBusy} + /> + } onClick={addInpaintMask} isDisabled={!isInpaintLayerEnabled}> diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarDuplicateButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarDuplicateButton.tsx index 2e2f5fa20a4..607cc16adc1 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarDuplicateButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarDuplicateButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { entityDuplicated } from 'features/controlLayers/store/canvasSlice'; import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors'; @@ -20,16 +21,17 @@ export const EntityListSelectedEntityActionBarDuplicateButton = memo(() => { }, [dispatch, selectedEntityIdentifier]); return ( - } - /> + + } + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFill.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFill.tsx index 62928f2094f..414668ed9b3 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFill.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFill.tsx @@ -1,15 +1,7 @@ -import { - Box, - Flex, - Popover, - PopoverBody, - PopoverContent, - PopoverTrigger, - Portal, - Tooltip, -} from '@invoke-ai/ui-library'; +import { Box, Flex, Popover, PopoverBody, PopoverContent, PopoverTrigger, Portal } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import RgbColorPicker from 'common/components/ColorPicker/RgbColorPicker'; +import { IAITooltip } from 'common/components/IAITooltip'; import { rgbColorToString } from 'common/util/colorCodeTransformers'; import { MaskFillStyle } from 'features/controlLayers/components/common/MaskFillStyle'; import { entityFillColorChanged, entityFillStyleChanged } from 'features/controlLayers/store/canvasSlice'; @@ -56,8 +48,8 @@ export const EntityListSelectedEntityActionBarFill = memo(() => { return ( - - + + { bg={rgbColorToString(fill.color)} /> - - + + diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx index bb4e809b4d1..2009777e2c6 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFilterButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useEntityFilter } from 'features/controlLayers/hooks/useEntityFilter'; import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors'; import { isFilterableEntityIdentifier } from 'features/controlLayers/store/types'; @@ -21,16 +22,17 @@ export const EntityListSelectedEntityActionBarFilterButton = memo(() => { } return ( - } - /> + + } + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarInvertMaskButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarInvertMaskButton.tsx index 361be5e70ef..dd08ce543d5 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarInvertMaskButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarInvertMaskButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { useInvertMask } from 'features/controlLayers/hooks/useInvertMask'; import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors'; @@ -28,16 +29,17 @@ export const EntityListSelectedEntityActionBarInvertMaskButton = memo(() => { : t('controlLayers.invertMask'); return ( - } - /> + + } + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSaveToAssetsButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSaveToAssetsButton.tsx index bf222a5f5d0..7718020506b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSaveToAssetsButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSaveToAssetsButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useEntityAdapterSafe } from 'features/controlLayers/contexts/EntityAdapterContext'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { useSaveLayerToAssets } from 'features/controlLayers/hooks/useSaveLayerToAssets'; @@ -28,16 +29,17 @@ export const EntityListSelectedEntityActionBarSaveToAssetsButton = memo(() => { } return ( - } - /> + + } + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSelectObjectButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSelectObjectButton.tsx index ca053c704d3..e6707c9ff51 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSelectObjectButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarSelectObjectButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useEntitySegmentAnything } from 'features/controlLayers/hooks/useEntitySegmentAnything'; import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors'; import { isSegmentableEntityIdentifier } from 'features/controlLayers/store/types'; @@ -21,16 +22,17 @@ export const EntityListSelectedEntityActionBarSelectObjectButton = memo(() => { } return ( - } - /> + + } + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarTransformButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarTransformButton.tsx index 0b1009ea0e9..e54f23167be 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarTransformButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarTransformButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useEntityTransform } from 'features/controlLayers/hooks/useEntityTransform'; import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors'; import { isTransformableEntityIdentifier } from 'features/controlLayers/store/types'; @@ -21,16 +22,17 @@ export const EntityListSelectedEntityActionBarTransformButton = memo(() => { } return ( - } - /> + + } + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch.tsx index 13a15363486..310007d9767 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasOperationIsolatedLayerPreviewSwitch.tsx @@ -1,5 +1,6 @@ -import { FormControl, FormLabel, Switch, Tooltip } from '@invoke-ai/ui-library'; +import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { selectIsolatedLayerPreview, settingsIsolatedLayerPreviewToggled, @@ -16,12 +17,12 @@ export const CanvasOperationIsolatedLayerPreviewSwitch = memo(() => { }, [dispatch]); return ( - + {t('controlLayers.settings.isolatedPreview')} - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx index 34f1607b954..05527face2a 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayer/ControlLayerControlAdapterModel.tsx @@ -1,5 +1,6 @@ -import { Combobox, FormControl, Tooltip } from '@invoke-ai/ui-library'; +import { Combobox, FormControl } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; import { selectBase } from 'features/controlLayers/store/paramsSlice'; import { memo, useCallback, useMemo } from 'react'; @@ -52,7 +53,7 @@ export const ControlLayerControlAdapterModel = memo(({ modelKey, onChange: onCha }); return ( - + - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx index ffb4e813a98..04cb201ce57 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RasterLayer/RasterLayerExportPSDButton.tsx @@ -1,4 +1,5 @@ import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { useExportCanvasToPSD } from 'features/controlLayers/hooks/useExportCanvasToPSD'; import { memo, useCallback } from 'react'; @@ -15,16 +16,17 @@ export const RasterLayerExportPSDButton = memo(() => { }, [exportCanvasToPSD]); return ( - } - /> + + } + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx index 501ebe76fdb..c350cb7e9ee 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageHeader.tsx @@ -2,6 +2,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; import { Flex, IconButton, Text } from '@invoke-ai/ui-library'; import { createSelector } from '@reduxjs/toolkit'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useRefImageEntity } from 'features/controlLayers/components/RefImage/useRefImageEntity'; import { useRefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; @@ -53,41 +54,44 @@ export const RefImageHeader = memo(() => { {warnings.length > 0 && ( - } - icon={} - colorScheme="warning" - /> + }> + } + colorScheme="warning" + /> + )} {!entity.isEnabled && ( Disabled )} - : } - /> - } - colorScheme="error" - /> + + : } + /> + + + } + colorScheme="error" + /> + ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageModel.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageModel.tsx index 70f6e75b6d8..384b03e6a7d 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageModel.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImageModel.tsx @@ -1,5 +1,6 @@ -import { Combobox, FormControl, Tooltip } from '@invoke-ai/ui-library'; +import { Combobox, FormControl } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; import { areBasesCompatibleForRefImage } from 'features/controlLayers/store/validators'; @@ -73,7 +74,7 @@ export const RefImageModel = memo(({ modelKey, onChangeModel }: Props) => { }); return ( - + { noOptionsMessage={noOptionsMessage} /> - + ); }); 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..976a8c981b0 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RefImage/RefImagePreview.tsx @@ -1,6 +1,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Flex, Icon, IconButton, Skeleton, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, Icon, IconButton, Skeleton, Text } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { round } from 'es-toolkit/compat'; import { useRefImageEntity } from 'features/controlLayers/components/RefImage/useRefImageEntity'; import { useRefImageIdContext } from 'features/controlLayers/contexts/RefImageIdContext'; @@ -125,7 +126,7 @@ export const RefImagePreview = memo(() => { ); } return ( - 0 ? : undefined}> + 0 ? : undefined}> { /> )} - + ); }); RefImagePreview.displayName = 'RefImagePreview'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx index c63a67ebdeb..a9081649c61 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RegionalGuidance/RegionalReferenceImageModel.tsx @@ -1,5 +1,6 @@ -import { Combobox, FormControl, Tooltip } from '@invoke-ai/ui-library'; +import { Combobox, FormControl } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox'; import { selectBase } from 'features/controlLayers/store/paramsSlice'; import { memo, useCallback, useMemo } from 'react'; @@ -54,7 +55,7 @@ export const RegionalReferenceImageModel = memo(({ modelKey, onChangeModel }: Pr }); return ( - + - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx b/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx index 9ddd1b4c592..df593fa2fd6 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObjectInfoTooltip.tsx @@ -1,4 +1,5 @@ -import { Flex, Icon, ListItem, Text, Tooltip, UnorderedList } from '@invoke-ai/ui-library'; +import { Flex, Icon, ListItem, Text, UnorderedList } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import type { PropsWithChildren } from 'react'; import { memo } from 'react'; import { Trans } from 'react-i18next'; @@ -48,11 +49,11 @@ SelectObjectHelpTooltipContent.displayName = 'SelectObjectHelpTooltipContent'; export const SelectObjectInfoTooltip = memo(() => { return ( - } minW={420}> + } minW={420}> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx index 0a92106a6e6..3c3db0e9f70 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/QueueItemCircularProgress.tsx @@ -1,5 +1,6 @@ import type { CircularProgressProps, SystemStyleObject } from '@invoke-ai/ui-library'; -import { CircularProgress, Tooltip } from '@invoke-ai/ui-library'; +import { CircularProgress } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { getProgressMessage } from 'features/controlLayers/components/StagingArea/shared'; import { memo } from 'react'; import type { S } from 'services/api/types'; @@ -26,7 +27,7 @@ export const QueueItemCircularProgress = memo(({ itemId, status, ...rest }: Prop } return ( - + - + ); }); QueueItemCircularProgress.displayName = 'QueueItemCircularProgress'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx index f9fc483eea5..f976be4c0fb 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaAutoSwitchButtons.tsx @@ -1,6 +1,7 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { selectStagingAreaAutoSwitch, @@ -28,30 +29,33 @@ export const StagingAreaAutoSwitchButtons = memo(() => { return ( <> - } - colorScheme={autoSwitch === 'off' ? 'invokeBlue' : 'base'} - onClick={onClickOff} - isDisabled={!shouldShowStagedImage} - /> - } - colorScheme={autoSwitch === 'switch_on_start' ? 'invokeBlue' : 'base'} - onClick={onClickSwitchOnStart} - isDisabled={!shouldShowStagedImage} - /> - } - colorScheme={autoSwitch === 'switch_on_finish' ? 'invokeBlue' : 'base'} - onClick={onClickSwitchOnFinished} - isDisabled={!shouldShowStagedImage} - /> + + } + colorScheme={autoSwitch === 'off' ? 'invokeBlue' : 'base'} + onClick={onClickOff} + isDisabled={!shouldShowStagedImage} + /> + + + } + colorScheme={autoSwitch === 'switch_on_start' ? 'invokeBlue' : 'base'} + onClick={onClickSwitchOnStart} + isDisabled={!shouldShowStagedImage} + /> + + + } + colorScheme={autoSwitch === 'switch_on_finish' ? 'invokeBlue' : 'base'} + onClick={onClickSwitchOnFinished} + isDisabled={!shouldShowStagedImage} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx index c18ca5cac19..37307052d72 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarAcceptButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useIsRegionFocused } from 'common/hooks/focus'; import { useStagingAreaContext } from 'features/controlLayers/components/StagingArea/context'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; @@ -30,15 +31,16 @@ export const StagingAreaToolbarAcceptButton = memo(() => { ); return ( - } - onClick={ctx.acceptSelected} - colorScheme="invokeBlue" - isDisabled={!acceptSelectedIsEnabled || !shouldShowStagedImage || cancelQueueItemsByDestination.isDisabled} - isLoading={cancelQueueItemsByDestination.isLoading} - /> + + } + onClick={ctx.acceptSelected} + colorScheme="invokeBlue" + isDisabled={!acceptSelectedIsEnabled || !shouldShowStagedImage || cancelQueueItemsByDestination.isDisabled} + isLoading={cancelQueueItemsByDestination.isLoading} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx index 467c80d6ff7..7fcdb20c52f 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardAllButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useStagingAreaContext } from 'features/controlLayers/components/StagingArea/context'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { useCancelQueueItemsByDestination } from 'features/queue/hooks/useCancelQueueItemsByDestination'; @@ -16,15 +17,16 @@ export const StagingAreaToolbarDiscardAllButton = memo(() => { const cancelQueueItemsByDestination = useCancelQueueItemsByDestination(); return ( - } - onClick={ctx.discardAll} - colorScheme="error" - isDisabled={cancelQueueItemsByDestination.isDisabled || !shouldShowStagedImage} - isLoading={cancelQueueItemsByDestination.isLoading} - /> + + } + onClick={ctx.discardAll} + colorScheme="error" + isDisabled={cancelQueueItemsByDestination.isDisabled || !shouldShowStagedImage} + isLoading={cancelQueueItemsByDestination.isLoading} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx index 8f912ca52e2..e5a4a33e3f5 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/StagingArea/StagingAreaToolbarDiscardSelectedButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useStagingAreaContext } from 'features/controlLayers/components/StagingArea/context'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { useCancelQueueItem } from 'features/queue/hooks/useCancelQueueItem'; @@ -18,15 +19,16 @@ export const StagingAreaToolbarDiscardSelectedButton = memo(() => { const { t } = useTranslation(); return ( - } - onClick={ctx.discardSelected} - colorScheme="invokeBlue" - isDisabled={!discardSelectedIsEnabled || cancelQueueItem.isDisabled || !shouldShowStagedImage} - isLoading={cancelQueueItem.isLoading} - /> + + } + onClick={ctx.discardSelected} + colorScheme="invokeBlue" + isDisabled={!discardSelectedIsEnabled || cancelQueueItem.isDisabled || !shouldShowStagedImage} + isLoading={cancelQueueItem.isLoading} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx index 30f5eb0c387..30287a73adc 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBboxButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { memo } from 'react'; @@ -19,7 +20,7 @@ export const ToolBboxButton = memo(() => { }); return ( - + } @@ -27,7 +28,7 @@ export const ToolBboxButton = memo(() => { variant="solid" onClick={selectBbox} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx index 1f3f2a00448..3d63d270a19 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolBrushButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { memo } from 'react'; @@ -19,7 +20,7 @@ export const ToolBrushButton = memo(() => { }); return ( - + } @@ -27,7 +28,7 @@ export const ToolBrushButton = memo(() => { variant="solid" onClick={selectBrush} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx index a8c590163e5..0c009bc4128 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolColorPickerButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { memo } from 'react'; @@ -19,7 +20,7 @@ export const ToolColorPickerButton = memo(() => { }); return ( - + } @@ -27,7 +28,7 @@ export const ToolColorPickerButton = memo(() => { variant="solid" onClick={selectColorPicker} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx index f9de6cc4aaa..d5c7f610f84 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolEraserButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { memo } from 'react'; @@ -19,7 +20,7 @@ export const ToolEraserButton = memo(() => { }); return ( - + } @@ -27,7 +28,7 @@ export const ToolEraserButton = memo(() => { variant="solid" onClick={selectEraser} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx index 13bd3b30086..65b5dbaadc5 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolFillColorPicker.tsx @@ -8,12 +8,12 @@ import { PopoverContent, PopoverTrigger, Portal, - Tooltip, useDisclosure, } from '@invoke-ai/ui-library'; import { createSelector } from '@reduxjs/toolkit'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import RgbaColorPicker from 'common/components/ColorPicker/RgbaColorPicker'; +import { IAITooltip } from 'common/components/IAITooltip'; import { rgbaColorToString } from 'common/util/colorCodeTransformers'; import { selectCanvasSettingsSlice, @@ -110,7 +110,7 @@ export const ToolFillColorPicker = memo(() => { h={8} data-text-tool-safezone="true" > - + { zIndex={fgColorzIndex} /> - + @@ -146,14 +146,15 @@ export const ToolFillColorPicker = memo(() => { - } - /> + + } + /> + diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientButton.tsx index 32275399180..94cd761dc95 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { GradientToolIcon } from 'features/controlLayers/components/Tool/GradientIcons'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { memo, useCallback } from 'react'; @@ -14,7 +15,7 @@ export const ToolGradientButton = memo(() => { const gradientLabel = t('controlLayers.tool.gradient', { defaultValue: 'Gradient' }); return ( - + } @@ -23,7 +24,7 @@ export const ToolGradientButton = memo(() => { variant="solid" onClick={handleClick} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx index a89ef6a470b..6ef0845499f 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientClipToggle.tsx @@ -1,5 +1,6 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { selectGradientClipEnabled, settingsGradientClipToggled, @@ -20,7 +21,7 @@ export const ToolGradientClipToggle = memo(() => { const label = t('controlLayers.gradient.clip', { defaultValue: 'Clip Gradient' }); return ( - + } @@ -29,7 +30,7 @@ export const ToolGradientClipToggle = memo(() => { colorScheme={isEnabled ? 'invokeBlue' : 'base'} onClick={onClick} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx index 03b18c466ee..255a9b1d4da 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolGradientModeToggle.tsx @@ -1,5 +1,6 @@ -import { ButtonGroup, IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { ButtonGroup, IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { GradientLinearIcon, GradientRadialIcon } from 'features/controlLayers/components/Tool/GradientIcons'; import { selectGradientType, settingsGradientTypeChanged } from 'features/controlLayers/store/canvasSettingsSlice'; import { memo, useCallback } from 'react'; @@ -15,7 +16,7 @@ export const ToolGradientModeToggle = memo(() => { return ( - + } @@ -23,8 +24,8 @@ export const ToolGradientModeToggle = memo(() => { variant="solid" onClick={onLinearClick} /> - - + + } @@ -32,7 +33,7 @@ export const ToolGradientModeToggle = memo(() => { variant="solid" onClick={onRadialClick} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx index cd842e64b60..c908eef50c1 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolMoveButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { memo } from 'react'; @@ -19,7 +20,7 @@ export const ToolMoveButton = memo(() => { }); return ( - + } @@ -27,7 +28,7 @@ export const ToolMoveButton = memo(() => { variant="solid" onClick={selectMove} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx index 93029390883..4bdb818ea49 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolRectButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { memo } from 'react'; @@ -19,7 +20,7 @@ export const ToolRectButton = memo(() => { }); return ( - + } @@ -27,7 +28,7 @@ export const ToolRectButton = memo(() => { variant="solid" onClick={selectRect} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx index f16c055fcf2..5aa9060ffb7 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Tool/ToolViewButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSelectTool, useToolIsSelected } from 'features/controlLayers/components/Tool/hooks'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; import { memo } from 'react'; @@ -19,7 +20,7 @@ export const ToolViewButton = memo(() => { }); return ( - + } @@ -27,7 +28,7 @@ export const ToolViewButton = memo(() => { variant="solid" onClick={selectView} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx index 017c550d84c..dcde2246b48 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton.tsx @@ -1,4 +1,5 @@ import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useIsRegionFocused } from 'common/hooks/focus'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; @@ -30,15 +31,16 @@ export const CanvasToolbarFitBboxToLayersButton = memo(() => { }); return ( - } - isDisabled={isBusy} - /> + + } + isDisabled={isBusy} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx index 80b818c7051..6652f2a0360 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToMasksButton.tsx @@ -1,4 +1,5 @@ import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useAutoFitBBoxToMasks } from 'features/controlLayers/hooks/useAutoFitBBoxToMasks'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { useVisibleEntityCountByType } from 'features/controlLayers/hooks/useVisibleEntityCountByType'; @@ -30,15 +31,16 @@ export const CanvasToolbarFitBboxToMasksButton = memo(() => { }); return ( - } - isDisabled={isBusy || !hasVisibleMasks} - /> + + } + isDisabled={isBusy || !hasVisibleMasks} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx index 729eaca1ed9..878bef17dc2 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarRedoButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { canvasRedo } from 'features/controlLayers/store/canvasSlice'; import { selectCanvasMayRedo } from 'features/controlLayers/store/selectors'; @@ -17,15 +18,16 @@ export const CanvasToolbarRedoButton = memo(() => { }, [dispatch]); return ( - } - variant="link" - alignSelf="stretch" - isDisabled={isBusy || !mayRedo} - /> + + } + variant="link" + alignSelf="stretch" + isDisabled={isBusy || !mayRedo} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx index cfa6454baf3..47e6bc700ef 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarResetViewButton.tsx @@ -1,4 +1,5 @@ import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useIsRegionFocused } from 'common/hooks/focus'; import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; @@ -59,14 +60,15 @@ export const CanvasToolbarResetViewButton = memo(() => { }); return ( - } - variant="link" - alignSelf="stretch" - /> + + } + variant="link" + alignSelf="stretch" + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx index 38ca25e41c9..419bd7acbf6 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarSaveToGalleryButton.tsx @@ -1,4 +1,5 @@ import { IconButton, useShiftModifier } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useSaveBboxToGallery, useSaveCanvasToGallery } from 'features/controlLayers/hooks/saveCanvasHooks'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { memo } from 'react'; @@ -13,16 +14,17 @@ export const CanvasToolbarSaveToGalleryButton = memo(() => { const saveBboxToGallery = useSaveBboxToGallery(); return ( - } - aria-label={shift ? t('controlLayers.saveBboxToGallery') : t('controlLayers.saveCanvasToGallery')} - colorScheme="invokeBlue" - tooltip={shift ? t('controlLayers.saveBboxToGallery') : t('controlLayers.saveCanvasToGallery')} - isDisabled={isBusy} - /> + + } + aria-label={shift ? t('controlLayers.saveBboxToGallery') : t('controlLayers.saveCanvasToGallery')} + colorScheme="invokeBlue" + isDisabled={isBusy} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx index 7a884013f0e..65df5ad229e 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Toolbar/CanvasToolbarUndoButton.tsx @@ -1,5 +1,6 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy'; import { canvasUndo } from 'features/controlLayers/store/canvasSlice'; import { selectCanvasMayUndo } from 'features/controlLayers/store/selectors'; @@ -17,15 +18,16 @@ export const CanvasToolbarUndoButton = memo(() => { }, [dispatch]); return ( - } - variant="link" - alignSelf="stretch" - isDisabled={isBusy || !mayUndo} - /> + + } + variant="link" + alignSelf="stretch" + isDisabled={isBusy || !mayUndo} + /> + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx b/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx index 65a9f538395..17dfe765fcc 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/Transform/TransformSmoothingControls.tsx @@ -1,5 +1,6 @@ -import { Flex, FormControl, FormLabel, Select, Switch, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, FormControl, FormLabel, Select, Switch } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { selectTransformSmoothingEnabled, selectTransformSmoothingMode, @@ -30,12 +31,12 @@ export const TransformSmoothingControls = memo(() => { return ( - + {t('controlLayers.transform.smoothing')} - + {t('controlLayers.transform.smoothingMode')} ); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx index b635c37d804..cf1896755a0 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressIndicator2.tsx @@ -1,5 +1,6 @@ import type { CircularProgressProps, SystemStyleObject } from '@invoke-ai/ui-library'; -import { CircularProgress, Tooltip } from '@invoke-ai/ui-library'; +import { CircularProgress } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { memo } from 'react'; import type { S } from 'services/api/types'; import { formatProgressMessage } from 'services/events/stores'; @@ -14,7 +15,7 @@ const circleStyles: SystemStyleObject = { export const ProgressIndicator = memo( ({ progressEvent, ...rest }: { progressEvent: S['InvocationProgressEvent'] } & CircularProgressProps) => { return ( - + - + ); } ); diff --git a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx index bb010b07c48..2d043c9c816 100644 --- a/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx +++ b/invokeai/frontend/web/src/features/lora/components/LoRASelect.tsx @@ -34,10 +34,9 @@ const LoRASelect = () => { if (model.base !== currentBaseModel) { return false; } - // For models with variant support: filter by variant when both main model and LoRA have variant info. - // LoRAs with no variant (null) are always shown (compatible with all variants). + // For Flux2: filter by variant when both main model and LoRA have variant info if ( - currentMainModelConfig && + currentMainModelConfig?.base === 'flux2' && 'variant' in currentMainModelConfig && currentMainModelConfig.variant && 'variant' in model && diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx index f124462e8b4..6050ab84b27 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx @@ -1,56 +1,224 @@ import { Box, Button, Flex, Heading } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; +import { getApiErrorDetail } from 'features/modelManagerV2/util/getApiErrorDetail'; import { toast } from 'features/toast/toast'; import { t } from 'i18next'; -import { memo, useCallback, useMemo } from 'react'; -import { useListModelInstallsQuery, usePruneCompletedModelInstallsMutation } from 'services/api/endpoints/models'; +import { memo, useCallback, useMemo, useRef, useState } from 'react'; +import { PiPauseBold, PiPlayBold, PiXBold } from 'react-icons/pi'; +import { + useCancelModelInstallMutation, + useListModelInstallsQuery, + usePauseModelInstallMutation, + usePruneCompletedModelInstallsMutation, + useResumeModelInstallMutation, +} from 'services/api/endpoints/models'; +import type { ModelInstallJob } from 'services/api/types'; +import { $isConnected } from 'services/events/stores'; import { ModelInstallQueueItem } from './ModelInstallQueueItem'; +const hasRestartRequired = (job: ModelInstallJob) => { + return job.download_parts?.some((part) => part.resume_required || part.status === 'error') ?? false; +}; + export const ModelInstallQueue = memo(() => { + const isConnected = useStore($isConnected); const { data } = useListModelInstallsQuery(); + const [bulkActionInProgress, setBulkActionInProgress] = useState<'pause' | 'resume' | 'cancel' | null>(null); + const bulkActionLockRef = useRef(false); + + const [cancelModelInstall] = useCancelModelInstallMutation(); + const [pauseModelInstall] = usePauseModelInstallMutation(); + const [resumeModelInstall] = useResumeModelInstallMutation(); + const [_pruneCompletedModelInstalls, { isLoading: isPruning }] = usePruneCompletedModelInstallsMutation(); + + const installActionIds = useMemo(() => { + const pauseable: number[] = []; + const resumable: number[] = []; + const cancelable: number[] = []; + + for (const model of data ?? []) { + if (model.status === 'downloading' || model.status === 'waiting') { + pauseable.push(model.id); + } + + if (model.status === 'paused') { + cancelable.push(model.id); + if (!hasRestartRequired(model)) { + resumable.push(model.id); + } + continue; + } + + if (model.status === 'running') { + cancelable.push(model.id); + } + } + + return { + pauseable, + resumable, + cancelable: cancelable.concat(pauseable), + } as const; + }, [data]); + + const { + pauseable: pauseableInstallIds, + resumable: resumableInstallIds, + cancelable: cancelableInstallIds, + } = installActionIds; + + const runBulkAction = useCallback( + async (action: 'pause' | 'resume' | 'cancel', installIds: number[]) => { + if (installIds.length === 0 || bulkActionLockRef.current || isPruning) { + return; + } + + bulkActionLockRef.current = true; + setBulkActionInProgress(action); + + try { + const results = await Promise.allSettled( + installIds.map((id) => { + if (action === 'pause') { + return pauseModelInstall(id).unwrap(); + } + if (action === 'resume') { + return resumeModelInstall(id).unwrap(); + } + return cancelModelInstall(id).unwrap(); + }) + ); + + const hasSucceeded = results.some((result) => result.status === 'fulfilled'); + const firstError = results.find((result) => result.status === 'rejected') as PromiseRejectedResult | undefined; + + if (hasSucceeded) { + const title = + action === 'pause' + ? t('toast.modelDownloadPaused') + : action === 'resume' + ? t('toast.modelDownloadResumed') + : t('toast.modelImportCanceled'); - const [_pruneCompletedModelInstalls] = usePruneCompletedModelInstallsMutation(); - - const pruneCompletedModelInstalls = useCallback(() => { - _pruneCompletedModelInstalls() - .unwrap() - .then((_) => { - toast({ - id: 'MODEL_INSTALL_QUEUE_PRUNED', - title: t('toast.prunedQueue'), - status: 'success', - }); - }) - .catch((error) => { - if (error) { toast({ - id: 'MODEL_INSTALL_QUEUE_PRUNE_FAILED', - title: `${error.data.detail} `, + id: `MODEL_INSTALL_QUEUE_${action.toUpperCase()}_ALL`, + title, + status: 'success', + }); + } + + if (firstError) { + toast({ + id: `MODEL_INSTALL_QUEUE_${action.toUpperCase()}_ALL_FAILED`, + title: getApiErrorDetail(firstError.reason), status: 'error', }); } + } finally { + bulkActionLockRef.current = false; + setBulkActionInProgress(null); + } + }, + [cancelModelInstall, isPruning, pauseModelInstall, resumeModelInstall] + ); + + const pruneCompletedModelInstalls = useCallback(async () => { + if (bulkActionLockRef.current) { + return; + } + + try { + await _pruneCompletedModelInstalls().unwrap(); + toast({ + id: 'MODEL_INSTALL_QUEUE_PRUNED', + title: t('toast.prunedQueue'), + status: 'success', }); + } catch (error) { + toast({ + id: 'MODEL_INSTALL_QUEUE_PRUNE_FAILED', + title: getApiErrorDetail(error), + status: 'error', + }); + } }, [_pruneCompletedModelInstalls]); + const hasPauseableInstalls = pauseableInstallIds.length > 0; + const hasResumableInstalls = resumableInstallIds.length > 0; + const hasCancelableInstalls = cancelableInstallIds.length > 0; + const showResumeAll = !hasPauseableInstalls && hasResumableInstalls; + const pauseResumeAvailable = hasPauseableInstalls || hasResumableInstalls; + const pruneAvailable = useMemo(() => { return data?.some( (model) => model.status === 'cancelled' || model.status === 'error' || model.status === 'completed' ); }, [data]); + const pauseResumeLabel = showResumeAll ? t('modelManager.resumeAll') : t('modelManager.pauseAll'); + const pauseResumeTooltip = showResumeAll ? t('modelManager.resumeAllTooltip') : t('modelManager.pauseAllTooltip'); + + const pauseOrResumeAll = useCallback(() => { + if (showResumeAll) { + void runBulkAction('resume', resumableInstallIds); + return; + } + + void runBulkAction('pause', pauseableInstallIds); + }, [pauseableInstallIds, resumableInstallIds, runBulkAction, showResumeAll]); + + const cancelAll = useCallback(() => { + void runBulkAction('cancel', cancelableInstallIds); + }, [cancelableInstallIds, runBulkAction]); + + const isBulkActionRunning = bulkActionInProgress !== null; + return ( - {t('modelManager.installQueue')} - + + {t('modelManager.installQueue')} + {!isConnected && ( + + + {t('modelManager.backendDisconnected')} + + + )} + + + + + + diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx index 87b50d0501f..27096c2b52b 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueBadge.tsx @@ -8,21 +8,23 @@ const STATUSES = { downloading: { colorScheme: 'yellow', translationKey: 'queue.in_progress' }, downloads_done: { colorScheme: 'yellow', translationKey: 'queue.in_progress' }, running: { colorScheme: 'yellow', translationKey: 'queue.in_progress' }, + paused: { colorScheme: 'orange', translationKey: 'queue.paused' }, completed: { colorScheme: 'green', translationKey: 'queue.completed' }, error: { colorScheme: 'red', translationKey: 'queue.failed' }, cancelled: { colorScheme: 'orange', translationKey: 'queue.canceled' }, -}; +} as const satisfies Partial>; const ModelInstallQueueBadge = ({ status }: { status?: ModelInstallStatus }) => { const { t } = useTranslation(); + const statusConfig = status ? STATUSES[status] : undefined; - if (!status || !Object.keys(STATUSES).includes(status)) { + if (!statusConfig) { return null; } return ( - - {t(STATUSES[status].translationKey)} + + {t(statusConfig.translationKey)} ); }; diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx index bfe81713d09..e2c7d69c946 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx @@ -1,10 +1,18 @@ -import { Flex, IconButton, Progress, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Badge, Box, Flex, IconButton, Progress, Text } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { isNil } from 'es-toolkit/compat'; +import { getApiErrorDetail } from 'features/modelManagerV2/util/getApiErrorDetail'; import { toast } from 'features/toast/toast'; import { t } from 'i18next'; -import { memo, useCallback, useMemo } from 'react'; -import { PiXBold } from 'react-icons/pi'; -import { useCancelModelInstallMutation } from 'services/api/endpoints/models'; +import { memo, useCallback, useEffect, useMemo, useRef } from 'react'; +import { PiArrowClockwiseBold, PiPauseBold, PiPlayBold, PiXBold } from 'react-icons/pi'; +import { + useCancelModelInstallMutation, + usePauseModelInstallMutation, + useRestartFailedModelInstallMutation, + useRestartModelInstallFileMutation, + useResumeModelInstallMutation, +} from 'services/api/endpoints/models'; import type { ModelInstallJob } from 'services/api/types'; import ModelInstallQueueBadge from './ModelInstallQueueBadge'; @@ -29,6 +37,11 @@ export const ModelInstallQueueItem = memo((props: ModelListItemProps) => { const { installJob } = props; const [deleteImportModel] = useCancelModelInstallMutation(); + const [pauseModelInstall] = usePauseModelInstallMutation(); + const [resumeModelInstall] = useResumeModelInstallMutation(); + const [restartFailedModelInstall] = useRestartFailedModelInstallMutation(); + const [restartModelInstallFile] = useRestartModelInstallFileMutation(); + const resumeFromScratchShown = useRef(false); const handleDeleteModelImport = useCallback(() => { deleteImportModel(installJob.id) @@ -41,15 +54,116 @@ export const ModelInstallQueueItem = memo((props: ModelListItemProps) => { }); }) .catch((error) => { - if (error) { + toast({ + id: 'MODEL_INSTALL_CANCEL_FAILED', + title: getApiErrorDetail(error), + status: 'error', + }); + }); + }, [deleteImportModel, installJob]); + + const handlePauseModelInstall = useCallback(() => { + pauseModelInstall(installJob.id) + .unwrap() + .then(() => { + toast({ + id: 'MODEL_INSTALL_PAUSED', + title: t('toast.modelDownloadPaused'), + status: 'success', + }); + }) + .catch((error) => { + toast({ + id: 'MODEL_INSTALL_PAUSE_FAILED', + title: getApiErrorDetail(error), + status: 'error', + }); + }); + }, [installJob, pauseModelInstall]); + + const hasRestartedFromScratch = useCallback((job: ModelInstallJob) => { + return ( + job.download_parts?.some( + (part) => + part.resume_from_scratch || (part.resume_message?.toLowerCase().includes('partial file missing') ?? false) + ) ?? false + ); + }, []); + + const handleResumeModelInstall = useCallback(() => { + resumeModelInstall(installJob.id) + .unwrap() + .then((job) => { + const restartedFromScratch = hasRestartedFromScratch(job); + if (restartedFromScratch && !resumeFromScratchShown.current) { + resumeFromScratchShown.current = true; toast({ - id: 'MODEL_INSTALL_CANCEL_FAILED', - title: `${error.data.detail} `, - status: 'error', + id: 'MODEL_INSTALL_RESTARTED_FROM_SCRATCH', + title: t('toast.modelDownloadRestartedFromScratch'), + status: 'warning', }); + return; } + toast({ + id: 'MODEL_INSTALL_RESUMED', + title: t('toast.modelDownloadResumed'), + status: 'success', + }); + }) + .catch((error) => { + toast({ + id: 'MODEL_INSTALL_RESUME_FAILED', + title: getApiErrorDetail(error), + status: 'error', + }); }); - }, [deleteImportModel, installJob]); + }, [hasRestartedFromScratch, installJob, resumeModelInstall]); + + const handleRestartFailed = useCallback(() => { + restartFailedModelInstall(installJob.id) + .unwrap() + .then(() => { + toast({ + id: 'MODEL_INSTALL_RESTART_FAILED', + title: t('toast.modelDownloadRestartFailed'), + status: 'success', + }); + }) + .catch((error) => { + toast({ + id: 'MODEL_INSTALL_RESTART_FAILED_ERROR', + title: getApiErrorDetail(error), + status: 'error', + }); + }); + }, [installJob.id, restartFailedModelInstall]); + + const handleRestartFile = useCallback( + (fileSource: string) => { + restartModelInstallFile({ id: installJob.id, file_source: fileSource }) + .unwrap() + .then(() => { + toast({ + id: 'MODEL_INSTALL_RESTART_FILE', + title: t('toast.modelDownloadRestartFile'), + status: 'success', + }); + }) + .catch((error) => { + toast({ + id: 'MODEL_INSTALL_RESTART_FILE_ERROR', + title: getApiErrorDetail(error), + status: 'error', + }); + }); + }, + [installJob.id, restartModelInstallFile] + ); + + const getRestartFileHandler = useCallback( + (fileSource: string) => () => handleRestartFile(fileSource), + [handleRestartFile] + ); const sourceLocation = useMemo(() => { switch (installJob.source.type) { @@ -87,47 +201,150 @@ export const ModelInstallQueueItem = memo((props: ModelListItemProps) => { return 100; } - if (isNil(installJob.bytes) || isNil(installJob.total_bytes)) { - return null; + const parts = installJob.download_parts; + if (parts && parts.length > 0) { + const totalBytesFromParts = parts.reduce((sum, part) => sum + (part.total_bytes ?? 0), 0); + const currentBytesFromParts = parts.reduce((sum, part) => sum + (part.bytes ?? 0), 0); + const totalBytes = Math.max(totalBytesFromParts, installJob.total_bytes ?? 0); + const currentBytes = Math.max(currentBytesFromParts, installJob.bytes ?? 0); + if (totalBytes > 0) { + return (currentBytes / totalBytes) * 100; + } + return 0; } - if (installJob.total_bytes === 0) { - return 0; + if (!isNil(installJob.bytes) && !isNil(installJob.total_bytes) && installJob.total_bytes > 0) { + return (installJob.bytes / installJob.total_bytes) * 100; } - return (installJob.bytes / installJob.total_bytes) * 100; - }, [installJob.bytes, installJob.status, installJob.total_bytes]); + return null; + }, [installJob.bytes, installJob.download_parts, installJob.status, installJob.total_bytes]); + + const restartRequiredParts = useMemo(() => { + return installJob.download_parts?.filter((part) => part.resume_required || part.status === 'error') ?? []; + }, [installJob.download_parts]); + + useEffect(() => { + if (resumeFromScratchShown.current) { + return; + } + const restartedFromScratch = hasRestartedFromScratch(installJob); + if (restartedFromScratch) { + resumeFromScratchShown.current = true; + toast({ + id: 'MODEL_INSTALL_RESTARTED_FROM_SCRATCH_AUTO', + title: t('toast.modelDownloadRestartedFromScratch'), + status: 'warning', + }); + } + }, [hasRestartedFromScratch, installJob]); + + const hasRestartRequired = restartRequiredParts.length > 0; + + const showPause = installJob.status === 'downloading' || installJob.status === 'waiting'; + const showResume = installJob.status === 'paused' && !hasRestartRequired; + const showCancel = + installJob.status === 'downloading' || + installJob.status === 'waiting' || + installJob.status === 'running' || + installJob.status === 'paused'; return ( - - }> - - - {modelName} - - - + <> + + } + > + + + {modelName} + + + + + + + {showResume && ( + } + onClick={handleResumeModelInstall} + variant="ghost" + /> + )} + {showPause && ( + } + onClick={handlePauseModelInstall} + variant="ghost" + /> + )} + {hasRestartRequired && ( + } + onClick={handleRestartFailed} + variant="ghost" + /> + )} + {showCancel && ( + } + onClick={handleDeleteModelImport} + variant="ghost" + /> + )} + {!showResume && !showPause && !showCancel && } - - } - onClick={handleDeleteModelImport} - variant="ghost" - /> - + + {hasRestartRequired && ( + + {restartRequiredParts.map((part) => { + const fileName = part.source.split('/').slice(-1)[0] ?? t('common.unknown'); + const isResumeRequired = part.resume_required; + return ( + + + {fileName} + + + {isResumeRequired ? t('modelManager.restartRequired') : t('queue.failed')} + + + {isResumeRequired ? t('modelManager.resumeRefused') : t('queue.failed')} + + } + onClick={getRestartFileHandler(part.source)} + variant="ghost" + /> + + ); + })} + + )} + ); }); @@ -141,11 +358,25 @@ type TooltipLabelProps = { const TooltipLabel = memo(({ name, source, installJob }: TooltipLabelProps) => { const progressString = useMemo(() => { - if (installJob.status !== 'downloading' || installJob.bytes === undefined || installJob.total_bytes === undefined) { + if (installJob.status !== 'downloading') { return ''; } - return `${formatBytes(installJob.bytes)} / ${formatBytes(installJob.total_bytes)}`; - }, [installJob.bytes, installJob.total_bytes, installJob.status]); + const parts = installJob.download_parts; + if (parts && parts.length > 0) { + const totalBytesFromParts = parts.reduce((sum, part) => sum + (part.total_bytes ?? 0), 0); + const currentBytesFromParts = parts.reduce((sum, part) => sum + (part.bytes ?? 0), 0); + const totalBytes = Math.max(totalBytesFromParts, installJob.total_bytes ?? 0); + const currentBytes = Math.max(currentBytesFromParts, installJob.bytes ?? 0); + if (totalBytes > 0) { + return `${formatBytes(currentBytes)} / ${formatBytes(totalBytes)}`; + } + return ''; + } + if (!isNil(installJob.bytes) && !isNil(installJob.total_bytes) && installJob.total_bytes > 0) { + return `${formatBytes(installJob.bytes)} / ${formatBytes(installJob.total_bytes)}`; + } + return ''; + }, [installJob.bytes, installJob.download_parts, installJob.total_bytes, installJob.status]); return ( <> diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx index 86350c54c42..62945291b46 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StarterModelsResults.tsx @@ -1,4 +1,5 @@ -import { Flex, Icon, IconButton, Input, InputGroup, InputRightElement, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, Icon, IconButton, Input, InputGroup, InputRightElement, Text } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import { map, size } from 'es-toolkit/compat'; import type { ChangeEventHandler } from 'react'; @@ -55,11 +56,11 @@ export const StarterModelsResults = memo(({ results }: StarterModelsResultsProps {t('modelManager.starterBundles')} - + - + {map(results.starter_bundles, (bundle) => ( diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx index 9547046ba41..d44a4ddb2f3 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx @@ -1,7 +1,8 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Badge, chakra, Checkbox, Flex, Spacer, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Badge, chakra, Checkbox, Flex, Spacer, Text } from '@invoke-ai/ui-library'; import { createSelector } from '@reduxjs/toolkit'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { selectModelManagerV2Slice, selectSelectedModelKeys, @@ -143,12 +144,12 @@ const ModelListItem = ({ model }: ModelListItemProps) => { {isMissing && ( - + {t('modelManager.missingFiles')} - + )} diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx index 5734d84e9cd..5277721a3ce 100644 --- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/SyncModelsButton.tsx @@ -1,4 +1,5 @@ -import { Button, Tooltip, useDisclosure } from '@invoke-ai/ui-library'; +import { Button, useDisclosure } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { PiArrowsClockwiseBold } from 'react-icons/pi'; @@ -15,7 +16,7 @@ export const SyncModelsButton = memo(() => { return ( <> - + - + ); diff --git a/invokeai/frontend/web/src/features/modelManagerV2/util/getApiErrorDetail.ts b/invokeai/frontend/web/src/features/modelManagerV2/util/getApiErrorDetail.ts new file mode 100644 index 00000000000..69f72cc37dd --- /dev/null +++ b/invokeai/frontend/web/src/features/modelManagerV2/util/getApiErrorDetail.ts @@ -0,0 +1,22 @@ +import { t } from 'i18next'; + +type ApiErrorLike = { + data?: { + detail?: string; + }; +}; + +export const getApiErrorDetail = (error: unknown): string => { + if (typeof error === 'object' && error !== null && 'data' in error) { + const { data } = error as ApiErrorLike; + if (typeof data?.detail === 'string') { + return data.detail; + } + } + + if (error instanceof Error && error.message) { + return error.message; + } + + return t('common.unknown'); +}; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx index 6c1a012e891..4cfe4658ca9 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeClassificationIcon.tsx @@ -1,4 +1,5 @@ -import { Icon, Tooltip } from '@invoke-ai/ui-library'; +import { Icon } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useNodeClassification } from 'features/nodes/hooks/useNodeClassification'; import type { Classification } from 'features/nodes/types/common'; import { memo } from 'react'; @@ -17,13 +18,13 @@ const InvocationNodeClassificationIcon = (_: Props) => { } return ( - } placement="top" shouldWrapChildren > - + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx index a257326f929..0e1623f39eb 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeInfoIcon.tsx @@ -1,4 +1,5 @@ -import { Flex, Icon, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, Icon, Text } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { compare } from 'compare-versions'; import { useInvocationNodeNotes } from 'features/nodes/hooks/useNodeNotes'; import { useNodeTemplateOrThrow } from 'features/nodes/hooks/useNodeTemplateOrThrow'; @@ -14,9 +15,9 @@ interface Props { export const InvocationNodeInfoIcon = memo(({ nodeId }: Props) => { return ( - } placement="top" shouldWrapChildren> + } placement="top" shouldWrapChildren> - + ); }); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx index a740a2ee3df..bed3b84f626 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx @@ -1,5 +1,6 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Badge, CircularProgress, Flex, Icon, Image, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Badge, CircularProgress, Flex, Icon, Image, Text } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useNodeExecutionState } from 'features/nodes/hooks/useNodeExecutionState'; import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants'; import type { NodeExecutionState } from 'features/nodes/types/invocation'; @@ -29,11 +30,11 @@ const InvocationNodeStatusIndicator = ({ nodeId }: Props) => { } return ( - } placement="top"> + } placement="top"> - + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx index ba3282459fd..f94430aade3 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldHandle.tsx @@ -1,6 +1,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Box, Tooltip } from '@invoke-ai/ui-library'; +import { Box } from '@invoke-ai/ui-library'; import { Handle, Position } from '@xyflow/react'; +import { IAITooltip } from 'common/components/IAITooltip'; import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor'; import { useConnectionErrorTKey, @@ -106,7 +107,7 @@ type HandleCommonProps = { const IdleHandle = memo(({ fieldTemplate, fieldTypeName, fieldColor, isModelField }: HandleCommonProps) => { return ( - + - + ); }); IdleHandle.displayName = 'IdleHandle'; @@ -139,7 +140,7 @@ const ConnectionInProgressHandle = memo( }, [fieldTypeName, t, connectionError]); return ( - + - + ); } ); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx index 396b05c2ac2..98a7d6ec57a 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputFieldTitle.tsx @@ -1,6 +1,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Icon, Input, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Icon, Input, Text } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useEditable } from 'common/hooks/useEditable'; import { InputFieldTooltipContent } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldTooltipContent'; import { @@ -90,7 +91,7 @@ export const InputFieldTitle = memo((props: Props) => { if (!editable.isEditing) { return ( - } openDelay={HANDLE_TOOLTIP_OPEN_DELAY} placement="top" @@ -108,7 +109,7 @@ export const InputFieldTitle = memo((props: Props) => { {editable.value} {isAddedToForm && } - + ); } diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx index cbe738f60a2..018552aba1a 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldHandle.tsx @@ -1,6 +1,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Box, Tooltip } from '@invoke-ai/ui-library'; +import { Box } from '@invoke-ai/ui-library'; import { Handle, Position } from '@xyflow/react'; +import { IAITooltip } from 'common/components/IAITooltip'; import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor'; import { useConnectionErrorTKey, @@ -106,7 +107,7 @@ type HandleCommonProps = { const IdleHandle = memo(({ fieldTemplate, fieldTypeName, fieldColor, isModelField }: HandleCommonProps) => { return ( - + - + ); }); IdleHandle.displayName = 'IdleHandle'; @@ -139,7 +140,7 @@ const ConnectionInProgressHandle = memo( }, [fieldTypeName, t, connectionErrorTKey]); return ( - + - + ); } ); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx index ac4715f71e0..df935db91c2 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTitle.tsx @@ -1,5 +1,6 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Text, Tooltip } from '@invoke-ai/ui-library'; +import { Text } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { OutputFieldTooltipContent } from 'features/nodes/components/flow/nodes/Invocation/fields/OutputFieldTooltipContent'; import { useConnectionErrorTKey, @@ -34,7 +35,7 @@ export const OutputFieldTitle = memo(({ nodeId, fieldName }: Props) => { const connectionErrorTKey = useConnectionErrorTKey(nodeId, fieldName, 'source'); return ( - } openDelay={HANDLE_TOOLTIP_OPEN_DELAY} placement="top" @@ -47,7 +48,7 @@ export const OutputFieldTitle = memo(({ nodeId, fieldName }: Props) => { > {fieldTemplate.title} - + ); }); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx index 10609fbc267..38706e9f6ba 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/AutoLayoutPopover.tsx @@ -18,6 +18,7 @@ import { } from '@invoke-ai/ui-library'; import { useReactFlow } from '@xyflow/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { buildUseBoolean } from 'common/hooks/useBoolean'; import { useAutoLayout } from 'features/nodes/hooks/useAutoLayout'; import { @@ -114,12 +115,9 @@ export const AutoLayoutPopover = memo(() => { return ( - } - onClick={popover.toggle} - /> + + } onClick={popover.toggle} /> + diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx index 68801954f50..9c36cb9a699 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx @@ -1,6 +1,7 @@ import { ButtonGroup, IconButton } from '@invoke-ai/ui-library'; import { useReactFlow } from '@xyflow/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { selectShouldShowMinimapPanel, shouldShowMinimapPanelChanged, @@ -40,32 +41,36 @@ const ViewportControls = () => { return ( - } - /> - } - /> - } - /> + + } + /> + + + } + /> + + + } + /> + - } - /> + + } + /> + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx index 719f84bf646..85c9ba38e87 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx @@ -1,4 +1,5 @@ import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { $addNodeCmdk } from 'features/nodes/store/nodesSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -12,13 +13,9 @@ const AddNodeButton = () => { }, []); return ( - } - onClick={onClick} - pointerEvents="auto" - /> + + } onClick={onClick} pointerEvents="auto" /> + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx index 86411f50265..d5a8cacceae 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx @@ -1,5 +1,6 @@ import { ConfirmationAlertDialog, Flex, IconButton, Text, useDisclosure } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useDoesWorkflowHaveUnsavedChanges } from 'features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher'; import { nodeEditorReset } from 'features/nodes/store/nodesSlice'; import { toast } from 'features/toast/toast'; @@ -35,13 +36,14 @@ const ClearFlowButton = () => { return ( <> - } - onClick={onClick} - pointerEvents="auto" - /> + + } + onClick={onClick} + pointerEvents="auto" + /> + { const saveOrSaveAsWorkflow = useSaveOrSaveAsWorkflow(); return ( - } - isDisabled={!doesWorkflowHaveUnsavedChanges} - onClick={saveOrSaveAsWorkflow} - pointerEvents="auto" - /> + + } + isDisabled={!doesWorkflowHaveUnsavedChanges} + onClick={saveOrSaveAsWorkflow} + pointerEvents="auto" + /> + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx index 091a2bf758d..256e53c1df8 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx @@ -1,5 +1,6 @@ -import { Text, Tooltip } from '@invoke-ai/ui-library'; +import { Text } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { linkifyOptions, linkifySx } from 'common/components/linkify'; import { selectWorkflowDescription } from 'features/nodes/store/selectors'; import Linkify from 'linkify-react'; @@ -13,11 +14,11 @@ export const ActiveWorkflowDescription = memo(() => { } return ( - + {description} - + ); }); diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowName.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowName.tsx index 024955f3c4b..c0cb776d313 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowName.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowName.tsx @@ -1,5 +1,6 @@ -import { Flex, Icon, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, Icon, Text } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useDoesWorkflowHaveUnsavedChanges } from 'features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher'; import { selectWorkflowName } from 'features/nodes/store/selectors'; import { selectWorkflowMode } from 'features/nodes/store/workflowLibrarySlice'; @@ -18,11 +19,11 @@ export const WorkflowName = () => { return ( {name.length ? ( - } placement="top"> + } placement="top"> {name} - + ) : ( {t('workflows.unnamedWorkflow')} @@ -30,11 +31,11 @@ export const WorkflowName = () => { )} {doesWorkflowHaveUnsavedChanges && mode === 'edit' && ( - + - + )} diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx index d5182ddd625..87f57e394c3 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/viewMode/WorkflowWarning.tsx @@ -1,4 +1,5 @@ -import { Flex, Icon, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, Icon } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useGetNodesNeedUpdate } from 'features/nodes/hooks/useGetNodesNeedUpdate'; import { PiWarningBold } from 'react-icons/pi'; @@ -12,10 +13,10 @@ export const WorkflowWarning = () => { } return ( - }> + }> - + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx index 025406a0f00..45324638dc4 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DeleteWorkflow.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useDeleteWorkflow } from 'features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog'; import type { MouseEvent } from 'react'; import { useCallback } from 'react'; @@ -17,7 +18,7 @@ export const DeleteWorkflow = ({ workflowId }: { workflowId: string }) => { [deleteWorkflow, workflowId] ); return ( - + { colorScheme="error" icon={} /> - + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx index 9e58790e8ca..f223ce40fbc 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/DownloadWorkflow.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useDownloadWorkflowById } from 'features/workflowLibrary/hooks/useDownloadWorkflowById'; import type { MouseEvent } from 'react'; import { useCallback } from 'react'; @@ -18,7 +19,7 @@ export const DownloadWorkflow = ({ workflowId }: { workflowId: string }) => { const { t } = useTranslation(); return ( - + { icon={} isLoading={downloadWorkflowById.isLoading} /> - + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx index 7298ba8a37c..b125c2c3706 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/EditWorkflow.tsx @@ -1,5 +1,6 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { workflowModeChanged } from 'features/nodes/store/workflowLibrarySlice'; import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog'; import type { MouseEvent } from 'react'; @@ -27,7 +28,7 @@ export const EditWorkflow = ({ workflowId }: { workflowId: string }) => { ); return ( - + { onClick={handleClickEdit} icon={} /> - + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx index 34dbe4bde8a..fad26ea11ba 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryListItemActions/ViewWorkflow.tsx @@ -1,5 +1,6 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { workflowModeChanged } from 'features/nodes/store/workflowLibrarySlice'; import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog'; import type { MouseEvent } from 'react'; @@ -27,7 +28,7 @@ export const ViewWorkflow = ({ workflowId }: { workflowId: string }) => { ); return ( - + { onClick={handleClickLoad} icon={} /> - + ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx index 4e8811e40c9..58dff83b52b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowThumbnail/WorkflowThumbnailField.tsx @@ -1,4 +1,5 @@ -import { Box, Button, Flex, Icon, IconButton, Image, Tooltip } from '@invoke-ai/ui-library'; +import { Box, Button, Flex, Icon, IconButton, Image } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { dropzoneAccept } from 'common/hooks/useImageUploadButton'; import { convertImageUrlToBlob } from 'common/util/convertImageUrlToBlob'; import { useCallback, useEffect, useState } from 'react'; @@ -88,7 +89,7 @@ export const WorkflowThumbnailField = ({ return ( <> - + - + ); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFlux2KleinLoRAs.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFlux2KleinLoRAs.ts new file mode 100644 index 00000000000..48f2632a4fd --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFlux2KleinLoRAs.ts @@ -0,0 +1,64 @@ +import type { RootState } from 'app/store/store'; +import { getPrefixedId } from 'features/controlLayers/konva/util'; +import { zModelIdentifierField } from 'features/nodes/types/common'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation, S } from 'services/api/types'; + +export const addFlux2KleinLoRAs = ( + state: RootState, + g: Graph, + denoise: Invocation<'flux2_denoise'>, + modelLoader: Invocation<'flux2_klein_model_loader'>, + textEncoder: Invocation<'flux2_klein_text_encoder'> +): void => { + const enabledLoRAs = state.loras.loras.filter((l) => l.isEnabled && l.model.base === 'flux2'); + const loraCount = enabledLoRAs.length; + + if (loraCount === 0) { + return; + } + + const loraMetadata: S['LoRAMetadataField'][] = []; + + // We will collect LoRAs into a single collection node, then pass them to the LoRA collection loader, which applies + // each LoRA to the transformer and Qwen3 text encoder. + const loraCollector = g.addNode({ + id: getPrefixedId('lora_collector'), + type: 'collect', + }); + const loraCollectionLoader = g.addNode({ + type: 'flux2_klein_lora_collection_loader', + id: getPrefixedId('flux2_klein_lora_collection_loader'), + }); + + g.addEdge(loraCollector, 'collection', loraCollectionLoader, 'loras'); + // Use model loader as transformer and qwen3_encoder input + g.addEdge(modelLoader, 'transformer', loraCollectionLoader, 'transformer'); + g.addEdge(modelLoader, 'qwen3_encoder', loraCollectionLoader, 'qwen3_encoder'); + // Reroute model connections through the LoRA collection loader + g.deleteEdgesTo(denoise, ['transformer']); + g.deleteEdgesTo(textEncoder, ['qwen3_encoder']); + g.addEdge(loraCollectionLoader, 'transformer', denoise, 'transformer'); + g.addEdge(loraCollectionLoader, 'qwen3_encoder', textEncoder, 'qwen3_encoder'); + + for (const lora of enabledLoRAs) { + const { weight } = lora; + const parsedModel = zModelIdentifierField.parse(lora.model); + + const loraSelector = g.addNode({ + type: 'lora_selector', + id: getPrefixedId('lora_selector'), + lora: parsedModel, + weight, + }); + + loraMetadata.push({ + model: parsedModel, + weight, + }); + + g.addEdge(loraSelector, 'lora', loraCollector, 'item'); + } + + g.upsertMetadata({ loras: loraMetadata }); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts index 585dae605b9..db7cba5961a 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts @@ -11,6 +11,7 @@ import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/ import { isFlux2ReferenceImageConfig, isFluxKontextReferenceImageConfig } from 'features/controlLayers/store/types'; import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/validators'; import { zImageField } from 'features/nodes/types/common'; +import { addFlux2KleinLoRAs } from 'features/nodes/util/graph/generation/addFlux2KleinLoRAs'; import { addFLUXFill } from 'features/nodes/util/graph/generation/addFLUXFill'; import { addFLUXLoRAs } from 'features/nodes/util/graph/generation/addFLUXLoRAs'; import { addFLUXReduxes } from 'features/nodes/util/graph/generation/addFLUXRedux'; @@ -259,6 +260,9 @@ export const buildFLUXGraph = async (arg: GraphBuilderArg): Promise; const flux2ModelLoader = modelLoader as Invocation<'flux2_klein_model_loader'>; const flux2L2i = l2i as Invocation<'flux2_vae_decode'>; + const flux2Cond = posCond as Invocation<'flux2_klein_text_encoder'>; + + addFlux2KleinLoRAs(state, g, flux2Denoise, flux2ModelLoader, flux2Cond); // FLUX.2 Klein has built-in multi-reference image editing - no separate model needed const validFlux2RefImageConfigs = selectRefImagesSlice(state) diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/NegativePromptToggleButton.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/NegativePromptToggleButton.tsx index 9dc09f802d0..af320dc0083 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/NegativePromptToggleButton.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/NegativePromptToggleButton.tsx @@ -1,5 +1,6 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { negativePromptChanged, selectHasNegativePrompt } from 'features/controlLayers/store/paramsSlice'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -25,7 +26,7 @@ export const NegativePromptToggleButton = memo(() => { ); return ( - + { px={0.5} colorScheme={hasNegativePrompt ? 'invokeBlue' : 'base'} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx b/invokeai/frontend/web/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx index e7e75336dcb..bd4d897109a 100644 --- a/invokeai/frontend/web/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/PostProcessing/ParamPostProcessingModel.tsx @@ -1,5 +1,6 @@ -import { Box, Combobox, Flex, FormControl, FormLabel, Tooltip } from '@invoke-ai/ui-library'; +import { Box, Combobox, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useModelCombobox } from 'common/hooks/useModelCombobox'; import { postProcessingModelChanged, selectPostProcessingModel } from 'features/parameters/store/upscaleSlice'; import { memo, useCallback, useMemo } from 'react'; @@ -39,7 +40,7 @@ const ParamPostProcessingModel = () => { {t('upscaling.postProcessingModel')} - + { isDisabled={options.length === 0} /> - + ); diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/ViewModePrompt.tsx b/invokeai/frontend/web/src/features/parameters/components/Prompts/ViewModePrompt.tsx index d2801dd34ce..b8218e82865 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Prompts/ViewModePrompt.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Prompts/ViewModePrompt.tsx @@ -1,5 +1,6 @@ -import { Box, Flex, Icon, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Box, Flex, Icon, Text } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { viewModeChanged } from 'features/stylePresets/store/stylePresetSlice'; import { getViewModeChunks } from 'features/stylePresets/util/getViewModeChunks'; import { useCallback, useMemo } from 'react'; @@ -59,7 +60,7 @@ export const ViewModePrompt = ({ - + - + ); diff --git a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx index 47a95f03c5d..afc7a5a4695 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Upscale/ParamSpandrelModel.tsx @@ -1,5 +1,6 @@ -import { Box, Combobox, Flex, FormControl, FormLabel, Tooltip } from '@invoke-ai/ui-library'; +import { Box, Combobox, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; import { useModelCombobox } from 'common/hooks/useModelCombobox'; import { selectUpscaleModel, upscaleModelChanged } from 'features/parameters/store/upscaleSlice'; @@ -42,7 +43,7 @@ const ParamSpandrelModel = () => { {t('upscaling.upscaleModel')} - + { isDisabled={options.length === 0} /> - + ); diff --git a/invokeai/frontend/web/src/features/prompt/AddPromptTriggerButton.tsx b/invokeai/frontend/web/src/features/prompt/AddPromptTriggerButton.tsx index bda8f2eed25..5ede184f512 100644 --- a/invokeai/frontend/web/src/features/prompt/AddPromptTriggerButton.tsx +++ b/invokeai/frontend/web/src/features/prompt/AddPromptTriggerButton.tsx @@ -1,4 +1,5 @@ -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiCodeBold } from 'react-icons/pi'; @@ -12,7 +13,7 @@ export const AddPromptTriggerButton = memo((props: Props) => { const { onOpen, isOpen } = props; const { t } = useTranslation(); return ( - + { icon={} onClick={onOpen} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx b/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx index 9f1d004ba87..c4c14a6a282 100644 --- a/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx +++ b/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx @@ -12,6 +12,7 @@ import type { BatchSizeResult } from 'features/nodes/util/node/resolveBatchValue import { getBatchSize } from 'features/nodes/util/node/resolveBatchValue'; import type { Reason } from 'features/queue/store/readiness'; import { $isReadyToEnqueue, $reasonsWhyCannotEnqueue, selectPromptsCount } from 'features/queue/store/readiness'; +import { selectSystemShouldEnableInformationalPopovers } from 'features/system/store/systemSlice'; import { selectActiveTab } from 'features/ui/store/uiSelectors'; import type { PropsWithChildren } from 'react'; import { memo, useEffect, useMemo, useState } from 'react'; @@ -24,6 +25,12 @@ type Props = TooltipProps & { }; export const InvokeButtonTooltip = ({ prepend, children, ...rest }: PropsWithChildren) => { + const shouldEnableInformationalPopovers = useAppSelector(selectSystemShouldEnableInformationalPopovers); + + if (!shouldEnableInformationalPopovers) { + return children; + } + return ( } maxW={512} {...rest}> {children} diff --git a/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx b/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx index e3cf278edf0..f350e8dea04 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx @@ -1,5 +1,6 @@ -import { Badge, Flex, IconButton, Spacer, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Badge, Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/paramsSlice'; import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts'; import { @@ -79,7 +80,7 @@ export const ActiveStylePreset = () => { {activeStylePreset.name} - + { colorScheme={viewMode ? 'invokeBlue' : 'base'} icon={} /> - - + + { aria-label={t('stylePresets.flatten')} icon={} /> - - + + { aria-label={t('stylePresets.clearTemplateSelection')} icon={} /> - + ); }; diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx index 089293d4e7a..bf5d3a7d246 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm/StylePresetImageField.tsx @@ -1,4 +1,5 @@ -import { Box, Button, Flex, Icon, IconButton, Image, Tooltip } from '@invoke-ai/ui-library'; +import { Box, Button, Flex, Icon, IconButton, Image } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { dropzoneAccept } from 'common/hooks/useImageUploadButton'; import { useCallback } from 'react'; import { useDropzone } from 'react-dropzone'; @@ -62,7 +63,7 @@ export const StylePresetImageField = (props: UseControllerProps - + - + ); diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx index a258b60efee..9aabd3666f9 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetImage.tsx @@ -1,4 +1,5 @@ -import { Flex, Icon, Image, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, Icon, Image } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { typedMemo } from 'common/util/typedMemo'; import { PiImage } from 'react-icons/pi'; @@ -7,7 +8,7 @@ const FALLBACK_ICON_SIZE = '24px'; const StylePresetImage = ({ presetImageUrl, imageWidth }: { presetImageUrl: string | null; imageWidth?: number }) => { return ( - - + ); }; diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeyListItem.tsx b/invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeyListItem.tsx index 6f2aa8d97f8..8440a77c78c 100644 --- a/invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeyListItem.tsx +++ b/invokeai/frontend/web/src/features/system/components/HotkeysModal/HotkeyListItem.tsx @@ -1,6 +1,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { Button, Flex, IconButton, Kbd, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Button, Flex, IconButton, Kbd, Text } from '@invoke-ai/ui-library'; import type { AppThunkDispatch } from 'app/store/store'; +import { IAITooltip } from 'common/components/IAITooltip'; import type { Hotkey, HotkeyConflictInfo } from 'features/system/components/HotkeysModal/useHotkeyData'; import { IS_MAC_OS } from 'features/system/components/HotkeysModal/useHotkeyData'; import { hotkeyChanged, hotkeyReset } from 'features/system/store/hotkeysSlice'; @@ -263,7 +264,7 @@ const HotkeyItem = memo( const hasConflict = conflict !== null; return ( - )} - + ); } return ( - + - + ); }; @@ -332,7 +333,7 @@ const HotkeyItem = memo( {isEditing && ( <> {!isNewHotkey && ( - + } @@ -341,9 +342,9 @@ const HotkeyItem = memo( colorScheme="error" onClick={onDeleteEdit} /> - + )} - + } @@ -351,8 +352,8 @@ const HotkeyItem = memo( variant="ghost" onClick={onCancelEdit} /> - - + + } @@ -361,7 +362,7 @@ const HotkeyItem = memo( onClick={onSaveEdit} disabled={!canSaveEdit} /> - + )} @@ -449,7 +450,7 @@ const HotkeyItemsDisplay = memo( )} {isCustomized && ( - + } @@ -458,10 +459,10 @@ const HotkeyItemsDisplay = memo( colorScheme="warning" onClick={onReset} /> - + )} {!isAddingNew && ( - + } @@ -469,7 +470,7 @@ const HotkeyItemsDisplay = memo( size="sm" onClick={onAddHotkey} /> - + )} diff --git a/invokeai/frontend/web/src/features/system/components/InvokeAILogoComponent.tsx b/invokeai/frontend/web/src/features/system/components/InvokeAILogoComponent.tsx index 0716a73c8c4..6167038cb94 100644 --- a/invokeai/frontend/web/src/features/system/components/InvokeAILogoComponent.tsx +++ b/invokeai/frontend/web/src/features/system/components/InvokeAILogoComponent.tsx @@ -1,4 +1,5 @@ -import { Image, Text, Tooltip } from '@invoke-ai/ui-library'; +import { Image, Text } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import InvokeLogoYellow from 'public/assets/images/invoke-symbol-ylw-lrg.svg'; import { memo, useMemo, useRef } from 'react'; import { useGetAppVersionQuery } from 'services/api/endpoints/appInfo'; @@ -14,7 +15,7 @@ const InvokeAILogoComponent = () => { }, [appVersion]); return ( - + { minH="24px" userSelect="none" /> - + ); }; diff --git a/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx b/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx index a02b9199b19..62aeae3877e 100644 --- a/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx +++ b/invokeai/frontend/web/src/features/system/components/StatusIndicator.tsx @@ -1,5 +1,6 @@ -import { Icon, Tooltip } from '@invoke-ai/ui-library'; +import { Icon } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; +import { IAITooltip } from 'common/components/IAITooltip'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiWarningBold } from 'react-icons/pi'; @@ -11,9 +12,9 @@ const StatusIndicator = () => { if (!isConnected) { return ( - + - + ); } diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingLeftPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingLeftPanelButtons.tsx index 81e8930e401..85a98ed0f55 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingLeftPanelButtons.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingLeftPanelButtons.tsx @@ -1,4 +1,5 @@ -import { ButtonGroup, Flex, Icon, IconButton, spinAnimation, Tooltip, useShiftModifier } from '@invoke-ai/ui-library'; +import { ButtonGroup, Flex, Icon, IconButton, spinAnimation, useShiftModifier } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser'; import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { useDeleteAllExceptCurrentQueueItemDialog } from 'features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog'; @@ -55,14 +56,14 @@ const ToggleLeftPanelButton = memo(() => { const { t } = useTranslation(); return ( - + } flexGrow={1} /> - + ); }); ToggleLeftPanelButton.displayName = 'ToggleLeftPanelButton'; @@ -117,7 +118,7 @@ const DeleteCurrentIconButton = memo(() => { const deleteCurrentQueueItem = useDeleteCurrentQueueItem(); return ( - + { colorScheme="error" flexGrow={1} /> - + ); }); @@ -138,7 +139,7 @@ const DeleteAllExceptCurrentIconButton = memo(() => { const deleteAllExceptCurrent = useDeleteAllExceptCurrentQueueItemDialog(); return ( - + { onClick={deleteAllExceptCurrent.openDialog} flexGrow={1} /> - + ); }); diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingRightPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingRightPanelButtons.tsx index dc0d8025fad..50099bad480 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingRightPanelButtons.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingRightPanelButtons.tsx @@ -1,4 +1,5 @@ -import { Flex, IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { Flex, IconButton } from '@invoke-ai/ui-library'; +import { IAITooltip } from 'common/components/IAITooltip'; import { navigationApi } from 'features/ui/layouts/navigation-api'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -17,14 +18,14 @@ const ToggleRightPanelButton = memo(() => { const { t } = useTranslation(); return ( - + } h={48} /> - + ); }); ToggleRightPanelButton.displayName = 'ToggleRightPanelButton'; diff --git a/invokeai/frontend/web/src/features/ui/components/TabButton.tsx b/invokeai/frontend/web/src/features/ui/components/TabButton.tsx index adc0d4ecfc2..e95e8c3dd8e 100644 --- a/invokeai/frontend/web/src/features/ui/components/TabButton.tsx +++ b/invokeai/frontend/web/src/features/ui/components/TabButton.tsx @@ -1,6 +1,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { IconButton, Tooltip } from '@invoke-ai/ui-library'; +import { IconButton } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; +import { IAITooltip } from 'common/components/IAITooltip'; import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter'; import { navigationApi } from 'features/ui/layouts/navigation-api'; import { selectActiveTab } from 'features/ui/store/uiSelectors'; @@ -23,7 +24,7 @@ export const TabButton = memo(({ tab, icon, label }: { tab: TabName; icon: React useCallbackOnDragEnter(selectTab, ref, 300); return ( - + - + ); }); diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts index 1117f3b61fe..f48b5867672 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/models.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts @@ -6,6 +6,7 @@ import type { operations, paths } from 'services/api/schema'; import type { AnyModelConfig, GetHFTokenStatusResponse, + ModelInstallJob, ResetHFTokenResponse, SetHFTokenArg, SetHFTokenResponse, @@ -66,6 +67,10 @@ type ListModelInstallsResponse = type CancelModelInstallResponse = paths['/api/v2/models/install/{id}']['delete']['responses']['201']['content']['application/json']; +type PauseModelInstallResponse = ModelInstallJob; +type ResumeModelInstallResponse = ModelInstallJob; +type RestartFailedModelInstallResponse = ModelInstallJob; +type RestartModelInstallFileResponse = ModelInstallJob; type PruneCompletedModelInstallsResponse = paths['/api/v2/models/install']['delete']['responses']['200']['content']['application/json']; @@ -267,6 +272,43 @@ export const modelsApi = api.injectEndpoints({ }, invalidatesTags: ['ModelInstalls'], }), + pauseModelInstall: build.mutation({ + query: (id) => { + return { + url: buildModelsUrl(`install/${id}/pause`), + method: 'POST', + }; + }, + invalidatesTags: ['ModelInstalls'], + }), + resumeModelInstall: build.mutation({ + query: (id) => { + return { + url: buildModelsUrl(`install/${id}/resume`), + method: 'POST', + }; + }, + invalidatesTags: ['ModelInstalls'], + }), + restartFailedModelInstall: build.mutation({ + query: (id) => { + return { + url: buildModelsUrl(`install/${id}/restart_failed`), + method: 'POST', + }; + }, + invalidatesTags: ['ModelInstalls'], + }), + restartModelInstallFile: build.mutation({ + query: ({ id, file_source }) => { + return { + url: buildModelsUrl(`install/${id}/restart_file`), + method: 'POST', + body: file_source, + }; + }, + invalidatesTags: ['ModelInstalls'], + }), pruneCompletedModelInstalls: build.mutation({ query: () => { return { @@ -410,6 +452,10 @@ export const { useLazyGetHuggingFaceModelsQuery, useListModelInstallsQuery, useCancelModelInstallMutation, + usePauseModelInstallMutation, + useResumeModelInstallMutation, + useRestartFailedModelInstallMutation, + useRestartModelInstallFileMutation, usePruneCompletedModelInstallsMutation, useGetStarterModelsQuery, useGetHFTokenStatusQuery, diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts index 8bbf79c4d0a..8b6cb454dbe 100644 --- a/invokeai/frontend/web/src/services/api/schema.ts +++ b/invokeai/frontend/web/src/services/api/schema.ts @@ -231,6 +231,7 @@ export type paths = { * * "waiting" -- Job is waiting in the queue to run * * "downloading" -- Model file(s) are downloading * * "running" -- Model has downloaded and the model probing and registration process is running + * * "paused" -- Job is paused and can be resumed * * "completed" -- Installation completed successfully * * "error" -- An error occurred. Details will be in the "error_type" and "error" fields. * * "cancelled" -- Job was cancelled before completion. @@ -328,6 +329,86 @@ export type paths = { patch?: never; trace?: never; }; + "/api/v2/models/install/{id}/pause": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Pause Model Install Job + * @description Pause the model install job corresponding to the given job ID. + */ + post: operations["pause_model_install_job"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v2/models/install/{id}/resume": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Resume Model Install Job + * @description Resume a paused model install job corresponding to the given job ID. + */ + post: operations["resume_model_install_job"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v2/models/install/{id}/restart_failed": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Restart Failed Model Install Job + * @description Restart failed or non-resumable file downloads for the given job. + */ + post: operations["restart_failed_model_install_job"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/v2/models/install/{id}/restart_file": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Restart Model Install File + * @description Restart a specific file download for the given job. + */ + post: operations["restart_model_install_file"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/v2/models/convert/{key}": { parameters: { query?: never; @@ -2171,7 +2252,7 @@ export type components = { */ type: "alpha_mask_to_tensor"; }; - AnyModelConfig: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + AnyModelConfig: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; /** * AppVersion * @description App Version Response @@ -7127,13 +7208,71 @@ export type components = { * @description Content type of downloaded file */ content_type?: string | null; + /** + * Canonical Url + * @description Canonical URL to request on resume + */ + canonical_url?: string | null; + /** + * Etag + * @description ETag from the remote server, if available + */ + etag?: string | null; + /** + * Last Modified + * @description Last-Modified from the remote server, if available + */ + last_modified?: string | null; + /** + * Final Url + * @description Final resolved URL after redirects, if available + */ + final_url?: string | null; + /** + * Expected Total Bytes + * @description Expected total size of the download + */ + expected_total_bytes?: number | null; + /** + * Resume Required + * @description True if server refused resume; restart required + * @default false + */ + resume_required?: boolean; + /** + * Resume Message + * @description Message explaining why resume is required + */ + resume_message?: string | null; + /** + * Resume From Scratch + * @description True if resume metadata existed but the partial file was missing and the download restarted from the beginning + * @default false + */ + resume_from_scratch?: boolean; }; /** * DownloadJobStatus * @description State of a download job. * @enum {string} */ - DownloadJobStatus: "waiting" | "running" | "completed" | "cancelled" | "error"; + DownloadJobStatus: "waiting" | "running" | "paused" | "completed" | "cancelled" | "error"; + /** + * DownloadPausedEvent + * @description Event model for download_paused + */ + DownloadPausedEvent: { + /** + * Timestamp + * @description The timestamp of the event + */ + timestamp: number; + /** + * Source + * @description The source of the download + */ + source: string; + }; /** * DownloadProgressEvent * @description Event model for download_progress @@ -8289,6 +8428,130 @@ export type components = { */ type: "flux2_denoise"; }; + /** + * Apply LoRA Collection - Flux2 Klein + * @description Applies a collection of LoRAs to a FLUX.2 Klein transformer and/or Qwen3 text encoder. + */ + Flux2KleinLoRACollectionLoader: { + /** + * 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; + /** + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. + * @default null + */ + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][] | null; + /** + * Transformer + * @description Transformer + * @default null + */ + transformer?: components["schemas"]["TransformerField"] | null; + /** + * Qwen3 Encoder + * @description Qwen3 tokenizer and text encoder + * @default null + */ + qwen3_encoder?: components["schemas"]["Qwen3EncoderField"] | null; + /** + * type + * @default flux2_klein_lora_collection_loader + * @constant + */ + type: "flux2_klein_lora_collection_loader"; + }; + /** + * Apply LoRA - Flux2 Klein + * @description Apply a LoRA model to a FLUX.2 Klein transformer and/or Qwen3 text encoder. + */ + Flux2KleinLoRALoaderInvocation: { + /** + * 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; + /** + * LoRA + * @description LoRA model to load + * @default null + */ + lora?: components["schemas"]["ModelIdentifierField"] | null; + /** + * Weight + * @description The weight at which the LoRA is applied to each model + * @default 0.75 + */ + weight?: number; + /** + * Transformer + * @description Transformer + * @default null + */ + transformer?: components["schemas"]["TransformerField"] | null; + /** + * Qwen3 Encoder + * @description Qwen3 tokenizer and text encoder + * @default null + */ + qwen3_encoder?: components["schemas"]["Qwen3EncoderField"] | null; + /** + * type + * @default flux2_klein_lora_loader + * @constant + */ + type: "flux2_klein_lora_loader"; + }; + /** + * Flux2KleinLoRALoaderOutput + * @description FLUX.2 Klein LoRA Loader Output + */ + Flux2KleinLoRALoaderOutput: { + /** + * Transformer + * @description Transformer + * @default null + */ + transformer: components["schemas"]["TransformerField"] | null; + /** + * Qwen3 Encoder + * @description Qwen3 tokenizer and text encoder + * @default null + */ + qwen3_encoder: components["schemas"]["Qwen3EncoderField"] | null; + /** + * type + * @default flux2_klein_lora_loader_output + * @constant + */ + type: "flux2_klein_lora_loader_output"; + }; /** * Main Model - Flux2 Klein * @description Loads a Flux2 Klein model, outputting its submodels. @@ -10013,7 +10276,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"]["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"]["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 @@ -10050,7 +10313,7 @@ export type components = { * @description The results of node executions */ results: { - [key: string]: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; + [key: string]: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; }; /** * Errors @@ -12876,7 +13139,7 @@ export type components = { * @description State of an install job running in the background. * @enum {string} */ - InstallStatus: "waiting" | "downloading" | "downloads_done" | "running" | "completed" | "error" | "cancelled"; + InstallStatus: "waiting" | "downloading" | "downloads_done" | "running" | "paused" | "completed" | "error" | "cancelled"; /** * Integer Batch * @description Create a batched generation, where the workflow is executed once for each integer in the batch. @@ -13229,7 +13492,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"]["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"]["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 @@ -13239,7 +13502,7 @@ export type components = { * Result * @description The result of the invocation */ - result: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; + result: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["Flux2KleinLoRALoaderOutput"] | components["schemas"]["Flux2KleinModelLoaderOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PBRMapsOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["PromptTemplateOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["ZImageConditioningOutput"] | components["schemas"]["ZImageControlOutput"] | components["schemas"]["ZImageLoRALoaderOutput"] | components["schemas"]["ZImageModelLoaderOutput"]; }; /** * InvocationErrorEvent @@ -13287,7 +13550,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"]["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"]["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 @@ -13364,6 +13627,8 @@ export type components = { float_range: components["schemas"]["FloatCollectionOutput"]; float_to_int: components["schemas"]["IntegerOutput"]; flux2_denoise: components["schemas"]["LatentsOutput"]; + flux2_klein_lora_collection_loader: components["schemas"]["Flux2KleinLoRALoaderOutput"]; + flux2_klein_lora_loader: components["schemas"]["Flux2KleinLoRALoaderOutput"]; flux2_klein_model_loader: components["schemas"]["Flux2KleinModelLoaderOutput"]; flux2_klein_text_encoder: components["schemas"]["FluxConditioningOutput"]; flux2_vae_decode: components["schemas"]["ImageOutput"]; @@ -13585,7 +13850,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"]["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"]["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 @@ -13654,7 +13919,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"]["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"]["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 @@ -15491,6 +15756,85 @@ export type components = { */ base: "flux"; }; + /** + * LoRA_Diffusers_Flux2_Config + * @description Model config for FLUX.2 (Klein) LoRA models in Diffusers format. + */ + LoRA_Diffusers_Flux2_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Type + * @default lora + * @constant + */ + type: "lora"; + /** + * Trigger Phrases + * @description Set of trigger phrases for this model + */ + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; + /** + * Format + * @default diffusers + * @constant + */ + format: "diffusers"; + /** + * Base + * @default flux2 + * @constant + */ + base: "flux2"; + variant: components["schemas"]["Flux2VariantType"] | null; + }; /** LoRA_Diffusers_SD1_Config */ LoRA_Diffusers_SD1_Config: { /** @@ -15870,6 +16214,85 @@ export type components = { */ base: "flux"; }; + /** + * LoRA_LyCORIS_Flux2_Config + * @description Model config for FLUX.2 (Klein) LoRA models in LyCORIS format. + */ + LoRA_LyCORIS_Flux2_Config: { + /** + * Key + * @description A unique key for this model. + */ + key: string; + /** + * Hash + * @description The hash of the model file(s). + */ + hash: string; + /** + * Path + * @description Path to the model on the filesystem. Relative paths are relative to the Invoke root directory. + */ + path: string; + /** + * File Size + * @description The size of the model in bytes. + */ + file_size: number; + /** + * Name + * @description Name of the model. + */ + name: string; + /** + * Description + * @description Model description + */ + description: string | null; + /** + * Source + * @description The original source of the model (path, URL or repo_id). + */ + source: string; + /** @description The type of source */ + source_type: components["schemas"]["ModelSourceType"]; + /** + * Source Api Response + * @description The original API response from the source, as stringified JSON. + */ + source_api_response: string | null; + /** + * Cover Image + * @description Url for image to preview model + */ + cover_image: string | null; + /** + * Type + * @default lora + * @constant + */ + type: "lora"; + /** + * Trigger Phrases + * @description Set of trigger phrases for this model + */ + trigger_phrases: string[] | null; + /** @description Default settings for this model */ + default_settings: components["schemas"]["LoraModelDefaultSettings"] | null; + /** + * Format + * @default lycoris + * @constant + */ + format: "lycoris"; + /** + * Base + * @default flux2 + * @constant + */ + base: "flux2"; + variant: components["schemas"]["Flux2VariantType"] | null; + }; /** LoRA_LyCORIS_SD1_Config */ LoRA_LyCORIS_SD1_Config: { /** @@ -20004,7 +20427,7 @@ export type components = { * Config * @description The installed model's config */ - config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; }; /** * ModelInstallDownloadProgressEvent @@ -20170,7 +20593,7 @@ export type components = { * Config Out * @description After successful installation, this will hold the configuration object. */ - config_out?: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]) | null; + config_out?: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]) | null; /** * Inplace * @description Leave model in its current location; otherwise install under models directory @@ -20256,7 +20679,7 @@ export type components = { * Config * @description The model's config */ - config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; /** * @description The submodel type, if any * @default null @@ -20277,7 +20700,7 @@ export type components = { * Config * @description The model's config */ - config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + config: components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; /** * @description The submodel type, if any * @default null @@ -20451,7 +20874,7 @@ export type components = { */ ModelsList: { /** Models */ - models: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"])[]; + models: (components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"])[]; }; /** * Multiply Integers @@ -27743,7 +28166,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Validation Error */ @@ -27793,7 +28216,7 @@ export interface operations { * "repo_variant": "fp16", * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -27898,7 +28321,7 @@ export interface operations { * "repo_variant": "fp16", * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -27969,7 +28392,7 @@ export interface operations { * "repo_variant": "fp16", * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ @@ -28473,6 +28896,166 @@ export interface operations { }; }; }; + pause_model_install_job: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Model install job ID */ + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The job was paused successfully */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ModelInstallJob"]; + }; + }; + /** @description No such job */ + 415: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + resume_model_install_job: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Model install job ID */ + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The job was resumed successfully */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ModelInstallJob"]; + }; + }; + /** @description No such job */ + 415: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + restart_failed_model_install_job: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Model install job ID */ + id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Failed files restarted successfully */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ModelInstallJob"]; + }; + }; + /** @description No such job */ + 415: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + restart_model_install_file: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Model install job ID */ + id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": string; + }; + }; + responses: { + /** @description File restarted successfully */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ModelInstallJob"]; + }; + }; + /** @description No such job */ + 415: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; convert_model: { parameters: { query?: never; @@ -28509,7 +29092,7 @@ export interface operations { * "repo_variant": "fp16", * "upcast_attention": false * } */ - "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; + "application/json": components["schemas"]["Main_Diffusers_SD1_Config"] | components["schemas"]["Main_Diffusers_SD2_Config"] | components["schemas"]["Main_Diffusers_SDXL_Config"] | components["schemas"]["Main_Diffusers_SDXLRefiner_Config"] | components["schemas"]["Main_Diffusers_SD3_Config"] | components["schemas"]["Main_Diffusers_FLUX_Config"] | components["schemas"]["Main_Diffusers_Flux2_Config"] | components["schemas"]["Main_Diffusers_CogView4_Config"] | components["schemas"]["Main_Diffusers_ZImage_Config"] | components["schemas"]["Main_Checkpoint_SD1_Config"] | components["schemas"]["Main_Checkpoint_SD2_Config"] | components["schemas"]["Main_Checkpoint_SDXL_Config"] | components["schemas"]["Main_Checkpoint_SDXLRefiner_Config"] | components["schemas"]["Main_Checkpoint_Flux2_Config"] | components["schemas"]["Main_Checkpoint_FLUX_Config"] | components["schemas"]["Main_Checkpoint_ZImage_Config"] | components["schemas"]["Main_BnBNF4_FLUX_Config"] | components["schemas"]["Main_GGUF_Flux2_Config"] | components["schemas"]["Main_GGUF_FLUX_Config"] | components["schemas"]["Main_GGUF_ZImage_Config"] | components["schemas"]["VAE_Checkpoint_SD1_Config"] | components["schemas"]["VAE_Checkpoint_SD2_Config"] | components["schemas"]["VAE_Checkpoint_SDXL_Config"] | components["schemas"]["VAE_Checkpoint_FLUX_Config"] | components["schemas"]["VAE_Checkpoint_Flux2_Config"] | components["schemas"]["VAE_Diffusers_SD1_Config"] | components["schemas"]["VAE_Diffusers_SDXL_Config"] | components["schemas"]["VAE_Diffusers_Flux2_Config"] | components["schemas"]["ControlNet_Checkpoint_SD1_Config"] | components["schemas"]["ControlNet_Checkpoint_SD2_Config"] | components["schemas"]["ControlNet_Checkpoint_SDXL_Config"] | components["schemas"]["ControlNet_Checkpoint_FLUX_Config"] | components["schemas"]["ControlNet_Checkpoint_ZImage_Config"] | components["schemas"]["ControlNet_Diffusers_SD1_Config"] | components["schemas"]["ControlNet_Diffusers_SD2_Config"] | components["schemas"]["ControlNet_Diffusers_SDXL_Config"] | components["schemas"]["ControlNet_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_SD1_Config"] | components["schemas"]["LoRA_LyCORIS_SD2_Config"] | components["schemas"]["LoRA_LyCORIS_SDXL_Config"] | components["schemas"]["LoRA_LyCORIS_Flux2_Config"] | components["schemas"]["LoRA_LyCORIS_FLUX_Config"] | components["schemas"]["LoRA_LyCORIS_ZImage_Config"] | components["schemas"]["LoRA_OMI_SDXL_Config"] | components["schemas"]["LoRA_OMI_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_SD1_Config"] | components["schemas"]["LoRA_Diffusers_SD2_Config"] | components["schemas"]["LoRA_Diffusers_SDXL_Config"] | components["schemas"]["LoRA_Diffusers_Flux2_Config"] | components["schemas"]["LoRA_Diffusers_FLUX_Config"] | components["schemas"]["LoRA_Diffusers_ZImage_Config"] | components["schemas"]["ControlLoRA_LyCORIS_FLUX_Config"] | components["schemas"]["T5Encoder_T5Encoder_Config"] | components["schemas"]["T5Encoder_BnBLLMint8_Config"] | components["schemas"]["Qwen3Encoder_Qwen3Encoder_Config"] | components["schemas"]["Qwen3Encoder_Checkpoint_Config"] | components["schemas"]["Qwen3Encoder_GGUF_Config"] | components["schemas"]["TI_File_SD1_Config"] | components["schemas"]["TI_File_SD2_Config"] | components["schemas"]["TI_File_SDXL_Config"] | components["schemas"]["TI_Folder_SD1_Config"] | components["schemas"]["TI_Folder_SD2_Config"] | components["schemas"]["TI_Folder_SDXL_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD1_Config"] | components["schemas"]["IPAdapter_InvokeAI_SD2_Config"] | components["schemas"]["IPAdapter_InvokeAI_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD1_Config"] | components["schemas"]["IPAdapter_Checkpoint_SD2_Config"] | components["schemas"]["IPAdapter_Checkpoint_SDXL_Config"] | components["schemas"]["IPAdapter_Checkpoint_FLUX_Config"] | components["schemas"]["T2IAdapter_Diffusers_SD1_Config"] | components["schemas"]["T2IAdapter_Diffusers_SDXL_Config"] | components["schemas"]["Spandrel_Checkpoint_Config"] | components["schemas"]["CLIPEmbed_Diffusers_G_Config"] | components["schemas"]["CLIPEmbed_Diffusers_L_Config"] | components["schemas"]["CLIPVision_Diffusers_Config"] | components["schemas"]["SigLIP_Diffusers_Config"] | components["schemas"]["FLUXRedux_Checkpoint_Config"] | components["schemas"]["LlavaOnevision_Diffusers_Config"] | components["schemas"]["Unknown_Config"]; }; }; /** @description Bad request */ diff --git a/tests/backend/image_util/__init__.py b/tests/backend/image_util/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/backend/image_util/test_vendor_mutable_defaults.py b/tests/backend/image_util/test_vendor_mutable_defaults.py new file mode 100644 index 00000000000..8cd74cbd6c3 --- /dev/null +++ b/tests/backend/image_util/test_vendor_mutable_defaults.py @@ -0,0 +1,106 @@ +"""Tests for the mutable default argument fix in imwatermark/vendor.py +and the bare except fix in sqlite_database.py.""" + +from logging import Logger +from unittest import mock + +import pytest + +from invokeai.backend.image_util.imwatermark.vendor import EmbedMaxDct, WatermarkEncoder + + +class TestSetByBitsNoSharedState: + """set_by_bits() used to have bits=[] as a default arg. + If it were still mutable, successive calls without an explicit arg + would accumulate state. After the fix (bits=None), each call gets + a fresh list.""" + + def test_set_by_bits_default_is_independent(self): + enc1 = WatermarkEncoder() + enc1.set_by_bits() + assert enc1._watermarks == [] + assert enc1._wmLen == 0 + + enc2 = WatermarkEncoder() + enc2.set_by_bits() + assert enc2._watermarks == [] + assert enc2._wmLen == 0 + + def test_set_by_bits_with_explicit_arg(self): + enc = WatermarkEncoder() + enc.set_by_bits([1, 0, 1]) + assert enc._watermarks == [1, 0, 1] + assert enc._wmLen == 3 + assert enc._wmType == "bits" + + +class TestEmbedMaxDctNoSharedState: + """EmbedMaxDct.__init__ used to have watermarks=[] and scales=[0,36,36]. + After the fix (both default to None), each instance gets its own list.""" + + def test_default_watermarks_independent(self): + e1 = EmbedMaxDct() + e1._watermarks.append(999) + + e2 = EmbedMaxDct() + assert 999 not in e2._watermarks + assert e2._watermarks == [] + + def test_default_scales_independent(self): + e1 = EmbedMaxDct() + e1._scales.append(72) + + e2 = EmbedMaxDct() + assert e2._scales == [0, 36, 36] + + def test_explicit_args_still_work(self): + wm = [1, 0, 1, 1] + sc = [0, 50, 50] + e = EmbedMaxDct(watermarks=wm, wmLen=4, scales=sc, block=8) + assert e._watermarks == wm + assert e._wmLen == 4 + assert e._scales == sc + assert e._block == 8 + + +class TestTransactionExceptException: + """The transaction() context manager used to have a bare `except:`. + After the fix it uses `except Exception:`, so BaseException subclasses + like KeyboardInterrupt and SystemExit should propagate instead of + being silently caught and rolled back.""" + + @staticmethod + def _make_db(): + """Create a minimal SqliteDatabase-like object with transaction().""" + # Import here so the test stays focused; we just need the real class. + from invokeai.app.services.shared.sqlite.sqlite_database import SqliteDatabase + + logger = mock.MagicMock(spec=Logger) + db = SqliteDatabase(db_path=None, logger=logger, verbose=False) + return db + + def test_regular_exception_rolls_back(self): + db = self._make_db() + + # create a table first in a successful transaction + with db.transaction() as cursor: + cursor.execute("CREATE TABLE t (id INTEGER)") + + # now try to insert and fail — the insert should be rolled back + with pytest.raises(ValueError): + with db.transaction() as cursor: + cursor.execute("INSERT INTO t VALUES (42)") + raise ValueError("boom") + + # the row should not exist after rollback + with db.transaction() as cursor: + cursor.execute("SELECT * FROM t") + assert cursor.fetchone() is None + + def test_keyboard_interrupt_propagates(self): + with pytest.raises(KeyboardInterrupt): + raise KeyboardInterrupt() + + def test_system_exit_propagates(self): + with pytest.raises(SystemExit): + raise SystemExit(1)