Skip to content

Commit 00f8c6d

Browse files
author
Val Brodsky
committed
Skip setup_ editor and ontology for chat evaluation projects
1 parent ee4cece commit 00f8c6d

File tree

4 files changed

+219
-20
lines changed

4 files changed

+219
-20
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from enum import Enum
2+
from typing import Optional
3+
4+
from labelbox.schema.media_type import MediaType
5+
6+
7+
class OntologyKind(Enum):
8+
ModelEvaluation = "MODEL_EVALUATION"
9+
Missing = None
10+
11+
@classmethod
12+
def is_supported(cls, value):
13+
return isinstance(value, cls)
14+
15+
@classmethod
16+
def get_ontology_kind_validation_error(cls, ontology_kind):
17+
return TypeError(f"{ontology_kind}: is not a valid ontology kind. Use"
18+
f" any of {OntologyKind.__members__.items()}"
19+
" from OntologyKind.")
20+
21+
22+
class EditorTaskType(Enum):
23+
ModelChatEvaluation = "MODEL_CHAT_EVALUATION"
24+
Missing = None
25+
26+
@classmethod
27+
def is_supported(cls, value):
28+
return isinstance(value, cls)
29+
30+
31+
class EditorTaskTypeMapper:
32+
33+
@staticmethod
34+
def to_editor_task_type(ontology_kind: OntologyKind,
35+
media_type: MediaType) -> EditorTaskType:
36+
if ontology_kind and OntologyKind.is_supported(
37+
ontology_kind) and media_type and MediaType.is_supported(
38+
media_type):
39+
editor_task_type = EditorTaskTypeMapper.map_to_editor_task_type(
40+
ontology_kind, media_type)
41+
else:
42+
editor_task_type = EditorTaskType.Missing
43+
44+
return editor_task_type
45+
46+
@staticmethod
47+
def map_to_editor_task_type(
48+
onotology_kind: OntologyKind,
49+
media_type: MediaType) -> Optional[EditorTaskType]:
50+
if onotology_kind == OntologyKind.ModelEvaluation and media_type == MediaType.Conversational:
51+
return EditorTaskType.ModelChatEvaluation
52+
else:
53+
return EditorTaskType.Missing

libs/labelbox/src/labelbox/schema/project.py

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from labelbox.schema.resource_tag import ResourceTag
3838
from labelbox.schema.task import Task
3939
from labelbox.schema.task_queue import TaskQueue
40-
from labelbox.schema.ontology_kind import (EditorTaskType)
40+
from labelbox.schema.ontology_kind import (EditorTaskType, OntologyKind)
4141

4242
if TYPE_CHECKING:
4343
from labelbox import BulkImportRequest
@@ -133,6 +133,9 @@ class Project(DbObject, Updateable, Deletable):
133133
#
134134
_wait_processing_max_seconds = 3600
135135

136+
def is_chat_evalution(self) -> bool:
137+
return self.media_type == MediaType.Conversational and self.editor_task_type == EditorTaskType.ModelChatEvaluation
138+
136139
def update(self, **kwargs):
137140
""" Updates this project with the specified attributes
138141
@@ -734,27 +737,35 @@ def setup_editor(self, ontology) -> None:
734737
Args:
735738
ontology (Ontology): The ontology to attach to the project
736739
"""
737-
if self.labeling_frontend() is not None:
738-
raise ResourceConflict("Editor is already set up.")
739740

740-
labeling_frontend = next(
741-
self.client.get_labeling_frontends(
742-
where=Entity.LabelingFrontend.name == "Editor"))
743-
self.labeling_frontend.connect(labeling_frontend)
741+
if self.labeling_frontend(
742+
) is not None and not self.is_chat_evalution():
743+
raise ResourceConflict("Editor is already set up.")
744744

745-
LFO = Entity.LabelingFrontendOptions
746-
self.client._create(
747-
LFO, {
748-
LFO.project:
749-
self,
750-
LFO.labeling_frontend:
751-
labeling_frontend,
752-
LFO.customization_options:
753-
json.dumps({
754-
"tools": [],
755-
"classifications": []
756-
})
757-
})
745+
if not self.is_chat_evalution():
746+
labeling_frontend = next(
747+
self.client.get_labeling_frontends(
748+
where=Entity.LabelingFrontend.name == "Editor"))
749+
self.labeling_frontend.connect(labeling_frontend)
750+
751+
LFO = Entity.LabelingFrontendOptions
752+
self.client._create(
753+
LFO, {
754+
LFO.project:
755+
self,
756+
LFO.labeling_frontend:
757+
labeling_frontend,
758+
LFO.customization_options:
759+
json.dumps({
760+
"tools": [],
761+
"classifications": []
762+
})
763+
})
764+
else:
765+
warnings.warn("""
766+
Skipping editor setup for a chat evaluation project.
767+
Editor was setup automatically.
768+
""")
758769

