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.

Files changed (261) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +9 -8
  3. synapse_sdk/cli/agent/__init__.py +25 -0
  4. synapse_sdk/cli/agent/config.py +104 -0
  5. synapse_sdk/cli/agent/select.py +197 -0
  6. synapse_sdk/cli/auth.py +104 -0
  7. synapse_sdk/cli/main.py +1025 -0
  8. synapse_sdk/cli/plugin/__init__.py +58 -0
  9. synapse_sdk/cli/plugin/create.py +566 -0
  10. synapse_sdk/cli/plugin/job.py +196 -0
  11. synapse_sdk/cli/plugin/publish.py +322 -0
  12. synapse_sdk/cli/plugin/run.py +131 -0
  13. synapse_sdk/cli/plugin/test.py +200 -0
  14. synapse_sdk/clients/README.md +239 -0
  15. synapse_sdk/clients/__init__.py +5 -0
  16. synapse_sdk/clients/_template.py +266 -0
  17. synapse_sdk/clients/agent/__init__.py +84 -29
  18. synapse_sdk/clients/agent/async_ray.py +289 -0
  19. synapse_sdk/clients/agent/container.py +83 -0
  20. synapse_sdk/clients/agent/plugin.py +101 -0
  21. synapse_sdk/clients/agent/ray.py +296 -39
  22. synapse_sdk/clients/backend/__init__.py +152 -12
  23. synapse_sdk/clients/backend/annotation.py +164 -22
  24. synapse_sdk/clients/backend/core.py +101 -0
  25. synapse_sdk/clients/backend/data_collection.py +292 -0
  26. synapse_sdk/clients/backend/hitl.py +87 -0
  27. synapse_sdk/clients/backend/integration.py +374 -46
  28. synapse_sdk/clients/backend/ml.py +134 -22
  29. synapse_sdk/clients/backend/models.py +247 -0
  30. synapse_sdk/clients/base.py +538 -59
  31. synapse_sdk/clients/exceptions.py +35 -7
  32. synapse_sdk/clients/pipeline/__init__.py +5 -0
  33. synapse_sdk/clients/pipeline/client.py +636 -0
  34. synapse_sdk/clients/protocols.py +178 -0
  35. synapse_sdk/clients/utils.py +86 -8
  36. synapse_sdk/clients/validation.py +58 -0
  37. synapse_sdk/enums.py +76 -0
  38. synapse_sdk/exceptions.py +168 -0
  39. synapse_sdk/integrations/__init__.py +74 -0
  40. synapse_sdk/integrations/_base.py +119 -0
  41. synapse_sdk/integrations/_context.py +53 -0
  42. synapse_sdk/integrations/ultralytics/__init__.py +78 -0
  43. synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
  44. synapse_sdk/integrations/ultralytics/_patches.py +124 -0
  45. synapse_sdk/loggers.py +476 -95
  46. synapse_sdk/mcp/MCP.md +69 -0
  47. synapse_sdk/mcp/__init__.py +48 -0
  48. synapse_sdk/mcp/__main__.py +6 -0
  49. synapse_sdk/mcp/config.py +349 -0
  50. synapse_sdk/mcp/prompts/__init__.py +4 -0
  51. synapse_sdk/mcp/resources/__init__.py +4 -0
  52. synapse_sdk/mcp/server.py +1352 -0
  53. synapse_sdk/mcp/tools/__init__.py +6 -0
  54. synapse_sdk/plugins/__init__.py +133 -9
  55. synapse_sdk/plugins/action.py +229 -0
  56. synapse_sdk/plugins/actions/__init__.py +82 -0
  57. synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
  58. synapse_sdk/plugins/actions/dataset/action.py +471 -0
  59. synapse_sdk/plugins/actions/export/__init__.py +55 -0
  60. synapse_sdk/plugins/actions/export/action.py +183 -0
  61. synapse_sdk/plugins/actions/export/context.py +59 -0
  62. synapse_sdk/plugins/actions/inference/__init__.py +84 -0
  63. synapse_sdk/plugins/actions/inference/action.py +285 -0
  64. synapse_sdk/plugins/actions/inference/context.py +81 -0
  65. synapse_sdk/plugins/actions/inference/deployment.py +322 -0
  66. synapse_sdk/plugins/actions/inference/serve.py +252 -0
  67. synapse_sdk/plugins/actions/train/__init__.py +54 -0
  68. synapse_sdk/plugins/actions/train/action.py +326 -0
  69. synapse_sdk/plugins/actions/train/context.py +57 -0
  70. synapse_sdk/plugins/actions/upload/__init__.py +49 -0
  71. synapse_sdk/plugins/actions/upload/action.py +165 -0
  72. synapse_sdk/plugins/actions/upload/context.py +61 -0
  73. synapse_sdk/plugins/config.py +98 -0
  74. synapse_sdk/plugins/context/__init__.py +109 -0
  75. synapse_sdk/plugins/context/env.py +113 -0
  76. synapse_sdk/plugins/datasets/__init__.py +113 -0
  77. synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
  78. synapse_sdk/plugins/datasets/converters/base.py +347 -0
  79. synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
  80. synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
  81. synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
  82. synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
  83. synapse_sdk/plugins/datasets/formats/dm.py +351 -0
  84. synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
  85. synapse_sdk/plugins/decorators.py +83 -0
  86. synapse_sdk/plugins/discovery.py +790 -0
  87. synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
  88. synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
  89. synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
  90. synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
  91. synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
  92. synapse_sdk/plugins/docs/README.md +513 -0
  93. synapse_sdk/plugins/docs/STEP.md +656 -0
  94. synapse_sdk/plugins/enums.py +70 -10
  95. synapse_sdk/plugins/errors.py +92 -0
  96. synapse_sdk/plugins/executors/__init__.py +43 -0
  97. synapse_sdk/plugins/executors/local.py +99 -0
  98. synapse_sdk/plugins/executors/ray/__init__.py +18 -0
  99. synapse_sdk/plugins/executors/ray/base.py +282 -0
  100. synapse_sdk/plugins/executors/ray/job.py +298 -0
  101. synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
  102. synapse_sdk/plugins/executors/ray/packaging.py +137 -0
  103. synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
  104. synapse_sdk/plugins/executors/ray/task.py +257 -0
  105. synapse_sdk/plugins/models/__init__.py +26 -0
  106. synapse_sdk/plugins/models/logger.py +173 -0
  107. synapse_sdk/plugins/models/pipeline.py +25 -0
  108. synapse_sdk/plugins/pipelines/__init__.py +81 -0
  109. synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
  110. synapse_sdk/plugins/pipelines/context.py +107 -0
  111. synapse_sdk/plugins/pipelines/display.py +311 -0
  112. synapse_sdk/plugins/runner.py +114 -0
  113. synapse_sdk/plugins/schemas/__init__.py +19 -0
  114. synapse_sdk/plugins/schemas/results.py +152 -0
  115. synapse_sdk/plugins/steps/__init__.py +63 -0
  116. synapse_sdk/plugins/steps/base.py +128 -0
  117. synapse_sdk/plugins/steps/context.py +90 -0
  118. synapse_sdk/plugins/steps/orchestrator.py +128 -0
  119. synapse_sdk/plugins/steps/registry.py +103 -0
  120. synapse_sdk/plugins/steps/utils/__init__.py +20 -0
  121. synapse_sdk/plugins/steps/utils/logging.py +85 -0
  122. synapse_sdk/plugins/steps/utils/timing.py +71 -0
  123. synapse_sdk/plugins/steps/utils/validation.py +68 -0
  124. synapse_sdk/plugins/templates/__init__.py +50 -0
  125. synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
  126. synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
  127. synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
  128. synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
  129. synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
  130. synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
  131. synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
  132. synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
  133. synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
  134. synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
  135. synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
  136. synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
  137. synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
  138. synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
  139. synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
  140. synapse_sdk/plugins/testing/__init__.py +25 -0
  141. synapse_sdk/plugins/testing/sample_actions.py +98 -0
  142. synapse_sdk/plugins/types.py +206 -0
  143. synapse_sdk/plugins/upload.py +595 -64
  144. synapse_sdk/plugins/utils.py +325 -37
  145. synapse_sdk/shared/__init__.py +25 -0
  146. synapse_sdk/utils/__init__.py +1 -0
  147. synapse_sdk/utils/auth.py +74 -0
  148. synapse_sdk/utils/file/__init__.py +58 -0
  149. synapse_sdk/utils/file/archive.py +449 -0
  150. synapse_sdk/utils/file/checksum.py +167 -0
  151. synapse_sdk/utils/file/download.py +286 -0
  152. synapse_sdk/utils/file/io.py +129 -0
  153. synapse_sdk/utils/file/requirements.py +36 -0
  154. synapse_sdk/utils/network.py +168 -0
  155. synapse_sdk/utils/storage/__init__.py +238 -0
  156. synapse_sdk/utils/storage/config.py +188 -0
  157. synapse_sdk/utils/storage/errors.py +52 -0
  158. synapse_sdk/utils/storage/providers/__init__.py +13 -0
  159. synapse_sdk/utils/storage/providers/base.py +76 -0
  160. synapse_sdk/utils/storage/providers/gcs.py +168 -0
  161. synapse_sdk/utils/storage/providers/http.py +250 -0
  162. synapse_sdk/utils/storage/providers/local.py +126 -0
  163. synapse_sdk/utils/storage/providers/s3.py +177 -0
  164. synapse_sdk/utils/storage/providers/sftp.py +208 -0
  165. synapse_sdk/utils/storage/registry.py +125 -0
  166. synapse_sdk/utils/websocket.py +99 -0
  167. synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
  168. synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
  169. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
  170. synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
  171. locale/en/LC_MESSAGES/messages.mo +0 -0
  172. locale/en/LC_MESSAGES/messages.po +0 -39
  173. locale/ko/LC_MESSAGES/messages.mo +0 -0
  174. locale/ko/LC_MESSAGES/messages.po +0 -34
  175. synapse_sdk/cli/create_plugin.py +0 -10
  176. synapse_sdk/clients/agent/core.py +0 -7
  177. synapse_sdk/clients/agent/service.py +0 -15
  178. synapse_sdk/clients/backend/dataset.py +0 -51
  179. synapse_sdk/clients/ray/__init__.py +0 -6
  180. synapse_sdk/clients/ray/core.py +0 -22
  181. synapse_sdk/clients/ray/serve.py +0 -20
  182. synapse_sdk/i18n.py +0 -35
  183. synapse_sdk/plugins/categories/__init__.py +0 -0
  184. synapse_sdk/plugins/categories/base.py +0 -235
  185. synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
  186. synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
  187. synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
  188. synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
  189. synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
  190. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
  191. synapse_sdk/plugins/categories/decorators.py +0 -13
  192. synapse_sdk/plugins/categories/export/__init__.py +0 -0
  193. synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
  194. synapse_sdk/plugins/categories/export/actions/export.py +0 -10
  195. synapse_sdk/plugins/categories/import/__init__.py +0 -0
  196. synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
  197. synapse_sdk/plugins/categories/import/actions/import.py +0 -10
  198. synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
  199. synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
  200. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
  201. synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
  202. synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
  203. synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
  204. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
  205. synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
  206. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
  207. synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
  208. synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
  209. synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
  210. synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
  211. synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
  212. synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
  213. synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
  214. synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
  215. synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
  216. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
  217. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
  218. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
  219. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
  220. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
  221. synapse_sdk/plugins/categories/registry.py +0 -16
  222. synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
  223. synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
  224. synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
  225. synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
  226. synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
  227. synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
  228. synapse_sdk/plugins/categories/templates.py +0 -32
  229. synapse_sdk/plugins/cli/__init__.py +0 -21
  230. synapse_sdk/plugins/cli/publish.py +0 -37
  231. synapse_sdk/plugins/cli/run.py +0 -67
  232. synapse_sdk/plugins/exceptions.py +0 -22
  233. synapse_sdk/plugins/models.py +0 -121
  234. synapse_sdk/plugins/templates/cookiecutter.json +0 -11
  235. synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
  236. synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
  237. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
  238. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
  239. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
  240. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
  241. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
  242. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
  243. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
  244. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
  245. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
  246. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
  247. synapse_sdk/shared/enums.py +0 -8
  248. synapse_sdk/utils/debug.py +0 -5
  249. synapse_sdk/utils/file.py +0 -87
  250. synapse_sdk/utils/module_loading.py +0 -29
  251. synapse_sdk/utils/pydantic/__init__.py +0 -0
  252. synapse_sdk/utils/pydantic/config.py +0 -4
  253. synapse_sdk/utils/pydantic/errors.py +0 -33
  254. synapse_sdk/utils/pydantic/validators.py +0 -7
  255. synapse_sdk/utils/storage.py +0 -91
  256. synapse_sdk/utils/string.py +0 -11
  257. synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
  258. synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
  259. synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
  260. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
  261. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,128 @@
