Skip to content

Commit d9148fb

Browse files
feat(nodes): add version to node schemas
The `@invocation` decorator is extended with an optional `version` arg. On execution of the decorator, the version string is parsed using the `semver` package (this was an indirect dependency and has been added to `pyproject.toml`). All built-in nodes are set with `version="1.0.0"`. The version is added to the OpenAPI Schema for consumption by the client.
1 parent 59cb630 commit d9148fb

File tree

19 files changed

+139
-71
lines changed

19 files changed

+139
-71
lines changed

invokeai/app/invocations/baseinvocation.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from pydantic import BaseModel, Field, validator
2727
from pydantic.fields import Undefined, ModelField
2828
from pydantic.typing import NoArgAnyCallable
29+
import semver
2930

3031
if TYPE_CHECKING:
3132
from ..services.invocation_services import InvocationServices
@@ -401,6 +402,9 @@ class UIConfigBase(BaseModel):
401402
tags: Optional[list[str]] = Field(default_factory=None, description="The node's tags")
402403
title: Optional[str] = Field(default=None, description="The node's display name")
403404
category: Optional[str] = Field(default=None, description="The node's category")
405+
version: Optional[str] = Field(
406+
default=None, description='The node\'s version. Should be a valid semver string e.g. "1.0.0" or "3.8.13".'
407+
)
404408

405409

406410
class InvocationContext:
@@ -499,6 +503,8 @@ def schema_extra(schema: dict[str, Any], model_class: Type[BaseModel]) -> None:
499503
schema["tags"] = uiconfig.tags
500504
if uiconfig and hasattr(uiconfig, "category"):
501505
schema["category"] = uiconfig.category
506+
if uiconfig and hasattr(uiconfig, "version"):
507+
schema["version"] = uiconfig.version
502508
if "required" not in schema or not isinstance(schema["required"], list):
503509
schema["required"] = list()
504510
schema["required"].extend(["type", "id"])
@@ -567,7 +573,11 @@ def validate_workflow_is_json(cls, v):
567573

568574

