Skip to content

Commit 620d3bc

Browse files
hakan458hakan458
andauthored
fix: BROS-392: Add Chat tag to label interface + fix prediction valid… (#584)
Co-authored-by: hakan458 <[email protected]>
1 parent 7cb675b commit 620d3bc

File tree

4 files changed

+70
-0
lines changed

4 files changed

+70
-0
lines changed

src/label_studio_sdk/label_interface/control_tags.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"taxonomy": "TaxonomyTag",
4040
"textarea": "TextAreaTag",
4141
"timeserieslabels": "TimeSeriesLabelsTag",
42+
"chatmessage": "ChatMessageTag",
4243
}
4344

4445

@@ -950,6 +951,43 @@ def to_json_schema(self):
950951
}
951952

952953

954+
class ChatMessageContent(BaseModel):
955+
role: str
956+
content: str
957+
createdAt: Optional[int] = None
958+
959+
960+
class ChatMessageValue(BaseModel):
961+
chatmessage: ChatMessageContent
962+
963+
964+
class ChatMessageTag(ControlTag):
965+
"""Control tag for chat messages targeting a `<Chat>` object.
966+
967+
This tag is a hybrid where `from_name == to_name` and `type == 'chatmessage'`.
968+
"""
969+
tag: str = "ChatMessage"
970+
_value_class: Type[ChatMessageValue] = ChatMessageValue
971+
972+
def to_json_schema(self):
973+
return {
974+
"type": "object",
975+
"required": ["chatmessage"],
976+
"properties": {
977+
"chatmessage": {
978+
"type": "object",
979+
"required": ["role", "content"],
980+
"properties": {
981+
"role": {"type": "string"},
982+
"content": {"type": "string"},
983+
"createdAt": {"type": "number"}
984+
}
985+
}
986+
},
987+
"description": f"Chat message for {self.to_name[0]}"
988+
}
989+
990+
953991
class RelationsTag(ControlTag):
954992
""" """
955993
tag: str = "Relations"

src/label_studio_sdk/label_interface/data_examples.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
"Header": "Task header",
1111
"Paragraphs": [{"author": "Alice", "text": "Hi, Bob."}, {"author": "Bob", "text": "Hello, Alice!"}, {"author": "Alice", "text": "What's up?"}, {"author": "Bob", "text": "Good. Ciao!"}, {"author": "Alice", "text": "Bye, Bob."}],
1212
"ParagraphsUrl": "<HOSTNAME>/samples/paragraphs.json?",
13+
"Chat": [
14+
{"role": "user", "content": "Hi there!", "createdAt": 1710000000000},
15+
{"role": "assistant", "content": "Hello! How can I help you today?", "createdAt": 1710000005000},
16+
{"role": "user", "content": "Can you summarize our onboarding checklist?", "createdAt": 1710000010000},
17+
{"role": "assistant", "content": "Sure. It includes account setup, roles, labeling instructions, GT tasks, overlap and review.", "createdAt": 1710000015000}
18+
],
1319
"Table": {"Card number": 18799210, "First name": "Max", "Last name": "Nobel"},
1420
"$videoHack": "<video src='<HOSTNAME>/static/samples/opossum_snow.mp4' width=100% controls>",
1521
"Video": "<HOSTNAME>/static/samples/opossum_snow.mp4",
@@ -85,6 +91,10 @@
8591
"AudioPlus": "<HOSTNAME>/static/samples/game.wav",
8692
"Header": "Task header",
8793
"Paragraphs": [{"author": "Alice", "text": "Hi, Bob."}, {"author": "Bob", "text": "Hello, Alice!"}, {"author": "Alice", "text": "What's up?"}, {"author": "Bob", "text": "Good. Ciao!"}, {"author": "Alice", "text": "Bye, Bob."}],
94+
"Chat": [
95+
{"role": "user", "content": "Hello!", "createdAt": 1710000000000},
96+
{"role": "assistant", "content": "Hi! What can I do for you?", "createdAt": 1710000004000}
97+
],
8898
"Table": {"Card number": 18799210, "First name": "Max", "Last name": "Nobel"},
8999
"$videoHack": "<video src='static/samples/opossum_snow.mp4' width=100% controls>",
90100
"Video": "<HOSTNAME>/static/samples/opossum_snow.mp4",

src/label_studio_sdk/label_interface/interface.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
from .base import LabelStudioTag
2929
from .control_tags import (
30+
ChatMessageTag,
3031
ControlTag,
3132
ChoicesTag,
3233
LabelsTag,
@@ -623,6 +624,18 @@ def parse(self, config_string: str) -> Tuple[Dict, Dict, Dict, etree._Element]:
623624
if lb:
624625
labels[lb.parent_name][lb.value] = lb
625626

627+
# Special handling: auto-create ChatMessage control for each Chat object
628+
chat_object_names = [name for name, obj in objects.items() if getattr(obj, 'tag', '').lower() == 'chat']
629+
for name in chat_object_names:
630+
if name not in controls:
631+
controls[name] = ChatMessageTag(
632+
tag='ChatMessage',
633+
name=name,
634+
to_name=[name],
635+
attr={"name": name, "toName": name}
636+
)
637+
638+
626639
return controls, objects, labels, xml_tree
627640

628641
@classmethod

src/label_studio_sdk/label_interface/object_tags.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"list": "ListTag",
2121
"paragraphs": "ParagraphsTag",
2222
"timeseries": "TimeSeriesTag",
23+
"chat": "ChatTag",
2324
}
2425

2526
_DATA_EXAMPLES = None
@@ -306,3 +307,11 @@ def _generate_example(self, examples, only_urls=False):
306307
else:
307308
# data is JSON
308309
return generate_time_series_json(time_column, value_columns, time_format)
310+
311+
class ChatTag(ObjectTag):
312+
""" """
313+
tag: str = "Chat"
314+
315+
def _generate_example(self, examples, only_urls=False):
316+
""" """
317+
return examples.get("Chat")

0 commit comments

Comments
 (0)