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,14 @@
|
|
|
1
|
+
class CriticalError(Exception):
|
|
2
|
+
"""Critical error."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, message: str = 'Critical error occured while processing task'):
|
|
5
|
+
self.message = message
|
|
6
|
+
super().__init__(self.message)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PreAnnotationToTaskFailed(Exception):
|
|
10
|
+
"""Pre-annotation to task failed."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, message: str = 'Pre-annotation to task failed'):
|
|
13
|
+
self.message = message
|
|
14
|
+
super().__init__(self.message)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Factory for creating ToTask strategies based on context."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Type
|
|
4
|
+
|
|
5
|
+
from .enums import AnnotationMethod
|
|
6
|
+
from .strategies.base import (
|
|
7
|
+
AnnotationStrategy,
|
|
8
|
+
DataExtractionStrategy,
|
|
9
|
+
MetricsStrategy,
|
|
10
|
+
PreProcessorStrategy,
|
|
11
|
+
ValidationStrategy,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ToTaskStrategyFactory:
|
|
16
|
+
"""Factory for creating action strategies based on context."""
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
# Import strategies here to avoid circular imports
|
|
20
|
+
from .strategies.annotation import FileAnnotationStrategy, InferenceAnnotationStrategy
|
|
21
|
+
from .strategies.extraction import FileUrlExtractionStrategy, InferenceDataExtractionStrategy
|
|
22
|
+
from .strategies.metrics import ProgressTrackingStrategy
|
|
23
|
+
from .strategies.preprocessor import PreProcessorManagementStrategy
|
|
24
|
+
from .strategies.validation import (
|
|
25
|
+
ProjectValidationStrategy,
|
|
26
|
+
TargetSpecificationValidationStrategy,
|
|
27
|
+
TaskValidationStrategy,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
self._annotation_strategies: Dict[AnnotationMethod, Type[AnnotationStrategy]] = {
|
|
31
|
+
AnnotationMethod.FILE: FileAnnotationStrategy,
|
|
32
|
+
AnnotationMethod.INFERENCE: InferenceAnnotationStrategy,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self._validation_strategies = {
|
|
36
|
+
'project': ProjectValidationStrategy,
|
|
37
|
+
'task': TaskValidationStrategy,
|
|
38
|
+
'target_spec': TargetSpecificationValidationStrategy,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
self._extraction_strategies = {
|
|
42
|
+
AnnotationMethod.FILE: FileUrlExtractionStrategy,
|
|
43
|
+
AnnotationMethod.INFERENCE: InferenceDataExtractionStrategy,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
self._preprocessor_strategy = PreProcessorManagementStrategy
|
|
47
|
+
self._metrics_strategy = ProgressTrackingStrategy
|
|
48
|
+
|
|
49
|
+
def create_annotation_strategy(self, method: AnnotationMethod) -> AnnotationStrategy:
|
|
50
|
+
"""Create annotation strategy based on method."""
|
|
51
|
+
strategy_class = self._annotation_strategies.get(method)
|
|
52
|
+
if not strategy_class:
|
|
53
|
+
raise ValueError(f'No annotation strategy available for method: {method}')
|
|
54
|
+
return strategy_class()
|
|
55
|
+
|
|
56
|
+
def create_validation_strategy(self, validation_type: str) -> ValidationStrategy:
|
|
57
|
+
"""Create validation strategy based on type."""
|
|
58
|
+
strategy_class = self._validation_strategies.get(validation_type)
|
|
59
|
+
if not strategy_class:
|
|
60
|
+
raise ValueError(f'No validation strategy available for type: {validation_type}')
|
|
61
|
+
return strategy_class()
|
|
62
|
+
|
|
63
|
+
def create_extraction_strategy(self, method: AnnotationMethod) -> DataExtractionStrategy:
|
|
64
|
+
"""Create data extraction strategy based on method."""
|
|
65
|
+
strategy_class = self._extraction_strategies.get(method)
|
|
66
|
+
if not strategy_class:
|
|
67
|
+
raise ValueError(f'No extraction strategy available for method: {method}')
|
|
68
|
+
return strategy_class()
|
|
69
|
+
|
|
70
|
+
def create_preprocessor_strategy(self) -> PreProcessorStrategy:
|
|
71
|
+
"""Create pre-processor management strategy."""
|
|
72
|
+
return self._preprocessor_strategy()
|
|
73
|
+
|
|
74
|
+
def create_metrics_strategy(self) -> MetricsStrategy:
|
|
75
|
+
"""Create metrics tracking strategy."""
|
|
76
|
+
return self._metrics_strategy()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from typing import Annotated, Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import AfterValidator, BaseModel, field_validator
|
|
4
|
+
from pydantic_core import PydanticCustomError
|
|
5
|
+
|
|
6
|
+
from synapse_sdk.clients.backend.models import JobStatus
|
|
7
|
+
from synapse_sdk.clients.exceptions import ClientError
|
|
8
|
+
from synapse_sdk.shared.enums import Context
|
|
9
|
+
from synapse_sdk.utils.pydantic.validators import non_blank
|
|
10
|
+
|
|
11
|
+
from .enums import AnnotateTaskDataStatus
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToTaskParams(BaseModel):
|
|
15
|
+
"""ToTask action parameters.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
name (str): The name of the action.
|
|
19
|
+
description (str | None): The description of the action.
|
|
20
|
+
project (int): The project ID.
|
|
21
|
+
agent (int): The agent ID.
|
|
22
|
+
task_filters (dict): The filters of tasks.
|
|
23
|
+
method (AnnotationMethod): The method of annotation.
|
|
24
|
+
target_specification_name (str | None): The name of the target specification.
|
|
25
|
+
model (int): The model ID.
|
|
26
|
+
pre_processor (int | None): The pre processor ID.
|
|
27
|
+
pre_processor_params (dict): The params of the pre processor.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
name: Annotated[str, AfterValidator(non_blank)]
|
|
31
|
+
description: Optional[str] = None
|
|
32
|
+
project: int
|
|
33
|
+
agent: int
|
|
34
|
+
task_filters: Dict[str, Any]
|
|
35
|
+
method: Optional[str] = None
|
|
36
|
+
target_specification_name: Optional[str] = None
|
|
37
|
+
model: Optional[int] = None
|
|
38
|
+
pre_processor: Optional[int] = None
|
|
39
|
+
pre_processor_params: Dict[str, Any]
|
|
40
|
+
|
|
41
|
+
@field_validator('project', mode='before')
|
|
42
|
+
@classmethod
|
|
43
|
+
def check_project_exists(cls, value: int, info) -> int:
|
|
44
|
+
"""Validate synapse-backend project exists."""
|
|
45
|
+
if not value:
|
|
46
|
+
return value
|
|
47
|
+
|
|
48
|
+
action = info.context['action']
|
|
49
|
+
client = action.client
|
|
50
|
+
try:
|
|
51
|
+
client.get_project(value)
|
|
52
|
+
except ClientError:
|
|
53
|
+
raise PydanticCustomError('client_error', 'Error occurred while checking project exists.')
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ToTaskResult(BaseModel):
|
|
58
|
+
"""Result model for ToTaskAction.start method.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
status (JobStatus): The job status from the action execution.
|
|
62
|
+
message (str): A descriptive message about the action result.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
status: JobStatus
|
|
66
|
+
message: str
|
|
67
|
+
|
|
68
|
+
def model_dump(self, **kwargs):
|
|
69
|
+
"""Override model_dump to return status as enum value."""
|
|
70
|
+
data = super().model_dump(**kwargs)
|
|
71
|
+
if 'status' in data and isinstance(data['status'], JobStatus):
|
|
72
|
+
data['status'] = data['status'].value
|
|
73
|
+
return data
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AnnotateTaskEventLog(BaseModel):
|
|
77
|
+
"""Annotate task event log model."""
|
|
78
|
+
|
|
79
|
+
info: Optional[str] = None
|
|
80
|
+
status: Context
|
|
81
|
+
created: str
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class AnnotateTaskDataLog(BaseModel):
|
|
85
|
+
"""Log model for annotate task data."""
|
|
86
|
+
|
|
87
|
+
task_info: Optional[str] = None
|
|
88
|
+
status: AnnotateTaskDataStatus
|
|
89
|
+
created: str
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class MetricsRecord(BaseModel):
|
|
93
|
+
"""Metrics record model."""
|
|
94
|
+
|
|
95
|
+
stand_by: int
|
|
96
|
+
failed: int
|
|
97
|
+
success: int
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""Orchestrator for coordinating ToTask action workflow using Facade pattern."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
from synapse_sdk.clients.backend.models import JobStatus
|
|
6
|
+
|
|
7
|
+
from .enums import AnnotationMethod, LogCode
|
|
8
|
+
from .exceptions import CriticalError, PreAnnotationToTaskFailed
|
|
9
|
+
from .factory import ToTaskStrategyFactory
|
|
10
|
+
from .models import ToTaskResult
|
|
11
|
+
from .strategies.base import ToTaskContext
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToTaskOrchestrator:
|
|
15
|
+
"""Facade that orchestrates the complete ToTask annotation workflow."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, context: ToTaskContext):
|
|
18
|
+
"""Initialize orchestrator with context and strategies.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
context: Shared context for the action execution
|
|
22
|
+
"""
|
|
23
|
+
self.context = context
|
|
24
|
+
self.factory = ToTaskStrategyFactory()
|
|
25
|
+
self.steps_completed = []
|
|
26
|
+
|
|
27
|
+
# Initialize strategies
|
|
28
|
+
self.project_validation = self.factory.create_validation_strategy('project')
|
|
29
|
+
self.task_validation = self.factory.create_validation_strategy('task')
|
|
30
|
+
self.target_spec_validation = self.factory.create_validation_strategy('target_spec')
|
|
31
|
+
self.metrics_strategy = self.factory.create_metrics_strategy()
|
|
32
|
+
|
|
33
|
+
def execute_workflow(self) -> Dict[str, Any]:
|
|
34
|
+
"""Execute the complete ToTask workflow with rollback support.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Dict containing the workflow result
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
# Step 1: Project and data collection validation
|
|
41
|
+
self._execute_step('project_validation', self._validate_project)
|
|
42
|
+
|
|
43
|
+
# Step 2: Task discovery and validation
|
|
44
|
+
self._execute_step('task_validation', self._validate_tasks)
|
|
45
|
+
|
|
46
|
+
# Step 3: Determine annotation method
|
|
47
|
+
self._execute_step('method_determination', self._determine_annotation_method)
|
|
48
|
+
|
|
49
|
+
# Step 4: Method-specific validation
|
|
50
|
+
self._execute_step('method_validation', self._validate_annotation_method)
|
|
51
|
+
|
|
52
|
+
# Step 5: Initialize processing
|
|
53
|
+
self._execute_step('processing_initialization', self._initialize_processing)
|
|
54
|
+
|
|
55
|
+
# Step 6: Process all tasks
|
|
56
|
+
self._execute_step('task_processing', self._process_all_tasks)
|
|
57
|
+
|
|
58
|
+
# Step 7: Finalize metrics and progress
|
|
59
|
+
self._execute_step('finalization', self._finalize_processing)
|
|
60
|
+
|
|
61
|
+
# Return success result
|
|
62
|
+
result = ToTaskResult(status=JobStatus.SUCCEEDED, message='Pre-annotation to task completed successfully')
|
|
63
|
+
return result.model_dump()
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
self._rollback_completed_steps()
|
|
67
|
+
if isinstance(e, PreAnnotationToTaskFailed):
|
|
68
|
+
raise e
|
|
69
|
+
raise PreAnnotationToTaskFailed(f'Workflow failed at step {len(self.steps_completed)}: {e}')
|
|
70
|
+
|
|
71
|
+
def _execute_step(self, step_name: str, step_func: callable):
|
|
72
|
+
"""Execute a workflow step with error handling and progress tracking.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
step_name: Name of the step for logging
|
|
76
|
+
step_func: Function to execute for this step
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Result of the step function
|
|
80
|
+
"""
|
|
81
|
+
self.context.logger.log_message_with_code(LogCode.STEP_STARTED, step_name)
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
result = step_func()
|
|
85
|
+
self.steps_completed.append(step_name)
|
|
86
|
+
self.context.logger.log_message_with_code(LogCode.STEP_COMPLETED, step_name)
|
|
87
|
+
return result
|
|
88
|
+
except Exception as e:
|
|
89
|
+
self.context.logger.log_message_with_code(LogCode.STEP_FAILED, step_name, str(e))
|
|
90
|
+
raise
|
|
91
|
+
|
|
92
|
+
def _validate_project(self):
|
|
93
|
+
"""Step 1: Validate project and data collection."""
|
|
94
|
+
result = self.project_validation.validate(self.context)
|
|
95
|
+
if not result['success']:
|
|
96
|
+
error_msg = result.get('error', 'Project validation failed')
|
|
97
|
+
self.context.logger.end_log()
|
|
98
|
+
raise PreAnnotationToTaskFailed(error_msg)
|
|
99
|
+
|
|
100
|
+
def _validate_tasks(self):
|
|
101
|
+
"""Step 2: Discover and validate tasks."""
|
|
102
|
+
result = self.task_validation.validate(self.context)
|
|
103
|
+
if not result['success']:
|
|
104
|
+
error_msg = result.get('error', 'Task validation failed')
|
|
105
|
+
self.context.logger.end_log()
|
|
106
|
+
raise PreAnnotationToTaskFailed(error_msg)
|
|
107
|
+
|
|
108
|
+
def _determine_annotation_method(self):
|
|
109
|
+
"""Step 3: Determine annotation method from parameters."""
|
|
110
|
+
method = self.context.params.get('method')
|
|
111
|
+
if method == AnnotationMethod.FILE:
|
|
112
|
+
self.context.annotation_method = AnnotationMethod.FILE
|
|
113
|
+
elif method == AnnotationMethod.INFERENCE:
|
|
114
|
+
self.context.annotation_method = AnnotationMethod.INFERENCE
|
|
115
|
+
else:
|
|
116
|
+
self.context.logger.log_message_with_code(LogCode.UNSUPPORTED_METHOD, method)
|
|
117
|
+
self.context.logger.end_log()
|
|
118
|
+
raise PreAnnotationToTaskFailed(f'Unsupported annotation method: {method}')
|
|
119
|
+
|
|
120
|
+
def _validate_annotation_method(self):
|
|
121
|
+
"""Step 4: Validate method-specific requirements."""
|
|
122
|
+
if self.context.annotation_method == AnnotationMethod.FILE:
|
|
123
|
+
result = self.target_spec_validation.validate(self.context)
|
|
124
|
+
if not result['success']:
|
|
125
|
+
error_msg = result.get('error', 'Target specification validation failed')
|
|
126
|
+
self.context.logger.end_log()
|
|
127
|
+
raise PreAnnotationToTaskFailed(error_msg)
|
|
128
|
+
|
|
129
|
+
def _initialize_processing(self):
|
|
130
|
+
"""Step 5: Initialize processing metrics and progress."""
|
|
131
|
+
total_tasks = len(self.context.task_ids)
|
|
132
|
+
self.context.update_metrics(0, 0, total_tasks)
|
|
133
|
+
self.metrics_strategy.update_progress(self.context, 0, total_tasks)
|
|
134
|
+
self.context.logger.log_message_with_code(LogCode.ANNOTATING_DATA)
|
|
135
|
+
|
|
136
|
+
def _process_all_tasks(self):
|
|
137
|
+
"""Step 6: Process all tasks using appropriate annotation strategy."""
|
|
138
|
+
annotation_strategy = self.factory.create_annotation_strategy(self.context.annotation_method)
|
|
139
|
+
|
|
140
|
+
total_tasks = len(self.context.task_ids)
|
|
141
|
+
success_count = 0
|
|
142
|
+
failed_count = 0
|
|
143
|
+
current_progress = 0
|
|
144
|
+
|
|
145
|
+
# Get task parameters
|
|
146
|
+
task_params = {
|
|
147
|
+
'fields': 'id,data,data_unit',
|
|
148
|
+
'expand': 'data_unit',
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Process each task
|
|
152
|
+
for task_id in self.context.task_ids:
|
|
153
|
+
try:
|
|
154
|
+
# Get task data
|
|
155
|
+
task_response = self.context.client.get_task(task_id, params=task_params)
|
|
156
|
+
if isinstance(task_response, str):
|
|
157
|
+
error_msg = 'Invalid task response'
|
|
158
|
+
self.context.logger.log_annotate_task_event(LogCode.INVALID_TASK_RESPONSE, task_id)
|
|
159
|
+
self.metrics_strategy.record_task_result(self.context, task_id, False, error_msg)
|
|
160
|
+
failed_count += 1
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
task_data: Dict[str, Any] = task_response
|
|
164
|
+
|
|
165
|
+
# Process task using annotation strategy
|
|
166
|
+
if self.context.annotation_method == AnnotationMethod.FILE:
|
|
167
|
+
target_spec_name = self.context.params.get('target_specification_name')
|
|
168
|
+
result = annotation_strategy.process_task(
|
|
169
|
+
self.context, task_id, task_data, target_specification_name=target_spec_name
|
|
170
|
+
)
|
|
171
|
+
else:
|
|
172
|
+
result = annotation_strategy.process_task(self.context, task_id, task_data)
|
|
173
|
+
|
|
174
|
+
# Record result
|
|
175
|
+
if result['success']:
|
|
176
|
+
success_count += 1
|
|
177
|
+
self.metrics_strategy.record_task_result(self.context, task_id, True)
|
|
178
|
+
else:
|
|
179
|
+
failed_count += 1
|
|
180
|
+
error_msg = result.get('error', 'Unknown error')
|
|
181
|
+
self.metrics_strategy.record_task_result(self.context, task_id, False, error_msg)
|
|
182
|
+
|
|
183
|
+
# Update progress
|
|
184
|
+
current_progress += 1
|
|
185
|
+
self.context.update_metrics(success_count, failed_count, total_tasks)
|
|
186
|
+
self.metrics_strategy.update_progress(self.context, current_progress, total_tasks)
|
|
187
|
+
self.metrics_strategy.update_metrics(self.context, total_tasks, success_count, failed_count)
|
|
188
|
+
|
|
189
|
+
except CriticalError:
|
|
190
|
+
self.context.logger.log_message_with_code(LogCode.CRITICAL_ERROR)
|
|
191
|
+
raise PreAnnotationToTaskFailed('Critical error occurred during task processing')
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
self.context.logger.log_annotate_task_event(LogCode.TASK_PROCESSING_FAILED, task_id, str(e))
|
|
195
|
+
self.metrics_strategy.record_task_result(self.context, task_id, False, str(e))
|
|
196
|
+
failed_count += 1
|
|
197
|
+
current_progress += 1
|
|
198
|
+
self.context.update_metrics(success_count, failed_count, total_tasks)
|
|
199
|
+
self.metrics_strategy.update_progress(self.context, current_progress, total_tasks)
|
|
200
|
+
self.metrics_strategy.update_metrics(self.context, total_tasks, success_count, failed_count)
|
|
201
|
+
|
|
202
|
+
def _finalize_processing(self):
|
|
203
|
+
"""Step 7: Finalize metrics."""
|
|
204
|
+
# Finalize metrics
|
|
205
|
+
self.metrics_strategy.finalize_metrics(self.context)
|
|
206
|
+
|
|
207
|
+
def _rollback_completed_steps(self):
|
|
208
|
+
"""Rollback completed steps in reverse order."""
|
|
209
|
+
for step in reversed(self.steps_completed):
|
|
210
|
+
try:
|
|
211
|
+
rollback_method = getattr(self, f'_rollback_{step}', None)
|
|
212
|
+
if rollback_method:
|
|
213
|
+
rollback_method()
|
|
214
|
+
except Exception as e:
|
|
215
|
+
self.context.logger.log_message_with_code(LogCode.ROLLBACK_FAILED, step, str(e))
|
|
216
|
+
|
|
217
|
+
# Execute any additional rollback actions
|
|
218
|
+
for action in reversed(self.context.rollback_actions):
|
|
219
|
+
try:
|
|
220
|
+
action()
|
|
221
|
+
except Exception as e:
|
|
222
|
+
self.context.logger.log_message_with_code(LogCode.ROLLBACK_ACTION_FAILED, str(e))
|
|
223
|
+
|
|
224
|
+
def _rollback_project_validation(self):
|
|
225
|
+
"""Rollback project validation step."""
|
|
226
|
+
# Clear cached project and data collection data
|
|
227
|
+
self.context.project = None
|
|
228
|
+
self.context.data_collection = None
|
|
229
|
+
|
|
230
|
+
def _rollback_task_validation(self):
|
|
231
|
+
"""Rollback task validation step."""
|
|
232
|
+
# Clear cached task data
|
|
233
|
+
self.context.task_ids = []
|
|
234
|
+
|
|
235
|
+
def _rollback_processing_initialization(self):
|
|
236
|
+
"""Rollback processing initialization step."""
|
|
237
|
+
# Reset metrics
|
|
238
|
+
self.context.update_metrics(0, 0, 0)
|
|
239
|
+
|
|
240
|
+
def _rollback_task_processing(self):
|
|
241
|
+
"""Rollback task processing step."""
|
|
242
|
+
# Clean up any temporary files
|
|
243
|
+
for temp_file in self.context.temp_files:
|
|
244
|
+
try:
|
|
245
|
+
import os
|
|
246
|
+
|
|
247
|
+
if os.path.exists(temp_file):
|
|
248
|
+
os.remove(temp_file)
|
|
249
|
+
except Exception:
|
|
250
|
+
pass # Best effort cleanup
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from synapse_sdk.plugins.models import Run
|
|
6
|
+
from synapse_sdk.shared.enums import Context
|
|
7
|
+
|
|
8
|
+
from .enums import LOG_MESSAGES, AnnotateTaskDataStatus, LogCode
|
|
9
|
+
from .models import AnnotateTaskDataLog, AnnotateTaskEventLog, MetricsRecord
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ToTaskRun(Run):
|
|
13
|
+
def log_message_with_code(self, code: LogCode, *args, level: Optional[Context] = None):
|
|
14
|
+
"""Log message using predefined code and optional level override."""
|
|
15
|
+
if code not in LOG_MESSAGES:
|
|
16
|
+
self.log_message(f'Unknown log code: {code}')
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
log_config = LOG_MESSAGES[code]
|
|
20
|
+
message = log_config['message'].format(*args) if args else log_config['message']
|
|
21
|
+
log_level = level or log_config['level']
|
|
22
|
+
|
|
23
|
+
if log_level:
|
|
24
|
+
self.log_message(message, context=log_level.value)
|
|
25
|
+
else:
|
|
26
|
+
self.log_message(message, context=Context.INFO.value)
|
|
27
|
+
|
|
28
|
+
def log_annotate_task_event(self, code: LogCode, *args, level: Optional[Context] = None):
|
|
29
|
+
"""Log annotate task event using predefined code."""
|
|
30
|
+
if code not in LOG_MESSAGES:
|
|
31
|
+
now = datetime.now().isoformat()
|
|
32
|
+
self.log(
|
|
33
|
+
'annotate_task_event',
|
|
34
|
+
AnnotateTaskEventLog(info=f'Unknown log code: {code}', status=Context.DANGER, created=now).model_dump(),
|
|
35
|
+
)
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
log_config = LOG_MESSAGES[code]
|
|
39
|
+
message = log_config['message'].format(*args) if args else log_config['message']
|
|
40
|
+
log_level = level or log_config['level'] or Context.INFO
|
|
41
|
+
|
|
42
|
+
now = datetime.now().isoformat()
|
|
43
|
+
self.log(
|
|
44
|
+
'annotate_task_event',
|
|
45
|
+
AnnotateTaskEventLog(info=message, status=log_level, created=now).model_dump(),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def log_annotate_task_data(self, task_info: Dict[str, Any], status: AnnotateTaskDataStatus):
|
|
49
|
+
"""Log annotate task data."""
|
|
50
|
+
now = datetime.now().isoformat()
|
|
51
|
+
self.log(
|
|
52
|
+
'annotate_task_data',
|
|
53
|
+
AnnotateTaskDataLog(task_info=json.dumps(task_info), status=status, created=now).model_dump(),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def log_metrics(self, record: MetricsRecord, category: str):
|
|
57
|
+
"""Log FileToTask metrics.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
record (MetricsRecord): The metrics record to log.
|
|
61
|
+
category (str): The category of the metrics.
|
|
62
|
+
"""
|
|
63
|
+
record = MetricsRecord.model_validate(record)
|
|
64
|
+
self.set_metrics(value=record.model_dump(), category=category)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Strategy classes for ToTask action refactoring."""
|
|
2
|
+
|
|
3
|
+
from .base import (
|
|
4
|
+
AnnotationStrategy,
|
|
5
|
+
DataExtractionStrategy,
|
|
6
|
+
MetricsStrategy,
|
|
7
|
+
PreProcessorStrategy,
|
|
8
|
+
ValidationStrategy,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'AnnotationStrategy',
|
|
13
|
+
'DataExtractionStrategy',
|
|
14
|
+
'MetricsStrategy',
|
|
15
|
+
'PreProcessorStrategy',
|
|
16
|
+
'ValidationStrategy',
|
|
17
|
+
]
|