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
@@ -1,21 +1,81 @@
1
- from enum import Enum
1
+ from __future__ import annotations
2
2
 
3
+ from enum import StrEnum
3
4
 
4
- class RunMethod(Enum):
5
- JOB = 'job'
6
- TASK = 'task'
7
- RESTAPI = 'restapi'
8
5
 
6
+ class PluginCategory(StrEnum):
7
+ """Categories for organizing plugins by functionality."""
9
8
 
10
- class PluginCategory(Enum):
11
9
  NEURAL_NET = 'neural_net'
12
10
  EXPORT = 'export'
13
- IMPORT = 'import'
11
+ UPLOAD = 'upload'
14
12
  SMART_TOOL = 'smart_tool'
15
13
  POST_ANNOTATION = 'post_annotation'
16
14
  PRE_ANNOTATION = 'pre_annotation'
17
15
  DATA_VALIDATION = 'data_validation'
16
+ CUSTOM = 'custom'
17
+
18
+
19
+ class RunMethod(StrEnum):
20
+ """Execution methods for plugin actions."""
21
+
22
+ JOB = 'job'
23
+ TASK = 'task'
24
+ SERVE = 'serve'
25
+
26
+
27
+ class PackageManager(StrEnum):
28
+ """Package managers for plugin dependencies."""
29
+
30
+ PIP = 'pip'
31
+ UV = 'uv'
32
+
33
+
34
+ class DataType(StrEnum):
35
+ """Data types handled by plugins."""
36
+
37
+ IMAGE = 'image'
38
+ TEXT = 'text'
39
+ VIDEO = 'video'
40
+ PCD = 'pcd'
41
+ AUDIO = 'audio'
42
+
43
+
44
+ class AnnotationCategory(StrEnum):
45
+ """Annotation categories for smart tools."""
46
+
47
+ OBJECT_DETECTION = 'object_detection'
48
+ CLASSIFICATION = 'classification'
49
+ SEGMENTATION = 'segmentation'
50
+ KEYPOINT = 'keypoint'
51
+ TEXT = 'text'
52
+
53
+
54
+ class AnnotationType(StrEnum):
55
+ """Annotation types for smart tools."""
56
+
57
+ BBOX = 'bbox'
58
+ POLYGON = 'polygon'
59
+ POINT = 'point'
60
+ LINE = 'line'
61
+ MASK = 'mask'
62
+ LABEL = 'label'
63
+
64
+
65
+ class SmartToolType(StrEnum):
66
+ """Smart tool implementation types."""
67
+
68
+ INTERACTIVE = 'interactive'
69
+ AUTOMATIC = 'automatic'
70
+ SEMI_AUTOMATIC = 'semi_automatic'
71
+
18
72
 
