Files
wehub-resource-sync cddb07a176
build container image / cpu (push) Waiting to run
build container image / cuda (push) Waiting to run
build container image / rocm (push) Waiting to run
frontend tests / frontend-tests (push) Waiting to run
openapi checks / openapi-checks (push) Waiting to run
python tests / py3.12: macos-default (push) Waiting to run
python tests / py3.11: windows-cpu (push) Waiting to run
python tests / py3.12: windows-cpu (push) Waiting to run
python tests / py3.11: linux-cpu (push) Waiting to run
python tests / py3.12: linux-cpu (push) Waiting to run
typegen checks / typegen-checks (push) Waiting to run
uv lock checks / uv-lock-checks (push) Waiting to run
frontend checks / frontend-checks (push) Waiting to run
lfs checks / lfs-check (push) Waiting to run
python checks / python-checks (push) Waiting to run
python tests / py3.11: macos-default (push) Waiting to run
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:06 +08:00

1423 lines
47 KiB
Python

from typing import Any
import pytest
from invokeai.app.services.session_processor.workflow_call_batch import (
build_child_workflow_session_results,
build_child_workflow_sessions,
)
from invokeai.app.services.session_queue.session_queue_common import TooManySessionsError
from invokeai.app.services.shared.graph import Graph, GraphExecutionState, WorkflowCallFrame
from invokeai.app.services.shared.workflow_graph_builder import UnsupportedWorkflowNodeError
def _invocation_node(node_id: str, invocation_type: str, inputs: dict[str, Any]) -> dict[str, Any]:
return {
"id": node_id,
"type": "invocation",
"position": {"x": 0, "y": 0},
"data": {
"id": node_id,
"type": invocation_type,
"version": "1.0.0",
"nodePack": "invokeai",
"label": "",
"notes": "",
"isOpen": True,
"isIntermediate": False,
"useCache": True,
"dynamicInputTemplates": {},
"inputs": inputs,
},
}
def _workflow_dump(
*,
nodes: list[dict[str, Any]],
edges: list[dict[str, Any]],
exposed_fields: list[dict[str, str]] | None = None,
) -> dict[str, Any]:
nodes = list(nodes)
edges = list(edges)
for node in list(nodes):
data = node.get("data", {})
if data.get("type") != "workflow_return":
continue
inputs = data.get("inputs", {})
if "collection" not in inputs:
continue
return_node_id = data["id"]
value_node_id = f"{return_node_id}-value"
collect_node_id = f"{return_node_id}-collect"
data["inputs"] = {"values": {"value": []}}
nodes.extend(
[
_invocation_node(
value_node_id,
"workflow_return_value",
{"key": {"value": "result"}, "value": {"value": None}},
),
_invocation_node(collect_node_id, "collect", {"collection": {"value": []}}),
]
)
for edge in edges:
if edge.get("target") == return_node_id and edge.get("targetHandle") == "collection":
edge["target"] = value_node_id
edge["targetHandle"] = "value"
edges.extend(
[
{
"id": f"edge-{value_node_id}-collect",
"type": "default",
"source": value_node_id,
"sourceHandle": "value",
"target": collect_node_id,
"targetHandle": "item",
},
{
"id": f"edge-{collect_node_id}-return",
"type": "default",
"source": collect_node_id,
"sourceHandle": "collection",
"target": return_node_id,
"targetHandle": "values",
},
]
)
return {
"name": "Child Workflow",
"author": "Tester",
"description": "",
"version": "1.0.0",
"contact": "",
"tags": "",
"notes": "",
"exposedFields": exposed_fields or [],
"meta": {"category": "user", "version": "1.0.0"},
"nodes": nodes,
"edges": edges,
"form": None,
}
def _call_frame() -> WorkflowCallFrame:
return WorkflowCallFrame(
prepared_call_node_id="prepared-call",
source_call_node_id="source-call",
workflow_id="workflow-batch",
depth=1,
)
def test_build_child_workflow_sessions_expands_direct_integer_batch() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": [2, 4, 6]}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
parent_session = GraphExecutionState(graph=Graph())
child_sessions = build_child_workflow_sessions(
parent_session=parent_session,
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert len(child_sessions) == 3
assert [child_session.graph.nodes["target"].value for child_session in child_sessions] == [2, 4, 6]
def test_build_child_workflow_session_results_preserves_batch_field_values() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": [2, 4]}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
}
],
)
child_results = build_child_workflow_session_results(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert [result.session.graph.nodes["target"].value for result in child_results] == [2, 4]
assert [
[
(field_value.node_path, field_value.field_name, field_value.value)
for field_value in result.field_values or []
]
for result in child_results
] == [[("target", "value", 2)], [("target", "value", 4)]]
def test_build_child_workflow_sessions_expands_direct_integer_batch_into_named_return_value() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": [2, 4, 6]}, "batch_group_id": {"value": "None"}},
),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-batch-return",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert len(child_sessions) == 3
assert [child_session.graph.nodes["return-value"].value for child_session in child_sessions] == [2, 4, 6]
def test_build_child_workflow_sessions_rejects_inaccessible_image_generator_board() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"image_generator",
{
"generator": {
"value": {
"type": "image_generator_images_from_board",
"board_id": "private-board",
"category": "images",
}
}
},
),
_invocation_node(
"batch",
"image_batch",
{"images": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "img_paste", {"image": {"value": None}, "board": {"value": None}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "images",
"target": "batch",
"targetHandle": "images",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "images",
"target": "target",
"targetHandle": "image",
},
{
"id": "edge-target-return",
"type": "default",
"source": "target",
"sourceHandle": "image",
"target": "return",
"targetHandle": "collection",
},
],
)
services = type(
"Services",
(),
{
"board_images": type(
"BoardImages",
(),
{
"get_all_board_image_names_for_board": staticmethod(
lambda board_id, categories, is_intermediate: ["img-a"]
)
},
)(),
"board_records": type(
"BoardRecords",
(),
{
"get": staticmethod(
lambda board_id: type(
"BoardRecord",
(),
{"board_id": board_id, "user_id": "owner-1", "board_visibility": "private"},
)()
)
},
)(),
"users": type(
"Users",
(),
{"get": staticmethod(lambda user_id: type("User", (), {"is_admin": False})())},
)(),
},
)()
with pytest.raises(UnsupportedWorkflowNodeError, match="does not have access to board 'private-board'"):
build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
services=services,
user_id="caller-1",
)
def test_build_child_workflow_sessions_allows_shared_image_generator_board() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"image_generator",
{
"generator": {
"value": {
"type": "image_generator_images_from_board",
"board_id": "shared-board",
"category": "images",
}
}
},
),
_invocation_node(
"batch",
"image_batch",
{"images": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "img_paste", {"image": {"value": None}, "board": {"value": None}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "images",
"target": "batch",
"targetHandle": "images",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "images",
"target": "target",
"targetHandle": "image",
},
{
"id": "edge-target-return",
"type": "default",
"source": "target",
"sourceHandle": "image",
"target": "return",
"targetHandle": "collection",
},
],
)
services = type(
"Services",
(),
{
"board_images": type(
"BoardImages",
(),
{
"get_all_board_image_names_for_board": staticmethod(
lambda board_id, categories, is_intermediate: ["img-a", "img-b"]
)
},
)(),
"board_records": type(
"BoardRecords",
(),
{
"get": staticmethod(
lambda board_id: type(
"BoardRecord",
(),
{"board_id": board_id, "user_id": "owner-1", "board_visibility": "shared"},
)()
)
},
)(),
"users": type(
"Users",
(),
{"get": staticmethod(lambda user_id: type("User", (), {"is_admin": False})())},
)(),
},
)()
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
services=services,
user_id="caller-1",
)
assert len(child_sessions) == 2
def test_build_child_workflow_sessions_zips_grouped_batches() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"batch-a",
"integer_batch",
{"integers": {"value": [1, 2, 3]}, "batch_group_id": {"value": "Group 1"}},
),
_invocation_node(
"batch-b",
"integer_batch",
{"integers": {"value": [10, 20, 30]}, "batch_group_id": {"value": "Group 1"}},
),
_invocation_node("target", "add", {"a": {"value": 0}, "b": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-a",
"type": "default",
"source": "batch-a",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "a",
},
{
"id": "edge-b",
"type": "default",
"source": "batch-b",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "b",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert len(child_sessions) == 3
assert [(child.graph.nodes["target"].a, child.graph.nodes["target"].b) for child in child_sessions] == [
(1, 10),
(2, 20),
(3, 30),
]
def test_build_child_workflow_sessions_expands_cartesian_batches() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"batch-a",
"integer_batch",
{"integers": {"value": [1, 2]}, "batch_group_id": {"value": "None"}},
),
_invocation_node(
"batch-b",
"integer_batch",
{"integers": {"value": [10, 20]}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "add", {"a": {"value": 0}, "b": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-a",
"type": "default",
"source": "batch-a",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "a",
},
{
"id": "edge-b",
"type": "default",
"source": "batch-b",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "b",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert len(child_sessions) == 4
assert [(child.graph.nodes["target"].a, child.graph.nodes["target"].b) for child in child_sessions] == [
(1, 10),
(1, 20),
(2, 10),
(2, 20),
]
def test_build_child_workflow_sessions_applies_parent_inputs_before_batch_expansion() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": [1]}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
exposed_fields=[{"nodeId": "batch", "fieldName": "integers"}],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={"saved_workflow_input::batch::integers": [9, 10]},
call_frame=_call_frame(),
maximum_children=10,
)
assert [child_session.graph.nodes["target"].value for child_session in child_sessions] == [9, 10]
def test_build_child_workflow_sessions_rejects_non_generator_connected_batch_nodes() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node("generator", "integer", {"value": {"value": 7}}),
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "integers",
"target": "batch",
"targetHandle": "integers",
}
],
)
with pytest.raises(UnsupportedWorkflowNodeError, match="connected batch child workflow inputs"):
build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
def test_build_child_workflow_sessions_supports_generator_backed_batch_input_through_connector() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"integer_generator",
{
"generator": {
"value": {
"type": "integer_generator_arithmetic_sequence",
"start": 1,
"step": 1,
"count": 3,
}
}
},
),
{
"id": "connector",
"type": "connector",
"position": {"x": 0, "y": 0},
"data": {"id": "connector", "type": "connector", "label": "Connector", "isOpen": True},
},
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-connector",
"type": "default",
"source": "generator",
"sourceHandle": "integers",
"target": "connector",
"targetHandle": "in",
},
{
"id": "edge-connector-batch",
"type": "default",
"source": "connector",
"sourceHandle": "out",
"target": "batch",
"targetHandle": "integers",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert [child_session.graph.nodes["target"].value for child_session in child_sessions] == [1, 2, 3]
def test_build_child_workflow_sessions_rejects_hybrid_child_workflows_with_unrelated_generator_nodes() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": [2, 4]}, "batch_group_id": {"value": "None"}},
),
_invocation_node(
"side-generator",
"integer_generator",
{
"generator": {
"value": {
"type": "integer_generator_arithmetic_sequence",
"start": 5,
"step": 1,
"count": 2,
}
}
},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
with pytest.raises(
UnsupportedWorkflowNodeError,
match="mix supported batch nodes with unrelated generator nodes",
):
build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
def test_build_child_workflow_sessions_supports_integer_generator() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"integer_generator",
{
"generator": {
"value": {
"type": "integer_generator_arithmetic_sequence",
"start": 3,
"step": 2,
"count": 3,
}
}
},
),
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "integers",
"target": "batch",
"targetHandle": "integers",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert [child_session.graph.nodes["target"].value for child_session in child_sessions] == [3, 5, 7]
def test_build_child_workflow_sessions_rejects_oversized_generator_before_resolution(
monkeypatch: pytest.MonkeyPatch,
) -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"integer_generator",
{
"generator": {
"value": {
"type": "integer_generator_arithmetic_sequence",
"start": 0,
"step": 1,
"count": 1_000_000,
}
}
},
),
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "integers",
"target": "batch",
"targetHandle": "integers",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-return",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "return",
"targetHandle": "collection",
},
],
)
monkeypatch.setattr(
"invokeai.app.services.session_processor.workflow_call_batch._resolve_integer_generator",
lambda value: pytest.fail("oversized generators must be rejected before resolution"),
)
with pytest.raises(TooManySessionsError, match="remaining queue capacity"):
build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
def test_build_child_workflow_sessions_supports_float_generator() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"float_generator",
{
"generator": {
"value": {
"type": "float_generator_linear_distribution",
"start": 0.5,
"end": 1.5,
"count": 3,
}
}
},
),
_invocation_node(
"batch",
"float_batch",
{"floats": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "float", {"value": {"value": 0.0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "floats",
"target": "batch",
"targetHandle": "floats",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "floats",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert [child_session.graph.nodes["target"].value for child_session in child_sessions] == [0.5, 1.0, 1.5]
def test_build_child_workflow_sessions_supports_string_generator() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"string_generator",
{
"generator": {
"value": {
"type": "string_generator_parse_string",
"input": "alpha,beta,gamma",
"splitOn": ",",
}
}
},
),
_invocation_node(
"batch",
"string_batch",
{"strings": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "string", {"value": {"value": ""}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "strings",
"target": "batch",
"targetHandle": "strings",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "strings",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert [child_session.graph.nodes["target"].value for child_session in child_sessions] == [
"alpha",
"beta",
"gamma",
]
def test_build_child_workflow_sessions_supports_string_dynamic_prompts_generator() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"string_generator",
{
"generator": {
"value": {
"type": "string_generator_dynamic_prompts_combinatorial",
"input": "a {red|blue} cube",
"maxPrompts": 10,
}
}
},
),
_invocation_node(
"batch",
"string_batch",
{"strings": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "string", {"value": {"value": ""}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "strings",
"target": "batch",
"targetHandle": "strings",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "strings",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert [child_session.graph.nodes["target"].value for child_session in child_sessions] == [
"a red cube",
"a blue cube",
]
def test_build_child_workflow_sessions_supports_seeded_uniform_random_generators() -> None:
integer_workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"integer_generator",
{
"generator": {
"value": {
"type": "integer_generator_random_distribution_uniform",
"min": 1,
"max": 3,
"count": 3,
"seed": 4,
}
}
},
),
_invocation_node(
"batch",
"integer_batch",
{"integers": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "integer", {"value": {"value": 0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "integers",
"target": "batch",
"targetHandle": "integers",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "integers",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
float_workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"float_generator",
{
"generator": {
"value": {
"type": "float_generator_random_distribution_uniform",
"min": 0.0,
"max": 1.0,
"count": 2,
"seed": 4,
}
}
},
),
_invocation_node(
"batch",
"float_batch",
{"floats": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "float", {"value": {"value": 0.0}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "floats",
"target": "batch",
"targetHandle": "floats",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "floats",
"target": "target",
"targetHandle": "value",
},
{
"id": "edge-target-collect",
"type": "default",
"source": "target",
"sourceHandle": "value",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
integer_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=integer_workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
float_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=float_workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
)
assert [child_session.graph.nodes["target"].value for child_session in integer_sessions] == [1, 1, 2]
assert [round(child_session.graph.nodes["target"].value, 6) for child_session in float_sessions] == [
0.236048,
0.103166,
]
def test_build_child_workflow_sessions_supports_image_generator_from_board() -> None:
workflow = _workflow_dump(
nodes=[
_invocation_node(
"generator",
"image_generator",
{
"generator": {
"value": {
"type": "image_generator_images_from_board",
"board_id": "board-1",
"category": "images",
}
}
},
),
_invocation_node(
"batch",
"image_batch",
{"images": {"value": []}, "batch_group_id": {"value": "None"}},
),
_invocation_node("target", "image", {"image": {"value": None}}),
_invocation_node("collect", "collect", {"collection": {"value": []}}),
_invocation_node("return", "workflow_return", {"collection": {"value": []}}),
],
edges=[
{
"id": "edge-generator-batch",
"type": "default",
"source": "generator",
"sourceHandle": "images",
"target": "batch",
"targetHandle": "images",
},
{
"id": "edge-batch-target",
"type": "default",
"source": "batch",
"sourceHandle": "images",
"target": "target",
"targetHandle": "image",
},
{
"id": "edge-collect-return",
"type": "default",
"source": "target",
"sourceHandle": "image",
"target": "collect",
"targetHandle": "item",
},
{
"id": "edge-target-return",
"type": "default",
"source": "collect",
"sourceHandle": "collection",
"target": "return",
"targetHandle": "collection",
},
],
)
services = type(
"Services",
(),
{
"board_images": type(
"BoardImages",
(),
{
"get_all_board_image_names_for_board": staticmethod(
lambda board_id, categories, is_intermediate: ["img-a", "img-b"]
)
},
)()
},
)()
child_sessions = build_child_workflow_sessions(
parent_session=GraphExecutionState(graph=Graph()),
workflow=workflow,
workflow_inputs={},
call_frame=_call_frame(),
maximum_children=10,
services=services,
user_id="user-1",
)
assert [child_session.graph.nodes["target"].image.image_name for child_session in child_sessions] == [
"img-a",
"img-b",
]