feat(setups): split publish result contract
This commit is contained in:
@@ -244,6 +244,7 @@ nodes when a setup should be callable from an external app:
|
||||
```text
|
||||
Koolook Publish Input -> place in Koolook Input
|
||||
Koolook Publish Output -> place in Koolook Output
|
||||
Koolook Publish Result -> place in Koolook Output
|
||||
```
|
||||
|
||||
`Koolook Publish Input` exposes stable multiline fields and outputs:
|
||||
@@ -263,14 +264,20 @@ switch INT output derived from mode
|
||||
folder STRING
|
||||
name STRING
|
||||
version STRING
|
||||
```
|
||||
|
||||
`Koolook Publish Result` exposes the resolved result value after workflow
|
||||
writer/path logic has run:
|
||||
|
||||
```text
|
||||
result STRING
|
||||
```
|
||||
|
||||
Publish detects these node classes and stores `setupSurface.app` with stable
|
||||
keys, user-facing labels, defaults, injection targets, and switch options. The
|
||||
external app should render the switch first, preserve the numeric switch values,
|
||||
and hide internal-only options such as Prompt while keeping their index stable
|
||||
for the workflow.
|
||||
keys, user-facing labels, defaults, injection targets, result targets, and
|
||||
switch options. The external app should render the switch first, preserve the
|
||||
numeric switch values, and hide internal-only options such as Prompt while
|
||||
keeping their index stable for the workflow.
|
||||
|
||||
## Callable Visual Workflow Standard
|
||||
|
||||
|
||||
+27
-6
@@ -83,7 +83,7 @@ class Koolook_PublishInput:
|
||||
|
||||
|
||||
class Koolook_PublishOutput:
|
||||
"""App-facing setup output controls and result path placeholder."""
|
||||
"""App-facing setup output controls."""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -113,32 +113,53 @@ class Koolook_PublishOutput:
|
||||
"tooltip": "Output version token or number.",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING")
|
||||
RETURN_NAMES = ("folder", "name", "version")
|
||||
CATEGORY = "Koolook/Publish"
|
||||
FUNCTION = "run"
|
||||
|
||||
def run(self, folder: str, name: str, version: str):
|
||||
return (folder, name, version)
|
||||
|
||||
|
||||
class Koolook_PublishResult:
|
||||
"""Resolved setup result surfaced back to the external app."""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"result": (
|
||||
"STRING",
|
||||
{
|
||||
"default": "",
|
||||
"multiline": True,
|
||||
"tooltip": "Result path/status surfaced back to the external app.",
|
||||
"tooltip": "Final resolved output path, folder, or status from the workflow.",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING")
|
||||
RETURN_NAMES = ("folder", "name", "version", "result")
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("result",)
|
||||
CATEGORY = "Koolook/Publish"
|
||||
FUNCTION = "run"
|
||||
|
||||
def run(self, folder: str, name: str, version: str, result: str):
|
||||
return (folder, name, version, result)
|
||||
def run(self, result: str):
|
||||
return (result,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Koolook_PublishInput": Koolook_PublishInput,
|
||||
"Koolook_PublishOutput": Koolook_PublishOutput,
|
||||
"Koolook_PublishResult": Koolook_PublishResult,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Koolook_PublishInput": "Koolook Publish Input",
|
||||
"Koolook_PublishOutput": "Koolook Publish Output",
|
||||
"Koolook_PublishResult": "Koolook Publish Result",
|
||||
}
|
||||
|
||||
+25
-1
@@ -27,6 +27,7 @@ SETUP_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$")
|
||||
VALIDATION_STATUSES = {"valid", "draft", "invalid"}
|
||||
PUBLISH_INPUT_CLASS = "Koolook_PublishInput"
|
||||
PUBLISH_OUTPUT_CLASS = "Koolook_PublishOutput"
|
||||
PUBLISH_RESULT_CLASS = "Koolook_PublishResult"
|
||||
PUBLISH_INPUT_FIELDS = (
|
||||
("sequence_folder", "Sequence folder", True),
|
||||
("qt_file", "QT file", True),
|
||||
@@ -43,12 +44,15 @@ PUBLISH_OUTPUT_FIELDS = (
|
||||
("folder", "Output folder", True),
|
||||
("name", "Output name", True),
|
||||
("version", "Version", True),
|
||||
)
|
||||
PUBLISH_RESULT_FIELDS = (
|
||||
("result", "Result", True),
|
||||
)
|
||||
WIDGET_ONLY_INPUTS_BY_CLASS = {
|
||||
"Text Multiline": ("text",),
|
||||
"Koolook_PublishInput": ("mode", "sequence_folder", "qt_file", "single_file", "prompt"),
|
||||
"Koolook_PublishOutput": ("folder", "name", "version", "result"),
|
||||
"Koolook_PublishOutput": ("folder", "name", "version"),
|
||||
"Koolook_PublishResult": ("result",),
|
||||
"EasyAIPipeline": (
|
||||
"shot_duration",
|
||||
"seed_value",
|
||||
@@ -376,11 +380,13 @@ def _infer_app_surface(visual_graph: dict[str, Any]) -> dict[str, Any]:
|
||||
output_nodes = _nodes_in_group(visual_graph, "Koolook Output")
|
||||
input_node = _first_node_of_type(input_nodes, PUBLISH_INPUT_CLASS)
|
||||
output_node = _first_node_of_type(output_nodes, PUBLISH_OUTPUT_CLASS)
|
||||
result_node = _first_node_of_type(output_nodes, PUBLISH_RESULT_CLASS)
|
||||
inputs = _publish_input_fields(input_node)
|
||||
outputs = _publish_output_fields(output_node)
|
||||
app: dict[str, Any] = {
|
||||
"inputs": inputs,
|
||||
"outputs": outputs,
|
||||
"results": _publish_result_fields(result_node),
|
||||
}
|
||||
if input_node is not None:
|
||||
app["switch"] = _publish_input_switch(input_node, inputs)
|
||||
@@ -433,6 +439,24 @@ def _publish_output_fields(node: dict[str, Any] | None) -> list[dict[str, Any]]:
|
||||
]
|
||||
|
||||
|
||||
def _publish_result_fields(node: dict[str, Any] | None) -> list[dict[str, Any]]:
|
||||
if node is None:
|
||||
return []
|
||||
return [
|
||||
{
|
||||
"key": key,
|
||||
"label": label,
|
||||
"visible": visible,
|
||||
"target": {
|
||||
"node": str(node.get("id")),
|
||||
"input": key,
|
||||
},
|
||||
"default": _app_widget_value(node, key),
|
||||
}
|
||||
for key, label, visible in PUBLISH_RESULT_FIELDS
|
||||
]
|
||||
|
||||
|
||||
def _publish_input_switch(
|
||||
node: dict[str, Any],
|
||||
inputs: list[dict[str, Any]],
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
from k_publish_contract import (
|
||||
Koolook_PublishInput,
|
||||
Koolook_PublishOutput,
|
||||
Koolook_PublishResult,
|
||||
NODE_CLASS_MAPPINGS,
|
||||
NODE_DISPLAY_NAME_MAPPINGS,
|
||||
)
|
||||
@@ -13,8 +14,10 @@ from k_publish_contract import (
|
||||
def test_publish_contract_nodes_register() -> None:
|
||||
assert NODE_CLASS_MAPPINGS["Koolook_PublishInput"] is Koolook_PublishInput
|
||||
assert NODE_CLASS_MAPPINGS["Koolook_PublishOutput"] is Koolook_PublishOutput
|
||||
assert NODE_CLASS_MAPPINGS["Koolook_PublishResult"] is Koolook_PublishResult
|
||||
assert NODE_DISPLAY_NAME_MAPPINGS["Koolook_PublishInput"] == "Koolook Publish Input"
|
||||
assert NODE_DISPLAY_NAME_MAPPINGS["Koolook_PublishOutput"] == "Koolook Publish Output"
|
||||
assert NODE_DISPLAY_NAME_MAPPINGS["Koolook_PublishResult"] == "Koolook Publish Result"
|
||||
|
||||
|
||||
def test_publish_input_exposes_stable_fields_and_switch_output() -> None:
|
||||
@@ -44,11 +47,20 @@ def test_publish_input_exposes_stable_fields_and_switch_output() -> None:
|
||||
def test_publish_output_exposes_stable_fields() -> None:
|
||||
spec = Koolook_PublishOutput.INPUT_TYPES()["required"]
|
||||
|
||||
assert list(spec) == ["folder", "name", "version", "result"]
|
||||
assert Koolook_PublishOutput.RETURN_NAMES == ("folder", "name", "version", "result")
|
||||
assert list(spec) == ["folder", "name", "version"]
|
||||
assert Koolook_PublishOutput.RETURN_NAMES == ("folder", "name", "version")
|
||||
assert Koolook_PublishOutput().run(
|
||||
folder="/out",
|
||||
name="mask",
|
||||
version="1",
|
||||
result="/out/mask_v001.png",
|
||||
) == ("/out", "mask", "1", "/out/mask_v001.png")
|
||||
) == ("/out", "mask", "1")
|
||||
|
||||
|
||||
def test_publish_result_exposes_resolved_result() -> None:
|
||||
spec = Koolook_PublishResult.INPUT_TYPES()["required"]
|
||||
|
||||
assert list(spec) == ["result"]
|
||||
assert Koolook_PublishResult.RETURN_NAMES == ("result",)
|
||||
assert Koolook_PublishResult().run(result="/out/mask_v001.png") == (
|
||||
"/out/mask_v001.png",
|
||||
)
|
||||
|
||||
@@ -778,7 +778,7 @@ def test_publish_setup_infers_app_surface_from_koolook_groups() -> None:
|
||||
}
|
||||
],
|
||||
"controls": [],
|
||||
"app": {"inputs": [], "outputs": []},
|
||||
"app": {"inputs": [], "outputs": [], "results": []},
|
||||
}
|
||||
|
||||
|
||||
@@ -820,12 +820,31 @@ def test_publish_setup_infers_app_contract_from_publish_nodes() -> None:
|
||||
"/shots/example/output",
|
||||
"publish-OUT",
|
||||
"1",
|
||||
"/shots/example/output/publish-OUT_v001.mov",
|
||||
],
|
||||
"outputs": [
|
||||
{"name": "folder", "type": "STRING", "links": []},
|
||||
{"name": "name", "type": "STRING", "links": []},
|
||||
{"name": "version", "type": "STRING", "links": []},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 300,
|
||||
"type": "Koolook_PublishResult",
|
||||
"title": "Koolook Publish Result",
|
||||
"pos": [520, 340],
|
||||
"size": [360, 160],
|
||||
"inputs": [
|
||||
{
|
||||
"name": "result",
|
||||
"type": "STRING",
|
||||
"widget": {"name": "result"},
|
||||
"link": None,
|
||||
}
|
||||
],
|
||||
"widgets_values": [
|
||||
"/shots/example/output/publish-OUT_v001.mov",
|
||||
],
|
||||
"outputs": [
|
||||
{"name": "result", "type": "STRING", "links": []},
|
||||
],
|
||||
},
|
||||
@@ -905,11 +924,13 @@ def test_publish_setup_infers_app_contract_from_publish_nodes() -> None:
|
||||
"target": {"node": "200", "input": "version"},
|
||||
"default": "1",
|
||||
},
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"key": "result",
|
||||
"label": "Result",
|
||||
"visible": True,
|
||||
"target": {"node": "200", "input": "result"},
|
||||
"target": {"node": "300", "input": "result"},
|
||||
"default": "/shots/example/output/publish-OUT_v001.mov",
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user