mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
docs(nodes): update INVOCATIONS.md
This commit is contained in:
@ -19,31 +19,56 @@ An invocation looks like this:
|
|||||||
```py
|
```py
|
||||||
class UpscaleInvocation(BaseInvocation):
|
class UpscaleInvocation(BaseInvocation):
|
||||||
"""Upscales an image."""
|
"""Upscales an image."""
|
||||||
type: Literal['upscale'] = 'upscale'
|
|
||||||
|
# fmt: off
|
||||||
|
type: Literal["upscale"] = "upscale"
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
image: Union[ImageField,None] = Field(description="The input image")
|
image: Union[ImageField, None] = Field(description="The input image", default=None)
|
||||||
strength: float = Field(default=0.75, gt=0, le=1, description="The strength")
|
strength: float = Field(default=0.75, gt=0, le=1, description="The strength")
|
||||||
level: Literal[2,4] = Field(default=2, description = "The upscale level")
|
level: Literal[2, 4] = Field(default=2, description="The upscale level")
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
# Schema customisation
|
||||||
|
class Config(InvocationConfig):
|
||||||
|
schema_extra = {
|
||||||
|
"ui": {
|
||||||
|
"tags": ["upscaling", "image"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.services.images.get(self.image.image_type, self.image.image_name)
|
image = context.services.images.get_pil_image(
|
||||||
results = context.services.generate.upscale_and_reconstruct(
|
self.image.image_origin, self.image.image_name
|
||||||
image_list = [[image, 0]],
|
)
|
||||||
upscale = (self.level, self.strength),
|
results = context.services.restoration.upscale_and_reconstruct(
|
||||||
strength = 0.0, # GFPGAN strength
|
image_list=[[image, 0]],
|
||||||
save_original = False,
|
upscale=(self.level, self.strength),
|
||||||
image_callback = None,
|
strength=0.0, # GFPGAN strength
|
||||||
|
save_original=False,
|
||||||
|
image_callback=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Results are image and seed, unwrap for now
|
# Results are image and seed, unwrap for now
|
||||||
# TODO: can this return multiple results?
|
# TODO: can this return multiple results?
|
||||||
image_type = ImageType.RESULT
|
image_dto = context.services.images.create(
|
||||||
image_name = context.services.images.create_name(context.graph_execution_state_id, self.id)
|
image=results[0][0],
|
||||||
context.services.images.save(image_type, image_name, results[0][0])
|
image_origin=ResourceOrigin.INTERNAL,
|
||||||
return ImageOutput(
|
image_category=ImageCategory.GENERAL,
|
||||||
image = ImageField(image_type = image_type, image_name = image_name)
|
node_id=self.id,
|
||||||
|
session_id=context.graph_execution_state_id,
|
||||||
|
is_intermediate=self.is_intermediate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return ImageOutput(
|
||||||
|
image=ImageField(
|
||||||
|
image_name=image_dto.image_name,
|
||||||
|
image_origin=image_dto.image_origin,
|
||||||
|
),
|
||||||
|
width=image_dto.width,
|
||||||
|
height=image_dto.height,
|
||||||
|
)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Each portion is important to implement correctly.
|
Each portion is important to implement correctly.
|
||||||
@ -95,25 +120,67 @@ Finally, note that for all linking, the `type` of the linked fields must match.
|
|||||||
If the `name` also matches, then the field can be **automatically linked** to a
|
If the `name` also matches, then the field can be **automatically linked** to a
|
||||||
previous invocation by name and matching.
|
previous invocation by name and matching.
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
```py
|
||||||
|
# Schema customisation
|
||||||
|
class Config(InvocationConfig):
|
||||||
|
schema_extra = {
|
||||||
|
"ui": {
|
||||||
|
"tags": ["upscaling", "image"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is an optional configuration for the invocation. It inherits from
|
||||||
|
pydantic's model `Config` class, and it used primarily to customize the
|
||||||
|
autogenerated OpenAPI schema.
|
||||||
|
|
||||||
|
The UI relies on the OpenAPI schema in two ways:
|
||||||
|
|
||||||
|
- An API client & Typescript types are generated from it. This happens at build
|
||||||
|
time.
|
||||||
|
- The node editor parses the schema into a template used by the UI to create the
|
||||||
|
node editor UI. This parsing happens at runtime.
|
||||||
|
|
||||||
|
In this example, a `ui` key has been added to the `schema_extra` dict to provide
|
||||||
|
some tags for the UI, to facilitate filtering nodes.
|
||||||
|
|
||||||
|
See the Schema Generation section below for more information.
|
||||||
|
|
||||||
### Invoke Function
|
### Invoke Function
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.services.images.get(self.image.image_type, self.image.image_name)
|
image = context.services.images.get_pil_image(
|
||||||
results = context.services.generate.upscale_and_reconstruct(
|
self.image.image_origin, self.image.image_name
|
||||||
image_list = [[image, 0]],
|
)
|
||||||
upscale = (self.level, self.strength),
|
results = context.services.restoration.upscale_and_reconstruct(
|
||||||
strength = 0.0, # GFPGAN strength
|
image_list=[[image, 0]],
|
||||||
save_original = False,
|
upscale=(self.level, self.strength),
|
||||||
image_callback = None,
|
strength=0.0, # GFPGAN strength
|
||||||
|
save_original=False,
|
||||||
|
image_callback=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Results are image and seed, unwrap for now
|
# Results are image and seed, unwrap for now
|
||||||
image_type = ImageType.RESULT
|
# TODO: can this return multiple results?
|
||||||
image_name = context.services.images.create_name(context.graph_execution_state_id, self.id)
|
image_dto = context.services.images.create(
|
||||||
context.services.images.save(image_type, image_name, results[0][0])
|
image=results[0][0],
|
||||||
|
image_origin=ResourceOrigin.INTERNAL,
|
||||||
|
image_category=ImageCategory.GENERAL,
|
||||||
|
node_id=self.id,
|
||||||
|
session_id=context.graph_execution_state_id,
|
||||||
|
is_intermediate=self.is_intermediate,
|
||||||
|
)
|
||||||
|
|
||||||
return ImageOutput(
|
return ImageOutput(
|
||||||
image = ImageField(image_type = image_type, image_name = image_name)
|
image=ImageField(
|
||||||
|
image_name=image_dto.image_name,
|
||||||
|
image_origin=image_dto.image_origin,
|
||||||
|
),
|
||||||
|
width=image_dto.width,
|
||||||
|
height=image_dto.height,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -135,9 +202,16 @@ scenarios. If you need functionality, please provide it as a service in the
|
|||||||
```py
|
```py
|
||||||
class ImageOutput(BaseInvocationOutput):
|
class ImageOutput(BaseInvocationOutput):
|
||||||
"""Base class for invocations that output an image"""
|
"""Base class for invocations that output an image"""
|
||||||
type: Literal['image'] = 'image'
|
|
||||||
|
|
||||||
image: ImageField = Field(default=None, description="The output image")
|
# fmt: off
|
||||||
|
type: Literal["image_output"] = "image_output"
|
||||||
|
image: ImageField = Field(default=None, description="The output image")
|
||||||
|
width: int = Field(description="The width of the image in pixels")
|
||||||
|
height: int = Field(description="The height of the image in pixels")
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
schema_extra = {"required": ["type", "image", "width", "height"]}
|
||||||
```
|
```
|
||||||
|
|
||||||
Output classes look like an invocation class without the invoke method. Prefer
|
Output classes look like an invocation class without the invoke method. Prefer
|
||||||
@ -168,35 +242,36 @@ Here's that `ImageOutput` class, without the needed schema customisation:
|
|||||||
class ImageOutput(BaseInvocationOutput):
|
class ImageOutput(BaseInvocationOutput):
|
||||||
"""Base class for invocations that output an image"""
|
"""Base class for invocations that output an image"""
|
||||||
|
|
||||||
type: Literal["image"] = "image"
|
# fmt: off
|
||||||
|
type: Literal["image_output"] = "image_output"
|
||||||
image: ImageField = Field(default=None, description="The output image")
|
image: ImageField = Field(default=None, description="The output image")
|
||||||
|
width: int = Field(description="The width of the image in pixels")
|
||||||
|
height: int = Field(description="The height of the image in pixels")
|
||||||
|
# fmt: on
|
||||||
```
|
```
|
||||||
|
|
||||||
The generated OpenAPI schema, and all clients/types generated from it, will have
|
The OpenAPI schema that results from this `ImageOutput` will have the `type`,
|
||||||
the `type` and `image` properties marked as optional, even though we know they
|
`image`, `width` and `height` properties marked as optional, even though we know
|
||||||
will always have a value by the time we can interact with them via the API.
|
they will always have a value.
|
||||||
|
|
||||||
Here's the same class, but with the schema customisation added:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class ImageOutput(BaseInvocationOutput):
|
class ImageOutput(BaseInvocationOutput):
|
||||||
"""Base class for invocations that output an image"""
|
"""Base class for invocations that output an image"""
|
||||||
|
|
||||||
type: Literal["image"] = "image"
|
# fmt: off
|
||||||
|
type: Literal["image_output"] = "image_output"
|
||||||
image: ImageField = Field(default=None, description="The output image")
|
image: ImageField = Field(default=None, description="The output image")
|
||||||
|
width: int = Field(description="The width of the image in pixels")
|
||||||
|
height: int = Field(description="The height of the image in pixels")
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
# Add schema customization
|
||||||
class Config:
|
class Config:
|
||||||
schema_extra = {
|
schema_extra = {"required": ["type", "image", "width", "height"]}
|
||||||
'required': [
|
|
||||||
'type',
|
|
||||||
'image',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The resultant schema (and any API client or types generated from it) will now
|
With the customization in place, the schema will now show these properties as
|
||||||
have see `type` as string literal `"image"` and `image` as an `ImageField`
|
required, obviating the need for extensive null checks in client code.
|
||||||
object.
|
|
||||||
|
|
||||||
See this `pydantic` issue for discussion on this solution:
|
See this `pydantic` issue for discussion on this solution:
|
||||||
<https://github.com/pydantic/pydantic/discussions/4577>
|
<https://github.com/pydantic/pydantic/discussions/4577>
|
||||||
|
Reference in New Issue
Block a user