Skip to content

Commit 5c73353

Browse files
committed
Add workflow management API - Resolves PLT-2503
1 parent bfa1df9 commit 5c73353

22 files changed

+1720
-1494
lines changed

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
NodePosition,
2020
)
2121

22-
# Import nodes from nodes.py (excluding LogicNode)
22+
# Import nodes from the nodes subdirectory
2323
from labelbox.schema.workflow.nodes import (
2424
InitialLabelingNode,
2525
InitialReworkNode,
@@ -28,14 +28,10 @@
2828
DoneNode,
2929
CustomReworkNode,
3030
UnknownWorkflowNode,
31+
LogicNode,
32+
AutoQANode,
3133
)
3234

33-
# Import LogicNode from its own module
34-
from labelbox.schema.workflow.logic_node import LogicNode
35-
36-
# Import AutoQANode from its own module
37-
from labelbox.schema.workflow.autoqa_node import AutoQANode
38-
3935
from labelbox.schema.workflow.edges import (
4036
WorkflowEdge,
4137
WorkflowEdgeFactory,

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

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
"""Base classes and mixins for Project Workflow nodes in Labelbox."""
22

33
import logging
4-
import random
5-
import string
64
from typing import Dict, List, Any, Optional, Tuple
75
from abc import abstractmethod
86
from pydantic import BaseModel, Field, ConfigDict
@@ -12,42 +10,6 @@
1210
logger = logging.getLogger(__name__)
1311

1412

15-
def format_time_duration(seconds: int) -> str:
16-
"""Convert seconds to human-readable time format.
17-
18-
Args:
19-
seconds: Time duration in seconds
20-
21-
Returns:
22-
Human-readable time string (e.g., "1h 30m", "5m 30s", "45s")
23-
24-
Examples:
25-
>>> format_time_duration(3600)
26-
'1h'
27-
>>> format_time_duration(90)
28-
'1m 30s'
29-
>>> format_time_duration(45)
30-
'45s'
31-
"""
32-
if seconds >= 3600: # >= 1 hour
33-
hours = seconds // 3600
34-
remaining_seconds = seconds % 3600
35-
if remaining_seconds == 0:
36-
return f"{hours}h"
37-
else:
38-
minutes = remaining_seconds // 60
39-
return f"{hours}h {minutes}m" if minutes > 0 else f"{hours}h"
40-
elif seconds >= 60: # >= 1 minute
41-
minutes = seconds // 60
42-
remaining_seconds = seconds % 60
43-
if remaining_seconds == 0:
44-
return f"{minutes}m"
45-
else:
46-
return f"{minutes}m {remaining_seconds}s"
47-
else:
48-
return f"{seconds}s"
49-
50-
5113
def format_metadata_operator(operator: str) -> Tuple[str, str]:
5214
"""Format metadata operator for display and JSON.
5315
@@ -80,15 +42,6 @@ def format_metadata_operator(operator: str) -> Tuple[str, str]:
8042
return operator_mappings.get(operator, (operator.upper(), operator))
8143

8244

83-
def generate_filter_id() -> str:
84-
"""Generate a unique filter ID for metadata filters.
85-
86-
Returns:
87-
Random 6-character string containing lowercase letters and digits
88-
"""
89-
return "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
90-
91-
9245
class NodePosition(BaseModel):
9346
"""Represents the position of a node in the workflow canvas.
9447

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class WorkflowEdge(BaseModel):
4242
)
4343
targetHandle: str = Field(
4444
alias="targetHandle",
45-
default="if",
45+
default="in",
4646
description="Input handle on target node (typically 'in')",
4747
)
4848

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

Lines changed: 1 addition & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import logging
88
from enum import Enum
9-
from typing import Any
109

1110
logger = logging.getLogger(__name__)
1211

@@ -36,30 +35,7 @@ class WorkflowDefinitionId(Enum):
3635
Done = "done"
3736
CustomReworkTask = "custom_rework_task"
3837
AutoQA = "auto_qa"
39-
Unknown = "unknown" # Fallback for potential future types
40-
41-
@classmethod
42-
def _missing_(cls, value: Any) -> "WorkflowDefinitionId":
43-
"""Handle missing enum values with appropriate fallbacks.
44-
45-
Args:
46-
value: The value that couldn't be found in the enum
47-
48-
Returns:
49-
A valid WorkflowDefinitionId with appropriate fallback logic
50-
"""
51-
# If value is None, return a valid default instead of Unknown
52-
if value is None:
53-
logger.warning(
54-
"Null WorkflowDefinitionId provided. Using default InitialLabelingTask."
55-
)
56-
return cls.InitialLabelingTask
57-
58-
# For other unrecognized values, log warning but use a valid ID
59-
logger.warning(
60-
f"Unknown WorkflowDefinitionId '{value}'. Using default InitialLabelingTask."
61-
)
62-
return cls.InitialLabelingTask
38+
Unknown = "unknown" # For unrecognized node types from API
6339

