synapse-sdk 1.0.0a23__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.
Files changed (228) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +310 -5
  3. synapse_sdk/cli/alias/__init__.py +22 -0
  4. synapse_sdk/cli/alias/create.py +36 -0
  5. synapse_sdk/cli/alias/dataclass.py +31 -0
  6. synapse_sdk/cli/alias/default.py +16 -0
  7. synapse_sdk/cli/alias/delete.py +15 -0
  8. synapse_sdk/cli/alias/list.py +19 -0
  9. synapse_sdk/cli/alias/read.py +15 -0
  10. synapse_sdk/cli/alias/update.py +17 -0
  11. synapse_sdk/cli/alias/utils.py +61 -0
  12. synapse_sdk/cli/code_server.py +687 -0
  13. synapse_sdk/cli/config.py +440 -0
  14. synapse_sdk/cli/devtools.py +90 -0
  15. synapse_sdk/cli/plugin/__init__.py +33 -0
  16. synapse_sdk/cli/{create_plugin.py → plugin/create.py} +2 -2
  17. synapse_sdk/{plugins/cli → cli/plugin}/publish.py +23 -15
  18. synapse_sdk/clients/agent/__init__.py +9 -3
  19. synapse_sdk/clients/agent/container.py +143 -0
  20. synapse_sdk/clients/agent/core.py +19 -0
  21. synapse_sdk/clients/agent/ray.py +298 -9
  22. synapse_sdk/clients/backend/__init__.py +30 -12
  23. synapse_sdk/clients/backend/annotation.py +13 -5
  24. synapse_sdk/clients/backend/core.py +31 -4
  25. synapse_sdk/clients/backend/data_collection.py +186 -0
  26. synapse_sdk/clients/backend/hitl.py +17 -0
  27. synapse_sdk/clients/backend/integration.py +16 -1
  28. synapse_sdk/clients/backend/ml.py +5 -1
  29. synapse_sdk/clients/backend/models.py +78 -0
  30. synapse_sdk/clients/base.py +384 -41
  31. synapse_sdk/clients/ray/serve.py +2 -0
  32. synapse_sdk/clients/validators/collections.py +31 -0
  33. synapse_sdk/devtools/config.py +94 -0
  34. synapse_sdk/devtools/server.py +41 -0
  35. synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
  36. synapse_sdk/devtools/streamlit_app/app.py +128 -0
  37. synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
  38. synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
  39. synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
  40. synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
  41. synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
  42. synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
  43. synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
  44. synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
  45. synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
  46. synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
  47. synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
  48. synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
  49. synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
  50. synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
  51. synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
  52. synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
  53. synapse_sdk/devtools/streamlit_app.py +10 -0
  54. synapse_sdk/loggers.py +120 -9
  55. synapse_sdk/plugins/README.md +1340 -0
  56. synapse_sdk/plugins/__init__.py +0 -13
  57. synapse_sdk/plugins/categories/base.py +117 -11
  58. synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
  59. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
  60. synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
  61. synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
  62. synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
  63. synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
  64. synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
  65. synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
  66. synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
  67. synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
  68. synapse_sdk/plugins/categories/export/templates/config.yaml +21 -0
  69. synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
  70. synapse_sdk/plugins/categories/export/templates/plugin/export.py +160 -0
  71. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +13 -12
  72. synapse_sdk/plugins/categories/neural_net/actions/train.py +1134 -31
  73. synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
  74. synapse_sdk/plugins/categories/neural_net/base/inference.py +1 -1
  75. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +32 -4
  76. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
  77. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
  78. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
  79. synapse_sdk/plugins/categories/{export/actions/export.py → pre_annotation/actions/pre_annotation/action.py} +4 -4
  80. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
  81. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +148 -0
  82. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
  83. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
  84. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
  85. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +100 -0
  86. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +248 -0
  87. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
  88. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
  89. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +265 -0
  90. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
  91. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
  92. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +92 -0
  93. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +243 -0
  94. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
  95. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
  96. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
  97. synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +2 -0
  98. synapse_sdk/plugins/categories/upload/__init__.py +0 -0
  99. synapse_sdk/plugins/categories/upload/actions/__init__.py +0 -0
  100. synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
  101. synapse_sdk/plugins/categories/upload/actions/upload/action.py +236 -0
  102. synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
  103. synapse_sdk/plugins/categories/upload/actions/upload/enums.py +493 -0
  104. synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
  105. synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
  106. synapse_sdk/plugins/categories/upload/actions/upload/models.py +214 -0
  107. synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
  108. synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
  109. synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
  110. synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
  111. synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
  112. synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
  113. synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
  114. synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +91 -0
  115. synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
  116. synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
  117. synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +201 -0
  118. synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +104 -0
  119. synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
  120. synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
  121. synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
  122. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
  123. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
  124. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
  125. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
  126. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +300 -0
  127. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +287 -0
  128. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
  129. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
  130. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
  131. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
  132. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
  133. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
  134. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
  135. synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
  136. synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
  137. synapse_sdk/plugins/categories/upload/templates/config.yaml +33 -0
  138. synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +310 -0
  139. synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +102 -0
  140. synapse_sdk/plugins/enums.py +3 -1
  141. synapse_sdk/plugins/models.py +148 -11
  142. synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
  143. synapse_sdk/plugins/templates/schema.json +491 -0
  144. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +1 -0
  145. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
  146. synapse_sdk/plugins/utils/__init__.py +46 -0
  147. synapse_sdk/plugins/utils/actions.py +119 -0
  148. synapse_sdk/plugins/utils/config.py +203 -0
  149. synapse_sdk/plugins/{utils.py → utils/legacy.py} +26 -46
  150. synapse_sdk/plugins/utils/ray_gcs.py +66 -0
  151. synapse_sdk/plugins/utils/registry.py +58 -0
  152. synapse_sdk/shared/__init__.py +25 -0
  153. synapse_sdk/shared/enums.py +93 -0
  154. synapse_sdk/types.py +19 -0
  155. synapse_sdk/utils/converters/__init__.py +240 -0
  156. synapse_sdk/utils/converters/coco/__init__.py +0 -0
  157. synapse_sdk/utils/converters/coco/from_dm.py +322 -0
  158. synapse_sdk/utils/converters/coco/to_dm.py +215 -0
  159. synapse_sdk/utils/converters/dm/__init__.py +57 -0
  160. synapse_sdk/utils/converters/dm/base.py +137 -0
  161. synapse_sdk/utils/converters/dm/from_v1.py +273 -0
  162. synapse_sdk/utils/converters/dm/to_v1.py +321 -0
  163. synapse_sdk/utils/converters/dm/tools/__init__.py +214 -0
  164. synapse_sdk/utils/converters/dm/tools/answer.py +95 -0
  165. synapse_sdk/utils/converters/dm/tools/bounding_box.py +132 -0
  166. synapse_sdk/utils/converters/dm/tools/bounding_box_3d.py +121 -0
  167. synapse_sdk/utils/converters/dm/tools/classification.py +75 -0
  168. synapse_sdk/utils/converters/dm/tools/keypoint.py +117 -0
  169. synapse_sdk/utils/converters/dm/tools/named_entity.py +111 -0
  170. synapse_sdk/utils/converters/dm/tools/polygon.py +122 -0
  171. synapse_sdk/utils/converters/dm/tools/polyline.py +124 -0
  172. synapse_sdk/utils/converters/dm/tools/prompt.py +94 -0
  173. synapse_sdk/utils/converters/dm/tools/relation.py +86 -0
  174. synapse_sdk/utils/converters/dm/tools/segmentation.py +141 -0
  175. synapse_sdk/utils/converters/dm/tools/segmentation_3d.py +83 -0
  176. synapse_sdk/utils/converters/dm/types.py +168 -0
  177. synapse_sdk/utils/converters/dm/utils.py +162 -0
  178. synapse_sdk/utils/converters/dm_legacy/__init__.py +56 -0
  179. synapse_sdk/utils/converters/dm_legacy/from_v1.py +627 -0
  180. synapse_sdk/utils/converters/dm_legacy/to_v1.py +367 -0
  181. synapse_sdk/utils/converters/pascal/__init__.py +0 -0
  182. synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
  183. synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
  184. synapse_sdk/utils/converters/yolo/__init__.py +0 -0
  185. synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
  186. synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
  187. synapse_sdk/utils/dataset.py +46 -0
  188. synapse_sdk/utils/encryption.py +158 -0
  189. synapse_sdk/utils/file/__init__.py +58 -0
  190. synapse_sdk/utils/file/archive.py +32 -0
  191. synapse_sdk/utils/file/checksum.py +56 -0
  192. synapse_sdk/utils/file/chunking.py +31 -0
  193. synapse_sdk/utils/file/download.py +385 -0
  194. synapse_sdk/utils/file/encoding.py +40 -0
  195. synapse_sdk/utils/file/io.py +22 -0
  196. synapse_sdk/utils/file/upload.py +165 -0
  197. synapse_sdk/utils/file/video/__init__.py +29 -0
  198. synapse_sdk/utils/file/video/transcode.py +307 -0
  199. synapse_sdk/utils/file.py.backup +301 -0
  200. synapse_sdk/utils/http.py +138 -0
  201. synapse_sdk/utils/network.py +309 -0
  202. synapse_sdk/utils/storage/__init__.py +72 -0
  203. synapse_sdk/utils/storage/providers/__init__.py +183 -0
  204. synapse_sdk/utils/storage/providers/file_system.py +134 -0
  205. synapse_sdk/utils/storage/providers/gcp.py +13 -0
  206. synapse_sdk/utils/storage/providers/http.py +190 -0
  207. synapse_sdk/utils/storage/providers/s3.py +91 -0
  208. synapse_sdk/utils/storage/providers/sftp.py +47 -0
  209. synapse_sdk/utils/storage/registry.py +17 -0
  210. synapse_sdk-2025.12.3.dist-info/METADATA +123 -0
  211. synapse_sdk-2025.12.3.dist-info/RECORD +279 -0
  212. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/WHEEL +1 -1
  213. synapse_sdk/clients/backend/dataset.py +0 -51
  214. synapse_sdk/plugins/categories/import/actions/import.py +0 -10
  215. synapse_sdk/plugins/cli/__init__.py +0 -21
  216. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
  217. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
  218. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
  219. synapse_sdk/utils/file.py +0 -168
  220. synapse_sdk/utils/storage.py +0 -91
  221. synapse_sdk-1.0.0a23.dist-info/METADATA +0 -44
  222. synapse_sdk-1.0.0a23.dist-info/RECORD +0 -114
  223. /synapse_sdk/{plugins/cli → cli/plugin}/run.py +0 -0
  224. /synapse_sdk/{plugins/categories/import → clients/validators}/__init__.py +0 -0
  225. /synapse_sdk/{plugins/categories/import/actions → devtools}/__init__.py +0 -0
  226. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/entry_points.txt +0 -0
  227. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info/licenses}/LICENSE +0 -0
  228. {synapse_sdk-1.0.0a23.dist-info → synapse_sdk-2025.12.3.dist-info}/top_level.txt +0 -0
@@ -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
@@ -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