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,50 +1,338 @@
1
- import json
1
+ """Plugin utilities for configuration parsing and action discovery."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
2
6
  from pathlib import Path
7
+ from typing import TYPE_CHECKING, Any, get_args, get_origin
8
+
9
+ from pydantic import BaseModel
10
+ from pydantic.fields import FieldInfo
11
+ from pydantic_core import PydanticUndefined
12
+
13
+ from synapse_sdk.plugins.enums import RunMethod
14
+ from synapse_sdk.utils.file.requirements import read_requirements
15
+
16
+ if TYPE_CHECKING:
17
+ from synapse_sdk.plugins.config import PluginConfig
18
+
19
+
20
+ def get_plugin_actions(config: dict | PluginConfig | Path | str) -> list[str]:
21
+ """Extract action names from plugin configuration.
22
+
23
+ Args:
24
+ config: Plugin config dict, PluginConfig instance, or path to config.yaml
25
+
26
+ Returns:
27
+ List of action names. Returns empty list on error.
28
+ """
29
+ from synapse_sdk.plugins.config import PluginConfig
30
+
31
+ if isinstance(config, (str, Path)):
32
+ config = _load_config_from_path(config)
33
+ if isinstance(config, PluginConfig):
34
+ return list(config.actions.keys())
35
+ if isinstance(config, dict):
36
+ return list(config.get('actions', {}).keys())
37
+ return []
38
+
39
+
40
+ def get_action_method(config: dict | PluginConfig, action: str) -> RunMethod:
41
+ """Get the run method for an action from config.
42
+
43
+ Args:
44
+ config: Plugin config dict or PluginConfig instance
45
+ action: Action name
46
+
47
+ Returns:
48
+ RunMethod enum value. Defaults to TASK if not found.
49
+ """
50
+ from synapse_sdk.plugins.config import PluginConfig
51
+
52
+ if isinstance(config, PluginConfig):
53
+ action_config = config.actions.get(action)
54
+ if action_config:
55
+ return action_config.method
56
+ elif isinstance(config, dict):
57
+ actions = config.get('actions', {})
58
+ action_config = actions.get(action, {})
59
+ method = action_config.get('method', 'task')
60
+ return RunMethod(method)
61
+ return RunMethod.TASK
62
+
63
+
64
+ def get_action_config(config: dict | PluginConfig, action: str) -> dict:
65
+ """Get the full configuration for a specific action.
66
+
67
+ Args:
68
+ config: Plugin config dict or PluginConfig instance
69
+ action: Action name
70
+
71
+ Returns:
72
+ Action configuration dictionary
73
+
74
+ Raises:
75
+ KeyError: If action not found
76
+ ValueError: If config type is invalid
77
+ """
78
+ from synapse_sdk.plugins.config import PluginConfig
79
+
80
+ if isinstance(config, PluginConfig):
81
+ action_cfg = config.actions.get(action)
82
+ if action_cfg:
83
+ return action_cfg.model_dump()
84
+ raise KeyError(f"Action '{action}' not found. Available: {list(config.actions.keys())}")
85
+ elif isinstance(config, dict):
86
+ actions = config.get('actions', {})
87
+ if action in actions:
88
+ return actions[action]
89
+ raise KeyError(f"Action '{action}' not found. Available: {list(actions.keys())}")
90
+ raise ValueError('Invalid config type')
91
+
92
+
93
+ def _load_config_from_path(path: Path | str) -> dict:
94
+ """Load plugin config from YAML file.
95
+
96
+ Args:
97
+ path: Path to config.yaml or directory containing it
98
+
99
+ Returns:
100
+ Parsed config dictionary
101
+ """
102
+ import yaml
103
+
104
+ path = Path(path)
105
+ if path.is_dir():
106
+ path = path / 'config.yaml'
107
+ with path.open() as f:
108
+ return yaml.safe_load(f)
109
+
110
+
111
+ # =============================================================================
112
+ # UI Schema Generation
113
+ # =============================================================================
114
+
115
+
116
+ def _type_to_formkit(annotation: type | None) -> str:
117
+ """Convert Python type annotation to FormKit input type.
118
+
119
+ Args:
120
+ annotation: Python type annotation (int, float, str, bool, etc.)
121
+
122
+ Returns:
123
+ FormKit input type string
124
+ """
125
+ if annotation is None:
126
+ return 'text'
127
+
128
+ # Handle Optional types
129
+ origin = get_origin(annotation)
130
+ if origin is type(None):
131
+ return 'text'
132
+
133
+ # Unwrap Optional/Union types
134
+ if origin is not None:
135
+ args = get_args(annotation)
136
+ # Filter out NoneType for Optional[X]
137
+ non_none_args = [a for a in args if a is not type(None)]
138
+ if non_none_args:
139
+ annotation = non_none_args[0]
140
+
141
+ # Map Python types to FormKit types
142
+ type_map: dict[type, str] = {
143
+ int: 'number',
144
+ float: 'number',
145
+ str: 'text',
146
+ bool: 'checkbox',
147
+ list: 'checkbox', # Multi-select
148
+ }
149
+
150
+ return type_map.get(annotation, 'text') # type: ignore[arg-type]
151
+
152
+
153
+ def _to_label(name: str) -> str:
154
+ """Convert snake_case field name to Title Case label.
155
+
156
+ Args:
157
+ name: Field name in snake_case (e.g., "batch_size")
158
+
159
+ Returns:
160
+ Human-readable label (e.g., "Batch Size")
161
+
162
+ Example:
163
+ >>> _to_label("batch_size")
164
+ "Batch Size"
165
+ >>> _to_label("learning_rate")
166
+ "Learning Rate"
167
+ """
168
+ return re.sub(r'_', ' ', name).title()
169
+
170
+
171
+ def _extract_constraints(field_info: FieldInfo) -> dict[str, Any]:
172
+ """Extract validation constraints from Pydantic FieldInfo.
173
+
174
+ Converts Pydantic constraints (ge, le, gt, lt) to FormKit validation rules.
175
+
176
+ Args:
177
+ field_info: Pydantic FieldInfo object
178
+
179
+ Returns:
180
+ Dict with min, max, step if applicable
181
+ """
182
+ constraints: dict[str, Any] = {}
183
+
184
+ # Extract from metadata (Pydantic v2 style)
185
+ for meta in field_info.metadata:
186
+ meta_type = type(meta).__name__
187
+
188
+ if meta_type == 'Ge': # Greater than or equal
189
+ constraints['min'] = meta.ge
190
+ elif meta_type == 'Le': # Less than or equal
191
+ constraints['max'] = meta.le
192
+ elif meta_type == 'Gt': # Greater than (use as min)
193
+ constraints['min'] = meta.gt
194
+ elif meta_type == 'Lt': # Less than (use as max)
195
+ constraints['max'] = meta.lt
196
+ elif meta_type == 'MultipleOf': # Step value
197
+ constraints['step'] = meta.multiple_of
198
+
199
+ return constraints
200
+
201
+
202
+ def pydantic_to_ui_schema(model: type[BaseModel]) -> list[dict[str, Any]]:
203
+ """Convert a Pydantic model to FormKit UI schema format.
204
+
205
+ This generates a UI schema compatible with the legacy config.yaml format,
206
+ suitable for rendering forms in the frontend.
207
+
208
+ Args:
209
+ model: Pydantic BaseModel class with field definitions
210
+
211
+ Returns:
212
+ List of FormKit schema items, one per field
213
+
214
+ Example:
215
+ >>> from pydantic import BaseModel, Field
216
+ >>>
217
+ >>> class TrainParams(BaseModel):
218
+ ... epochs: int = Field(default=50, ge=1, le=1000)
219
+ ... batch_size: int = Field(default=8, ge=1, le=512)
220
+ ... learning_rate: float = Field(default=0.001)
221
+ ...
222
+ >>> schema = pydantic_to_ui_schema(TrainParams)
223
+ >>> schema[0]
224
+ {
225
+ '$formkit': 'number',
226
+ 'name': 'epochs',
227
+ 'label': 'Epochs',
228
+ 'value': 50,
229
+ 'placeholder': 50,
230
+ 'min': 1,
231
+ 'max': 1000,
232
+ 'number': True
233
+ }
234
+
235
+ Custom UI via json_schema_extra:
236
+ >>> class Params(BaseModel):
237
+ ... model_size: str = Field(
238
+ ... default="medium",
239
+ ... json_schema_extra={
240
+ ... "formkit": "select",
241
+ ... "options": ["small", "medium", "large"],
242
+ ... "help": "Model size selection"
243
+ ... }
244
+ ... )
245
+ """
246
+ schema: list[dict[str, Any]] = []
247
+
248
+ for name, field_info in model.model_fields.items():
249
+ # Start with basic item structure
250
+ item: dict[str, Any] = {
251
+ '$formkit': _type_to_formkit(field_info.annotation),
252
+ 'name': name,
253
+ 'label': _to_label(name),
254
+ }
255
+
256
+ # Add default value (check for PydanticUndefined)
257
+ if field_info.default is not PydanticUndefined and field_info.default is not None:
258
+ item['value'] = field_info.default
259
+ item['placeholder'] = field_info.default
260
+
261
+ # Add description as help text
262
+ if field_info.description:
263
+ item['help'] = field_info.description
264
+
265
+ # Check if field is required (no default)
266
+ if field_info.is_required():
267
+ item['required'] = True
268
+
269
+ # Add validation constraints
270
+ constraints = _extract_constraints(field_info)
271
+ item.update(constraints)
272
+
273
+ # Add number flag for numeric types
274
+ if item['$formkit'] == 'number':
275
+ item['number'] = True
276
+
277
+ # Apply custom overrides from json_schema_extra
278
+ if field_info.json_schema_extra:
279
+ extra = field_info.json_schema_extra
280
+ if callable(extra):
281
+ # Handle callable json_schema_extra
282
+ extra_dict: dict[str, Any] = {}
283
+ extra(extra_dict)
284
+ extra = extra_dict
3
285
 
4
- from synapse_sdk.plugins.categories.registry import _REGISTERED_ACTIONS, register_actions
5
- from synapse_sdk.plugins.enums import PluginCategory
6
- from synapse_sdk.utils.file import get_dict_from_file
286
+ if isinstance(extra, dict):
287
+ # Override formkit type if specified
288
+ if 'formkit' in extra:
289
+ item['$formkit'] = extra['formkit']
7
290
 
291
+ # Copy other properties
292
+ for key in ('options', 'help', 'required', 'step', 'min', 'max'):
293
+ if key in extra:
294
+ item[key] = extra[key]
8
295
 
9
- def get_action(action, params_data, *args, **kwargs):
10
- if isinstance(params_data, str):
11
- try:
12
- params = json.loads(params_data)
13
- except json.JSONDecodeError:
14
- params = get_dict_from_file(params_data)
15
- else:
16
- params = params_data
296
+ schema.append(item)
17
297
 
18
- config_data = kwargs.pop('config', False)
19
- if config_data:
20
- if isinstance(config_data, str):
21
- config = read_plugin_config(plugin_path=config_data)
22
- else:
23
- config = config_data
24
- else:
25
- config = read_plugin_config()
26
- category = config['category']
27
- return get_action_class(category, action)(params, config, *args, **kwargs)
298
+ return schema
28
299
 
29
300
 
30
- def get_action_class(category, action):
31
- register_actions()
32
- return _REGISTERED_ACTIONS[category][action]
301
+ def get_action_ui_schema(
302
+ model: type[BaseModel],
303
+ action_name: str | None = None,
304
+ ) -> dict[str, Any]:
305
+ """Get UI schema for an action's parameters.
33
306
 