1
+ """Workflow step base class and result dataclass.
2
+
3
+ Provides the foundation for defining workflow steps
4
+ with execution, skip conditions, and rollback support.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from abc import ABC, abstractmethod
10
+ from dataclasses import dataclass, field
11
+ from datetime import datetime
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ if TYPE_CHECKING:
15
+ from synapse_sdk.plugins.steps.context import BaseStepContext
16
+
17
+
18
+ @dataclass
19
+ class StepResult:
20
+ """Result of a workflow step execution.
21
+
22
+ Attributes:
23
+ success: Whether the step completed successfully.
24
+ data: Output data from the step.
25
+ error: Error message if step failed.
26
+ rollback_data: Data needed for rollback on failure.
27
+ skipped: Whether the step was skipped.
28
+ timestamp: When the step completed.
29
+
30
+ Example:
31
+ >>> result = StepResult(success=True, data={'files': 10})
32
+ >>> if not result.success:
33
+ ... print(f"Failed: {result.error}")
34
+ """
35
+
36
+ success: bool = True
37
+ data: dict[str, Any] = field(default_factory=dict)
38
+ error: str | None = None
39
+ rollback_data: dict[str, Any] = field(default_factory=dict)
40
+ skipped: bool = False
41
+ timestamp: datetime = field(default_factory=datetime.now)
42
+
43
+
44
+ class BaseStep[C: BaseStepContext](ABC):
45
+ """Abstract base class for workflow steps.
46
+
47
+ Type parameter C is the context type (must extend BaseStepContext).
48
+ Implement this class to define custom workflow steps with
49
+ execution, skip conditions, and rollback support.
50
+
51
+ Attributes:
52
+ name: Unique identifier for the step.
53
+ progress_weight: Relative weight (0.0-1.0) for progress calculation.
54
+
55
+ Example:
56
+ >>> class ValidateStep(BaseStep[MyContext]):
57
+ ... @property
58
+ ... def name(self) -> str:
59
+ ... return 'validate'
60
+ ...
61
+ ... @property
62
+ ... def progress_weight(self) -> float:
63
+ ... return 0.1
64
+ ...
65
+ ... def execute(self, context: MyContext) -> StepResult:
66
+ ... if not context.data:
67
+ ... return StepResult(success=False, error='No data')
68
+ ... return StepResult(success=True)
69
+ """
70
+
71
+ @property
72
+ @abstractmethod
73
+ def name(self) -> str:
74
+ """Step identifier.
75
+
76
+ Returns:
77
+ Unique name for this step.
78
+ """
79
+ ...
80
+
81
+ @property
82
+ @abstractmethod
83
+ def progress_weight(self) -> float:
84
+ """Relative weight for progress calculation.
85
+
86
+ Returns:
87
+ Float between 0.0 and 1.0 representing this step's
88
+ portion of total workflow progress.
89
+ """
90
+ ...
91
+
92
+ @abstractmethod
93
+ def execute(self, context: C) -> StepResult:
94
+ """Execute the step.
95
+
96
+ Args:
97
+ context: Shared context with params and state.
98
+
99
+ Returns:
100
+ StepResult indicating success/failure and any output data.
101
+ """
102
+ ...
103
+
104
+ def can_skip(self, context: C) -> bool:
105
+ """Check if step can be skipped.
106
+
107
+ Override to implement conditional step execution.
108
+
109
+ Args:
110
+ context: Shared context.
111
+
112
+ Returns:
113
+ True if step should be skipped, False otherwise.
114
+ Default: False.
115
+ """
116
+ return False
117
+
118
+ def rollback(self, context: C, result: StepResult) -> None:
119
+ """Rollback step on workflow failure.
120
+
121
+ Override to implement cleanup when a later step fails.
122
+ Called in reverse order for all executed steps.
123
+
124
+ Args:
125
+ context: Shared context.
126
+ result: The result from this step's execution.
127
+ """
128
+ pass
@@ -0,0 +1,90 @@
1
+ """Base context for step-based workflows.
2
+
3
+ Provides the abstract base class for sharing state between workflow steps.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass, field
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ if TYPE_CHECKING:
12
+ from synapse_sdk.plugins.context import RuntimeContext
13
+ from synapse_sdk.plugins.steps.base import StepResult
14
+
15
+
16
+ @dataclass
17
+ class BaseStepContext:
18
+ """Abstract base context for step-based workflows.
19
+
20
+ Provides the common interface for step contexts. Subclass this
21
+ to add action-specific state fields.
22
+
23
+ Attributes:
24
+ runtime_ctx: Parent RuntimeContext with logger, env, client.
25
+ step_results: Results from each executed step.
26
+ errors: Accumulated error messages.
27
+ current_step: Name of the currently executing step (set by Orchestrator).
28
+
29
+ Example:
30
+ >>> @dataclass
31
+ ... class UploadContext(BaseStepContext):
32
+ ... params: dict[str, Any] = field(default_factory=dict)
33
+ ... uploaded_files: list[str] = field(default_factory=list)
34
+ >>>
35
+ >>> ctx = UploadContext(runtime_ctx=runtime_ctx)
36
+ >>> ctx.log('upload_start', {'count': 10})
37
+ """
38
+
39
+ runtime_ctx: RuntimeContext
40
+ step_results: list[StepResult] = field(default_factory=list)
41
+ errors: list[str] = field(default_factory=list)
42
+ current_step: str | None = field(default=None, init=False)
43
+
44
+ def _set_current_step(self, step_name: str | None) -> None:
45
+ """Set the currently executing step name (internal use by Orchestrator).
46
+
47
+ Args:
48
+ step_name: Name of the step being executed, or None to clear.
49
+ """
50
+ self.current_step = step_name
51
+
52
+ def log(self, event: str, data: dict[str, Any], file: str | None = None) -> None:
53
+ """Log an event via runtime context.
54
+
55
+ Args:
56
+ event: Event name/type.
57
+ data: Dictionary of event data.
58
+ file: Optional file path associated with the event.
59
+ """
60
+ self.runtime_ctx.log(event, data, file)
61
+
62
+ def set_progress(self, current: int, total: int, category: str | None = None) -> None:
63
+ """Set progress via runtime context.
64
+
65
+ If category is not provided, uses current_step as the category.
66
+
67
+ Args:
68
+ current: Current progress value.
69
+ total: Total progress value.
70
+ category: Optional category name. Defaults to current_step if not provided.
71
+ """
72
+ effective_category = category if category is not None else self.current_step
73
+ self.runtime_ctx.set_progress(current, total, effective_category)
74
+
75
+ def set_metrics(self, value: dict[str, Any], category: str | None = None) -> None:
76
+ """Set metrics via runtime context.
77
+
78
+ If category is not provided, uses current_step as the category.
79
+
80
+ Args:
81
+ value: Dictionary of metric values.
82
+ category: Category name. Defaults to current_step if not provided.
83
+
84
+ Raises:
85
+ ValueError: If category is not provided and current_step is None.
86
+ """
87
+ effective_category = category if category is not None else self.current_step
88
+ if effective_category is None:
89
+ raise ValueError('category must be provided when not executing within a step (current_step is None)')
90
+ self.runtime_ctx.set_metrics(value, effective_category)
@@ -0,0 +1,128 @@
1
+ """Orchestrator for executing workflow steps with rollback support."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from synapse_sdk.plugins.steps.base import BaseStep, StepResult
9
+
10
+ if TYPE_CHECKING:
11
+ from synapse_sdk.plugins.steps.context import BaseStepContext
12
+ from synapse_sdk.plugins.steps.registry import StepRegistry
13
+
14
+
15
+ class Orchestrator[C: BaseStepContext]:
16
+ """Executes workflow steps with progress tracking and rollback.
17
+
18
+ Type parameter C is the context type shared between steps.
19
+ Runs steps in order, tracking progress based on step weights.
20
+ On failure, automatically rolls back executed steps in reverse order.
21
+
22
+ Attributes:
23
+ registry: StepRegistry containing ordered steps.
24
+ context: Shared context for step communication.
25
+ progress_callback: Optional callback for progress updates.
26
+
27
+ Example:
28
+ >>> registry = StepRegistry[MyContext]()
29
+ >>> registry.register(InitStep())
30
+ >>> registry.register(ProcessStep())
31
+ >>> context = MyContext(runtime_ctx=runtime_ctx)
32
+ >>> orchestrator = Orchestrator(registry, context)
33
+ >>> result = orchestrator.execute()
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ registry: StepRegistry[C],
39
+ context: C,
40
+ progress_callback: Callable[[int, int], None] | None = None,
41
+ ) -> None:
42
+ """Initialize orchestrator.
43
+
44
+ Args:
45
+ registry: StepRegistry with steps to execute.
46
+ context: Shared context for steps.
47
+ progress_callback: Optional callback(current, total) for progress.
48
+ """
49
+ self._registry = registry
50
+ self._context = context
51
+ self._progress_callback = progress_callback
52
+ self._executed_steps: list[tuple[BaseStep[C], StepResult]] = []
53
+
54
+ def execute(self) -> dict[str, Any]:
55
+ """Execute all steps in order with rollback on failure.
56
+
57
+ Returns:
58
+ Dict with success status and step count.
59
+
60
+ Raises:
61
+ RuntimeError: If any step fails (after rollback).
62
+ """
63
+ steps = self._registry.get_steps()
64
+ total_weight = self._registry.total_weight
65
+ completed_weight = 0.0
66
+
67
+ try:
68
+ for step in steps:
69
+ # Set current step name for auto-category support
70
+ self._context._set_current_step(step.name)
71
+
72
+ # Check skip condition
73
+ if step.can_skip(self._context):
74
+ result = StepResult(success=True, skipped=True)
75
+ self._context.step_results.append(result)
76
+ completed_weight += step.progress_weight
77
+ self._update_progress(completed_weight, total_weight)
78
+ continue
79
+
80
+ # Execute step
81
+ try:
82
+ result = step.execute(self._context)
83
+ except Exception as e:
84
+ result = StepResult(success=False, error=str(e))
85
+
86
+ self._context.step_results.append(result)
87
+ self._executed_steps.append((step, result))
88
+
89
+ if not result.success:
90
+ self._rollback()
91
+ raise RuntimeError(f"Step '{step.name}' failed: {result.error}")
92
+
93
+ # Update progress
94
+ completed_weight += step.progress_weight
95
+ self._update_progress(completed_weight, total_weight)
96
+
97
+ return {
98
+ 'success': True,
99
+ 'steps_executed': len(self._executed_steps),
100
+ 'steps_total': len(steps),
101
+ }
102
+ finally:
103
+ # Clear current step after execution completes or fails
104
+ self._context._set_current_step(None)
105
+
106
+ def _update_progress(self, completed: float, total: float) -> None:
107
+ """Update progress via callback.
108
+
109
+ Args:
110
+ completed: Completed weight sum.
111
+ total: Total weight sum.
112
+ """
113
+ if self._progress_callback and total > 0:
114
+ progress_pct = int((completed / total) * 100)
115
+ self._progress_callback(progress_pct, 100)
116
+
117
+ def _rollback(self) -> None:
118
+ """Rollback executed steps in reverse order.
119
+
120
+ Best-effort rollback - errors during rollback are logged but
121
+ do not prevent other steps from rolling back.
122
+ """
123
+ for step, result in reversed(self._executed_steps):
124
+ try:
125
+ step.rollback(self._context, result)
126
+ except Exception:
127
+ # Best effort rollback - log but continue
128
+ self._context.errors.append(f"Rollback failed for step '{step.name}'")
@@ -0,0 +1,103 @@
1
+ """Step registry for managing ordered workflow steps."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from synapse_sdk.plugins.steps.base import BaseStep
9
+ from synapse_sdk.plugins.steps.context import BaseStepContext
10
+
11
+
12
+ class StepRegistry[C: BaseStepContext]:
13
+ """Registry for managing ordered workflow steps.
14
+
15
+ Type parameter C is the context type for steps in this registry.
16
+ Maintains an ordered list of steps and provides methods for
17
+ registration, removal, and insertion at specific positions.
18
+
19
+ Example:
20
+ >>> registry = StepRegistry[MyContext]()
21
+ >>> registry.register(InitStep())
22
+ >>> registry.register(ProcessStep())
23
+ >>> registry.insert_before('process', ValidateStep())
24
+ >>> for step in registry.get_steps():
25
+ ... print(step.name) # init, validate, process
26
+ """
27
+
28
+ def __init__(self) -> None:
29
+ """Initialize empty registry."""
30
+ self._steps: list[BaseStep[C]] = []
31
+
32
+ def register(self, step: BaseStep[C]) -> None:
33
+ """Add step to end of workflow.
34
+
35
+ Args:
36
+ step: Step instance to register.
37
+ """
38
+ self._steps.append(step)
39
+
40
+ def unregister(self, name: str) -> None:
41
+ """Remove step by name.
42
+
43
+ Args:
44
+ name: Name of step to remove.
45
+ """
46
+ self._steps = [s for s in self._steps if s.name != name]
47
+
48
+ def get_steps(self) -> list[BaseStep[C]]:
49
+ """Get ordered list of steps.
50
+
51
+ Returns:
52
+ Copy of the step list in execution order.
53
+ """
54
+ return list(self._steps)
55
+
56
+ def insert_after(self, after_name: str, step: BaseStep[C]) -> None:
57
+ """Insert step after another step.
58
+
59
+ Args:
60
+ after_name: Name of existing step to insert after.
61
+ step: Step instance to insert.
62
+
63
+ Raises:
64
+ ValueError: If step with after_name not found.
65
+ """
66
+ for i, s in enumerate(self._steps):
67
+ if s.name == after_name:
68
+ self._steps.insert(i + 1, step)
69
+ return
70
+ raise ValueError(f"Step '{after_name}' not found")
71
+
72
+ def insert_before(self, before_name: str, step: BaseStep[C]) -> None:
73
+ """Insert step before another step.
74
+
75
+ Args:
76
+ before_name: Name of existing step to insert before.
77
+ step: Step instance to insert.
78
+
79
+ Raises:
80
+ ValueError: If step with before_name not found.
81
+ """
82
+ for i, s in enumerate(self._steps):
83
+ if s.name == before_name:
84
+ self._steps.insert(i, step)
85
+ return
86
+ raise ValueError(f"Step '{before_name}' not found")
87
+
88
+ @property
89
+ def total_weight(self) -> float:
90
+ """Sum of all step weights.
91
+
92
+ Returns:
93
+ Total progress weight across all registered steps.
94
+ """
95
+ return sum(s.progress_weight for s in self._steps)
96
+
97
+ def __len__(self) -> int:
98
+ """Return number of registered steps."""
99
+ return len(self._steps)
100
+
101
+ def __bool__(self) -> bool:
102
+ """Return True if any steps are registered."""
103
+ return bool(self._steps)
@@ -0,0 +1,20 @@
1
+ """Utility step wrappers for Synapse SDK.
2
+
3
+ This module provides utility step wrappers that enhance step execution
4
+ with additional functionality such as logging, timing, and validation.
5
+
6
+ Classes:
7
+ LoggingStep: Wrapper that adds logging to step execution.
8
+ TimingStep: Wrapper that adds timing measurement to step execution.
9
+ ValidationStep: Wrapper that adds pre-execution validation to steps.
10
+ """
11
+
12
+ from synapse_sdk.plugins.steps.utils.logging import LoggingStep
13
+ from synapse_sdk.plugins.steps.utils.timing import TimingStep
14
+ from synapse_sdk.plugins.steps.utils.validation import ValidationStep
15
+
16
+ __all__ = [
17
+ 'LoggingStep',
18
+ 'TimingStep',
19
+ 'ValidationStep',
20
+ ]
@@ -0,0 +1,85 @@
1
+ """Logging step wrapper for workflow steps."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import TYPE_CHECKING
7
+
8
+ from synapse_sdk.plugins.steps.base import BaseStep, StepResult
9
+
10
+ if TYPE_CHECKING:
11
+ from synapse_sdk.plugins.steps.context import BaseStepContext
12
+
13
+
14
+ class LoggingStep[C: BaseStepContext](BaseStep[C]):
15
+ """Wraps a step with start/end logging including timing.
16
+
17
+ Logs step_start event before execution and step_end event after,
18
+ including elapsed time in seconds.
19
+
20
+ Example:
21
+ >>> logged_step = LoggingStep(MyProcessStep())
22
+ >>> registry.register(logged_step)
23
+ >>> # Logs: step_start {'step': 'process'}
24
+ >>> # Logs: step_end {'step': 'process', 'elapsed': 1.23, 'success': True}
25
+ """
26
+
27
+ def __init__(self, step: BaseStep[C]) -> None:
28
+ """Initialize with step to wrap.
29
+
30
+ Args:
31
+ step: The step to wrap with logging.
32
+ """
33
+ self._wrapped = step
34
+
35
+ @property
36
+ def name(self) -> str:
37
+ """Return wrapped step name with 'logged_' prefix."""
38
+ return f'logged_{self._wrapped.name}'
39
+
40
+ @property
41
+ def progress_weight(self) -> float:
42
+ """Return wrapped step's progress weight."""
43
+ return self._wrapped.progress_weight
44
+
45
+ def execute(self, context: C) -> StepResult:
46
+ """Execute wrapped step with logging.
47
+
48
+ Args:
49
+ context: Shared context.
50
+
51
+ Returns:
52
+ Result from wrapped step execution.
53
+ """
54
+ step_name = self._wrapped.name
55
+ context.log('step_start', {'step': step_name})
56
+
57
+ start = time.perf_counter()
58
+ result = self._wrapped.execute(context)
59
+ elapsed = time.perf_counter() - start
60
+
61
+ context.log(
62
+ 'step_end',
63
+ {
64
+ 'step': step_name,
65
+ 'elapsed': round(elapsed, 3),
66
+ 'success': result.success,
67
+ 'skipped': result.skipped,
68
+ },
69
+ )
70
+
71
+ return result
72
+
73
+ def can_skip(self, context: C) -> bool:
74
+ """Delegate to wrapped step."""
75
+ return self._wrapped.can_skip(context)
76
+
77
+ def rollback(self, context: C, result: StepResult) -> None:
78
+ """Delegate rollback to wrapped step with logging.
79
+
80
+ Args:
81
+ context: Shared context.
82
+ result: Result from this step's execution.
83
+ """
84
+ context.log('step_rollback', {'step': self._wrapped.name})
85
+ self._wrapped.rollback(context, result)
@@ -0,0 +1,71 @@
1
+ """Timing step wrapper for workflow steps."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import TYPE_CHECKING
7
+
8
+ from synapse_sdk.plugins.steps.base import BaseStep, StepResult
9
+
10
+ if TYPE_CHECKING:
11
+ from synapse_sdk.plugins.steps.context import BaseStepContext
12
+
13
+
14
+ class TimingStep[C: BaseStepContext](BaseStep[C]):
15
+ """Wraps a step with duration measurement.
16
+
17
+ Measures execution time and adds 'duration_seconds' to result data.
18
+
19
+ Example:
20
+ >>> timed_step = TimingStep(MyProcessStep())
21
+ >>> registry.register(timed_step)
22
+ >>> result = orchestrator.execute()
23
+ >>> print(result.data['duration_seconds']) # 1.234
24
+ """
25
+
26
+ def __init__(self, step: BaseStep[C]) -> None:
27
+ """Initialize with step to wrap.
28
+
29
+ Args:
30
+ step: The step to wrap with timing.
31
+ """
32
+ self._wrapped = step
33
+
34
+ @property
35
+ def name(self) -> str:
36
+ """Return wrapped step name with 'timed_' prefix."""
37
+ return f'timed_{self._wrapped.name}'
38
+
39
+ @property
40
+ def progress_weight(self) -> float:
41
+ """Return wrapped step's progress weight."""
42
+ return self._wrapped.progress_weight
43
+
44
+ def execute(self, context: C) -> StepResult:
45
+ """Execute wrapped step with timing.
46
+
47
+ Args:
48
+ context: Shared context.
49
+
50
+ Returns:
51
+ Result from wrapped step with duration_seconds added.
52
+ """
53
+ start = time.perf_counter()
54
+ result = self._wrapped.execute(context)
55
+ elapsed = time.perf_counter() - start
56
+
57
+ result.data['duration_seconds'] = round(elapsed, 6)
58
+ return result
59
+
60
+ def can_skip(self, context: C) -> bool:
61
+ """Delegate to wrapped step."""
62
+ return self._wrapped.can_skip(context)
63
+
64
+ def rollback(self, context: C, result: StepResult) -> None:
65
+ """Delegate rollback to wrapped step.
66
+
67
+ Args:
68
+ context: Shared context.
69
+ result: Result from this step's execution.
70
+ """
71
+ self._wrapped.rollback(context, result)
@@ -0,0 +1,68 @@
1
+ """Validation step for checking context state."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from typing import TYPE_CHECKING
7
+
8
+ from synapse_sdk.plugins.steps.base import BaseStep, StepResult
9
+
10
+ if TYPE_CHECKING:
11
+ from synapse_sdk.plugins.steps.context import BaseStepContext
12
+
13
+
14
+ class ValidationStep[C: BaseStepContext](BaseStep[C]):
15
+ """Validates context state before proceeding.
16
+
17
+ Takes a validator function that checks context and returns
18
+ (is_valid, error_message). Fails the step if validation fails.
19
+
20
+ Example:
21
+ >>> def check_data(ctx: MyContext) -> tuple[bool, str | None]:
22
+ ... if not ctx.data:
23
+ ... return False, 'No data loaded'
24
+ ... return True, None
25
+ >>>
26
+ >>> registry.register(ValidationStep(check_data, name='validate_data'))
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ validator: Callable[[C], tuple[bool, str | None]],
32
+ name: str = 'validate',
33
+ progress_weight: float = 0.05,
34
+ ) -> None:
35
+ """Initialize validation step.
36
+
37
+ Args:
38
+ validator: Function that takes context and returns (is_valid, error).
39
+ name: Step name (default: 'validate').
40
+ progress_weight: Progress weight (default: 0.05).
41
+ """
42
+ self._validator = validator
43
+ self._name = name
44
+ self._progress_weight = progress_weight
45
+
46
+ @property
47
+ def name(self) -> str:
48
+ """Return step name."""
49
+ return self._name
50
+
51
+ @property
52
+ def progress_weight(self) -> float:
53
+ """Return progress weight."""
54
+ return self._progress_weight
55
+
56
+ def execute(self, context: C) -> StepResult:
57
+ """Execute validation.
58
+
59
+ Args:
60
+ context: Shared context to validate.
61
+
62
+ Returns:
63
+ StepResult with success=False if validation fails.
64
+ """
65
+ is_valid, error = self._validator(context)
66
+ if not is_valid:
67
+ return StepResult(success=False, error=error)
68
+ return StepResult(success=True)