Skip to content

Commit 5311011

Browse files
chore: restore working behavior
1 parent 9afd82d commit 5311011

File tree

4 files changed

+37
-23
lines changed

4 files changed

+37
-23
lines changed

libs/labelbox/src/labelbox/data/annotation_types/temporal.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
frame-level precision. All temporal classifications support nested hierarchies.
66
"""
77

8-
from typing import List, Optional, Tuple, Union
8+
from typing import Any, Dict, List, Optional, Tuple, Union
99
from pydantic import BaseModel, Field
1010

11+
from ...annotated_types import Cuid
12+
1113

1214
class TemporalClassificationAnswer(BaseModel):
1315
"""
@@ -21,6 +23,8 @@ class TemporalClassificationAnswer(BaseModel):
2123
frames (List[Tuple[int, int]]): List of (start_frame, end_frame) ranges in milliseconds
2224
classifications (Optional[List[Union[TemporalClassificationText, TemporalClassificationQuestion]]]):
2325
Nested classifications within this answer
26+
feature_schema_id (Optional[Cuid]): Feature schema identifier
27+
extra (Dict[str, Any]): Additional metadata
2428
2529
Example:
2630
>>> # Radio answer with nested classifications
@@ -49,6 +53,8 @@ class TemporalClassificationAnswer(BaseModel):
4953
classifications: Optional[
5054
List[Union["TemporalClassificationText", "TemporalClassificationQuestion"]]
5155
] = None
56+
feature_schema_id: Optional[Cuid] = None
57+
extra: Dict[str, Any] = Field(default_factory=dict)
5258

5359

5460
class TemporalClassificationText(BaseModel):
@@ -63,6 +69,8 @@ class TemporalClassificationText(BaseModel):
6369
value (List[Tuple[int, int, str]]): List of (start_frame, end_frame, text_value) tuples
6470
classifications (Optional[List[Union[TemporalClassificationText, TemporalClassificationQuestion]]]):
6571
Nested classifications
72+
feature_schema_id (Optional[Cuid]): Feature schema identifier
73+
extra (Dict[str, Any]): Additional metadata
6674
6775
Example:
6876
>>> # Simple text with multiple temporal values
@@ -99,6 +107,8 @@ class TemporalClassificationText(BaseModel):
99107
classifications: Optional[
100108
List[Union["TemporalClassificationText", "TemporalClassificationQuestion"]]
101109
] = None
110+
feature_schema_id: Optional[Cuid] = None
111+
extra: Dict[str, Any] = Field(default_factory=dict)
102112

103113

104114
class TemporalClassificationQuestion(BaseModel):
@@ -111,8 +121,8 @@ class TemporalClassificationQuestion(BaseModel):
111121
Args:
112122
name (str): Name of the question/classification
113123
value (List[TemporalClassificationAnswer]): List of answer options with frame ranges
114-
classifications (Optional[List[Union[TemporalClassificationText, TemporalClassificationQuestion]]]):
115-
Nested classifications (typically not used at question level)
124+
feature_schema_id (Optional[Cuid]): Feature schema identifier
125+
extra (Dict[str, Any]): Additional metadata
116126
117127
Note:
118128
- Radio: Single answer in the value list
@@ -177,6 +187,8 @@ class TemporalClassificationQuestion(BaseModel):
177187
classifications: Optional[
178188
List[Union["TemporalClassificationText", "TemporalClassificationQuestion"]]
179189
] = None
190+
feature_schema_id: Optional[Cuid] = None
191+
extra: Dict[str, Any] = Field(default_factory=dict)
180192

181193

182194
# Update forward references for recursive types

