synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__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 synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +9 -8
- synapse_sdk/cli/agent/__init__.py +25 -0
- synapse_sdk/cli/agent/config.py +104 -0
- synapse_sdk/cli/agent/select.py +197 -0
- synapse_sdk/cli/auth.py +104 -0
- synapse_sdk/cli/main.py +1025 -0
- synapse_sdk/cli/plugin/__init__.py +58 -0
- synapse_sdk/cli/plugin/create.py +566 -0
- synapse_sdk/cli/plugin/job.py +196 -0
- synapse_sdk/cli/plugin/publish.py +322 -0
- synapse_sdk/cli/plugin/run.py +131 -0
- synapse_sdk/cli/plugin/test.py +200 -0
- synapse_sdk/clients/README.md +239 -0
- synapse_sdk/clients/__init__.py +5 -0
- synapse_sdk/clients/_template.py +266 -0
- synapse_sdk/clients/agent/__init__.py +84 -29
- synapse_sdk/clients/agent/async_ray.py +289 -0
- synapse_sdk/clients/agent/container.py +83 -0
- synapse_sdk/clients/agent/plugin.py +101 -0
- synapse_sdk/clients/agent/ray.py +296 -39
- synapse_sdk/clients/backend/__init__.py +152 -12
- synapse_sdk/clients/backend/annotation.py +164 -22
- synapse_sdk/clients/backend/core.py +101 -0
- synapse_sdk/clients/backend/data_collection.py +292 -0
- synapse_sdk/clients/backend/hitl.py +87 -0
- synapse_sdk/clients/backend/integration.py +374 -46
- synapse_sdk/clients/backend/ml.py +134 -22
- synapse_sdk/clients/backend/models.py +247 -0
- synapse_sdk/clients/base.py +538 -59
- synapse_sdk/clients/exceptions.py +35 -7
- synapse_sdk/clients/pipeline/__init__.py +5 -0
- synapse_sdk/clients/pipeline/client.py +636 -0
- synapse_sdk/clients/protocols.py +178 -0
- synapse_sdk/clients/utils.py +86 -8
- synapse_sdk/clients/validation.py +58 -0
- synapse_sdk/enums.py +76 -0
- synapse_sdk/exceptions.py +168 -0
- synapse_sdk/integrations/__init__.py +74 -0
- synapse_sdk/integrations/_base.py +119 -0
- synapse_sdk/integrations/_context.py +53 -0
- synapse_sdk/integrations/ultralytics/__init__.py +78 -0
- synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
- synapse_sdk/integrations/ultralytics/_patches.py +124 -0
- synapse_sdk/loggers.py +476 -95
- synapse_sdk/mcp/MCP.md +69 -0
- synapse_sdk/mcp/__init__.py +48 -0
- synapse_sdk/mcp/__main__.py +6 -0
- synapse_sdk/mcp/config.py +349 -0
- synapse_sdk/mcp/prompts/__init__.py +4 -0
- synapse_sdk/mcp/resources/__init__.py +4 -0
- synapse_sdk/mcp/server.py +1352 -0
- synapse_sdk/mcp/tools/__init__.py +6 -0
- synapse_sdk/plugins/__init__.py +133 -9
- synapse_sdk/plugins/action.py +229 -0
- synapse_sdk/plugins/actions/__init__.py +82 -0
- synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
- synapse_sdk/plugins/actions/dataset/action.py +471 -0
- synapse_sdk/plugins/actions/export/__init__.py +55 -0
- synapse_sdk/plugins/actions/export/action.py +183 -0
- synapse_sdk/plugins/actions/export/context.py +59 -0
- synapse_sdk/plugins/actions/inference/__init__.py +84 -0
- synapse_sdk/plugins/actions/inference/action.py +285 -0
- synapse_sdk/plugins/actions/inference/context.py +81 -0
- synapse_sdk/plugins/actions/inference/deployment.py +322 -0
- synapse_sdk/plugins/actions/inference/serve.py +252 -0
- synapse_sdk/plugins/actions/train/__init__.py +54 -0
- synapse_sdk/plugins/actions/train/action.py +326 -0
- synapse_sdk/plugins/actions/train/context.py +57 -0
- synapse_sdk/plugins/actions/upload/__init__.py +49 -0
- synapse_sdk/plugins/actions/upload/action.py +165 -0
- synapse_sdk/plugins/actions/upload/context.py +61 -0
- synapse_sdk/plugins/config.py +98 -0
- synapse_sdk/plugins/context/__init__.py +109 -0
- synapse_sdk/plugins/context/env.py +113 -0
- synapse_sdk/plugins/datasets/__init__.py +113 -0
- synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
- synapse_sdk/plugins/datasets/converters/base.py +347 -0
- synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
- synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
- synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
- synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
- synapse_sdk/plugins/datasets/formats/dm.py +351 -0
- synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
- synapse_sdk/plugins/decorators.py +83 -0
- synapse_sdk/plugins/discovery.py +790 -0
- synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
- synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
- synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
- synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
- synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
- synapse_sdk/plugins/docs/README.md +513 -0
- synapse_sdk/plugins/docs/STEP.md +656 -0
- synapse_sdk/plugins/enums.py +70 -10
- synapse_sdk/plugins/errors.py +92 -0
- synapse_sdk/plugins/executors/__init__.py +43 -0
- synapse_sdk/plugins/executors/local.py +99 -0
- synapse_sdk/plugins/executors/ray/__init__.py +18 -0
- synapse_sdk/plugins/executors/ray/base.py +282 -0
- synapse_sdk/plugins/executors/ray/job.py +298 -0
- synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
- synapse_sdk/plugins/executors/ray/packaging.py +137 -0
- synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
- synapse_sdk/plugins/executors/ray/task.py +257 -0
- synapse_sdk/plugins/models/__init__.py +26 -0
- synapse_sdk/plugins/models/logger.py +173 -0
- synapse_sdk/plugins/models/pipeline.py +25 -0
- synapse_sdk/plugins/pipelines/__init__.py +81 -0
- synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
- synapse_sdk/plugins/pipelines/context.py +107 -0
- synapse_sdk/plugins/pipelines/display.py +311 -0
- synapse_sdk/plugins/runner.py +114 -0
- synapse_sdk/plugins/schemas/__init__.py +19 -0
- synapse_sdk/plugins/schemas/results.py +152 -0
- synapse_sdk/plugins/steps/__init__.py +63 -0
- synapse_sdk/plugins/steps/base.py +128 -0
- synapse_sdk/plugins/steps/context.py +90 -0
- synapse_sdk/plugins/steps/orchestrator.py +128 -0
- synapse_sdk/plugins/steps/registry.py +103 -0
- synapse_sdk/plugins/steps/utils/__init__.py +20 -0
- synapse_sdk/plugins/steps/utils/logging.py +85 -0
- synapse_sdk/plugins/steps/utils/timing.py +71 -0
- synapse_sdk/plugins/steps/utils/validation.py +68 -0
- synapse_sdk/plugins/templates/__init__.py +50 -0
- synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
- synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
- synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
- synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
- synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
- synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
- synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
- synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
- synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
- synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
- synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
- synapse_sdk/plugins/testing/__init__.py +25 -0
- synapse_sdk/plugins/testing/sample_actions.py +98 -0
- synapse_sdk/plugins/types.py +206 -0
- synapse_sdk/plugins/upload.py +595 -64
- synapse_sdk/plugins/utils.py +325 -37
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/__init__.py +1 -0
- synapse_sdk/utils/auth.py +74 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +449 -0
- synapse_sdk/utils/file/checksum.py +167 -0
- synapse_sdk/utils/file/download.py +286 -0
- synapse_sdk/utils/file/io.py +129 -0
- synapse_sdk/utils/file/requirements.py +36 -0
- synapse_sdk/utils/network.py +168 -0
- synapse_sdk/utils/storage/__init__.py +238 -0
- synapse_sdk/utils/storage/config.py +188 -0
- synapse_sdk/utils/storage/errors.py +52 -0
- synapse_sdk/utils/storage/providers/__init__.py +13 -0
- synapse_sdk/utils/storage/providers/base.py +76 -0
- synapse_sdk/utils/storage/providers/gcs.py +168 -0
- synapse_sdk/utils/storage/providers/http.py +250 -0
- synapse_sdk/utils/storage/providers/local.py +126 -0
- synapse_sdk/utils/storage/providers/s3.py +177 -0
- synapse_sdk/utils/storage/providers/sftp.py +208 -0
- synapse_sdk/utils/storage/registry.py +125 -0
- synapse_sdk/utils/websocket.py +99 -0
- synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
- synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
- synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
- locale/en/LC_MESSAGES/messages.mo +0 -0
- locale/en/LC_MESSAGES/messages.po +0 -39
- locale/ko/LC_MESSAGES/messages.mo +0 -0
- locale/ko/LC_MESSAGES/messages.po +0 -34
- synapse_sdk/cli/create_plugin.py +0 -10
- synapse_sdk/clients/agent/core.py +0 -7
- synapse_sdk/clients/agent/service.py +0 -15
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/clients/ray/__init__.py +0 -6
- synapse_sdk/clients/ray/core.py +0 -22
- synapse_sdk/clients/ray/serve.py +0 -20
- synapse_sdk/i18n.py +0 -35
- synapse_sdk/plugins/categories/__init__.py +0 -0
- synapse_sdk/plugins/categories/base.py +0 -235
- synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
- synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
- synapse_sdk/plugins/categories/decorators.py +0 -13
- synapse_sdk/plugins/categories/export/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/export.py +0 -10
- synapse_sdk/plugins/categories/import/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
- synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
- synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
- synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
- synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
- synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
- synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
- synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
- synapse_sdk/plugins/categories/registry.py +0 -16
- synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
- synapse_sdk/plugins/categories/templates.py +0 -32
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/cli/run.py +0 -67
- synapse_sdk/plugins/exceptions.py +0 -22
- synapse_sdk/plugins/models.py +0 -121
- synapse_sdk/plugins/templates/cookiecutter.json +0 -11
- synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
- synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
- synapse_sdk/shared/enums.py +0 -8
- synapse_sdk/utils/debug.py +0 -5
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/module_loading.py +0 -29
- synapse_sdk/utils/pydantic/__init__.py +0 -0
- synapse_sdk/utils/pydantic/config.py +0 -4
- synapse_sdk/utils/pydantic/errors.py +0 -33
- synapse_sdk/utils/pydantic/validators.py +0 -7
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk/utils/string.py +0 -11
- synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
- synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
|
@@ -1,29 +1,171 @@
|
|
|
1
|
-
|
|
2
|
-
from synapse_sdk.clients.utils import get_default_url_conversion
|
|
1
|
+
"""Annotation client mixin for project and task operations."""
|
|
3
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
def get_project(self, pk):
|
|
7
|
-
path = f'projects/{pk}/'
|
|
8
|
-
return self._get(path)
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
path = f'task_tags/{pk}/'
|
|
12
|
-
return self._get(path)
|
|
7
|
+
from synapse_sdk.clients.backend.models import SetTagsRequest, TaskCreateRequest
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return self._list(path, data=data)
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from synapse_sdk.clients.protocols import ClientProtocol
|
|
17
11
|
|
|
18
|
-
def list_tasks(self, data, url_conversion=None, list_all=False):
|
|
19
|
-
path = 'tasks/'
|
|
20
|
-
url_conversion = get_default_url_conversion(url_conversion, files_fields=['files'])
|
|
21
|
-
return self._list(path, data=data, url_conversion=url_conversion, list_all=list_all)
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return self._post(path, data=data)
|
|
13
|
+
class AnnotationClientMixin:
|
|
14
|
+
"""Mixin for annotation-related API endpoints.
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
Provides methods for managing projects, tasks, and task tags.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def get_project(self: ClientProtocol, project_id: int) -> dict[str, Any]:
|
|
20
|
+
"""Get project details by ID.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
project_id: Project ID.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Project data including configuration and statistics.
|
|
27
|
+
"""
|
|
28
|
+
return self._get(f'projects/{project_id}/')
|
|
29
|
+
|
|
30
|
+
def get_task(
|
|
31
|
+
self: ClientProtocol,
|
|
32
|
+
task_id: int,
|
|
33
|
+
*,
|
|
34
|
+
params: dict[str, Any] | None = None,
|
|
35
|
+
) -> dict[str, Any]:
|
|
36
|
+
"""Get task details by ID.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
task_id: Task ID.
|
|
40
|
+
params: Optional query parameters (e.g., expand, fields).
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Task data including annotations and metadata.
|
|
44
|
+
"""
|
|
45
|
+
return self._get(f'tasks/{task_id}/', params=params)
|
|
46
|
+
|
|
47
|
+
def annotate_task_data(
|
|
48
|
+
self: ClientProtocol,
|
|
49
|
+
task_id: int,
|
|
50
|
+
data: dict[str, Any],
|
|
51
|
+
) -> dict[str, Any]:
|
|
52
|
+
"""Submit annotation data for a task.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
task_id: Task ID to annotate.
|
|
56
|
+
data: Annotation data payload.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Updated task data.
|
|
60
|
+
"""
|
|
61
|
+
return self._put(f'tasks/{task_id}/annotate_task_data/', data=data)
|
|
62
|
+
|
|
63
|
+
def get_task_tag(self: ClientProtocol, tag_id: int) -> dict[str, Any]:
|
|
64
|
+
"""Get task tag details by ID.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
tag_id: Tag ID.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Tag data including name and color.
|
|
71
|
+
"""
|
|
72
|
+
return self._get(f'task_tags/{tag_id}/')
|
|
73
|
+
|
|
74
|
+
def list_task_tags(
|
|
75
|
+
self: ClientProtocol,
|
|
76
|
+
params: dict[str, Any] | None = None,
|
|
77
|
+
) -> dict[str, Any]:
|
|
78
|
+
"""List available task tags.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
params: Optional query parameters for filtering.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Paginated list of task tags.
|
|
85
|
+
"""
|
|
86
|
+
return self._get('task_tags/', params=params)
|
|
87
|
+
|
|
88
|
+
def list_tasks(
|
|
89
|
+
self: ClientProtocol,
|
|
90
|
+
params: dict[str, Any] | None = None,
|
|
91
|
+
*,
|
|
92
|
+
url_conversion: dict[str, Any] | None = None,
|
|
93
|
+
list_all: bool = False,
|
|
94
|
+
) -> dict[str, Any] | tuple[Any, int]:
|
|
95
|
+
"""List tasks with optional pagination.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
params: Query parameters for filtering (project, status, etc.).
|
|
99
|
+
url_conversion: URL-to-path conversion config for file fields.
|
|
100
|
+
list_all: If True, returns (generator, count) for all results.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Paginated task list, or (generator, count) if list_all=True.
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
>>> # Get first page
|
|
107
|
+
>>> tasks = client.list_tasks({'project': 123})
|
|
108
|
+
>>> # Get all tasks as generator
|
|
109
|
+
>>> tasks_gen, count = client.list_tasks({'project': 123}, list_all=True)
|
|
110
|
+
"""
|
|
111
|
+
if url_conversion is None:
|
|
112
|
+
url_conversion = {'files_fields': ['files']}
|
|
113
|
+
|
|
114
|
+
return self._list(
|
|
115
|
+
'sdk/tasks/',
|
|
116
|
+
params=params,
|
|
117
|
+
url_conversion=url_conversion,
|
|
118
|
+
list_all=list_all,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def create_tasks(
|
|
122
|
+
self: ClientProtocol,
|
|
123
|
+
data: dict[str, Any] | list[dict[str, Any]],
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
"""Create one or more annotation tasks.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
data: Task data or list of task data.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Created task(s) response.
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> client.create_tasks({
|
|
135
|
+
... 'project': 123,
|
|
136
|
+
... 'data': [{'image': 'path/to/image.jpg'}]
|
|
137
|
+
... })
|
|
138
|
+
"""
|
|
139
|
+
return self._post('tasks/', request_model=TaskCreateRequest, data=data)
|
|
140
|
+
|
|
141
|
+
def set_tags_tasks(
|
|
142
|
+
self: ClientProtocol,
|
|
143
|
+
data: dict[str, Any],
|
|
144
|
+
*,
|
|
145
|
+
params: dict[str, Any] | None = None,
|
|
146
|
+
) -> dict[str, Any]:
|
|
147
|
+
"""Set tags on multiple tasks.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
data: Tag assignment data with 'ids', 'tags', and 'action'.
|
|
151
|
+
params: Optional query parameters.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Operation result.
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
>>> client.set_tags_tasks({
|
|
158
|
+
... 'ids': [1, 2, 3],
|
|
159
|
+
... 'tags': [10, 20],
|
|
160
|
+
... 'action': 'add' # or 'remove'
|
|
161
|
+
... })
|
|
162
|
+
"""
|
|
163
|
+
return self._post(
|
|
164
|
+
'tasks/set_tags/',
|
|
165
|
+
request_model=SetTagsRequest,
|
|
166
|
+
data=data,
|
|
167
|
+
params=params,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
__all__ = ['AnnotationClientMixin']
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Core backend client mixin with chunked upload support."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
from synapse_sdk.clients.backend.models import (
|
|
11
|
+
ChunkedUploadFinalizeResponse,
|
|
12
|
+
ChunkedUploadResponse,
|
|
13
|
+
)
|
|
14
|
+
from synapse_sdk.utils.file.io import DEFAULT_CHUNK_SIZE, read_file_in_chunks
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
|
|
19
|
+
from synapse_sdk.clients.protocols import ClientProtocol
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CoreClientMixin:
|
|
23
|
+
"""Mixin providing chunked upload functionality.
|
|
24
|
+
|
|
25
|
+
Supports resumable uploads with MD5 integrity verification.
|
|
26
|
+
Files are uploaded in 50MB chunks by default.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def create_chunked_upload(
|
|
30
|
+
self: ClientProtocol,
|
|
31
|
+
file_path: str | Path,
|
|
32
|
+
*,
|
|
33
|
+
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
|
34
|
+
on_progress: Callable[[int, int], None] | None = None,
|
|
35
|
+
) -> dict[str, Any]:
|
|
36
|
+
"""Upload a file in chunks with MD5 integrity verification.
|
|
37
|
+
|
|
38
|
+
Files are uploaded in configurable chunks (default 50MB) with
|
|
39
|
+
Content-Range headers for resumable uploads. MD5 hash is calculated
|
|
40
|
+
incrementally during upload and verified on finalization.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
file_path: Path to the file to upload.
|
|
44
|
+
chunk_size: Size of each chunk in bytes (default 50MB).
|
|
45
|
+
on_progress: Optional callback(bytes_uploaded, total_bytes) for progress.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Finalized upload response with file ID and checksum.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
FileNotFoundError: If file doesn't exist.
|
|
52
|
+
ClientError: If upload fails.
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> def progress(uploaded, total):
|
|
56
|
+
... print(f'{uploaded}/{total} bytes')
|
|
57
|
+
>>> result = client.create_chunked_upload('/path/to/file.zip', on_progress=progress)
|
|
58
|
+
>>> result['id']
|
|
59
|
+
123
|
|
60
|
+
"""
|
|
61
|
+
path = Path(file_path)
|
|
62
|
+
if not path.exists():
|
|
63
|
+
raise FileNotFoundError(f'File not found: {path}')
|
|
64
|
+
|
|
65
|
+
total_size = os.path.getsize(path)
|
|
66
|
+
hash_md5 = hashlib.md5()
|
|
67
|
+
|
|
68
|
+
# Initial upload URL
|
|
69
|
+
url = 'chunked_upload/'
|
|
70
|
+
offset = 0
|
|
71
|
+
|
|
72
|
+
# Upload each chunk
|
|
73
|
+
for chunk in read_file_in_chunks(path, chunk_size):
|
|
74
|
+
hash_md5.update(chunk)
|
|
75
|
+
|
|
76
|
+
response = self._put(
|
|
77
|
+
url,
|
|
78
|
+
data={'filename': path.name},
|
|
79
|
+
files={'file': ('chunk', chunk)},
|
|
80
|
+
headers={'Content-Range': f'bytes {offset}-{offset + len(chunk) - 1}/{total_size}'},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Validate response
|
|
84
|
+
chunk_response = ChunkedUploadResponse.model_validate(response)
|
|
85
|
+
offset = chunk_response.offset
|
|
86
|
+
url = chunk_response.url
|
|
87
|
+
|
|
88
|
+
# Progress callback
|
|
89
|
+
if on_progress:
|
|
90
|
+
on_progress(offset, total_size)
|
|
91
|
+
|
|
92
|
+
# Finalize with MD5 checksum
|
|
93
|
+
result = self._post(url, data={'md5': hash_md5.hexdigest()})
|
|
94
|
+
|
|
95
|
+
# Validate final response
|
|
96
|
+
ChunkedUploadFinalizeResponse.model_validate(result)
|
|
97
|
+
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
__all__ = ['CoreClientMixin']
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"""Data collection client mixin for dataset management."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from synapse_sdk.clients.backend.models import DataFileResponse, DataUnitCreateRequest
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
|
|
14
|
+
from synapse_sdk.clients.protocols import ClientProtocol
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Auto-use chunked upload for files larger than 50MB
|
|
18
|
+
CHUNKED_UPLOAD_THRESHOLD = 1024 * 1024 * 50
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _batch_list(items: list, batch_size: int) -> list[list]:
|
|
22
|
+
"""Split a list into batches of specified size."""
|
|
23
|
+
return [items[i : i + batch_size] for i in range(0, len(items), batch_size)]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DataCollectionClientMixin:
|
|
27
|
+
"""Mixin for data collection API endpoints.
|
|
28
|
+
|
|
29
|
+
Provides methods for managing data collections, files, and units.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def list_data_collections(self: ClientProtocol) -> dict[str, Any]:
|
|
33
|
+
"""List all data collections.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Paginated list of data collections.
|
|
37
|
+
"""
|
|
38
|
+
return self._get('data_collections/')
|
|
39
|
+
|
|
40
|
+
def get_data_collection(
|
|
41
|
+
self: ClientProtocol,
|
|
42
|
+
collection_id: int,
|
|
43
|
+
) -> dict[str, Any]:
|
|
44
|
+
"""Get data collection details by ID.
|
|
45
|
+
|
|
46
|
+
Automatically expands file specifications.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
collection_id: Data collection ID.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Collection data including file specifications.
|
|
53
|
+
"""
|
|
54
|
+
return self._get(
|
|
55
|
+
f'data_collections/{collection_id}/',
|
|
56
|
+
params={'expand': 'file_specifications'},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def create_data_file(
|
|
60
|
+
self: ClientProtocol,
|
|
61
|
+
file_path: str | Path,
|
|
62
|
+
*,
|
|
63
|
+
use_chunked_upload: bool | None = None,
|
|
64
|
+
) -> dict[str, Any]:
|
|
65
|
+
"""Upload a data file.
|
|
66
|
+
|
|
67
|
+
Automatically uses chunked upload for files >50MB unless
|
|
68
|
+
explicitly specified.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
file_path: Path to the file to upload.
|
|
72
|
+
use_chunked_upload: Force chunked (True) or direct (False) upload.
|
|
73
|
+
None = auto-detect based on file size.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
File data with ID, checksum, and size.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
FileNotFoundError: If file doesn't exist.
|
|
80
|
+
"""
|
|
81
|
+
path = Path(file_path)
|
|
82
|
+
if not path.exists():
|
|
83
|
+
raise FileNotFoundError(f'File not found: {path}')
|
|
84
|
+
|
|
85
|
+
# Auto-detect upload method based on file size
|
|
86
|
+
if use_chunked_upload is None:
|
|
87
|
+
use_chunked_upload = path.stat().st_size > CHUNKED_UPLOAD_THRESHOLD
|
|
88
|
+
|
|
89
|
+
if use_chunked_upload:
|
|
90
|
+
upload_result = self.create_chunked_upload(path)
|
|
91
|
+
response = self._post(
|
|
92
|
+
'data_files/',
|
|
93
|
+
data={'chunked_upload': upload_result['id']},
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
response = self._post('data_files/', files={'file': path})
|
|
97
|
+
|
|
98
|
+
DataFileResponse.model_validate(response)
|
|
99
|
+
return response
|
|
100
|
+
|
|
101
|
+
def get_data_unit(
|
|
102
|
+
self: ClientProtocol,
|
|
103
|
+
unit_id: int,
|
|
104
|
+
*,
|
|
105
|
+
params: dict[str, Any] | None = None,
|
|
106
|
+
) -> dict[str, Any]:
|
|
107
|
+
"""Get data unit details by ID.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
unit_id: Data unit ID.
|
|
111
|
+
params: Optional query parameters.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Data unit with files and metadata.
|
|
115
|
+
"""
|
|
116
|
+
return self._get(f'data_units/{unit_id}/', params=params)
|
|
117
|
+
|
|
118
|
+
def create_data_units(
|
|
119
|
+
self: ClientProtocol,
|
|
120
|
+
data: dict[str, Any] | list[dict[str, Any]],
|
|
121
|
+
) -> dict[str, Any]:
|
|
122
|
+
"""Create data unit bindings.
|
|
123
|
+
|
|
124
|
+
Links uploaded files to a data collection.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
data: Data unit(s) to create.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Created data unit(s).
|
|
131
|
+
"""
|
|
132
|
+
return self._post(
|
|
133
|
+
'data_units/',
|
|
134
|
+
request_model=DataUnitCreateRequest,
|
|
135
|
+
data=data,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def list_data_units(
|
|
139
|
+
self: ClientProtocol,
|
|
140
|
+
params: dict[str, Any] | None = None,
|
|
141
|
+
*,
|
|
142
|
+
url_conversion: dict[str, Any] | None = None,
|
|
143
|
+
list_all: bool = False,
|
|
144
|
+
page_size: int = 100,
|
|
145
|
+
timeout: int = 60,
|
|
146
|
+
) -> dict[str, Any] | tuple[Any, int]:
|
|
147
|
+
"""List data units with optional pagination.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
params: Query parameters for filtering.
|
|
151
|
+
url_conversion: URL-to-path conversion config.
|
|
152
|
+
list_all: If True, returns (generator, count).
|
|
153
|
+
page_size: Number of items per page. Default 100 (larger pages
|
|
154
|
+
reduce API calls; timeout increased to handle heavy payloads).
|
|
155
|
+
timeout: Read timeout in seconds. Default 60 (longer timeout
|
|
156
|
+
for large page sizes with heavy file metadata).
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Paginated list or (generator, count).
|
|
160
|
+
"""
|
|
161
|
+
if url_conversion is None:
|
|
162
|
+
url_conversion = {'files_fields': ['files']}
|
|
163
|
+
|
|
164
|
+
if params is None:
|
|
165
|
+
params = {}
|
|
166
|
+
params.setdefault('page_size', page_size)
|
|
167
|
+
|
|
168
|
+
return self._list(
|
|
169
|
+
'data_units/',
|
|
170
|
+
params=params,
|
|
171
|
+
url_conversion=url_conversion,
|
|
172
|
+
list_all=list_all,
|
|
173
|
+
timeout=(5, timeout), # (connect_timeout, read_timeout)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def verify_data_files_checksums(
|
|
177
|
+
self: ClientProtocol,
|
|
178
|
+
checksums: list[str],
|
|
179
|
+
) -> dict[str, Any]:
|
|
180
|
+
"""Verify if data files with given checksums exist.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
checksums: List of MD5 checksums to verify.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Verification result with existing checksums.
|
|
187
|
+
"""
|
|
188
|
+
return self._post('data_files/verify_checksums/', data={'checksums': checksums})
|
|
189
|
+
|
|
190
|
+
def upload_data_file(
|
|
191
|
+
self: ClientProtocol,
|
|
192
|
+
data: dict[str, Any],
|
|
193
|
+
collection_id: int,
|
|
194
|
+
*,
|
|
195
|
+
use_chunked_upload: bool | None = None,
|
|
196
|
+
) -> dict[str, Any]:
|
|
197
|
+
"""Upload individual files for a data unit and return binding data.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
data: Data unit definition with 'files' dict mapping names to paths.
|
|
201
|
+
collection_id: Target data collection ID.
|
|
202
|
+
use_chunked_upload: Force chunked (True) or direct (False) upload.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Data ready for create_data_units() with checksums.
|
|
206
|
+
|
|
207
|
+
Example:
|
|
208
|
+
>>> result = client.upload_data_file(
|
|
209
|
+
... {'files': {'image': '/path/to/img.jpg'}, 'meta': {'label': 1}},
|
|
210
|
+
... collection_id=123
|
|
211
|
+
... )
|
|
212
|
+
>>> # result['files']['image']['checksum'] is populated
|
|
213
|
+
"""
|
|
214
|
+
files_data = {}
|
|
215
|
+
|
|
216
|
+
for name, file_path in data.get('files', {}).items():
|
|
217
|
+
if isinstance(file_path, str):
|
|
218
|
+
path = Path(file_path)
|
|
219
|
+
else:
|
|
220
|
+
path = file_path
|
|
221
|
+
|
|
222
|
+
upload_result = self.create_data_file(path, use_chunked_upload=use_chunked_upload)
|
|
223
|
+
|
|
224
|
+
files_data[name] = {
|
|
225
|
+
'checksum': upload_result['checksum'],
|
|
226
|
+
'path': str(path),
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
'files': files_data,
|
|
231
|
+
'data_collection': collection_id,
|
|
232
|
+
'meta': data.get('meta', {}),
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
def upload_data_collection(
|
|
236
|
+
self: ClientProtocol,
|
|
237
|
+
collection_id: int,
|
|
238
|
+
data: list[dict[str, Any]],
|
|
239
|
+
*,
|
|
240
|
+
project_id: int | None = None,
|
|
241
|
+
batch_size: int = 1000,
|
|
242
|
+
max_workers: int = 10,
|
|
243
|
+
on_progress: Callable[[int, int], None] | None = None,
|
|
244
|
+
) -> None:
|
|
245
|
+
"""Bulk upload data to a collection.
|
|
246
|
+
|
|
247
|
+
Uploads files in parallel using a thread pool, then creates
|
|
248
|
+
data units in batches. Optionally creates annotation tasks.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
collection_id: Target data collection ID.
|
|
252
|
+
data: List of data unit definitions.
|
|
253
|
+
project_id: Optional project ID to create tasks for.
|
|
254
|
+
batch_size: Number of data units per batch (default 1000).
|
|
255
|
+
max_workers: Number of parallel upload threads (default 10).
|
|
256
|
+
on_progress: Optional callback(completed, total) for progress.
|
|
257
|
+
|
|
258
|
+
Example:
|
|
259
|
+
>>> data = [
|
|
260
|
+
... {'files': {'image': '/path/1.jpg'}, 'meta': {'label': 'cat'}},
|
|
261
|
+
... {'files': {'image': '/path/2.jpg'}, 'meta': {'label': 'dog'}},
|
|
262
|
+
... ]
|
|
263
|
+
>>> client.upload_data_collection(123, data, project_id=456)
|
|
264
|
+
"""
|
|
265
|
+
total = len(data)
|
|
266
|
+
completed = 0
|
|
267
|
+
upload_results: list[dict[str, Any]] = []
|
|
268
|
+
|
|
269
|
+
# Upload files in parallel
|
|
270
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
271
|
+
futures = {executor.submit(self.upload_data_file, item, collection_id): i for i, item in enumerate(data)}
|
|
272
|
+
|
|
273
|
+
for future in as_completed(futures):
|
|
274
|
+
result = future.result()
|
|
275
|
+
upload_results.append(result)
|
|
276
|
+
completed += 1
|
|
277
|
+
|
|
278
|
+
if on_progress:
|
|
279
|
+
on_progress(completed, total)
|
|
280
|
+
|
|
281
|
+
# Create data units in batches
|
|
282
|
+
for batch in _batch_list(upload_results, batch_size):
|
|
283
|
+
created_units = self.create_data_units(batch)
|
|
284
|
+
|
|
285
|
+
# Optionally create tasks
|
|
286
|
+
if project_id is not None:
|
|
287
|
+
task_data = [{'data_unit': unit['id']} for unit in created_units.get('results', [created_units])]
|
|
288
|
+
if task_data:
|
|
289
|
+
self._post('tasks/', data={'project': project_id, 'data': task_data})
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
__all__ = ['DataCollectionClientMixin']
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""HITL (Human-in-the-Loop) client mixin for assignment operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from synapse_sdk.clients.backend.models import SetTagsRequest
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from synapse_sdk.clients.protocols import ClientProtocol
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HITLClientMixin:
|
|
14
|
+
"""Mixin for HITL-related API endpoints.
|
|
15
|
+
|
|
16
|
+
Provides methods for managing annotation assignments.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def get_assignment(self: ClientProtocol, assignment_id: int) -> dict[str, Any]:
|
|
20
|
+
"""Get assignment details by ID.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
assignment_id: Assignment ID.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Assignment data including task and annotator info.
|
|
27
|
+
"""
|
|
28
|
+
return self._get(f'assignments/{assignment_id}/')
|
|
29
|
+
|
|
30
|
+
def list_assignments(
|
|
31
|
+
self: ClientProtocol,
|
|
32
|
+
params: dict[str, Any] | None = None,
|
|
33
|
+
*,
|
|
34
|
+
url_conversion: dict[str, Any] | None = None,
|
|
35
|
+
list_all: bool = False,
|
|
36
|
+
) -> dict[str, Any] | tuple[Any, int]:
|
|
37
|
+
"""List assignments with optional pagination.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
params: Query parameters for filtering.
|
|
41
|
+
url_conversion: URL-to-path conversion config.
|
|
42
|
+
list_all: If True, returns (generator, count).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Paginated list or (generator, count).
|
|
46
|
+
"""
|
|
47
|
+
if url_conversion is None:
|
|
48
|
+
url_conversion = {'files_fields': ['files']}
|
|
49
|
+
|
|
50
|
+
return self._list(
|
|
51
|
+
'sdk/assignments/',
|
|
52
|
+
params=params,
|
|
53
|
+
url_conversion=url_conversion,
|
|
54
|
+
list_all=list_all,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def set_tags_assignments(
|
|
58
|
+
self: ClientProtocol,
|
|
59
|
+
data: dict[str, Any],
|
|
60
|
+
*,
|
|
61
|
+
params: dict[str, Any] | None = None,
|
|
62
|
+
) -> dict[str, Any]:
|
|
63
|
+
"""Set tags on multiple assignments.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
data: Tag assignment data with 'ids', 'tags', and 'action'.
|
|
67
|
+
params: Optional query parameters.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Operation result.
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> client.set_tags_assignments({
|
|
74
|
+
... 'ids': [1, 2, 3],
|
|
75
|
+
... 'tags': [10, 20],
|
|
76
|
+
... 'action': 'add' # or 'remove'
|
|
77
|
+
... })
|
|
78
|
+
"""
|
|
79
|
+
return self._post(
|
|
80
|
+
'assignments/set_tags/',
|
|
81
|
+
request_model=SetTagsRequest,
|
|
82
|
+
data=data,
|
|
83
|
+
params=params,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
__all__ = ['HITLClientMixin']
|