2121from labelbox .adv_client import AdvClient
2222from labelbox .orm import query
2323from labelbox .orm .db_object import DbObject
24- from labelbox .orm .model import Entity
24+ from labelbox .orm .model import Entity , Field
2525from labelbox .pagination import PaginatedCollection
2626from labelbox .schema import role
2727from labelbox .schema .conflict_resolution_strategy import ConflictResolutionStrategy
5252from labelbox .schema .slice import CatalogSlice , ModelSlice
5353from labelbox .schema .task import Task
5454from labelbox .schema .user import User
55+ from labelbox .schema .ontology_kind import (OntologyKind , EditorTaskTypeMapper ,
56+ EditorTaskType )
5557
5658logger = logging .getLogger (__name__ )
5759
@@ -563,14 +565,16 @@ def get_labeling_frontends(self, where=None) -> List[LabelingFrontend]:
563565 """
564566 return self ._get_all (Entity .LabelingFrontend , where )
565567
566- def _create (self , db_object_type , data ):
568+ def _create (self , db_object_type , data , extra_params = {} ):
567569 """ Creates an object on the server. Attribute values are
568570 passed as keyword arguments:
569571
570572 Args:
571573 db_object_type (type): A DbObjectType subtype.
572574 data (dict): Keys are attributes or their names (in Python,
573575 snake-case convention) and values are desired attribute values.
576+ extra_params (dict): Additional parameters to pass to GraphQL.
577+ These have to be Field(...): value pairs.
574578 Returns:
575579 A new object of the given DB object type.
576580 Raises:
@@ -585,6 +589,7 @@ def _create(self, db_object_type, data):
585589 for attr , value in data .items ()
586590 }
587591
592+ data = {** data , ** extra_params }
588593 query_string , params = query .create (db_object_type , data )
589594 res = self .execute (query_string , params )
590595 res = res ["create%s" % db_object_type .type_name ()]
@@ -699,17 +704,26 @@ def create_project(self, **kwargs) -> Project:
699704 )
700705
701706 media_type = kwargs .get ("media_type" )
702- if media_type :
703- if MediaType .is_supported (media_type ):
704- media_type = media_type .value
705- else :
706- raise TypeError (f"{ media_type } is not a valid media type. Use"
707- f" any of { MediaType .get_supported_members ()} "
708- " from MediaType. Example: MediaType.Image." )
707+ if media_type and MediaType .is_supported (media_type ):
708+ media_type_value = media_type .value
709+ elif media_type :
710+ raise TypeError (f"{ media_type } is not a valid media type. Use"
711+ f" any of { MediaType .get_supported_members ()} "
712+ " from MediaType. Example: MediaType.Image." )
709713 else :
710714 logger .warning (
711715 "Creating a project without specifying media_type"
712716 " through this method will soon no longer be supported." )
717+ media_type_value = None
718+
719+ ontology_kind = kwargs .pop ("ontology_kind" , None )
720+ if ontology_kind and OntologyKind .is_supported (ontology_kind ):
721+ editor_task_type_value = EditorTaskTypeMapper .to_editor_task_type (
722+ ontology_kind , media_type ).value
723+ elif ontology_kind :
724+ raise OntologyKind .get_ontology_kind_validation_error (ontology_kind )
725+ else :
726+ editor_task_type_value = None
713727
714728 quality_mode = kwargs .get ("quality_mode" )
715729 if not quality_mode :
@@ -728,12 +742,47 @@ def create_project(self, **kwargs) -> Project:
728742 else :
729743 raise ValueError (f"{ quality_mode } is not a valid quality mode." )
730744
731- return self ._create (Entity .Project , {
732- ** data ,
733- ** ({
734- "media_type" : media_type
735- } if media_type else {})
736- })
745+ params = {** data }
746+ if media_type_value :
747+ params ["media_type" ] = media_type_value
748+ if editor_task_type_value :
749+ params ["editor_task_type" ] = editor_task_type_value
750+
751+ extra_params = {
752+ Field .String ("dataset_name_or_id" ):
753+ params .pop ("dataset_name_or_id" , None ),
754+ Field .Boolean ("append_to_existing_dataset" ):
755+ params .pop ("append_to_existing_dataset" , None ),
756+ Field .Int ("data_row_count" ):
757+ params .pop ("data_row_count" , None ),
758+ }
759+ extra_params = {k : v for k , v in extra_params .items () if v is not None }
760+
761+ return self ._create (Entity .Project , params , extra_params )
762+
763+ def create_model_evaluation_project (
764+ self ,
765+ dataset_name_or_id : str ,
766+ append_to_existing_dataset : bool = False ,
767+ data_row_count : int = 100 ,
768+ ** kwargs ) -> Project :
769+ """
770+ Use this method exclusively to create a chat model evaluation project.
771+ Args:
772+ dataset_name_or_id: The name or id of the dataset to use for the project
773+ append_to_existing_dataset: If True, the project will append assets (data rows) to the existing dataset
774+ data_row_count: The number of data row assets to use for the project
775+ **kwargs: Additional parameters to pass to the the create_project method
776+ Returns:
777+ Project: The created project
778+ """
779+ kwargs ["media_type" ] = MediaType .Conversational
780+ kwargs ["ontology_kind" ] = OntologyKind .ModelEvaluation
781+ kwargs ["dataset_name_or_id" ] = dataset_name_or_id
782+ kwargs ["append_to_existing_dataset" ] = append_to_existing_dataset
783+ kwargs ["data_row_count" ] = data_row_count
784+
785+ return self .create_project (** kwargs )
737786
738787 def get_roles (self ) -> List [Role ]:
739788 """
@@ -938,18 +987,22 @@ def rootSchemaPayloadToFeatureSchema(client, payload):
938987 rootSchemaPayloadToFeatureSchema ,
939988 ['rootSchemaNodes' , 'nextCursor' ])
940989
941- def create_ontology_from_feature_schemas (self ,
942- name ,
943- feature_schema_ids ,
944- media_type = None ) -> Ontology :
990+ def create_ontology_from_feature_schemas (
991+ self ,
992+ name ,
993+ feature_schema_ids ,
994+ media_type : MediaType = None ,
995+ ontology_kind : OntologyKind = None ) -> Ontology :
945996 """
946997 Creates an ontology from a list of feature schema ids
947998
948999 Args:
9491000 name (str): Name of the ontology
9501001 feature_schema_ids (List[str]): List of feature schema ids corresponding to
9511002 top level tools and classifications to include in the ontology
952- media_type (MediaType or None): Media type of a new ontology
1003+ media_type (MediaType or None): Media type of a new ontology. NOTE for chat evaluation, we currently foce media_type to Conversational
1004+ ontology_kind (OntologyKind or None): set to OntologyKind.ModelEvaluation if the ontology is for chat evaluation,
1005+ leave as None otherwise.
9531006 Returns:
9541007 The created Ontology
9551008 """
@@ -979,7 +1032,20 @@ def create_ontology_from_feature_schemas(self,
9791032 "Neither `tool` or `classification` found in the normalized feature schema"
9801033 )
9811034 normalized = {'tools' : tools , 'classifications' : classifications }
982- return self .create_ontology (name , normalized , media_type )
1035+
1036+ if ontology_kind and ontology_kind is OntologyKind .ModelEvaluation :
1037+ if media_type is None :
1038+ media_type = MediaType .Conversational
1039+ else :
1040+ if media_type is not MediaType .Conversational :
1041+ raise ValueError (
1042+ "For chat evaluation, media_type must be Conversational."
1043+ )
1044+
1045+ return self .create_ontology (name = name ,
1046+ normalized = normalized ,
1047+ media_type = media_type ,
1048+ ontology_kind = ontology_kind )
9831049
9841050 def delete_unused_feature_schema (self , feature_schema_id : str ) -> None :
9851051 """
@@ -1166,7 +1232,11 @@ def get_unused_feature_schemas(self, after: str = None) -> List[str]:
11661232 "Failed to get unused feature schemas, message: " +
11671233 str (response .json ()['message' ]))
11681234
1169- def create_ontology (self , name , normalized , media_type = None ) -> Ontology :
1235+ def create_ontology (self ,
1236+ name ,
1237+ normalized ,
1238+ media_type : MediaType = None ,
1239+ ontology_kind : OntologyKind = None ) -> Ontology :
11701240 """
11711241 Creates an ontology from normalized data
11721242 >>> normalized = {"tools" : [{'tool': 'polygon', 'name': 'cat', 'color': 'black'}], "classifications" : []}
@@ -1184,26 +1254,43 @@ def create_ontology(self, name, normalized, media_type=None) -> Ontology:
11841254 name (str): Name of the ontology
11851255 normalized (dict): A normalized ontology payload. See above for details.
11861256 media_type (MediaType or None): Media type of a new ontology
1257+ ontology_kind (OntologyKind or None): set to OntologyKind.ModelEvaluation if the ontology is for chat evaluation,
1258+ leave as None otherwise.
1259+
11871260 Returns:
11881261 The created Ontology
1262+
1263+ NOTE caller of this method is expected to set media_type to Conversational if ontology_kind is ModelEvaluation
11891264 """
11901265
1266+ media_type_value = None
11911267 if media_type :
11921268 if MediaType .is_supported (media_type ):
1193- media_type = media_type .value
1269+ media_type_value = media_type .value
11941270 else :
11951271 raise get_media_type_validation_error (media_type )
11961272
1273+ if ontology_kind and OntologyKind .is_supported (ontology_kind ):
1274+ editor_task_type_value = EditorTaskTypeMapper .to_editor_task_type (
1275+ ontology_kind , media_type ).value
1276+ elif ontology_kind :
1277+ raise OntologyKind .get_ontology_kind_validation_error (ontology_kind )
1278+ else :
1279+ editor_task_type_value = None
1280+
11971281 query_str = """mutation upsertRootSchemaNodePyApi($data: UpsertOntologyInput!){
11981282 upsertOntology(data: $data){ %s }
11991283 } """ % query .results_query_part (Entity .Ontology )
12001284 params = {
12011285 'data' : {
12021286 'name' : name ,
12031287 'normalized' : json .dumps (normalized ),
1204- 'mediaType' : media_type
1288+ 'mediaType' : media_type_value
12051289 }
12061290 }
1291+ if editor_task_type_value :
1292+ params ['data' ]['editorTaskType' ] = editor_task_type_value
1293+
12071294 res = self .execute (query_str , params )
12081295 return Entity .Ontology (self , res ['upsertOntology' ])
12091296
0 commit comments