|
from typing import Optional |
|
from enum import Enum |
|
|
|
from pydantic import BaseModel, Field |
|
|
|
from comfy.comfy_types.node_typing import IO |
|
from comfy_api_nodes.mapper_utils import model_field_to_node_input |
|
|
|
|
|
def test_model_field_to_float_input(): |
|
"""Tests mapping a float field with constraints.""" |
|
|
|
class ModelWithFloatField(BaseModel): |
|
cfg_scale: Optional[float] = Field( |
|
default=0.5, |
|
description="Flexibility in video generation", |
|
ge=0.0, |
|
le=1.0, |
|
multiple_of=0.001, |
|
) |
|
|
|
expected_output = ( |
|
IO.FLOAT, |
|
{ |
|
"default": 0.5, |
|
"tooltip": "Flexibility in video generation", |
|
"min": 0.0, |
|
"max": 1.0, |
|
"step": 0.001, |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.FLOAT, ModelWithFloatField, "cfg_scale" |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_float_input_no_constraints(): |
|
"""Tests mapping a float field with no constraints.""" |
|
|
|
class ModelWithFloatField(BaseModel): |
|
cfg_scale: Optional[float] = Field(default=0.5) |
|
|
|
expected_output = ( |
|
IO.FLOAT, |
|
{ |
|
"default": 0.5, |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.FLOAT, ModelWithFloatField, "cfg_scale" |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_int_input(): |
|
"""Tests mapping an int field with constraints.""" |
|
|
|
class ModelWithIntField(BaseModel): |
|
num_frames: Optional[int] = Field( |
|
default=10, |
|
description="Number of frames to generate", |
|
ge=1, |
|
le=100, |
|
multiple_of=1, |
|
) |
|
|
|
expected_output = ( |
|
IO.INT, |
|
{ |
|
"default": 10, |
|
"tooltip": "Number of frames to generate", |
|
"min": 1, |
|
"max": 100, |
|
"step": 1, |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input(IO.INT, ModelWithIntField, "num_frames") |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_string_input(): |
|
"""Tests mapping a string field.""" |
|
|
|
class ModelWithStringField(BaseModel): |
|
prompt: Optional[str] = Field( |
|
default="A beautiful sunset over a calm ocean", |
|
description="A prompt for the video generation", |
|
) |
|
|
|
expected_output = ( |
|
IO.STRING, |
|
{ |
|
"default": "A beautiful sunset over a calm ocean", |
|
"tooltip": "A prompt for the video generation", |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input(IO.STRING, ModelWithStringField, "prompt") |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_string_input_multiline(): |
|
"""Tests mapping a string field.""" |
|
|
|
class ModelWithStringField(BaseModel): |
|
prompt: Optional[str] = Field( |
|
default="A beautiful sunset over a calm ocean", |
|
description="A prompt for the video generation", |
|
) |
|
|
|
expected_output = ( |
|
IO.STRING, |
|
{ |
|
"default": "A beautiful sunset over a calm ocean", |
|
"tooltip": "A prompt for the video generation", |
|
"multiline": True, |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.STRING, ModelWithStringField, "prompt", multiline=True |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_combo_input(): |
|
"""Tests mapping a combo field.""" |
|
|
|
class MockEnum(str, Enum): |
|
option_1 = "option 1" |
|
option_2 = "option 2" |
|
option_3 = "option 3" |
|
|
|
class ModelWithComboField(BaseModel): |
|
model_name: Optional[MockEnum] = Field("option 1", description="Model Name") |
|
|
|
expected_output = ( |
|
IO.COMBO, |
|
{ |
|
"options": ["option 1", "option 2", "option 3"], |
|
"default": "option 1", |
|
"tooltip": "Model Name", |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.COMBO, ModelWithComboField, "model_name", enum_type=MockEnum |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_combo_input_no_options(): |
|
"""Tests mapping a combo field with no options.""" |
|
|
|
class ModelWithComboField(BaseModel): |
|
model_name: Optional[str] = Field(description="Model Name") |
|
|
|
expected_output = ( |
|
IO.COMBO, |
|
{ |
|
"tooltip": "Model Name", |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.COMBO, ModelWithComboField, "model_name" |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_image_input(): |
|
"""Tests mapping an image field.""" |
|
|
|
class ModelWithImageField(BaseModel): |
|
image: Optional[str] = Field( |
|
default=None, |
|
description="An image for the video generation", |
|
) |
|
|
|
expected_output = ( |
|
IO.IMAGE, |
|
{ |
|
"default": None, |
|
"tooltip": "An image for the video generation", |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input(IO.IMAGE, ModelWithImageField, "image") |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_node_input_no_description(): |
|
"""Tests mapping a field with no description.""" |
|
|
|
class ModelWithNoDescriptionField(BaseModel): |
|
field: Optional[str] = Field(default="default value") |
|
|
|
expected_output = ( |
|
IO.STRING, |
|
{ |
|
"default": "default value", |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.STRING, ModelWithNoDescriptionField, "field" |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_node_input_no_default(): |
|
"""Tests mapping a field with no default.""" |
|
|
|
class ModelWithNoDefaultField(BaseModel): |
|
field: Optional[str] = Field(description="A field with no default") |
|
|
|
expected_output = ( |
|
IO.STRING, |
|
{ |
|
"tooltip": "A field with no default", |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.STRING, ModelWithNoDefaultField, "field" |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_node_input_no_metadata(): |
|
"""Tests mapping a field with no metadata or properties defined on the schema.""" |
|
|
|
class ModelWithNoMetadataField(BaseModel): |
|
field: Optional[str] = Field() |
|
|
|
expected_output = ( |
|
IO.STRING, |
|
{}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.STRING, ModelWithNoMetadataField, "field" |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|
|
|
|
def test_model_field_to_node_input_default_is_none(): |
|
""" |
|
Tests mapping a field with a default of `None`. |
|
I.e., the default field should be included as the schema explicitly sets it to `None`. |
|
""" |
|
|
|
class ModelWithNoneDefaultField(BaseModel): |
|
field: Optional[str] = Field( |
|
default=None, description="A field with a default of None" |
|
) |
|
|
|
expected_output = ( |
|
IO.STRING, |
|
{ |
|
"default": None, |
|
"tooltip": "A field with a default of None", |
|
}, |
|
) |
|
|
|
actual_output = model_field_to_node_input( |
|
IO.STRING, ModelWithNoneDefaultField, "field" |
|
) |
|
|
|
assert actual_output[0] == expected_output[0] |
|
assert actual_output[1] == expected_output[1] |
|
|