synapse-sdk 2025.9.5__py3-none-any.whl → 2025.10.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/clients/base.py +129 -9
- synapse_sdk/devtools/docs/docs/api/clients/base.md +230 -8
- synapse_sdk/devtools/docs/docs/api/plugins/models.md +58 -3
- synapse_sdk/devtools/docs/docs/plugins/categories/neural-net-plugins/train-action-overview.md +663 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-action.md +934 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-overview.md +585 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-template.md +715 -0
- synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +39 -0
- synapse_sdk/devtools/docs/docs/plugins/plugins.md +12 -5
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/base.md +230 -8
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/neural-net-plugins/train-action-overview.md +621 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-action.md +934 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-overview.md +585 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-template.md +715 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +39 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current.json +16 -4
- synapse_sdk/devtools/docs/sidebars.ts +45 -1
- synapse_sdk/plugins/README.md +487 -80
- synapse_sdk/plugins/categories/base.py +1 -0
- synapse_sdk/plugins/categories/export/actions/export/action.py +8 -3
- synapse_sdk/plugins/categories/export/actions/export/utils.py +108 -8
- synapse_sdk/plugins/categories/export/templates/config.yaml +18 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +97 -0
- synapse_sdk/plugins/categories/neural_net/actions/train.py +592 -22
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +150 -3
- 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 +145 -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 +97 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +250 -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 +284 -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 +87 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +127 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +2 -1
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +8 -1
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +0 -1
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +134 -94
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +2 -2
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +6 -2
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +24 -9
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +130 -18
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +147 -37
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +10 -5
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +31 -6
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +65 -37
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +17 -2
- synapse_sdk/plugins/categories/upload/templates/README.md +394 -0
- synapse_sdk/plugins/models.py +62 -0
- synapse_sdk/utils/file/download.py +261 -0
- {synapse_sdk-2025.9.5.dist-info → synapse_sdk-2025.10.6.dist-info}/METADATA +15 -2
- {synapse_sdk-2025.9.5.dist-info → synapse_sdk-2025.10.6.dist-info}/RECORD +74 -43
- synapse_sdk/devtools/docs/docs/plugins/developing-upload-template.md +0 -1463
- synapse_sdk/devtools/docs/docs/plugins/upload-plugins.md +0 -1964
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/developing-upload-template.md +0 -1463
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/upload-plugins.md +0 -2077
- {synapse_sdk-2025.9.5.dist-info → synapse_sdk-2025.10.6.dist-info}/WHEEL +0 -0
- {synapse_sdk-2025.9.5.dist-info → synapse_sdk-2025.10.6.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-2025.9.5.dist-info → synapse_sdk-2025.10.6.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-2025.9.5.dist-info → synapse_sdk-2025.10.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""Annotation strategies for ToTask action."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
from ..enums import AnnotateTaskDataStatus, LogCode
|
|
6
|
+
from .base import AnnotationStrategy, ToTaskContext
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FileAnnotationStrategy(AnnotationStrategy):
|
|
10
|
+
"""Strategy for file-based annotation processing."""
|
|
11
|
+
|
|
12
|
+
def process_task(
|
|
13
|
+
self, context: ToTaskContext, task_id: int, task_data: Dict[str, Any], target_specification_name: str, **kwargs
|
|
14
|
+
) -> Dict[str, Any]:
|
|
15
|
+
"""Process a single task for file-based annotation.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
context: Shared context for the action execution
|
|
19
|
+
task_id: The task ID to process
|
|
20
|
+
task_data: The task data dictionary
|
|
21
|
+
target_specification_name: The name of the target specification
|
|
22
|
+
**kwargs: Additional parameters
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Dict with 'success' boolean and optional 'error' message
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
client = context.client
|
|
29
|
+
logger = context.logger
|
|
30
|
+
|
|
31
|
+
# Get data unit
|
|
32
|
+
data_unit = task_data.get('data_unit')
|
|
33
|
+
if not data_unit:
|
|
34
|
+
error_msg = 'Task does not have a data unit'
|
|
35
|
+
logger.log_annotate_task_event(LogCode.NO_DATA_UNIT, task_id)
|
|
36
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
37
|
+
return {'success': False, 'error': error_msg}
|
|
38
|
+
|
|
39
|
+
# Get data unit files
|
|
40
|
+
data_unit_files = data_unit.get('files', {})
|
|
41
|
+
if not data_unit_files:
|
|
42
|
+
error_msg = 'Data unit does not have files'
|
|
43
|
+
logger.log_annotate_task_event(LogCode.NO_DATA_UNIT_FILES, task_id)
|
|
44
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
45
|
+
return {'success': False, 'error': error_msg}
|
|
46
|
+
|
|
47
|
+
# Extract primary file URL from task data
|
|
48
|
+
primary_file_url, primary_file_original_name = self._extract_primary_file_url(task_data)
|
|
49
|
+
if not primary_file_url:
|
|
50
|
+
error_msg = 'Primary image URL not found in task data'
|
|
51
|
+
logger.log_annotate_task_event(LogCode.PRIMARY_IMAGE_URL_NOT_FOUND, task_id)
|
|
52
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
53
|
+
return {'success': False, 'error': error_msg}
|
|
54
|
+
|
|
55
|
+
# Get target specification file
|
|
56
|
+
target_file = data_unit_files.get(target_specification_name)
|
|
57
|
+
if not target_file:
|
|
58
|
+
error_msg = 'File specification not found'
|
|
59
|
+
logger.log_annotate_task_event(LogCode.FILE_SPEC_NOT_FOUND, task_id)
|
|
60
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
61
|
+
return {'success': False, 'error': error_msg}
|
|
62
|
+
|
|
63
|
+
# Get target file details
|
|
64
|
+
target_file_url = target_file.get('url')
|
|
65
|
+
target_file_original_name = target_file.get('file_name_original')
|
|
66
|
+
|
|
67
|
+
if not target_file_original_name:
|
|
68
|
+
error_msg = 'File original name not found'
|
|
69
|
+
logger.log_annotate_task_event(LogCode.FILE_ORIGINAL_NAME_NOT_FOUND, task_id)
|
|
70
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
71
|
+
return {'success': False, 'error': error_msg}
|
|
72
|
+
|
|
73
|
+
if not target_file_url:
|
|
74
|
+
error_msg = 'URL not found'
|
|
75
|
+
logger.log_annotate_task_event(LogCode.URL_NOT_FOUND, task_id)
|
|
76
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
77
|
+
return {'success': False, 'error': error_msg}
|
|
78
|
+
|
|
79
|
+
# Fetch and process the data using template
|
|
80
|
+
try:
|
|
81
|
+
# Convert data to task object using action's entrypoint
|
|
82
|
+
annotation_to_task = context.entrypoint(logger)
|
|
83
|
+
converted_data = annotation_to_task.convert_data_from_file(
|
|
84
|
+
primary_file_url, primary_file_original_name, target_file_url, target_file_original_name
|
|
85
|
+
)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
if 'requests' in str(type(e)):
|
|
88
|
+
error_msg = f'Failed to fetch data from URL: {str(e)}'
|
|
89
|
+
logger.log_annotate_task_event(LogCode.FETCH_DATA_FAILED, target_file_url, task_id)
|
|
90
|
+
else:
|
|
91
|
+
error_msg = f'Failed to convert data to task object: {str(e)}'
|
|
92
|
+
logger.log_annotate_task_event(LogCode.CONVERT_DATA_FAILED, str(e), task_id)
|
|
93
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
94
|
+
return {'success': False, 'error': error_msg}
|
|
95
|
+
|
|
96
|
+
# Submit annotation data
|
|
97
|
+
try:
|
|
98
|
+
client.annotate_task_data(task_id, data={'action': 'submit', 'data': converted_data})
|
|
99
|
+
logger.log_annotate_task_data(
|
|
100
|
+
{'task_id': task_id, 'target_spec': target_specification_name}, AnnotateTaskDataStatus.SUCCESS
|
|
101
|
+
)
|
|
102
|
+
return {'success': True}
|
|
103
|
+
except Exception as e:
|
|
104
|
+
error_msg = f'Failed to submit annotation data: {str(e)}'
|
|
105
|
+
logger.log_annotate_task_event(LogCode.ANNOTATION_SUBMISSION_FAILED, task_id, str(e))
|
|
106
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
107
|
+
return {'success': False, 'error': error_msg}
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
error_msg = f'Failed to process file annotation for task {task_id}: {str(e)}'
|
|
111
|
+
context.logger.log_annotate_task_event(LogCode.TASK_PROCESSING_FAILED, task_id, str(e))
|
|
112
|
+
context.logger.log_annotate_task_data(
|
|
113
|
+
{'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED
|
|
114
|
+
)
|
|
115
|
+
return {'success': False, 'error': error_msg}
|
|
116
|
+
|
|
117
|
+
def _extract_primary_file_url(self, task_data: Dict[str, Any]) -> tuple:
|
|
118
|
+
"""Extract the primary file URL from task data.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
task_data: The task data dictionary
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Tuple of (primary_file_url, primary_file_original_name)
|
|
125
|
+
"""
|
|
126
|
+
data_unit = task_data.get('data_unit', {})
|
|
127
|
+
files = data_unit.get('files', {})
|
|
128
|
+
|
|
129
|
+
for file_info in files.values():
|
|
130
|
+
if isinstance(file_info, dict) and file_info.get('is_primary') and file_info.get('url'):
|
|
131
|
+
return file_info['url'], file_info.get('file_name_original')
|
|
132
|
+
|
|
133
|
+
return None, None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class InferenceAnnotationStrategy(AnnotationStrategy):
|
|
137
|
+
"""Strategy for inference-based annotation processing."""
|
|
138
|
+
|
|
139
|
+
def process_task(self, context: ToTaskContext, task_id: int, task_data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
|
|
140
|
+
"""Process a single task for inference-based annotation.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
context: Shared context for the action execution
|
|
144
|
+
task_id: The task ID to process
|
|
145
|
+
task_data: The task data dictionary
|
|
146
|
+
**kwargs: Additional parameters
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Dict with 'success' boolean and optional 'error' message
|
|
150
|
+
"""
|
|
151
|
+
try:
|
|
152
|
+
client = context.client
|
|
153
|
+
logger = context.logger
|
|
154
|
+
|
|
155
|
+
# Get pre-processor ID from parameters
|
|
156
|
+
pre_processor_id = context.params.get('pre_processor')
|
|
157
|
+
if not pre_processor_id:
|
|
158
|
+
error_msg = 'Pre-processor ID is required for inference annotation method'
|
|
159
|
+
logger.log_annotate_task_event(LogCode.NO_PREPROCESSOR_ID, task_id)
|
|
160
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
161
|
+
return {'success': False, 'error': error_msg}
|
|
162
|
+
|
|
163
|
+
# Get pre-processor info using factory-created strategy
|
|
164
|
+
from ..factory import ToTaskStrategyFactory
|
|
165
|
+
|
|
166
|
+
factory = ToTaskStrategyFactory()
|
|
167
|
+
preprocessor_strategy = factory.create_preprocessor_strategy()
|
|
168
|
+
|
|
169
|
+
pre_processor_info = preprocessor_strategy.get_preprocessor_info(context, pre_processor_id)
|
|
170
|
+
if not pre_processor_info['success']:
|
|
171
|
+
error_msg = pre_processor_info.get('error', 'Failed to get pre-processor info')
|
|
172
|
+
logger.log_annotate_task_event(LogCode.INFERENCE_PREPROCESSOR_FAILED, task_id, error_msg)
|
|
173
|
+
return pre_processor_info
|
|
174
|
+
|
|
175
|
+
pre_processor_code = pre_processor_info['code']
|
|
176
|
+
pre_processor_version = pre_processor_info['version']
|
|
177
|
+
|
|
178
|
+
# Ensure pre-processor is running
|
|
179
|
+
pre_processor_status = preprocessor_strategy.ensure_preprocessor_running(context, pre_processor_code)
|
|
180
|
+
if not pre_processor_status['success']:
|
|
181
|
+
error_msg = pre_processor_status.get('error', 'Failed to ensure pre-processor running')
|
|
182
|
+
logger.log_annotate_task_event(LogCode.INFERENCE_PREPROCESSOR_FAILED, task_id, error_msg)
|
|
183
|
+
return pre_processor_status
|
|
184
|
+
|
|
185
|
+
# Extract primary file URL using factory-created strategy
|
|
186
|
+
extraction_strategy = factory.create_extraction_strategy(context.annotation_method)
|
|
187
|
+
primary_file_url, _ = extraction_strategy.extract_data(context, task_data)
|
|
188
|
+
if not primary_file_url:
|
|
189
|
+
error_msg = 'Primary image URL not found in task data'
|
|
190
|
+
logger.log_annotate_task_event(LogCode.PRIMARY_IMAGE_URL_NOT_FOUND, task_id)
|
|
191
|
+
logger.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
192
|
+
return {'success': False, 'error': error_msg}
|
|
193
|
+
|
|
194
|
+
# Run inference
|
|
195
|
+
inference_result = self._run_inference(
|
|
196
|
+
client,
|
|
197
|
+
pre_processor_code,
|
|
198
|
+
pre_processor_version,
|
|
199
|
+
primary_file_url,
|
|
200
|
+
context.params['agent'],
|
|
201
|
+
context.params['model'],
|
|
202
|
+
pre_processor_params=context.params.get('pre_processor_params', {}),
|
|
203
|
+
)
|
|
204
|
+
if not inference_result['success']:
|
|
205
|
+
error_msg = inference_result.get('error', 'Failed to run inference')
|
|
206
|
+
logger.log_annotate_task_event(LogCode.INFERENCE_PREPROCESSOR_FAILED, task_id, error_msg)
|
|
207
|
+
return inference_result
|
|
208
|
+
|
|
209
|
+
# Convert and submit inference data
|
|
210
|
+
try:
|
|
211
|
+
# This would need to be injected or configured based on the action's entrypoint
|
|
212
|
+
# For now, we'll assume the conversion is done externally
|
|
213
|
+
converted_result = inference_result['data'] # Simplified for refactoring
|
|
214
|
+
|
|
215
|
+
client.annotate_task_data(task_id, data={'action': 'submit', 'data': converted_result})
|
|
216
|
+
logger.log_annotate_task_data(
|
|
217
|
+
{'task_id': task_id, 'pre_processor_id': pre_processor_id}, AnnotateTaskDataStatus.SUCCESS
|
|
218
|
+
)
|
|
219
|
+
return {'success': True, 'pre_processor_id': pre_processor_id}
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
error_msg = f'Failed to convert/submit inference data: {str(e)}'
|
|
223
|
+
logger.log_annotate_task_event(LogCode.INFERENCE_PREPROCESSOR_FAILED, task_id, error_msg)
|
|
224
|
+
return {'success': False, 'error': error_msg}
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
error_msg = f'Failed to process inference for task {task_id}: {str(e)}'
|
|
228
|
+
context.logger.log_message_with_code(LogCode.INFERENCE_PROCESSING_FAILED, task_id, str(e))
|
|
229
|
+
context.logger.log_annotate_task_data(
|
|
230
|
+
{'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED
|
|
231
|
+
)
|
|
232
|
+
return {'success': False, 'error': error_msg}
|
|
233
|
+
|
|
234
|
+
def _run_inference(
|
|
235
|
+
self,
|
|
236
|
+
client: Any,
|
|
237
|
+
pre_processor_code: str,
|
|
238
|
+
pre_processor_version: str,
|
|
239
|
+
primary_file_url: str,
|
|
240
|
+
agent: int,
|
|
241
|
+
model: int,
|
|
242
|
+
pre_processor_params: Dict[str, Any] = {},
|
|
243
|
+
) -> Dict[str, Any]:
|
|
244
|
+
"""Run inference using the pre-processor.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
client: Backend client instance
|
|
248
|
+
pre_processor_code: Pre-processor code
|
|
249
|
+
pre_processor_version: Pre-processor version
|
|
250
|
+
primary_file_url: URL of the primary file to process
|
|
251
|
+
agent: Agent id for inference
|
|
252
|
+
model: Model id for inference
|
|
253
|
+
pre_processor_params: Additional parameters for the pre-processor
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Dict with inference results or error
|
|
257
|
+
"""
|
|
258
|
+
try:
|
|
259
|
+
if not agent or not model:
|
|
260
|
+
return {'success': False, 'error': 'Parameters not available'}
|
|
261
|
+
|
|
262
|
+
pre_processor_params['image_path'] = primary_file_url
|
|
263
|
+
|
|
264
|
+
inference_payload = {
|
|
265
|
+
'agent': agent,
|
|
266
|
+
'action': 'inference',
|
|
267
|
+
'version': pre_processor_version,
|
|
268
|
+
'params': {
|
|
269
|
+
'model': model,
|
|
270
|
+
'method': 'post',
|
|
271
|
+
'json': pre_processor_params,
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
inference_data = client.run_plugin(pre_processor_code, inference_payload)
|
|
276
|
+
|
|
277
|
+
# Every inference api should return None if failed to inference.
|
|
278
|
+
if inference_data is None:
|
|
279
|
+
return {'success': False, 'error': 'Inference data is None'}
|
|
280
|
+
|
|
281
|
+
return {'success': True, 'data': inference_data}
|
|
282
|
+
|
|
283
|
+
except Exception as e:
|
|
284
|
+
return {'success': False, 'error': f'Failed to run inference: {str(e)}'}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Abstract base classes for ToTask strategies."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
7
|
+
|
|
8
|
+
from ..enums import AnnotationMethod
|
|
9
|
+
from ..models import MetricsRecord
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ToTaskContext:
|
|
13
|
+
"""Shared context for ToTask action execution."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
params: Dict[str, Any],
|
|
18
|
+
client: BackendClient,
|
|
19
|
+
logger: Any,
|
|
20
|
+
entrypoint: Any = None,
|
|
21
|
+
config: Optional[Dict[str, Any]] = None,
|
|
22
|
+
plugin_config: Optional[Dict[str, Any]] = None,
|
|
23
|
+
job_id: Optional[str] = None,
|
|
24
|
+
progress_categories: Optional[Dict[str, Any]] = None,
|
|
25
|
+
metrics_categories: Optional[Dict[str, Any]] = None,
|
|
26
|
+
project: Optional[Dict[str, Any]] = None,
|
|
27
|
+
data_collection: Optional[Dict[str, Any]] = None,
|
|
28
|
+
task_ids: Optional[List[int]] = None,
|
|
29
|
+
metrics: Optional[MetricsRecord] = None,
|
|
30
|
+
annotation_method: Optional[AnnotationMethod] = None,
|
|
31
|
+
):
|
|
32
|
+
self.params = params
|
|
33
|
+
self.client = client
|
|
34
|
+
self.logger = logger
|
|
35
|
+
self.entrypoint = entrypoint
|
|
36
|
+
self.config = config
|
|
37
|
+
self.plugin_config = plugin_config
|
|
38
|
+
self.job_id = job_id
|
|
39
|
+
self.progress_categories = progress_categories
|
|
40
|
+
self.metrics_categories = metrics_categories
|
|
41
|
+
self.project = project
|
|
42
|
+
self.data_collection = data_collection
|
|
43
|
+
self.task_ids = task_ids or []
|
|
44
|
+
self.metrics = metrics or MetricsRecord(stand_by=0, failed=0, success=0)
|
|
45
|
+
self.annotation_method = annotation_method
|
|
46
|
+
self.temp_files: List[str] = []
|
|
47
|
+
self.rollback_actions: List[callable] = []
|
|
48
|
+
|
|
49
|
+
def add_temp_file(self, file_path: str):
|
|
50
|
+
"""Track temporary files for cleanup."""
|
|
51
|
+
self.temp_files.append(file_path)
|
|
52
|
+
|
|
53
|
+
def add_rollback_action(self, action: callable):
|
|
54
|
+
"""Add rollback action for error recovery."""
|
|
55
|
+
self.rollback_actions.append(action)
|
|
56
|
+
|
|
57
|
+
def update_metrics(self, success_count: int, failed_count: int, total_count: int):
|
|
58
|
+
"""Update execution metrics."""
|
|
59
|
+
self.metrics = MetricsRecord(
|
|
60
|
+
stand_by=total_count - success_count - failed_count, failed=failed_count, success=success_count
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ValidationStrategy(ABC):
|
|
65
|
+
"""Abstract base class for validation strategies."""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def validate(self, context: ToTaskContext) -> Dict[str, Any]:
|
|
69
|
+
"""Validate specific aspects of the ToTask execution.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
context: Shared context for the action execution
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Dict with 'success' boolean and optional 'error' message
|
|
76
|
+
"""
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class AnnotationStrategy(ABC):
|
|
81
|
+
"""Abstract base class for annotation strategies."""
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def process_task(self, context: ToTaskContext, task_id: int, task_data: Dict[str, Any], **kwargs) -> Dict[str, Any]:
|
|
85
|
+
"""Process a single task for annotation.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
context: Shared context for the action execution
|
|
89
|
+
task_id: The task ID to process
|
|
90
|
+
task_data: The task data dictionary
|
|
91
|
+
**kwargs: Additional method-specific parameters
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Dict with 'success' boolean and optional 'error' message
|
|
95
|
+
"""
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class PreProcessorStrategy(ABC):
|
|
100
|
+
"""Abstract base class for pre-processor management strategies."""
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def get_preprocessor_info(self, context: ToTaskContext, preprocessor_id: int) -> Dict[str, Any]:
|
|
104
|
+
"""Get pre-processor information.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
context: Shared context for the action execution
|
|
108
|
+
preprocessor_id: The pre-processor ID
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Dict with pre-processor info or error
|
|
112
|
+
"""
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
@abstractmethod
|
|
116
|
+
def ensure_preprocessor_running(self, context: ToTaskContext, preprocessor_code: str) -> Dict[str, Any]:
|
|
117
|
+
"""Ensure pre-processor is running.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
context: Shared context for the action execution
|
|
121
|
+
preprocessor_code: The pre-processor code
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Dict indicating success or failure
|
|
125
|
+
"""
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class DataExtractionStrategy(ABC):
|
|
130
|
+
"""Abstract base class for data extraction strategies."""
|
|
131
|
+
|
|
132
|
+
@abstractmethod
|
|
133
|
+
def extract_data(self, context: ToTaskContext, task_data: Dict[str, Any]) -> Tuple[Optional[str], Optional[str]]:
|
|
134
|
+
"""Extract required data from task.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
context: Shared context for the action execution
|
|
138
|
+
task_data: The task data dictionary
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Tuple of extracted data values
|
|
142
|
+
"""
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class MetricsStrategy(ABC):
|
|
147
|
+
"""Abstract base class for metrics strategies."""
|
|
148
|
+
|
|
149
|
+
@abstractmethod
|
|
150
|
+
def update_progress(self, context: ToTaskContext, current: int, total: int):
|
|
151
|
+
"""Update progress tracking.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
context: Shared context for the action execution
|
|
155
|
+
current: Current progress count
|
|
156
|
+
total: Total items to process
|
|
157
|
+
"""
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
@abstractmethod
|
|
161
|
+
def record_task_result(self, context: ToTaskContext, task_id: int, success: bool, error: Optional[str] = None):
|
|
162
|
+
"""Record the result of processing a single task.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
context: Shared context for the action execution
|
|
166
|
+
task_id: The task ID that was processed
|
|
167
|
+
success: Whether the task processing was successful
|
|
168
|
+
error: Error message if unsuccessful
|
|
169
|
+
"""
|
|
170
|
+
pass
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Data extraction strategies for ToTask action."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from .base import DataExtractionStrategy, ToTaskContext
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FileUrlExtractionStrategy(DataExtractionStrategy):
|
|
9
|
+
"""Strategy for extracting file URLs from task data."""
|
|
10
|
+
|
|
11
|
+
def extract_data(self, context: ToTaskContext, task_data: Dict[str, Any]) -> str | None:
|
|
12
|
+
"""Extract primary file URL from task data.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
context: Shared context for the action execution
|
|
16
|
+
task_data: The task data dictionary
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
str: The primary file URL or None if not found
|
|
20
|
+
"""
|
|
21
|
+
try:
|
|
22
|
+
# This implementation follows the original _extract_primary_file_url logic
|
|
23
|
+
data_unit = task_data.get('data_unit')
|
|
24
|
+
if not data_unit:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
data_unit_files = data_unit.get('files', {})
|
|
28
|
+
if not data_unit_files:
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
# Find primary file URL
|
|
32
|
+
for key in data_unit_files:
|
|
33
|
+
if data_unit_files[key]['is_primary']:
|
|
34
|
+
return data_unit_files[key]['url']
|
|
35
|
+
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
except Exception as e:
|
|
39
|
+
context.logger.log_message_with_code(
|
|
40
|
+
context.logger.LogCode.DATA_EXTRACTION_FAILED
|
|
41
|
+
if hasattr(context.logger, 'LogCode')
|
|
42
|
+
else 'DATA_EXTRACTION_FAILED',
|
|
43
|
+
str(e),
|
|
44
|
+
)
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class InferenceDataExtractionStrategy(DataExtractionStrategy):
|
|
49
|
+
"""Strategy for extracting inference data from task data."""
|
|
50
|
+
|
|
51
|
+
def extract_data(self, context: ToTaskContext, task_data: Dict[str, Any]) -> Tuple[Optional[str], Optional[str]]:
|
|
52
|
+
"""Extract data needed for inference processing.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
context: Shared context for the action execution
|
|
56
|
+
task_data: The task data dictionary
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Tuple of (primary_file_url, inference_metadata)
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
# Reuse the file URL extraction logic
|
|
63
|
+
file_strategy = FileUrlExtractionStrategy()
|
|
64
|
+
primary_url = file_strategy.extract_data(context, task_data)
|
|
65
|
+
|
|
66
|
+
# Extract additional inference-specific metadata
|
|
67
|
+
data_unit = task_data.get('data_unit', {})
|
|
68
|
+
inference_metadata = {
|
|
69
|
+
'data_unit_id': data_unit.get('id'),
|
|
70
|
+
'task_id': task_data.get('id'),
|
|
71
|
+
'additional_params': context.params.get('inference_params', {}),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return primary_url, str(inference_metadata) if inference_metadata else None
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
context.logger.log_message_with_code(
|
|
78
|
+
context.logger.LogCode.DATA_EXTRACTION_FAILED
|
|
79
|
+
if hasattr(context.logger, 'LogCode')
|
|
80
|
+
else 'DATA_EXTRACTION_FAILED',
|
|
81
|
+
str(e),
|
|
82
|
+
)
|
|
83
|
+
return None, None
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Metrics and progress tracking strategies for ToTask action."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from ..enums import AnnotateTaskDataStatus, LogCode
|
|
6
|
+
from .base import MetricsStrategy, ToTaskContext
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProgressTrackingStrategy(MetricsStrategy):
|
|
10
|
+
"""Strategy for tracking progress and metrics during task processing."""
|
|
11
|
+
|
|
12
|
+
def update_progress(self, context: ToTaskContext, current: int, total: int):
|
|
13
|
+
"""Update progress tracking.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
context: Shared context for the action execution
|
|
17
|
+
current: Current progress count
|
|
18
|
+
total: Total items to process
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
context.logger.set_progress(current, total, category='annotate_task_data')
|
|
22
|
+
except Exception as e:
|
|
23
|
+
context.logger.log_message_with_code(LogCode.PROGRESS_UPDATE_FAILED, str(e))
|
|
24
|
+
|
|
25
|
+
def record_task_result(self, context: ToTaskContext, task_id: int, success: bool, error: Optional[str] = None):
|
|
26
|
+
"""Record the result of processing a single task.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
context: Shared context for the action execution
|
|
30
|
+
task_id: The task ID that was processed
|
|
31
|
+
success: Whether the task processing was successful
|
|
32
|
+
error: Error message if unsuccessful
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
if success:
|
|
36
|
+
status = AnnotateTaskDataStatus.SUCCESS
|
|
37
|
+
log_data = {'task_id': task_id}
|
|
38
|
+
else:
|
|
39
|
+
status = AnnotateTaskDataStatus.FAILED
|
|
40
|
+
log_data = {'task_id': task_id, 'error': error or 'Unknown error'}
|
|
41
|
+
|
|
42
|
+
context.logger.log_annotate_task_data(log_data, status)
|
|
43
|
+
|
|
44
|
+
except Exception as e:
|
|
45
|
+
context.logger.log_message_with_code(LogCode.METRICS_RECORDING_FAILED, str(e))
|
|
46
|
+
|
|
47
|
+
def update_metrics(self, context: ToTaskContext, total_tasks: int, success_count: int, failed_count: int):
|
|
48
|
+
"""Update execution metrics.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
context: Shared context for the action execution
|
|
52
|
+
total_tasks: Total number of tasks
|
|
53
|
+
success_count: Number of successful tasks
|
|
54
|
+
failed_count: Number of failed tasks
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
stand_by_count = total_tasks - success_count - failed_count
|
|
58
|
+
context.update_metrics(success_count, failed_count, total_tasks)
|
|
59
|
+
|
|
60
|
+
# Update metrics in the logger
|
|
61
|
+
metrics_data = {
|
|
62
|
+
'stand_by': stand_by_count,
|
|
63
|
+
'failed': failed_count,
|
|
64
|
+
'success': success_count,
|
|
65
|
+
}
|
|
66
|
+
context.logger.log_metrics(metrics_data, 'annotate_task_data')
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
context.logger.log_message_with_code(LogCode.METRICS_UPDATE_FAILED, str(e))
|
|
70
|
+
|
|
71
|
+
def finalize_metrics(self, context: ToTaskContext):
|
|
72
|
+
"""Finalize metrics at the end of processing.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
context: Shared context for the action execution
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
metrics = context.metrics
|
|
79
|
+
total_tasks = len(context.task_ids)
|
|
80
|
+
|
|
81
|
+
context.logger.log_message_with_code(LogCode.ANNOTATION_COMPLETED, metrics.success, metrics.failed)
|
|
82
|
+
|
|
83
|
+
# Final progress update
|
|
84
|
+
self.update_progress(context, total_tasks, total_tasks)
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
context.logger.log_message_with_code(LogCode.METRICS_FINALIZATION_FAILED, str(e))
|