6440

6541
class NodeType(Enum):
@@ -91,19 +67,6 @@ class NodeOutput(str, Enum):
9167
Rejected = "else" # Alias for review node rejected output
9268
Default = "out"
9369

94-
@classmethod
95-
def _missing_(cls, value: Any) -> Any:
96-
"""Handle missing enum values by using the value as-is.
97-
98-
Args:
99-
value: The value that couldn't be found in the enum
100-
101-
Returns:
102-
The original value for flexible usage
103-
"""
104-
logger.warning(f"Unknown NodeOutput '{value}'. Using as-is.")
105-
return value
106-
10770

10871
class NodeInput(str, Enum):
10972
"""Available input types for workflow nodes.
@@ -114,19 +77,6 @@ class NodeInput(str, Enum):
11477

11578
Default = "in"
11679

117-
@classmethod
118-
def _missing_(cls, value: Any) -> Any:
119-
"""Handle missing enum values by using the value as-is.
120-
121-
Args:
122-
value: The value that couldn't be found in the enum
123-
124-
Returns:
125-
The original value for flexible usage
126-
"""
127-
logger.warning(f"Unknown NodeInput '{value}'. Using as-is.")
128-
return value
129-
13080

13181
class MatchFilters(str, Enum):
13282
"""Available match filter options for LogicNode.
@@ -137,19 +87,6 @@ class MatchFilters(str, Enum):
13787
Any = "any" # Maps to filter_logic "or" - matches if any filter passes
13888
All = "all" # Maps to filter_logic "and" - matches if all filters pass
13989

140-
@classmethod
141-
def _missing_(cls, value):
142-
"""Handle missing enum values with All as default.
143-
144-
Args:
145-
value: The value that couldn't be found in the enum
146-
147-
Returns:
148-
MatchFilters.All as the safe default
149-
"""
150-
logger.warning(f"Unknown MatchFilters '{value}'. Using All as default.")
151-
return cls.All
152-
15390

15491
class Scope(str, Enum):
15592
"""Available scope options for AutoQANode.
@@ -160,19 +97,6 @@ class Scope(str, Enum):
16097
Any = "any" # Passes if any annotation meets the criteria
16198
All = "all" # Passes only if all annotations meet the criteria
16299

163-
@classmethod
164-
def _missing_(cls, value):
165-
"""Handle missing enum values with All as default.
166-
167-
Args:
168-
value: The value that couldn't be found in the enum
169-
170-
Returns:
171-
Scope.All as the safe default
172-
"""
173-
logger.warning(f"Unknown Scope '{value}'. Using All as default.")
174-
return cls.All
175-
176100

177101
class FilterField(str, Enum):
178102
"""Available filter fields for LogicNode filters.
@@ -209,19 +133,6 @@ class FilterField(str, Enum):
209133
# Search filters
210134
NlSearch = "NlSearch"
211135

212-
@classmethod
213-
def _missing_(cls, value: Any) -> Any:
214-
"""Handle missing enum values by using the value as-is.
215-
216-
Args:
217-
value: The value that couldn't be found in the enum
218-
219-
Returns:
220-
The original value for flexible usage
221-
"""
222-
logger.warning(f"Unknown FilterField '{value}'. Using as-is.")
223-
return value
224-
225136

226137
class FilterOperator(str, Enum):
227138
"""Available filter operators for LogicNode filters.
@@ -250,16 +161,3 @@ class FilterOperator(str, Enum):
250161

251162
# Range operators
252163
Between = "between"
253-
254-
@classmethod
255-
def _missing_(cls, value: Any) -> Any:
256-
"""Handle missing enum values by using the value as-is.
257-
258-
Args:
259-
value: The value that couldn't be found in the enum
260-
261-
Returns:
262-
The original value for flexible usage
263-
"""
264-
logger.warning(f"Unknown FilterOperator '{value}'. Using as-is.")
265-
return value

0 commit comments

Comments
 (0)