InvokeAI 6.9.0rc3__py3-none-any.whl → 6.10.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- invokeai/app/api/dependencies.py +2 -0
- invokeai/app/api/routers/model_manager.py +91 -2
- invokeai/app/api/routers/workflows.py +9 -0
- invokeai/app/invocations/fields.py +19 -0
- invokeai/app/invocations/image_to_latents.py +23 -5
- invokeai/app/invocations/latents_to_image.py +2 -25
- invokeai/app/invocations/metadata.py +9 -1
- invokeai/app/invocations/model.py +8 -0
- invokeai/app/invocations/primitives.py +12 -0
- invokeai/app/invocations/prompt_template.py +57 -0
- invokeai/app/invocations/z_image_control.py +112 -0
- invokeai/app/invocations/z_image_denoise.py +610 -0
- invokeai/app/invocations/z_image_image_to_latents.py +102 -0
- invokeai/app/invocations/z_image_latents_to_image.py +103 -0
- invokeai/app/invocations/z_image_lora_loader.py +153 -0
- invokeai/app/invocations/z_image_model_loader.py +135 -0
- invokeai/app/invocations/z_image_text_encoder.py +197 -0
- invokeai/app/services/model_install/model_install_common.py +14 -1
- invokeai/app/services/model_install/model_install_default.py +119 -19
- invokeai/app/services/model_records/model_records_base.py +12 -0
- invokeai/app/services/model_records/model_records_sql.py +17 -0
- invokeai/app/services/shared/graph.py +132 -77
- invokeai/app/services/workflow_records/workflow_records_base.py +8 -0
- invokeai/app/services/workflow_records/workflow_records_sqlite.py +42 -0
- invokeai/app/util/step_callback.py +3 -0
- invokeai/backend/model_manager/configs/controlnet.py +47 -1
- invokeai/backend/model_manager/configs/factory.py +26 -1
- invokeai/backend/model_manager/configs/lora.py +43 -1
- invokeai/backend/model_manager/configs/main.py +113 -0
- invokeai/backend/model_manager/configs/qwen3_encoder.py +156 -0
- invokeai/backend/model_manager/load/model_cache/torch_module_autocast/custom_modules/custom_diffusers_rms_norm.py +40 -0
- invokeai/backend/model_manager/load/model_cache/torch_module_autocast/custom_modules/custom_layer_norm.py +25 -0
- invokeai/backend/model_manager/load/model_cache/torch_module_autocast/torch_module_autocast.py +11 -2
- invokeai/backend/model_manager/load/model_loaders/lora.py +11 -0
- invokeai/backend/model_manager/load/model_loaders/z_image.py +935 -0
- invokeai/backend/model_manager/load/model_util.py +6 -1
- invokeai/backend/model_manager/metadata/metadata_base.py +12 -5
- invokeai/backend/model_manager/model_on_disk.py +3 -0
- invokeai/backend/model_manager/starter_models.py +70 -0
- invokeai/backend/model_manager/taxonomy.py +5 -0
- invokeai/backend/model_manager/util/select_hf_files.py +23 -8
- invokeai/backend/patches/layer_patcher.py +34 -16
- invokeai/backend/patches/layers/lora_layer_base.py +2 -1
- invokeai/backend/patches/lora_conversions/flux_aitoolkit_lora_conversion_utils.py +17 -2
- invokeai/backend/patches/lora_conversions/flux_xlabs_lora_conversion_utils.py +92 -0
- invokeai/backend/patches/lora_conversions/formats.py +5 -0
- invokeai/backend/patches/lora_conversions/z_image_lora_constants.py +8 -0
- invokeai/backend/patches/lora_conversions/z_image_lora_conversion_utils.py +155 -0
- invokeai/backend/quantization/gguf/ggml_tensor.py +27 -4
- invokeai/backend/quantization/gguf/loaders.py +47 -12
- invokeai/backend/stable_diffusion/diffusion/conditioning_data.py +13 -0
- invokeai/backend/util/devices.py +25 -0
- invokeai/backend/util/hotfixes.py +2 -2
- invokeai/backend/z_image/__init__.py +16 -0
- invokeai/backend/z_image/extensions/__init__.py +1 -0
- invokeai/backend/z_image/extensions/regional_prompting_extension.py +207 -0
- invokeai/backend/z_image/text_conditioning.py +74 -0
- invokeai/backend/z_image/z_image_control_adapter.py +238 -0
- invokeai/backend/z_image/z_image_control_transformer.py +643 -0
- invokeai/backend/z_image/z_image_controlnet_extension.py +531 -0
- invokeai/backend/z_image/z_image_patchify_utils.py +135 -0
- invokeai/backend/z_image/z_image_transformer_patch.py +234 -0
- invokeai/frontend/web/dist/assets/App-CYhlZO3Q.js +161 -0
- invokeai/frontend/web/dist/assets/{browser-ponyfill-CN1j0ARZ.js → browser-ponyfill-DHZxq1nk.js} +1 -1
- invokeai/frontend/web/dist/assets/index-dgSJAY--.js +530 -0
- invokeai/frontend/web/dist/index.html +1 -1
- invokeai/frontend/web/dist/locales/de.json +24 -6
- invokeai/frontend/web/dist/locales/en.json +70 -1
- invokeai/frontend/web/dist/locales/es.json +0 -5
- invokeai/frontend/web/dist/locales/fr.json +0 -6
- invokeai/frontend/web/dist/locales/it.json +17 -64
- invokeai/frontend/web/dist/locales/ja.json +379 -44
- invokeai/frontend/web/dist/locales/ru.json +0 -6
- invokeai/frontend/web/dist/locales/vi.json +7 -54
- invokeai/frontend/web/dist/locales/zh-CN.json +0 -6
- invokeai/version/invokeai_version.py +1 -1
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/METADATA +3 -3
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/RECORD +84 -60
- invokeai/frontend/web/dist/assets/App-Cn9UyjoV.js +0 -161
- invokeai/frontend/web/dist/assets/index-BDrf9CL-.js +0 -530
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/WHEEL +0 -0
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/entry_points.txt +0 -0
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/licenses/LICENSE +0 -0
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/licenses/LICENSE-SD1+SD2.txt +0 -0
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/licenses/LICENSE-SDXL.txt +0 -0
- {invokeai-6.9.0rc3.dist-info → invokeai-6.10.0rc1.dist-info}/top_level.txt +0 -0
invokeai/app/api/dependencies.py
CHANGED
|
@@ -49,6 +49,7 @@ from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
|
|
|
49
49
|
FLUXConditioningInfo,
|
|
50
50
|
SD3ConditioningInfo,
|
|
51
51
|
SDXLConditioningInfo,
|
|
52
|
+
ZImageConditioningInfo,
|
|
52
53
|
)
|
|
53
54
|
from invokeai.backend.util.logging import InvokeAILogger
|
|
54
55
|
from invokeai.version.invokeai_version import __version__
|
|
@@ -129,6 +130,7 @@ class ApiDependencies:
|
|
|
129
130
|
FLUXConditioningInfo,
|
|
130
131
|
SD3ConditioningInfo,
|
|
131
132
|
CogView4ConditioningInfo,
|
|
133
|
+
ZImageConditioningInfo,
|
|
132
134
|
],
|
|
133
135
|
ephemeral=True,
|
|
134
136
|
),
|
|
@@ -28,7 +28,7 @@ from invokeai.app.services.model_records import (
|
|
|
28
28
|
UnknownModelException,
|
|
29
29
|
)
|
|
30
30
|
from invokeai.app.util.suppress_output import SuppressOutput
|
|
31
|
-
from invokeai.backend.model_manager.configs.factory import AnyModelConfig
|
|
31
|
+
from invokeai.backend.model_manager.configs.factory import AnyModelConfig, ModelConfigFactory
|
|
32
32
|
from invokeai.backend.model_manager.configs.main import (
|
|
33
33
|
Main_Checkpoint_SD1_Config,
|
|
34
34
|
Main_Checkpoint_SD2_Config,
|
|
@@ -38,6 +38,7 @@ from invokeai.backend.model_manager.configs.main import (
|
|
|
38
38
|
from invokeai.backend.model_manager.load.model_cache.cache_stats import CacheStats
|
|
39
39
|
from invokeai.backend.model_manager.metadata.fetch.huggingface import HuggingFaceMetadataFetch
|
|
40
40
|
from invokeai.backend.model_manager.metadata.metadata_base import ModelMetadataWithFiles, UnknownMetadataException
|
|
41
|
+
from invokeai.backend.model_manager.model_on_disk import ModelOnDisk
|
|
41
42
|
from invokeai.backend.model_manager.search import ModelSearch
|
|
42
43
|
from invokeai.backend.model_manager.starter_models import (
|
|
43
44
|
STARTER_BUNDLES,
|
|
@@ -191,6 +192,40 @@ async def get_model_record(
|
|
|
191
192
|
raise HTTPException(status_code=404, detail=str(e))
|
|
192
193
|
|
|
193
194
|
|
|
195
|
+
@model_manager_router.post(
|
|
196
|
+
"/i/{key}/reidentify",
|
|
197
|
+
operation_id="reidentify_model",
|
|
198
|
+
responses={
|
|
199
|
+
200: {
|
|
200
|
+
"description": "The model configuration was retrieved successfully",
|
|
201
|
+
"content": {"application/json": {"example": example_model_config}},
|
|
202
|
+
},
|
|
203
|
+
400: {"description": "Bad request"},
|
|
204
|
+
404: {"description": "The model could not be found"},
|
|
205
|
+
},
|
|
206
|
+
)
|
|
207
|
+
async def reidentify_model(
|
|
208
|
+
key: Annotated[str, Path(description="Key of the model to reidentify.")],
|
|
209
|
+
) -> AnyModelConfig:
|
|
210
|
+
"""Attempt to reidentify a model by re-probing its weights file."""
|
|
211
|
+
try:
|
|
212
|
+
config = ApiDependencies.invoker.services.model_manager.store.get_model(key)
|
|
213
|
+
models_path = ApiDependencies.invoker.services.configuration.models_path
|
|
214
|
+
if pathlib.Path(config.path).is_relative_to(models_path):
|
|
215
|
+
model_path = pathlib.Path(config.path)
|
|
216
|
+
else:
|
|
217
|
+
model_path = models_path / config.path
|
|
218
|
+
mod = ModelOnDisk(model_path)
|
|
219
|
+
result = ModelConfigFactory.from_model_on_disk(mod)
|
|
220
|
+
if result.config is None:
|
|
221
|
+
raise InvalidModelException("Unable to identify model format")
|
|
222
|
+
result.config.key = config.key # retain the same key
|
|
223
|
+
new_config = ApiDependencies.invoker.services.model_manager.store.replace_model(config.key, result.config)
|
|
224
|
+
return new_config
|
|
225
|
+
except UnknownModelException as e:
|
|
226
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
227
|
+
|
|
228
|
+
|
|
194
229
|
class FoundModel(BaseModel):
|
|
195
230
|
path: str = Field(description="Path to the model")
|
|
196
231
|
is_installed: bool = Field(description="Whether or not the model is already installed")
|
|
@@ -238,9 +273,10 @@ async def scan_for_models(
|
|
|
238
273
|
found_model = FoundModel(path=path, is_installed=is_installed)
|
|
239
274
|
scan_results.append(found_model)
|
|
240
275
|
except Exception as e:
|
|
276
|
+
error_type = type(e).__name__
|
|
241
277
|
raise HTTPException(
|
|
242
278
|
status_code=500,
|
|
243
|
-
detail=f"An error occurred while searching the directory: {
|
|
279
|
+
detail=f"An error occurred while searching the directory: {error_type}",
|
|
244
280
|
)
|
|
245
281
|
return scan_results
|
|
246
282
|
|
|
@@ -411,6 +447,59 @@ async def delete_model(
|
|
|
411
447
|
raise HTTPException(status_code=404, detail=str(e))
|
|
412
448
|
|
|
413
449
|
|
|
450
|
+
class BulkDeleteModelsRequest(BaseModel):
|
|
451
|
+
"""Request body for bulk model deletion."""
|
|
452
|
+
|
|
453
|
+
keys: List[str] = Field(description="List of model keys to delete")
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class BulkDeleteModelsResponse(BaseModel):
|
|
457
|
+
"""Response body for bulk model deletion."""
|
|
458
|
+
|
|
459
|
+
deleted: List[str] = Field(description="List of successfully deleted model keys")
|
|
460
|
+
failed: List[dict] = Field(description="List of failed deletions with error messages")
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@model_manager_router.post(
|
|
464
|
+
"/i/bulk_delete",
|
|
465
|
+
operation_id="bulk_delete_models",
|
|
466
|
+
responses={
|
|
467
|
+
200: {"description": "Models deleted (possibly with some failures)"},
|
|
468
|
+
},
|
|
469
|
+
status_code=200,
|
|
470
|
+
)
|
|
471
|
+
async def bulk_delete_models(
|
|
472
|
+
request: BulkDeleteModelsRequest = Body(description="List of model keys to delete"),
|
|
473
|
+
) -> BulkDeleteModelsResponse:
|
|
474
|
+
"""
|
|
475
|
+
Delete multiple model records from database.
|
|
476
|
+
|
|
477
|
+
The configuration records will be removed. The corresponding weights files will be
|
|
478
|
+
deleted as well if they reside within the InvokeAI "models" directory.
|
|
479
|
+
Returns a list of successfully deleted keys and failed deletions with error messages.
|
|
480
|
+
"""
|
|
481
|
+
logger = ApiDependencies.invoker.services.logger
|
|
482
|
+
installer = ApiDependencies.invoker.services.model_manager.install
|
|
483
|
+
|
|
484
|
+
deleted = []
|
|
485
|
+
failed = []
|
|
486
|
+
|
|
487
|
+
for key in request.keys:
|
|
488
|
+
try:
|
|
489
|
+
installer.delete(key)
|
|
490
|
+
deleted.append(key)
|
|
491
|
+
logger.info(f"Deleted model: {key}")
|
|
492
|
+
except UnknownModelException as e:
|
|
493
|
+
logger.error(f"Failed to delete model {key}: {str(e)}")
|
|
494
|
+
failed.append({"key": key, "error": str(e)})
|
|
495
|
+
except Exception as e:
|
|
496
|
+
logger.error(f"Failed to delete model {key}: {str(e)}")
|
|
497
|
+
failed.append({"key": key, "error": str(e)})
|
|
498
|
+
|
|
499
|
+
logger.info(f"Bulk delete completed: {len(deleted)} deleted, {len(failed)} failed")
|
|
500
|
+
return BulkDeleteModelsResponse(deleted=deleted, failed=failed)
|
|
501
|
+
|
|
502
|
+
|
|
414
503
|
@model_manager_router.delete(
|
|
415
504
|
"/i/{key}/image",
|
|
416
505
|
operation_id="delete_model_image",
|
|
@@ -223,6 +223,15 @@ async def get_workflow_thumbnail(
|
|
|
223
223
|
raise HTTPException(status_code=404)
|
|
224
224
|
|
|
225
225
|
|
|
226
|
+
@workflows_router.get("/tags", operation_id="get_all_tags")
|
|
227
|
+
async def get_all_tags(
|
|
228
|
+
categories: Optional[list[WorkflowCategory]] = Query(default=None, description="The categories to include"),
|
|
229
|
+
) -> list[str]:
|
|
230
|
+
"""Gets all unique tags from workflows"""
|
|
231
|
+
|
|
232
|
+
return ApiDependencies.invoker.services.workflow_records.get_all_tags(categories=categories)
|
|
233
|
+
|
|
234
|
+
|
|
226
235
|
@workflows_router.get("/counts_by_tag", operation_id="get_counts_by_tag")
|
|
227
236
|
async def get_counts_by_tag(
|
|
228
237
|
tags: list[str] = Query(description="The tags to get counts for"),
|
|
@@ -154,6 +154,7 @@ class FieldDescriptions:
|
|
|
154
154
|
clip = "CLIP (tokenizer, text encoder, LoRAs) and skipped layer count"
|
|
155
155
|
t5_encoder = "T5 tokenizer and text encoder"
|
|
156
156
|
glm_encoder = "GLM (THUDM) tokenizer and text encoder"
|
|
157
|
+
qwen3_encoder = "Qwen3 tokenizer and text encoder"
|
|
157
158
|
clip_embed_model = "CLIP Embed loader"
|
|
158
159
|
clip_g_model = "CLIP-G Embed loader"
|
|
159
160
|
unet = "UNet (scheduler, LoRAs)"
|
|
@@ -169,6 +170,7 @@ class FieldDescriptions:
|
|
|
169
170
|
flux_model = "Flux model (Transformer) to load"
|
|
170
171
|
sd3_model = "SD3 model (MMDiTX) to load"
|
|
171
172
|
cogview4_model = "CogView4 model (Transformer) to load"
|
|
173
|
+
z_image_model = "Z-Image model (Transformer) to load"
|
|
172
174
|
sdxl_main_model = "SDXL Main model (UNet, VAE, CLIP1, CLIP2) to load"
|
|
173
175
|
sdxl_refiner_model = "SDXL Refiner Main Modde (UNet, VAE, CLIP2) to load"
|
|
174
176
|
onnx_main_model = "ONNX Main model (UNet, VAE, CLIP) to load"
|
|
@@ -241,6 +243,12 @@ class BoardField(BaseModel):
|
|
|
241
243
|
board_id: str = Field(description="The id of the board")
|
|
242
244
|
|
|
243
245
|
|
|
246
|
+
class StylePresetField(BaseModel):
|
|
247
|
+
"""A style preset primitive field"""
|
|
248
|
+
|
|
249
|
+
style_preset_id: str = Field(description="The id of the style preset")
|
|
250
|
+
|
|
251
|
+
|
|
244
252
|
class DenoiseMaskField(BaseModel):
|
|
245
253
|
"""An inpaint mask field"""
|
|
246
254
|
|
|
@@ -321,6 +329,17 @@ class CogView4ConditioningField(BaseModel):
|
|
|
321
329
|
conditioning_name: str = Field(description="The name of conditioning tensor")
|
|
322
330
|
|
|
323
331
|
|
|
332
|
+
class ZImageConditioningField(BaseModel):
|
|
333
|
+
"""A Z-Image conditioning tensor primitive value"""
|
|
334
|
+
|
|
335
|
+
conditioning_name: str = Field(description="The name of conditioning tensor")
|
|
336
|
+
mask: Optional[TensorField] = Field(
|
|
337
|
+
default=None,
|
|
338
|
+
description="The mask associated with this conditioning tensor for regional prompting. "
|
|
339
|
+
"Excluded regions should be set to False, included regions should be set to True.",
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
|
|
324
343
|
class ConditioningField(BaseModel):
|
|
325
344
|
"""A conditioning tensor primitive value"""
|
|
326
345
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from contextlib import nullcontext
|
|
2
2
|
from functools import singledispatchmethod
|
|
3
|
+
from typing import Literal
|
|
3
4
|
|
|
4
5
|
import einops
|
|
5
6
|
import torch
|
|
@@ -20,7 +21,7 @@ from invokeai.app.invocations.fields import (
|
|
|
20
21
|
Input,
|
|
21
22
|
InputField,
|
|
22
23
|
)
|
|
23
|
-
from invokeai.app.invocations.model import VAEField
|
|
24
|
+
from invokeai.app.invocations.model import BaseModelType, VAEField
|
|
24
25
|
from invokeai.app.invocations.primitives import LatentsOutput
|
|
25
26
|
from invokeai.app.services.shared.invocation_context import InvocationContext
|
|
26
27
|
from invokeai.backend.model_manager.load.load_base import LoadedModel
|
|
@@ -29,13 +30,21 @@ from invokeai.backend.stable_diffusion.vae_tiling import patch_vae_tiling_params
|
|
|
29
30
|
from invokeai.backend.util.devices import TorchDevice
|
|
30
31
|
from invokeai.backend.util.vae_working_memory import estimate_vae_working_memory_sd15_sdxl
|
|
31
32
|
|
|
33
|
+
"""
|
|
34
|
+
SDXL VAE color compensation values determined experimentally to reduce color drift.
|
|
35
|
+
If more reliable values are found in the future (e.g. individual color channels), they can be updated.
|
|
36
|
+
SD1.5, TAESD, TAESDXL VAEs distort in less predictable ways, so no compensation is offered at this time.
|
|
37
|
+
"""
|
|
38
|
+
COMPENSATION_OPTIONS = Literal["None", "SDXL"]
|
|
39
|
+
COLOR_COMPENSATION_MAP = {"None": [1, 0], "SDXL": [1.015, -0.002]}
|
|
40
|
+
|
|
32
41
|
|
|
33
42
|
@invocation(
|
|
34
43
|
"i2l",
|
|
35
44
|
title="Image to Latents - SD1.5, SDXL",
|
|
36
45
|
tags=["latents", "image", "vae", "i2l"],
|
|
37
46
|
category="latents",
|
|
38
|
-
version="1.
|
|
47
|
+
version="1.2.0",
|
|
39
48
|
)
|
|
40
49
|
class ImageToLatentsInvocation(BaseInvocation):
|
|
41
50
|
"""Encodes an image into latents."""
|
|
@@ -52,6 +61,10 @@ class ImageToLatentsInvocation(BaseInvocation):
|
|
|
52
61
|
# offer a way to directly set None values.
|
|
53
62
|
tile_size: int = InputField(default=0, multiple_of=8, description=FieldDescriptions.vae_tile_size)
|
|
54
63
|
fp32: bool = InputField(default=False, description=FieldDescriptions.fp32)
|
|
64
|
+
color_compensation: COMPENSATION_OPTIONS = InputField(
|
|
65
|
+
default="None",
|
|
66
|
+
description="Apply VAE scaling compensation when encoding images (reduces color drift).",
|
|
67
|
+
)
|
|
55
68
|
|
|
56
69
|
@classmethod
|
|
57
70
|
def vae_encode(
|
|
@@ -62,7 +75,7 @@ class ImageToLatentsInvocation(BaseInvocation):
|
|
|
62
75
|
image_tensor: torch.Tensor,
|
|
63
76
|
tile_size: int = 0,
|
|
64
77
|
) -> torch.Tensor:
|
|
65
|
-
assert isinstance(vae_info.model, (AutoencoderKL, AutoencoderTiny))
|
|
78
|
+
assert isinstance(vae_info.model, (AutoencoderKL, AutoencoderTiny)), "VAE must be of type SD-1.5 or SDXL"
|
|
66
79
|
estimated_working_memory = estimate_vae_working_memory_sd15_sdxl(
|
|
67
80
|
operation="encode",
|
|
68
81
|
image_tensor=image_tensor,
|
|
@@ -71,7 +84,7 @@ class ImageToLatentsInvocation(BaseInvocation):
|
|
|
71
84
|
fp32=upcast,
|
|
72
85
|
)
|
|
73
86
|
with vae_info.model_on_device(working_mem_bytes=estimated_working_memory) as (_, vae):
|
|
74
|
-
assert isinstance(vae, (AutoencoderKL, AutoencoderTiny))
|
|
87
|
+
assert isinstance(vae, (AutoencoderKL, AutoencoderTiny)), "VAE must be of type SD-1.5 or SDXL"
|
|
75
88
|
orig_dtype = vae.dtype
|
|
76
89
|
if upcast:
|
|
77
90
|
vae.to(dtype=torch.float32)
|
|
@@ -127,9 +140,14 @@ class ImageToLatentsInvocation(BaseInvocation):
|
|
|
127
140
|
image = context.images.get_pil(self.image.image_name)
|
|
128
141
|
|
|
129
142
|
vae_info = context.models.load(self.vae.vae)
|
|
130
|
-
assert isinstance(vae_info.model, (AutoencoderKL, AutoencoderTiny))
|
|
143
|
+
assert isinstance(vae_info.model, (AutoencoderKL, AutoencoderTiny)), "VAE must be of type SD-1.5 or SDXL"
|
|
131
144
|
|
|
132
145
|
image_tensor = image_resized_to_grid_as_tensor(image.convert("RGB"))
|
|
146
|
+
|
|
147
|
+
if self.color_compensation != "None" and vae_info.config.base == BaseModelType.StableDiffusionXL:
|
|
148
|
+
scale, bias = COLOR_COMPENSATION_MAP[self.color_compensation]
|
|
149
|
+
image_tensor = image_tensor * scale + bias
|
|
150
|
+
|
|
133
151
|
if image_tensor.dim() == 3:
|
|
134
152
|
image_tensor = einops.rearrange(image_tensor, "c h w -> 1 c h w")
|
|
135
153
|
|
|
@@ -2,12 +2,6 @@ from contextlib import nullcontext
|
|
|
2
2
|
|
|
3
3
|
import torch
|
|
4
4
|
from diffusers.image_processor import VaeImageProcessor
|
|
5
|
-
from diffusers.models.attention_processor import (
|
|
6
|
-
AttnProcessor2_0,
|
|
7
|
-
LoRAAttnProcessor2_0,
|
|
8
|
-
LoRAXFormersAttnProcessor,
|
|
9
|
-
XFormersAttnProcessor,
|
|
10
|
-
)
|
|
11
5
|
from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL
|
|
12
6
|
from diffusers.models.autoencoders.autoencoder_tiny import AutoencoderTiny
|
|
13
7
|
|
|
@@ -77,26 +71,9 @@ class LatentsToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
|
|
|
77
71
|
assert isinstance(vae, (AutoencoderKL, AutoencoderTiny))
|
|
78
72
|
latents = latents.to(TorchDevice.choose_torch_device())
|
|
79
73
|
if self.fp32:
|
|
74
|
+
# FP32 mode: convert everything to float32 for maximum precision
|
|
80
75
|
vae.to(dtype=torch.float32)
|
|
81
|
-
|
|
82
|
-
use_torch_2_0_or_xformers = hasattr(vae.decoder, "mid_block") and isinstance(
|
|
83
|
-
vae.decoder.mid_block.attentions[0].processor,
|
|
84
|
-
(
|
|
85
|
-
AttnProcessor2_0,
|
|
86
|
-
XFormersAttnProcessor,
|
|
87
|
-
LoRAXFormersAttnProcessor,
|
|
88
|
-
LoRAAttnProcessor2_0,
|
|
89
|
-
),
|
|
90
|
-
)
|
|
91
|
-
# if xformers or torch_2_0 is used attention block does not need
|
|
92
|
-
# to be in float32 which can save lots of memory
|
|
93
|
-
if use_torch_2_0_or_xformers:
|
|
94
|
-
vae.post_quant_conv.to(latents.dtype)
|
|
95
|
-
vae.decoder.conv_in.to(latents.dtype)
|
|
96
|
-
vae.decoder.mid_block.to(latents.dtype)
|
|
97
|
-
else:
|
|
98
|
-
latents = latents.float()
|
|
99
|
-
|
|
76
|
+
latents = latents.float()
|
|
100
77
|
else:
|
|
101
78
|
vae.to(dtype=torch.float16)
|
|
102
79
|
latents = latents.half()
|
|
@@ -158,6 +158,10 @@ GENERATION_MODES = Literal[
|
|
|
158
158
|
"cogview4_img2img",
|
|
159
159
|
"cogview4_inpaint",
|
|
160
160
|
"cogview4_outpaint",
|
|
161
|
+
"z_image_txt2img",
|
|
162
|
+
"z_image_img2img",
|
|
163
|
+
"z_image_inpaint",
|
|
164
|
+
"z_image_outpaint",
|
|
161
165
|
]
|
|
162
166
|
|
|
163
167
|
|
|
@@ -166,7 +170,7 @@ GENERATION_MODES = Literal[
|
|
|
166
170
|
title="Core Metadata",
|
|
167
171
|
tags=["metadata"],
|
|
168
172
|
category="metadata",
|
|
169
|
-
version="2.
|
|
173
|
+
version="2.1.0",
|
|
170
174
|
classification=Classification.Internal,
|
|
171
175
|
)
|
|
172
176
|
class CoreMetadataInvocation(BaseInvocation):
|
|
@@ -217,6 +221,10 @@ class CoreMetadataInvocation(BaseInvocation):
|
|
|
217
221
|
default=None,
|
|
218
222
|
description="The VAE used for decoding, if the main model's default was not used",
|
|
219
223
|
)
|
|
224
|
+
qwen3_encoder: Optional[ModelIdentifierField] = InputField(
|
|
225
|
+
default=None,
|
|
226
|
+
description="The Qwen3 text encoder model used for Z-Image inference",
|
|
227
|
+
)
|
|
220
228
|
|
|
221
229
|
# High resolution fix metadata.
|
|
222
230
|
hrf_enabled: Optional[bool] = InputField(
|
|
@@ -72,6 +72,14 @@ class GlmEncoderField(BaseModel):
|
|
|
72
72
|
text_encoder: ModelIdentifierField = Field(description="Info to load text_encoder submodel")
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
class Qwen3EncoderField(BaseModel):
|
|
76
|
+
"""Field for Qwen3 text encoder used by Z-Image models."""
|
|
77
|
+
|
|
78
|
+
tokenizer: ModelIdentifierField = Field(description="Info to load tokenizer submodel")
|
|
79
|
+
text_encoder: ModelIdentifierField = Field(description="Info to load text_encoder submodel")
|
|
80
|
+
loras: List[LoRAField] = Field(default_factory=list, description="LoRAs to apply on model loading")
|
|
81
|
+
|
|
82
|
+
|
|
75
83
|
class VAEField(BaseModel):
|
|
76
84
|
vae: ModelIdentifierField = Field(description="Info to load vae submodel")
|
|
77
85
|
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
|
|
@@ -27,6 +27,7 @@ from invokeai.app.invocations.fields import (
|
|
|
27
27
|
SD3ConditioningField,
|
|
28
28
|
TensorField,
|
|
29
29
|
UIComponent,
|
|
30
|
+
ZImageConditioningField,
|
|
30
31
|
)
|
|
31
32
|
from invokeai.app.services.images.images_common import ImageDTO
|
|
32
33
|
from invokeai.app.services.shared.invocation_context import InvocationContext
|
|
@@ -461,6 +462,17 @@ class CogView4ConditioningOutput(BaseInvocationOutput):
|
|
|
461
462
|
return cls(conditioning=CogView4ConditioningField(conditioning_name=conditioning_name))
|
|
462
463
|
|
|
463
464
|
|
|
465
|
+
@invocation_output("z_image_conditioning_output")
|
|
466
|
+
class ZImageConditioningOutput(BaseInvocationOutput):
|
|
467
|
+
"""Base class for nodes that output a Z-Image text conditioning tensor."""
|
|
468
|
+
|
|
469
|
+
conditioning: ZImageConditioningField = OutputField(description=FieldDescriptions.cond)
|
|
470
|
+
|
|
471
|
+
@classmethod
|
|
472
|
+
def build(cls, conditioning_name: str) -> "ZImageConditioningOutput":
|
|
473
|
+
return cls(conditioning=ZImageConditioningField(conditioning_name=conditioning_name))
|
|
474
|
+
|
|
475
|
+
|
|
464
476
|
@invocation_output("conditioning_output")
|
|
465
477
|
class ConditioningOutput(BaseInvocationOutput):
|
|
466
478
|
"""Base class for nodes that output a single conditioning tensor"""
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from invokeai.app.invocations.baseinvocation import BaseInvocation, BaseInvocationOutput, invocation, invocation_output
|
|
2
|
+
from invokeai.app.invocations.fields import InputField, OutputField, StylePresetField, UIComponent
|
|
3
|
+
from invokeai.app.services.shared.invocation_context import InvocationContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@invocation_output("prompt_template_output")
|
|
7
|
+
class PromptTemplateOutput(BaseInvocationOutput):
|
|
8
|
+
"""Output for the Prompt Template node"""
|
|
9
|
+
|
|
10
|
+
positive_prompt: str = OutputField(description="The positive prompt with the template applied")
|
|
11
|
+
negative_prompt: str = OutputField(description="The negative prompt with the template applied")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@invocation(
|
|
15
|
+
"prompt_template",
|
|
16
|
+
title="Prompt Template",
|
|
17
|
+
tags=["prompt", "template", "style", "preset"],
|
|
18
|
+
category="prompt",
|
|
19
|
+
version="1.0.0",
|
|
20
|
+
)
|
|
21
|
+
class PromptTemplateInvocation(BaseInvocation):
|
|
22
|
+
"""Applies a Style Preset template to positive and negative prompts.
|
|
23
|
+
|
|
24
|
+
Select a Style Preset and provide positive/negative prompts. The node replaces
|
|
25
|
+
{prompt} placeholders in the template with your input prompts.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
style_preset: StylePresetField = InputField(
|
|
29
|
+
description="The Style Preset to use as a template",
|
|
30
|
+
)
|
|
31
|
+
positive_prompt: str = InputField(
|
|
32
|
+
default="",
|
|
33
|
+
description="The positive prompt to insert into the template's {prompt} placeholder",
|
|
34
|
+
ui_component=UIComponent.Textarea,
|
|
35
|
+
)
|
|
36
|
+
negative_prompt: str = InputField(
|
|
37
|
+
default="",
|
|
38
|
+
description="The negative prompt to insert into the template's {prompt} placeholder",
|
|
39
|
+
ui_component=UIComponent.Textarea,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def invoke(self, context: InvocationContext) -> PromptTemplateOutput:
|
|
43
|
+
# Fetch the style preset from the database
|
|
44
|
+
style_preset = context._services.style_preset_records.get(self.style_preset.style_preset_id)
|
|
45
|
+
|
|
46
|
+
# Get the template prompts
|
|
47
|
+
positive_template = style_preset.preset_data.positive_prompt
|
|
48
|
+
negative_template = style_preset.preset_data.negative_prompt
|
|
49
|
+
|
|
50
|
+
# Replace {prompt} placeholder with the input prompts
|
|
51
|
+
rendered_positive = positive_template.replace("{prompt}", self.positive_prompt)
|
|
52
|
+
rendered_negative = negative_template.replace("{prompt}", self.negative_prompt)
|
|
53
|
+
|
|
54
|
+
return PromptTemplateOutput(
|
|
55
|
+
positive_prompt=rendered_positive,
|
|
56
|
+
negative_prompt=rendered_negative,
|
|
57
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Copyright (c) 2024, Lincoln D. Stein and the InvokeAI Development Team
|
|
2
|
+
"""Z-Image Control invocation for spatial conditioning."""
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
from invokeai.app.invocations.baseinvocation import (
|
|
7
|
+
BaseInvocation,
|
|
8
|
+
BaseInvocationOutput,
|
|
9
|
+
Classification,
|
|
10
|
+
invocation,
|
|
11
|
+
invocation_output,
|
|
12
|
+
)
|
|
13
|
+
from invokeai.app.invocations.fields import (
|
|
14
|
+
FieldDescriptions,
|
|
15
|
+
ImageField,
|
|
16
|
+
InputField,
|
|
17
|
+
OutputField,
|
|
18
|
+
)
|
|
19
|
+
from invokeai.app.invocations.model import ModelIdentifierField
|
|
20
|
+
from invokeai.app.services.shared.invocation_context import InvocationContext
|
|
21
|
+
from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ZImageControlField(BaseModel):
|
|
25
|
+
"""A Z-Image control conditioning field for spatial control (Canny, HED, Depth, Pose, MLSD)."""
|
|
26
|
+
|
|
27
|
+
image_name: str = Field(description="The name of the preprocessed control image")
|
|
28
|
+
control_model: ModelIdentifierField = Field(description="The Z-Image ControlNet adapter model")
|
|
29
|
+
control_context_scale: float = Field(
|
|
30
|
+
default=0.75,
|
|
31
|
+
ge=0.0,
|
|
32
|
+
le=2.0,
|
|
33
|
+
description="The strength of the control signal. Recommended range: 0.65-0.80.",
|
|
34
|
+
)
|
|
35
|
+
begin_step_percent: float = Field(
|
|
36
|
+
default=0.0,
|
|
37
|
+
ge=0.0,
|
|
38
|
+
le=1.0,
|
|
39
|
+
description="When the control is first applied (% of total steps)",
|
|
40
|
+
)
|
|
41
|
+
end_step_percent: float = Field(
|
|
42
|
+
default=1.0,
|
|
43
|
+
ge=0.0,
|
|
44
|
+
le=1.0,
|
|
45
|
+
description="When the control is last applied (% of total steps)",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@invocation_output("z_image_control_output")
|
|
50
|
+
class ZImageControlOutput(BaseInvocationOutput):
|
|
51
|
+
"""Z-Image Control output containing control configuration."""
|
|
52
|
+
|
|
53
|
+
control: ZImageControlField = OutputField(description="Z-Image control conditioning")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@invocation(
|
|
57
|
+
"z_image_control",
|
|
58
|
+
title="Z-Image ControlNet",
|
|
59
|
+
tags=["image", "z-image", "control", "controlnet"],
|
|
60
|
+
category="control",
|
|
61
|
+
version="1.1.0",
|
|
62
|
+
classification=Classification.Prototype,
|
|
63
|
+
)
|
|
64
|
+
class ZImageControlInvocation(BaseInvocation):
|
|
65
|
+
"""Configure Z-Image ControlNet for spatial conditioning.
|
|
66
|
+
|
|
67
|
+
Takes a preprocessed control image (e.g., Canny edges, depth map, pose)
|
|
68
|
+
and a Z-Image ControlNet adapter model to enable spatial control.
|
|
69
|
+
|
|
70
|
+
Supports 5 control modes: Canny, HED, Depth, Pose, MLSD.
|
|
71
|
+
Recommended control_context_scale: 0.65-0.80.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
image: ImageField = InputField(
|
|
75
|
+
description="The preprocessed control image (Canny, HED, Depth, Pose, or MLSD)",
|
|
76
|
+
)
|
|
77
|
+
control_model: ModelIdentifierField = InputField(
|
|
78
|
+
description=FieldDescriptions.controlnet_model,
|
|
79
|
+
title="Control Model",
|
|
80
|
+
ui_model_base=BaseModelType.ZImage,
|
|
81
|
+
ui_model_type=ModelType.ControlNet,
|
|
82
|
+
)
|
|
83
|
+
control_context_scale: float = InputField(
|
|
84
|
+
default=0.75,
|
|
85
|
+
ge=0.0,
|
|
86
|
+
le=2.0,
|
|
87
|
+
description="Strength of the control signal. Recommended range: 0.65-0.80.",
|
|
88
|
+
title="Control Scale",
|
|
89
|
+
)
|
|
90
|
+
begin_step_percent: float = InputField(
|
|
91
|
+
default=0.0,
|
|
92
|
+
ge=0.0,
|
|
93
|
+
le=1.0,
|
|
94
|
+
description="When the control is first applied (% of total steps)",
|
|
95
|
+
)
|
|
96
|
+
end_step_percent: float = InputField(
|
|
97
|
+
default=1.0,
|
|
98
|
+
ge=0.0,
|
|
99
|
+
le=1.0,
|
|
100
|
+
description="When the control is last applied (% of total steps)",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def invoke(self, context: InvocationContext) -> ZImageControlOutput:
|
|
104
|
+
return ZImageControlOutput(
|
|
105
|
+
control=ZImageControlField(
|
|
106
|
+
image_name=self.image.image_name,
|
|
107
|
+
control_model=self.control_model,
|
|
108
|
+
control_context_scale=self.control_context_scale,
|
|
109
|
+
begin_step_percent=self.begin_step_percent,
|
|
110
|
+
end_step_percent=self.end_step_percent,
|
|
111
|
+
)
|
|
112
|
+
)
|