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,656 @@
|
|
|
1
|
+
# Step Implementation Guide
|
|
2
|
+
|
|
3
|
+
Step-based workflows enable complex multi-phase operations with progress tracking, conditional execution, and automatic rollback on failure.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Steps are composable building blocks for workflows. Each step:
|
|
8
|
+
- Has a unique name and progress weight
|
|
9
|
+
- Executes independently with shared context
|
|
10
|
+
- Can be skipped conditionally
|
|
11
|
+
- Supports rollback on workflow failure
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
flowchart LR
|
|
15
|
+
subgraph Workflow["Step-Based Workflow"]
|
|
16
|
+
S1["Step 1<br/>weight: 0.2"] --> S2["Step 2<br/>weight: 0.5"]
|
|
17
|
+
S2 --> S3["Step 3<br/>weight: 0.3"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Context["Shared Context"] -.-> S1
|
|
21
|
+
Context -.-> S2
|
|
22
|
+
Context -.-> S3
|
|
23
|
+
|
|
24
|
+
style Workflow fill:#e1f5fe
|
|
25
|
+
style Context fill:#fff3e0
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Core Components
|
|
31
|
+
|
|
32
|
+
### BaseStep[C]
|
|
33
|
+
|
|
34
|
+
Location: `synapse_sdk/plugins/steps/base.py`
|
|
35
|
+
|
|
36
|
+
Abstract base class for all workflow steps. Generic type `C` must extend `BaseStepContext`.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from abc import ABC, abstractmethod
|
|
40
|
+
from synapse_sdk.plugins.steps import BaseStep, StepResult
|
|
41
|
+
|
|
42
|
+
class BaseStep[C: BaseStepContext](ABC):
|
|
43
|
+
@property
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def name(self) -> str:
|
|
46
|
+
"""Unique step identifier."""
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def progress_weight(self) -> float:
|
|
52
|
+
"""Weight for progress calculation (0.0-1.0)."""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def execute(self, context: C) -> StepResult:
|
|
57
|
+
"""Execute the step logic."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
def can_skip(self, context: C) -> bool:
|
|
61
|
+
"""Optional: Return True to skip this step."""
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
def rollback(self, context: C, result: StepResult) -> None:
|
|
65
|
+
"""Optional: Cleanup when a later step fails."""
|
|
66
|
+
pass
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### StepResult
|
|
70
|
+
|
|
71
|
+
Location: `synapse_sdk/plugins/steps/base.py`
|
|
72
|
+
|
|
73
|
+
Dataclass representing step execution outcome.
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from dataclasses import dataclass, field
|
|
77
|
+
from datetime import datetime
|
|
78
|
+
from typing import Any
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class StepResult:
|
|
82
|
+
success: bool = True # Did step complete successfully?
|
|
83
|
+
data: dict[str, Any] = field(default_factory=dict) # Output data
|
|
84
|
+
error: str | None = None # Error message if failed
|
|
85
|
+
rollback_data: dict[str, Any] = field(default_factory=dict) # Data for rollback
|
|
86
|
+
skipped: bool = False # Was step skipped?
|
|
87
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### BaseStepContext
|
|
91
|
+
|
|
92
|
+
Location: `synapse_sdk/plugins/steps/context.py`
|
|
93
|
+
|
|
94
|
+
Base class for shared state between steps.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from dataclasses import dataclass, field
|
|
98
|
+
from synapse_sdk.plugins.context import RuntimeContext
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class BaseStepContext:
|
|
102
|
+
runtime_ctx: RuntimeContext # Logger, env, clients
|
|
103
|
+
step_results: list[StepResult] = field(default_factory=list)
|
|
104
|
+
errors: list[str] = field(default_factory=list)
|
|
105
|
+
current_step: str | None = None # Set by Orchestrator
|
|
106
|
+
|
|
107
|
+
def log(self, event: str, data: dict, file: str | None = None) -> None:
|
|
108
|
+
"""Log an event via runtime context."""
|
|
109
|
+
self.runtime_ctx.log(event, data, file)
|
|
110
|
+
|
|
111
|
+
def set_progress(self, current: int, total: int, category: str | None = None) -> None:
|
|
112
|
+
"""Set progress. Uses current_step as category if not provided."""
|
|
113
|
+
effective_category = category or self.current_step
|
|
114
|
+
self.runtime_ctx.set_progress(current, total, effective_category)
|
|
115
|
+
|
|
116
|
+
def set_metrics(self, value: dict, category: str | None = None) -> None:
|
|
117
|
+
"""Set metrics. Uses current_step as category if not provided."""
|
|
118
|
+
effective_category = category or self.current_step
|
|
119
|
+
self.runtime_ctx.set_metrics(value, effective_category)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### StepRegistry[C]
|
|
123
|
+
|
|
124
|
+
Location: `synapse_sdk/plugins/steps/registry.py`
|
|
125
|
+
|
|
126
|
+
Manages ordered collection of steps.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from synapse_sdk.plugins.steps import StepRegistry, BaseStep
|
|
130
|
+
|
|
131
|
+
registry = StepRegistry[MyContext]()
|
|
132
|
+
|
|
133
|
+
# Register steps in order
|
|
134
|
+
registry.register(Step1())
|
|
135
|
+
registry.register(Step2())
|
|
136
|
+
registry.register(Step3())
|
|
137
|
+
|
|
138
|
+
# Dynamic insertion
|
|
139
|
+
registry.insert_after('step1', NewStep()) # Insert after step1
|
|
140
|
+
registry.insert_before('step3', NewStep()) # Insert before step3
|
|
141
|
+
|
|
142
|
+
# Remove step
|
|
143
|
+
registry.unregister('step_name')
|
|
144
|
+
|
|
145
|
+
# Access
|
|
146
|
+
steps = registry.get_steps() # Returns copy of step list
|
|
147
|
+
total = registry.total_weight # Sum of all progress weights
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Orchestrator[C]
|
|
151
|
+
|
|
152
|
+
Location: `synapse_sdk/plugins/steps/orchestrator.py`
|
|
153
|
+
|
|
154
|
+
Executes steps with progress tracking and automatic rollback.
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from synapse_sdk.plugins.steps import Orchestrator, StepRegistry
|
|
158
|
+
|
|
159
|
+
orchestrator = Orchestrator(
|
|
160
|
+
registry=registry,
|
|
161
|
+
context=context,
|
|
162
|
+
progress_callback=lambda curr, total: print(f"{curr}%"),
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
result = orchestrator.execute()
|
|
166
|
+
# Returns: {'success': True, 'steps_executed': 3, 'steps_total': 3}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Creating Custom Steps
|
|
172
|
+
|
|
173
|
+
### Step 1: Define Context
|
|
174
|
+
|
|
175
|
+
Create a context class with fields for shared state:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from dataclasses import dataclass, field
|
|
179
|
+
from synapse_sdk.plugins.steps import BaseStepContext
|
|
180
|
+
|
|
181
|
+
@dataclass
|
|
182
|
+
class ImageProcessingContext(BaseStepContext):
|
|
183
|
+
"""Context for image processing workflow."""
|
|
184
|
+
|
|
185
|
+
# Input parameters
|
|
186
|
+
input_dir: str = ''
|
|
187
|
+
output_dir: str = ''
|
|
188
|
+
resize_width: int = 800
|
|
189
|
+
|
|
190
|
+
# Shared state populated by steps
|
|
191
|
+
image_paths: list[str] = field(default_factory=list)
|
|
192
|
+
processed_images: list[str] = field(default_factory=list)
|
|
193
|
+
failed_images: list[str] = field(default_factory=list)
|
|
194
|
+
total_bytes_saved: int = 0
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Step 2: Implement Steps
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import os
|
|
201
|
+
from synapse_sdk.plugins.steps import BaseStep, StepResult
|
|
202
|
+
|
|
203
|
+
class DiscoverImagesStep(BaseStep[ImageProcessingContext]):
|
|
204
|
+
"""Find all images in input directory."""
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def name(self) -> str:
|
|
208
|
+
return 'discover_images'
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def progress_weight(self) -> float:
|
|
212
|
+
return 0.1 # 10% of workflow
|
|
213
|
+
|
|
214
|
+
def execute(self, context: ImageProcessingContext) -> StepResult:
|
|
215
|
+
# Find images
|
|
216
|
+
images = []
|
|
217
|
+
for file in os.listdir(context.input_dir):
|
|
218
|
+
if file.lower().endswith(('.jpg', '.jpeg', '.png')):
|
|
219
|
+
images.append(os.path.join(context.input_dir, file))
|
|
220
|
+
|
|
221
|
+
if not images:
|
|
222
|
+
return StepResult(
|
|
223
|
+
success=False,
|
|
224
|
+
error=f"No images found in {context.input_dir}",
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Store in context for next steps
|
|
228
|
+
context.image_paths = images
|
|
229
|
+
|
|
230
|
+
return StepResult(
|
|
231
|
+
success=True,
|
|
232
|
+
data={'image_count': len(images)},
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class ResizeImagesStep(BaseStep[ImageProcessingContext]):
|
|
237
|
+
"""Resize all discovered images."""
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def name(self) -> str:
|
|
241
|
+
return 'resize_images'
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def progress_weight(self) -> float:
|
|
245
|
+
return 0.7 # 70% of workflow
|
|
246
|
+
|
|
247
|
+
def execute(self, context: ImageProcessingContext) -> StepResult:
|
|
248
|
+
processed = []
|
|
249
|
+
failed = []
|
|
250
|
+
bytes_saved = 0
|
|
251
|
+
|
|
252
|
+
for i, path in enumerate(context.image_paths):
|
|
253
|
+
# Report progress within step
|
|
254
|
+
context.set_progress(i + 1, len(context.image_paths))
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
output_path, saved = self._resize_image(
|
|
258
|
+
path,
|
|
259
|
+
context.output_dir,
|
|
260
|
+
context.resize_width,
|
|
261
|
+
)
|
|
262
|
+
processed.append(output_path)
|
|
263
|
+
bytes_saved += saved
|
|
264
|
+
except Exception as e:
|
|
265
|
+
failed.append(path)
|
|
266
|
+
context.log('resize_failed', {'path': path, 'error': str(e)})
|
|
267
|
+
|
|
268
|
+
# Update context
|
|
269
|
+
context.processed_images = processed
|
|
270
|
+
context.failed_images = failed
|
|
271
|
+
context.total_bytes_saved = bytes_saved
|
|
272
|
+
|
|
273
|
+
return StepResult(
|
|
274
|
+
success=True,
|
|
275
|
+
data={
|
|
276
|
+
'processed': len(processed),
|
|
277
|
+
'failed': len(failed),
|
|
278
|
+
'bytes_saved': bytes_saved,
|
|
279
|
+
},
|
|
280
|
+
rollback_data={'output_files': processed}, # For cleanup
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
def rollback(self, context: ImageProcessingContext, result: StepResult) -> None:
|
|
284
|
+
"""Delete processed files on failure."""
|
|
285
|
+
for path in result.rollback_data.get('output_files', []):
|
|
286
|
+
try:
|
|
287
|
+
os.remove(path)
|
|
288
|
+
except OSError:
|
|
289
|
+
context.errors.append(f"Failed to delete: {path}")
|
|
290
|
+
|
|
291
|
+
def _resize_image(self, path: str, output_dir: str, width: int) -> tuple[str, int]:
|
|
292
|
+
# Image resizing implementation
|
|
293
|
+
...
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class GenerateReportStep(BaseStep[ImageProcessingContext]):
|
|
297
|
+
"""Generate summary report."""
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def name(self) -> str:
|
|
301
|
+
return 'generate_report'
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def progress_weight(self) -> float:
|
|
305
|
+
return 0.2 # 20% of workflow
|
|
306
|
+
|
|
307
|
+
def can_skip(self, context: ImageProcessingContext) -> bool:
|
|
308
|
+
# Skip if nothing was processed
|
|
309
|
+
return len(context.processed_images) == 0
|
|
310
|
+
|
|
311
|
+
def execute(self, context: ImageProcessingContext) -> StepResult:
|
|
312
|
+
report = {
|
|
313
|
+
'total_images': len(context.image_paths),
|
|
314
|
+
'processed': len(context.processed_images),
|
|
315
|
+
'failed': len(context.failed_images),
|
|
316
|
+
'bytes_saved': context.total_bytes_saved,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
# Log report
|
|
320
|
+
context.log('processing_complete', report)
|
|
321
|
+
|
|
322
|
+
return StepResult(success=True, data=report)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Step 3: Run Workflow
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
from synapse_sdk.plugins.steps import StepRegistry, Orchestrator
|
|
329
|
+
|
|
330
|
+
def process_images(runtime_ctx, input_dir: str, output_dir: str) -> dict:
|
|
331
|
+
# Create registry and register steps
|
|
332
|
+
registry = StepRegistry[ImageProcessingContext]()
|
|
333
|
+
registry.register(DiscoverImagesStep())
|
|
334
|
+
registry.register(ResizeImagesStep())
|
|
335
|
+
registry.register(GenerateReportStep())
|
|
336
|
+
|
|
337
|
+
# Create context
|
|
338
|
+
context = ImageProcessingContext(
|
|
339
|
+
runtime_ctx=runtime_ctx,
|
|
340
|
+
input_dir=input_dir,
|
|
341
|
+
output_dir=output_dir,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Execute with progress callback
|
|
345
|
+
def on_progress(current: int, total: int):
|
|
346
|
+
runtime_ctx.set_progress(current, total, category='overall')
|
|
347
|
+
|
|
348
|
+
orchestrator = Orchestrator(
|
|
349
|
+
registry=registry,
|
|
350
|
+
context=context,
|
|
351
|
+
progress_callback=on_progress,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
try:
|
|
355
|
+
result = orchestrator.execute()
|
|
356
|
+
return {
|
|
357
|
+
'success': True,
|
|
358
|
+
'processed': len(context.processed_images),
|
|
359
|
+
'failed': len(context.failed_images),
|
|
360
|
+
}
|
|
361
|
+
except RuntimeError as e:
|
|
362
|
+
return {
|
|
363
|
+
'success': False,
|
|
364
|
+
'error': str(e),
|
|
365
|
+
'errors': context.errors,
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Orchestrator Execution Flow
|
|
372
|
+
|
|
373
|
+
```mermaid
|
|
374
|
+
flowchart TD
|
|
375
|
+
A["orchestrator.execute()"] --> B["For each step in registry"]
|
|
376
|
+
B --> C["Set context.current_step = step.name"]
|
|
377
|
+
C --> D{"step.can_skip(context)?"}
|
|
378
|
+
|
|
379
|
+
D -->|Yes| E["Create skipped StepResult"]
|
|
380
|
+
D -->|No| F["Try: result = step.execute(context)"]
|
|
381
|
+
|
|
382
|
+
F -->|Exception| G["result = StepResult(success=False, error)"]
|
|
383
|
+
F -->|Success| H["result from execute()"]
|
|
384
|
+
|
|
385
|
+
E --> I["Append result to context.step_results"]
|
|
386
|
+
G --> I
|
|
387
|
+
H --> I
|
|
388
|
+
|
|
389
|
+
I --> J{"result.success?"}
|
|
390
|
+
|
|
391
|
+
J -->|No| K["_rollback() in reverse order"]
|
|
392
|
+
K --> L["Raise RuntimeError"]
|
|
393
|
+
|
|
394
|
+
J -->|Yes| M["Update progress via callback"]
|
|
395
|
+
M --> N{"More steps?"}
|
|
396
|
+
|
|
397
|
+
N -->|Yes| B
|
|
398
|
+
N -->|No| O["Clear context.current_step"]
|
|
399
|
+
O --> P["Return success summary"]
|
|
400
|
+
|
|
401
|
+
style A fill:#e8f5e9
|
|
402
|
+
style P fill:#e8f5e9
|
|
403
|
+
style L fill:#ffcdd2
|
|
404
|
+
style D fill:#fff9c4
|
|
405
|
+
style J fill:#fff9c4
|
|
406
|
+
style N fill:#fff9c4
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Key Behaviors
|
|
410
|
+
|
|
411
|
+
1. **Sequential Execution**: Steps run in registration order
|
|
412
|
+
2. **Auto-Category**: `context.current_step` is set automatically for logging/metrics
|
|
413
|
+
3. **Progress Calculation**: Based on cumulative step weights
|
|
414
|
+
4. **Automatic Rollback**: On failure, `rollback()` called in reverse order
|
|
415
|
+
5. **Best-Effort Rollback**: Rollback errors logged but don't stop other rollbacks
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Utility Steps
|
|
420
|
+
|
|
421
|
+
### LoggingStep
|
|
422
|
+
|
|
423
|
+
Wraps any step with start/end logging:
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
from synapse_sdk.plugins.steps.utils import LoggingStep
|
|
427
|
+
|
|
428
|
+
# Wrap a step
|
|
429
|
+
logged_step = LoggingStep(ResizeImagesStep())
|
|
430
|
+
|
|
431
|
+
# Logs:
|
|
432
|
+
# - step_start: {step: 'logged_resize_images'}
|
|
433
|
+
# - step_end: {step: 'logged_resize_images', elapsed: 1.23, success: True}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### TimingStep
|
|
437
|
+
|
|
438
|
+
Wraps any step to measure execution duration:
|
|
439
|
+
|
|
440
|
+
```python
|
|
441
|
+
from synapse_sdk.plugins.steps.utils import TimingStep
|
|
442
|
+
|
|
443
|
+
timed_step = TimingStep(ResizeImagesStep())
|
|
444
|
+
|
|
445
|
+
# Result includes:
|
|
446
|
+
# result.data['duration_seconds'] = 1.234567
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### ValidationStep
|
|
450
|
+
|
|
451
|
+
Validates context state before proceeding:
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
from synapse_sdk.plugins.steps.utils import ValidationStep
|
|
455
|
+
|
|
456
|
+
def validate_input(context: ImageProcessingContext) -> tuple[bool, str | None]:
|
|
457
|
+
if not context.input_dir:
|
|
458
|
+
return False, "input_dir is required"
|
|
459
|
+
if not os.path.isdir(context.input_dir):
|
|
460
|
+
return False, f"Directory not found: {context.input_dir}"
|
|
461
|
+
return True, None
|
|
462
|
+
|
|
463
|
+
validate_step = ValidationStep(
|
|
464
|
+
validator=validate_input,
|
|
465
|
+
name='validate_input',
|
|
466
|
+
progress_weight=0.05,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
registry.register(validate_step)
|
|
470
|
+
registry.register(DiscoverImagesStep())
|
|
471
|
+
# ...
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Composing Utility Steps
|
|
475
|
+
|
|
476
|
+
```python
|
|
477
|
+
from synapse_sdk.plugins.steps.utils import LoggingStep, TimingStep
|
|
478
|
+
|
|
479
|
+
# Combine wrappers
|
|
480
|
+
step = LoggingStep(TimingStep(ResizeImagesStep()))
|
|
481
|
+
|
|
482
|
+
# Result: logged + timed step
|
|
483
|
+
# Logs start/end AND measures duration
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Integration with Actions
|
|
489
|
+
|
|
490
|
+
### BaseTrainAction Example
|
|
491
|
+
|
|
492
|
+
```python
|
|
493
|
+
from synapse_sdk.plugins.actions.train import BaseTrainAction, TrainContext
|
|
494
|
+
from synapse_sdk.plugins.steps import StepRegistry
|
|
495
|
+
|
|
496
|
+
class MyTrainAction(BaseTrainAction[TrainParams]):
|
|
497
|
+
def setup_steps(self, registry: StepRegistry[TrainContext]) -> None:
|
|
498
|
+
"""Override to register training steps."""
|
|
499
|
+
registry.register(PrepareDatasetStep())
|
|
500
|
+
registry.register(InitializeModelStep())
|
|
501
|
+
registry.register(TrainModelStep())
|
|
502
|
+
registry.register(SaveCheckpointStep())
|
|
503
|
+
registry.register(UploadModelStep())
|
|
504
|
+
|
|
505
|
+
def execute(self) -> dict:
|
|
506
|
+
# BaseTrainAction handles orchestration
|
|
507
|
+
return super().execute()
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### BaseUploadAction Example
|
|
511
|
+
|
|
512
|
+
```python
|
|
513
|
+
from synapse_sdk.plugins.actions.upload import BaseUploadAction, UploadContext
|
|
514
|
+
from synapse_sdk.plugins.steps import StepRegistry
|
|
515
|
+
|
|
516
|
+
class MyUploadAction(BaseUploadAction[UploadParams]):
|
|
517
|
+
def setup_steps(self, registry: StepRegistry[UploadContext]) -> None:
|
|
518
|
+
registry.register(ValidateFilesStep())
|
|
519
|
+
registry.register(OrganizeFilesStep())
|
|
520
|
+
registry.register(UploadFilesStep())
|
|
521
|
+
registry.register(VerifyUploadStep())
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Progress Weight Guidelines
|
|
527
|
+
|
|
528
|
+
Progress weights should reflect **perceived duration** for better UX:
|
|
529
|
+
|
|
530
|
+
| Step Type | Typical Weight | Notes |
|
|
531
|
+
|-----------|---------------|-------|
|
|
532
|
+
| Validation | 0.05 - 0.10 | Quick checks |
|
|
533
|
+
| Data loading | 0.10 - 0.20 | I/O bound |
|
|
534
|
+
| Processing | 0.50 - 0.80 | Main work |
|
|
535
|
+
| Cleanup/Report | 0.05 - 0.10 | Finalization |
|
|
536
|
+
|
|
537
|
+
**Example Distribution:**
|
|
538
|
+
|
|
539
|
+
```python
|
|
540
|
+
class ValidateStep(BaseStep):
|
|
541
|
+
progress_weight = 0.05 # 5%
|
|
542
|
+
|
|
543
|
+
class LoadDataStep(BaseStep):
|
|
544
|
+
progress_weight = 0.15 # 15%
|
|
545
|
+
|
|
546
|
+
class ProcessStep(BaseStep):
|
|
547
|
+
progress_weight = 0.70 # 70%
|
|
548
|
+
|
|
549
|
+
class SaveResultsStep(BaseStep):
|
|
550
|
+
progress_weight = 0.10 # 10%
|
|
551
|
+
# Total: 1.0 (100%)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Best Practices
|
|
557
|
+
|
|
558
|
+
### Context Design
|
|
559
|
+
|
|
560
|
+
```python
|
|
561
|
+
# Good: Clear field purposes
|
|
562
|
+
@dataclass
|
|
563
|
+
class GoodContext(BaseStepContext):
|
|
564
|
+
# Input (set at creation)
|
|
565
|
+
config_path: str
|
|
566
|
+
|
|
567
|
+
# Intermediate state (populated by steps)
|
|
568
|
+
loaded_data: dict = field(default_factory=dict)
|
|
569
|
+
|
|
570
|
+
# Output (final results)
|
|
571
|
+
results: list = field(default_factory=list)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Error Handling
|
|
575
|
+
|
|
576
|
+
```python
|
|
577
|
+
def execute(self, context: MyContext) -> StepResult:
|
|
578
|
+
try:
|
|
579
|
+
result = self._do_work(context)
|
|
580
|
+
return StepResult(success=True, data=result)
|
|
581
|
+
except ValidationError as e:
|
|
582
|
+
# Expected error - return failure
|
|
583
|
+
return StepResult(success=False, error=str(e))
|
|
584
|
+
except Exception as e:
|
|
585
|
+
# Unexpected error - log and return failure
|
|
586
|
+
context.log('unexpected_error', {'error': str(e)})
|
|
587
|
+
return StepResult(success=False, error=f"Unexpected: {e}")
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Rollback Design
|
|
591
|
+
|
|
592
|
+
```python
|
|
593
|
+
def execute(self, context: MyContext) -> StepResult:
|
|
594
|
+
created_files = []
|
|
595
|
+
|
|
596
|
+
for item in context.items:
|
|
597
|
+
path = self._create_file(item)
|
|
598
|
+
created_files.append(path)
|
|
599
|
+
|
|
600
|
+
return StepResult(
|
|
601
|
+
success=True,
|
|
602
|
+
rollback_data={'files': created_files}, # Save for cleanup
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
def rollback(self, context: MyContext, result: StepResult) -> None:
|
|
606
|
+
for path in result.rollback_data.get('files', []):
|
|
607
|
+
try:
|
|
608
|
+
os.remove(path)
|
|
609
|
+
except OSError as e:
|
|
610
|
+
# Log but continue rollback
|
|
611
|
+
context.errors.append(f"Rollback failed for {path}: {e}")
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### Step Independence
|
|
615
|
+
|
|
616
|
+
```python
|
|
617
|
+
# Good: Step reads from context, writes to context
|
|
618
|
+
class ProcessStep(BaseStep[MyContext]):
|
|
619
|
+
def execute(self, context: MyContext) -> StepResult:
|
|
620
|
+
# Read from context (set by previous step)
|
|
621
|
+
data = context.loaded_data
|
|
622
|
+
|
|
623
|
+
# Process
|
|
624
|
+
results = self._process(data)
|
|
625
|
+
|
|
626
|
+
# Write to context (for next step)
|
|
627
|
+
context.results = results
|
|
628
|
+
|
|
629
|
+
return StepResult(success=True)
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## File Structure
|
|
635
|
+
|
|
636
|
+
```
|
|
637
|
+
synapse_sdk/plugins/steps/
|
|
638
|
+
├── __init__.py # Public exports
|
|
639
|
+
├── base.py # BaseStep, StepResult
|
|
640
|
+
├── context.py # BaseStepContext
|
|
641
|
+
├── registry.py # StepRegistry
|
|
642
|
+
├── orchestrator.py # Orchestrator
|
|
643
|
+
└── utils/
|
|
644
|
+
├── __init__.py
|
|
645
|
+
├── logging.py # LoggingStep
|
|
646
|
+
├── timing.py # TimingStep
|
|
647
|
+
└── validation.py # ValidationStep
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
## Related Documentation
|
|
653
|
+
|
|
654
|
+
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - Overall plugin system architecture
|
|
655
|
+
- **[OVERVIEW.md](OVERVIEW.md)** - Getting started with plugins
|
|
656
|
+
- **[README.md](README.md)** - Quick reference and extension guide
|