libs/labelbox/src/labelbox/data/serialization/ndjson/label.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
TemporalClassificationText,
3030
TemporalClassificationQuestion,
3131
)
32-
from .temporal import create_temporal_ndjson_classifications
32+
from .temporal import create_temporal_ndjson_annotations
3333
from labelbox.types import DocumentRectangle, DocumentEntity
3434
from .classification import (
3535
NDChecklistSubclass,
@@ -75,7 +75,7 @@ def from_common(
7575
yield from cls._create_relationship_annotations(label)
7676
yield from cls._create_non_video_annotations(label)
7777
yield from cls._create_video_annotations(label)
78-
yield from cls._create_temporal_classifications(label)
78+
yield from cls._create_temporal_annotations(label)
7979

8080
@staticmethod
8181
def _get_consecutive_frames(
@@ -167,7 +167,7 @@ def _create_video_annotations(
167167
yield NDObject.from_common(segments, label.data)
168168

169169
@classmethod
170-
def _create_temporal_classifications(
170+
def _create_temporal_annotations(
171171
cls, label: Label
172172
) -> Generator[BaseModel, None, None]:
173173
"""Create temporal annotations with nested classifications using new temporal classes."""
@@ -182,7 +182,7 @@ def _create_temporal_classifications(
182182
return
183183

184184
# Use the new temporal serializer to create NDJSON annotations
185-
ndjson_annotations = create_temporal_ndjson_classifications(
185+
ndjson_annotations = create_temporal_ndjson_annotations(
186186
temporal_annotations, label.data.global_key
187187
)
188188

libs/labelbox/src/labelbox/data/serialization/ndjson/temporal.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class TemporalNDJSON(BaseModel):
2626
dataRow: Dict[str, str]
2727

2828

29-
def create_temporal_ndjson_classifications(
29+
def create_temporal_ndjson_annotations(
3030
annotations: List[
3131
Union[TemporalClassificationText, TemporalClassificationQuestion]
3232
],
@@ -45,10 +45,11 @@ def create_temporal_ndjson_classifications(
4545
if not annotations:
4646
return []
4747

48-
# Group by classification name
48+
# Group by classification name/schema_id
4949
groups = defaultdict(list)
5050
for ann in annotations:
51-
groups[ann.name].append(ann)
51+
key = ann.feature_schema_id or ann.name
52+
groups[key].append(ann)
5253

5354
results = []
5455
for group_key, group_anns in groups.items():
@@ -266,12 +267,13 @@ def _process_nested_classifications(
266267
"""
267268
Process nested classifications recursively.
268269
269-
Groups by name and processes each group.
270+
Groups by name/schema_id and processes each group.
270271
"""
271272
# Group by name
272273
groups = defaultdict(list)
273274
for cls in classifications:
274-
groups[cls.name].append(cls)
275+
key = cls.feature_schema_id or cls.name
276+
groups[key].append(cls)
275277

276278
results = []
277279
for group_key, group_items in groups.items():

libs/labelbox/tests/data/serialization/ndjson/test_temporal.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import labelbox.types as lb_types
44
from labelbox.data.serialization.ndjson.temporal import (
5-
create_temporal_ndjson_classifications,
5+
create_temporal_ndjson_annotations,
66
)
77

88

@@ -18,7 +18,7 @@ def test_temporal_text_simple():
1818
)
1919
]
2020

21-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
21+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
2222

2323
assert len(result) == 1
2424
assert result[0].name == "transcription"
@@ -49,7 +49,7 @@ def test_temporal_question_radio():
4949
)
5050
]
5151

52-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
52+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
5353

5454
assert len(result) == 1
5555
assert result[0].name == "speaker"
@@ -78,7 +78,7 @@ def test_temporal_question_checklist():
7878
)
7979
]
8080

81-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
81+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
8282

8383
assert len(result) == 1
8484
assert result[0].name == "audio_quality"
@@ -123,7 +123,7 @@ def test_temporal_text_nested():
123123
)
124124
]
125125

126-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
126+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
127127

128128
assert len(result) == 1
129129
assert result[0].name == "transcription"
@@ -185,7 +185,7 @@ def test_temporal_question_nested():
185185
)
186186
]
187187

188-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
188+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
189189

190190
assert len(result) == 1
191191
answer = result[0].answer[0]
@@ -227,7 +227,7 @@ def test_frame_validation_discard_invalid():
227227
)
228228
]
229229

230-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
230+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
231231

232232
# Find the nested notes classification
233233
answer = result[0].answer[0]
@@ -251,7 +251,7 @@ def test_frame_deduplication():
251251
)
252252
]
253253

254-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
254+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
255255

256256
# Should only have one entry
257257
assert len(result[0].answer) == 1
@@ -291,7 +291,7 @@ def test_mixed_text_and_question_nesting():
291291
)
292292
]
293293

294-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
294+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
295295

296296
assert len(result) == 1
297297
answer = result[0].answer[0]
@@ -341,7 +341,7 @@ def test_inductive_structure_text_with_shared_nested_radio():
341341
)
342342
]
343343

344-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
344+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
345345

346346
assert len(result) == 1
347347
assert result[0].name == "content_notes"
@@ -416,7 +416,7 @@ def test_inductive_structure_checklist_with_multiple_text_values():
416416
)
417417
]
418418

419-
result = create_temporal_ndjson_classifications(annotations, "test-global-key")
419+
result = create_temporal_ndjson_annotations(annotations, "test-global-key")
420420

421421
assert len(result) == 1
422422
assert result[0].name == "checklist_class"

0 commit comments

Comments
 (0)