569575
def invocation(
570-
invocation_type: str, title: Optional[str] = None, tags: Optional[list[str]] = None, category: Optional[str] = None
576+
invocation_type: str,
577+
title: Optional[str] = None,
578+
tags: Optional[list[str]] = None,
579+
category: Optional[str] = None,
580+
version: Optional[str] = None,
571581
) -> Callable[[Type[GenericBaseInvocation]], Type[GenericBaseInvocation]]:
572582
"""
573583
Adds metadata to an invocation.
@@ -594,6 +604,9 @@ def wrapper(cls: Type[GenericBaseInvocation]) -> Type[GenericBaseInvocation]:
594604
cls.UIConfig.tags = tags
595605
if category is not None:
596606
cls.UIConfig.category = category
607+
if version is not None:
608+
semver.Version.parse(version) # raises ValueError if invalid semver
609+
cls.UIConfig.version = version
597610

598611
# Add the invocation type to the pydantic model of the invocation
599612
invocation_type_annotation = Literal[invocation_type] # type: ignore

invokeai/app/invocations/collections.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
1111

1212

13-
@invocation("range", title="Integer Range", tags=["collection", "integer", "range"], category="collections")
13+
@invocation(
14+
"range", title="Integer Range", tags=["collection", "integer", "range"], category="collections", version="1.0.0"
15+
)
1416
class RangeInvocation(BaseInvocation):
1517
"""Creates a range of numbers from start to stop with step"""
1618

@@ -33,6 +35,7 @@ def invoke(self, context: InvocationContext) -> IntegerCollectionOutput:
3335
title="Integer Range of Size",
3436
tags=["collection", "integer", "size", "range"],
3537
category="collections",
38+
version="1.0.0",
3639
)
3740
class RangeOfSizeInvocation(BaseInvocation):
3841
"""Creates a range from start to start + size with step"""
@@ -50,6 +53,7 @@ def invoke(self, context: InvocationContext) -> IntegerCollectionOutput:
5053
title="Random Range",
5154
tags=["range", "integer", "random", "collection"],
5255
category="collections",
56+
version="1.0.0",
5357
)
5458
class RandomRangeInvocation(BaseInvocation):
5559
"""Creates a collection of random numbers"""

invokeai/app/invocations/compel.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class ConditioningFieldData:
4444
# PerpNeg = "perp_neg"
4545

4646

47-
@invocation("compel", title="Prompt", tags=["prompt", "compel"], category="conditioning")
47+
@invocation("compel", title="Prompt", tags=["prompt", "compel"], category="conditioning", version="1.0.0")
4848
class CompelInvocation(BaseInvocation):
4949
"""Parse prompt using compel package to conditioning."""
5050

@@ -267,6 +267,7 @@ def _lora_loader():
267267
title="SDXL Prompt",
268268
tags=["sdxl", "compel", "prompt"],
269269
category="conditioning",
270+
version="1.0.0",
270271
)
271272
class SDXLCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase):
272273
"""Parse prompt using compel package to conditioning."""
@@ -351,6 +352,7 @@ def invoke(self, context: InvocationContext) -> ConditioningOutput:
351352
title="SDXL Refiner Prompt",
352353
tags=["sdxl", "compel", "prompt"],
353354
category="conditioning",
355+
version="1.0.0",
354356
)
355357
class SDXLRefinerCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase):
356358
"""Parse prompt using compel package to conditioning."""
@@ -403,7 +405,7 @@ class ClipSkipInvocationOutput(BaseInvocationOutput):
403405
clip: ClipField = OutputField(default=None, description=FieldDescriptions.clip, title="CLIP")
404406

405407

406-
@invocation("clip_skip", title="CLIP Skip", tags=["clipskip", "clip", "skip"], category="conditioning")
408+
@invocation("clip_skip", title="CLIP Skip", tags=["clipskip", "clip", "skip"], category="conditioning", version="1.0.0")
407409
class ClipSkipInvocation(BaseInvocation):
408410
"""Skip layers in clip text_encoder model."""
409411

invokeai/app/invocations/controlnet_image_processors.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class ControlOutput(BaseInvocationOutput):
9595
control: ControlField = OutputField(description=FieldDescriptions.control)
9696

9797

98-
@invocation("controlnet", title="ControlNet", tags=["controlnet"], category="controlnet")
98+
@invocation("controlnet", title="ControlNet", tags=["controlnet"], category="controlnet", version="1.0.0")
9999
class ControlNetInvocation(BaseInvocation):
100100
"""Collects ControlNet info to pass to other nodes"""
101101

@@ -127,7 +127,9 @@ def invoke(self, context: InvocationContext) -> ControlOutput:
127127
)
128128

129129

130-
@invocation("image_processor", title="Base Image Processor", tags=["controlnet"], category="controlnet")
130+
@invocation(
131+
"image_processor", title="Base Image Processor", tags=["controlnet"], category="controlnet", version="1.0.0"
132+
)
131133
class ImageProcessorInvocation(BaseInvocation):
132134
"""Base class for invocations that preprocess images for ControlNet"""
133135

@@ -171,6 +173,7 @@ def invoke(self, context: InvocationContext) -> ImageOutput:
171173
title="Canny Processor",
172174
tags=["controlnet", "canny"],
173175
category="controlnet",
176+
version="1.0.0",
174177
)
175178
class CannyImageProcessorInvocation(ImageProcessorInvocation):
176179
"""Canny edge detection for ControlNet"""
@@ -193,6 +196,7 @@ def run_processor(self, image):
193196
title="HED (softedge) Processor",
194197
tags=["controlnet", "hed", "softedge"],
195198
category="controlnet",
199+
version="1.0.0",
196200
)
197201
class HedImageProcessorInvocation(ImageProcessorInvocation):
198202
"""Applies HED edge detection to image"""
@@ -221,6 +225,7 @@ def run_processor(self, image):
221225
title="Lineart Processor",
222226
tags=["controlnet", "lineart"],
223227
category="controlnet",
228+
version="1.0.0",
224229
)
225230
class LineartImageProcessorInvocation(ImageProcessorInvocation):
226231
"""Applies line art processing to image"""
@@ -242,6 +247,7 @@ def run_processor(self, image):
242247
title="Lineart Anime Processor",
243248
tags=["controlnet", "lineart", "anime"],
244249
category="controlnet",
250+
version="1.0.0",
245251
)
246252
class LineartAnimeImageProcessorInvocation(ImageProcessorInvocation):
247253
"""Applies line art anime processing to image"""
@@ -264,6 +270,7 @@ def run_processor(self, image):
264270
title="Openpose Processor",
265271
tags=["controlnet", "openpose", "pose"],
266272
category="controlnet",
273+
version="1.0.0",
267274
)
268275
class OpenposeImageProcessorInvocation(ImageProcessorInvocation):
269276
"""Applies Openpose processing to image"""
@@ -288,6 +295,7 @@ def run_processor(self, image):
288295
title="Midas Depth Processor",
289296
tags=["controlnet", "midas"],
290297
category="controlnet",
298+
version="1.0.0",
291299
)
292300
class MidasDepthImageProcessorInvocation(ImageProcessorInvocation):
293301
"""Applies Midas depth processing to image"""
@@ -314,6 +322,7 @@ def run_processor(self, image):
314322
title="Normal BAE Processor",
315323
tags=["controlnet"],
316324
category="controlnet",
325+
version="1.0.0",
317326
)
318327
class NormalbaeImageProcessorInvocation(ImageProcessorInvocation):
319328
"""Applies NormalBae processing to image"""
@@ -329,7 +338,9 @@ def run_processor(self, image):
329338
return processed_image
330339

331340

332-
@invocation("mlsd_image_processor", title="MLSD Processor", tags=["controlnet", "mlsd"], category="controlnet")
341+
@invocation(
342+
"mlsd_image_processor", title="MLSD Processor", tags=["controlnet", "mlsd"], category="controlnet", version="1.0.0"
343+
)
333344
class MlsdImageProcessorInvocation(ImageProcessorInvocation):
334345
"""Applies MLSD processing to image"""
335346

@@ -350,7 +361,9 @@ def run_processor(self, image):
350361
return processed_image
351362

352363

353-
@invocation("pidi_image_processor", title="PIDI Processor", tags=["controlnet", "pidi"], category="controlnet")
364+
@invocation(
365+
"pidi_image_processor", title="PIDI Processor", tags=["controlnet", "pidi"], category="controlnet", version="1.0.0"
366+
)
354367
class PidiImageProcessorInvocation(ImageProcessorInvocation):
355368
"""Applies PIDI processing to image"""
356369

@@ -376,6 +389,7 @@ def run_processor(self, image):
376389
title="Content Shuffle Processor",
377390
tags=["controlnet", "contentshuffle"],
378391
category="controlnet",
392+
version="1.0.0",
379393
)
380394
class ContentShuffleImageProcessorInvocation(ImageProcessorInvocation):
381395
"""Applies content shuffle processing to image"""
@@ -405,6 +419,7 @@ def run_processor(self, image):
405419
title="Zoe (Depth) Processor",
406420
tags=["controlnet", "zoe", "depth"],
407421
category="controlnet",
422+
version="1.0.0",
408423
)
409424
class ZoeDepthImageProcessorInvocation(ImageProcessorInvocation):
410425
"""Applies Zoe depth processing to image"""
@@ -420,6 +435,7 @@ def run_processor(self, image):
420435
title="Mediapipe Face Processor",
421436
tags=["controlnet", "mediapipe", "face"],
422437
category="controlnet",
438+
version="1.0.0",
423439
)
424440
class MediapipeFaceProcessorInvocation(ImageProcessorInvocation):
425441
"""Applies mediapipe face processing to image"""
@@ -442,6 +458,7 @@ def run_processor(self, image):
442458
title="Leres (Depth) Processor",
443459
tags=["controlnet", "leres", "depth"],
444460
category="controlnet",
461+
version="1.0.0",
445462
)
446463
class LeresImageProcessorInvocation(ImageProcessorInvocation):
447464
"""Applies leres processing to image"""
@@ -470,6 +487,7 @@ def run_processor(self, image):
470487
title="Tile Resample Processor",
471488
tags=["controlnet", "tile"],
472489
category="controlnet",
490+
version="1.0.0",
473491
)
474492
class TileResamplerProcessorInvocation(ImageProcessorInvocation):
475493
"""Tile resampler processor"""
@@ -509,6 +527,7 @@ def run_processor(self, img):
509527
title="Segment Anything Processor",
510528
tags=["controlnet", "segmentanything"],
511529
category="controlnet",
530+
version="1.0.0",
512531
)
513532
class SegmentAnythingProcessorInvocation(ImageProcessorInvocation):
514533
"""Applies segment anything processing to image"""

invokeai/app/invocations/cv.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@
1010
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
1111

1212

13-
@invocation(
14-
"cv_inpaint",
15-
title="OpenCV Inpaint",
16-
tags=["opencv", "inpaint"],
17-
category="inpaint",
18-
)
13+
@invocation("cv_inpaint", title="OpenCV Inpaint", tags=["opencv", "inpaint"], category="inpaint", version="1.0.0")
1914
class CvInpaintInvocation(BaseInvocation):
2015
"""Simple inpaint using opencv."""
2116

0 commit comments

Comments
 (0)