synapse-sdk 1.0.0b5__py3-none-any.whl → 2025.12.3__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.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/code_server.py +305 -33
- synapse_sdk/clients/agent/__init__.py +2 -1
- synapse_sdk/clients/agent/container.py +143 -0
- synapse_sdk/clients/agent/ray.py +296 -38
- synapse_sdk/clients/backend/annotation.py +1 -1
- synapse_sdk/clients/backend/core.py +31 -4
- synapse_sdk/clients/backend/data_collection.py +82 -7
- synapse_sdk/clients/backend/hitl.py +1 -1
- synapse_sdk/clients/backend/ml.py +1 -1
- synapse_sdk/clients/base.py +211 -61
- synapse_sdk/loggers.py +46 -0
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/categories/base.py +59 -9
- synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
- synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
- synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
- synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
- synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
- synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
- synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
- synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
- synapse_sdk/plugins/categories/export/templates/config.yaml +19 -1
- synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +153 -177
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1130 -32
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +157 -4
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +7 -4
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/action.py +10 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +148 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +100 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +248 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +265 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +92 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +243 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +236 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +493 -0
- synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
- synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +214 -0
- synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
- synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
- synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +91 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +201 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +104 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +300 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +287 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
- synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
- synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
- synapse_sdk/plugins/categories/upload/templates/config.yaml +28 -2
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +310 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +82 -20
- synapse_sdk/plugins/models.py +111 -9
- synapse_sdk/plugins/templates/plugin-config-schema.json +7 -0
- synapse_sdk/plugins/templates/schema.json +7 -0
- synapse_sdk/plugins/utils/__init__.py +3 -0
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/converters/dm/__init__.py +42 -41
- synapse_sdk/utils/converters/dm/base.py +137 -0
- synapse_sdk/utils/converters/dm/from_v1.py +208 -562
- synapse_sdk/utils/converters/dm/to_v1.py +258 -304
- synapse_sdk/utils/converters/dm/tools/__init__.py +214 -0
- synapse_sdk/utils/converters/dm/tools/answer.py +95 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box.py +132 -0
- synapse_sdk/utils/converters/dm/tools/bounding_box_3d.py +121 -0
- synapse_sdk/utils/converters/dm/tools/classification.py +75 -0
- synapse_sdk/utils/converters/dm/tools/keypoint.py +117 -0
- synapse_sdk/utils/converters/dm/tools/named_entity.py +111 -0
- synapse_sdk/utils/converters/dm/tools/polygon.py +122 -0
- synapse_sdk/utils/converters/dm/tools/polyline.py +124 -0
- synapse_sdk/utils/converters/dm/tools/prompt.py +94 -0
- synapse_sdk/utils/converters/dm/tools/relation.py +86 -0
- synapse_sdk/utils/converters/dm/tools/segmentation.py +141 -0
- synapse_sdk/utils/converters/dm/tools/segmentation_3d.py +83 -0
- synapse_sdk/utils/converters/dm/types.py +168 -0
- synapse_sdk/utils/converters/dm/utils.py +162 -0
- synapse_sdk/utils/converters/dm_legacy/__init__.py +56 -0
- synapse_sdk/utils/converters/dm_legacy/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm_legacy/to_v1.py +367 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +32 -0
- synapse_sdk/utils/file/checksum.py +56 -0
- synapse_sdk/utils/file/chunking.py +31 -0
- synapse_sdk/utils/file/download.py +385 -0
- synapse_sdk/utils/file/encoding.py +40 -0
- synapse_sdk/utils/file/io.py +22 -0
- synapse_sdk/utils/file/upload.py +165 -0
- synapse_sdk/utils/file/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/{file.py → file.py.backup} +77 -0
- synapse_sdk/utils/network.py +272 -0
- synapse_sdk/utils/storage/__init__.py +6 -2
- synapse_sdk/utils/storage/providers/file_system.py +6 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/METADATA +19 -2
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/RECORD +134 -74
- synapse_sdk/devtools/docs/.gitignore +0 -20
- synapse_sdk/devtools/docs/README.md +0 -41
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +0 -12
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +0 -44
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -24
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +0 -29
- synapse_sdk/devtools/docs/blog/authors.yml +0 -25
- synapse_sdk/devtools/docs/blog/tags.yml +0 -19
- synapse_sdk/devtools/docs/docusaurus.config.ts +0 -138
- synapse_sdk/devtools/docs/package-lock.json +0 -17455
- synapse_sdk/devtools/docs/package.json +0 -47
- synapse_sdk/devtools/docs/sidebars.ts +0 -44
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +0 -71
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- synapse_sdk/devtools/docs/src/css/custom.css +0 -30
- synapse_sdk/devtools/docs/src/pages/index.module.css +0 -23
- synapse_sdk/devtools/docs/src/pages/index.tsx +0 -21
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +0 -7
- synapse_sdk/devtools/docs/static/.nojekyll +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
- synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
- synapse_sdk/devtools/docs/static/img/logo.png +0 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +0 -170
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- synapse_sdk/devtools/docs/tsconfig.json +0 -8
- synapse_sdk/plugins/categories/export/actions/export.py +0 -346
- synapse_sdk/plugins/categories/export/enums.py +0 -7
- synapse_sdk/plugins/categories/neural_net/actions/gradio.py +0 -151
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +0 -943
- synapse_sdk/plugins/categories/upload/actions/upload.py +0 -954
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0b5.dist-info → synapse_sdk-2025.12.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Keypoint Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 9.2):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- coordinate {x, y} → data [x, y]
|
|
9
|
+
- classification.class → classification
|
|
10
|
+
- classification.{other} → attrs[{name, value}]
|
|
11
|
+
|
|
12
|
+
V2 → V1:
|
|
13
|
+
- data [x, y] → coordinate {x, y}
|
|
14
|
+
- classification → classification.class
|
|
15
|
+
- attrs[{name, value}] → classification.{name: value} (excluding special attrs)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class KeypointProcessor:
|
|
22
|
+
"""Keypoint Tool Processor
|
|
23
|
+
|
|
24
|
+
V1 coordinate: {x, y}
|
|
25
|
+
V2 data: [x, y]
|
|
26
|
+
|
|
27
|
+
Simplest single point structure.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
tool_name = 'keypoint'
|
|
31
|
+
|
|
32
|
+
# V1 meta fields (not stored in attrs)
|
|
33
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
34
|
+
|
|
35
|
+
# Special attrs not restored to V1 classification (_ prefix)
|
|
36
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
37
|
+
|
|
38
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
39
|
+
"""Convert V1 keypoint to V2
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
v1_annotation: V1 annotations[] item
|
|
43
|
+
v1_data: V1 annotationsData[] item (same ID)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
V2 format keypoint annotation
|
|
47
|
+
"""
|
|
48
|
+
coordinate = v1_data.get('coordinate', {})
|
|
49
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
50
|
+
|
|
51
|
+
# V2 data: [x, y] - extract x, y from coordinate object
|
|
52
|
+
data = [coordinate.get('x', 0), coordinate.get('y', 0)]
|
|
53
|
+
|
|
54
|
+
# Build V2 attrs
|
|
55
|
+
attrs: list[dict[str, Any]] = []
|
|
56
|
+
|
|
57
|
+
# Add other classification properties to attrs (excluding class)
|
|
58
|
+
for key, value in classification_obj.items():
|
|
59
|
+
if key != 'class':
|
|
60
|
+
attrs.append({'name': key, 'value': value})
|
|
61
|
+
|
|
62
|
+
# Build V2 annotation
|
|
63
|
+
return {
|
|
64
|
+
'id': v1_annotation.get('id', ''),
|
|
65
|
+
'classification': classification_obj.get('class', ''),
|
|
66
|
+
'attrs': attrs,
|
|
67
|
+
'data': data,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
71
|
+
"""Convert V2 keypoint to V1
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
v2_annotation: V2 annotation object
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
(V1 annotation, V1 annotationData) tuple
|
|
78
|
+
"""
|
|
79
|
+
annotation_id = v2_annotation.get('id', '')
|
|
80
|
+
classification_str = v2_annotation.get('classification', '')
|
|
81
|
+
attrs = v2_annotation.get('attrs', [])
|
|
82
|
+
data = v2_annotation.get('data', [])
|
|
83
|
+
|
|
84
|
+
# Build V1 coordinate: [x, y] → {x, y}
|
|
85
|
+
coordinate: dict[str, Any] = {}
|
|
86
|
+
if isinstance(data, list) and len(data) >= 2:
|
|
87
|
+
coordinate = {
|
|
88
|
+
'x': data[0],
|
|
89
|
+
'y': data[1],
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Build V1 classification
|
|
93
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
94
|
+
|
|
95
|
+
# Restore properties from attrs
|
|
96
|
+
for attr in attrs:
|
|
97
|
+
name = attr.get('name', '')
|
|
98
|
+
value = attr.get('value')
|
|
99
|
+
|
|
100
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
101
|
+
# Add non-internal attrs to classification
|
|
102
|
+
classification[name] = value
|
|
103
|
+
|
|
104
|
+
# V1 annotation (meta info)
|
|
105
|
+
v1_annotation: dict[str, Any] = {
|
|
106
|
+
'id': annotation_id,
|
|
107
|
+
'tool': self.tool_name,
|
|
108
|
+
'classification': classification,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# V1 annotationData (coordinate info)
|
|
112
|
+
v1_data: dict[str, Any] = {
|
|
113
|
+
'id': annotation_id,
|
|
114
|
+
'coordinate': coordinate,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Named Entity Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 9.5):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- ranges, content → data {ranges, content}
|
|
9
|
+
- classification.class → classification
|
|
10
|
+
- classification.{other} → attrs[{name, value}]
|
|
11
|
+
|
|
12
|
+
V2 → V1:
|
|
13
|
+
- data {ranges, content} → ranges, content
|
|
14
|
+
- classification → classification.class
|
|
15
|
+
- attrs[{name, value}] → classification.{name: value} (excluding special attrs)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class NamedEntityProcessor:
|
|
22
|
+
"""Named Entity Tool Processor
|
|
23
|
+
|
|
24
|
+
V1 annotationData: {ranges: [...], content: str}
|
|
25
|
+
V2 data: {ranges: [...], content: str}
|
|
26
|
+
|
|
27
|
+
Used with text media type.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
tool_name = 'named_entity'
|
|
31
|
+
|
|
32
|
+
# V1 meta fields (not stored in attrs)
|
|
33
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
34
|
+
|
|
35
|
+
# Special attrs not restored to V1 classification (_ prefix)
|
|
36
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
37
|
+
|
|
38
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
39
|
+
"""Convert V1 named entity to V2
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
v1_annotation: V1 annotations[] item
|
|
43
|
+
v1_data: V1 annotationsData[] item (same ID)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
V2 format named entity annotation
|
|
47
|
+
"""
|
|
48
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
49
|
+
|
|
50
|
+
# V2 data: {ranges, content}
|
|
51
|
+
data = {
|
|
52
|
+
'ranges': v1_data.get('ranges', []),
|
|
53
|
+
'content': v1_data.get('content', ''),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Build V2 attrs
|
|
57
|
+
attrs: list[dict[str, Any]] = []
|
|
58
|
+
|
|
59
|
+
# Add other classification properties to attrs (excluding class)
|
|
60
|
+
for key, value in classification_obj.items():
|
|
61
|
+
if key != 'class':
|
|
62
|
+
attrs.append({'name': key, 'value': value})
|
|
63
|
+
|
|
64
|
+
# Build V2 annotation
|
|
65
|
+
return {
|
|
66
|
+
'id': v1_annotation.get('id', ''),
|
|
67
|
+
'classification': classification_obj.get('class', ''),
|
|
68
|
+
'attrs': attrs,
|
|
69
|
+
'data': data,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
73
|
+
"""Convert V2 named entity to V1
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
v2_annotation: V2 annotation object
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
(V1 annotation, V1 annotationData) tuple
|
|
80
|
+
"""
|
|
81
|
+
annotation_id = v2_annotation.get('id', '')
|
|
82
|
+
classification_str = v2_annotation.get('classification', '')
|
|
83
|
+
attrs = v2_annotation.get('attrs', [])
|
|
84
|
+
data = v2_annotation.get('data', {})
|
|
85
|
+
|
|
86
|
+
# Build V1 classification
|
|
87
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
88
|
+
|
|
89
|
+
# Restore properties from attrs
|
|
90
|
+
for attr in attrs:
|
|
91
|
+
name = attr.get('name', '')
|
|
92
|
+
value = attr.get('value')
|
|
93
|
+
|
|
94
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
95
|
+
classification[name] = value
|
|
96
|
+
|
|
97
|
+
# V1 annotation (meta info)
|
|
98
|
+
v1_annotation: dict[str, Any] = {
|
|
99
|
+
'id': annotation_id,
|
|
100
|
+
'tool': self.tool_name,
|
|
101
|
+
'classification': classification,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# V1 annotationData (ranges, content)
|
|
105
|
+
v1_data: dict[str, Any] = {
|
|
106
|
+
'id': annotation_id,
|
|
107
|
+
'ranges': data.get('ranges', []),
|
|
108
|
+
'content': data.get('content', ''),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Polygon Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 4.2):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- coordinate [{x, y, id}, ...] → data [[x, y], ...]
|
|
9
|
+
- classification.class → classification
|
|
10
|
+
- classification.{other} → attrs[{name, value}]
|
|
11
|
+
|
|
12
|
+
V2 → V1:
|
|
13
|
+
- data [[x, y], ...] → coordinate [{x, y, id}, ...]
|
|
14
|
+
- classification → classification.class
|
|
15
|
+
- attrs[{name, value}] → classification.{name: value} (excluding special attrs)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from ..utils import generate_random_id
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PolygonProcessor:
|
|
24
|
+
"""Polygon Tool Processor
|
|
25
|
+
|
|
26
|
+
V1 coordinate: [{x, y, id}, ...]
|
|
27
|
+
V2 data: [[x, y], ...]
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
tool_name = 'polygon'
|
|
31
|
+
|
|
32
|
+
# V1 meta fields (not stored in attrs)
|
|
33
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
34
|
+
|
|
35
|
+
# Special attrs not restored to V1 classification (_ prefix)
|
|
36
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
37
|
+
|
|
38
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
39
|
+
"""Convert V1 polygon to V2
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
v1_annotation: V1 annotations[] item
|
|
43
|
+
v1_data: V1 annotationsData[] item (same ID)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
V2 format polygon annotation
|
|
47
|
+
"""
|
|
48
|
+
coordinate = v1_data.get('coordinate', [])
|
|
49
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
50
|
+
|
|
51
|
+
# V2 data: [[x, y], ...] - extract only x, y from coordinate array
|
|
52
|
+
data = []
|
|
53
|
+
for point in coordinate:
|
|
54
|
+
if isinstance(point, dict):
|
|
55
|
+
data.append([point.get('x', 0), point.get('y', 0)])
|
|
56
|
+
|
|
57
|
+
# Build V2 attrs
|
|
58
|
+
attrs: list[dict[str, Any]] = []
|
|
59
|
+
|
|
60
|
+
# Add other classification properties to attrs (excluding class)
|
|
61
|
+
for key, value in classification_obj.items():
|
|
62
|
+
if key != 'class':
|
|
63
|
+
attrs.append({'name': key, 'value': value})
|
|
64
|
+
|
|
65
|
+
# Build V2 annotation
|
|
66
|
+
return {
|
|
67
|
+
'id': v1_annotation.get('id', ''),
|
|
68
|
+
'classification': classification_obj.get('class', ''),
|
|
69
|
+
'attrs': attrs,
|
|
70
|
+
'data': data,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
74
|
+
"""Convert V2 polygon to V1
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
v2_annotation: V2 annotation object
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
(V1 annotation, V1 annotationData) tuple
|
|
81
|
+
"""
|
|
82
|
+
annotation_id = v2_annotation.get('id', '')
|
|
83
|
+
classification_str = v2_annotation.get('classification', '')
|
|
84
|
+
attrs = v2_annotation.get('attrs', [])
|
|
85
|
+
data = v2_annotation.get('data', [])
|
|
86
|
+
|
|
87
|
+
# Build V1 coordinate: [[x, y], ...] → [{x, y, id}, ...]
|
|
88
|
+
coordinate: list[dict[str, Any]] = []
|
|
89
|
+
for point in data:
|
|
90
|
+
if isinstance(point, list) and len(point) >= 2:
|
|
91
|
+
coordinate.append({
|
|
92
|
+
'x': point[0],
|
|
93
|
+
'y': point[1],
|
|
94
|
+
'id': generate_random_id(),
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
# Build V1 classification
|
|
98
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
99
|
+
|
|
100
|
+
# Restore properties from attrs
|
|
101
|
+
for attr in attrs:
|
|
102
|
+
name = attr.get('name', '')
|
|
103
|
+
value = attr.get('value')
|
|
104
|
+
|
|
105
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
106
|
+
# Add non-internal attrs to classification
|
|
107
|
+
classification[name] = value
|
|
108
|
+
|
|
109
|
+
# V1 annotation (meta info)
|
|
110
|
+
v1_annotation: dict[str, Any] = {
|
|
111
|
+
'id': annotation_id,
|
|
112
|
+
'tool': self.tool_name,
|
|
113
|
+
'classification': classification,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# V1 annotationData (coordinate info)
|
|
117
|
+
v1_data: dict[str, Any] = {
|
|
118
|
+
'id': annotation_id,
|
|
119
|
+
'coordinate': coordinate,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Polyline Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 4.2):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- coordinate [{x, y, id}, ...] → data [[x, y], ...]
|
|
9
|
+
- classification.class → classification
|
|
10
|
+
- classification.{other} → attrs[{name, value}]
|
|
11
|
+
|
|
12
|
+
V2 → V1:
|
|
13
|
+
- data [[x, y], ...] → coordinate [{x, y, id}, ...]
|
|
14
|
+
- classification → classification.class
|
|
15
|
+
- attrs[{name, value}] → classification.{name: value} (excluding special attrs)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from ..utils import generate_random_id
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PolylineProcessor:
|
|
24
|
+
"""Polyline Tool Processor
|
|
25
|
+
|
|
26
|
+
V1 coordinate: [{x, y, id}, ...]
|
|
27
|
+
V2 data: [[x, y], ...]
|
|
28
|
+
|
|
29
|
+
Same data structure as polygon, only tool_name differs.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
tool_name = 'polyline'
|
|
33
|
+
|
|
34
|
+
# V1 meta fields (not stored in attrs)
|
|
35
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
36
|
+
|
|
37
|
+
# Special attrs not restored to V1 classification (_ prefix)
|
|
38
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
39
|
+
|
|
40
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
41
|
+
"""Convert V1 polyline to V2
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
v1_annotation: V1 annotations[] item
|
|
45
|
+
v1_data: V1 annotationsData[] item (same ID)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
V2 format polyline annotation
|
|
49
|
+
"""
|
|
50
|
+
coordinate = v1_data.get('coordinate', [])
|
|
51
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
52
|
+
|
|
53
|
+
# V2 data: [[x, y], ...] - extract only x, y from coordinate array
|
|
54
|
+
data = []
|
|
55
|
+
for point in coordinate:
|
|
56
|
+
if isinstance(point, dict):
|
|
57
|
+
data.append([point.get('x', 0), point.get('y', 0)])
|
|
58
|
+
|
|
59
|
+
# Build V2 attrs
|
|
60
|
+
attrs: list[dict[str, Any]] = []
|
|
61
|
+
|
|
62
|
+
# Add other classification properties to attrs (excluding class)
|
|
63
|
+
for key, value in classification_obj.items():
|
|
64
|
+
if key != 'class':
|
|
65
|
+
attrs.append({'name': key, 'value': value})
|
|
66
|
+
|
|
67
|
+
# Build V2 annotation
|
|
68
|
+
return {
|
|
69
|
+
'id': v1_annotation.get('id', ''),
|
|
70
|
+
'classification': classification_obj.get('class', ''),
|
|
71
|
+
'attrs': attrs,
|
|
72
|
+
'data': data,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
76
|
+
"""Convert V2 polyline to V1
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
v2_annotation: V2 annotation object
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
(V1 annotation, V1 annotationData) tuple
|
|
83
|
+
"""
|
|
84
|
+
annotation_id = v2_annotation.get('id', '')
|
|
85
|
+
classification_str = v2_annotation.get('classification', '')
|
|
86
|
+
attrs = v2_annotation.get('attrs', [])
|
|
87
|
+
data = v2_annotation.get('data', [])
|
|
88
|
+
|
|
89
|
+
# Build V1 coordinate: [[x, y], ...] → [{x, y, id}, ...]
|
|
90
|
+
coordinate: list[dict[str, Any]] = []
|
|
91
|
+
for point in data:
|
|
92
|
+
if isinstance(point, list) and len(point) >= 2:
|
|
93
|
+
coordinate.append({
|
|
94
|
+
'x': point[0],
|
|
95
|
+
'y': point[1],
|
|
96
|
+
'id': generate_random_id(),
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
# Build V1 classification
|
|
100
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
101
|
+
|
|
102
|
+
# Restore properties from attrs
|
|
103
|
+
for attr in attrs:
|
|
104
|
+
name = attr.get('name', '')
|
|
105
|
+
value = attr.get('value')
|
|
106
|
+
|
|
107
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
108
|
+
# Add non-internal attrs to classification
|
|
109
|
+
classification[name] = value
|
|
110
|
+
|
|
111
|
+
# V1 annotation (meta info)
|
|
112
|
+
v1_annotation: dict[str, Any] = {
|
|
113
|
+
'id': annotation_id,
|
|
114
|
+
'tool': self.tool_name,
|
|
115
|
+
'classification': classification,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# V1 annotationData (coordinate info)
|
|
119
|
+
v1_data: dict[str, Any] = {
|
|
120
|
+
'id': annotation_id,
|
|
121
|
+
'coordinate': coordinate,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 9.10):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- input → data.input
|
|
9
|
+
- Other fields from annotationsData → preserved in data
|
|
10
|
+
- classification.class → classification
|
|
11
|
+
- classification.{other} → attrs[{name, value}]
|
|
12
|
+
|
|
13
|
+
V2 → V1:
|
|
14
|
+
- data.input → input
|
|
15
|
+
- Other fields from data → preserved in annotationsData
|
|
16
|
+
- classification → classification.class
|
|
17
|
+
- attrs[{name, value}] → classification.{name: value}
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PromptProcessor:
|
|
24
|
+
"""Prompt Tool Processor
|
|
25
|
+
|
|
26
|
+
V1 annotationsData: {id, tool, input: [{type, value, changeHistory}]}
|
|
27
|
+
V2 data: {input: [...], model?, displayName?, generatedBy?, timestamp?}
|
|
28
|
+
|
|
29
|
+
Prompt annotation data conversion.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
tool_name = 'prompt'
|
|
33
|
+
|
|
34
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
35
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
36
|
+
# Fields to copy from annotationsData to V2 data
|
|
37
|
+
_DATA_FIELDS = {'input', 'model', 'displayName', 'generatedBy', 'timestamp'}
|
|
38
|
+
|
|
39
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
40
|
+
"""Convert V1 prompt to V2"""
|
|
41
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
42
|
+
|
|
43
|
+
# Build V2 data (input and other fields)
|
|
44
|
+
data: dict[str, Any] = {}
|
|
45
|
+
for key in self._DATA_FIELDS:
|
|
46
|
+
if key in v1_data:
|
|
47
|
+
data[key] = v1_data[key]
|
|
48
|
+
|
|
49
|
+
# Build V2 attrs (all classification properties excluding class)
|
|
50
|
+
attrs: list[dict[str, Any]] = []
|
|
51
|
+
for key, value in classification_obj.items():
|
|
52
|
+
if key != 'class':
|
|
53
|
+
attrs.append({'name': key, 'value': value})
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
'id': v1_annotation.get('id', ''),
|
|
57
|
+
'classification': classification_obj.get('class', ''),
|
|
58
|
+
'attrs': attrs,
|
|
59
|
+
'data': data,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
63
|
+
"""Convert V2 prompt to V1"""
|
|
64
|
+
annotation_id = v2_annotation.get('id', '')
|
|
65
|
+
classification_str = v2_annotation.get('classification', '')
|
|
66
|
+
attrs = v2_annotation.get('attrs', [])
|
|
67
|
+
data = v2_annotation.get('data', {})
|
|
68
|
+
|
|
69
|
+
# Build V1 classification
|
|
70
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
71
|
+
for attr in attrs:
|
|
72
|
+
name = attr.get('name', '')
|
|
73
|
+
value = attr.get('value')
|
|
74
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
75
|
+
classification[name] = value
|
|
76
|
+
|
|
77
|
+
v1_annotation: dict[str, Any] = {
|
|
78
|
+
'id': annotation_id,
|
|
79
|
+
'tool': self.tool_name,
|
|
80
|
+
'classification': classification,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Build V1 annotationsData
|
|
84
|
+
v1_data: dict[str, Any] = {
|
|
85
|
+
'id': annotation_id,
|
|
86
|
+
'tool': self.tool_name,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Copy fields from data
|
|
90
|
+
for key in self._DATA_FIELDS:
|
|
91
|
+
if key in data:
|
|
92
|
+
v1_data[key] = data[key]
|
|
93
|
+
|
|
94
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Relation Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 9.9):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- annotationId, targetAnnotationId → data [from_id, to_id]
|
|
9
|
+
- classification.class → classification
|
|
10
|
+
- classification.{other} → attrs[{name, value}]
|
|
11
|
+
|
|
12
|
+
V2 → V1:
|
|
13
|
+
- data [from_id, to_id] → annotationId, targetAnnotationId
|
|
14
|
+
- classification → classification.class
|
|
15
|
+
- attrs[{name, value}] → classification.{name: value}
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RelationProcessor:
|
|
22
|
+
"""Relation Tool Processor
|
|
23
|
+
|
|
24
|
+
V1 annotationData: {annotationId, targetAnnotationId}
|
|
25
|
+
V2 data: [from_id, to_id]
|
|
26
|
+
|
|
27
|
+
Represents relationship between two annotations.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
tool_name = 'relation'
|
|
31
|
+
|
|
32
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
33
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
34
|
+
|
|
35
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
36
|
+
"""Convert V1 relation to V2"""
|
|
37
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
38
|
+
|
|
39
|
+
# V2 data: [from_id, to_id]
|
|
40
|
+
data = [
|
|
41
|
+
v1_data.get('annotationId', ''),
|
|
42
|
+
v1_data.get('targetAnnotationId', ''),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# Build V2 attrs
|
|
46
|
+
attrs: list[dict[str, Any]] = []
|
|
47
|
+
for key, value in classification_obj.items():
|
|
48
|
+
if key != 'class':
|
|
49
|
+
attrs.append({'name': key, 'value': value})
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
'id': v1_annotation.get('id', ''),
|
|
53
|
+
'classification': classification_obj.get('class', ''),
|
|
54
|
+
'attrs': attrs,
|
|
55
|
+
'data': data,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
59
|
+
"""Convert V2 relation to V1"""
|
|
60
|
+
annotation_id = v2_annotation.get('id', '')
|
|
61
|
+
classification_str = v2_annotation.get('classification', '')
|
|
62
|
+
attrs = v2_annotation.get('attrs', [])
|
|
63
|
+
data = v2_annotation.get('data', [])
|
|
64
|
+
|
|
65
|
+
# Build V1 classification
|
|
66
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
67
|
+
for attr in attrs:
|
|
68
|
+
name = attr.get('name', '')
|
|
69
|
+
value = attr.get('value')
|
|
70
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
71
|
+
classification[name] = value
|
|
72
|
+
|
|
73
|
+
v1_annotation: dict[str, Any] = {
|
|
74
|
+
'id': annotation_id,
|
|
75
|
+
'tool': self.tool_name,
|
|
76
|
+
'classification': classification,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# V1 annotationData
|
|
80
|
+
v1_data: dict[str, Any] = {
|
|
81
|
+
'id': annotation_id,
|
|
82
|
+
'annotationId': data[0] if len(data) > 0 else '',
|
|
83
|
+
'targetAnnotationId': data[1] if len(data) > 1 else '',
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return v1_annotation, v1_data
|