Skip to content

Commit 03134f7

Browse files
committed
Merge branch 'develop' into sdubinin/model-1489
2 parents 1677269 + ce38800 commit 03134f7

File tree

9 files changed

+184
-14
lines changed

9 files changed

+184
-14
lines changed

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
project = 'Python SDK reference'
1717
copyright = '2024, Labelbox'
1818
author = 'Labelbox'
19-
release = '3.75.1'
19+
release = '3.76.0'
2020

2121
# -- General configuration ---------------------------------------------------
2222

docs/labelbox/labeling-service.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Project Labeling Service
2+
===============================================================================================
3+
4+
.. automodule:: labelbox.schema.labeling_service
5+
:members:
6+
:show-inheritance:

libs/labelbox/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
11
# Changelog
2+
# Version 3.76.0 (2024-07-29)
3+
# Added
4+
* Added Project get_labeling_service(), request_labeling_service() and get_labeling_service_status()
5+
* Added project and ontology creation for prompt response projects: Client create_prompt_response_generation_project(), create_response_creation_project() in https://github.com/Labelbox/labelbox-python/pull/1726
6+
* Added is_benchmark_enabled, is_consensus_enabled to Project in https://github.com/Labelbox/labelbox-python/pull/1745
7+
8+
## Updated
9+
* Made Project quality modes a list to allow combining more than 1 quality mode per project in https://github.com/Labelbox/labelbox-python/pull/1683
10+
11+
## Notebooks
12+
* Added back in export migration guide in https://github.com/Labelbox/labelbox-python/pull/1736
13+
* Added correct data param to video notebookin https://github.com/Labelbox/labelbox-python/pull/1732
14+
NOTE: the notebooks will be removed and moved to this repo https://github.com/Labelbox/labelbox-notebooks soon
15+
16+
## Other
17+
* Use connection pool for all http and graphql requests in https://github.com/Labelbox/labelbox-python/pull/1733
18+
219
# Version 3.75.1 (2024-07-16)
320
## Removed
421
* Project MEDIA_TYPE JSON https://github.com/Labelbox/labelbox-python/pull/1728

