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,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DM Schema V1/V2 Tool-Specific Processors
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-11
|
|
5
|
+
|
|
6
|
+
Implement and register processors in this module when adding new tool support.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any, Protocol, runtime_checkable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@runtime_checkable
|
|
13
|
+
class ToolProcessor(Protocol):
|
|
14
|
+
"""Tool-Specific Conversion Processor Interface
|
|
15
|
+
|
|
16
|
+
Implement this protocol when adding new tool support.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
tool_name: Tool name (e.g., 'bounding_box', 'polygon')
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
>>> class KeypointProcessor:
|
|
23
|
+
... tool_name = "keypoint"
|
|
24
|
+
...
|
|
25
|
+
... def to_v2(self, v1_annotation, v1_data):
|
|
26
|
+
... # V1 → V2 conversion logic
|
|
27
|
+
... return {...}
|
|
28
|
+
...
|
|
29
|
+
... def to_v1(self, v2_annotation):
|
|
30
|
+
... # V2 → V1 conversion logic
|
|
31
|
+
... return ({...}, {...})
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def tool_name(self) -> str:
|
|
36
|
+
"""Tool name (e.g., 'bounding_box', 'polygon')"""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
40
|
+
"""Convert V1 annotation to V2 format
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
v1_annotation: V1 annotations[] item
|
|
44
|
+
v1_data: V1 annotationsData[] item (same ID)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
V2 format annotation object (id, classification, attrs, data)
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: Data cannot be converted
|
|
51
|
+
"""
|
|
52
|
+
...
|
|
53
|
+
|
|
54
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
55
|
+
"""Convert V2 annotation to V1 format
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
v2_annotation: V2 annotation object
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
(V1 annotation, V1 annotationData) tuple
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValueError: Data cannot be converted
|
|
65
|
+
"""
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# =============================================================================
|
|
70
|
+
# Processor Registry
|
|
71
|
+
# =============================================================================
|
|
72
|
+
|
|
73
|
+
# Global registry storing registered processors
|
|
74
|
+
_PROCESSOR_REGISTRY: dict[str, ToolProcessor] = {}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def register_tool_processor(processor: ToolProcessor) -> None:
|
|
78
|
+
"""Register a tool processor to the global registry
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
processor: ToolProcessor implementation
|
|
82
|
+
"""
|
|
83
|
+
_PROCESSOR_REGISTRY[processor.tool_name] = processor
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_tool_processor(tool_name: str) -> ToolProcessor | None:
|
|
87
|
+
"""Get a tool processor from the global registry
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
tool_name: Tool name
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Registered processor or None
|
|
94
|
+
"""
|
|
95
|
+
return _PROCESSOR_REGISTRY.get(tool_name)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get_all_processors() -> dict[str, ToolProcessor]:
|
|
99
|
+
"""Return all registered processors
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Tool name → processor dictionary
|
|
103
|
+
"""
|
|
104
|
+
return _PROCESSOR_REGISTRY.copy()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# =============================================================================
|
|
108
|
+
# Processor Import and Auto-Registration
|
|
109
|
+
# =============================================================================
|
|
110
|
+
|
|
111
|
+
# Auto-registered when processor modules are imported
|
|
112
|
+
# Note: Actual processors are imported later to prevent circular imports
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _register_default_processors() -> None:
|
|
116
|
+
"""Register default processors
|
|
117
|
+
|
|
118
|
+
Called on module load to register default processors.
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
from .bounding_box import BoundingBoxProcessor
|
|
122
|
+
|
|
123
|
+
register_tool_processor(BoundingBoxProcessor())
|
|
124
|
+
except ImportError:
|
|
125
|
+
pass # Not yet implemented
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
from .polygon import PolygonProcessor
|
|
129
|
+
|
|
130
|
+
register_tool_processor(PolygonProcessor())
|
|
131
|
+
except ImportError:
|
|
132
|
+
pass # Not yet implemented
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
from .polyline import PolylineProcessor
|
|
136
|
+
|
|
137
|
+
register_tool_processor(PolylineProcessor())
|
|
138
|
+
except ImportError:
|
|
139
|
+
pass # Not yet implemented
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
from .keypoint import KeypointProcessor
|
|
143
|
+
|
|
144
|
+
register_tool_processor(KeypointProcessor())
|
|
145
|
+
except ImportError:
|
|
146
|
+
pass # Not yet implemented
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
from .bounding_box_3d import BoundingBox3DProcessor
|
|
150
|
+
|
|
151
|
+
register_tool_processor(BoundingBox3DProcessor())
|
|
152
|
+
except ImportError:
|
|
153
|
+
pass # Not yet implemented
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
from .segmentation import SegmentationProcessor
|
|
157
|
+
|
|
158
|
+
register_tool_processor(SegmentationProcessor())
|
|
159
|
+
except ImportError:
|
|
160
|
+
pass # Not yet implemented
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
from .named_entity import NamedEntityProcessor
|
|
164
|
+
|
|
165
|
+
register_tool_processor(NamedEntityProcessor())
|
|
166
|
+
except ImportError:
|
|
167
|
+
pass # Not yet implemented
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
from .segmentation_3d import Segmentation3DProcessor
|
|
171
|
+
|
|
172
|
+
register_tool_processor(Segmentation3DProcessor())
|
|
173
|
+
except ImportError:
|
|
174
|
+
pass # Not yet implemented
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
from .classification import ClassificationProcessor
|
|
178
|
+
|
|
179
|
+
register_tool_processor(ClassificationProcessor())
|
|
180
|
+
except ImportError:
|
|
181
|
+
pass # Not yet implemented
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
from .relation import RelationProcessor
|
|
185
|
+
|
|
186
|
+
register_tool_processor(RelationProcessor())
|
|
187
|
+
except ImportError:
|
|
188
|
+
pass # Not yet implemented
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
from .prompt import PromptProcessor
|
|
192
|
+
|
|
193
|
+
register_tool_processor(PromptProcessor())
|
|
194
|
+
except ImportError:
|
|
195
|
+
pass # Not yet implemented
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
from .answer import AnswerProcessor
|
|
199
|
+
|
|
200
|
+
register_tool_processor(AnswerProcessor())
|
|
201
|
+
except ImportError:
|
|
202
|
+
pass # Not yet implemented
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# Attempt to register default processors on module load
|
|
206
|
+
_register_default_processors()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
__all__ = [
|
|
210
|
+
'ToolProcessor',
|
|
211
|
+
'register_tool_processor',
|
|
212
|
+
'get_tool_processor',
|
|
213
|
+
'get_all_processors',
|
|
214
|
+
]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Answer Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 9.11):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- output → data.output
|
|
9
|
+
- model, displayName, generatedBy, promptAnnotationId → preserved in data
|
|
10
|
+
- classification.class → classification
|
|
11
|
+
- classification.{other} → attrs[{name, value}]
|
|
12
|
+
|
|
13
|
+
V2 → V1:
|
|
14
|
+
- data.output → output
|
|
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 AnswerProcessor:
|
|
24
|
+
"""Answer Tool Processor
|
|
25
|
+
|
|
26
|
+
V1 annotationsData: {id, tool, model, output: [{type, value, primaryKey, changeHistory}],
|
|
27
|
+
displayName, generatedBy, promptAnnotationId}
|
|
28
|
+
V2 data: {output: [...], model, displayName, generatedBy, promptAnnotationId, timestamp?}
|
|
29
|
+
|
|
30
|
+
Answer annotation data conversion.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
tool_name = 'answer'
|
|
34
|
+
|
|
35
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
36
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
37
|
+
# Fields to copy from annotationsData to V2 data
|
|
38
|
+
_DATA_FIELDS = {'output', 'model', 'displayName', 'generatedBy', 'promptAnnotationId', 'timestamp'}
|
|
39
|
+
|
|
40
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
41
|
+
"""Convert V1 answer to V2"""
|
|
42
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
43
|
+
|
|
44
|
+
# Build V2 data (output and other fields)
|
|
45
|
+
data: dict[str, Any] = {}
|
|
46
|
+
for key in self._DATA_FIELDS:
|
|
47
|
+
if key in v1_data:
|
|
48
|
+
data[key] = v1_data[key]
|
|
49
|
+
|
|
50
|
+
# Build V2 attrs (all classification properties excluding class)
|
|
51
|
+
attrs: list[dict[str, Any]] = []
|
|
52
|
+
for key, value in classification_obj.items():
|
|
53
|
+
if key != 'class':
|
|
54
|
+
attrs.append({'name': key, 'value': value})
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
'id': v1_annotation.get('id', ''),
|
|
58
|
+
'classification': classification_obj.get('class', ''),
|
|
59
|
+
'attrs': attrs,
|
|
60
|
+
'data': data,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
64
|
+
"""Convert V2 answer to V1"""
|
|
65
|
+
annotation_id = v2_annotation.get('id', '')
|
|
66
|
+
classification_str = v2_annotation.get('classification', '')
|
|
67
|
+
attrs = v2_annotation.get('attrs', [])
|
|
68
|
+
data = v2_annotation.get('data', {})
|
|
69
|
+
|
|
70
|
+
# Build V1 classification
|
|
71
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
72
|
+
for attr in attrs:
|
|
73
|
+
name = attr.get('name', '')
|
|
74
|
+
value = attr.get('value')
|
|
75
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
76
|
+
classification[name] = value
|
|
77
|
+
|
|
78
|
+
v1_annotation: dict[str, Any] = {
|
|
79
|
+
'id': annotation_id,
|
|
80
|
+
'tool': self.tool_name,
|
|
81
|
+
'classification': classification,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Build V1 annotationsData
|
|
85
|
+
v1_data: dict[str, Any] = {
|
|
86
|
+
'id': annotation_id,
|
|
87
|
+
'tool': self.tool_name,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Copy fields from data
|
|
91
|
+
for key in self._DATA_FIELDS:
|
|
92
|
+
if key in data:
|
|
93
|
+
v1_data[key] = data[key]
|
|
94
|
+
|
|
95
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bounding Box Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-11
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 4.1):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- coordinate.{x, y, width, height} → data[x, y, width, height]
|
|
9
|
+
- coordinate.rotation → attrs[{name:"rotation", value}]
|
|
10
|
+
- classification.class → classification
|
|
11
|
+
- classification.{other} → attrs[{name, value}]
|
|
12
|
+
|
|
13
|
+
V2 → V1:
|
|
14
|
+
- data[0,1,2,3] → coordinate.{x, y, width, height}
|
|
15
|
+
- attrs.rotation → coordinate.rotation
|
|
16
|
+
- classification → classification.class
|
|
17
|
+
- attrs[{name, value}] → classification.{name: value} (excluding special attrs)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BoundingBoxProcessor:
|
|
24
|
+
"""Bounding Box Tool Processor
|
|
25
|
+
|
|
26
|
+
V1 coordinate: {x, y, width, height, rotation?}
|
|
27
|
+
V2 data: [x, y, width, height]
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
tool_name = 'bounding_box'
|
|
31
|
+
|
|
32
|
+
# V1 meta fields (not stored in attrs)
|
|
33
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
34
|
+
|
|
35
|
+
# Fields to restore from V2 attrs to coordinate
|
|
36
|
+
_COORDINATE_ATTRS = {'rotation'}
|
|
37
|
+
|
|
38
|
+
# Special attrs not restored to V1 classification (_ prefix)
|
|
39
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
40
|
+
|
|
41
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
42
|
+
"""Convert V1 bounding box to V2
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
v1_annotation: V1 annotations[] item
|
|
46
|
+
v1_data: V1 annotationsData[] item (same ID)
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
V2 format bounding box annotation
|
|
50
|
+
"""
|
|
51
|
+
coordinate = v1_data.get('coordinate', {})
|
|
52
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
53
|
+
|
|
54
|
+
# V2 data: [x, y, width, height]
|
|
55
|
+
data = [
|
|
56
|
+
coordinate.get('x', 0),
|
|
57
|
+
coordinate.get('y', 0),
|
|
58
|
+
coordinate.get('width', 0),
|
|
59
|
+
coordinate.get('height', 0),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
# Build V2 attrs
|
|
63
|
+
attrs: list[dict[str, Any]] = []
|
|
64
|
+
|
|
65
|
+
# Add rotation to attrs
|
|
66
|
+
if 'rotation' in coordinate:
|
|
67
|
+
attrs.append({'name': 'rotation', 'value': coordinate['rotation']})
|
|
68
|
+
|
|
69
|
+
# Add other classification properties to attrs (excluding class)
|
|
70
|
+
for key, value in classification_obj.items():
|
|
71
|
+
if key != 'class':
|
|
72
|
+
attrs.append({'name': key, 'value': value})
|
|
73
|
+
|
|
74
|
+
# Build V2 annotation
|
|
75
|
+
return {
|
|
76
|
+
'id': v1_annotation.get('id', ''),
|
|
77
|
+
'classification': classification_obj.get('class', ''),
|
|
78
|
+
'attrs': attrs,
|
|
79
|
+
'data': data,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
83
|
+
"""Convert V2 bounding box to V1
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
v2_annotation: V2 annotation object
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
(V1 annotation, V1 annotationData) tuple
|
|
90
|
+
"""
|
|
91
|
+
annotation_id = v2_annotation.get('id', '')
|
|
92
|
+
classification_str = v2_annotation.get('classification', '')
|
|
93
|
+
attrs = v2_annotation.get('attrs', [])
|
|
94
|
+
data = v2_annotation.get('data', [0, 0, 0, 0])
|
|
95
|
+
|
|
96
|
+
# Build V1 coordinate
|
|
97
|
+
coordinate: dict[str, Any] = {
|
|
98
|
+
'x': data[0] if len(data) > 0 else 0,
|
|
99
|
+
'y': data[1] if len(data) > 1 else 0,
|
|
100
|
+
'width': data[2] if len(data) > 2 else 0,
|
|
101
|
+
'height': data[3] if len(data) > 3 else 0,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Build V1 classification
|
|
105
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
106
|
+
|
|
107
|
+
# Restore properties from attrs
|
|
108
|
+
for attr in attrs:
|
|
109
|
+
name = attr.get('name', '')
|
|
110
|
+
value = attr.get('value')
|
|
111
|
+
|
|
112
|
+
if name in self._COORDINATE_ATTRS:
|
|
113
|
+
# Add rotation etc. to coordinate
|
|
114
|
+
coordinate[name] = value
|
|
115
|
+
elif not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
116
|
+
# Add non-internal attrs to classification
|
|
117
|
+
classification[name] = value
|
|
118
|
+
|
|
119
|
+
# V1 annotation (meta info)
|
|
120
|
+
v1_annotation: dict[str, Any] = {
|
|
121
|
+
'id': annotation_id,
|
|
122
|
+
'tool': self.tool_name,
|
|
123
|
+
'classification': classification,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# V1 annotationData (coordinate info)
|
|
127
|
+
v1_data: dict[str, Any] = {
|
|
128
|
+
'id': annotation_id,
|
|
129
|
+
'coordinate': coordinate,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
3D Bounding Box Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 9.6):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- psr {position, scale, rotation} → data {position, scale, rotation}
|
|
9
|
+
- classification.class → classification
|
|
10
|
+
- classification.{other} → attrs[{name, value}]
|
|
11
|
+
|
|
12
|
+
V2 → V1:
|
|
13
|
+
- data {position, scale, rotation} → psr {position, scale, rotation}
|
|
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 BoundingBox3DProcessor:
|
|
22
|
+
"""3D Bounding Box Tool Processor
|
|
23
|
+
|
|
24
|
+
V1 psr: {position, scale, rotation} each {x, y, z}
|
|
25
|
+
V2 data: {position, scale, rotation} each {x, y, z}
|
|
26
|
+
|
|
27
|
+
PSR structure is identical, only key name changes (psr → data).
|
|
28
|
+
Used with pcd (point cloud) media type.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
tool_name = '3d_bounding_box'
|
|
32
|
+
|
|
33
|
+
# V1 meta fields (not stored in attrs)
|
|
34
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
35
|
+
|
|
36
|
+
# Special attrs not restored to V1 classification (_ prefix)
|
|
37
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
38
|
+
|
|
39
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
40
|
+
"""Convert V1 3D bounding box to V2
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
v1_annotation: V1 annotations[] item
|
|
44
|
+
v1_data: V1 annotationsData[] item (same ID)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
V2 format 3D bounding box annotation
|
|
48
|
+
"""
|
|
49
|
+
psr = v1_data.get('psr', {})
|
|
50
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
51
|
+
|
|
52
|
+
# V2 data: copy psr structure as-is
|
|
53
|
+
data = {
|
|
54
|
+
'position': psr.get('position', {'x': 0, 'y': 0, 'z': 0}),
|
|
55
|
+
'scale': psr.get('scale', {'x': 0, 'y': 0, 'z': 0}),
|
|
56
|
+
'rotation': psr.get('rotation', {'x': 0, 'y': 0, 'z': 0}),
|
|
57
|
+
}
|
|
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 3D bounding box 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 psr: copy data structure as-is
|
|
90
|
+
psr: dict[str, Any] = {
|
|
91
|
+
'position': data.get('position', {'x': 0, 'y': 0, 'z': 0}),
|
|
92
|
+
'scale': data.get('scale', {'x': 0, 'y': 0, 'z': 0}),
|
|
93
|
+
'rotation': data.get('rotation', {'x': 0, 'y': 0, 'z': 0}),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Build V1 classification
|
|
97
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
98
|
+
|
|
99
|
+
# Restore properties from attrs
|
|
100
|
+
for attr in attrs:
|
|
101
|
+
name = attr.get('name', '')
|
|
102
|
+
value = attr.get('value')
|
|
103
|
+
|
|
104
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
105
|
+
# Add non-internal attrs to classification
|
|
106
|
+
classification[name] = value
|
|
107
|
+
|
|
108
|
+
# V1 annotation (meta info)
|
|
109
|
+
v1_annotation: dict[str, Any] = {
|
|
110
|
+
'id': annotation_id,
|
|
111
|
+
'tool': self.tool_name,
|
|
112
|
+
'classification': classification,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# V1 annotationData (coordinate info)
|
|
116
|
+
v1_data: dict[str, Any] = {
|
|
117
|
+
'id': annotation_id,
|
|
118
|
+
'psr': psr,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return v1_annotation, v1_data
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Classification Tool Processor
|
|
3
|
+
|
|
4
|
+
Created: 2025-12-12
|
|
5
|
+
|
|
6
|
+
Conversion Rules (see data-model.md 9.8):
|
|
7
|
+
V1 → V2:
|
|
8
|
+
- classification.class → classification
|
|
9
|
+
- classification.{other} → attrs[{name, value}]
|
|
10
|
+
- annotationsData contains only id
|
|
11
|
+
|
|
12
|
+
V2 → V1:
|
|
13
|
+
- classification → classification.class
|
|
14
|
+
- attrs[{name, value}] → classification.{name: value}
|
|
15
|
+
- data is empty object {}
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClassificationProcessor:
|
|
22
|
+
"""Classification Tool Processor
|
|
23
|
+
|
|
24
|
+
V1: All properties in annotations, only id in annotationsData
|
|
25
|
+
V2: data is empty object, all properties stored in attrs
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
tool_name = 'classification'
|
|
29
|
+
|
|
30
|
+
_META_FIELDS = {'isLocked', 'isVisible', 'isValid', 'isDrawCompleted', 'label', 'id', 'tool'}
|
|
31
|
+
_INTERNAL_ATTR_PREFIX = '_'
|
|
32
|
+
|
|
33
|
+
def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
|
|
34
|
+
"""Convert V1 classification to V2"""
|
|
35
|
+
classification_obj = v1_annotation.get('classification') or {}
|
|
36
|
+
|
|
37
|
+
# Build V2 attrs (all properties excluding class)
|
|
38
|
+
attrs: list[dict[str, Any]] = []
|
|
39
|
+
for key, value in classification_obj.items():
|
|
40
|
+
if key != 'class':
|
|
41
|
+
attrs.append({'name': key, 'value': value})
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
'id': v1_annotation.get('id', ''),
|
|
45
|
+
'classification': classification_obj.get('class', ''),
|
|
46
|
+
'attrs': attrs,
|
|
47
|
+
'data': {}, # Empty object
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
|
|
51
|
+
"""Convert V2 classification to V1"""
|
|
52
|
+
annotation_id = v2_annotation.get('id', '')
|
|
53
|
+
classification_str = v2_annotation.get('classification', '')
|
|
54
|
+
attrs = v2_annotation.get('attrs', [])
|
|
55
|
+
|
|
56
|
+
# Build V1 classification
|
|
57
|
+
classification: dict[str, Any] = {'class': classification_str}
|
|
58
|
+
for attr in attrs:
|
|
59
|
+
name = attr.get('name', '')
|
|
60
|
+
value = attr.get('value')
|
|
61
|
+
if not name.startswith(self._INTERNAL_ATTR_PREFIX):
|
|
62
|
+
classification[name] = value
|
|
63
|
+
|
|
64
|
+
v1_annotation: dict[str, Any] = {
|
|
65
|
+
'id': annotation_id,
|
|
66
|
+
'tool': self.tool_name,
|
|
67
|
+
'classification': classification,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# annotationsData contains only id
|
|
71
|
+
v1_data: dict[str, Any] = {
|
|
72
|
+
'id': annotation_id,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return v1_annotation, v1_data
|