Skip to content

Commit 8c14b63

Browse files
authored
Merge pull request #996 from Labelbox/VB/conversational-annotation_AL-5107_2
[AL-5107] Vb/conversational annotation al 5107 2
2 parents 11793a4 + 3527c81 commit 8c14b63

File tree

17 files changed

+432
-21
lines changed

17 files changed

+432
-21
lines changed

labelbox/data/annotation_types/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
from .annotation import ObjectAnnotation
1111
from .annotation import VideoObjectAnnotation
1212

13-
from .ner import TextEntity
13+
from .ner import ConversationEntity
1414
from .ner import DocumentEntity
1515
from .ner import DocumentTextSelection
16+
from .ner import TextEntity
1617

1718
from .classification import Checklist
1819
from .classification import ClassificationAnswer

labelbox/data/annotation_types/annotation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .classification import Checklist, Dropdown, Radio, Text
77
from .feature import FeatureSchema
88
from .geometry import Geometry, Rectangle, Point
9-
from .ner import DocumentEntity, TextEntity
9+
from .ner import DocumentEntity, TextEntity, ConversationEntity
1010

1111

1212
class BaseAnnotation(FeatureSchema, abc.ABC):
@@ -51,7 +51,7 @@ class ObjectAnnotation(BaseAnnotation, ConfidenceMixin):
5151
classifications (Optional[List[ClassificationAnnotation]]): Optional sub classification of the annotation
5252
extra (Dict[str, Any])
5353
"""
54-
value: Union[TextEntity, DocumentEntity, Geometry]
54+
value: Union[TextEntity, ConversationEntity, DocumentEntity, Geometry]
5555
classifications: List[ClassificationAnnotation] = []
5656

5757

labelbox/data/annotation_types/label.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import labelbox
88
from labelbox.data.annotation_types.data.tiled_image import TiledImageData
9-
from labelbox.data.annotation_types.ner import DocumentEntity
109
from labelbox.schema import ontology
1110
from .annotation import (ClassificationAnnotation, ObjectAnnotation,
1211
VideoClassificationAnnotation, VideoObjectAnnotation)
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
from .text_entity import TextEntity
1+
from .conversation_entity import ConversationEntity
22
from .document_entity import DocumentEntity, DocumentTextSelection
3+
from .text_entity import TextEntity
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from labelbox.data.annotation_types.ner.text_entity import TextEntity
2+
from labelbox.utils import _CamelCaseMixin
3+
4+
5+
class ConversationEntity(TextEntity, _CamelCaseMixin):
6+
""" Represents a text entity """
7+
message_id: str

labelbox/data/annotation_types/ner/document_entity.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,4 @@ def validate_page(cls, v):
1919

2020
class DocumentEntity(_CamelCaseMixin, BaseModel):
2121
""" Represents a text entity """
22-
name: str
2322
text_selections: List[DocumentTextSelection]

labelbox/data/serialization/ndjson/label.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from ...annotation_types.collection import LabelCollection, LabelGenerator
1111
from ...annotation_types.data import ImageData, TextData, VideoData
1212
from ...annotation_types.label import Label
13-
from ...annotation_types.ner import TextEntity
13+
from ...annotation_types.ner import TextEntity, ConversationEntity
1414
from ...annotation_types.classification import Dropdown
1515
from ...annotation_types.metrics import ScalarMetric, ConfusionMatrixMetric
1616

@@ -52,7 +52,6 @@ def _generate_annotations(
5252
annots = []
5353
data_row = annotations[0].data_row
5454
for annotation in annotations:
55-
5655
if isinstance(annotation, NDSegments):
5756
annots.extend(
5857
NDSegments.to_common(annotation, annotation.name,
@@ -72,7 +71,8 @@ def _generate_annotations(
7271

7372
def _infer_media_type(
7473
self, data_row: DataRow,
75-
annotations: List[Union[TextEntity, VideoClassificationAnnotation,
74+
annotations: List[Union[TextEntity, ConversationEntity,
75+
VideoClassificationAnnotation,
7676
VideoObjectAnnotation, ObjectAnnotation,
7777
ClassificationAnnotation, ScalarMetric,
7878
ConfusionMatrixMetric]]
@@ -82,7 +82,7 @@ def _infer_media_type(
8282

8383
types = {type(annotation) for annotation in annotations}
8484
data = ImageData
85-
if TextEntity in types:
85+
if (TextEntity in types) or (ConversationEntity in types):
8686
data = TextData
8787
elif VideoClassificationAnnotation in types or VideoObjectAnnotation in types:
8888
data = VideoData

labelbox/data/serialization/ndjson/objects.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from io import BytesIO
33
from typing import Any, Dict, List, Tuple, Union, Optional
44
import base64
5+
from labelbox.data.annotation_types.ner.conversation_entity import ConversationEntity
56
from labelbox.data.mixins import ConfidenceMixin
67
import numpy as np
78

@@ -399,6 +400,35 @@ def from_common(cls,
399400
confidence=confidence)
400401

401402

403+
class NDConversationEntity(NDTextEntity):
404+
message_id: str
405+
406+
def to_common(self) -> ConversationEntity:
407+
return ConversationEntity(start=self.location.start,
408+
end=self.location.end,
409+
message_id=self.message_id)
410+
411+
@classmethod
412+
def from_common(
413+
cls,
414+
conversation_entity: ConversationEntity,
415+
classifications: List[ClassificationAnnotation],
416+
name: str,
417+
feature_schema_id: Cuid,
418+
extra: Dict[str, Any],
419+
data: Union[ImageData, TextData],
420+
confidence: Optional[float] = None) -> "NDConversationEntity":
421+
return cls(location=Location(start=conversation_entity.start,
422+
end=conversation_entity.end),
423+
message_id=conversation_entity.message_id,
424+
dataRow=DataRow(id=data.uid),
425+
name=name,
426+
schema_id=feature_schema_id,
427+
uuid=extra.get('uuid'),
428+
classifications=classifications,
429+
confidence=confidence)
430+
431+
402432
class NDObject:
403433

404434
@staticmethod
@@ -463,6 +493,7 @@ def lookup_object(
463493
Mask: NDMask,
464494
TextEntity: NDTextEntity,
465495
DocumentEntity: NDDocumentEntity,
496+
ConversationEntity: NDConversationEntity,
466497
}.get(type(annotation.value))
467498
if result is None:
468499
raise TypeError(
@@ -471,7 +502,11 @@ def lookup_object(
471502
return result
472503

473504

505+
# NOTE: Deserialization of subclasses in pydantic is a known PIA, see here https://blog.devgenius.io/deserialize-child-classes-with-pydantic-that-gonna-work-784230e1cf83
506+
# I could implement the registry approach suggested there, but I found that if I list subclass (that has more attributes) before the parent class, it works
507+
# This is a bit of a hack, but it works for now
508+
NDEntityType = Union[NDConversationEntity, NDTextEntity]
474509
NDObjectType = Union[NDLine, NDPolygon, NDPoint, NDRectangle, NDMask,
475-
NDTextEntity, NDDocumentEntity]
510+
NDEntityType, NDDocumentEntity]
476511

477512
NDFrameObjectType = NDFrameRectangle, NDFramePoint, NDFrameLine

tests/assets/conversation-1.json

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
{
2+
"type":
3+
"application/vnd.labelbox.conversational",
4+
"version":
5+
1,
6+
"messages": [{
7+
"user": {
8+
"userId": "0",
9+
"name": "User"
10+
},
11+
"canLabel":
12+
true,
13+
"content":
14+
"I'd really like to take my client out to a nice restaurant that serves indian food.",
15+
"messageId":
16+
"0",
17+
"align":
18+
"left"
19+
}, {
20+
"user": {
21+
"userId": "1",
22+
"name": "Bot"
23+
},
24+
"canLabel":
25+
false,
26+
"content":
27+
"I show many restaurants that serve Indian food in that price range. What area would you like to travel to?",
28+
"messageId":
29+
"1",
30+
"align":
31+
"right"
32+
}, {
33+
"user": {
34+
"userId": "0",
35+
"name": "User"
36+
},
37+
"canLabel":
38+
true,
39+
"content":
40+
"I am looking for an expensive indian restaurant in the area of centre.",
41+
"messageId":
42+
"2",
43+
"align":
44+
"left"
45+
}, {
46+
"user": {
47+
"userId": "1",
48+
"name": "Bot"
49+
},
50+
"canLabel":
51+
false,
52+
"content":
53+
"Might I recommend Saffron Brasserie? That is an expensive Indian restaurant in the center of town. I can book a table for you, if you like.",
54+
"messageId":
55+
"3",
56+
"align":
57+
"right"
58+
}, {
59+
"user": {
60+
"userId": "0",
61+
"name": "User"
62+
},
63+
"canLabel": true,
64+
"content": "Sure thing, please book for 6 people at 19:30 on Saturday.",
65+
"messageId": "4",
66+
"align": "left"
67+
}, {
68+
"user": {
69+
"userId": "1",
70+
"name": "Bot"
71+
},
72+
"canLabel":
73+
false,
74+
"content":
75+
"Booking was successful. The table will be reserved for 15 minutes. Your Reference number is : RF00JUFQ.",
76+
"messageId":
77+
"5",
78+
"align":
79+
"right"
80+
}, {
81+
"user": {
82+
"userId": "0",
83+
"name": "User"
84+
},
85+
"canLabel":
86+
true,
87+
"content":
88+
"Okay great! Thank you so much. Could you also help me find a 3 star hotel in the area. I don't need wifi either.",
89+
"messageId":
90+
"6",
91+
"align":
92+
"left"
93+
}, {
94+
"user": {
95+
"userId": "1",
96+
"name": "Bot"
97+
},
98+
"canLabel":
99+
false,
100+
"content":
101+
"The alpha-milton guest house is in the north and is moderately priced. It has 3 stars and no internet or parking. Would you like to book a room?",
102+
"messageId":
103+
"7",
104+
"align":
105+
"right"
106+
}, {
107+
"user": {
108+
"userId": "0",
109+
"name": "User"
110+
},
111+
"canLabel": true,
112+
"content": "That sounds great. Please book that now.",
113+
"messageId": "8",
114+
"align": "left"
115+
}, {
116+
"user": {
117+
"userId": "1",
118+
"name": "Bot"
119+
},
120+
"canLabel": false,
121+
"content": "May I ask how many people are in your group?",
122+
"messageId": "9",
123+
"align": "right"
124+
}, {
125+
"user": {
126+
"userId": "0",
127+
"name": "User"
128+
},
129+
"canLabel": true,
130+
"content": "I have 6 people in my group. ",
131+
"messageId": "10",
132+
"align": "left"
133+
}, {
134+
"user": {
135+
"userId": "1",
136+
"name": "Bot"
137+
},
138+
"canLabel": false,
139+
"content": "How many days would you like to stay?",
140+
"messageId": "11",
141+
"align": "right"
142+
}, {
143+
"user": {
144+
"userId": "0",
145+
"name": "User"
146+
},
147+
"canLabel": true,
148+
"content": "2 nights, starting the same day as the reservation.",
149+
"messageId": "12",
150+
"align": "left"
151+
}, {
152+
"user": {
153+
"userId": "1",
154+
"name": "Bot"
155+
},
156+
"canLabel":
157+
false,
158+
"content":
159+
"I'm sorry. It looks like they're full. Would you like me to look for something else?",
160+
"messageId":
161+
"13",
162+
"align":
163+
"right"
164+
}, {
165+
"user": {
166+
"userId": "0",
167+
"name": "User"
168+
},
169+
"canLabel":
170+
true,
171+
"content":
172+
"Yes please. Is there something else available in that area? ",
173+
"messageId":
174+
"14",
175+
"align":
176+
"left"
177+
}, {
178+
"user": {
179+
"userId": "1",
180+
"name": "Bot"
181+
},
182+
"canLabel":
183+
false,
184+
"content":
185+
"I'm sorry, it looks like that is the only 3 star hotel in that area, would you like me to look somewhere else?",
186+
"messageId":
187+
"15",
188+
"align":
189+
"right"
190+
}, {
191+
"user": {
192+
"userId": "0",
193+
"name": "User"
194+
},
195+
"canLabel":
196+
true,
197+
"content":
198+
"Can we try for 1 night instead of 2? I'll need the reference number please.",
199+
"messageId":
200+
"16",
201+
"align":
202+
"left"
203+
}, {
204+
"user": {
205+
"userId": "1",
206+
"name": "Bot"
207+
},
208+
"canLabel":
209+
false,
210+
"content":
211+
"I was able to get one night, the reference number is 9XVT8M5T.",
212+
"messageId":
213+
"17",
214+
"align":
215+
"right"
216+
}, {
217+
"user": {
218+
"userId": "0",
219+
"name": "User"
220+
},
221+
"canLabel": true,
222+
"content": "Thank you so much!",
223+
"messageId": "18",
224+
"align": "left"
225+
}, {
226+
"user": {
227+
"userId": "1",
228+
"name": "Bot"
229+
},
230+
"canLabel": false,
231+
"content": "I'm glad to help, you're welcome!",
232+
"messageId": "19",
233+
"align": "right"
234+
}]
235+
}

0 commit comments

Comments
 (0)