759770
query_str = """mutation ConnectOntologyPyApi($projectId: ID!, $ontologyId: ID!){
760771
project(where: {id: $projectId}) {connectOntology(ontologyId: $ontologyId) {id}}}"""
@@ -776,6 +787,14 @@ def setup(self, labeling_frontend, labeling_frontend_options) -> None:
776787
to `str` using `json.dumps`.
777788
"""
778789

790+
if self.is_chat_evalution():
791+
warnings.warn("""
792+
This project is a chat evaluation project.
793+
Editor was setup automatically.
794+
No need to call this method.
795+
""")
796+
return
797+
779798
if self.labeling_frontend() is not None:
780799
raise ResourceConflict("Editor is already set up.")
781800

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import pytest
2+
from labelbox import OntologyBuilder, Tool
3+
from labelbox import MediaType
4+
from labelbox.schema.ontology_kind import OntologyKind
5+
from labelbox.schema.labeling_frontend import LabelingFrontend
6+
7+
8+
def test_create_chat_evaluation_ontology_project(client,
9+
chat_evaluation_ontology,
10+
chat_evaluation_project):
11+
ontology = chat_evaluation_ontology
12+
13+
# here we are essentially testing the ontology creation which is a fixture
14+
assert ontology
15+
assert ontology.name
16+
assert len(ontology.tools()) == 3
17+
for tool in ontology.tools():
18+
assert tool.schema_id
19+
assert tool.feature_schema_id
20+
21+
project = chat_evaluation_project
22+
# editor = list(
23+
# client.get_labeling_frontends(
24+
# where=LabelingFrontend.name == "editor"))[0]
25+
# project.setup(editor, ontology)
26+
27+
project.setup_editor(ontology)
28+
29+
assert project.labeling_frontend().name == "Editor"
30+
assert project.ontology().name == ontology.name
31+
32+
33+
@pytest.fixture
34+
def tools_json():
35+
tools = [{
36+
'tool': 'message-single-selection',
37+
'name': 'model output single selection',
38+
'required': False,
39+
'color': '#ff0000',
40+
'classifications': [],
41+
'schemaNodeId': None,
42+
'featureSchemaId': None
43+
}, {
44+
'tool': 'message-multi-selection',
45+
'name': 'model output multi selection',
46+
'required': False,
47+
'color': '#00ff00',
48+
'classifications': [],
49+
'schemaNodeId': None,
50+
'featureSchemaId': None
51+
}, {
52+
'tool': 'message-ranking',
53+
'name': 'model output multi ranking',
54+
'required': False,
55+
'color': '#0000ff',
56+
'classifications': [],
57+
'schemaNodeId': None,
58+
'featureSchemaId': None
59+
}]
60+
61+
return tools
62+
63+
64+
@pytest.fixture
65+
def features_from_json(client, tools_json):
66+
tools = tools_json
67+
features = {client.create_feature_schema(t) for t in tools if t}
68+
69+
yield features
70+
71+
for f in features:
72+
client.delete_unused_feature_schema(f.uid)
73+
74+
75+
@pytest.fixture
76+
def ontology_from_feature_ids(client, features_from_json):
77+
feature_ids = {f.uid for f in features_from_json}
78+
ontology = client.create_ontology_from_feature_schemas(
79+
name="test-model-chat-evaluation-ontology{rand_gen(str)}",
80+
feature_schema_ids=feature_ids,
81+
media_type=MediaType.Conversational,
82+
ontology_kind=OntologyKind.ModelEvaluation,
83+
)
84+
85+
yield ontology
86+
87+
client.delete_unused_ontology(ontology.uid)
88+
89+
90+
def test_ontology_create_feature_schema(ontology_from_feature_ids,
91+
features_from_json, tools_json):
92+
created_ontology = ontology_from_feature_ids
93+
feature_schema_ids = {f.uid for f in features_from_json}
94+
tools_normalized = created_ontology.normalized['tools']
95+
tools = tools_json
96+
97+
for tool in tools:
98+
generated_tool = next(
99+
t for t in tools_normalized if t['name'] == tool['name'])
100+
assert generated_tool['schemaNodeId'] is not None
101+
assert generated_tool['featureSchemaId'] in feature_schema_ids
102+
assert generated_tool['tool'] == tool['tool']
103+
assert generated_tool['name'] == tool['name']
104+
assert generated_tool['required'] == tool['required']
105+
assert generated_tool['color'] == tool['color']
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from labelbox.schema.ontology_kind import OntologyKind, EditorTaskType, EditorTaskTypeMapper
2+
from labelbox.schema.media_type import MediaType
3+
4+
5+
def test_ontology_kind_conversions_from_editor_task_type():
6+
ontology_kind = OntologyKind.ModelEvaluation
7+
media_type = MediaType.Conversational
8+
editor_task_type = EditorTaskTypeMapper.to_editor_task_type(
9+
ontology_kind, media_type)
10+
assert editor_task_type == EditorTaskType.ModelChatEvaluation
11+
12+
ontology_kind = OntologyKind.Missing
13+
media_type = MediaType.Image
14+
editor_task_type = EditorTaskTypeMapper.to_editor_task_type(
15+
ontology_kind, media_type)
16+
assert editor_task_type == EditorTaskType.Missing
17+
18+
ontology_kind = OntologyKind.ModelEvaluation
19+
media_type = MediaType.Video
20+
editor_task_type = EditorTaskTypeMapper.to_editor_task_type(
21+
ontology_kind, media_type)
22+
assert editor_task_type == EditorTaskType.Missing

0 commit comments

Comments
 (0)