label-studio-sdk 1.0.10__py3-none-any.whl → 1.0.12__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 +21 -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/converter/converter.py +22 -0
- label_studio_sdk/converter/exports/brush_to_coco.py +332 -0
- label_studio_sdk/converter/main.py +14 -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 +610 -0
- label_studio_sdk/tokens/client_ext.py +94 -0
- label_studio_sdk/types/__init__.py +14 -0
- label_studio_sdk/types/access_token_response.py +22 -0
- label_studio_sdk/types/annotation.py +2 -1
- label_studio_sdk/types/annotation_completed_by.py +6 -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/types/rotate_token_response.py +22 -0
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.12.dist-info}/METADATA +3 -2
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.12.dist-info}/RECORD +40 -23
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.12.dist-info}/WHEEL +1 -1
- {label_studio_sdk-1.0.10.dist-info → label_studio_sdk-1.0.12.dist-info}/LICENSE +0 -0
|
@@ -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)
|