libs/labelbox/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "labelbox"
3-
version = "3.75.1"
3+
version = "3.76.0"
44
description = "Labelbox Python API"
55
authors = [{ name = "Labelbox", email = "engineering@labelbox.com" }]
66
dependencies = [

libs/labelbox/src/labelbox/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "labelbox"
22

3-
__version__ = "3.75.1"
3+
__version__ = "3.76.0"
44

55
from labelbox.client import Client
66
from labelbox.schema.project import Project
@@ -43,3 +43,4 @@
4343
from labelbox.schema.identifiable import UniqueId, GlobalKey
4444
from labelbox.schema.ontology_kind import OntologyKind
4545
from labelbox.schema.project_overview import ProjectOverview, ProjectOverviewDetailed
46+
from labelbox.schema.labeling_service import LabelingService, LabelingServiceStatus

libs/labelbox/src/labelbox/client.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -895,17 +895,17 @@ def create_prompt_response_generation_project(self,
895895
NOTE: Only a dataset_name or dataset_id should be included
896896
897897
Examples:
898-
>>> client.create_prompt_response_generation_project(name=project_name, dataset_name="new data set", project_kind=MediaType.LLMPromptResponseCreation)
899-
>>> This creates a new dataset with a default number of rows (100), creates new project and assigns a batch of the newly created datarows to the project.
898+
>>> client.create_prompt_response_generation_project(name=project_name, dataset_name="new data set", media_type=MediaType.LLMPromptResponseCreation)
899+
>>> This creates a new dataset with a default number of rows (100), creates new prompt and response creation project and assigns a batch of the newly created data rows to the project.
900900
901-
>>> client.create_prompt_response_generation_project(name=project_name, dataset_name="new data set", data_row_count=10, project_kind=MediaType.LLMPromptCreation)
902-
>>> This creates a new dataset with 10 data rows, creates new project and assigns a batch of the newly created datarows to the project.
901+
>>> client.create_prompt_response_generation_project(name=project_name, dataset_name="new data set", data_row_count=10, media_type=MediaType.LLMPromptCreation)
902+
>>> This creates a new dataset with 10 data rows, creates new prompt creation project and assigns a batch of the newly created datarows to the project.
903903
904-
>>> client.create_prompt_response_generation_project(name=project_name, dataset_id="clr00u8j0j0j0", project_kind=MediaType.LLMPromptCreation)
905-
>>> This creates a new project, and adds 100 datarows to the dataset with id "clr00u8j0j0j0" and assigns a batch of the newly created data rows to the project.
904+
>>> client.create_prompt_response_generation_project(name=project_name, dataset_id="clr00u8j0j0j0", media_type=MediaType.LLMPromptCreation)
905+
>>> This creates a new prompt creation project, and adds 100 datarows to the dataset with id "clr00u8j0j0j0" and assigns a batch of the newly created data rows to the project.
906906
907-
>>> client.create_prompt_response_generation_project(name=project_name, dataset_id="clr00u8j0j0j0", data_row_count=10, project_kind=MediaType.LLMPromptResponseCreation)
908-
>>> This creates a new project, and adds 100 datarows to the dataset with id "clr00u8j0j0j0" and assigns a batch of the newly created 10 data rows to the project.
907+
>>> client.create_prompt_response_generation_project(name=project_name, dataset_id="clr00u8j0j0j0", data_row_count=10, media_type=MediaType.LLMPromptResponseCreation)
908+
>>> This creates a new prompt and response creation project, and adds 100 datarows to the dataset with id "clr00u8j0j0j0" and assigns a batch of the newly created 10 data rows to the project.
909909
910910
"""
911911
if not dataset_id and not dataset_name:
@@ -1501,8 +1501,8 @@ def create_ontology(self,
15011501
name (str): Name of the ontology
15021502
normalized (dict): A normalized ontology payload. See above for details.
15031503
media_type (MediaType or None): Media type of a new ontology
1504-
ontology_kind (OntologyKind or None): set to OntologyKind.ModelEvaluation if the ontology is for chat evaluation,
1505-
leave as None otherwise.
1504+
ontology_kind (OntologyKind or None): set to OntologyKind.ModelEvaluation if the ontology is for chat evaluation or
1505+
OntologyKind.ResponseCreation if ontology is for response creation, leave as None otherwise.
15061506
15071507
Returns:
15081508
The created Ontology
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from datetime import datetime
2+
from enum import Enum
3+
from typing import Any
4+
from typing_extensions import Annotated
5+
6+
from labelbox.exceptions import ResourceNotFoundError
7+
8+
from labelbox.pydantic_compat import BaseModel, Field
9+
from labelbox.utils import _CamelCaseMixin
10+
11+
Cuid = Annotated[str, Field(min_length=25, max_length=25)]
12+
13+
class LabelingServiceStatus(Enum):
14+
Accepted = 'ACCEPTED',
15+
Calibration = 'CALIBRATION',
16+
Complete = 'COMPLETE',
17+
Production = 'PRODUCTION',
18+
Requested = 'REQUESTED',
19+
SetUp = 'SET_UP'
20+
21+
22+
class LabelingService(BaseModel):
23+
id: Cuid
24+
project_id: Cuid
25+
created_at: datetime
26+
updated_at: datetime
27+
created_by_id: Cuid
28+
status: LabelingServiceStatus
29+
client: Any # type Any to avoid circular import from client
30+
31+
def __init__(self, **kwargs):
32+
super().__init__(**kwargs)
33+
if not self.client.enable_experimental:
34+
raise RuntimeError(
35+
"Please enable experimental in client to use LabelingService")
36+
37+
class Config(_CamelCaseMixin.Config):
38+
...
39+
40+
@classmethod
41+
def start(cls, client, project_id: Cuid) -> 'LabelingService':
42+
"""
43+
Starts the labeling service for the project. This is equivalent to a UI acction to Request Specialized Labelers
44+
45+
Returns:
46+
LabelingService: The labeling service for the project.
47+
Raises:
48+
Exception: If the service fails to start.
49+
"""
50+
query_str = """mutation CreateProjectBoostWorkforcePyApi($projectId: ID!) {
51+
upsertProjectBoostWorkforce(data: { projectId: $projectId }) {
52+
success
53+
}
54+
}"""
55+
result = client.execute(query_str, {"projectId": project_id})
56+
success = result["upsertProjectBoostWorkforce"]["success"]
57+
if not success:
58+
raise Exception("Failed to start labeling service")
59+
return cls.get(client, project_id)
60+
61+
@classmethod
62+
def get(cls, client, project_id: Cuid) -> 'LabelingService':
63+
"""
64+
Returns the labeling service associated with the project.
65+
66+
Raises:
67+
ResourceNotFoundError: If the project does not have a labeling service.
68+
"""
69+
query = """
70+
query GetProjectBoostWorkforcePyApi($projectId: ID!) {
71+
projectBoostWorkforce(data: { projectId: $projectId }) {
72+
id
73+
projectId
74+
createdAt
75+
updatedAt
76+
createdById
77+
status
78+
}
79+
}
80+
"""
81+
result = client.execute(query, {"projectId": project_id})
82+
if result["projectBoostWorkforce"] is None:
83+
raise ResourceNotFoundError(
84+
message="The project does not have a labeling service.")
85+
data = result["projectBoostWorkforce"]
86+
data["client"] = client
87+
return LabelingService(**data)

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, TypeVar, Union, overload
1010
from urllib.parse import urlparse
1111

12+
from labelbox.schema.labeling_service import LabelingService, LabelingServiceStatus
1213
import requests
1314

1415
from labelbox import parser
@@ -149,7 +150,7 @@ def is_chat_evaluation(self) -> bool:
149150
True if this project is a live chat evaluation project, False otherwise
150151
"""
151152
return self.media_type == MediaType.Conversational and self.editor_task_type == EditorTaskType.ModelChatEvaluation
152-
153+
153154
def is_prompt_response(self) -> bool:
154155
"""
155156
Returns:
@@ -1915,6 +1916,39 @@ def clone(self) -> "Project":
19151916
result = self.client.execute(mutation, {"projectId": self.uid})
19161917
return self.client.get_project(result["cloneProject"]["id"])
19171918

1919+
@experimental
1920+
def get_labeling_service(self) -> LabelingService:
1921+
"""Get the labeling service for this project.
1922+
1923+
Raises:
1924+
ResourceNotFoundError if the project does not have a labeling service.
1925+
1926+
Returns:
1927+
LabelingService: The labeling service for this project.
1928+
"""
1929+
return LabelingService.get(self.client, self.uid)
1930+
1931+
@experimental
1932+
def get_labeling_service_status(self) -> LabelingServiceStatus:
1933+
"""Get the labeling service status for this project.
1934+
1935+
Raises:
1936+
ResourceNotFoundError if the project does not have a labeling service.
1937+
1938+
Returns:
1939+
LabelingServiceStatus: The labeling service status for this project.
1940+
"""
1941+
return self.get_labeling_service().status
1942+
1943+
@experimental
1944+
def request_labeling_service(self) -> LabelingService:
1945+
"""Get the labeling service for this project.
1946+
1947+
Returns:
1948+
LabelingService: The labeling service for this project.
1949+
"""
1950+
return LabelingService.start(self.client, self.uid) # type: ignore
1951+
19181952

19191953
class ProjectMember(DbObject):
19201954
user = Relationship.ToOne("User", cache=True)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import pytest
2+
3+
from labelbox.exceptions import ResourceNotFoundError
4+
from labelbox.schema.labeling_service import LabelingServiceStatus
5+
6+
7+
def test_get_labeling_service_throws_exception(project):
8+
with pytest.raises(ResourceNotFoundError): # No labeling service by default
9+
project.get_labeling_service()
10+
with pytest.raises(ResourceNotFoundError): # No labeling service by default
11+
project.get_labeling_service_status()
12+
13+
14+
def test_start_labeling_service(project):
15+
labeling_service = project.request_labeling_service()
16+
assert labeling_service.status == LabelingServiceStatus.SetUp
17+
assert labeling_service.project_id == project.uid
18+
19+
# Check that the labeling service is now available
20+
labeling_service = project.get_labeling_service()
21+
assert labeling_service.status == LabelingServiceStatus.SetUp
22+
assert labeling_service.project_id == project.uid
23+
24+
labeling_service_status = project.get_labeling_service_status()
25+
assert labeling_service_status == LabelingServiceStatus.SetUp

0 commit comments

Comments
 (0)