Skip to content

Commit 8cc6076

Browse files
authored
[PLT-3337] SDK - Workflow Management Remove input tests (#2031)
1 parent a9cf6b5 commit 8cc6076

File tree

3 files changed

+98
-25
lines changed

3 files changed

+98
-25
lines changed

libs/labelbox/src/labelbox/schema/workflow/workflow_utils.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -117,31 +117,6 @@ def validate_node_connections(
117117
"node_type": node_type,
118118
}
119119
)
120-
elif len(predecessors) > 1:
121-
# Check if all predecessors are initial nodes
122-
node_map = {n.id: n for n in nodes}
123-
predecessor_nodes = [
124-
node_map.get(pred_id) for pred_id in predecessors
125-
]
126-
all_initial = all(
127-
pred_node
128-
and pred_node.definition_id in initial_node_types
129-
for pred_node in predecessor_nodes
130-
if pred_node is not None
131-
)
132-
133-
if not all_initial:
134-
preds_info = ", ".join(
135-
[p[:8] + "..." for p in predecessors]
136-
)
137-
errors.append(
138-
{
139-
"reason": f"has multiple incoming connections ({len(predecessors)}) but not all are from initial nodes",
140-
"node_id": node.id,
141-
"node_type": node_type,
142-
"details": f"Connected from: {preds_info}",
143-
}
144-
)
145120

146121
# Check outgoing connections (except terminal nodes)
147122
if node.definition_id not in terminal_node_types:

libs/labelbox/tests/integration/test_workflow.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,40 @@ def test_workflow_creation(client, test_projects):
9696
assert WorkflowDefinitionId.Done in node_types
9797

9898

99+
def test_workflow_allows_multiple_incoming_from_non_initial_nodes(
100+
client, test_projects
101+
):
102+
"""
103+
Nodes may have multiple incoming connections from any nodes (not only initial nodes).
104+
105+
This used to fail validation when a node had >1 predecessor and at least one
106+
predecessor was not an initial node.
107+
"""
108+
source_project, _ = test_projects
109+
110+
workflow = source_project.get_workflow()
111+
initial_nodes = workflow.reset_to_initial_nodes(
112+
labeling_config=LabelingConfig(instructions="Start labeling here")
113+
)
114+
115+
logic = workflow.add_node(
116+
type=NodeType.Logic,
117+
name="Gate",
118+
filters=ProjectWorkflowFilter([labeled_by.is_one_of(["test-user"])]),
119+
)
120+
review = workflow.add_node(type=NodeType.Review, name="Review Task")
121+
done = workflow.add_node(type=NodeType.Done, name="Done")
122+
123+
# Multiple incoming connections to review, including from a non-initial node (logic)
124+
workflow.add_edge(initial_nodes.labeling, logic)
125+
workflow.add_edge(logic, review, NodeOutput.If)
126+
workflow.add_edge(initial_nodes.rework, review)
127+
workflow.add_edge(review, done, NodeOutput.Approved)
128+
129+
# Should validate and update successfully
130+
workflow.update_config(reposition=False)
131+
132+
99133
def test_workflow_creation_simple(client):
100134
"""Test creating a simple workflow with the working pattern."""
101135
# Create a new project for this test
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from dataclasses import dataclass
2+
3+
from labelbox.schema.workflow.enums import WorkflowDefinitionId
4+
from labelbox.schema.workflow.graph import ProjectWorkflowGraph
5+
from labelbox.schema.workflow.workflow_utils import WorkflowValidator
6+
7+
8+
@dataclass(frozen=True)
9+
class _Node:
10+
id: str
11+
definition_id: WorkflowDefinitionId
12+
13+
14+
def test_validate_node_connections_allows_multiple_incoming_from_non_initial_nodes():
15+
"""
16+
Regression test: nodes may have multiple incoming connections from any nodes.
17+
18+
Historically validation required that if a node had >1 predecessors, they all had
19+
to be initial nodes. Workflow Management now allows multi-input nodes from any
20+
nodes, so this must not error.
21+
"""
22+
initial_labeling = _Node(
23+
id="initial_labeling",
24+
definition_id=WorkflowDefinitionId.InitialLabelingTask,
25+
)
26+
initial_rework = _Node(
27+
id="initial_rework",
28+
definition_id=WorkflowDefinitionId.InitialReworkTask,
29+
)
30+
logic = _Node(id="logic", definition_id=WorkflowDefinitionId.Logic)
31+
review = _Node(id="review", definition_id=WorkflowDefinitionId.ReviewTask)
32+
done = _Node(id="done", definition_id=WorkflowDefinitionId.Done)
33+
34+
nodes = [initial_labeling, initial_rework, logic, review, done]
35+
36+
graph = ProjectWorkflowGraph()
37+
graph.add_edge(initial_labeling.id, logic.id)
38+
graph.add_edge(logic.id, review.id)
39+
graph.add_edge(initial_rework.id, review.id)
40+
graph.add_edge(review.id, done.id)
41+
42+
errors = WorkflowValidator.validate_node_connections(nodes, graph)
43+
assert errors == []
44+
45+
46+
def test_validate_node_connections_still_flags_missing_incoming_connections():
47+
"""Non-initial nodes must still have at least one incoming connection."""
48+
initial_labeling = _Node(
49+
id="initial_labeling",
50+
definition_id=WorkflowDefinitionId.InitialLabelingTask,
51+
)
52+
review = _Node(id="review", definition_id=WorkflowDefinitionId.ReviewTask)
53+
done = _Node(id="done", definition_id=WorkflowDefinitionId.Done)
54+
55+
nodes = [initial_labeling, review, done]
56+
graph = ProjectWorkflowGraph()
57+
graph.add_edge(initial_labeling.id, done.id)
58+
59+
errors = WorkflowValidator.validate_node_connections(nodes, graph)
60+
assert any(
61+
e.get("node_id") == review.id
62+
and e.get("reason") == "has no incoming connections"
63+
for e in errors
64+
)

0 commit comments

Comments
 (0)