Skip to content

Commit 3ee25d1

Browse files
author
Val Brodsky
committed
Add an ability to create a model chat evaluation ontology
Add create_model_chat_evaluation_ontology_from_feature_schemas
1 parent 70b1ac0 commit 3ee25d1

File tree

5 files changed

+211
-6
lines changed

5 files changed

+211
-6
lines changed

libs/labelbox/src/labelbox/client.py

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from labelbox.schema.slice import CatalogSlice, ModelSlice
5353
from labelbox.schema.task import Task
5454
from labelbox.schema.user import User
55+
from labelbox.schema.editor_task_type import EditorTaskType
5556

5657
logger = logging.getLogger(__name__)
5758

@@ -735,6 +736,12 @@ def create_project(self, **kwargs) -> Project:
735736
} if media_type else {})
736737
})
737738

739+
def create_model_chat_project(self, **kwargs) -> Project:
740+
kwargs["media_type"] = media_type.MediaType.Conversational
741+
kwargs["editor_task_type"] = editor_task_type.EditorTaskType.ModelChatEvaluation
742+
743+
return self.create_project(**kwargs)
744+
738745
def get_roles(self) -> List[Role]:
739746
"""
740747
Returns:
@@ -938,10 +945,12 @@ def rootSchemaPayloadToFeatureSchema(client, payload):
938945
rootSchemaPayloadToFeatureSchema,
939946
['rootSchemaNodes', 'nextCursor'])
940947

941-
def create_ontology_from_feature_schemas(self,
942-
name,
943-
feature_schema_ids,
944-
media_type=None) -> Ontology:
948+
def create_ontology_from_feature_schemas(
949+
self,
950+
name,
951+
feature_schema_ids,
952+
media_type: MediaType = None,
953+
editor_task_type: EditorTaskType = None) -> Ontology:
945954
"""
946955
Creates an ontology from a list of feature schema ids
947956
@@ -979,7 +988,10 @@ def create_ontology_from_feature_schemas(self,
979988
"Neither `tool` or `classification` found in the normalized feature schema"
980989
)
981990
normalized = {'tools': tools, 'classifications': classifications}
982-
return self.create_ontology(name, normalized, media_type)
991+
return self.create_ontology(name=name,
992+
normalized=normalized,
993+
media_type=media_type,
994+
editor_task_type=editor_task_type)
983995

984996
def delete_unused_feature_schema(self, feature_schema_id: str) -> None:
985997
"""
@@ -1166,7 +1178,11 @@ def get_unused_feature_schemas(self, after: str = None) -> List[str]:
11661178
"Failed to get unused feature schemas, message: " +
11671179
str(response.json()['message']))
11681180

1169-
def create_ontology(self, name, normalized, media_type=None) -> Ontology:
1181+
def create_ontology(self,
1182+
name,
1183+
normalized,
1184+
media_type: MediaType = None,
1185+
editor_task_type: EditorTaskType = None) -> Ontology:
11701186
"""
11711187
Creates an ontology from normalized data
11721188
>>> normalized = {"tools" : [{'tool': 'polygon', 'name': 'cat', 'color': 'black'}], "classifications" : []}
@@ -1194,6 +1210,13 @@ def create_ontology(self, name, normalized, media_type=None) -> Ontology:
11941210
else:
11951211
raise get_media_type_validation_error(media_type)
11961212

1213+
if editor_task_type:
1214+
if EditorTaskType.is_supported(editor_task_type):
1215+
editor_task_type = editor_task_type.value
1216+
else:
1217+
raise EditorTaskType.get_editor_task_type_validation_error(
1218+
editor_task_type)
1219+
11971220
query_str = """mutation upsertRootSchemaNodePyApi($data: UpsertOntologyInput!){
11981221
upsertOntology(data: $data){ %s }
11991222
} """ % query.results_query_part(Entity.Ontology)
@@ -1204,9 +1227,65 @@ def create_ontology(self, name, normalized, media_type=None) -> Ontology:
12041227
'mediaType': media_type
12051228
}
12061229
}
1230+
if editor_task_type:
1231+
params['data']['editorTaskType'] = editor_task_type
1232+
12071233
res = self.execute(query_str, params)
12081234
return Entity.Ontology(self, res['upsertOntology'])
12091235

