mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-17 17:37:55 -05:00
Compare commits
26 Commits
v5.13.0rc2
...
saas-targe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
649596cec5 | ||
|
|
45aa84c01a | ||
|
|
064d5787c9 | ||
|
|
d81b23adff | ||
|
|
c72480fd1b | ||
|
|
3704573ef8 | ||
|
|
01fbf2ce4d | ||
|
|
96e7003449 | ||
|
|
80197b8856 | ||
|
|
0187bc671e | ||
|
|
31584daabe | ||
|
|
a6cb522fed | ||
|
|
f70be1e415 | ||
|
|
a2901f2b46 | ||
|
|
b61c66c3a9 | ||
|
|
c77f9ec202 | ||
|
|
2c5c35647f | ||
|
|
bf0fdbd10e | ||
|
|
731d317a42 | ||
|
|
e81579f752 | ||
|
|
9a10e98c0b | ||
|
|
27fdc139b7 | ||
|
|
0a00805afc | ||
|
|
7b38143fbd | ||
|
|
4c5ad1b7d7 | ||
|
|
d80cc962ad |
@@ -1,8 +1,7 @@
|
||||
import typing
|
||||
from enum import Enum
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
from importlib.metadata import distributions
|
||||
from pathlib import Path
|
||||
from platform import python_version
|
||||
from typing import Optional
|
||||
|
||||
import torch
|
||||
@@ -44,24 +43,6 @@ class AppVersion(BaseModel):
|
||||
highlights: Optional[list[str]] = Field(default=None, description="Highlights of release")
|
||||
|
||||
|
||||
class AppDependencyVersions(BaseModel):
|
||||
"""App depencency Versions Response"""
|
||||
|
||||
accelerate: str = Field(description="accelerate version")
|
||||
compel: str = Field(description="compel version")
|
||||
cuda: Optional[str] = Field(description="CUDA version")
|
||||
diffusers: str = Field(description="diffusers version")
|
||||
numpy: str = Field(description="Numpy version")
|
||||
opencv: str = Field(description="OpenCV version")
|
||||
onnx: str = Field(description="ONNX version")
|
||||
pillow: str = Field(description="Pillow (PIL) version")
|
||||
python: str = Field(description="Python version")
|
||||
torch: str = Field(description="PyTorch version")
|
||||
torchvision: str = Field(description="PyTorch Vision version")
|
||||
transformers: str = Field(description="transformers version")
|
||||
xformers: Optional[str] = Field(description="xformers version")
|
||||
|
||||
|
||||
class AppConfig(BaseModel):
|
||||
"""App Config Response"""
|
||||
|
||||
@@ -76,27 +57,19 @@ async def get_version() -> AppVersion:
|
||||
return AppVersion(version=__version__)
|
||||
|
||||
|
||||
@app_router.get("/app_deps", operation_id="get_app_deps", status_code=200, response_model=AppDependencyVersions)
|
||||
async def get_app_deps() -> AppDependencyVersions:
|
||||
@app_router.get("/app_deps", operation_id="get_app_deps", status_code=200, response_model=dict[str, str])
|
||||
async def get_app_deps() -> dict[str, str]:
|
||||
deps: dict[str, str] = {dist.metadata["Name"]: dist.version for dist in distributions()}
|
||||
try:
|
||||
xformers = version("xformers")
|
||||
except PackageNotFoundError:
|
||||
xformers = None
|
||||
return AppDependencyVersions(
|
||||
accelerate=version("accelerate"),
|
||||
compel=version("compel"),
|
||||
cuda=torch.version.cuda,
|
||||
diffusers=version("diffusers"),
|
||||
numpy=version("numpy"),
|
||||
opencv=version("opencv-python"),
|
||||
onnx=version("onnx"),
|
||||
pillow=version("pillow"),
|
||||
python=python_version(),
|
||||
torch=torch.version.__version__,
|
||||
torchvision=version("torchvision"),
|
||||
transformers=version("transformers"),
|
||||
xformers=xformers,
|
||||
)
|
||||
cuda = torch.version.cuda or "N/A"
|
||||
except Exception:
|
||||
cuda = "N/A"
|
||||
|
||||
deps["CUDA"] = cuda
|
||||
|
||||
sorted_deps = dict(sorted(deps.items(), key=lambda item: item[0].lower()))
|
||||
|
||||
return sorted_deps
|
||||
|
||||
|
||||
@app_router.get("/config", operation_id="get_config", status_code=200, response_model=AppConfig)
|
||||
|
||||
@@ -582,6 +582,8 @@ def invocation(
|
||||
|
||||
fields: dict[str, tuple[Any, FieldInfo]] = {}
|
||||
|
||||
original_model_fields: dict[str, OriginalModelField] = {}
|
||||
|
||||
for field_name, field_info in cls.model_fields.items():
|
||||
annotation = field_info.annotation
|
||||
assert annotation is not None, f"{field_name} on invocation {invocation_type} has no type annotation."
|
||||
@@ -589,7 +591,7 @@ def invocation(
|
||||
f"{field_name} on invocation {invocation_type} has a non-dict json_schema_extra, did you forget to use InputField?"
|
||||
)
|
||||
|
||||
cls._original_model_fields[field_name] = OriginalModelField(annotation=annotation, field_info=field_info)
|
||||
original_model_fields[field_name] = OriginalModelField(annotation=annotation, field_info=field_info)
|
||||
|
||||
validate_field_default(cls.__name__, field_name, invocation_type, annotation, field_info)
|
||||
|
||||
@@ -676,6 +678,7 @@ def invocation(
|
||||
docstring = cls.__doc__
|
||||
new_class = create_model(cls.__qualname__, __base__=cls, __module__=cls.__module__, **fields) # type: ignore
|
||||
new_class.__doc__ = docstring
|
||||
new_class._original_model_fields = original_model_fields
|
||||
|
||||
InvocationRegistry.register_invocation(new_class)
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ class CreateGradientMaskInvocation(BaseInvocation):
|
||||
main_model_config = context.models.get_config(self.unet.unet.key)
|
||||
assert isinstance(main_model_config, MainConfigBase)
|
||||
if main_model_config.variant is ModelVariantType.Inpaint:
|
||||
mask = mask_tensor
|
||||
mask = dilated_mask_tensor
|
||||
vae_info: LoadedModel = context.models.load(self.vae.vae)
|
||||
image = context.images.get_pil(self.image.image_name)
|
||||
image_tensor = image_resized_to_grid_as_tensor(image.convert("RGB"))
|
||||
|
||||
@@ -64,6 +64,7 @@ class UIType(str, Enum, metaclass=MetaEnum):
|
||||
Imagen3Model = "Imagen3ModelField"
|
||||
Imagen4Model = "Imagen4ModelField"
|
||||
ChatGPT4oModel = "ChatGPT4oModelField"
|
||||
FluxKontextModel = "FluxKontextModelField"
|
||||
# endregion
|
||||
|
||||
# region Misc Field Types
|
||||
|
||||
@@ -17,8 +17,6 @@ def run_app() -> None:
|
||||
|
||||
import uvicorn
|
||||
|
||||
from invokeai.app.invocations.baseinvocation import InvocationRegistry
|
||||
from invokeai.app.invocations.load_custom_nodes import load_custom_nodes
|
||||
from invokeai.app.services.config.config_default import get_config
|
||||
from invokeai.app.util.torch_cuda_allocator import configure_torch_cuda_allocator
|
||||
from invokeai.backend.util.logging import InvokeAILogger
|
||||
@@ -34,6 +32,8 @@ def run_app() -> None:
|
||||
configure_torch_cuda_allocator(app_config.pytorch_cuda_alloc_conf, logger)
|
||||
|
||||
# This import must happen after configure_torch_cuda_allocator() is called, because the module imports torch.
|
||||
from invokeai.app.invocations.baseinvocation import InvocationRegistry
|
||||
from invokeai.app.invocations.load_custom_nodes import load_custom_nodes
|
||||
from invokeai.backend.util.devices import TorchDevice
|
||||
|
||||
torch_device_name = TorchDevice.get_torch_device_name()
|
||||
|
||||
@@ -205,6 +205,7 @@ class FieldIdentifier(BaseModel):
|
||||
kind: Literal["input", "output"] = Field(description="The kind of field")
|
||||
node_id: str = Field(description="The ID of the node")
|
||||
field_name: str = Field(description="The name of the field")
|
||||
user_label: str | None = Field(description="The user label of the field, if any")
|
||||
|
||||
|
||||
class SessionQueueItemWithoutGraph(BaseModel):
|
||||
|
||||
@@ -62,11 +62,14 @@ class HuggingFaceMetadataFetch(ModelMetadataFetchBase):
|
||||
# If this too fails, raise exception.
|
||||
|
||||
model_info = None
|
||||
|
||||
# Handling for our special syntax - we only want the base HF `org/repo` here.
|
||||
repo_id = id.split("::")[0] or id
|
||||
while not model_info:
|
||||
try:
|
||||
model_info = HfApi().model_info(repo_id=id, files_metadata=True, revision=variant)
|
||||
model_info = HfApi().model_info(repo_id=repo_id, files_metadata=True, revision=variant)
|
||||
except RepositoryNotFoundError as excp:
|
||||
raise UnknownMetadataException(f"'{id}' not found. See trace for details.") from excp
|
||||
raise UnknownMetadataException(f"'{repo_id}' not found. See trace for details.") from excp
|
||||
except RevisionNotFoundError:
|
||||
if variant is None:
|
||||
raise
|
||||
@@ -75,14 +78,14 @@ class HuggingFaceMetadataFetch(ModelMetadataFetchBase):
|
||||
|
||||
files: list[RemoteModelFile] = []
|
||||
|
||||
_, name = id.split("/")
|
||||
_, name = repo_id.split("/")
|
||||
|
||||
for s in model_info.siblings or []:
|
||||
assert s.rfilename is not None
|
||||
assert s.size is not None
|
||||
files.append(
|
||||
RemoteModelFile(
|
||||
url=hf_hub_url(id, s.rfilename, revision=variant or "main"),
|
||||
url=hf_hub_url(repo_id, s.rfilename, revision=variant or "main"),
|
||||
path=Path(name, s.rfilename),
|
||||
size=s.size,
|
||||
sha256=s.lfs.get("sha256") if s.lfs else None,
|
||||
|
||||
@@ -29,6 +29,7 @@ class BaseModelType(str, Enum):
|
||||
Imagen3 = "imagen3"
|
||||
Imagen4 = "imagen4"
|
||||
ChatGPT4o = "chatgpt-4o"
|
||||
FluxKontext = "flux-kontext"
|
||||
|
||||
|
||||
class ModelType(str, Enum):
|
||||
|
||||
@@ -1147,6 +1147,7 @@
|
||||
"modelIncompatibleScaledBboxWidth": "Scaled bbox width is {{width}} but {{model}} requires multiple of {{multiple}}",
|
||||
"modelIncompatibleScaledBboxHeight": "Scaled bbox height is {{height}} but {{model}} requires multiple of {{multiple}}",
|
||||
"fluxModelMultipleControlLoRAs": "Can only use 1 Control LoRA at a time",
|
||||
"fluxKontextMultipleReferenceImages": "Can only use 1 Reference Image at a time with Flux Kontext",
|
||||
"canvasIsFiltering": "Canvas is busy (filtering)",
|
||||
"canvasIsTransforming": "Canvas is busy (transforming)",
|
||||
"canvasIsRasterizing": "Canvas is busy (rasterizing)",
|
||||
@@ -1337,6 +1338,7 @@
|
||||
"fluxFillIncompatibleWithT2IAndI2I": "FLUX Fill is not compatible with Text to Image or Image to Image. Use other FLUX models for these tasks.",
|
||||
"imagenIncompatibleGenerationMode": "Google {{model}} supports Text to Image only. Use other models for Image to Image, Inpainting and Outpainting tasks.",
|
||||
"chatGPT4oIncompatibleGenerationMode": "ChatGPT 4o supports Text to Image and Image to Image only. Use other models Inpainting and Outpainting tasks.",
|
||||
"fluxKontextIncompatibleGenerationMode": "Flux Kontext supports Text to Image only. Use other models for Image to Image, Inpainting and Outpainting tasks.",
|
||||
"problemUnpublishingWorkflow": "Problem Unpublishing Workflow",
|
||||
"problemUnpublishingWorkflowDescription": "There was a problem unpublishing the workflow. Please try again.",
|
||||
"workflowUnpublished": "Workflow Unpublished"
|
||||
@@ -2426,9 +2428,8 @@
|
||||
"whatsNew": {
|
||||
"whatsNewInInvoke": "What's New in Invoke",
|
||||
"items": [
|
||||
"Nvidia 50xx GPUs: Invoke uses PyTorch 2.7.0, which is required for these GPUs.",
|
||||
"Model Relationships: Link LoRAs to main models, and the LoRAs will show up first in the list.",
|
||||
"IP Adapter: New Style (Strong) and Style (Precise) methods for SDXL and SD1.5 models."
|
||||
"Inpainting: Per-mask noise levels and denoise limits.",
|
||||
"Canvas: Smarter aspect ratios for SDXL and improved scroll-to-zoom."
|
||||
],
|
||||
"readReleaseNotes": "Read Release Notes",
|
||||
"watchRecentReleaseVideos": "Watch Recent Release Videos",
|
||||
|
||||
@@ -1086,11 +1086,11 @@
|
||||
"menuItemAutoAdd": "Aggiungi automaticamente a questa bacheca",
|
||||
"cancel": "Annulla",
|
||||
"addBoard": "Aggiungi Bacheca",
|
||||
"bottomMessage": "L'eliminazione di questa bacheca e delle sue immagini ripristinerà tutte le funzionalità che le stanno attualmente utilizzando.",
|
||||
"bottomMessage": "L'eliminazione delle immagini reimposterà tutte le funzionalità che le stanno utilizzando.",
|
||||
"changeBoard": "Cambia Bacheca",
|
||||
"loading": "Caricamento in corso ...",
|
||||
"clearSearch": "Cancella Ricerca",
|
||||
"topMessage": "Questa bacheca contiene immagini utilizzate nelle seguenti funzionalità:",
|
||||
"topMessage": "Questa selezione contiene immagini utilizzate nelle seguenti funzionalità:",
|
||||
"move": "Sposta",
|
||||
"myBoard": "Bacheca",
|
||||
"searchBoard": "Cerca bacheche ...",
|
||||
@@ -1101,7 +1101,7 @@
|
||||
"deleteBoardOnly": "solo la Bacheca",
|
||||
"deleteBoard": "Elimina Bacheca",
|
||||
"deleteBoardAndImages": "Bacheca e Immagini",
|
||||
"deletedBoardsCannotbeRestored": "Le bacheche eliminate non possono essere ripristinate. Selezionando \"Elimina solo bacheca\" le immagini verranno spostate nella bacheca \"Non categorizzato\".",
|
||||
"deletedBoardsCannotbeRestored": "Le bacheche e le immagini eliminate non possono essere ripristinate. Selezionando \"Elimina solo bacheca\" le immagini verranno spostate in uno stato non categorizzato.",
|
||||
"movingImagesToBoard_one": "Spostare {{count}} immagine nella bacheca:",
|
||||
"movingImagesToBoard_many": "Spostare {{count}} immagini nella bacheca:",
|
||||
"movingImagesToBoard_other": "Spostare {{count}} immagini nella bacheca:",
|
||||
@@ -1123,8 +1123,11 @@
|
||||
"noBoards": "Nessuna bacheca {{boardType}}",
|
||||
"hideBoards": "Nascondi bacheche",
|
||||
"viewBoards": "Visualizza bacheche",
|
||||
"deletedPrivateBoardsCannotbeRestored": "Le bacheche cancellate non possono essere ripristinate. Selezionando 'Cancella solo bacheca', le immagini verranno spostate nella bacheca \"Non categorizzato\" privata dell'autore dell'immagine.",
|
||||
"updateBoardError": "Errore durante l'aggiornamento della bacheca"
|
||||
"deletedPrivateBoardsCannotbeRestored": "Le bacheche e le immagini eliminate non possono essere ripristinate. Selezionando \"Elimina solo bacheca\", le immagini verranno spostate in uno stato privato e non categorizzato per l'autore dell'immagine.",
|
||||
"updateBoardError": "Errore durante l'aggiornamento della bacheca",
|
||||
"uncategorizedImages": "Immagini non categorizzate",
|
||||
"deleteAllUncategorizedImages": "Elimina tutte le immagini non categorizzate",
|
||||
"deletedImagesCannotBeRestored": "Le immagini eliminate non possono essere ripristinate."
|
||||
},
|
||||
"queue": {
|
||||
"queueFront": "Aggiungi all'inizio della coda",
|
||||
@@ -2449,9 +2452,8 @@
|
||||
"watchRecentReleaseVideos": "Guarda i video su questa versione",
|
||||
"watchUiUpdatesOverview": "Guarda le novità dell'interfaccia",
|
||||
"items": [
|
||||
"GPU Nvidia 50xx: Invoke utilizza PyTorch 2.7.0, necessario per queste GPU.",
|
||||
"Relazioni tra modelli: collega i LoRA ai modelli principali e i LoRA verranno visualizzati per primi nell'elenco.",
|
||||
"Adattatore IP: nuovi metodi Style (Strong) e Style (Precise) per i modelli SDXL e SD1.5."
|
||||
"Inpainting: livelli di rumore per maschera e limiti di denoise.",
|
||||
"Canvas: proporzioni più intelligenti per SDXL e scorrimento e zoom migliorati."
|
||||
]
|
||||
},
|
||||
"system": {
|
||||
|
||||
@@ -392,7 +392,7 @@
|
||||
"title": "全選択"
|
||||
},
|
||||
"addNode": {
|
||||
"desc": "ノード追加メニューを開く.",
|
||||
"desc": "ノード追加メニューを開く。",
|
||||
"title": "ノードを追加"
|
||||
},
|
||||
"pasteSelectionWithEdges": {
|
||||
@@ -1156,11 +1156,11 @@
|
||||
"unknownField": "不明なフィールド",
|
||||
"unexpectedField_withName": "予期しないフィールド\"{{name}}\"",
|
||||
"loadingTemplates": "読み込み中 {{name}}",
|
||||
"validateConnectionsHelp": "無効な接続が行われたり,無効なグラフが呼び出されたりしないようにします.",
|
||||
"validateConnectionsHelp": "無効な接続が行われたり,無効なグラフが呼び出されたりしないようにします",
|
||||
"validateConnections": "接続とグラフを確認する",
|
||||
"saveToGallery": "ギャラリーに保存",
|
||||
"newWorkflowDesc": "新しいワークフローを作りますか?",
|
||||
"unknownFieldType": "$t(nodes.unknownField)型:{type}}",
|
||||
"unknownFieldType": "$t(nodes.unknownField)型: {{type}}",
|
||||
"unsupportedArrayItemType": "サポートされていない配列項目型です \"{{type}}\"",
|
||||
"unableToLoadWorkflow": "ワークフローが読み込めません",
|
||||
"unableToValidateWorkflow": "ワークフローを確認できません",
|
||||
@@ -1203,13 +1203,13 @@
|
||||
"downloadBoard": "ボードをダウンロード",
|
||||
"changeBoard": "ボードを変更",
|
||||
"loading": "ロード中...",
|
||||
"topMessage": "このボードには、以下の機能で使用されている画像が含まれています:",
|
||||
"bottomMessage": "このボードおよび画像を削除すると、現在これらを利用している機能はリセットされます。",
|
||||
"topMessage": "この選択には、次の機能で使用される画像が含まれています:",
|
||||
"bottomMessage": "この画像を削除すると、現在利用している機能はリセットされます。",
|
||||
"clearSearch": "検索をクリア",
|
||||
"deleteBoard": "ボードの削除",
|
||||
"deleteBoardAndImages": "ボードと画像の削除",
|
||||
"deleteBoardOnly": "ボードのみ削除",
|
||||
"deletedBoardsCannotbeRestored": "削除されたボードは復元できません。\"ボードのみ削除\"を選択すると画像は未分類に移動されます。",
|
||||
"deletedBoardsCannotbeRestored": "削除したボードと画像は復元できません。「ボードのみ削除」を選択すると、画像は未分類の状態になります。",
|
||||
"movingImagesToBoard_other": "{{count}} の画像をボードに移動:",
|
||||
"hideBoards": "ボードを隠す",
|
||||
"assetsWithCount_other": "{{count}} のアセット",
|
||||
@@ -1224,9 +1224,12 @@
|
||||
"imagesWithCount_other": "{{count}} の画像",
|
||||
"updateBoardError": "ボード更新エラー",
|
||||
"selectedForAutoAdd": "自動追加に選択済み",
|
||||
"deletedPrivateBoardsCannotbeRestored": "削除されたボードは復元できません。\"ボードのみ削除\"を選択すると画像はその作成者のプライベートな未分類に移動されます。",
|
||||
"deletedPrivateBoardsCannotbeRestored": "削除されたボードと画像は復元できません。「ボードのみ削除」を選択すると、画像は作成者に対して非公開の未分類状態になります。",
|
||||
"noBoards": "{{boardType}} ボードがありません",
|
||||
"viewBoards": "ボードを表示"
|
||||
"viewBoards": "ボードを表示",
|
||||
"uncategorizedImages": "分類されていない画像",
|
||||
"deleteAllUncategorizedImages": "分類されていないすべての画像を削除",
|
||||
"deletedImagesCannotBeRestored": "削除した画像は復元できません."
|
||||
},
|
||||
"invocationCache": {
|
||||
"invocationCache": "呼び出しキャッシュ",
|
||||
@@ -1292,25 +1295,49 @@
|
||||
]
|
||||
},
|
||||
"paramUpscaleMethod": {
|
||||
"heading": "アップスケール手法"
|
||||
"heading": "アップスケール手法",
|
||||
"paragraphs": [
|
||||
"高解像度修正のために画像を拡大するために使用される方法。"
|
||||
]
|
||||
},
|
||||
"upscaleModel": {
|
||||
"heading": "アップスケールモデル"
|
||||
"heading": "アップスケールモデル",
|
||||
"paragraphs": [
|
||||
"アップスケールモデルは、ディテールを追加する前に画像を出力サイズに合わせて拡大縮小します。サポートされているアップスケールモデルであればどれでも使用できますが、写真や線画など、特定の種類の画像に特化したモデルもあります。"
|
||||
]
|
||||
},
|
||||
"paramAspect": {
|
||||
"heading": "縦横比"
|
||||
"heading": "縦横比",
|
||||
"paragraphs": [
|
||||
"生成される画像のアスペクト比。比率を変更すると、幅と高さもそれに応じて更新されます。",
|
||||
"「最適化」は、選択したモデルの幅と高さを最適な寸法に設定します。"
|
||||
]
|
||||
},
|
||||
"refinerSteps": {
|
||||
"heading": "ステップ"
|
||||
"heading": "ステップ",
|
||||
"paragraphs": [
|
||||
"生成プロセスのリファイナー部分で実行されるステップの数。",
|
||||
"生成ステップと似ています。"
|
||||
]
|
||||
},
|
||||
"paramVAE": {
|
||||
"heading": "VAE"
|
||||
"heading": "VAE",
|
||||
"paragraphs": [
|
||||
"AI 出力を最終画像に変換するために使用されるモデル。"
|
||||
]
|
||||
},
|
||||
"scale": {
|
||||
"heading": "スケール"
|
||||
"heading": "スケール",
|
||||
"paragraphs": [
|
||||
"スケールは出力画像のサイズを制御し、入力画像の解像度の倍数に基づいて決定されます。例えば、1024x1024の画像を2倍に拡大すると、2048x2048の出力が生成されます。"
|
||||
]
|
||||
},
|
||||
"refinerScheduler": {
|
||||
"heading": "スケジューラー"
|
||||
"heading": "スケジューラー",
|
||||
"paragraphs": [
|
||||
"生成プロセスのリファイナー部分で使用されるスケジューラ。",
|
||||
"生成スケジューラに似ています。"
|
||||
]
|
||||
},
|
||||
"compositingCoherenceMode": {
|
||||
"heading": "モード",
|
||||
@@ -1319,10 +1346,16 @@
|
||||
]
|
||||
},
|
||||
"paramModel": {
|
||||
"heading": "モデル"
|
||||
"heading": "モデル",
|
||||
"paragraphs": [
|
||||
"生成に使用されるモデル。異なるモデルは、異なる美的結果とコンテンツを生成するように特化するようにトレーニングされています。"
|
||||
]
|
||||
},
|
||||
"paramHeight": {
|
||||
"heading": "高さ"
|
||||
"heading": "高さ",
|
||||
"paragraphs": [
|
||||
"生成される画像の高さ。8の倍数にする必要があります。"
|
||||
]
|
||||
},
|
||||
"paramSteps": {
|
||||
"heading": "ステップ",
|
||||
@@ -1345,7 +1378,11 @@
|
||||
]
|
||||
},
|
||||
"paramIterations": {
|
||||
"heading": "生成回数"
|
||||
"heading": "生成回数",
|
||||
"paragraphs": [
|
||||
"生成する画像の数。",
|
||||
"動的プロンプトが有効になっている場合、各プロンプトはこの回数生成されます。"
|
||||
]
|
||||
},
|
||||
"controlNet": {
|
||||
"heading": "ControlNet",
|
||||
@@ -1354,7 +1391,10 @@
|
||||
]
|
||||
},
|
||||
"paramWidth": {
|
||||
"heading": "幅"
|
||||
"heading": "幅",
|
||||
"paragraphs": [
|
||||
"生成される画像の幅。8の倍数にする必要があります。"
|
||||
]
|
||||
},
|
||||
"lora": {
|
||||
"heading": "LoRA",
|
||||
@@ -1369,7 +1409,11 @@
|
||||
]
|
||||
},
|
||||
"patchmatchDownScaleSize": {
|
||||
"heading": "Downscale"
|
||||
"heading": "Downscale",
|
||||
"paragraphs": [
|
||||
"埋め込む前にどの程度のダウンスケーリングが行われるか。",
|
||||
"ダウンスケーリングを大きくするとパフォーマンスは向上しますが、品質は低下します。"
|
||||
]
|
||||
},
|
||||
"controlNetWeight": {
|
||||
"heading": "重み",
|
||||
@@ -1511,6 +1555,124 @@
|
||||
"paragraphs": [
|
||||
"アウトペインティングまたはインペインティングのプロセス中に埋め込む方法."
|
||||
]
|
||||
},
|
||||
"paramGuidance": {
|
||||
"paragraphs": [
|
||||
"プロンプトが生成プロセスにどの程度影響するかを制御します。",
|
||||
"ガイダンス値が高すぎると過飽和状態になる可能性があり、ガイダンス値が高すぎるか低すぎると生成結果に歪みが生じる可能性があります。ガイダンスはFLUX DEVモデルにのみ適用されます。"
|
||||
],
|
||||
"heading": "ガイダンス"
|
||||
},
|
||||
"paramDenoisingStrength": {
|
||||
"paragraphs": [
|
||||
"生成されたイメージがラスター レイヤーとどの程度異なるかを制御します。",
|
||||
"強度が低いほど、結合された表示ラスターレイヤーに近くなります。強度が高いほど、グローバルプロンプトに大きく依存します。",
|
||||
"表示されるコンテンツを持つラスター レイヤーがない場合、この設定は無視されます。"
|
||||
],
|
||||
"heading": "ディノイジングストレングス"
|
||||
},
|
||||
"refinerStart": {
|
||||
"heading": "リファイナースタート",
|
||||
"paragraphs": [
|
||||
"生成プロセスのどの時点でリファイナーが使用され始めるか。",
|
||||
"0 はリファイナーが生成プロセス全体で使用されることを意味し、0.8 は、リファイナーが生成プロセスの最後の 20% で使用されることを意味します。"
|
||||
]
|
||||
},
|
||||
"optimizedDenoising": {
|
||||
"heading": "イメージtoイメージの最適化",
|
||||
"paragraphs": [
|
||||
"「イメージtoイメージを最適化」を有効にすると、Fluxモデルを用いた画像間変換およびインペインティング変換において、より段階的なノイズ除去強度スケールが適用されます。この設定により、画像に適用される変化量を制御する能力が向上しますが、標準のノイズ除去強度スケールを使用したい場合はオフにすることができます。この設定は現在調整中で、ベータ版です。"
|
||||
]
|
||||
},
|
||||
"refinerPositiveAestheticScore": {
|
||||
"heading": "ポジティブ美的スコア",
|
||||
"paragraphs": [
|
||||
"トレーニング データに基づいて、美的スコアの高い画像に類似するように生成を重み付けします。"
|
||||
]
|
||||
},
|
||||
"paramCFGScale": {
|
||||
"paragraphs": [
|
||||
"プロンプトが生成プロセスにどの程度影響するかを制御します。",
|
||||
"CFG スケールの値が高すぎると、飽和しすぎて生成結果が歪む可能性があります。 "
|
||||
],
|
||||
"heading": "CFGスケール"
|
||||
},
|
||||
"paramVAEPrecision": {
|
||||
"paragraphs": [
|
||||
"VAE エンコードおよびデコード時に使用される精度。",
|
||||
"Fp16/Half 精度は、画像のわずかな変化を犠牲にして、より効率的です。"
|
||||
],
|
||||
"heading": "VAE精度"
|
||||
},
|
||||
"refinerModel": {
|
||||
"heading": "リファイナーモデル",
|
||||
"paragraphs": [
|
||||
"生成プロセスの精製部分で使用されるモデル。",
|
||||
"世代モデルに似ています。"
|
||||
]
|
||||
},
|
||||
"refinerCfgScale": {
|
||||
"heading": "CFGスケール",
|
||||
"paragraphs": [
|
||||
"プロンプトが生成プロセスに与える影響を制御する。",
|
||||
"生成CFG スケールに似ています。"
|
||||
]
|
||||
},
|
||||
"seamlessTilingYAxis": {
|
||||
"heading": "シームレスタイリングY軸",
|
||||
"paragraphs": [
|
||||
"画像を垂直軸に沿ってシームレスに並べます。"
|
||||
]
|
||||
},
|
||||
"scaleBeforeProcessing": {
|
||||
"heading": "プロセス前のスケール値",
|
||||
"paragraphs": [
|
||||
"「自動」は、画像生成プロセスの前に、選択した領域をモデルに最適なサイズに拡大縮小します。",
|
||||
"「手動」では、画像生成プロセスの前に、選択した領域を拡大縮小する幅と高さを選択できます。"
|
||||
]
|
||||
},
|
||||
"creativity": {
|
||||
"heading": "クリエイティビティ",
|
||||
"paragraphs": [
|
||||
"クリエイティビティは、ディテールを追加する際のモデルに与えられる自由度を制御します。クリエイティビティが低いと元のイメージに近いままになり、クリエイティビティが高いとより多くの変化を加えることができます。プロンプトを使用する場合、クリエイティビティが高いとプロンプトの影響が増します。"
|
||||
]
|
||||
},
|
||||
"paramHrf": {
|
||||
"heading": "高解像度修正を有効にする",
|
||||
"paragraphs": [
|
||||
"モデルに最適な解像度よりも高い解像度で、高品質な画像を生成します。通常、生成された画像内の重複を防ぐために使用されます。"
|
||||
]
|
||||
},
|
||||
"seamlessTilingXAxis": {
|
||||
"heading": "シームレスタイリングX軸",
|
||||
"paragraphs": [
|
||||
"画像を水平軸に沿ってシームレスに並べます。"
|
||||
]
|
||||
},
|
||||
"paramCFGRescaleMultiplier": {
|
||||
"paragraphs": [
|
||||
"ゼロ端末 SNR (ztsnr) を使用してトレーニングされたモデルに使用される、CFG ガイダンスのリスケールマルチプライヤー。",
|
||||
"これらのモデルの場合、推奨値は 0.7 です。"
|
||||
],
|
||||
"heading": "CFG リスケールマルチプライヤー"
|
||||
},
|
||||
"structure": {
|
||||
"heading": "ストラクチャ",
|
||||
"paragraphs": [
|
||||
"ストラクチャは、出力画像が元のレイアウトにどれだけ忠実に従うかを制御します。低いストラクチャでは大幅な変更が可能ですが、高いストラクチャでは元の構成とレイアウトが厳密に維持されます。"
|
||||
]
|
||||
},
|
||||
"refinerNegativeAestheticScore": {
|
||||
"paragraphs": [
|
||||
"トレーニング データに基づいて、美観スコアが低い画像に類似するように生成に重み付けします。"
|
||||
],
|
||||
"heading": "ネガティブ美的スコア"
|
||||
},
|
||||
"fluxDevLicense": {
|
||||
"heading": "非商用ライセンス",
|
||||
"paragraphs": [
|
||||
"FLUX.1 [dev]モデルは、FLUX [dev]非商用ライセンスに基づいてライセンスされています。Invokeでこのモデルタイプを商用目的で使用する場合は、当社のウェブサイトをご覧ください。"
|
||||
]
|
||||
}
|
||||
},
|
||||
"accordions": {
|
||||
@@ -1683,7 +1845,106 @@
|
||||
"workflows": "ワークフロー",
|
||||
"ascending": "昇順",
|
||||
"name": "名前",
|
||||
"descending": "降順"
|
||||
"descending": "降順",
|
||||
"searchPlaceholder": "名前、説明、タグで検索",
|
||||
"projectWorkflows": "プロジェクトワークフロー",
|
||||
"searchWorkflows": "ワークフローを検索",
|
||||
"updated": "アップデート",
|
||||
"published": "公表",
|
||||
"builder": {
|
||||
"label": "ラベル",
|
||||
"containerPlaceholder": "空のコンテナ",
|
||||
"showDescription": "説明を表示",
|
||||
"emptyRootPlaceholderEditMode": "開始するには、フォーム要素またはノード フィールドをここにドラッグします。",
|
||||
"divider": "仕切り",
|
||||
"deleteAllElements": "すべてのフォーム要素を削除",
|
||||
"heading": "見出し",
|
||||
"nodeField": "ノードフィールド",
|
||||
"zoomToNode": "ノードにズーム",
|
||||
"dropdown": "ドロップダウン",
|
||||
"resetOptions": "オプションをリセット",
|
||||
"both": "両方",
|
||||
"builder": "フォームビルダー",
|
||||
"text": "テキスト",
|
||||
"row": "行",
|
||||
"multiLine": "マルチライン",
|
||||
"resetAllNodeFields": "すべてのノードフィールドをリセット",
|
||||
"slider": "スライダー",
|
||||
"layout": "レイアウト",
|
||||
"addToForm": "フォームに追加",
|
||||
"headingPlaceholder": "空の見出し",
|
||||
"nodeFieldTooltip": "ノード フィールドを追加するには、ワークフロー エディターのフィールドにある小さなプラス記号ボタンをクリックするか、フィールド名をフォームにドラッグします。",
|
||||
"workflowBuilderAlphaWarning": "ワークフロービルダーは現在アルファ版です。安定版リリースまでに互換性に影響する変更が発生する可能性があります。",
|
||||
"component": "コンポーネント",
|
||||
"textPlaceholder": "空のテキスト",
|
||||
"emptyRootPlaceholderViewMode": "このワークフローのフォームの作成を開始するには、[編集] をクリックします。",
|
||||
"addOption": "オプションを追加",
|
||||
"singleLine": "単線",
|
||||
"numberInput": "数値入力",
|
||||
"column": "列",
|
||||
"container": "コンテナ",
|
||||
"containerRowLayout": "コンテナ(行レイアウト)",
|
||||
"containerColumnLayout": "コンテナ(列レイアウト)",
|
||||
"maximum": "最大",
|
||||
"published": "公開済み",
|
||||
"publishedWorkflowOutputs": "アウトプット",
|
||||
"minimum": "最小",
|
||||
"publish": "公開",
|
||||
"unpublish": "非公開",
|
||||
"publishedWorkflowInputs": "インプット"
|
||||
},
|
||||
"chooseWorkflowFromLibrary": "ライブラリからワークフローを選択",
|
||||
"unnamedWorkflow": "名前のないワークフロー",
|
||||
"download": "ダウンロード",
|
||||
"savingWorkflow": "ワークフローを保存しています...",
|
||||
"problemSavingWorkflow": "ワークフローの保存に関する問題",
|
||||
"convertGraph": "グラフを変換",
|
||||
"downloadWorkflow": "ファイルに保存",
|
||||
"saveWorkflow": "ワークフローを保存",
|
||||
"userWorkflows": "ユーザーワークフロー",
|
||||
"yourWorkflows": "あなたのワークフロー",
|
||||
"edit": "編集",
|
||||
"workflowLibrary": "ワークフローライブラリ",
|
||||
"workflowSaved": "ワークフローが保存されました",
|
||||
"clearWorkflowSearchFilter": "ワークフロー検索フィルタをクリア",
|
||||
"workflowCleared": "ワークフローが作成されました",
|
||||
"autoLayout": "オートレイアウト",
|
||||
"view": "ビュー",
|
||||
"saveChanges": "変更を保存",
|
||||
"noDescription": "説明なし",
|
||||
"recommended": "あなたへのおすすめ",
|
||||
"noRecentWorkflows": "最近のワークフローがありません",
|
||||
"problemLoading": "ワークフローのローディングに関する問題",
|
||||
"newWorkflowCreated": "新しいワークフローが作成されました",
|
||||
"noWorkflows": "ワークフローがありません",
|
||||
"copyShareLink": "共有リンクをコピー",
|
||||
"copyShareLinkForWorkflow": "ワークフローの共有リンクをコピー",
|
||||
"workflowThumbnail": "ワークフローサムネイル",
|
||||
"loadWorkflow": "$t(common.load) ワークフロー",
|
||||
"shared": "共有",
|
||||
"openWorkflow": "ワークフローを開く",
|
||||
"emptyStringPlaceholder": "<空の文字列>",
|
||||
"browseWorkflows": "ワークフローを閲覧する",
|
||||
"saveWorkflowAs": "ワークフローとして保存",
|
||||
"private": "プライベート",
|
||||
"deselectAll": "すべて選択解除",
|
||||
"delete": "削除",
|
||||
"openLibrary": "ライブラリを開く",
|
||||
"loadMore": "もっと読み込む",
|
||||
"saveWorkflowToProject": "ワークフローをプロジェクトに保存",
|
||||
"created": "作成されました",
|
||||
"workflowEditorMenu": "ワークフローエディターメニュー",
|
||||
"defaultWorkflows": "デフォルトワークフロー",
|
||||
"allLoaded": "すべてのワークフローが読み込まれました",
|
||||
"filterByTags": "タグでフィルター",
|
||||
"recentlyOpened": "最近開いた",
|
||||
"opened": "オープン",
|
||||
"deleteWorkflow": "ワークフローを削除",
|
||||
"deleteWorkflow2": "このワークフローを削除してもよろしいですか? 元に戻すことはできません。",
|
||||
"loadFromGraph": "グラフからワークフローをロード",
|
||||
"workflowName": "ワークフロー名",
|
||||
"loading": "ワークフローをロードしています",
|
||||
"uploadWorkflow": "ファイルからロードする"
|
||||
},
|
||||
"system": {
|
||||
"logNamespaces": {
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"boards": "Bảng",
|
||||
"selectedForAutoAdd": "Đã Chọn Để Tự động thêm",
|
||||
"myBoard": "Bảng Của Tôi",
|
||||
"deletedPrivateBoardsCannotbeRestored": "Bảng đã xoá sẽ không thể khôi phục lại. Chọn 'Chỉ Xoá Bảng' sẽ dời ảnh vào trạng thái chưa phân loại riêng cho chủ ảnh.",
|
||||
"deletedPrivateBoardsCannotbeRestored": "Bảng và ảnh đã xoá sẽ không thể khôi phục lại. Chọn 'Chỉ Xoá Bảng' sẽ dời ảnh vào trạng thái chưa phân loại riêng cho chủ ảnh.",
|
||||
"changeBoard": "Thay Đổi Bảng",
|
||||
"clearSearch": "Làm Sạch Thanh Tìm Kiếm",
|
||||
"updateBoardError": "Lỗi khi cập nhật Bảng",
|
||||
@@ -41,18 +41,21 @@
|
||||
"deleteBoard": "Xoá Bảng",
|
||||
"deleteBoardAndImages": "Xoá Bảng Lẫn Hình ảnh",
|
||||
"deleteBoardOnly": "Chỉ Xoá Bảng",
|
||||
"deletedBoardsCannotbeRestored": "Bảng đã xoá sẽ không thể khôi phục lại. Chọn 'Chỉ Xoá Bảng' sẽ dời ảnh vào trạng thái chưa phân loại.",
|
||||
"bottomMessage": "Xoá bảng này lẫn ảnh của nó sẽ khởi động lại mọi tính năng đang sử dụng chúng.",
|
||||
"deletedBoardsCannotbeRestored": "Bảng và ảnh đã xoá sẽ không thể khôi phục lại. Chọn 'Chỉ Xoá Bảng' sẽ dời ảnh vào trạng thái chưa phân loại.",
|
||||
"bottomMessage": "Việc xóa ảnh sẽ khởi động lại mọi tính năng đang sử dụng chúng.",
|
||||
"menuItemAutoAdd": "Tự động thêm cho Bảng này",
|
||||
"move": "Di Chuyển",
|
||||
"topMessage": "Bảng này chứa ảnh được dùng với những tính năng sau:",
|
||||
"topMessage": "Lựa chọn này chứa ảnh được dùng với những tính năng sau:",
|
||||
"uncategorized": "Chưa Sắp Xếp",
|
||||
"archived": "Được Lưu Trữ",
|
||||
"loading": "Đang Tải...",
|
||||
"selectBoard": "Chọn Bảng",
|
||||
"archiveBoard": "Lưu trữ Bảng",
|
||||
"unarchiveBoard": "Ngừng Lưu Trữ Bảng",
|
||||
"assetsWithCount_other": "{{count}} tài nguyên"
|
||||
"assetsWithCount_other": "{{count}} tài nguyên",
|
||||
"uncategorizedImages": "Ảnh Chưa Sắp Xếp",
|
||||
"deleteAllUncategorizedImages": "Xoá Tất Cả Ảnh Chưa Sắp Xếp",
|
||||
"deletedImagesCannotBeRestored": "Ảnh đã xoá không thể phục hồi lại."
|
||||
},
|
||||
"gallery": {
|
||||
"swapImages": "Đổi Hình Ảnh",
|
||||
@@ -2059,7 +2062,7 @@
|
||||
"colorPicker": "Chọn Màu"
|
||||
},
|
||||
"mergingLayers": "Đang gộp layer",
|
||||
"controlLayerEmptyState": "<UploadButton>Tải lên ảnh</UploadButton>, kéo thả ảnh từ <GalleryButton>thư viện</GalleryButton> vào layer này, hoặc vẽ trên canvas để bắt đầu.",
|
||||
"controlLayerEmptyState": "<UploadButton>Tải lên ảnh</UploadButton>, kéo thả ảnh từ <GalleryButton>thư viện</GalleryButton> vào layer này, <PullBboxButton>kéo hộp giới hạn vào layer này</PullBboxButton>, hoặc vẽ trên canvas để bắt đầu.",
|
||||
"referenceImageEmptyState": "<UploadButton>Tải lên hình ảnh</UploadButton>, kéo ảnh từ <GalleryButton>thư viện ảnh</GalleryButton> vào layer này, hoặc <PullBboxButton>kéo hộp giới hạn vào layer này</PullBboxButton> để bắt đầu.",
|
||||
"useImage": "Dùng Hình Ảnh",
|
||||
"resetCanvasLayers": "Khởi Động Lại Layer Canvas",
|
||||
@@ -2108,7 +2111,11 @@
|
||||
"imageInfluence": "Ảnh Chi Phối",
|
||||
"medium": "Vừa",
|
||||
"highest": "Cao Nhất"
|
||||
}
|
||||
},
|
||||
"addDenoiseLimit": "Thêm $t(controlLayers.denoiseLimit)",
|
||||
"imageNoise": "Độ Nhiễu Hình Ảnh",
|
||||
"denoiseLimit": "Giới Hạn Khử Nhiễu",
|
||||
"addImageNoise": "Thêm $t(controlLayers.imageNoise)"
|
||||
},
|
||||
"stylePresets": {
|
||||
"negativePrompt": "Lệnh Tiêu Cực",
|
||||
@@ -2249,7 +2256,8 @@
|
||||
"problemUnpublishingWorkflowDescription": "Có vấn đề khi ngừng đăng tải workflow. Vui lòng thử lại sau.",
|
||||
"workflowUnpublished": "Workflow Đã Được Ngừng Đăng Tải",
|
||||
"problemUnpublishingWorkflow": "Có Vấn Đề Khi Ngừng Đăng Tải Workflow",
|
||||
"chatGPT4oIncompatibleGenerationMode": "ChatGPT 4o chỉ hỗ trợ Từ Ngữ Sang Hình Ảnh và Hình Ảnh Sang Hình Ảnh. Hãy dùng model khác cho các tác vụ Inpaint và Outpaint."
|
||||
"chatGPT4oIncompatibleGenerationMode": "ChatGPT 4o chỉ hỗ trợ Từ Ngữ Sang Hình Ảnh và Hình Ảnh Sang Hình Ảnh. Hãy dùng model khác cho các tác vụ Inpaint và Outpaint.",
|
||||
"imagenIncompatibleGenerationMode": "Google {{model}} chỉ hỗ trợ Từ Ngữ Sang Hình Ảnh. Dùng các model khác cho Hình Ảnh Sang Hình Ảnh, Inpaint và Outpaint."
|
||||
},
|
||||
"ui": {
|
||||
"tabs": {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatch
|
||||
import { buildChatGPT4oGraph } from 'features/nodes/util/graph/generation/buildChatGPT4oGraph';
|
||||
import { buildCogView4Graph } from 'features/nodes/util/graph/generation/buildCogView4Graph';
|
||||
import { buildFLUXGraph } from 'features/nodes/util/graph/generation/buildFLUXGraph';
|
||||
import { buildFluxKontextGraph } from 'features/nodes/util/graph/generation/buildFluxKontextGraph';
|
||||
import { buildImagen3Graph } from 'features/nodes/util/graph/generation/buildImagen3Graph';
|
||||
import { buildImagen4Graph } from 'features/nodes/util/graph/generation/buildImagen4Graph';
|
||||
import { buildSD1Graph } from 'features/nodes/util/graph/generation/buildSD1Graph';
|
||||
@@ -59,6 +60,8 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
|
||||
return await buildImagen4Graph(state, manager);
|
||||
case 'chatgpt-4o':
|
||||
return await buildChatGPT4oGraph(state, manager);
|
||||
case 'flux-kontext':
|
||||
return await buildFluxKontextGraph(state, manager);
|
||||
default:
|
||||
assert(false, `No graph builders for base ${base}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Spinner } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useAllEntityAdapters } from 'features/controlLayers/contexts/EntityAdapterContext';
|
||||
import { computed } from 'nanostores';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
export const CanvasBusySpinner = memo(() => {
|
||||
const canvasManager = useCanvasManager();
|
||||
const allEntityAdapters = useAllEntityAdapters();
|
||||
const $isPendingRectCalculation = useMemo(
|
||||
() =>
|
||||
computed(
|
||||
allEntityAdapters.map(({ transformer }) => transformer.$isPendingRectCalculation),
|
||||
(...values) => values.some((v) => v)
|
||||
),
|
||||
[allEntityAdapters]
|
||||
);
|
||||
const isPendingRectCalculation = useStore($isPendingRectCalculation);
|
||||
const isRasterizing = useStore(canvasManager.stateApi.$isRasterizing);
|
||||
const isCompositing = useStore(canvasManager.compositor.$isBusy);
|
||||
|
||||
if (isRasterizing || isCompositing || isPendingRectCalculation) {
|
||||
return <Spinner opacity={0.3} />;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
CanvasBusySpinner.displayName = 'CanvasBusySpinner';
|
||||
@@ -12,6 +12,7 @@ import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
|
||||
import { CanvasAlertsPreserveMask } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask';
|
||||
import { CanvasAlertsSelectedEntityStatus } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus';
|
||||
import { CanvasAlertsSendingToGallery } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSendingTo';
|
||||
import { CanvasBusySpinner } from 'features/controlLayers/components/CanvasBusySpinner';
|
||||
import { CanvasContextMenuGlobalMenuItems } from 'features/controlLayers/components/CanvasContextMenu/CanvasContextMenuGlobalMenuItems';
|
||||
import { CanvasContextMenuSelectedEntityMenuItems } from 'features/controlLayers/components/CanvasContextMenu/CanvasContextMenuSelectedEntityMenuItems';
|
||||
import { CanvasDropArea } from 'features/controlLayers/components/CanvasDropArea';
|
||||
@@ -106,6 +107,9 @@ export const CanvasMainPanelContent = memo(() => {
|
||||
<MenuContent />
|
||||
</Menu>
|
||||
</Flex>
|
||||
<Flex position="absolute" bottom={4} insetInlineEnd={4}>
|
||||
<CanvasBusySpinner />
|
||||
</Flex>
|
||||
</CanvasManagerProviderGate>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@@ -168,3 +168,33 @@ export const useEntityAdapter = (
|
||||
assert(adapter, 'useEntityAdapter must be used within a EntityAdapterContext');
|
||||
return adapter;
|
||||
};
|
||||
|
||||
export const useAllEntityAdapters = () => {
|
||||
const canvasManager = useCanvasManager();
|
||||
const regionalGuidanceAdapters = useSyncExternalStore(
|
||||
canvasManager.adapters.regionMasks.subscribe,
|
||||
canvasManager.adapters.regionMasks.getSnapshot
|
||||
);
|
||||
const rasterLayerAdapters = useSyncExternalStore(
|
||||
canvasManager.adapters.rasterLayers.subscribe,
|
||||
canvasManager.adapters.rasterLayers.getSnapshot
|
||||
);
|
||||
const controlLayerAdapters = useSyncExternalStore(
|
||||
canvasManager.adapters.controlLayers.subscribe,
|
||||
canvasManager.adapters.controlLayers.getSnapshot
|
||||
);
|
||||
const inpaintMaskAdapters = useSyncExternalStore(
|
||||
canvasManager.adapters.inpaintMasks.subscribe,
|
||||
canvasManager.adapters.inpaintMasks.getSnapshot
|
||||
);
|
||||
const allEntityAdapters = useMemo(() => {
|
||||
return [
|
||||
...Array.from(rasterLayerAdapters.values()),
|
||||
...Array.from(controlLayerAdapters.values()),
|
||||
...Array.from(inpaintMaskAdapters.values()),
|
||||
...Array.from(regionalGuidanceAdapters.values()),
|
||||
];
|
||||
}, [controlLayerAdapters, inpaintMaskAdapters, rasterLayerAdapters, regionalGuidanceAdapters]);
|
||||
|
||||
return allEntityAdapters;
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ import type {
|
||||
import {
|
||||
initialChatGPT4oReferenceImage,
|
||||
initialControlNet,
|
||||
initialFluxKontextReferenceImage,
|
||||
initialIPAdapter,
|
||||
initialT2IAdapter,
|
||||
} from 'features/controlLayers/store/util';
|
||||
@@ -87,6 +88,12 @@ export const selectDefaultRefImageConfig = createSelector(
|
||||
return referenceImage;
|
||||
}
|
||||
|
||||
if (selectedMainModel?.base === 'flux-kontext') {
|
||||
const referenceImage = deepClone(initialFluxKontextReferenceImage);
|
||||
referenceImage.model = zModelIdentifierField.parse(selectedMainModel);
|
||||
return referenceImage;
|
||||
}
|
||||
|
||||
const { data } = query;
|
||||
let model: IPAdapterModelConfig | null = null;
|
||||
if (data) {
|
||||
|
||||
@@ -2,10 +2,12 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
selectIsChatGTP4o,
|
||||
selectIsCogView4,
|
||||
selectIsFluxKontext,
|
||||
selectIsImagen3,
|
||||
selectIsImagen4,
|
||||
selectIsSD3,
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectActiveReferenceImageEntities } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasEntityType } from 'features/controlLayers/store/types';
|
||||
import { useMemo } from 'react';
|
||||
import type { Equals } from 'tsafe';
|
||||
@@ -17,23 +19,28 @@ export const useIsEntityTypeEnabled = (entityType: CanvasEntityType) => {
|
||||
const isImagen3 = useAppSelector(selectIsImagen3);
|
||||
const isImagen4 = useAppSelector(selectIsImagen4);
|
||||
const isChatGPT4o = useAppSelector(selectIsChatGTP4o);
|
||||
const isFluxKontext = useAppSelector(selectIsFluxKontext);
|
||||
const activeReferenceImageEntities = useAppSelector(selectActiveReferenceImageEntities);
|
||||
|
||||
const isEntityTypeEnabled = useMemo<boolean>(() => {
|
||||
switch (entityType) {
|
||||
case 'reference_image':
|
||||
if (isFluxKontext) {
|
||||
return activeReferenceImageEntities.length === 0;
|
||||
}
|
||||
return !isSD3 && !isCogView4 && !isImagen3 && !isImagen4;
|
||||
case 'regional_guidance':
|
||||
return !isSD3 && !isCogView4 && !isImagen3 && !isImagen4 && !isChatGPT4o;
|
||||
return !isSD3 && !isCogView4 && !isImagen3 && !isImagen4 && !isFluxKontext && !isChatGPT4o;
|
||||
case 'control_layer':
|
||||
return !isSD3 && !isCogView4 && !isImagen3 && !isImagen4 && !isChatGPT4o;
|
||||
return !isSD3 && !isCogView4 && !isImagen3 && !isImagen4 && !isFluxKontext && !isChatGPT4o;
|
||||
case 'inpaint_mask':
|
||||
return !isImagen3 && !isImagen4 && !isChatGPT4o;
|
||||
return !isImagen3 && !isImagen4 && !isFluxKontext && !isChatGPT4o;
|
||||
case 'raster_layer':
|
||||
return !isImagen3 && !isImagen4 && !isChatGPT4o;
|
||||
return !isImagen3 && !isImagen4 && !isFluxKontext && !isChatGPT4o;
|
||||
default:
|
||||
assert<Equals<typeof entityType, never>>(false);
|
||||
}
|
||||
}, [entityType, isSD3, isCogView4, isImagen3, isImagen4, isChatGPT4o]);
|
||||
}, [entityType, isSD3, isCogView4, isImagen3, isImagen4, isFluxKontext, isChatGPT4o, activeReferenceImageEntities]);
|
||||
|
||||
return isEntityTypeEnabled;
|
||||
};
|
||||
|
||||
@@ -24,12 +24,13 @@ import {
|
||||
selectCanvasSlice,
|
||||
selectEntity,
|
||||
} from 'features/controlLayers/store/selectors';
|
||||
import {
|
||||
type CanvasEntityIdentifier,
|
||||
type CanvasRenderableEntityState,
|
||||
isRasterLayerEntityIdentifier,
|
||||
type Rect,
|
||||
import type {
|
||||
CanvasEntityIdentifier,
|
||||
CanvasRenderableEntityState,
|
||||
LifecycleCallback,
|
||||
Rect,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { isRasterLayerEntityIdentifier } from 'features/controlLayers/store/types';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import Konva from 'konva';
|
||||
import { atom } from 'nanostores';
|
||||
@@ -40,11 +41,6 @@ import stableHash from 'stable-hash';
|
||||
import { assert } from 'tsafe';
|
||||
import type { Jsonifiable, JsonObject } from 'type-fest';
|
||||
|
||||
// Ideally, we'd type `adapter` as `CanvasEntityAdapterBase`, but the generics make this tricky. `CanvasEntityAdapter`
|
||||
// is a union of all entity adapters and is functionally identical to `CanvasEntityAdapterBase`. We'll need to do a
|
||||
// type assertion below in the `onInit` method, which calls these callbacks.
|
||||
type InitCallback = (adapter: CanvasEntityAdapter) => Promise<boolean>;
|
||||
|
||||
export abstract class CanvasEntityAdapterBase<
|
||||
T extends CanvasRenderableEntityState,
|
||||
U extends string,
|
||||
@@ -118,7 +114,7 @@ export abstract class CanvasEntityAdapterBase<
|
||||
/**
|
||||
* Callbacks that are executed when the module is initialized.
|
||||
*/
|
||||
private static initCallbacks = new Set<InitCallback>();
|
||||
private static initCallbacks = new Set<LifecycleCallback>();
|
||||
|
||||
/**
|
||||
* Register a callback to be run when an entity adapter is initialized.
|
||||
@@ -165,7 +161,7 @@ export abstract class CanvasEntityAdapterBase<
|
||||
* return false;
|
||||
* });
|
||||
*/
|
||||
static registerInitCallback = (callback: InitCallback) => {
|
||||
static registerInitCallback = (callback: LifecycleCallback) => {
|
||||
const wrapped = async (adapter: CanvasEntityAdapter) => {
|
||||
const result = await callback(adapter);
|
||||
if (result) {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
roundRect,
|
||||
} from 'features/controlLayers/konva/util';
|
||||
import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import type { Coordinate, Rect, RectWithRotation } from 'features/controlLayers/store/types';
|
||||
import type { Coordinate, LifecycleCallback, Rect, RectWithRotation } from 'features/controlLayers/store/types';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import Konva from 'konva';
|
||||
import type { GroupConfig } from 'konva/lib/Group';
|
||||
@@ -123,7 +123,7 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
/**
|
||||
* Whether the transformer is currently calculating the rect of the parent.
|
||||
*/
|
||||
$isPendingRectCalculation = atom<boolean>(true);
|
||||
$isPendingRectCalculation = atom<boolean>(false);
|
||||
|
||||
/**
|
||||
* A set of subscriptions that should be cleaned up when the transformer is destroyed.
|
||||
@@ -177,6 +177,11 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
*/
|
||||
transformMutex = new Mutex();
|
||||
|
||||
/**
|
||||
* Callbacks that are executed when the bbox is updated.
|
||||
*/
|
||||
private static bboxUpdatedCallbacks = new Set<LifecycleCallback>();
|
||||
|
||||
konva: {
|
||||
transformer: Konva.Transformer;
|
||||
proxyRect: Konva.Rect;
|
||||
@@ -908,6 +913,8 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
this.parent.renderer.konva.objectGroup.setAttrs(groupAttrs);
|
||||
this.parent.bufferRenderer.konva.group.setAttrs(groupAttrs);
|
||||
}
|
||||
|
||||
CanvasEntityTransformer.runBboxUpdatedCallbacks(this.parent);
|
||||
};
|
||||
|
||||
calculateRect = debounce(() => {
|
||||
@@ -1026,6 +1033,23 @@ export class CanvasEntityTransformer extends CanvasModuleBase {
|
||||
this.konva.outlineRect.visible(false);
|
||||
};
|
||||
|
||||
static registerBboxUpdatedCallback = (callback: LifecycleCallback) => {
|
||||
const wrapped = async (adapter: CanvasEntityAdapter) => {
|
||||
const result = await callback(adapter);
|
||||
if (result) {
|
||||
this.bboxUpdatedCallbacks.delete(wrapped);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
this.bboxUpdatedCallbacks.add(wrapped);
|
||||
};
|
||||
|
||||
private static runBboxUpdatedCallbacks = (adapter: CanvasEntityAdapter) => {
|
||||
for (const callback of this.bboxUpdatedCallbacks) {
|
||||
callback(adapter);
|
||||
}
|
||||
};
|
||||
|
||||
repr = () => {
|
||||
return {
|
||||
id: this.id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Property } from 'csstype';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { CanvasModuleBase } from 'features/controlLayers/konva/CanvasModuleBase';
|
||||
import { getKonvaNodeDebugAttrs, getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { getKonvaNodeDebugAttrs, getPrefixedId, getRectUnion } from 'features/controlLayers/konva/util';
|
||||
import type { Coordinate, Dimensions, Rect, StageAttrs } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||
@@ -186,6 +186,18 @@ export class CanvasStageModule extends CanvasModuleBase {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fits the bbox and layers to the stage. The union of the bbox and the visible layers will be centered and scaled
|
||||
* to fit the stage with some padding.
|
||||
*/
|
||||
fitBboxAndLayersToStage = (): void => {
|
||||
const layersRect = this.manager.compositor.getVisibleRectOfType();
|
||||
const bboxRect = this.manager.stateApi.getBbox().rect;
|
||||
const unionRect = getRectUnion(layersRect, bboxRect);
|
||||
this.log.trace({ bboxRect, layersRect, unionRect }, 'Fitting bbox and layers to stage');
|
||||
this.fitRect(unionRect);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fits a rectangle to the stage. The rectangle will be centered and scaled to fit the stage with some padding.
|
||||
*
|
||||
@@ -218,14 +230,23 @@ export class CanvasStageModule extends CanvasModuleBase {
|
||||
this._intendedScale = scale;
|
||||
this._activeSnapPoint = null;
|
||||
|
||||
this.konva.stage.setAttrs({
|
||||
const tween = new Konva.Tween({
|
||||
node: this.konva.stage,
|
||||
duration: 0.15,
|
||||
x,
|
||||
y,
|
||||
scaleX: scale,
|
||||
scaleY: scale,
|
||||
easing: Konva.Easings.EaseInOut,
|
||||
onUpdate: () => {
|
||||
this.syncStageAttrs();
|
||||
},
|
||||
onFinish: () => {
|
||||
this.syncStageAttrs();
|
||||
tween.destroy();
|
||||
},
|
||||
});
|
||||
|
||||
this.syncStageAttrs({ x, y, scale });
|
||||
tween.play();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,7 +69,13 @@ import type {
|
||||
IPMethodV2,
|
||||
T2IAdapterConfig,
|
||||
} from './types';
|
||||
import { getEntityIdentifier, isChatGPT4oAspectRatioID, isImagenAspectRatioID, isRenderableEntity } from './types';
|
||||
import {
|
||||
getEntityIdentifier,
|
||||
isChatGPT4oAspectRatioID,
|
||||
isFluxKontextAspectRatioID,
|
||||
isImagenAspectRatioID,
|
||||
isRenderableEntity,
|
||||
} from './types';
|
||||
import {
|
||||
converters,
|
||||
getControlLayerState,
|
||||
@@ -81,6 +87,7 @@ import {
|
||||
initialChatGPT4oReferenceImage,
|
||||
initialControlLoRA,
|
||||
initialControlNet,
|
||||
initialFluxKontextReferenceImage,
|
||||
initialFLUXRedux,
|
||||
initialIPAdapter,
|
||||
initialT2IAdapter,
|
||||
@@ -686,6 +693,16 @@ export const canvasSlice = createSlice({
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.ipAdapter.model.base === 'flux-kontext') {
|
||||
// Switching to flux-kontext
|
||||
entity.ipAdapter = {
|
||||
...initialFluxKontextReferenceImage,
|
||||
image: entity.ipAdapter.image,
|
||||
model: entity.ipAdapter.model,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.ipAdapter.model.type === 'flux_redux') {
|
||||
// Switching to flux_redux
|
||||
entity.ipAdapter = {
|
||||
@@ -1322,6 +1339,31 @@ export const canvasSlice = createSlice({
|
||||
}
|
||||
state.bbox.aspectRatio.value = state.bbox.rect.width / state.bbox.rect.height;
|
||||
state.bbox.aspectRatio.isLocked = true;
|
||||
} else if (state.bbox.modelBase === 'flux-kontext' && isFluxKontextAspectRatioID(id)) {
|
||||
if (id === '3:4') {
|
||||
state.bbox.rect.width = 880;
|
||||
state.bbox.rect.height = 1184;
|
||||
} else if (id === '4:3') {
|
||||
state.bbox.rect.width = 1184;
|
||||
state.bbox.rect.height = 880;
|
||||
} else if (id === '9:16') {
|
||||
state.bbox.rect.width = 752;
|
||||
state.bbox.rect.height = 1392;
|
||||
} else if (id === '16:9') {
|
||||
state.bbox.rect.width = 1392;
|
||||
state.bbox.rect.height = 752;
|
||||
} else if (id === '21:9') {
|
||||
state.bbox.rect.width = 1568;
|
||||
state.bbox.rect.height = 672;
|
||||
} else if (id === '9:21') {
|
||||
state.bbox.rect.width = 672;
|
||||
state.bbox.rect.height = 1568;
|
||||
} else if (id === '1:1') {
|
||||
state.bbox.rect.width = 1024;
|
||||
state.bbox.rect.height = 1024;
|
||||
}
|
||||
state.bbox.aspectRatio.value = state.bbox.rect.width / state.bbox.rect.height;
|
||||
state.bbox.aspectRatio.isLocked = true;
|
||||
} else {
|
||||
state.bbox.aspectRatio.isLocked = true;
|
||||
state.bbox.aspectRatio.value = ASPECT_RATIO_MAP[id].ratio;
|
||||
|
||||
@@ -383,6 +383,7 @@ export const selectIsCogView4 = createParamsSelector((params) => params.model?.b
|
||||
export const selectIsImagen3 = createParamsSelector((params) => params.model?.base === 'imagen3');
|
||||
export const selectIsImagen4 = createParamsSelector((params) => params.model?.base === 'imagen4');
|
||||
export const selectIsChatGTP4o = createParamsSelector((params) => params.model?.base === 'chatgpt-4o');
|
||||
export const selectIsFluxKontext = createParamsSelector((params) => params.model?.base === 'flux-kontext');
|
||||
|
||||
export const selectModel = createParamsSelector((params) => params.model);
|
||||
export const selectModelKey = createParamsSelector((params) => params.model?.key);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { CanvasEntityAdapter } from 'features/controlLayers/konva/CanvasEntity/types';
|
||||
import { fetchModelConfigByIdentifier } from 'features/metadata/util/modelFetchingHelpers';
|
||||
import { zMainModelBase, zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import type { ParameterLoRAModel } from 'features/parameters/types/parameterSchemas';
|
||||
@@ -257,6 +258,13 @@ const zChatGPT4oReferenceImageConfig = z.object({
|
||||
});
|
||||
export type ChatGPT4oReferenceImageConfig = z.infer<typeof zChatGPT4oReferenceImageConfig>;
|
||||
|
||||
const zFluxKontextReferenceImageConfig = z.object({
|
||||
type: z.literal('flux_kontext_reference_image'),
|
||||
image: zImageWithDims.nullable(),
|
||||
model: zServerValidatedModelIdentifierField.nullable(),
|
||||
});
|
||||
export type FluxKontextReferenceImageConfig = z.infer<typeof zFluxKontextReferenceImageConfig>;
|
||||
|
||||
const zCanvasEntityBase = z.object({
|
||||
id: zId,
|
||||
name: zName,
|
||||
@@ -267,7 +275,12 @@ const zCanvasEntityBase = z.object({
|
||||
const zCanvasReferenceImageState = zCanvasEntityBase.extend({
|
||||
type: z.literal('reference_image'),
|
||||
// This should be named `referenceImage` but we need to keep it as `ipAdapter` for backwards compatibility
|
||||
ipAdapter: z.discriminatedUnion('type', [zIPAdapterConfig, zFLUXReduxConfig, zChatGPT4oReferenceImageConfig]),
|
||||
ipAdapter: z.discriminatedUnion('type', [
|
||||
zIPAdapterConfig,
|
||||
zFLUXReduxConfig,
|
||||
zChatGPT4oReferenceImageConfig,
|
||||
zFluxKontextReferenceImageConfig,
|
||||
]),
|
||||
});
|
||||
export type CanvasReferenceImageState = z.infer<typeof zCanvasReferenceImageState>;
|
||||
|
||||
@@ -279,6 +292,9 @@ export const isFLUXReduxConfig = (config: CanvasReferenceImageState['ipAdapter']
|
||||
export const isChatGPT4oReferenceImageConfig = (
|
||||
config: CanvasReferenceImageState['ipAdapter']
|
||||
): config is ChatGPT4oReferenceImageConfig => config.type === 'chatgpt_4o_reference_image';
|
||||
export const isFluxKontextReferenceImageConfig = (
|
||||
config: CanvasReferenceImageState['ipAdapter']
|
||||
): config is FluxKontextReferenceImageConfig => config.type === 'flux_kontext_reference_image';
|
||||
|
||||
const zFillStyle = z.enum(['solid', 'grid', 'crosshatch', 'diagonal', 'horizontal', 'vertical']);
|
||||
export type FillStyle = z.infer<typeof zFillStyle>;
|
||||
@@ -405,7 +421,7 @@ export type StagingAreaImage = {
|
||||
offsetY: number;
|
||||
};
|
||||
|
||||
export const zAspectRatioID = z.enum(['Free', '16:9', '3:2', '4:3', '1:1', '3:4', '2:3', '9:16']);
|
||||
export const zAspectRatioID = z.enum(['Free', '21:9', '9:21', '16:9', '3:2', '4:3', '1:1', '3:4', '2:3', '9:16']);
|
||||
|
||||
export const zImagen3AspectRatioID = z.enum(['16:9', '4:3', '1:1', '3:4', '9:16']);
|
||||
export const isImagenAspectRatioID = (v: unknown): v is z.infer<typeof zImagen3AspectRatioID> =>
|
||||
@@ -415,6 +431,10 @@ export const zChatGPT4oAspectRatioID = z.enum(['3:2', '1:1', '2:3']);
|
||||
export const isChatGPT4oAspectRatioID = (v: unknown): v is z.infer<typeof zChatGPT4oAspectRatioID> =>
|
||||
zChatGPT4oAspectRatioID.safeParse(v).success;
|
||||
|
||||
export const zFluxKontextAspectRatioID = z.enum(['21:9', '4:3', '1:1', '3:4', '9:21', '16:9', '9:16']);
|
||||
export const isFluxKontextAspectRatioID = (v: unknown): v is z.infer<typeof zFluxKontextAspectRatioID> =>
|
||||
zFluxKontextAspectRatioID.safeParse(v).success;
|
||||
|
||||
export type AspectRatioID = z.infer<typeof zAspectRatioID>;
|
||||
export const isAspectRatioID = (v: unknown): v is AspectRatioID => zAspectRatioID.safeParse(v).success;
|
||||
|
||||
@@ -611,3 +631,7 @@ export const isMaskEntityIdentifier = (
|
||||
): entityIdentifier is CanvasEntityIdentifier<'inpaint_mask' | 'regional_guidance'> => {
|
||||
return isInpaintMaskEntityIdentifier(entityIdentifier) || isRegionalGuidanceEntityIdentifier(entityIdentifier);
|
||||
};
|
||||
|
||||
// Ideally, we'd type `adapter` as `CanvasEntityAdapterBase`, but the generics make this tricky. `CanvasEntityAdapter`
|
||||
// is a union of all entity adapters and is functionally identical to `CanvasEntityAdapterBase`.
|
||||
export type LifecycleCallback = (adapter: CanvasEntityAdapter) => Promise<boolean>;
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
ChatGPT4oReferenceImageConfig,
|
||||
ControlLoRAConfig,
|
||||
ControlNetConfig,
|
||||
FluxKontextReferenceImageConfig,
|
||||
FLUXReduxConfig,
|
||||
ImageWithDims,
|
||||
IPAdapterConfig,
|
||||
@@ -83,6 +84,11 @@ export const initialChatGPT4oReferenceImage: ChatGPT4oReferenceImageConfig = {
|
||||
image: null,
|
||||
model: null,
|
||||
};
|
||||
export const initialFluxKontextReferenceImage: FluxKontextReferenceImageConfig = {
|
||||
type: 'flux_kontext_reference_image',
|
||||
image: null,
|
||||
model: null,
|
||||
};
|
||||
export const initialT2IAdapter: T2IAdapterConfig = {
|
||||
type: 't2i_adapter',
|
||||
model: null,
|
||||
|
||||
@@ -19,6 +19,7 @@ type Props = {
|
||||
withDownload?: boolean;
|
||||
withCopy?: boolean;
|
||||
extraCopyActions?: { label: string; getData: (data: unknown) => unknown }[];
|
||||
wrapData?: boolean;
|
||||
} & FlexProps;
|
||||
|
||||
const overlayscrollbarsOptions = getOverlayScrollbarsParams({
|
||||
@@ -29,7 +30,16 @@ const overlayscrollbarsOptions = getOverlayScrollbarsParams({
|
||||
const ChakraPre = chakra('pre');
|
||||
|
||||
const DataViewer = (props: Props) => {
|
||||
const { label, data, fileName, withDownload = true, withCopy = true, extraCopyActions, ...rest } = props;
|
||||
const {
|
||||
label,
|
||||
data,
|
||||
fileName,
|
||||
withDownload = true,
|
||||
withCopy = true,
|
||||
extraCopyActions,
|
||||
wrapData = true,
|
||||
...rest
|
||||
} = props;
|
||||
const dataString = useMemo(() => (isString(data) ? data : formatter.Serialize(data)) ?? '', [data]);
|
||||
const shift = useShiftModifier();
|
||||
const clipboard = useClipboard();
|
||||
@@ -53,7 +63,7 @@ const DataViewer = (props: Props) => {
|
||||
<Flex bg="base.800" borderRadius="base" flexGrow={1} w="full" h="full" position="relative" {...rest}>
|
||||
<Box position="absolute" top={0} left={0} right={0} bottom={0} overflow="auto" p={2} fontSize="sm">
|
||||
<OverlayScrollbarsComponent defer style={overlayScrollbarsStyles} options={overlayscrollbarsOptions}>
|
||||
<ChakraPre whiteSpace="pre-wrap">{dataString}</ChakraPre>
|
||||
<ChakraPre whiteSpace={wrapData ? 'pre-wrap' : undefined}>{dataString}</ChakraPre>
|
||||
</OverlayScrollbarsComponent>
|
||||
</Box>
|
||||
<Flex position="absolute" top={0} insetInlineEnd={0} p={2}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { AppDispatch, RootState } from 'app/store/store';
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { selectDefaultIPAdapter, selectDefaultRefImageConfig } from 'features/controlLayers/hooks/addLayerHooks';
|
||||
import { CanvasEntityTransformer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityTransformer';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { canvasReset } from 'features/controlLayers/store/actions';
|
||||
import {
|
||||
@@ -173,15 +174,24 @@ export const newCanvasFromImage = async (arg: {
|
||||
imageObject = imageDTOToImageObject(imageDTO);
|
||||
}
|
||||
|
||||
const { x, y } = selectBboxRect(state);
|
||||
const addFitOnLayerInitCallback = (adapterId: string) => {
|
||||
CanvasEntityTransformer.registerBboxUpdatedCallback((adapter) => {
|
||||
// Skip the callback if the adapter is not the one we are creating
|
||||
if (adapter.id !== adapterId) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
adapter.manager.stage.fitBboxAndLayersToStage();
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'raster_layer': {
|
||||
const overrides = {
|
||||
id: getPrefixedId('raster_layer'),
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
} satisfies Partial<CanvasRasterLayerState>;
|
||||
addFitOnLayerInitCallback(overrides.id);
|
||||
dispatch(canvasReset());
|
||||
// The `bboxChangedFromCanvas` reducer does no validation! Careful!
|
||||
dispatch(bboxChangedFromCanvas({ x: 0, y: 0, width, height }));
|
||||
@@ -192,9 +202,9 @@ export const newCanvasFromImage = async (arg: {
|
||||
const overrides = {
|
||||
id: getPrefixedId('control_layer'),
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
controlAdapter: deepClone(initialControlNet),
|
||||
} satisfies Partial<CanvasControlLayerState>;
|
||||
addFitOnLayerInitCallback(overrides.id);
|
||||
dispatch(canvasReset());
|
||||
// The `bboxChangedFromCanvas` reducer does no validation! Careful!
|
||||
dispatch(bboxChangedFromCanvas({ x: 0, y: 0, width, height }));
|
||||
@@ -205,8 +215,8 @@ export const newCanvasFromImage = async (arg: {
|
||||
const overrides = {
|
||||
id: getPrefixedId('inpaint_mask'),
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
} satisfies Partial<CanvasInpaintMaskState>;
|
||||
addFitOnLayerInitCallback(overrides.id);
|
||||
dispatch(canvasReset());
|
||||
// The `bboxChangedFromCanvas` reducer does no validation! Careful!
|
||||
dispatch(bboxChangedFromCanvas({ x: 0, y: 0, width, height }));
|
||||
@@ -217,8 +227,8 @@ export const newCanvasFromImage = async (arg: {
|
||||
const overrides = {
|
||||
id: getPrefixedId('regional_guidance'),
|
||||
objects: [imageObject],
|
||||
position: { x, y },
|
||||
} satisfies Partial<CanvasRegionalGuidanceState>;
|
||||
addFitOnLayerInitCallback(overrides.id);
|
||||
dispatch(canvasReset());
|
||||
// The `bboxChangedFromCanvas` reducer does no validation! Careful!
|
||||
dispatch(bboxChangedFromCanvas({ x: 0, y: 0, width, height }));
|
||||
|
||||
@@ -19,6 +19,7 @@ export const BASE_COLOR_MAP: Record<BaseModelType, string> = {
|
||||
imagen3: 'pink',
|
||||
imagen4: 'pink',
|
||||
'chatgpt-4o': 'pink',
|
||||
'flux-kontext': 'pink',
|
||||
};
|
||||
|
||||
const ModelBaseBadge = ({ base }: Props) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { FloatFieldSlider } from 'features/nodes/components/flow/nodes/Invocatio
|
||||
import ChatGPT4oModelFieldInputComponent from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/ChatGPT4oModelFieldInputComponent';
|
||||
import { FloatFieldCollectionInputComponent } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatFieldCollectionInputComponent';
|
||||
import { FloatGeneratorFieldInputComponent } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/FloatGeneratorFieldComponent';
|
||||
import FluxKontextModelFieldInputComponent from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/FluxKontextModelFieldInputComponent';
|
||||
import { ImageFieldCollectionInputComponent } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageFieldCollectionInputComponent';
|
||||
import { ImageGeneratorFieldInputComponent } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageGeneratorFieldComponent';
|
||||
import Imagen3ModelFieldInputComponent from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/Imagen3ModelFieldInputComponent';
|
||||
@@ -50,6 +51,8 @@ import {
|
||||
isFloatFieldInputTemplate,
|
||||
isFloatGeneratorFieldInputInstance,
|
||||
isFloatGeneratorFieldInputTemplate,
|
||||
isFluxKontextModelFieldInputInstance,
|
||||
isFluxKontextModelFieldInputTemplate,
|
||||
isFluxMainModelFieldInputInstance,
|
||||
isFluxMainModelFieldInputTemplate,
|
||||
isFluxReduxModelFieldInputInstance,
|
||||
@@ -417,6 +420,13 @@ export const InputFieldRenderer = memo(({ nodeId, fieldName, settings }: Props)
|
||||
return <Imagen4ModelFieldInputComponent nodeId={nodeId} field={field} fieldTemplate={template} />;
|
||||
}
|
||||
|
||||
if (isFluxKontextModelFieldInputTemplate(template)) {
|
||||
if (!isFluxKontextModelFieldInputInstance(field)) {
|
||||
return null;
|
||||
}
|
||||
return <FluxKontextModelFieldInputComponent nodeId={nodeId} field={field} fieldTemplate={template} />;
|
||||
}
|
||||
|
||||
if (isChatGPT4oModelFieldInputTemplate(template)) {
|
||||
if (!isChatGPT4oModelFieldInputInstance(field)) {
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { ModelFieldCombobox } from 'features/nodes/components/flow/nodes/Invocation/fields/inputs/ModelFieldCombobox';
|
||||
import { fieldFluxKontextModelValueChanged } from 'features/nodes/store/nodesSlice';
|
||||
import type {
|
||||
FluxKontextModelFieldInputInstance,
|
||||
FluxKontextModelFieldInputTemplate,
|
||||
} from 'features/nodes/types/field';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useFluxKontextModels } from 'services/api/hooks/modelsByType';
|
||||
import type { ApiModelConfig } from 'services/api/types';
|
||||
|
||||
import type { FieldComponentProps } from './types';
|
||||
|
||||
const FluxKontextModelFieldInputComponent = (
|
||||
props: FieldComponentProps<FluxKontextModelFieldInputInstance, FluxKontextModelFieldInputTemplate>
|
||||
) => {
|
||||
const { nodeId, field } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [modelConfigs, { isLoading }] = useFluxKontextModels();
|
||||
|
||||
const onChange = useCallback(
|
||||
(value: ApiModelConfig | null) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
fieldFluxKontextModelValueChanged({
|
||||
nodeId,
|
||||
fieldName: field.name,
|
||||
value,
|
||||
})
|
||||
);
|
||||
},
|
||||
[dispatch, field.name, nodeId]
|
||||
);
|
||||
|
||||
return (
|
||||
<ModelFieldCombobox
|
||||
value={field.value}
|
||||
modelConfigs={modelConfigs}
|
||||
isLoadingConfigs={isLoading}
|
||||
onChange={onChange}
|
||||
required={props.fieldTemplate.required}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(FluxKontextModelFieldInputComponent);
|
||||
@@ -19,6 +19,10 @@ import { useGetBatchStatusQuery } from 'services/api/endpoints/queue';
|
||||
import { useGetWorkflowQuery } from 'services/api/endpoints/workflows';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
type FieldIdentiferWithLabel = FieldIdentifier & { label: string | null };
|
||||
type FieldIdentiferWithLabelAndType = FieldIdentiferWithLabel & { type: string };
|
||||
|
||||
export const $isPublishing = atom(false);
|
||||
export const $isInPublishFlow = atom(false);
|
||||
export const $outputNodeId = atom<string | null>(null);
|
||||
export const $isSelectingOutputNode = atom(false);
|
||||
@@ -53,21 +57,26 @@ export const selectFieldIdentifiersWithInvocationTypes = createSelector(
|
||||
selectWorkflowFormNodeFieldFieldIdentifiersDeduped,
|
||||
selectNodesSlice,
|
||||
(fieldIdentifiers, nodes) => {
|
||||
const result: { nodeId: string; fieldName: string; type: string }[] = [];
|
||||
const result: FieldIdentiferWithLabelAndType[] = [];
|
||||
for (const fieldIdentifier of fieldIdentifiers) {
|
||||
const node = nodes.nodes.find((node) => node.id === fieldIdentifier.nodeId);
|
||||
assert(isInvocationNode(node), `Node ${fieldIdentifier.nodeId} not found`);
|
||||
result.push({ nodeId: fieldIdentifier.nodeId, fieldName: fieldIdentifier.fieldName, type: node.data.type });
|
||||
result.push({
|
||||
nodeId: fieldIdentifier.nodeId,
|
||||
fieldName: fieldIdentifier.fieldName,
|
||||
type: node.data.type,
|
||||
label: node.data.inputs[fieldIdentifier.fieldName]?.label ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
);
|
||||
|
||||
export const getPublishInputs = (fieldIdentifiers: (FieldIdentifier & { type: string })[], templates: Templates) => {
|
||||
export const getPublishInputs = (fieldIdentifiers: FieldIdentiferWithLabelAndType[], templates: Templates) => {
|
||||
// Certain field types are not allowed to be input fields on a published workflow
|
||||
const publishable: FieldIdentifier[] = [];
|
||||
const unpublishable: FieldIdentifier[] = [];
|
||||
const publishable: FieldIdentiferWithLabel[] = [];
|
||||
const unpublishable: FieldIdentiferWithLabel[] = [];
|
||||
for (const fieldIdentifier of fieldIdentifiers) {
|
||||
const fieldTemplate = templates[fieldIdentifier.type]?.inputs[fieldIdentifier.fieldName];
|
||||
if (!fieldTemplate) {
|
||||
@@ -121,11 +130,13 @@ const NODE_TYPE_PUBLISH_DENYLIST = [
|
||||
'metadata_to_controlnets',
|
||||
'metadata_to_ip_adapters',
|
||||
'metadata_to_t2i_adapters',
|
||||
'google_imagen3_generate',
|
||||
'google_imagen3_edit',
|
||||
'google_imagen4_generate',
|
||||
'chatgpt_create_image',
|
||||
'chatgpt_edit_image',
|
||||
'google_imagen3_generate_image',
|
||||
'google_imagen3_edit_image',
|
||||
'google_imagen4_generate_image',
|
||||
'chatgpt_4o_generate_image',
|
||||
'chatgpt_4o_edit_image',
|
||||
'flux_kontext_generate_image',
|
||||
'flux_kontext_edit_image',
|
||||
];
|
||||
|
||||
export const selectHasUnpublishableNodes = createSelector(selectNodes, (nodes) => {
|
||||
|
||||
@@ -34,6 +34,7 @@ import type {
|
||||
FieldValue,
|
||||
FloatFieldValue,
|
||||
FloatGeneratorFieldValue,
|
||||
FluxKontextModelFieldValue,
|
||||
FluxReduxModelFieldValue,
|
||||
FluxVAEModelFieldValue,
|
||||
ImageFieldCollectionValue,
|
||||
@@ -75,6 +76,7 @@ import {
|
||||
zFloatFieldCollectionValue,
|
||||
zFloatFieldValue,
|
||||
zFloatGeneratorFieldValue,
|
||||
zFluxKontextModelFieldValue,
|
||||
zFluxReduxModelFieldValue,
|
||||
zFluxVAEModelFieldValue,
|
||||
zImageFieldCollectionValue,
|
||||
@@ -527,6 +529,9 @@ export const nodesSlice = createSlice({
|
||||
fieldChatGPT4oModelValueChanged: (state, action: FieldValueAction<ChatGPT4oModelFieldValue>) => {
|
||||
fieldValueReducer(state, action, zChatGPT4oModelFieldValue);
|
||||
},
|
||||
fieldFluxKontextModelValueChanged: (state, action: FieldValueAction<FluxKontextModelFieldValue>) => {
|
||||
fieldValueReducer(state, action, zFluxKontextModelFieldValue);
|
||||
},
|
||||
fieldEnumModelValueChanged: (state, action: FieldValueAction<EnumFieldValue>) => {
|
||||
fieldValueReducer(state, action, zEnumFieldValue);
|
||||
},
|
||||
@@ -697,6 +702,7 @@ export const {
|
||||
fieldImagen3ModelValueChanged,
|
||||
fieldImagen4ModelValueChanged,
|
||||
fieldChatGPT4oModelValueChanged,
|
||||
fieldFluxKontextModelValueChanged,
|
||||
fieldFloatGeneratorValueChanged,
|
||||
fieldIntegerGeneratorValueChanged,
|
||||
fieldStringGeneratorValueChanged,
|
||||
|
||||
@@ -78,6 +78,7 @@ const zBaseModel = z.enum([
|
||||
'imagen3',
|
||||
'imagen4',
|
||||
'chatgpt-4o',
|
||||
'flux-kontext',
|
||||
]);
|
||||
export type BaseModelType = z.infer<typeof zBaseModel>;
|
||||
export const zMainModelBase = z.enum([
|
||||
@@ -90,6 +91,7 @@ export const zMainModelBase = z.enum([
|
||||
'imagen3',
|
||||
'imagen4',
|
||||
'chatgpt-4o',
|
||||
'flux-kontext',
|
||||
]);
|
||||
export type MainModelBase = z.infer<typeof zMainModelBase>;
|
||||
export const isMainModelBase = (base: unknown): base is MainModelBase => zMainModelBase.safeParse(base).success;
|
||||
|
||||
@@ -260,6 +260,10 @@ const zChatGPT4oModelFieldType = zFieldTypeBase.extend({
|
||||
name: z.literal('ChatGPT4oModelField'),
|
||||
originalType: zStatelessFieldType.optional(),
|
||||
});
|
||||
const zFluxKontextModelFieldType = zFieldTypeBase.extend({
|
||||
name: z.literal('FluxKontextModelField'),
|
||||
originalType: zStatelessFieldType.optional(),
|
||||
});
|
||||
const zSchedulerFieldType = zFieldTypeBase.extend({
|
||||
name: z.literal('SchedulerField'),
|
||||
originalType: zStatelessFieldType.optional(),
|
||||
@@ -313,6 +317,7 @@ const zStatefulFieldType = z.union([
|
||||
zImagen3ModelFieldType,
|
||||
zImagen4ModelFieldType,
|
||||
zChatGPT4oModelFieldType,
|
||||
zFluxKontextModelFieldType,
|
||||
zColorFieldType,
|
||||
zSchedulerFieldType,
|
||||
zFloatGeneratorFieldType,
|
||||
@@ -354,6 +359,7 @@ const modelFieldTypeNames = [
|
||||
zImagen3ModelFieldType.shape.name.value,
|
||||
zImagen4ModelFieldType.shape.name.value,
|
||||
zChatGPT4oModelFieldType.shape.name.value,
|
||||
zFluxKontextModelFieldType.shape.name.value,
|
||||
// Stateless model fields
|
||||
'UNetField',
|
||||
'VAEField',
|
||||
@@ -1231,6 +1237,24 @@ export const isImagen4ModelFieldInputTemplate =
|
||||
buildTemplateTypeGuard<Imagen4ModelFieldInputTemplate>('Imagen4ModelField');
|
||||
// #endregion
|
||||
|
||||
// #region FluxKontextModelField
|
||||
export const zFluxKontextModelFieldValue = zModelIdentifierField.optional();
|
||||
const zFluxKontextModelFieldInputInstance = zFieldInputInstanceBase.extend({
|
||||
value: zFluxKontextModelFieldValue,
|
||||
});
|
||||
const zFluxKontextModelFieldInputTemplate = zFieldInputTemplateBase.extend({
|
||||
type: zFluxKontextModelFieldType,
|
||||
originalType: zFieldType.optional(),
|
||||
default: zFluxKontextModelFieldValue,
|
||||
});
|
||||
export type FluxKontextModelFieldValue = z.infer<typeof zFluxKontextModelFieldValue>;
|
||||
export type FluxKontextModelFieldInputInstance = z.infer<typeof zFluxKontextModelFieldInputInstance>;
|
||||
export type FluxKontextModelFieldInputTemplate = z.infer<typeof zFluxKontextModelFieldInputTemplate>;
|
||||
export const isFluxKontextModelFieldInputInstance = buildInstanceTypeGuard(zFluxKontextModelFieldInputInstance);
|
||||
export const isFluxKontextModelFieldInputTemplate =
|
||||
buildTemplateTypeGuard<FluxKontextModelFieldInputTemplate>('FluxKontextModelField');
|
||||
// #endregion
|
||||
|
||||
// #region ChatGPT4oModelField
|
||||
export const zChatGPT4oModelFieldValue = zModelIdentifierField.optional();
|
||||
const zChatGPT4oModelFieldInputInstance = zFieldInputInstanceBase.extend({
|
||||
@@ -1882,6 +1906,7 @@ export const zStatefulFieldValue = z.union([
|
||||
zFluxReduxModelFieldValue,
|
||||
zImagen3ModelFieldValue,
|
||||
zImagen4ModelFieldValue,
|
||||
zFluxKontextModelFieldValue,
|
||||
zChatGPT4oModelFieldValue,
|
||||
zColorFieldValue,
|
||||
zSchedulerFieldValue,
|
||||
@@ -1976,6 +2001,7 @@ const zStatefulFieldInputTemplate = z.union([
|
||||
zImagen3ModelFieldInputTemplate,
|
||||
zImagen4ModelFieldInputTemplate,
|
||||
zChatGPT4oModelFieldInputTemplate,
|
||||
zFluxKontextModelFieldInputTemplate,
|
||||
zColorFieldInputTemplate,
|
||||
zSchedulerFieldInputTemplate,
|
||||
zStatelessFieldInputTemplate,
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice';
|
||||
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
|
||||
import { isFluxKontextReferenceImageConfig } from 'features/controlLayers/store/types';
|
||||
import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/validators';
|
||||
import type { ImageField } from 'features/nodes/types/common';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import {
|
||||
CANVAS_OUTPUT_PREFIX,
|
||||
getBoardField,
|
||||
selectPresetModifiedPrompts,
|
||||
} from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import { type GraphBuilderReturn, UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
|
||||
import { t } from 'i18next';
|
||||
import { selectMainModelConfig } from 'services/api/endpoints/models';
|
||||
import type { Equals } from 'tsafe';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
export const buildFluxKontextGraph = async (state: RootState, manager: CanvasManager): Promise<GraphBuilderReturn> => {
|
||||
const generationMode = await manager.compositor.getGenerationMode();
|
||||
|
||||
if (generationMode !== 'txt2img') {
|
||||
throw new UnsupportedGenerationModeError(t('toast.fluxKontextIncompatibleGenerationMode'));
|
||||
}
|
||||
|
||||
log.debug({ generationMode }, 'Building Flux Kontext graph');
|
||||
|
||||
const model = selectMainModelConfig(state);
|
||||
|
||||
const canvas = selectCanvasSlice(state);
|
||||
const canvasSettings = selectCanvasSettingsSlice(state);
|
||||
|
||||
const { bbox } = canvas;
|
||||
const { positivePrompt } = selectPresetModifiedPrompts(state);
|
||||
|
||||
assert(model, 'No model found in state');
|
||||
assert(model.base === 'flux-kontext', 'Model is not a Flux Kontext model');
|
||||
|
||||
const is_intermediate = canvasSettings.sendToCanvas;
|
||||
const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state);
|
||||
|
||||
const validRefImages = canvas.referenceImages.entities
|
||||
.filter((entity) => entity.isEnabled)
|
||||
.filter((entity) => isFluxKontextReferenceImageConfig(entity.ipAdapter))
|
||||
.filter((entity) => getGlobalReferenceImageWarnings(entity, model).length === 0);
|
||||
|
||||
let input_image: ImageField | undefined = undefined;
|
||||
|
||||
if (validRefImages[0]) {
|
||||
assert(validRefImages.length === 1, 'Flux Kontext can have at most one reference image');
|
||||
|
||||
assert(validRefImages[0].ipAdapter.image, 'Image is required for reference image');
|
||||
input_image = {
|
||||
image_name: validRefImages[0].ipAdapter.image.image_name,
|
||||
};
|
||||
}
|
||||
|
||||
if (generationMode === 'txt2img') {
|
||||
const g = new Graph(getPrefixedId('flux_kontext_txt2img_graph'));
|
||||
const fluxKontextImage = g.addNode({
|
||||
// @ts-expect-error: These nodes are not available in the OSS application
|
||||
type: input_image ? 'flux_kontext_edit_image' : 'flux_kontext_generate_image',
|
||||
id: getPrefixedId(CANVAS_OUTPUT_PREFIX),
|
||||
model: zModelIdentifierField.parse(model),
|
||||
positive_prompt: positivePrompt,
|
||||
aspect_ratio: bbox.aspectRatio.id,
|
||||
use_cache: false,
|
||||
is_intermediate,
|
||||
board,
|
||||
input_image,
|
||||
prompt_upsampling: true,
|
||||
});
|
||||
g.upsertMetadata({
|
||||
positive_prompt: positivePrompt,
|
||||
model: Graph.getModelMetadataField(model),
|
||||
width: bbox.rect.width,
|
||||
height: bbox.rect.height,
|
||||
});
|
||||
return {
|
||||
g,
|
||||
positivePromptFieldIdentifier: { nodeId: fluxKontextImage.id, fieldName: 'positive_prompt' },
|
||||
};
|
||||
}
|
||||
|
||||
assert<Equals<typeof generationMode, never>>(false, 'Invalid generation mode for Flux Kontext');
|
||||
};
|
||||
@@ -36,6 +36,7 @@ const FIELD_VALUE_FALLBACK_MAP: Record<StatefulFieldType['name'], FieldValue> =
|
||||
Imagen3ModelField: undefined,
|
||||
Imagen4ModelField: undefined,
|
||||
ChatGPT4oModelField: undefined,
|
||||
FluxKontextModelField: undefined,
|
||||
FloatGeneratorField: undefined,
|
||||
IntegerGeneratorField: undefined,
|
||||
StringGeneratorField: undefined,
|
||||
|
||||
@@ -16,6 +16,7 @@ import type {
|
||||
FloatFieldCollectionInputTemplate,
|
||||
FloatFieldInputTemplate,
|
||||
FloatGeneratorFieldInputTemplate,
|
||||
FluxKontextModelFieldInputTemplate,
|
||||
FluxMainModelFieldInputTemplate,
|
||||
FluxReduxModelFieldInputTemplate,
|
||||
FluxVAEModelFieldInputTemplate,
|
||||
@@ -613,6 +614,20 @@ const buildImagen4ModelFieldInputTemplate: FieldInputTemplateBuilder<Imagen4Mode
|
||||
};
|
||||
return template;
|
||||
};
|
||||
|
||||
const buildFluxKontextModelFieldInputTemplate: FieldInputTemplateBuilder<FluxKontextModelFieldInputTemplate> = ({
|
||||
schemaObject,
|
||||
baseField,
|
||||
fieldType,
|
||||
}) => {
|
||||
const template: FluxKontextModelFieldInputTemplate = {
|
||||
...baseField,
|
||||
type: fieldType,
|
||||
default: schemaObject.default ?? undefined,
|
||||
};
|
||||
return template;
|
||||
};
|
||||
|
||||
const buildChatGPT4oModelFieldInputTemplate: FieldInputTemplateBuilder<ChatGPT4oModelFieldInputTemplate> = ({
|
||||
schemaObject,
|
||||
baseField,
|
||||
@@ -835,6 +850,7 @@ export const TEMPLATE_BUILDER_MAP: Record<StatefulFieldType['name'], FieldInputT
|
||||
Imagen3ModelField: buildImagen3ModelFieldInputTemplate,
|
||||
Imagen4ModelField: buildImagen4ModelFieldInputTemplate,
|
||||
ChatGPT4oModelField: buildChatGPT4oModelFieldInputTemplate,
|
||||
FluxKontextModelField: buildFluxKontextModelFieldInputTemplate,
|
||||
FloatGeneratorField: buildFloatGeneratorFieldInputTemplate,
|
||||
IntegerGeneratorField: buildIntegerGeneratorFieldInputTemplate,
|
||||
StringGeneratorField: buildStringGeneratorFieldInputTemplate,
|
||||
|
||||
@@ -3,12 +3,18 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import { bboxAspectRatioIdChanged } from 'features/controlLayers/store/canvasSlice';
|
||||
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { selectIsChatGTP4o, selectIsImagen3, selectIsImagen4 } from 'features/controlLayers/store/paramsSlice';
|
||||
import {
|
||||
selectIsChatGTP4o,
|
||||
selectIsFluxKontext,
|
||||
selectIsImagen3,
|
||||
selectIsImagen4,
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectAspectRatioID } from 'features/controlLayers/store/selectors';
|
||||
import {
|
||||
isAspectRatioID,
|
||||
zAspectRatioID,
|
||||
zChatGPT4oAspectRatioID,
|
||||
zFluxKontextAspectRatioID,
|
||||
zImagen3AspectRatioID,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import type { ChangeEventHandler } from 'react';
|
||||
@@ -24,6 +30,7 @@ export const BboxAspectRatioSelect = memo(() => {
|
||||
const isImagen3 = useAppSelector(selectIsImagen3);
|
||||
const isChatGPT4o = useAppSelector(selectIsChatGTP4o);
|
||||
const isImagen4 = useAppSelector(selectIsImagen4);
|
||||
const isFluxKontext = useAppSelector(selectIsFluxKontext);
|
||||
const options = useMemo(() => {
|
||||
// Imagen3 and ChatGPT4o have different aspect ratio options, and do not support freeform sizes
|
||||
if (isImagen3 || isImagen4) {
|
||||
@@ -32,9 +39,12 @@ export const BboxAspectRatioSelect = memo(() => {
|
||||
if (isChatGPT4o) {
|
||||
return zChatGPT4oAspectRatioID.options;
|
||||
}
|
||||
if (isFluxKontext) {
|
||||
return zFluxKontextAspectRatioID.options;
|
||||
}
|
||||
// All other models
|
||||
return zAspectRatioID.options;
|
||||
}, [isImagen3, isChatGPT4o, isImagen4]);
|
||||
}, [isImagen3, isChatGPT4o, isImagen4, isFluxKontext]);
|
||||
|
||||
const onChange = useCallback<ChangeEventHandler<HTMLSelectElement>>(
|
||||
(e) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { AspectRatioID } from 'features/controlLayers/store/types';
|
||||
|
||||
export const ASPECT_RATIO_MAP: Record<Exclude<AspectRatioID, 'Free'>, { ratio: number; inverseID: AspectRatioID }> = {
|
||||
'21:9': { ratio: 21 / 9, inverseID: '9:21' },
|
||||
'16:9': { ratio: 16 / 9, inverseID: '9:16' },
|
||||
'3:2': { ratio: 3 / 2, inverseID: '2:3' },
|
||||
'4:3': { ratio: 4 / 3, inverseID: '4:3' },
|
||||
@@ -8,4 +9,5 @@ export const ASPECT_RATIO_MAP: Record<Exclude<AspectRatioID, 'Free'>, { ratio: n
|
||||
'3:4': { ratio: 3 / 4, inverseID: '4:3' },
|
||||
'2:3': { ratio: 2 / 3, inverseID: '3:2' },
|
||||
'9:16': { ratio: 9 / 16, inverseID: '16:9' },
|
||||
'9:21': { ratio: 9 / 21, inverseID: '21:9' },
|
||||
};
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectIsChatGTP4o, selectIsImagen3, selectIsImagen4 } from 'features/controlLayers/store/paramsSlice';
|
||||
import {
|
||||
selectIsChatGTP4o,
|
||||
selectIsFluxKontext,
|
||||
selectIsImagen3,
|
||||
selectIsImagen4,
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
|
||||
export const useIsApiModel = () => {
|
||||
const isImagen3 = useAppSelector(selectIsImagen3);
|
||||
const isImagen4 = useAppSelector(selectIsImagen4);
|
||||
const isChatGPT4o = useAppSelector(selectIsChatGTP4o);
|
||||
const isFluxKontext = useAppSelector(selectIsFluxKontext);
|
||||
|
||||
return isImagen3 || isImagen4 || isChatGPT4o;
|
||||
return isImagen3 || isImagen4 || isChatGPT4o || isFluxKontext;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ export const MODEL_TYPE_MAP: Record<BaseModelType, string> = {
|
||||
imagen3: 'Imagen3',
|
||||
imagen4: 'Imagen4',
|
||||
'chatgpt-4o': 'ChatGPT 4o',
|
||||
'flux-kontext': 'Flux Kontext',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,7 @@ export const MODEL_TYPE_SHORT_MAP: Record<BaseModelType, string> = {
|
||||
imagen3: 'Imagen3',
|
||||
imagen4: 'Imagen4',
|
||||
'chatgpt-4o': 'ChatGPT 4o',
|
||||
'flux-kontext': 'Flux Kontext',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -83,6 +85,10 @@ export const CLIP_SKIP_MAP: Record<BaseModelType, { maxClip: number; markers: nu
|
||||
maxClip: 0,
|
||||
markers: [],
|
||||
},
|
||||
'flux-kontext': {
|
||||
maxClip: 0,
|
||||
markers: [],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -124,4 +130,4 @@ export const SCHEDULER_OPTIONS: ComboboxOption[] = [
|
||||
/**
|
||||
* List of base models that make API requests
|
||||
*/
|
||||
export const API_BASE_MODELS = ['imagen3', 'imagen4', 'chatgpt-4o'];
|
||||
export const API_BASE_MODELS = ['imagen3', 'imagen4', 'chatgpt-4o', 'flux-kontext'];
|
||||
|
||||
@@ -21,6 +21,7 @@ export const getOptimalDimension = (base?: BaseModelType | null): number => {
|
||||
case 'imagen3':
|
||||
case 'imagen4':
|
||||
case 'chatgpt-4o':
|
||||
case 'flux-kontext':
|
||||
default:
|
||||
return 1024;
|
||||
}
|
||||
@@ -81,6 +82,7 @@ export const getGridSize = (base?: BaseModelType | null): number => {
|
||||
case 'sdxl':
|
||||
case 'imagen3':
|
||||
case 'chatgpt-4o':
|
||||
case 'flux-kontext':
|
||||
default:
|
||||
return 8;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { buildWorkflowWithValidation } from 'features/nodes/util/workflow/buildW
|
||||
import { groupBy } from 'lodash-es';
|
||||
import { useCallback } from 'react';
|
||||
import { enqueueMutationFixedCacheKeyOptions, queueApi } from 'services/api/endpoints/queue';
|
||||
import type { Batch, EnqueueBatchArg } from 'services/api/types';
|
||||
import type { Batch, EnqueueBatchArg, S } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const enqueueRequestedWorkflows = createAction('app/enqueueRequestedWorkflows');
|
||||
@@ -106,12 +106,13 @@ export const useEnqueueWorkflows = () => {
|
||||
// Derive the input fields from the builder's selected node field elements
|
||||
const fieldIdentifiers = selectFieldIdentifiersWithInvocationTypes(state);
|
||||
const inputs = getPublishInputs(fieldIdentifiers, templates);
|
||||
const api_input_fields = inputs.publishable.map(({ nodeId, fieldName }) => {
|
||||
const api_input_fields = inputs.publishable.map(({ nodeId, fieldName, label }) => {
|
||||
return {
|
||||
kind: 'input',
|
||||
node_id: nodeId,
|
||||
field_name: fieldName,
|
||||
} as const;
|
||||
user_label: label,
|
||||
} satisfies S['FieldIdentifier'];
|
||||
});
|
||||
|
||||
// Derive the output fields from the builder's selected output node
|
||||
@@ -126,7 +127,8 @@ export const useEnqueueWorkflows = () => {
|
||||
kind: 'output',
|
||||
node_id: outputNodeId,
|
||||
field_name: fieldName,
|
||||
} as const;
|
||||
user_label: null,
|
||||
} satisfies S['FieldIdentifier'];
|
||||
});
|
||||
|
||||
assert(nodesState.id, 'Workflow without ID cannot be used for API validation run');
|
||||
|
||||
@@ -516,6 +516,17 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
|
||||
}
|
||||
});
|
||||
|
||||
const enabledGlobalReferenceLayers = canvas.referenceImages.entities.filter(
|
||||
(referenceImage) => referenceImage.isEnabled
|
||||
);
|
||||
|
||||
// Flux Kontext only supports 1x Reference Image at a time.
|
||||
const referenceImageCount = enabledGlobalReferenceLayers.length;
|
||||
|
||||
if (model?.base === 'flux-kontext' && referenceImageCount > 1) {
|
||||
reasons.push({ content: i18n.t('parameters.invoke.fluxKontextMultipleReferenceImages') });
|
||||
}
|
||||
|
||||
canvas.referenceImages.entities
|
||||
.filter((entity) => entity.isEnabled)
|
||||
.forEach((entity, i) => {
|
||||
|
||||
@@ -58,7 +58,7 @@ const AboutModal = ({ children }: AboutModalProps) => {
|
||||
{cloneElement(children, {
|
||||
onClick: onOpen,
|
||||
})}
|
||||
<Modal isOpen={isOpen} onClose={onClose} isCentered size="2xl" useInert={false}>
|
||||
<Modal isOpen={isOpen} onClose={onClose} isCentered size="5xl" useInert={false}>
|
||||
<ModalOverlay />
|
||||
<ModalContent maxH="80vh" h="34rem">
|
||||
<ModalHeader>{t('accessibility.about')}</ModalHeader>
|
||||
@@ -66,7 +66,7 @@ const AboutModal = ({ children }: AboutModalProps) => {
|
||||
<ModalBody display="flex" flexDir="column" gap={4}>
|
||||
<Grid templateColumns="repeat(2, 1fr)" h="full">
|
||||
<GridItem backgroundColor="base.750" borderRadius="base" p="4" h="full">
|
||||
<DataViewer label={t('common.systemInformation')} data={localData} />
|
||||
<DataViewer label={t('common.systemInformation')} data={localData} wrapData={false} />
|
||||
</GridItem>
|
||||
<GridItem>
|
||||
<Flex flexDir="column" gap={3} justifyContent="center" alignItems="center" h="full">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { $openAPISchemaUrl } from 'app/store/nanostores/openAPISchemaUrl';
|
||||
import type { OpenAPIV3_1 } from 'openapi-types';
|
||||
import type { paths } from 'services/api/schema';
|
||||
import type { AppConfig, AppDependencyVersions, AppVersion } from 'services/api/types';
|
||||
import type { AppConfig, AppVersion } from 'services/api/types';
|
||||
|
||||
import { api, buildV1Url } from '..';
|
||||
|
||||
@@ -22,7 +22,10 @@ export const appInfoApi = api.injectEndpoints({
|
||||
}),
|
||||
providesTags: ['FetchOnReconnect'],
|
||||
}),
|
||||
getAppDeps: build.query<AppDependencyVersions, void>({
|
||||
getAppDeps: build.query<
|
||||
paths['/api/v1/app/app_deps']['get']['responses']['200']['content']['application/json'],
|
||||
void
|
||||
>({
|
||||
query: () => ({
|
||||
url: buildAppInfoUrl('app_deps'),
|
||||
method: 'GET',
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
isControlLayerModelConfig,
|
||||
isControlLoRAModelConfig,
|
||||
isControlNetModelConfig,
|
||||
isFluxKontextModelConfig,
|
||||
isFluxMainModelModelConfig,
|
||||
isFluxReduxModelConfig,
|
||||
isFluxVAEModelConfig,
|
||||
@@ -85,7 +86,11 @@ export const useCLIPVisionModels = buildModelsHook(isCLIPVisionModelConfig);
|
||||
export const useSigLipModels = buildModelsHook(isSigLipModelConfig);
|
||||
export const useFluxReduxModels = buildModelsHook(isFluxReduxModelConfig);
|
||||
export const useGlobalReferenceImageModels = buildModelsHook(
|
||||
(config) => isIPAdapterModelConfig(config) || isFluxReduxModelConfig(config) || isChatGPT4oModelConfig(config)
|
||||
(config) =>
|
||||
isIPAdapterModelConfig(config) ||
|
||||
isFluxReduxModelConfig(config) ||
|
||||
isChatGPT4oModelConfig(config) ||
|
||||
isFluxKontextModelConfig(config)
|
||||
);
|
||||
export const useRegionalReferenceImageModels = buildModelsHook(
|
||||
(config) => isIPAdapterModelConfig(config) || isFluxReduxModelConfig(config)
|
||||
@@ -94,6 +99,7 @@ export const useLLaVAModels = buildModelsHook(isLLaVAModelConfig);
|
||||
export const useImagen3Models = buildModelsHook(isImagen3ModelConfig);
|
||||
export const useImagen4Models = buildModelsHook(isImagen4ModelConfig);
|
||||
export const useChatGPT4oModels = buildModelsHook(isChatGPT4oModelConfig);
|
||||
export const useFluxKontextModels = buildModelsHook(isFluxKontextModelConfig);
|
||||
|
||||
// const buildModelsSelector =
|
||||
// <T extends AnyModelConfig>(typeGuard: (config: AnyModelConfig) => config is T): Selector<RootState, T[]> =>
|
||||
|
||||
@@ -1925,77 +1925,6 @@ export type components = {
|
||||
*/
|
||||
watermarking_methods: string[];
|
||||
};
|
||||
/**
|
||||
* AppDependencyVersions
|
||||
* @description App depencency Versions Response
|
||||
*/
|
||||
AppDependencyVersions: {
|
||||
/**
|
||||
* Accelerate
|
||||
* @description accelerate version
|
||||
*/
|
||||
accelerate: string;
|
||||
/**
|
||||
* Compel
|
||||
* @description compel version
|
||||
*/
|
||||
compel: string;
|
||||
/**
|
||||
* Cuda
|
||||
* @description CUDA version
|
||||
*/
|
||||
cuda: string | null;
|
||||
/**
|
||||
* Diffusers
|
||||
* @description diffusers version
|
||||
*/
|
||||
diffusers: string;
|
||||
/**
|
||||
* Numpy
|
||||
* @description Numpy version
|
||||
*/
|
||||
numpy: string;
|
||||
/**
|
||||
* Opencv
|
||||
* @description OpenCV version
|
||||
*/
|
||||
opencv: string;
|
||||
/**
|
||||
* Onnx
|
||||
* @description ONNX version
|
||||
*/
|
||||
onnx: string;
|
||||
/**
|
||||
* Pillow
|
||||
* @description Pillow (PIL) version
|
||||
*/
|
||||
pillow: string;
|
||||
/**
|
||||
* Python
|
||||
* @description Python version
|
||||
*/
|
||||
python: string;
|
||||
/**
|
||||
* Torch
|
||||
* @description PyTorch version
|
||||
*/
|
||||
torch: string;
|
||||
/**
|
||||
* Torchvision
|
||||
* @description PyTorch Vision version
|
||||
*/
|
||||
torchvision: string;
|
||||
/**
|
||||
* Transformers
|
||||
* @description transformers version
|
||||
*/
|
||||
transformers: string;
|
||||
/**
|
||||
* Xformers
|
||||
* @description xformers version
|
||||
*/
|
||||
xformers: string | null;
|
||||
};
|
||||
/**
|
||||
* AppVersion
|
||||
* @description App Version Response
|
||||
@@ -2146,7 +2075,7 @@ export type components = {
|
||||
* @description Base model type.
|
||||
* @enum {string}
|
||||
*/
|
||||
BaseModelType: "any" | "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "cogview4" | "imagen3" | "imagen4" | "chatgpt-4o";
|
||||
BaseModelType: "any" | "sd-1" | "sd-2" | "sd-3" | "sdxl" | "sdxl-refiner" | "flux" | "cogview4" | "imagen3" | "imagen4" | "chatgpt-4o" | "flux-kontext";
|
||||
/** Batch */
|
||||
Batch: {
|
||||
/**
|
||||
@@ -7067,6 +6996,11 @@ export type components = {
|
||||
* @description The name of the field
|
||||
*/
|
||||
field_name: string;
|
||||
/**
|
||||
* User Label
|
||||
* @description The user label of the field, if any
|
||||
*/
|
||||
user_label: string | null;
|
||||
};
|
||||
/**
|
||||
* FieldKind
|
||||
@@ -21190,7 +21124,7 @@ export type components = {
|
||||
* used, and the type will be ignored. They are included here for backwards compatibility.
|
||||
* @enum {string}
|
||||
*/
|
||||
UIType: "MainModelField" | "CogView4MainModelField" | "FluxMainModelField" | "SD3MainModelField" | "SDXLMainModelField" | "SDXLRefinerModelField" | "ONNXModelField" | "VAEModelField" | "FluxVAEModelField" | "LoRAModelField" | "ControlNetModelField" | "IPAdapterModelField" | "T2IAdapterModelField" | "T5EncoderModelField" | "CLIPEmbedModelField" | "CLIPLEmbedModelField" | "CLIPGEmbedModelField" | "SpandrelImageToImageModelField" | "ControlLoRAModelField" | "SigLipModelField" | "FluxReduxModelField" | "LLaVAModelField" | "Imagen3ModelField" | "Imagen4ModelField" | "ChatGPT4oModelField" | "SchedulerField" | "AnyField" | "CollectionField" | "CollectionItemField" | "DEPRECATED_Boolean" | "DEPRECATED_Color" | "DEPRECATED_Conditioning" | "DEPRECATED_Control" | "DEPRECATED_Float" | "DEPRECATED_Image" | "DEPRECATED_Integer" | "DEPRECATED_Latents" | "DEPRECATED_String" | "DEPRECATED_BooleanCollection" | "DEPRECATED_ColorCollection" | "DEPRECATED_ConditioningCollection" | "DEPRECATED_ControlCollection" | "DEPRECATED_FloatCollection" | "DEPRECATED_ImageCollection" | "DEPRECATED_IntegerCollection" | "DEPRECATED_LatentsCollection" | "DEPRECATED_StringCollection" | "DEPRECATED_BooleanPolymorphic" | "DEPRECATED_ColorPolymorphic" | "DEPRECATED_ConditioningPolymorphic" | "DEPRECATED_ControlPolymorphic" | "DEPRECATED_FloatPolymorphic" | "DEPRECATED_ImagePolymorphic" | "DEPRECATED_IntegerPolymorphic" | "DEPRECATED_LatentsPolymorphic" | "DEPRECATED_StringPolymorphic" | "DEPRECATED_UNet" | "DEPRECATED_Vae" | "DEPRECATED_CLIP" | "DEPRECATED_Collection" | "DEPRECATED_CollectionItem" | "DEPRECATED_Enum" | "DEPRECATED_WorkflowField" | "DEPRECATED_IsIntermediate" | "DEPRECATED_BoardField" | "DEPRECATED_MetadataItem" | "DEPRECATED_MetadataItemCollection" | "DEPRECATED_MetadataItemPolymorphic" | "DEPRECATED_MetadataDict";
|
||||
UIType: "MainModelField" | "CogView4MainModelField" | "FluxMainModelField" | "SD3MainModelField" | "SDXLMainModelField" | "SDXLRefinerModelField" | "ONNXModelField" | "VAEModelField" | "FluxVAEModelField" | "LoRAModelField" | "ControlNetModelField" | "IPAdapterModelField" | "T2IAdapterModelField" | "T5EncoderModelField" | "CLIPEmbedModelField" | "CLIPLEmbedModelField" | "CLIPGEmbedModelField" | "SpandrelImageToImageModelField" | "ControlLoRAModelField" | "SigLipModelField" | "FluxReduxModelField" | "LLaVAModelField" | "Imagen3ModelField" | "Imagen4ModelField" | "ChatGPT4oModelField" | "FluxKontextModelField" | "SchedulerField" | "AnyField" | "CollectionField" | "CollectionItemField" | "DEPRECATED_Boolean" | "DEPRECATED_Color" | "DEPRECATED_Conditioning" | "DEPRECATED_Control" | "DEPRECATED_Float" | "DEPRECATED_Image" | "DEPRECATED_Integer" | "DEPRECATED_Latents" | "DEPRECATED_String" | "DEPRECATED_BooleanCollection" | "DEPRECATED_ColorCollection" | "DEPRECATED_ConditioningCollection" | "DEPRECATED_ControlCollection" | "DEPRECATED_FloatCollection" | "DEPRECATED_ImageCollection" | "DEPRECATED_IntegerCollection" | "DEPRECATED_LatentsCollection" | "DEPRECATED_StringCollection" | "DEPRECATED_BooleanPolymorphic" | "DEPRECATED_ColorPolymorphic" | "DEPRECATED_ConditioningPolymorphic" | "DEPRECATED_ControlPolymorphic" | "DEPRECATED_FloatPolymorphic" | "DEPRECATED_ImagePolymorphic" | "DEPRECATED_IntegerPolymorphic" | "DEPRECATED_LatentsPolymorphic" | "DEPRECATED_StringPolymorphic" | "DEPRECATED_UNet" | "DEPRECATED_Vae" | "DEPRECATED_CLIP" | "DEPRECATED_Collection" | "DEPRECATED_CollectionItem" | "DEPRECATED_Enum" | "DEPRECATED_WorkflowField" | "DEPRECATED_IsIntermediate" | "DEPRECATED_BoardField" | "DEPRECATED_MetadataItem" | "DEPRECATED_MetadataItemCollection" | "DEPRECATED_MetadataItemPolymorphic" | "DEPRECATED_MetadataDict";
|
||||
/** UNetField */
|
||||
UNetField: {
|
||||
/** @description Info to load unet submodel */
|
||||
@@ -24226,7 +24160,9 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["AppDependencyVersions"];
|
||||
"application/json": {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -31,7 +31,6 @@ export type InvocationJSONSchemaExtra = S['UIConfigBase'];
|
||||
// App Info
|
||||
export type AppVersion = S['AppVersion'];
|
||||
export type AppConfig = S['AppConfig'];
|
||||
export type AppDependencyVersions = S['AppDependencyVersions'];
|
||||
|
||||
// Images
|
||||
export type ImageDTO = S['ImageDTO'];
|
||||
@@ -241,6 +240,10 @@ export const isImagen4ModelConfig = (config: AnyModelConfig): config is ApiModel
|
||||
return config.type === 'main' && config.base === 'imagen4';
|
||||
};
|
||||
|
||||
export const isFluxKontextModelConfig = (config: AnyModelConfig): config is ApiModelConfig => {
|
||||
return config.type === 'main' && config.base === 'flux-kontext';
|
||||
};
|
||||
|
||||
export const isNonRefinerMainModelConfig = (config: AnyModelConfig): config is MainModelConfig => {
|
||||
return config.type === 'main' && config.base !== 'sdxl-refiner';
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "5.13.0rc2"
|
||||
__version__ = "5.14.0"
|
||||
|
||||
@@ -109,6 +109,12 @@ dependencies = [
|
||||
"humanize==4.12.1",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
# Prevent opencv-python from ever being chosen during dependency resolution.
|
||||
# This prevents conflicts with opencv-contrib-python, which Invoke requires.
|
||||
override-dependencies = ["opencv-python; sys_platform=='never'"]
|
||||
|
||||
|
||||
[project.scripts]
|
||||
"invokeai-web" = "invokeai.app.run_app:run_app"
|
||||
|
||||
|
||||
15
uv.lock
generated
15
uv.lock
generated
@@ -13,6 +13,9 @@ resolution-markers = [
|
||||
"(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')",
|
||||
]
|
||||
|
||||
[manifest]
|
||||
overrides = [{ name = "opencv-python", marker = "sys_platform == 'never'" }]
|
||||
|
||||
[[package]]
|
||||
name = "absl-py"
|
||||
version = "2.2.1"
|
||||
@@ -948,7 +951,7 @@ version = "0.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy" },
|
||||
{ name = "opencv-python" },
|
||||
{ name = "opencv-python", marker = "sys_platform == 'never'" },
|
||||
{ name = "pillow" },
|
||||
{ name = "pywavelets" },
|
||||
{ name = "torch" },
|
||||
@@ -2043,17 +2046,9 @@ name = "opencv-python"
|
||||
version = "4.9.0.80"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy" },
|
||||
{ name = "numpy", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/72/da7c69a3542071bf1e8f65336721b8b2659194425438d988f79bc14ed9cc/opencv-python-4.9.0.80.tar.gz", hash = "sha256:1a9f0e6267de3a1a1db0c54213d022c7c8b5b9ca4b580e80bdc58516c922c9e1", size = 92896686 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/35/69/b657974ddcbba54d59d7d62b01e60a8b815e35f415b996e4d355be0ac7b4/opencv_python-4.9.0.80-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:7e5f7aa4486651a6ebfa8ed4b594b65bd2d2f41beeb4241a3e4b1b85acbbbadb", size = 55689340 },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/df/b56175c3fb5bc058774bdcf35f5a71cf9c3c5b909f98a1c688eb71cd3b1f/opencv_python-4.9.0.80-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71dfb9555ccccdd77305fc3dcca5897fbf0cf28b297c51ee55e079c065d812a3", size = 35354525 },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/00/2adf376707c7965bb4569f28f73fafe303c404d01047b10e3b52761be086/opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b34a52e9da36dda8c151c6394aed602e4b17fa041df0b9f5b93ae10b0fcca2a", size = 41289855 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/64/7fdfb9386511cd6805451e012c537073a79a958a58795c4e602e538c388c/opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4088cab82b66a3b37ffc452976b14a3c599269c247895ae9ceb4066d8188a57", size = 62208946 },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/9e/db1c2d56c04b97981c06663384f45f28950a73d9acf840c4006d60d0a1ff/opencv_python-4.9.0.80-cp37-abi3-win32.whl", hash = "sha256:dcf000c36dd1651118a2462257e3a9e76db789a78432e1f303c7bac54f63ef6c", size = 28546907 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/ec/9dabb6a9abfdebb3c45b0cc52dec901caafef2b2c7e7d6a839ed86d81e91/opencv_python-4.9.0.80-cp37-abi3-win_amd64.whl", hash = "sha256:3f16f08e02b2a2da44259c7cc712e779eff1dd8b55fdb0323e8cab09548086c0", size = 38624911 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opt-einsum"
|
||||
|
||||
Reference in New Issue
Block a user