label-studio-sdk 1.0.10__py3-none-any.whl → 1.0.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of label-studio-sdk might be problematic. Click here for more details.
- label_studio_sdk/__init__.py +17 -1
- label_studio_sdk/_extensions/label_studio_tools/core/utils/json_schema.py +5 -0
- label_studio_sdk/base_client.py +8 -0
- label_studio_sdk/core/client_wrapper.py +34 -15
- label_studio_sdk/errors/__init__.py +3 -1
- label_studio_sdk/errors/not_found_error.py +9 -0
- label_studio_sdk/errors/unauthorized_error.py +9 -0
- label_studio_sdk/jwt_settings/__init__.py +2 -0
- label_studio_sdk/jwt_settings/client.py +259 -0
- label_studio_sdk/label_interface/control_tags.py +15 -2
- label_studio_sdk/label_interface/interface.py +80 -1
- label_studio_sdk/label_interface/object_tags.py +2 -2
- label_studio_sdk/projects/__init__.py +2 -1
- label_studio_sdk/projects/client.py +4 -0
- label_studio_sdk/projects/exports/client_ext.py +106 -40
- label_studio_sdk/projects/pauses/__init__.py +2 -0
- label_studio_sdk/projects/pauses/client.py +704 -0
- label_studio_sdk/projects/types/projects_update_response.py +10 -0
- label_studio_sdk/tokens/__init__.py +2 -0
- label_studio_sdk/tokens/client.py +470 -0
- label_studio_sdk/tokens/client_ext.py +94 -0
- label_studio_sdk/types/__init__.py +10 -0
- label_studio_sdk/types/access_token_response.py +22 -0
- label_studio_sdk/types/api_token_response.py +32 -0
- label_studio_sdk/types/jwt_settings_response.py +32 -0
- label_studio_sdk/types/model_provider_connection_provider.py +1 -1
- label_studio_sdk/types/pause.py +34 -0
- label_studio_sdk/types/pause_paused_by.py +5 -0
- label_studio_sdk/types/project.py +10 -0
- label_studio_sdk/types/prompt_version_provider.py +1 -1
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.11.dist-info}/METADATA +2 -1
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.11.dist-info}/RECORD +34 -20
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.11.dist-info}/WHEEL +1 -1
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.11.dist-info}/LICENSE +0 -0
|
@@ -119,7 +119,7 @@ class ObjectTag(LabelStudioTag):
|
|
|
119
119
|
tag=tag.tag,
|
|
120
120
|
attr=dict(tag.attrib),
|
|
121
121
|
name=tag.attrib.get("name"),
|
|
122
|
-
value=tag.attrib
|
|
122
|
+
value=tag.attrib.get('valueList', tag.attrib.get('value')),
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
@classmethod
|
|
@@ -127,7 +127,7 @@ class ObjectTag(LabelStudioTag):
|
|
|
127
127
|
"""
|
|
128
128
|
Check if tag is input
|
|
129
129
|
"""
|
|
130
|
-
return bool(tag.attrib.get("name") and tag.attrib.get("value"))
|
|
130
|
+
return bool(tag.attrib.get("name") and (tag.attrib.get("value") or tag.attrib.get("valueList")))
|
|
131
131
|
|
|
132
132
|
@property
|
|
133
133
|
def value_type(self):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# This file was auto-generated by Fern from our API Definition.
|
|
2
2
|
|
|
3
3
|
from .types import ProjectsCreateResponse, ProjectsImportTasksResponse, ProjectsListResponse, ProjectsUpdateResponse
|
|
4
|
-
from . import exports
|
|
4
|
+
from . import exports, pauses
|
|
5
5
|
from .exports import ExportsConvertResponse, ExportsListFormatsResponseItem
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
@@ -12,4 +12,5 @@ __all__ = [
|
|
|
12
12
|
"ProjectsListResponse",
|
|
13
13
|
"ProjectsUpdateResponse",
|
|
14
14
|
"exports",
|
|
15
|
+
"pauses",
|
|
15
16
|
]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import typing
|
|
4
4
|
from ..core.client_wrapper import SyncClientWrapper
|
|
5
|
+
from .pauses.client import PausesClient
|
|
5
6
|
from .exports.client import ExportsClient
|
|
6
7
|
from ..core.request_options import RequestOptions
|
|
7
8
|
from ..core.pagination import SyncPager
|
|
@@ -17,6 +18,7 @@ from .types.projects_import_tasks_response import ProjectsImportTasksResponse
|
|
|
17
18
|
from ..errors.bad_request_error import BadRequestError
|
|
18
19
|
from ..types.project_label_config import ProjectLabelConfig
|
|
19
20
|
from ..core.client_wrapper import AsyncClientWrapper
|
|
21
|
+
from .pauses.client import AsyncPausesClient
|
|
20
22
|
from .exports.client import AsyncExportsClient
|
|
21
23
|
from ..core.pagination import AsyncPager
|
|
22
24
|
|
|
@@ -27,6 +29,7 @@ OMIT = typing.cast(typing.Any, ...)
|
|
|
27
29
|
class ProjectsClient:
|
|
28
30
|
def __init__(self, *, client_wrapper: SyncClientWrapper):
|
|
29
31
|
self._client_wrapper = client_wrapper
|
|
32
|
+
self.pauses = PausesClient(client_wrapper=self._client_wrapper)
|
|
30
33
|
self.exports = ExportsClient(client_wrapper=self._client_wrapper)
|
|
31
34
|
|
|
32
35
|
def list(
|
|
@@ -701,6 +704,7 @@ class ProjectsClient:
|
|
|
701
704
|
class AsyncProjectsClient:
|
|
702
705
|
def __init__(self, *, client_wrapper: AsyncClientWrapper):
|
|
703
706
|
self._client_wrapper = client_wrapper
|
|
707
|
+
self.pauses = AsyncPausesClient(client_wrapper=self._client_wrapper)
|
|
704
708
|
self.exports = AsyncExportsClient(client_wrapper=self._client_wrapper)
|
|
705
709
|
|
|
706
710
|
async def list(
|
|
@@ -23,28 +23,60 @@ class ExportTimeoutError(ApiError):
|
|
|
23
23
|
)
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
class ExportFailedError(ApiError):
|
|
27
|
+
|
|
28
|
+
def __init__(self, export_snapshot):
|
|
29
|
+
super().__init__(
|
|
30
|
+
status_code=500,
|
|
31
|
+
body=f"Export failed: {export_snapshot}"
|
|
32
|
+
)
|
|
33
33
|
|
|
34
|
-
def _bytestream_to_binary(bytestream: typing.Iterable[bytes]) -> bytes:
|
|
35
|
-
fileobj = _bytestream_to_fileobj(bytestream)
|
|
36
|
-
return fileobj.getvalue()
|
|
37
34
|
|
|
38
|
-
def
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
def _check_status(export_snapshot, converted_format_id, status):
|
|
36
|
+
if converted_format_id:
|
|
37
|
+
converted_format = next((c for c in export_snapshot.converted_formats if c.id == converted_format_id), None)
|
|
38
|
+
if converted_format and converted_format.status == status:
|
|
39
|
+
return True
|
|
40
|
+
else:
|
|
41
|
+
if export_snapshot.status == status:
|
|
42
|
+
return True
|
|
43
|
+
return False
|
|
41
44
|
|
|
42
|
-
def _bytestream_to_pandas(bytestream: typing.Iterable[bytes]) -> pd.DataFrame:
|
|
43
|
-
fileobj = _bytestream_to_fileobj(bytestream)
|
|
44
|
-
return pd.read_csv(fileobj)
|
|
45
45
|
|
|
46
46
|
class ExportsClientExt(ExportsClient):
|
|
47
47
|
|
|
48
|
+
def _bytestream_to_fileobj(self, bytestream: typing.Iterable[bytes] | bytes) -> typing.BinaryIO:
|
|
49
|
+
buffer = BytesIO()
|
|
50
|
+
if isinstance(bytestream, typing.Iterable):
|
|
51
|
+
for chunk in bytestream:
|
|
52
|
+
buffer.write(chunk)
|
|
53
|
+
else:
|
|
54
|
+
buffer.write(bytestream)
|
|
55
|
+
buffer.seek(0)
|
|
56
|
+
return buffer
|
|
57
|
+
|
|
58
|
+
def _bytestream_to_binary(self, bytestream: typing.Iterable[bytes]) -> bytes:
|
|
59
|
+
fileobj = self._bytestream_to_fileobj(bytestream)
|
|
60
|
+
return fileobj.getvalue()
|
|
61
|
+
|
|
62
|
+
def _bytestream_to_json(self, bytestream: typing.Iterable[bytes]) -> dict:
|
|
63
|
+
fileobj = self._bytestream_to_fileobj(bytestream)
|
|
64
|
+
return json.load(fileobj)
|
|
65
|
+
|
|
66
|
+
def _bytestream_to_pandas(self, bytestream: typing.Iterable[bytes]) -> pd.DataFrame:
|
|
67
|
+
fileobj = self._bytestream_to_fileobj(bytestream)
|
|
68
|
+
return pd.read_csv(fileobj)
|
|
69
|
+
|
|
70
|
+
def _poll_export(self, project_id, export_snapshot, converted_format_id, timeout):
|
|
71
|
+
start_time = time.time()
|
|
72
|
+
while not _check_status(export_snapshot, None, 'completed'):
|
|
73
|
+
export_snapshot = self.get(project_id, export_pk=export_snapshot.id)
|
|
74
|
+
if _check_status(export_snapshot, None, 'failed'):
|
|
75
|
+
raise ExportFailedError(export_snapshot)
|
|
76
|
+
if time.time() - start_time > timeout:
|
|
77
|
+
raise ExportTimeoutError(export_snapshot)
|
|
78
|
+
time.sleep(1)
|
|
79
|
+
|
|
48
80
|
def _get_bytestream(
|
|
49
81
|
self,
|
|
50
82
|
project_id: int,
|
|
@@ -59,14 +91,13 @@ class ExportsClientExt(ExportsClient):
|
|
|
59
91
|
if version.edition == "Enterprise":
|
|
60
92
|
# Enterprise edition exports are async, so we need to wait for the export job to complete
|
|
61
93
|
export_snapshot = self.create(project_id, **(create_kwargs or {}))
|
|
94
|
+
# Poll for base (JSON) export to complete
|
|
95
|
+
self._poll_export(project_id, export_snapshot, None, timeout)
|
|
96
|
+
# Convert to requested format if not JSON
|
|
62
97
|
if export_type != "JSON":
|
|
63
|
-
self.convert(project_id, export_pk=export_snapshot.id, export_type=export_type, **(convert_kwargs or {}))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
export_snapshot = self.get(project_id, export_pk=export_snapshot.id)
|
|
67
|
-
if time.time() - start_time > timeout:
|
|
68
|
-
raise ExportTimeoutError(export_snapshot)
|
|
69
|
-
time.sleep(1)
|
|
98
|
+
converted_proc = self.convert(project_id, export_pk=export_snapshot.id, export_type=export_type, **(convert_kwargs or {}))
|
|
99
|
+
self._poll_export(project_id, export_snapshot, converted_proc.converted_format, timeout)
|
|
100
|
+
|
|
70
101
|
bytestream = self.download(project_id, export_pk=export_snapshot.id, export_type=export_type, request_options={'chunk_size': 1024}, **(download_kwargs or {}))
|
|
71
102
|
else:
|
|
72
103
|
# Community edition exports are sync, so we can download the file immediately
|
|
@@ -75,21 +106,57 @@ class ExportsClientExt(ExportsClient):
|
|
|
75
106
|
|
|
76
107
|
def as_file(self, project_id: int, export_type: str = "JSON", timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
77
108
|
bytestream = self._get_bytestream(project_id, export_type, timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
78
|
-
return _bytestream_to_fileobj(bytestream)
|
|
109
|
+
return self._bytestream_to_fileobj(bytestream)
|
|
79
110
|
|
|
80
111
|
def as_binary(self, project_id: int, export_type: str = "JSON", timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
81
112
|
bytestream = self._get_bytestream(project_id, export_type, timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
82
|
-
return _bytestream_to_binary(bytestream)
|
|
113
|
+
return self._bytestream_to_binary(bytestream)
|
|
83
114
|
|
|
84
115
|
def as_json(self, project_id: int, timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
85
116
|
bytestream = self._get_bytestream(project_id, "JSON", timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
86
|
-
return _bytestream_to_json(bytestream)
|
|
117
|
+
return self._bytestream_to_json(bytestream)
|
|
87
118
|
|
|
88
119
|
def as_pandas(self, project_id: int, timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
89
120
|
bytestream = self._get_bytestream(project_id, "CSV", timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
90
|
-
return _bytestream_to_pandas(bytestream)
|
|
121
|
+
return self._bytestream_to_pandas(bytestream)
|
|
91
122
|
|
|
92
123
|
class AsyncExportsClientExt(AsyncExportsClient):
|
|
124
|
+
|
|
125
|
+
async def _bytestream_to_fileobj(self, bytestream: typing.AsyncGenerator[bytes, None] | bytes):
|
|
126
|
+
"""Convert bytestream to file-like object"""
|
|
127
|
+
fileobj = BytesIO()
|
|
128
|
+
if isinstance(bytestream, typing.AsyncGenerator):
|
|
129
|
+
async for chunk in bytestream:
|
|
130
|
+
fileobj.write(chunk)
|
|
131
|
+
else:
|
|
132
|
+
fileobj.write(bytestream)
|
|
133
|
+
fileobj.seek(0)
|
|
134
|
+
return fileobj
|
|
135
|
+
|
|
136
|
+
async def _bytestream_to_binary(self, bytestream):
|
|
137
|
+
"""Convert bytestream to binary data"""
|
|
138
|
+
fileobj = await self._bytestream_to_fileobj(bytestream)
|
|
139
|
+
return fileobj.getvalue()
|
|
140
|
+
|
|
141
|
+
async def _bytestream_to_json(self, bytestream):
|
|
142
|
+
"""Convert bytestream to JSON object"""
|
|
143
|
+
fileobj = await self._bytestream_to_fileobj(bytestream)
|
|
144
|
+
return json.load(fileobj)
|
|
145
|
+
|
|
146
|
+
async def _bytestream_to_pandas(self, bytestream):
|
|
147
|
+
"""Convert bytestream to pandas DataFrame"""
|
|
148
|
+
fileobj = await self._bytestream_to_fileobj(bytestream)
|
|
149
|
+
return pd.read_csv(fileobj)
|
|
150
|
+
|
|
151
|
+
async def _poll_export(self, project_id, export_snapshot, converted_format_id, timeout):
|
|
152
|
+
start_time = time.time()
|
|
153
|
+
while not _check_status(export_snapshot, None, 'completed'):
|
|
154
|
+
export_snapshot = await self.get(project_id, export_pk=export_snapshot.id)
|
|
155
|
+
if _check_status(export_snapshot, None, 'failed'):
|
|
156
|
+
raise ExportFailedError(export_snapshot)
|
|
157
|
+
if time.time() - start_time > timeout:
|
|
158
|
+
raise ExportTimeoutError(export_snapshot)
|
|
159
|
+
await asyncio.sleep(1)
|
|
93
160
|
|
|
94
161
|
async def _get_bytestream(
|
|
95
162
|
self,
|
|
@@ -104,31 +171,30 @@ class AsyncExportsClientExt(AsyncExportsClient):
|
|
|
104
171
|
if version.edition == "Enterprise":
|
|
105
172
|
# Enterprise edition exports are async, so we need to wait for the export job to complete
|
|
106
173
|
export_snapshot = await self.create(project_id, **(create_kwargs or {}))
|
|
174
|
+
# Poll for base (JSON) export to complete
|
|
175
|
+
await self._poll_export(project_id, export_snapshot, None, timeout)
|
|
176
|
+
# Convert to requested format if not JSON
|
|
107
177
|
if export_type != "JSON":
|
|
108
|
-
await self.convert(project_id, export_pk=export_snapshot.id, export_type=export_type, **(convert_kwargs or {}))
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if time.time() - start_time > timeout:
|
|
113
|
-
raise ExportTimeoutError(export_snapshot)
|
|
114
|
-
await asyncio.sleep(1)
|
|
115
|
-
bytestream = await self.download(project_id, export_pk=export_snapshot.id, export_type=export_type, request_options={'chunk_size': 1024}, **(download_kwargs or {}))
|
|
178
|
+
converted_proc = await self.convert(project_id, export_pk=export_snapshot.id, export_type=export_type, **(convert_kwargs or {}))
|
|
179
|
+
await self._poll_export(project_id, export_snapshot, converted_proc.converted_format, timeout)
|
|
180
|
+
|
|
181
|
+
bytestream = self.download(project_id, export_pk=export_snapshot.id, export_type=export_type, request_options={'chunk_size': 1024}, **(download_kwargs or {}))
|
|
116
182
|
else:
|
|
117
|
-
bytestream =
|
|
183
|
+
bytestream = self.download_sync(project_id, export_type=export_type, download_all_tasks=True, download_resources=True)
|
|
118
184
|
return bytestream
|
|
119
185
|
|
|
120
186
|
async def as_file(self, project_id: int, export_type: str = "JSON", timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
121
187
|
bytestream = await self._get_bytestream(project_id, export_type, timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
122
|
-
return _bytestream_to_fileobj(bytestream)
|
|
188
|
+
return await self._bytestream_to_fileobj(bytestream)
|
|
123
189
|
|
|
124
190
|
async def as_binary(self, project_id: int, export_type: str = "JSON", timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
125
191
|
bytestream = await self._get_bytestream(project_id, export_type, timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
126
|
-
return _bytestream_to_binary(bytestream)
|
|
192
|
+
return await self._bytestream_to_binary(bytestream)
|
|
127
193
|
|
|
128
194
|
async def as_json(self, project_id: int, timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
129
195
|
bytestream = await self._get_bytestream(project_id, "JSON", timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
130
|
-
return _bytestream_to_json(bytestream)
|
|
196
|
+
return await self._bytestream_to_json(bytestream)
|
|
131
197
|
|
|
132
198
|
async def as_pandas(self, project_id: int, timeout: int = 60, create_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, convert_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None, download_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None):
|
|
133
199
|
bytestream = await self._get_bytestream(project_id, "CSV", timeout, create_kwargs, convert_kwargs, download_kwargs)
|
|
134
|
-
return _bytestream_to_pandas(bytestream)
|
|
200
|
+
return await self._bytestream_to_pandas(bytestream)
|