1236+
def create_model_chat_evaluation_ontology(self, name, normalized):
1237+
"""
1238+
Creates a model chat evalutation ontology from normalized data
1239+
>>> normalized = {"tools" : [{'tool': 'message-single-selection', 'name': 'model output single selection', 'color': '#ff0000',},
1240+
{'tool': 'message-multi-selection', 'name': 'model output multi selection', 'color': '#00ff00',},
1241+
{'tool': 'message-ranking', 'name': 'model output multi ranking', 'color': '#0000ff',}]
1242+
}
1243+
>>> ontology = client.create_ontology("ontology-name", normalized)
1244+
1245+
Or use the ontology builder
1246+
>>> ontology_builder = OntologyBuilder(tools=[
1247+
Tool(tool=Tool.Type.MESSAGE_SINGLE_SELECTION,
1248+
name="model output single selection"),
1249+
Tool(tool=Tool.Type.MESSAGE_MULTI_SELECTION,
1250+
name="model output multi selection"),
1251+
Tool(tool=Tool.Type.MESSAGE_RANKING,
1252+
name="model output multi ranking"),
1253+
],)
1254+
1255+
>>> ontology = client.create_model_chat_evaluation_ontology("Multi-chat ontology", ontology_builder.asdict())
1256+
1257+
Args:
1258+
name (str): Name of the ontology
1259+
normalized (dict): A normalized ontology payload. See above for details.
1260+
Returns:
1261+
The created Ontology
1262+
"""
1263+
1264+
return self.create_ontology(
1265+
name=name,
1266+
normalized=normalized,
1267+
media_type=MediaType.Conversational,
1268+
editor_task_type=EditorTaskType.ModelChatEvaluation)
1269+
1270+
def create_model_chat_evaluation_ontology_from_feature_schemas(
1271+
self, name, feature_schema_ids):
1272+
"""
1273+
Creates an ontology from a list of feature schema ids
1274+
1275+
Args:
1276+
name (str): Name of the ontology
1277+
feature_schema_ids (List[str]): List of feature schema ids corresponding to
1278+
top level tools and classifications to include in the ontology
1279+
Returns:
1280+
The created Ontology
1281+
"""
1282+
1283+
return self.create_ontology_from_feature_schemas(
1284+
name=name,
1285+
feature_schema_ids=feature_schema_ids,
1286+
media_type=MediaType.Conversational,
1287+
editor_task_type=EditorTaskType.ModelChatEvaluation)
1288+
12101289
def create_feature_schema(self, normalized):
12111290
"""
12121291
Creates a feature schema from normalized data.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from enum import Enum
2+
3+
4+
class EditorTaskType(Enum):
5+
ModelEvaluationWithUploadedAsset = "MODEL_EVALUATION_WITH_UPLOADED_ASSET",
6+
ResponseCreation = "RESPONSE_CREATION",
7+
ModelChatEvaluation = "MODEL_CHAT_EVALUATION"
8+
9+
@classmethod
10+
def is_supported(cls, value):
11+
return isinstance(value, cls)
12+
13+
@classmethod
14+
def get_media_type_validation_error(cls, editor_task_type):
15+
return TypeError(f"{editor_task_type}: is not a valid media type. Use"
16+
f" any of {EditorTaskType.__members__.items()}"
17+
" from EditorTaskType.")

libs/labelbox/src/labelbox/schema/ontology.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ class Type(Enum):
252252
LINE = "line"
253253
NER = "named-entity"
254254
RELATIONSHIP = "edge"
255+
MESSAGE_SINGLE_SELECTION = 'message-single-selection'
256+
MESSAGE_MULTI_SELECTION = 'message-multi-selection'
257+
MESSAGE_RANKING = 'message-ranking'
255258

256259
tool: Type
257260
name: str

libs/labelbox/tests/integration/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,25 @@ def _upload_invalid_data_rows_for_dataset(dataset: Dataset):
339339
return _upload_invalid_data_rows_for_dataset
340340

341341

342+
@pytest.fixture
343+
def model_chat_evaluation_ontology(client, rand_gen):
344+
ontology_name = f"test-model-chat-evaluation-ontology-{rand_gen(str)}"
345+
ontology_builder = OntologyBuilder(tools=[
346+
Tool(tool=Tool.Type.MESSAGE_SINGLE_SELECTION,
347+
name="model output single selection"),
348+
Tool(tool=Tool.Type.MESSAGE_MULTI_SELECTION,
349+
name="model output multi selection"),
350+
Tool(tool=Tool.Type.MESSAGE_RANKING, name="model output multi ranking"),
351+
],)
352+
353+
ontology = client.create_model_chat_evaluation_ontology(
354+
ontology_name, ontology_builder.asdict())
355+
356+
yield ontology
357+
358+
client.delete_unused_ontology(ontology.uid)
359+
360+
342361
def pytest_configure():
343362
pytest.report = defaultdict(int)
344363

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import pytest
2+
from labelbox import OntologyBuilder, Tool
3+
from labelbox import MediaType
4+
5+
6+
def test_create_model_chat_evaluation_ontology(model_chat_evaluation_ontology):
7+
ontology = model_chat_evaluation_ontology
8+
9+
# here we are essentially testing the ontology creation which is a fixture
10+
assert ontology
11+
assert ontology.name
12+
assert len(ontology.tools()) == 3
13+
for tool in ontology.tools():
14+
assert tool.schema_id
15+
assert tool.feature_schema_id
16+
17+
18+
@pytest.fixture
19+
def tools_json():
20+
tools = [{
21+
'tool': 'message-single-selection',
22+
'name': 'model output single selection',
23+
'required': False,
24+
'color': '#ff0000',
25+
'classifications': [],
26+
'schemaNodeId': None,
27+
'featureSchemaId': None
28+
}, {
29+
'tool': 'message-multi-selection',
30+
'name': 'model output multi selection',
31+
'required': False,
32+
'color': '#00ff00',
33+
'classifications': [],
34+
'schemaNodeId': None,
35+
'featureSchemaId': None
36+
}, {
37+
'tool': 'message-ranking',
38+
'name': 'model output multi ranking',
39+
'required': False,
40+
'color': '#0000ff',
41+
'classifications': [],
42+
'schemaNodeId': None,
43+
'featureSchemaId': None
44+
}]
45+
46+
return tools
47+
48+
49+
@pytest.fixture
50+
def features_from_json(client, tools_json):
51+
tools = tools_json
52+
features = {client.create_feature_schema(t) for t in tools if t}
53+
54+
yield features
55+
56+
for f in features:
57+
client.delete_unused_feature_schema(f.uid)
58+
59+
60+
@pytest.fixture
61+
def ontology_from_feature_ids(client, features_from_json):
62+
feature_ids = {f.uid for f in features_from_json}
63+
ontology = client.create_model_chat_evaluation_ontology_from_feature_schemas(
64+
name="test-model-chat-evaluation-ontology{rand_gen(str)}",
65+
feature_schema_ids=feature_ids,
66+
)
67+
68+
yield ontology
69+
70+
client.delete_unused_ontology(ontology.uid)
71+
72+
73+
def test_ontology_create_feature_schema(ontology_from_feature_ids, features_from_json, tools_json):
74+
created_ontology = ontology_from_feature_ids
75+
feature_schema_ids = {f.uid for f in features_from_json}
76+
tools_normalized = created_ontology.normalized['tools']
77+
tools = tools_json
78+
79+
for tool in tools:
80+
generated_tool = next(
81+
t for t in tools_normalized if t['name'] == tool['name'])
82+
assert generated_tool['schemaNodeId'] is not None
83+
assert generated_tool['featureSchemaId'] in feature_schema_ids
84+
assert generated_tool['tool'] == tool['tool']
85+
assert generated_tool['name'] == tool['name']
86+
assert generated_tool['required'] == tool['required']
87+
assert generated_tool['color'] == tool['color']

0 commit comments

Comments
 (0)