synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__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/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +9 -8
- synapse_sdk/cli/agent/__init__.py +25 -0
- synapse_sdk/cli/agent/config.py +104 -0
- synapse_sdk/cli/agent/select.py +197 -0
- synapse_sdk/cli/auth.py +104 -0
- synapse_sdk/cli/main.py +1025 -0
- synapse_sdk/cli/plugin/__init__.py +58 -0
- synapse_sdk/cli/plugin/create.py +566 -0
- synapse_sdk/cli/plugin/job.py +196 -0
- synapse_sdk/cli/plugin/publish.py +322 -0
- synapse_sdk/cli/plugin/run.py +131 -0
- synapse_sdk/cli/plugin/test.py +200 -0
- synapse_sdk/clients/README.md +239 -0
- synapse_sdk/clients/__init__.py +5 -0
- synapse_sdk/clients/_template.py +266 -0
- synapse_sdk/clients/agent/__init__.py +84 -29
- synapse_sdk/clients/agent/async_ray.py +289 -0
- synapse_sdk/clients/agent/container.py +83 -0
- synapse_sdk/clients/agent/plugin.py +101 -0
- synapse_sdk/clients/agent/ray.py +296 -39
- synapse_sdk/clients/backend/__init__.py +152 -12
- synapse_sdk/clients/backend/annotation.py +164 -22
- synapse_sdk/clients/backend/core.py +101 -0
- synapse_sdk/clients/backend/data_collection.py +292 -0
- synapse_sdk/clients/backend/hitl.py +87 -0
- synapse_sdk/clients/backend/integration.py +374 -46
- synapse_sdk/clients/backend/ml.py +134 -22
- synapse_sdk/clients/backend/models.py +247 -0
- synapse_sdk/clients/base.py +538 -59
- synapse_sdk/clients/exceptions.py +35 -7
- synapse_sdk/clients/pipeline/__init__.py +5 -0
- synapse_sdk/clients/pipeline/client.py +636 -0
- synapse_sdk/clients/protocols.py +178 -0
- synapse_sdk/clients/utils.py +86 -8
- synapse_sdk/clients/validation.py +58 -0
- synapse_sdk/enums.py +76 -0
- synapse_sdk/exceptions.py +168 -0
- synapse_sdk/integrations/__init__.py +74 -0
- synapse_sdk/integrations/_base.py +119 -0
- synapse_sdk/integrations/_context.py +53 -0
- synapse_sdk/integrations/ultralytics/__init__.py +78 -0
- synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
- synapse_sdk/integrations/ultralytics/_patches.py +124 -0
- synapse_sdk/loggers.py +476 -95
- synapse_sdk/mcp/MCP.md +69 -0
- synapse_sdk/mcp/__init__.py +48 -0
- synapse_sdk/mcp/__main__.py +6 -0
- synapse_sdk/mcp/config.py +349 -0
- synapse_sdk/mcp/prompts/__init__.py +4 -0
- synapse_sdk/mcp/resources/__init__.py +4 -0
- synapse_sdk/mcp/server.py +1352 -0
- synapse_sdk/mcp/tools/__init__.py +6 -0
- synapse_sdk/plugins/__init__.py +133 -9
- synapse_sdk/plugins/action.py +229 -0
- synapse_sdk/plugins/actions/__init__.py +82 -0
- synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
- synapse_sdk/plugins/actions/dataset/action.py +471 -0
- synapse_sdk/plugins/actions/export/__init__.py +55 -0
- synapse_sdk/plugins/actions/export/action.py +183 -0
- synapse_sdk/plugins/actions/export/context.py +59 -0
- synapse_sdk/plugins/actions/inference/__init__.py +84 -0
- synapse_sdk/plugins/actions/inference/action.py +285 -0
- synapse_sdk/plugins/actions/inference/context.py +81 -0
- synapse_sdk/plugins/actions/inference/deployment.py +322 -0
- synapse_sdk/plugins/actions/inference/serve.py +252 -0
- synapse_sdk/plugins/actions/train/__init__.py +54 -0
- synapse_sdk/plugins/actions/train/action.py +326 -0
- synapse_sdk/plugins/actions/train/context.py +57 -0
- synapse_sdk/plugins/actions/upload/__init__.py +49 -0
- synapse_sdk/plugins/actions/upload/action.py +165 -0
- synapse_sdk/plugins/actions/upload/context.py +61 -0
- synapse_sdk/plugins/config.py +98 -0
- synapse_sdk/plugins/context/__init__.py +109 -0
- synapse_sdk/plugins/context/env.py +113 -0
- synapse_sdk/plugins/datasets/__init__.py +113 -0
- synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
- synapse_sdk/plugins/datasets/converters/base.py +347 -0
- synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
- synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
- synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
- synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
- synapse_sdk/plugins/datasets/formats/dm.py +351 -0
- synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
- synapse_sdk/plugins/decorators.py +83 -0
- synapse_sdk/plugins/discovery.py +790 -0
- synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
- synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
- synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
- synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
- synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
- synapse_sdk/plugins/docs/README.md +513 -0
- synapse_sdk/plugins/docs/STEP.md +656 -0
- synapse_sdk/plugins/enums.py +70 -10
- synapse_sdk/plugins/errors.py +92 -0
- synapse_sdk/plugins/executors/__init__.py +43 -0
- synapse_sdk/plugins/executors/local.py +99 -0
- synapse_sdk/plugins/executors/ray/__init__.py +18 -0
- synapse_sdk/plugins/executors/ray/base.py +282 -0
- synapse_sdk/plugins/executors/ray/job.py +298 -0
- synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
- synapse_sdk/plugins/executors/ray/packaging.py +137 -0
- synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
- synapse_sdk/plugins/executors/ray/task.py +257 -0
- synapse_sdk/plugins/models/__init__.py +26 -0
- synapse_sdk/plugins/models/logger.py +173 -0
- synapse_sdk/plugins/models/pipeline.py +25 -0
- synapse_sdk/plugins/pipelines/__init__.py +81 -0
- synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
- synapse_sdk/plugins/pipelines/context.py +107 -0
- synapse_sdk/plugins/pipelines/display.py +311 -0
- synapse_sdk/plugins/runner.py +114 -0
- synapse_sdk/plugins/schemas/__init__.py +19 -0
- synapse_sdk/plugins/schemas/results.py +152 -0
- synapse_sdk/plugins/steps/__init__.py +63 -0
- synapse_sdk/plugins/steps/base.py +128 -0
- synapse_sdk/plugins/steps/context.py +90 -0
- synapse_sdk/plugins/steps/orchestrator.py +128 -0
- synapse_sdk/plugins/steps/registry.py +103 -0
- synapse_sdk/plugins/steps/utils/__init__.py +20 -0
- synapse_sdk/plugins/steps/utils/logging.py +85 -0
- synapse_sdk/plugins/steps/utils/timing.py +71 -0
- synapse_sdk/plugins/steps/utils/validation.py +68 -0
- synapse_sdk/plugins/templates/__init__.py +50 -0
- synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
- synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
- synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
- synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
- synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
- synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
- synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
- synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
- synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
- synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
- synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
- synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
- synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
- synapse_sdk/plugins/testing/__init__.py +25 -0
- synapse_sdk/plugins/testing/sample_actions.py +98 -0
- synapse_sdk/plugins/types.py +206 -0
- synapse_sdk/plugins/upload.py +595 -64
- synapse_sdk/plugins/utils.py +325 -37
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/utils/__init__.py +1 -0
- synapse_sdk/utils/auth.py +74 -0
- synapse_sdk/utils/file/__init__.py +58 -0
- synapse_sdk/utils/file/archive.py +449 -0
- synapse_sdk/utils/file/checksum.py +167 -0
- synapse_sdk/utils/file/download.py +286 -0
- synapse_sdk/utils/file/io.py +129 -0
- synapse_sdk/utils/file/requirements.py +36 -0
- synapse_sdk/utils/network.py +168 -0
- synapse_sdk/utils/storage/__init__.py +238 -0
- synapse_sdk/utils/storage/config.py +188 -0
- synapse_sdk/utils/storage/errors.py +52 -0
- synapse_sdk/utils/storage/providers/__init__.py +13 -0
- synapse_sdk/utils/storage/providers/base.py +76 -0
- synapse_sdk/utils/storage/providers/gcs.py +168 -0
- synapse_sdk/utils/storage/providers/http.py +250 -0
- synapse_sdk/utils/storage/providers/local.py +126 -0
- synapse_sdk/utils/storage/providers/s3.py +177 -0
- synapse_sdk/utils/storage/providers/sftp.py +208 -0
- synapse_sdk/utils/storage/registry.py +125 -0
- synapse_sdk/utils/websocket.py +99 -0
- synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
- synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
- synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
- locale/en/LC_MESSAGES/messages.mo +0 -0
- locale/en/LC_MESSAGES/messages.po +0 -39
- locale/ko/LC_MESSAGES/messages.mo +0 -0
- locale/ko/LC_MESSAGES/messages.po +0 -34
- synapse_sdk/cli/create_plugin.py +0 -10
- synapse_sdk/clients/agent/core.py +0 -7
- synapse_sdk/clients/agent/service.py +0 -15
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/clients/ray/__init__.py +0 -6
- synapse_sdk/clients/ray/core.py +0 -22
- synapse_sdk/clients/ray/serve.py +0 -20
- synapse_sdk/i18n.py +0 -35
- synapse_sdk/plugins/categories/__init__.py +0 -0
- synapse_sdk/plugins/categories/base.py +0 -235
- synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
- synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
- synapse_sdk/plugins/categories/decorators.py +0 -13
- synapse_sdk/plugins/categories/export/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/export/actions/export.py +0 -10
- synapse_sdk/plugins/categories/import/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
- synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
- synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
- synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
- synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
- synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
- synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
- synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
- synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
- synapse_sdk/plugins/categories/registry.py +0 -16
- synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
- synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
- synapse_sdk/plugins/categories/templates.py +0 -32
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/cli/run.py +0 -67
- synapse_sdk/plugins/exceptions.py +0 -22
- synapse_sdk/plugins/models.py +0 -121
- synapse_sdk/plugins/templates/cookiecutter.json +0 -11
- synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
- synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
- synapse_sdk/shared/enums.py +0 -8
- synapse_sdk/utils/debug.py +0 -5
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/module_loading.py +0 -29
- synapse_sdk/utils/pydantic/__init__.py +0 -0
- synapse_sdk/utils/pydantic/config.py +0 -4
- synapse_sdk/utils/pydantic/errors.py +0 -33
- synapse_sdk/utils/pydantic/validators.py +0 -7
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk/utils/string.py +0 -11
- synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
- synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Train action base class with optional step support."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from synapse_sdk.plugins.action import BaseAction
|
|
11
|
+
from synapse_sdk.plugins.actions.train.context import TrainContext
|
|
12
|
+
from synapse_sdk.plugins.enums import PluginCategory
|
|
13
|
+
from synapse_sdk.plugins.steps import Orchestrator, StepRegistry
|
|
14
|
+
|
|
15
|
+
P = TypeVar('P', bound=BaseModel)
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseTrainParams(BaseModel):
|
|
22
|
+
"""Base parameters for training actions.
|
|
23
|
+
|
|
24
|
+
Provides common fields used across training workflows.
|
|
25
|
+
Extend this class to add plugin-specific training parameters.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
checkpoint: Optional model ID to use as starting checkpoint.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> class MyTrainParams(BaseTrainParams):
|
|
32
|
+
... epochs: int = 100
|
|
33
|
+
... batch_size: int = 32
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
checkpoint: int | None = Field(default=None, description='Checkpoint model ID to resume from')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TrainProgressCategories:
|
|
40
|
+
"""Standard progress category names for training workflows.
|
|
41
|
+
|
|
42
|
+
Use these constants with set_progress() to track multi-phase training:
|
|
43
|
+
- DATASET: Data loading and preprocessing
|
|
44
|
+
- TRAIN: Model training iterations
|
|
45
|
+
- MODEL_UPLOAD: Final model upload to backend
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
>>> self.set_progress(1, 10, TrainProgressCategories.DATASET)
|
|
49
|
+
>>> self.set_progress(50, 100, TrainProgressCategories.TRAIN)
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
DATASET: str = 'dataset'
|
|
53
|
+
TRAIN: str = 'train'
|
|
54
|
+
MODEL_UPLOAD: str = 'model_upload'
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class BaseTrainAction(BaseAction[P]):
|
|
58
|
+
"""Base class for training actions.
|
|
59
|
+
|
|
60
|
+
Provides helper methods for common training workflows:
|
|
61
|
+
dataset fetching, model creation, and progress tracking.
|
|
62
|
+
|
|
63
|
+
Supports two execution modes:
|
|
64
|
+
1. Simple execute: Override execute() directly for simple workflows
|
|
65
|
+
2. Step-based: Override setup_steps() to register workflow steps
|
|
66
|
+
|
|
67
|
+
If setup_steps() registers any steps, the step-based workflow
|
|
68
|
+
takes precedence and execute() is not called directly.
|
|
69
|
+
|
|
70
|
+
Attributes:
|
|
71
|
+
category: Plugin category (defaults to NEURAL_NET).
|
|
72
|
+
progress: Standard progress category names.
|
|
73
|
+
|
|
74
|
+
Example (without result schema):
|
|
75
|
+
>>> class MyTrainAction(BaseTrainAction[MyParams]):
|
|
76
|
+
... def execute(self) -> dict[str, Any]:
|
|
77
|
+
... return {'weights_path': '/model.pt'}
|
|
78
|
+
|
|
79
|
+
Example (with result schema):
|
|
80
|
+
>>> class TrainResult(BaseModel):
|
|
81
|
+
... weights_path: str
|
|
82
|
+
... final_loss: float
|
|
83
|
+
>>>
|
|
84
|
+
>>> class MyTrainAction(BaseTrainAction[MyParams]):
|
|
85
|
+
... result_model = TrainResult
|
|
86
|
+
...
|
|
87
|
+
... def execute(self) -> TrainResult:
|
|
88
|
+
... return TrainResult(weights_path='/model.pt', final_loss=0.1)
|
|
89
|
+
|
|
90
|
+
Example (step-based):
|
|
91
|
+
>>> class MyTrainAction(BaseTrainAction[MyParams]):
|
|
92
|
+
... def setup_steps(self, registry: StepRegistry[TrainContext]) -> None:
|
|
93
|
+
... registry.register(LoadDatasetStep())
|
|
94
|
+
... registry.register(TrainStep())
|
|
95
|
+
... registry.register(UploadModelStep())
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
category = PluginCategory.NEURAL_NET
|
|
99
|
+
progress = TrainProgressCategories()
|
|
100
|
+
|
|
101
|
+
def autolog(self, framework: str) -> None:
|
|
102
|
+
"""Enable automatic logging for an ML framework.
|
|
103
|
+
|
|
104
|
+
Call this before creating model objects. The SDK will automatically
|
|
105
|
+
attach callbacks to log progress, metrics, and artifacts.
|
|
106
|
+
|
|
107
|
+
Supported frameworks:
|
|
108
|
+
- 'ultralytics': YOLO object detection models
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
framework: Framework name (e.g., 'ultralytics').
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
ValueError: If framework is not recognized.
|
|
115
|
+
ImportError: If framework package is not installed.
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
>>> def execute(self):
|
|
119
|
+
... self.autolog('ultralytics')
|
|
120
|
+
... model = YOLO('yolov8n.pt') # Callbacks auto-attached
|
|
121
|
+
... model.train(epochs=100) # Metrics logged automatically
|
|
122
|
+
"""
|
|
123
|
+
from synapse_sdk.integrations import autolog as _autolog
|
|
124
|
+
|
|
125
|
+
_autolog(framework, self)
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def client(self) -> BackendClient:
|
|
129
|
+
"""Backend client from context.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
BackendClient instance.
|
|
133
|
+
|
|
134
|
+
Raises:
|
|
135
|
+
RuntimeError: If no client in context.
|
|
136
|
+
"""
|
|
137
|
+
if self.ctx.client is None:
|
|
138
|
+
raise RuntimeError(
|
|
139
|
+
'No client in context. Either provide a client via RuntimeContext '
|
|
140
|
+
'or override the helper methods (get_dataset, create_model, get_model).'
|
|
141
|
+
)
|
|
142
|
+
return self.ctx.client
|
|
143
|
+
|
|
144
|
+
def setup_steps(self, registry: StepRegistry[TrainContext]) -> None:
|
|
145
|
+
"""Register workflow steps for step-based execution.
|
|
146
|
+
|
|
147
|
+
Override this method to register custom steps for your training workflow.
|
|
148
|
+
If steps are registered, step-based execution takes precedence.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
registry: StepRegistry to register steps with.
|
|
152
|
+
|
|
153
|
+
Example:
|
|
154
|
+
>>> def setup_steps(self, registry: StepRegistry[TrainContext]) -> None:
|
|
155
|
+
... registry.register(LoadDatasetStep())
|
|
156
|
+
... registry.register(TrainStep())
|
|
157
|
+
... registry.register(UploadModelStep())
|
|
158
|
+
"""
|
|
159
|
+
pass # Default: no steps, uses simple execute()
|
|
160
|
+
|
|
161
|
+
def create_context(self) -> TrainContext:
|
|
162
|
+
"""Create training context for step-based workflow.
|
|
163
|
+
|
|
164
|
+
Override to customize context creation or add additional state.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
TrainContext instance with params and runtime context.
|
|
168
|
+
"""
|
|
169
|
+
params_dict = self.params.model_dump() if hasattr(self.params, 'model_dump') else dict(self.params)
|
|
170
|
+
return TrainContext(
|
|
171
|
+
runtime_ctx=self.ctx,
|
|
172
|
+
params=params_dict,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
def run(self) -> Any:
|
|
176
|
+
"""Run the action, using steps if registered.
|
|
177
|
+
|
|
178
|
+
This method is called by executors. It checks if steps are
|
|
179
|
+
registered and uses step-based execution if so.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Action result (dict or any return type).
|
|
183
|
+
"""
|
|
184
|
+
# Check if steps are registered
|
|
185
|
+
registry: StepRegistry[TrainContext] = StepRegistry()
|
|
186
|
+
self.setup_steps(registry)
|
|
187
|
+
|
|
188
|
+
if registry:
|
|
189
|
+
# Step-based execution
|
|
190
|
+
context = self.create_context()
|
|
191
|
+
orchestrator: Orchestrator[TrainContext] = Orchestrator(
|
|
192
|
+
registry=registry,
|
|
193
|
+
context=context,
|
|
194
|
+
progress_callback=lambda curr, total: self.set_progress(curr, total),
|
|
195
|
+
)
|
|
196
|
+
result = orchestrator.execute()
|
|
197
|
+
|
|
198
|
+
# Add context data to result
|
|
199
|
+
if context.model:
|
|
200
|
+
result['model'] = context.model
|
|
201
|
+
|
|
202
|
+
return result
|
|
203
|
+
|
|
204
|
+
# Simple execute mode
|
|
205
|
+
return self.execute()
|
|
206
|
+
|
|
207
|
+
def get_dataset(self) -> dict[str, Any]:
|
|
208
|
+
"""Fetch training dataset from backend.
|
|
209
|
+
|
|
210
|
+
Default implementation uses params.dataset_id to fetch
|
|
211
|
+
via client.get_data_collection(). Override for custom behavior.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Dataset metadata dictionary.
|
|
215
|
+
|
|
216
|
+
Raises:
|
|
217
|
+
ValueError: If params.dataset_id is not set.
|
|
218
|
+
RuntimeError: If no client in context.
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
>>> # Override for S3:
|
|
222
|
+
>>> def get_dataset(self) -> dict[str, Any]:
|
|
223
|
+
... return download_from_s3(self.params.s3_path)
|
|
224
|
+
"""
|
|
225
|
+
dataset_id = getattr(self.params, 'dataset_id', None)
|
|
226
|
+
if dataset_id is None:
|
|
227
|
+
raise ValueError(
|
|
228
|
+
'params.dataset_id is required for default get_dataset(). '
|
|
229
|
+
'Either set dataset_id in your params model or override get_dataset().'
|
|
230
|
+
)
|
|
231
|
+
return self.client.get_data_collection(dataset_id)
|
|
232
|
+
|
|
233
|
+
def create_model(self, path: str, **kwargs: Any) -> dict[str, Any]:
|
|
234
|
+
"""Upload trained model to backend.
|
|
235
|
+
|
|
236
|
+
Default implementation uploads via client.create_model().
|
|
237
|
+
Override for custom behavior (e.g., MLflow, S3).
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
path: Local path to model artifacts.
|
|
241
|
+
**kwargs: Additional fields for model creation.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Created model metadata dictionary.
|
|
245
|
+
|
|
246
|
+
Raises:
|
|
247
|
+
RuntimeError: If no client in context.
|
|
248
|
+
|
|
249
|
+
Example:
|
|
250
|
+
>>> model = self.create_model('./model', name='my-model')
|
|
251
|
+
"""
|
|
252
|
+
return self.client.create_model({
|
|
253
|
+
'file': path,
|
|
254
|
+
**kwargs,
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
def get_model(self, model_id: int) -> dict[str, Any]:
|
|
258
|
+
"""Retrieve existing model by ID.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
model_id: Model identifier.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Model metadata dictionary.
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
RuntimeError: If no client in context.
|
|
268
|
+
"""
|
|
269
|
+
return self.client.get_model(model_id)
|
|
270
|
+
|
|
271
|
+
def get_checkpoint(self) -> dict[str, Any] | None:
|
|
272
|
+
"""Get checkpoint for training, either from context or by fetching from backend.
|
|
273
|
+
|
|
274
|
+
This method handles checkpoint resolution in the following order:
|
|
275
|
+
1. If ctx.checkpoint is already set (remote mode), returns it directly
|
|
276
|
+
2. If params has a 'checkpoint' field (model ID), fetches and extracts the model
|
|
277
|
+
|
|
278
|
+
The returned checkpoint dict contains:
|
|
279
|
+
- category: 'base' or fine-tuned model category
|
|
280
|
+
- path: Local path to the extracted model files
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
Checkpoint dict with 'category' and 'path', or None if no checkpoint.
|
|
284
|
+
|
|
285
|
+
Raises:
|
|
286
|
+
RuntimeError: If no client in context and checkpoint ID provided.
|
|
287
|
+
FileNotFoundError: If model file cannot be downloaded/extracted.
|
|
288
|
+
|
|
289
|
+
Example:
|
|
290
|
+
>>> checkpoint = self.get_checkpoint()
|
|
291
|
+
>>> if checkpoint:
|
|
292
|
+
... model_path = checkpoint['path']
|
|
293
|
+
... is_base = checkpoint['category'] == 'base'
|
|
294
|
+
"""
|
|
295
|
+
from synapse_sdk.utils.file import extract_archive, get_temp_path
|
|
296
|
+
|
|
297
|
+
# If checkpoint is already in context (remote mode), return it
|
|
298
|
+
if self.ctx.checkpoint is not None:
|
|
299
|
+
return self.ctx.checkpoint
|
|
300
|
+
|
|
301
|
+
# Check if params has a checkpoint field (model ID)
|
|
302
|
+
checkpoint_id = getattr(self.params, 'checkpoint', None)
|
|
303
|
+
if checkpoint_id is None:
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
# Fetch model from backend
|
|
307
|
+
model = self.get_model(checkpoint_id)
|
|
308
|
+
|
|
309
|
+
# The model['file'] is downloaded by the client's url_conversion
|
|
310
|
+
model_file = Path(model['file'])
|
|
311
|
+
|
|
312
|
+
# Extract to temp path
|
|
313
|
+
output_path = get_temp_path(f'models/{model_file.stem}')
|
|
314
|
+
if not output_path.exists():
|
|
315
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
316
|
+
extract_archive(model_file, output_path)
|
|
317
|
+
|
|
318
|
+
# Determine category - base models vs fine-tuned
|
|
319
|
+
category = model.get('category') or 'base'
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
'category': category,
|
|
323
|
+
'path': output_path,
|
|
324
|
+
'id': model.get('id'),
|
|
325
|
+
'name': model.get('name'),
|
|
326
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Train context for sharing state between workflow steps."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from synapse_sdk.plugins.steps import BaseStepContext
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class TrainContext(BaseStepContext):
|
|
16
|
+
"""Shared context passed between training workflow steps.
|
|
17
|
+
|
|
18
|
+
Extends BaseStepContext with training-specific state fields.
|
|
19
|
+
Carries parameters and accumulated state as the workflow
|
|
20
|
+
progresses through steps.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
params: Training parameters (from action params).
|
|
24
|
+
dataset: Loaded dataset (populated by dataset step).
|
|
25
|
+
model_path: Path to trained model (populated by training step).
|
|
26
|
+
model: Created model metadata (populated by upload step).
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
>>> context = TrainContext(
|
|
30
|
+
... runtime_ctx=runtime_ctx,
|
|
31
|
+
... params={'dataset_id': 1, 'epochs': 10},
|
|
32
|
+
... )
|
|
33
|
+
>>> # Steps populate state as they execute
|
|
34
|
+
>>> context.dataset = loaded_dataset
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
# Training parameters
|
|
38
|
+
params: dict[str, Any] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
# Processing state (populated by steps)
|
|
41
|
+
dataset: Any | None = None
|
|
42
|
+
model_path: str | None = None
|
|
43
|
+
model: dict[str, Any] | None = None
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def client(self) -> BackendClient:
|
|
47
|
+
"""Backend client from runtime context.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
BackendClient instance.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
RuntimeError: If no client in runtime context.
|
|
54
|
+
"""
|
|
55
|
+
if self.runtime_ctx.client is None:
|
|
56
|
+
raise RuntimeError('No client in runtime context')
|
|
57
|
+
return self.runtime_ctx.client
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Upload action module with workflow step support.
|
|
2
|
+
|
|
3
|
+
Provides a full step-based workflow system for upload actions:
|
|
4
|
+
- BaseUploadAction: Base class for upload workflows
|
|
5
|
+
- UploadContext: Upload-specific context extending BaseStepContext
|
|
6
|
+
- UploadProgressCategories: Standard progress category names
|
|
7
|
+
|
|
8
|
+
For step infrastructure (BaseStep, StepRegistry, Orchestrator),
|
|
9
|
+
use the steps module:
|
|
10
|
+
from synapse_sdk.plugins.steps import BaseStep, StepRegistry
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
>>> from synapse_sdk.plugins.steps import BaseStep, StepResult
|
|
14
|
+
>>> from synapse_sdk.plugins.actions.upload import (
|
|
15
|
+
... BaseUploadAction,
|
|
16
|
+
... UploadContext,
|
|
17
|
+
... )
|
|
18
|
+
>>>
|
|
19
|
+
>>> class InitStep(BaseStep[UploadContext]):
|
|
20
|
+
... @property
|
|
21
|
+
... def name(self) -> str:
|
|
22
|
+
... return 'initialize'
|
|
23
|
+
...
|
|
24
|
+
... @property
|
|
25
|
+
... def progress_weight(self) -> float:
|
|
26
|
+
... return 0.1
|
|
27
|
+
...
|
|
28
|
+
... def execute(self, context: UploadContext) -> StepResult:
|
|
29
|
+
... # Initialize storage, validate params
|
|
30
|
+
... return StepResult(success=True)
|
|
31
|
+
>>>
|
|
32
|
+
>>> class MyUploadAction(BaseUploadAction[MyParams]):
|
|
33
|
+
... def setup_steps(self, registry) -> None:
|
|
34
|
+
... registry.register(InitStep())
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from synapse_sdk.plugins.actions.upload.action import (
|
|
38
|
+
BaseUploadAction,
|
|
39
|
+
UploadProgressCategories,
|
|
40
|
+
)
|
|
41
|
+
from synapse_sdk.plugins.actions.upload.context import UploadContext
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
# Action
|
|
45
|
+
'BaseUploadAction',
|
|
46
|
+
'UploadProgressCategories',
|
|
47
|
+
# Context
|
|
48
|
+
'UploadContext',
|
|
49
|
+
]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Upload action base class with workflow step support."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from synapse_sdk.plugins.action import BaseAction
|
|
10
|
+
from synapse_sdk.plugins.actions.upload.context import UploadContext
|
|
11
|
+
from synapse_sdk.plugins.enums import PluginCategory
|
|
12
|
+
from synapse_sdk.plugins.steps import Orchestrator, StepRegistry
|
|
13
|
+
|
|
14
|
+
P = TypeVar('P', bound=BaseModel)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class UploadProgressCategories:
|
|
21
|
+
"""Standard progress category names for upload workflows.
|
|
22
|
+
|
|
23
|
+
.. deprecated::
|
|
24
|
+
With automatic category inference from step names (US2),
|
|
25
|
+
explicit category constants are less necessary. Step names
|
|
26
|
+
are now automatically used as progress/metrics categories.
|
|
27
|
+
|
|
28
|
+
Use these constants with set_progress() to track upload phases:
|
|
29
|
+
- INITIALIZE: Storage and path setup
|
|
30
|
+
- VALIDATE: File validation
|
|
31
|
+
- UPLOAD: File upload to storage
|
|
32
|
+
- CLEANUP: Post-upload cleanup
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
>>> self.set_progress(1, 4, self.progress.INITIALIZE)
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
INITIALIZE: str = 'initialize'
|
|
39
|
+
VALIDATE: str = 'validate'
|
|
40
|
+
UPLOAD: str = 'upload'
|
|
41
|
+
CLEANUP: str = 'cleanup'
|
|
42
|
+
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
"""Initialize with deprecation warning."""
|
|
45
|
+
import warnings
|
|
46
|
+
|
|
47
|
+
warnings.warn(
|
|
48
|
+
'UploadProgressCategories is deprecated. '
|
|
49
|
+
'Step names are now automatically used as progress/metrics categories. '
|
|
50
|
+
'Consider using step.name directly or omitting the category parameter.',
|
|
51
|
+
DeprecationWarning,
|
|
52
|
+
stacklevel=2,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class BaseUploadAction(BaseAction[P]):
|
|
57
|
+
"""Base class for upload actions with workflow step support.
|
|
58
|
+
|
|
59
|
+
Provides a full step-based workflow system:
|
|
60
|
+
- Override setup_steps() to register custom steps
|
|
61
|
+
- Steps execute in order with automatic rollback on failure
|
|
62
|
+
- Progress tracked across all steps based on weights
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
category: Plugin category (defaults to UPLOAD).
|
|
66
|
+
progress: Standard progress category names.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
>>> class MyUploadAction(BaseUploadAction[MyParams]):
|
|
70
|
+
... action_name = 'upload'
|
|
71
|
+
... params_model = MyParams
|
|
72
|
+
...
|
|
73
|
+
... def setup_steps(self, registry: StepRegistry) -> None:
|
|
74
|
+
... registry.register(InitializeStep())
|
|
75
|
+
... registry.register(ValidateStep())
|
|
76
|
+
... registry.register(UploadFilesStep())
|
|
77
|
+
... registry.register(CleanupStep())
|
|
78
|
+
>>>
|
|
79
|
+
>>> # Steps are executed in order with automatic rollback on failure
|
|
80
|
+
>>> # Progress is tracked based on step weights
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
category = PluginCategory.UPLOAD
|
|
84
|
+
progress = UploadProgressCategories()
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def client(self) -> BackendClient:
|
|
88
|
+
"""Backend client from context.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
BackendClient instance.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
RuntimeError: If no client in context.
|
|
95
|
+
"""
|
|
96
|
+
if self.ctx.client is None:
|
|
97
|
+
raise RuntimeError('No client in context. Provide a client via RuntimeContext.')
|
|
98
|
+
return self.ctx.client
|
|
99
|
+
|
|
100
|
+
def setup_steps(self, registry: StepRegistry[UploadContext]) -> None:
|
|
101
|
+
"""Register workflow steps.
|
|
102
|
+
|
|
103
|
+
Override this method to register custom steps for your upload workflow.
|
|
104
|
+
Steps are executed in registration order.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
registry: StepRegistry to register steps with.
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
>>> def setup_steps(self, registry: StepRegistry[UploadContext]) -> None:
|
|
111
|
+
... registry.register(InitializeStep())
|
|
112
|
+
... registry.register(ValidateStep())
|
|
113
|
+
... registry.register(UploadFilesStep())
|
|
114
|
+
"""
|
|
115
|
+
pass # Subclasses override to add steps
|
|
116
|
+
|
|
117
|
+
def create_context(self) -> UploadContext:
|
|
118
|
+
"""Create upload context for the workflow.
|
|
119
|
+
|
|
120
|
+
Override to customize context creation or add additional state.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
UploadContext instance with params and runtime context.
|
|
124
|
+
"""
|
|
125
|
+
params_dict = self.params.model_dump() if hasattr(self.params, 'model_dump') else dict(self.params)
|
|
126
|
+
return UploadContext(
|
|
127
|
+
params=params_dict,
|
|
128
|
+
runtime_ctx=self.ctx,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def execute(self) -> dict[str, Any]:
|
|
132
|
+
"""Execute the upload workflow.
|
|
133
|
+
|
|
134
|
+
Creates registry, registers steps via setup_steps(), creates context,
|
|
135
|
+
and runs the orchestrator.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Dict with success status and workflow results.
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
RuntimeError: If no steps registered or a step fails.
|
|
142
|
+
"""
|
|
143
|
+
# Setup
|
|
144
|
+
registry: StepRegistry[UploadContext] = StepRegistry()
|
|
145
|
+
self.setup_steps(registry)
|
|
146
|
+
|
|
147
|
+
if not registry:
|
|
148
|
+
raise RuntimeError('No steps registered. Override setup_steps() to register workflow steps.')
|
|
149
|
+
|
|
150
|
+
# Create context and orchestrator
|
|
151
|
+
context = self.create_context()
|
|
152
|
+
orchestrator: Orchestrator[UploadContext] = Orchestrator(
|
|
153
|
+
registry=registry,
|
|
154
|
+
context=context,
|
|
155
|
+
progress_callback=lambda curr, total: self.set_progress(curr, total),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Execute workflow
|
|
159
|
+
result = orchestrator.execute()
|
|
160
|
+
|
|
161
|
+
# Add upload-specific result data
|
|
162
|
+
result['uploaded_files'] = len(context.uploaded_files)
|
|
163
|
+
result['data_units'] = len(context.data_units)
|
|
164
|
+
|
|
165
|
+
return result
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Upload context for sharing state between workflow steps."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from synapse_sdk.plugins.steps import BaseStepContext
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class UploadContext(BaseStepContext):
|
|
16
|
+
"""Shared context passed between upload workflow steps.
|
|
17
|
+
|
|
18
|
+
Extends BaseStepContext with upload-specific state fields.
|
|
19
|
+
Carries parameters and accumulated state as the workflow
|
|
20
|
+
progresses through steps.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
params: Upload parameters (from action params).
|
|
24
|
+
storage: Storage configuration (populated by init step).
|
|
25
|
+
pathlib_cwd: Working directory path (populated by init step).
|
|
26
|
+
organized_files: Files organized for upload (populated by organize step).
|
|
27
|
+
uploaded_files: Successfully uploaded files (populated by upload step).
|
|
28
|
+
data_units: Created data units (populated by generate step).
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> context = UploadContext(
|
|
32
|
+
... runtime_ctx=runtime_ctx,
|
|
33
|
+
... params={'storage': 1, 'path': '/data'},
|
|
34
|
+
... )
|
|
35
|
+
>>> # Steps populate state as they execute
|
|
36
|
+
>>> context.organized_files.append({'path': 'file1.jpg'})
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# Upload parameters
|
|
40
|
+
params: dict[str, Any] = field(default_factory=dict)
|
|
41
|
+
|
|
42
|
+
# Processing state (populated by steps)
|
|
43
|
+
storage: Any | None = None
|
|
44
|
+
pathlib_cwd: Any | None = None
|
|
45
|
+
organized_files: list[dict[str, Any]] = field(default_factory=list)
|
|
46
|
+
uploaded_files: list[dict[str, Any]] = field(default_factory=list)
|
|
47
|
+
data_units: list[dict[str, Any]] = field(default_factory=list)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def client(self) -> BackendClient:
|
|
51
|
+
"""Backend client from runtime context.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
BackendClient instance.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
RuntimeError: If no client in runtime context.
|
|
58
|
+
"""
|
|
59
|
+
if self.runtime_ctx.client is None:
|
|
60
|
+
raise RuntimeError('No client in runtime context')
|
|
61
|
+
return self.runtime_ctx.client
|