307
+ Returns the schema in the format expected by the backend API.
34
308
 
35
- def get_available_actions(category):
36
- register_actions()
37
- return list(_REGISTERED_ACTIONS[category].keys())
309
+ Args:
310
+ model: Pydantic model class for action parameters
311
+ action_name: Optional action name for the response
38
312
 
313
+ Returns:
314
+ Dict with action name and ui_schemas list
39
315
 
40
- def get_plugin_categories():
41
- return [plugin_category.value for plugin_category in PluginCategory]
316
+ Example:
317
+ >>> schema = get_action_ui_schema(TrainParams, 'train')
318
+ >>> schema
319
+ {
320
+ 'action': 'train',
321
+ 'ui_schemas': [...]
322
+ }
323
+ """
324
+ return {
325
+ 'action': action_name,
326
+ 'ui_schemas': pydantic_to_ui_schema(model),
327
+ }
42
328
 
43
329
 
44
- def read_plugin_config(plugin_path=None):
45
- config_file_name = 'config.yaml'
46
- if plugin_path:
47
- config_path = Path(plugin_path) / config_file_name
48
- else:
49
- config_path = config_file_name
50
- return get_dict_from_file(config_path)
330
+ __all__ = [
331
+ 'get_plugin_actions',
332
+ 'get_action_method',
333
+ 'get_action_config',
334
+ 'read_requirements',
335
+ # Schema utilities
336
+ 'pydantic_to_ui_schema',
337
+ 'get_action_ui_schema',
338
+ ]
@@ -0,0 +1,25 @@
1
+ import os
2
+
3
+
4
+ def needs_sentry_init():
5
+ return os.getenv('SENTRY_DSN') is not None
6
+
7
+
8
+ def init_sentry():
9
+ import sentry_sdk
10
+ from sentry_sdk.integrations.ray import RayIntegration
11
+
12
+ dsn = os.getenv('SENTRY_DSN')
13
+ if dsn is None:
14
+ return
15
+
16
+ sentry_sdk.init(
17
+ dsn=dsn,
18
+ environment=os.getenv('DEPLOYMENT_TARGET', 'development'),
19
+ integrations=[RayIntegration()],
20
+ send_default_pii=True,
21
+ )
22
+
23
+
24
+ def worker_process_setup_hook(*_, **__):
25
+ init_sentry()
@@ -0,0 +1 @@
1
+ """Synapse SDK utilities."""
@@ -0,0 +1,74 @@
1
+ """Authentication utilities for SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from synapse_sdk.clients.backend import BackendClient
12
+
13
+ # Environment variable names
14
+ ENV_SYNAPSE_HOST = 'SYNAPSE_HOST'
15
+ ENV_SYNAPSE_ACCESS_TOKEN = 'SYNAPSE_ACCESS_TOKEN'
16
+
17
+ # Default host
18
+ DEFAULT_HOST = 'https://api.synapse.sh'
19
+
20
+ # Config file path
21
+ CONFIG_FILE = Path.home() / '.synapse' / 'config.json'
22
+
23
+
24
+ def load_credentials() -> tuple[str | None, str | None]:
25
+ """Load credentials from environment or config file.
26
+
27
+ Priority:
28
+ 1. Environment variables (SYNAPSE_HOST, SYNAPSE_ACCESS_TOKEN)
29
+ 2. Config file (~/.synapse/config.json)
30
+
31
+ Returns:
32
+ Tuple of (host, token). Either may be None if not found.
33
+ """
34
+ host = os.environ.get(ENV_SYNAPSE_HOST)
35
+ token = os.environ.get(ENV_SYNAPSE_ACCESS_TOKEN)
36
+
37
+ # Fall back to config.json
38
+ if not token and CONFIG_FILE.exists():
39
+ try:
40
+ config = json.loads(CONFIG_FILE.read_text())
41
+ if not host:
42
+ host = config.get('host')
43
+ if not token:
44
+ token = config.get('access_token')
45
+ except (json.JSONDecodeError, OSError):
46
+ pass
47
+
48
+ return host, token
49
+
50
+
51
+ def create_backend_client() -> BackendClient | None:
52
+ """Create a BackendClient from environment/credentials if available.
53
+
54
+ Returns:
55
+ BackendClient if credentials are available, None otherwise.
56
+ """
57
+ host, token = load_credentials()
58
+
59
+ if not token:
60
+ return None
61
+
62
+ from synapse_sdk.clients.backend import BackendClient
63
+
64
+ return BackendClient(base_url=host or DEFAULT_HOST, access_token=token)
65
+
66
+
67
+ __all__ = [
68
+ 'ENV_SYNAPSE_HOST',
69
+ 'ENV_SYNAPSE_ACCESS_TOKEN',
70
+ 'DEFAULT_HOST',
71
+ 'CONFIG_FILE',
72
+ 'load_credentials',
73
+ 'create_backend_client',
74
+ ]
@@ -0,0 +1,58 @@
1
+ """File utilities module."""
2
+
3
+ from .archive import (
4
+ ArchiveFilter,
5
+ ProgressCallback,
6
+ create_archive,
7
+ create_archive_from_git,
8
+ extract_archive,
9
+ get_archive_size,
10
+ list_archive_contents,
11
+ )
12
+ from .checksum import (
13
+ HashAlgorithm,
14
+ calculate_checksum,
15
+ calculate_checksum_from_bytes,
16
+ calculate_checksum_from_file_object,
17
+ verify_checksum,
18
+ )
19
+ from .download import (
20
+ adownload_file,
21
+ afiles_url_to_path,
22
+ afiles_url_to_path_from_objs,
23
+ download_file,
24
+ files_url_to_path,
25
+ files_url_to_path_from_objs,
26
+ )
27
+ from .io import get_dict_from_file, get_temp_path
28
+ from .requirements import read_requirements
29
+
30
+ __all__ = [
31
+ # I/O
32
+ 'get_temp_path',
33
+ 'get_dict_from_file',
34
+ # Download (sync)
35
+ 'download_file',
36
+ 'files_url_to_path',
37
+ 'files_url_to_path_from_objs',
38
+ # Download (async)
39
+ 'adownload_file',
40
+ 'afiles_url_to_path',
41
+ 'afiles_url_to_path_from_objs',
42
+ # Requirements
43
+ 'read_requirements',
44
+ # Checksum
45
+ 'HashAlgorithm',
46
+ 'calculate_checksum',
47
+ 'calculate_checksum_from_bytes',
48
+ 'calculate_checksum_from_file_object',
49
+ 'verify_checksum',
50
+ # Archive
51
+ 'ProgressCallback',
52
+ 'ArchiveFilter',
53
+ 'create_archive',
54
+ 'create_archive_from_git',
55
+ 'extract_archive',
56
+ 'list_archive_contents',
57
+ 'get_archive_size',
58
+ ]