19
- @classmethod
20
- def choices(cls):
21
- return [(member.value, member.name.replace('_', ' ').title()) for member in cls]
73
+ __all__ = [
74
+ 'PluginCategory',
75
+ 'RunMethod',
76
+ 'PackageManager',
77
+ 'DataType',
78
+ 'AnnotationCategory',
79
+ 'AnnotationType',
80
+ 'SmartToolType',
81
+ ]
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ class PluginError(Exception):
7
+ """Base exception for plugin-related errors."""
8
+
9
+ def __init__(self, message: str, details: Any = None):
10
+ self.message = message
11
+ self.details = details
12
+ super().__init__(message)
13
+
14
+ def __repr__(self) -> str:
15
+ return f'{self.__class__.__name__}(message={self.message!r}, details={self.details!r})'
16
+
17
+
18
+ class ValidationError(PluginError):
19
+ """Raised when plugin parameters fail validation."""
20
+
21
+ pass
22
+
23
+
24
+ class ActionNotFoundError(PluginError):
25
+ """Raised when the requested action doesn't exist in the plugin."""
26
+
27
+ pass
28
+
29
+
30
+ class ExecutionError(PluginError):
31
+ """Raised when action execution fails."""
32
+
33
+ pass
34
+
35
+
36
+ class PluginUploadError(PluginError):
37
+ """Raised when plugin upload fails.
38
+
39
+ Covers storage upload failures, network errors during upload,
40
+ and other upload-related issues.
41
+ """
42
+
43
+ pass
44
+
45
+
46
+ class ArchiveError(PluginError):
47
+ """Raised when archive creation fails.
48
+
49
+ Covers ZIP creation failures, git ls-files failures,
50
+ and file permission errors during archiving.
51
+ """
52
+
53
+ pass
54
+
55
+
56
+ class BuildError(PluginError):
57
+ """Raised when wheel build fails.
58
+
59
+ Covers wheel build failures, missing pyproject.toml,
60
+ and package manager not found errors.
61
+ """
62
+
63
+ pass
64
+
65
+
66
+ class ChecksumMismatchError(PluginError):
67
+ """Raised when checksum verification fails.
68
+
69
+ Indicates file integrity issues - the actual checksum
70
+ does not match the expected value.
71
+ """
72
+
73
+ pass
74
+
75
+
76
+ class PluginRunError(PluginError):
77
+ """Raised when plugin run fails."""
78
+
79
+ pass
80
+
81
+
82
+ __all__ = [
83
+ 'ActionNotFoundError',
84
+ 'ArchiveError',
85
+ 'BuildError',
86
+ 'ChecksumMismatchError',
87
+ 'ExecutionError',
88
+ 'PluginError',
89
+ 'PluginRunError',
90
+ 'PluginUploadError',
91
+ 'ValidationError',
92
+ ]
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
4
+
5
+ if TYPE_CHECKING:
6
+ from synapse_sdk.plugins.action import BaseAction
7
+
8
+
9
+ @runtime_checkable
10
+ class ExecutorProtocol(Protocol):
11
+ """Protocol for plugin action executors.
12
+
13
+ Executors handle the lifecycle of action execution:
14
+ 1. Build RuntimeContext (logger, env, job_id)
15
+ 2. Instantiate the action with validated params
16
+ 3. Call execute() and handle errors
17
+ 4. Clean up and return result
18
+ """
19
+
20
+ def execute(
21
+ self,
22
+ action_cls: type[BaseAction],
23
+ params: dict[str, Any],
24
+ **kwargs: Any,
25
+ ) -> Any:
26
+ """Execute an action class with parameters.
27
+
28
+ Args:
29
+ action_cls: The BaseAction subclass to execute.
30
+ params: Raw parameters dict (will be validated).
31
+ **kwargs: Executor-specific options.
32
+
33
+ Returns:
34
+ Action result.
35
+
36
+ Raises:
37
+ ValidationError: If params fail validation.
38
+ ExecutionError: If action execution fails.
39
+ """
40
+ ...
41
+
42
+
43
+ __all__ = ['ExecutorProtocol']
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from synapse_sdk.loggers import ConsoleLogger
6
+ from synapse_sdk.plugins.action import NoResult, validate_result
7
+ from synapse_sdk.plugins.context import PluginEnvironment, RuntimeContext
8
+ from synapse_sdk.plugins.errors import ExecutionError, ValidationError
9
+ from synapse_sdk.utils.auth import create_backend_client
10
+
11
+ if TYPE_CHECKING:
12
+ from synapse_sdk.plugins.action import BaseAction
13
+
14
+
15
+ class LocalExecutor:
16
+ """Execute actions in the current process.
17
+
18
+ Best for development and testing. Uses ConsoleLogger by default.
19
+
20
+ Example:
21
+ >>> executor = LocalExecutor()
22
+ >>> result = executor.execute(TrainAction, {'epochs': 10})
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ env: PluginEnvironment | dict[str, Any] | None = None,
28
+ job_id: str | None = None,
29
+ ) -> None:
30
+ """Initialize executor.
31
+
32
+ Args:
33
+ env: Environment config. If None, loads from os.environ.
34
+ job_id: Optional job identifier.
35
+ """
36
+ if env is None:
37
+ self._env = PluginEnvironment.from_environ()
38
+ elif isinstance(env, dict):
39
+ self._env = PluginEnvironment(env)
40
+ else:
41
+ self._env = env
42
+ self._job_id = job_id
43
+ self._client = create_backend_client()
44
+
45
+ def execute(
46
+ self,
47
+ action_cls: type[BaseAction],
48
+ params: dict[str, Any],
49
+ **kwargs: Any,
50
+ ) -> Any:
51
+ """Execute action synchronously in current process.
52
+
53
+ Args:
54
+ action_cls: BaseAction subclass to execute.
55
+ params: Parameters dict to validate and pass.
56
+ **kwargs: Ignored (for protocol compatibility).
57
+
58
+ Returns:
59
+ Action result from execute().
60
+
61
+ Raises:
62
+ ValidationError: If params fail validation.
63
+ ExecutionError: If action raises an exception.
64
+ """
65
+ del kwargs # Unused, for protocol compatibility
66
+
67
+ # Validate params
68
+ try:
69
+ validated_params = action_cls.params_model.model_validate(params)
70
+ except Exception as e:
71
+ raise ValidationError(f'Parameter validation failed: {e}') from e
72
+
73
+ # Build context
74
+ logger = ConsoleLogger()
75
+ ctx = RuntimeContext(
76
+ logger=logger,
77
+ env=self._env.to_dict(),
78
+ job_id=self._job_id,
79
+ client=self._client,
80
+ )
81
+
82
+ # Instantiate and execute
83
+ action = action_cls(validated_params, ctx)
84
+ try:
85
+ result = action.execute()
86
+ except Exception as e:
87
+ logger.finish()
88
+ raise ExecutionError(f'Action execution failed: {e}') from e
89
+
90
+ # Validate result with warning-only mode
91
+ result_model = getattr(action_cls, 'result_model', NoResult)
92
+ if result_model is not NoResult:
93
+ result = validate_result(result, result_model, logger)
94
+
95
+ logger.finish()
96
+ return result
97
+
98
+
99
+ __all__ = ['LocalExecutor']
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ from synapse_sdk.plugins.executors.ray.base import BaseRayExecutor, read_requirements
4
+ from synapse_sdk.plugins.executors.ray.job import RayJobExecutor
5
+ from synapse_sdk.plugins.executors.ray.jobs_api import RayJobsApiExecutor
6
+ from synapse_sdk.plugins.executors.ray.pipeline import PipelineDefinition, RayPipelineExecutor
7
+ from synapse_sdk.plugins.executors.ray.task import RayActorExecutor, RayTaskExecutor
8
+
9
+ __all__ = [
10
+ 'BaseRayExecutor',
11
+ 'PipelineDefinition',
12
+ 'RayActorExecutor',
13
+ 'RayJobExecutor',
14
+ 'RayJobsApiExecutor',
15
+ 'RayPipelineExecutor',
16
+ 'RayTaskExecutor',
17
+ 'read_requirements',
18
+ ]
@@ -0,0 +1,282 @@
1
+ """Base class for Ray executors with shared runtime env logic."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any, Literal
7
+
8
+ from synapse_sdk.plugins.context import PluginEnvironment
9
+ from synapse_sdk.plugins.enums import PackageManager
10
+
11
+
12
+ def read_requirements(file_path: str | Path) -> list[str] | None:
13
+ """Read and parse a requirements.txt file.
14
+
15
+ Args:
16
+ file_path: Path to the requirements.txt file.
17
+
18
+ Returns:
19
+ List of requirement strings, or None if file doesn't exist.
20
+ """
21
+ path = Path(file_path)
22
+ if not path.exists():
23
+ return None
24
+
25
+ requirements = []
26
+ for line in path.read_text().splitlines():
27
+ stripped = line.strip()
28
+ if stripped and not stripped.startswith('#'):
29
+ requirements.append(stripped)
30
+ return requirements
31
+
32
+
33
+ class BaseRayExecutor:
34
+ """Base class for Ray executors with shared runtime env building logic."""
35
+
36
+ def __init__(
37
+ self,
38
+ env: PluginEnvironment | dict[str, Any] | None = None,
39
+ *,
40
+ runtime_env: dict[str, Any] | None = None,
41
+ working_dir: str | Path | None = None,
42
+ requirements_file: str | Path | None = None,
43
+ package_manager: PackageManager | Literal['pip', 'uv'] = PackageManager.PIP,
44
+ package_manager_options: list[str] | None = None,
45
+ wheels_dir: str = 'wheels',
46
+ ray_address: str = 'auto',
47
+ include_sdk: bool = False,
48
+ ) -> None:
49
+ """Initialize base Ray executor.
50
+
51
+ Args:
52
+ env: Environment config for the action. If None, loads from os.environ.
53
+ runtime_env: Ray runtime environment config.
54
+ working_dir: Plugin working directory.
55
+ requirements_file: Path to requirements.txt.
56
+ package_manager: Package manager to use ('pip' or 'uv').
57
+ package_manager_options: Additional options for the package manager.
58
+ wheels_dir: Directory containing .whl files relative to working_dir.
59
+ ray_address: Ray cluster address (for detecting remote mode).
60
+ include_sdk: If True, bundle local SDK with upload (for development).
61
+ """
62
+ if env is None:
63
+ self._env = PluginEnvironment.from_environ()
64
+ elif isinstance(env, dict):
65
+ self._env = PluginEnvironment(env)
66
+ else:
67
+ self._env = env
68
+
69
+ self._runtime_env = runtime_env or {}
70
+ self._working_dir = Path(working_dir) if working_dir else None
71
+ self._requirements_file = Path(requirements_file) if requirements_file else None
72
+ self._package_manager = PackageManager(package_manager)
73
+ self._package_manager_options = package_manager_options
74
+ self._wheels_dir = wheels_dir
75
+ self._ray_address = ray_address
76
+ self._include_sdk = include_sdk
77
+ self._gcs_uri: str | None = None # Cached GCS URI
78
+
79
+ def _ray_init(self) -> None:
80
+ """Initialize Ray connection with SDK bundling if requested."""
81
+ import ray
82
+
83
+ if ray.is_initialized():
84
+ return
85
+
86
+ # Build init kwargs
87
+ init_kwargs: dict[str, Any] = {
88
+ 'address': self._ray_address,
89
+ 'ignore_reinit_error': True,
90
+ }
91
+
92
+ # Build runtime_env for init level
93
+ runtime_env: dict[str, Any] = {}
94
+
95
+ # Include SDK at init level (directories only work here, not at actor level)
96
+ if self._include_sdk:
97
+ import synapse_sdk
98
+
99
+ sdk_path = str(Path(synapse_sdk.__file__).parent)
100
+ runtime_env['py_modules'] = [sdk_path]
101
+
102
+ # Include working_dir at init level for local mode
103
+ # (local paths are only supported at ray.init() level, not at actor level)
104
+ if self._working_dir and not self._is_remote_cluster():
105
+ runtime_env['working_dir'] = str(self._working_dir)
106
+
107
+ if runtime_env:
108
+ init_kwargs['runtime_env'] = runtime_env
109
+
110
+ ray.init(**init_kwargs)
111
+
112
+ def _is_remote_cluster(self) -> bool:
113
+ """Check if connecting to a remote Ray cluster."""
114
+ # Remote if address starts with ray:// protocol
115
+ return self._ray_address.startswith('ray://')
116
+
117
+ def _get_working_dir_uri(self) -> str | None:
118
+ """Get working directory URI, uploading to GCS for remote clusters."""
119
+ if not self._working_dir:
120
+ return None
121
+
122
+ # For remote clusters, upload to GCS
123
+ if self._is_remote_cluster():
124
+ if self._gcs_uri is None:
125
+ from synapse_sdk.plugins.executors.ray.packaging import upload_working_dir_to_gcs
126
+
127
+ self._gcs_uri = upload_working_dir_to_gcs(self._working_dir)
128
+ return self._gcs_uri
129
+
130
+ # Local mode - use path directly
131
+ return str(self._working_dir)
132
+
133
+ def _build_runtime_env(self) -> dict[str, Any]:
134
+ """Build runtime environment with working_dir, requirements, and env vars."""
135
+ runtime_env = {**self._runtime_env}
136
+
137
+ # Set working_dir if provided (uploads to GCS for remote clusters)
138
+ # Note: Local paths are only supported at ray.init() level, not at actor level
139
+ if self._working_dir and 'working_dir' not in runtime_env:
140
+ if self._is_remote_cluster():
141
+ working_dir_uri = self._get_working_dir_uri()
142
+ if working_dir_uri:
143
+ runtime_env['working_dir'] = working_dir_uri
144
+ # For local mode, working_dir is passed at ray.init() level
145
+
146
+ # Build package manager config with requirements and wheels
147
+ pm_key = str(self._package_manager) # 'pip' or 'uv'
148
+ requirements = self._get_requirements() or []
149
+ wheel_files = self._get_wheel_files()
150
+
151
+ # Check if requirements.txt contains pip args (lines starting with -)
152
+ # If so, use file path instead of package list (Ray only supports pip args via file)
153
+ has_pip_args = any(req.strip().startswith('-') for req in requirements)
154
+ req_file_path = self._get_requirements_file_path()
155
+
156
+ if has_pip_args and req_file_path:
157
+ # Use requirements.txt file path to preserve pip args like --extra-index-url
158
+ # Reference the file relative to working_dir
159
+ runtime_env[pm_key] = 'requirements.txt'
160
+ # Add wheel files separately if any
161
+ if wheel_files:
162
+ # Wheels need to be in packages list, so we need a hybrid approach
163
+ # For now, log a warning - wheels with pip args is not fully supported
164
+ pass
165
+ else:
166
+ # Combine requirements and wheel files as package list
167
+ all_packages = requirements + wheel_files
168
+
169
+ if all_packages:
170
+ # Initialize package manager config
171
+ if pm_key not in runtime_env:
172
+ runtime_env[pm_key] = {'packages': []}
173
+ elif isinstance(runtime_env[pm_key], list):
174
+ runtime_env[pm_key] = {'packages': runtime_env[pm_key]}
175
+
176
+ runtime_env[pm_key].setdefault('packages', [])
177
+ runtime_env[pm_key]['packages'].extend(all_packages)
178
+
179
+ # Apply package manager options (only when using packages list)
180
+ pm_options = self._get_package_manager_options()
181
+ if pm_options and pm_key in runtime_env and isinstance(runtime_env[pm_key], dict):
182
+ for key, value in pm_options.items():
183
+ runtime_env[pm_key][key] = value
184
+
185
+ # Add env vars
186
+ runtime_env.setdefault('env_vars', {})
187
+ runtime_env['env_vars'].update(self._env.to_dict())
188
+
189
+ # Include Synapse credentials for backend client on workers
190
+ from synapse_sdk.utils.auth import ENV_SYNAPSE_ACCESS_TOKEN, ENV_SYNAPSE_HOST, load_credentials
191
+
192
+ host, token = load_credentials()
193
+ if host and ENV_SYNAPSE_HOST not in runtime_env['env_vars']:
194
+ runtime_env['env_vars'][ENV_SYNAPSE_HOST] = host
195
+ if token and ENV_SYNAPSE_ACCESS_TOKEN not in runtime_env['env_vars']:
196
+ runtime_env['env_vars'][ENV_SYNAPSE_ACCESS_TOKEN] = token
197
+
198
+ return runtime_env
199
+
200
+ def _get_package_manager_options(self) -> dict[str, Any]:
201
+ """Get package manager options with defaults.
202
+
203
+ Returns:
204
+ Dict of package manager options.
205
+ """
206
+ user_options = self._package_manager_options or []
207
+
208
+ if self._package_manager == PackageManager.UV:
209
+ defaults = ['--no-cache']
210
+ options_list = defaults.copy()
211
+ for opt in user_options:
212
+ if opt not in options_list:
213
+ options_list.append(opt)
214
+ return {'uv_pip_install_options': options_list}
215
+ else:
216
+ # pip - use pip_install_options with --upgrade flag
217
+ defaults = ['--upgrade']
218
+ options_list = defaults.copy()
219
+ for opt in user_options:
220
+ if opt not in options_list:
221
+ options_list.append(opt)
222
+ return {'pip_install_options': options_list}
223
+
224
+ def _get_requirements(self) -> list[str] | None:
225
+ """Get requirements from file.
226
+
227
+ Returns:
228
+ List of requirements, or None if no requirements file found.
229
+ """
230
+ # Explicit requirements file takes priority
231
+ if self._requirements_file:
232
+ return read_requirements(self._requirements_file)
233
+
234
+ # Auto-discover from working_dir
235
+ if self._working_dir:
236
+ req_path = self._working_dir / 'requirements.txt'
237
+ return read_requirements(req_path)
238
+
239
+ return None
240
+
241
+ def _get_requirements_file_path(self) -> Path | None:
242
+ """Get path to requirements.txt file if it exists.
243
+
244
+ Returns:
245
+ Path to requirements.txt, or None if not found.
246
+ """
247
+ if self._requirements_file and Path(self._requirements_file).exists():
248
+ return Path(self._requirements_file)
249
+
250
+ if self._working_dir:
251
+ req_path = self._working_dir / 'requirements.txt'
252
+ if req_path.exists():
253
+ return req_path
254
+
255
+ return None
256
+
257
+ def _get_wheel_files(self) -> list[str]:
258
+ """Get wheel file paths for Ray runtime env.
259
+
260
+ Scans the wheels_dir for .whl files and returns them as Ray-compatible
261
+ paths using ${RAY_RUNTIME_ENV_CREATE_WORKING_DIR}.
262
+
263
+ Returns:
264
+ List of wheel file paths for Ray.
265
+ """
266
+ if not self._working_dir:
267
+ return []
268
+
269
+ wheels_path = self._working_dir / self._wheels_dir
270
+ if not wheels_path.exists():
271
+ return []
272
+
273
+ wheel_files = []
274
+ for whl in wheels_path.glob('*.whl'):
275
+ # Use Ray's working dir variable for the path
276
+ ray_path = f'${{RAY_RUNTIME_ENV_CREATE_WORKING_DIR}}/{self._wheels_dir}/{whl.name}'
277
+ wheel_files.append(ray_path)
278
+
279
+ return wheel_files
280
+
281
+
282
+ __all__ = ['BaseRayExecutor', 'read_requirements']