synapse-sdk 1.0.0a59__py3-none-any.whl → 1.0.0a60__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse-sdk might be problematic. Click here for more details.

Files changed (85) hide show
  1. synapse_sdk/cli/__init__.py +246 -5
  2. synapse_sdk/cli/alias/utils.py +1 -1
  3. synapse_sdk/cli/config.py +339 -0
  4. synapse_sdk/cli/devtools.py +61 -0
  5. synapse_sdk/cli/plugin/publish.py +3 -4
  6. synapse_sdk/clients/agent/__init__.py +7 -2
  7. synapse_sdk/clients/agent/ray.py +37 -6
  8. synapse_sdk/clients/backend/__init__.py +5 -9
  9. synapse_sdk/clients/backend/annotation.py +4 -0
  10. synapse_sdk/clients/base.py +42 -3
  11. synapse_sdk/devtools/__init__.py +0 -0
  12. synapse_sdk/devtools/config.py +94 -0
  13. synapse_sdk/devtools/docs/.gitignore +20 -0
  14. synapse_sdk/devtools/docs/README.md +41 -0
  15. synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
  16. synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
  17. synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
  18. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  19. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +29 -0
  20. synapse_sdk/devtools/docs/blog/authors.yml +25 -0
  21. synapse_sdk/devtools/docs/blog/tags.yml +19 -0
  22. synapse_sdk/devtools/docs/docusaurus.config.ts +138 -0
  23. synapse_sdk/devtools/docs/package-lock.json +17455 -0
  24. synapse_sdk/devtools/docs/package.json +47 -0
  25. synapse_sdk/devtools/docs/sidebars.ts +36 -0
  26. synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
  27. synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  28. synapse_sdk/devtools/docs/src/css/custom.css +30 -0
  29. synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
  30. synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
  31. synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
  32. synapse_sdk/devtools/docs/static/.nojekyll +0 -0
  33. synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
  34. synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
  35. synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
  36. synapse_sdk/devtools/docs/static/img/logo.png +0 -0
  37. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  38. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
  39. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  40. synapse_sdk/devtools/docs/tsconfig.json +8 -0
  41. synapse_sdk/devtools/models.py +55 -0
  42. synapse_sdk/devtools/server.py +829 -0
  43. synapse_sdk/devtools/web/.gitignore +2 -0
  44. synapse_sdk/devtools/web/README.md +34 -0
  45. synapse_sdk/devtools/web/dist/index.html +17 -0
  46. synapse_sdk/devtools/web/index.html +16 -0
  47. synapse_sdk/devtools/web/jsconfig.json +15 -0
  48. synapse_sdk/devtools/web/package-lock.json +2609 -0
  49. synapse_sdk/devtools/web/package.json +27 -0
  50. synapse_sdk/devtools/web/pnpm-lock.yaml +1055 -0
  51. synapse_sdk/devtools/web/src/App.jsx +14 -0
  52. synapse_sdk/devtools/web/src/App.module.css +33 -0
  53. synapse_sdk/devtools/web/src/assets/favicon.ico +0 -0
  54. synapse_sdk/devtools/web/src/components/Breadcrumbs.jsx +42 -0
  55. synapse_sdk/devtools/web/src/components/Layout.jsx +12 -0
  56. synapse_sdk/devtools/web/src/components/LogViewer.jsx +266 -0
  57. synapse_sdk/devtools/web/src/components/MessageViewer.jsx +150 -0
  58. synapse_sdk/devtools/web/src/components/NavigationSidebar.jsx +137 -0
  59. synapse_sdk/devtools/web/src/components/ServerStatusBar.jsx +245 -0
  60. synapse_sdk/devtools/web/src/components/icons.jsx +325 -0
  61. synapse_sdk/devtools/web/src/index.css +470 -0
  62. synapse_sdk/devtools/web/src/index.jsx +15 -0
  63. synapse_sdk/devtools/web/src/logo.svg +1 -0
  64. synapse_sdk/devtools/web/src/router.jsx +34 -0
  65. synapse_sdk/devtools/web/src/utils/api.js +425 -0
  66. synapse_sdk/devtools/web/src/views/ApplicationDetailView.jsx +241 -0
  67. synapse_sdk/devtools/web/src/views/ApplicationsView.jsx +224 -0
  68. synapse_sdk/devtools/web/src/views/HomeView.jsx +197 -0
  69. synapse_sdk/devtools/web/src/views/JobDetailView.jsx +310 -0
  70. synapse_sdk/devtools/web/src/views/PluginView.jsx +914 -0
  71. synapse_sdk/devtools/web/vite.config.js +13 -0
  72. synapse_sdk/plugins/categories/neural_net/actions/tune.py +1 -1
  73. synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +236 -64
  74. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +14 -2
  75. synapse_sdk/plugins/templates/plugin-config-schema.json +409 -0
  76. synapse_sdk/plugins/templates/schema.json +484 -0
  77. synapse_sdk/utils/converters/__init__.py +145 -0
  78. synapse_sdk/utils/converters/coco/__init__.py +0 -0
  79. synapse_sdk/utils/converters/coco/from_dm.py +269 -0
  80. {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a60.dist-info}/METADATA +9 -22
  81. {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a60.dist-info}/RECORD +85 -17
  82. {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a60.dist-info}/WHEEL +0 -0
  83. {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a60.dist-info}/entry_points.txt +0 -0
  84. {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a60.dist-info}/licenses/LICENSE +0 -0
  85. {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a60.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "vite";
2
+ import tailwindcss from "@tailwindcss/vite";
3
+ import solidPlugin from "vite-plugin-solid";
4
+
5
+ export default defineConfig({
6
+ plugins: [tailwindcss(), solidPlugin()],
7
+ server: {
8
+ port: 3000,
9
+ },
10
+ build: {
11
+ target: "esnext",
12
+ },
13
+ });
@@ -260,10 +260,10 @@ class TuneAction(TrainAction):
260
260
 
261
261
  from ray.tune.schedulers import (
262
262
  ASHAScheduler,
263
+ FIFOScheduler,
263
264
  HyperBandScheduler,
264
265
  MedianStoppingRule,
265
266
  PopulationBasedTraining,
266
- FIFOScheduler,
267
267
  )
268
268
 
269
269
  if tune_config.get('scheduler') is None:
@@ -1,12 +1,13 @@
1
1
  import json
2
2
  from datetime import datetime
3
3
  from enum import Enum
4
- from typing import Annotated
4
+ from typing import Annotated, Any, Dict, List, Optional
5
5
 
6
6
  import requests
7
7
  from pydantic import AfterValidator, BaseModel, field_validator
8
8
  from pydantic_core import PydanticCustomError
9
9
 
10
+ from synapse_sdk.clients.backend import BackendClient
10
11
  from synapse_sdk.clients.exceptions import ClientError
11
12
  from synapse_sdk.plugins.categories.base import Action
12
13
  from synapse_sdk.plugins.categories.decorators import register_action
@@ -29,7 +30,7 @@ class ToTaskRun(Run):
29
30
  class AnnotateTaskDataLog(BaseModel):
30
31
  """Log model for annotate task data."""
31
32
 
32
- task_info: str | None
33
+ task_info: Optional[str] = None
33
34
  status: AnnotateTaskDataStatus
34
35
  created: str
35
36
 
@@ -40,7 +41,7 @@ class ToTaskRun(Run):
40
41
  failed: int
41
42
  success: int
42
43
 
43
- def log_annotate_task_data(self, task_info: dict, status: AnnotateTaskDataStatus):
44
+ def log_annotate_task_data(self, task_info: Dict[str, Any], status: AnnotateTaskDataStatus):
44
45
  """Log annotate task data."""
45
46
  now = datetime.now().isoformat()
46
47
  self.log(
@@ -56,7 +57,7 @@ class ToTaskRun(Run):
56
57
  category (str): The category of the metrics.
57
58
  """
58
59
  record = self.MetricsRecord.model_validate(record)
59
- self.set_metrics(value=record.dict(), category=category)
60
+ self.set_metrics(value=record.model_dump(), category=category)
60
61
 
61
62
 
62
63
  class ToTaskParams(BaseModel):
@@ -66,7 +67,7 @@ class ToTaskParams(BaseModel):
66
67
  name (str): The name of the action.
67
68
  description (str | None): The description of the action.
68
69
  project (int): The project ID.
69
- task_filter (dict): The filter of tasks.
70
+ task_filters (dict): The filters of tasks.
70
71
  method (AnnotationMethod): The method of annotation.
71
72
  target_specification_name (str | None): The name of the target specification.
72
73
  pre_processor (int | None): The pre processor ID.
@@ -74,17 +75,17 @@ class ToTaskParams(BaseModel):
74
75
  """
75
76
 
76
77
  name: Annotated[str, AfterValidator(non_blank)]
77
- description: str | None = None
78
+ description: Optional[str] = None
78
79
  project: int
79
- task_filter: dict
80
- method: AnnotationMethod | None = None
81
- target_specification_name: str | None = None
82
- pre_processor: int | None = None
83
- pre_processor_params: dict
80
+ task_filters: Dict[str, Any]
81
+ method: Optional[AnnotationMethod] = None
82
+ target_specification_name: Optional[str] = None
83
+ pre_processor: Optional[int] = None
84
+ pre_processor_params: Dict[str, Any]
84
85
 
85
86
  @field_validator('project', mode='before')
86
87
  @classmethod
87
- def check_project_exists(cls, value: str, info) -> str:
88
+ def check_project_exists(cls, value: int, info) -> int:
88
89
  """Validate synapse-backend project exists."""
89
90
  if not value:
90
91
  return value
@@ -100,15 +101,42 @@ class ToTaskParams(BaseModel):
100
101
 
101
102
  @register_action
102
103
  class ToTaskAction(Action):
103
- """ToTask action class.
104
-
105
- * Annotate data to tasks.
104
+ """ToTask action for pre-annotation data processing.
105
+
106
+ This action handles the process of annotating data to tasks in a project. It supports
107
+ two annotation methods: file-based annotation and inference-based annotation.
108
+
109
+ File-based annotation fetches data from file URLs specified in task data units,
110
+ downloads and processes JSON data, and updates task data with the processed information.
111
+ It also validates target specification names against file specifications.
112
+
113
+ Inference-based annotation is currently not supported but will support model inference
114
+ for automatic data annotation in future implementations.
115
+
116
+ Attrs:
117
+ name (str): Action name, set to 'to_task'.
118
+ category (PluginCategory): Plugin category, set to PRE_ANNOTATION.
119
+ method (RunMethod): Execution method, set to JOB.
120
+ run_class (Type[ToTaskRun]): Run class for this action.
121
+ params_model (Type[ToTaskParams]): Parameter validation model.
122
+ progress_categories (Dict): Progress tracking configuration.
123
+ metrics_categories (Set[str]): Metrics categories for this action.
124
+
125
+ Note:
126
+ This action requires a valid project with an associated data collection.
127
+ For file-based annotation, the target_specification_name must exist in the
128
+ project's file specifications.
129
+
130
+ Raises:
131
+ ValueError: If run instance or parameters are not properly initialized.
132
+ ClientError: If there are issues with backend API calls.
106
133
  """
107
134
 
108
135
  name = 'to_task'
109
136
  category = PluginCategory.PRE_ANNOTATION
110
137
  method = RunMethod.JOB
111
138
  run_class = ToTaskRun
139
+ params_model = ToTaskParams
112
140
  progress_categories = {
113
141
  'annotate_task_data': {
114
142
  'proportion': 100,
@@ -117,94 +145,238 @@ class ToTaskAction(Action):
117
145
  metrics_categories = {'annotate_task_data'}
118
146
 
119
147
  def start(self):
120
- """Start file_to_task action.
148
+ """Start to_task action.
121
149
 
122
150
  * Generate tasks.
123
151
  * Annotate data to tasks.
124
152
  """
153
+ if not self.run or not self.params:
154
+ raise ValueError('Run instance or parameters not properly initialized')
155
+
156
+ # Type assertion to help the linter
157
+ assert isinstance(self.run, ToTaskRun)
158
+ assert isinstance(self.run.client, BackendClient)
159
+
160
+ client: BackendClient = self.run.client
161
+ project_id = self.params['project']
162
+ project_response = client.get_project(project_id)
163
+ if isinstance(project_response, str):
164
+ self.run.log_message('Invalid project response received.')
165
+ self.run.end_log()
166
+ return {}
167
+ project: Dict[str, Any] = project_response
168
+
169
+ data_collection_id = project.get('data_collection')
170
+ if not data_collection_id:
171
+ self.run.log_message('Project does not have a data collection.')
172
+ self.run.end_log()
173
+ return {}
125
174
 
126
- # entrypoint = self.entrypoint(self.run)
127
- client = self.run.client
128
- # project_id = self.params['project']
129
- # project = client.get_project(project_id)
130
- # data_collection_id = project['dataset']
131
- # data_collection = client.get_dataset(data_collection_id)
175
+ data_collection_response = client.get_data_collection(data_collection_id)
176
+ if isinstance(data_collection_response, str):
177
+ self.run.log_message('Invalid data collection response received.')
178
+ self.run.end_log()
179
+ return {}
180
+ data_collection: Dict[str, Any] = data_collection_response
132
181
 
133
182
  # Generate tasks if provided project is empty.
134
183
  task_ids_query_params = {
135
184
  'project': self.params['project'],
136
185
  'fields': 'id',
137
186
  }
138
- if self.params.get('task_filter'):
139
- task_ids_query_params.update(self.params['task_filter'])
187
+ if self.params['task_filters']:
188
+ task_ids_query_params.update(self.params['task_filters'])
140
189
  task_ids_generator, task_ids_count = client.list_tasks(params=task_ids_query_params, list_all=True)
141
- task_ids = [item['id'] for item in task_ids_generator]
190
+ task_ids = [int(item.get('id', 0)) for item in task_ids_generator if isinstance(item, dict) and item.get('id')]
142
191
 
143
192
  # If no tasks found, break the job.
144
193
  if not task_ids_count:
145
194
  self.run.log_message('Tasks to annotate not found.')
146
195
  self.run.end_log()
196
+ return {}
147
197
 
148
198
  # Annotate data to tasks.
149
- task_data_annotation_type = self.params['task_data_annotation_type']
150
- if task_data_annotation_type == AnnotationMethod.FILE:
151
- self._handle_annotate_data_from_files(task_ids)
152
- elif task_data_annotation_type == AnnotationMethod.INFERENCE:
199
+ method = self.params.get('method')
200
+ if method == AnnotationMethod.FILE:
201
+ # Check if target specification name exists in file specifications.
202
+ target_specification_name = self.params.get('target_specification_name')
203
+ if not target_specification_name:
204
+ self.run.log_message('Target specification name is required for file annotation method.')
205
+ self.run.end_log()
206
+ return {}
207
+
208
+ file_specifications = data_collection.get('file_specifications', [])
209
+ target_spec_exists = any(spec.get('name') == target_specification_name for spec in file_specifications)
210
+ if not target_spec_exists:
211
+ self.run.log_message(
212
+ f'Target specification name "{target_specification_name}" not found in file specifications'
213
+ )
214
+ self.run.end_log()
215
+ return {}
216
+ self._handle_annotate_data_from_files(task_ids, target_specification_name)
217
+ elif method == AnnotationMethod.INFERENCE:
153
218
  self._handle_annotate_data_with_inference(task_ids)
219
+ else:
220
+ self.run.log_message(f'Unsupported annotation method: {method}')
221
+ self.run.end_log()
222
+ return {}
154
223
 
155
224
  return {}
156
225
 
157
- def _handle_annotate_data_from_files(self, task_ids: list[int]):
226
+ def _handle_annotate_data_from_files(self, task_ids: List[int], target_specification_name: str):
158
227
  """Handle annotate data from files to tasks.
159
228
 
160
229
  Args:
161
- task_ids (list[int]): List of task IDs to annotate data to.
230
+ task_ids (List[int]): List of task IDs to annotate data to.
231
+ target_specification_name (str): The name of the target specification.
162
232
  """
163
- client = self.run.client
164
- if not (target_task_data_specification_code := self.params.get('target_task_data_specification_code')):
165
- self.run.log_message('Target task data specification code not found.')
166
- self.run.end_log()
233
+ if not self.run or not self.params:
234
+ raise ValueError('Run instance or parameters not properly initialized')
235
+
236
+ # Type assertion to help the linter
237
+ assert isinstance(self.run, ToTaskRun)
238
+ assert isinstance(self.run.client, BackendClient)
239
+
240
+ client: BackendClient = self.run.client
167
241
  task_params = {
168
242
  'fields': 'id,data,data_unit',
169
243
  'expand': 'data_unit',
170
244
  }
171
- for task_id in task_ids:
172
- task = client.get_task(task_id, params=task_params)
173
- data_file = task['data_unit']['files'].get(target_task_data_specification_code)
174
- if not data_file:
175
- self.run.log_message(f'File specification not found for task {task_id}')
176
- self.run.log_annotate_task_data(
177
- {'task_id': task_id, 'error': 'File specification not found'}, AnnotateTaskDataStatus.FAILED
178
- )
179
- continue
180
- url = data_file.get('url')
181
- if not url:
182
- self.run.log_message(f'URL not found for task {task_id}')
183
- self.run.log_annotate_task_data(
184
- {'task_id': task_id, 'error': 'URL not found'}, AnnotateTaskDataStatus.FAILED
185
- )
186
- continue
187
245
 
246
+ total_tasks = len(task_ids)
247
+ success_count = 0
248
+ failed_count = 0
249
+ current_progress = 0
250
+
251
+ # Initialize metrics and progress
252
+ self._update_metrics(total_tasks, success_count, failed_count)
253
+ self.run.set_progress(0, total_tasks, category='annotate_task_data')
254
+ self.run.log_message('Annotating data to tasks...')
255
+
256
+ # Process each task
257
+ for task_id in task_ids:
188
258
  try:
189
- response = requests.get(url)
190
- response.raise_for_status() # Raise an exception for 4XX/5XX responses
191
- data = json.loads(response.content)
192
- client.patch_task(task_id, data={'data': data})
259
+ result = self._process_single_task(client, task_id, task_params, target_specification_name)
260
+ if result['success']:
261
+ success_count += 1
262
+ else:
263
+ failed_count += 1
264
+
265
+ current_progress += 1
266
+ self._update_metrics(total_tasks, success_count, failed_count)
267
+ self.run.set_progress(current_progress, total_tasks, category='annotate_task_data')
193
268
 
194
- # Log success
195
- self.run.log_annotate_task_data({'task_id': task_id, 'url': url}, AnnotateTaskDataStatus.SUCCESS)
196
269
  except Exception as e:
197
- self.run.log_message(f'Failed to get content from URL for task {task_id}: {str(e)}')
198
- self.run.log_annotate_task_data(
199
- {'task_id': task_id, 'url': url, 'error': str(e)}, AnnotateTaskDataStatus.FAILED
200
- )
201
- continue
270
+ self.run.log_message(f'Failed to process task {task_id}: {str(e)}')
271
+ self.run.log_annotate_task_data({'task_id': task_id, 'error': str(e)}, AnnotateTaskDataStatus.FAILED)
272
+ failed_count += 1
273
+ current_progress += 1
274
+ self._update_metrics(total_tasks, success_count, failed_count)
275
+ self.run.set_progress(current_progress, total_tasks, category='annotate_task_data')
276
+
277
+ # Finalize progress
278
+ self.run.set_progress(total_tasks, total_tasks, category='annotate_task_data')
279
+ self.run.log_message(f'Annotation completed. Success: {success_count}, Failed: {failed_count}')
280
+
281
+ def _process_single_task(
282
+ self, client: BackendClient, task_id: int, task_params: Dict[str, Any], target_specification_name: str
283
+ ) -> Dict[str, Any]:
284
+ """Process a single task for annotation.
285
+
286
+ Args:
287
+ client (BackendClient): The backend client instance.
288
+ task_id (int): The task ID to process.
289
+ task_params (Dict[str, Any]): Parameters for getting task data.
290
+ target_specification_name (str): The name of the target specification.
291
+
292
+ Returns:
293
+ Dict[str, Any]: Result dictionary with 'success' boolean and optional 'error' message.
294
+ """
295
+ if not self.run:
296
+ raise ValueError('Run instance not properly initialized')
297
+
298
+ # Type assertion to help the linter
299
+ assert isinstance(self.run, ToTaskRun)
300
+
301
+ # Get task data
302
+ task_response = client.get_task(task_id, params=task_params)
303
+ if isinstance(task_response, str):
304
+ error_msg = 'Invalid task response'
305
+ self.run.log_message(f'{error_msg} received for task {task_id}')
306
+ self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
307
+ return {'success': False, 'error': error_msg}
308
+
309
+ task: Dict[str, Any] = task_response
310
+
311
+ # Extract data file information
312
+ data_unit = task.get('data_unit', {})
313
+ files = data_unit.get('files', {})
314
+ data_file = files.get(target_specification_name)
315
+
316
+ if not data_file:
317
+ error_msg = 'File specification not found'
318
+ self.run.log_message(f'{error_msg} for task {task_id}')
319
+ self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
320
+ return {'success': False, 'error': error_msg}
321
+
322
+ url = data_file.get('url')
323
+ if not url:
324
+ error_msg = 'URL not found'
325
+ self.run.log_message(f'{error_msg} for task {task_id}')
326
+ self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
327
+ return {'success': False, 'error': error_msg}
328
+
329
+ # Fetch and process the data
330
+ try:
331
+ response = requests.get(url)
332
+ response.raise_for_status()
333
+ data = json.loads(response.content)
334
+
335
+ # Submit annotation data
336
+ client.annotate_task_data(task_id, data={'action': 'submit', 'data': data})
337
+
338
+ # Log success
339
+ self.run.log_annotate_task_data({'task_id': task_id, 'url': url}, AnnotateTaskDataStatus.SUCCESS)
340
+ return {'success': True}
341
+
342
+ except requests.RequestException as e:
343
+ error_msg = f'Failed to fetch data from URL: {str(e)}'
344
+ self.run.log_message(f'{error_msg} for task {task_id}')
345
+ self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
346
+ return {'success': False, 'error': error_msg}
347
+ except json.JSONDecodeError as e:
348
+ error_msg = f'Failed to parse JSON data: {str(e)}'
349
+ self.run.log_message(f'{error_msg} for task {task_id}')
350
+ self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
351
+ return {'success': False, 'error': error_msg}
352
+
353
+ def _update_metrics(self, total_tasks: int, success_count: int, failed_count: int):
354
+ """Update metrics for task annotation progress.
202
355
 
203
- def _handle_annotate_data_with_inference(self, task_ids: list[int]):
356
+ Args:
357
+ total_tasks (int): Total number of tasks to process.
358
+ success_count (int): Number of successfully processed tasks.
359
+ failed_count (int): Number of failed tasks.
360
+ """
361
+ if not self.run:
362
+ raise ValueError('Run instance not properly initialized')
363
+
364
+ # Type assertion to help the linter
365
+ assert isinstance(self.run, ToTaskRun)
366
+
367
+ metrics = self.run.MetricsRecord(
368
+ stand_by=total_tasks - success_count - failed_count, failed=failed_count, success=success_count
369
+ )
370
+ self.run.log_metrics(metrics, 'annotate_task_data')
371
+
372
+ def _handle_annotate_data_with_inference(self, task_ids: List[int]):
204
373
  """Handle annotate data with inference to tasks.
205
374
 
206
375
  Args:
207
- task_ids (list[int]): List of task IDs to annotate data to.
376
+ task_ids (List[int]): List of task IDs to annotate data to.
208
377
  """
378
+ if not self.run:
379
+ raise ValueError('Run instance not properly initialized')
380
+
209
381
  self.run.log_message('Pre annotation with inference is not supported.')
210
382
  self.run.end_log()
@@ -3,5 +3,17 @@ actions:
3
3
  entrypoint: plugin.pre_annotation.pre_annotate
4
4
  to_task:
5
5
  entrypoint: plugin.to_task.AnnotationToTask
6
- ui_schema: |
7
- Dumped FormKit Schema for pre_annotation plugin to_task action's custom options
6
+ ui_schema:
7
+ - $formkit: "radio"
8
+ name: "schema_to_convert"
9
+ label: "변환 스키마"
10
+ help: "변환 스키마 선택"
11
+ required: True
12
+ value: "<yolo>"
13
+ option:
14
+ - label: "Yolo"
15
+ value: "yolo"
16
+ - label: "Coco"
17
+ value: "coco"
18
+ - label: "Pascal"
19
+ value: "pascal"