synapse-sdk 1.0.0a35__py3-none-any.whl → 2025.11.7__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 (307) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +308 -5
  3. synapse_sdk/cli/alias/utils.py +1 -1
  4. synapse_sdk/cli/code_server.py +687 -0
  5. synapse_sdk/cli/config.py +440 -0
  6. synapse_sdk/cli/devtools.py +90 -0
  7. synapse_sdk/cli/plugin/publish.py +23 -15
  8. synapse_sdk/clients/agent/__init__.py +9 -3
  9. synapse_sdk/clients/agent/container.py +133 -0
  10. synapse_sdk/clients/agent/core.py +19 -0
  11. synapse_sdk/clients/agent/ray.py +298 -9
  12. synapse_sdk/clients/backend/__init__.py +28 -12
  13. synapse_sdk/clients/backend/annotation.py +9 -1
  14. synapse_sdk/clients/backend/core.py +31 -4
  15. synapse_sdk/clients/backend/data_collection.py +186 -0
  16. synapse_sdk/clients/backend/hitl.py +1 -1
  17. synapse_sdk/clients/backend/integration.py +4 -3
  18. synapse_sdk/clients/backend/ml.py +1 -1
  19. synapse_sdk/clients/backend/models.py +35 -1
  20. synapse_sdk/clients/base.py +309 -36
  21. synapse_sdk/clients/ray/serve.py +2 -0
  22. synapse_sdk/devtools/__init__.py +0 -0
  23. synapse_sdk/devtools/config.py +94 -0
  24. synapse_sdk/devtools/docs/.gitignore +20 -0
  25. synapse_sdk/devtools/docs/README.md +41 -0
  26. synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
  27. synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
  28. synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
  29. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  30. synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +29 -0
  31. synapse_sdk/devtools/docs/blog/authors.yml +25 -0
  32. synapse_sdk/devtools/docs/blog/tags.yml +19 -0
  33. synapse_sdk/devtools/docs/docs/api/clients/agent.md +43 -0
  34. synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
  35. synapse_sdk/devtools/docs/docs/api/clients/backend.md +420 -0
  36. synapse_sdk/devtools/docs/docs/api/clients/base.md +257 -0
  37. synapse_sdk/devtools/docs/docs/api/clients/core-mixin.md +477 -0
  38. synapse_sdk/devtools/docs/docs/api/clients/data-collection-mixin.md +422 -0
  39. synapse_sdk/devtools/docs/docs/api/clients/hitl-mixin.md +554 -0
  40. synapse_sdk/devtools/docs/docs/api/clients/index.md +391 -0
  41. synapse_sdk/devtools/docs/docs/api/clients/integration-mixin.md +571 -0
  42. synapse_sdk/devtools/docs/docs/api/clients/ml-mixin.md +578 -0
  43. synapse_sdk/devtools/docs/docs/api/clients/ray.md +342 -0
  44. synapse_sdk/devtools/docs/docs/api/index.md +52 -0
  45. synapse_sdk/devtools/docs/docs/api/plugins/categories.md +43 -0
  46. synapse_sdk/devtools/docs/docs/api/plugins/models.md +114 -0
  47. synapse_sdk/devtools/docs/docs/api/plugins/utils.md +328 -0
  48. synapse_sdk/devtools/docs/docs/categories.md +0 -0
  49. synapse_sdk/devtools/docs/docs/cli-usage.md +280 -0
  50. synapse_sdk/devtools/docs/docs/concepts/index.md +38 -0
  51. synapse_sdk/devtools/docs/docs/configuration.md +83 -0
  52. synapse_sdk/devtools/docs/docs/contributing.md +306 -0
  53. synapse_sdk/devtools/docs/docs/examples/index.md +29 -0
  54. synapse_sdk/devtools/docs/docs/faq.md +179 -0
  55. synapse_sdk/devtools/docs/docs/features/converters/index.md +455 -0
  56. synapse_sdk/devtools/docs/docs/features/index.md +24 -0
  57. synapse_sdk/devtools/docs/docs/features/utils/file.md +415 -0
  58. synapse_sdk/devtools/docs/docs/features/utils/network.md +378 -0
  59. synapse_sdk/devtools/docs/docs/features/utils/storage.md +57 -0
  60. synapse_sdk/devtools/docs/docs/features/utils/types.md +51 -0
  61. synapse_sdk/devtools/docs/docs/installation.md +94 -0
  62. synapse_sdk/devtools/docs/docs/introduction.md +47 -0
  63. synapse_sdk/devtools/docs/docs/plugins/categories/neural-net-plugins/train-action-overview.md +814 -0
  64. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
  65. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
  66. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
  67. synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
  68. synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
  69. synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
  70. synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
  71. synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +1092 -0
  72. synapse_sdk/devtools/docs/docs/plugins/plugins.md +852 -0
  73. synapse_sdk/devtools/docs/docs/quickstart.md +78 -0
  74. synapse_sdk/devtools/docs/docs/troubleshooting.md +519 -0
  75. synapse_sdk/devtools/docs/docs/tutorial-basics/_category_.json +8 -0
  76. synapse_sdk/devtools/docs/docs/tutorial-basics/congratulations.md +23 -0
  77. synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
  78. synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-document.md +57 -0
  79. synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-page.md +43 -0
  80. synapse_sdk/devtools/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
  81. synapse_sdk/devtools/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
  82. synapse_sdk/devtools/docs/docs/tutorial-extras/_category_.json +7 -0
  83. synapse_sdk/devtools/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
  84. synapse_sdk/devtools/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
  85. synapse_sdk/devtools/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
  86. synapse_sdk/devtools/docs/docs/tutorial-extras/translate-your-site.md +88 -0
  87. synapse_sdk/devtools/docs/docusaurus.config.ts +148 -0
  88. synapse_sdk/devtools/docs/i18n/ko/code.json +325 -0
  89. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/agent.md +43 -0
  90. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/annotation-mixin.md +289 -0
  91. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/backend.md +420 -0
  92. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/base.md +257 -0
  93. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/core-mixin.md +417 -0
  94. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/data-collection-mixin.md +356 -0
  95. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/hitl-mixin.md +192 -0
  96. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/index.md +391 -0
  97. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/integration-mixin.md +479 -0
  98. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ml-mixin.md +284 -0
  99. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ray.md +342 -0
  100. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/index.md +52 -0
  101. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/plugins/models.md +114 -0
  102. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/categories.md +0 -0
  103. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/cli-usage.md +280 -0
  104. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/concepts/index.md +38 -0
  105. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/configuration.md +83 -0
  106. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/contributing.md +306 -0
  107. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/index.md +29 -0
  108. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md +179 -0
  109. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/converters/index.md +30 -0
  110. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/index.md +24 -0
  111. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/file.md +415 -0
  112. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/network.md +378 -0
  113. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/storage.md +60 -0
  114. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/types.md +51 -0
  115. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/installation.md +94 -0
  116. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/introduction.md +47 -0
  117. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/neural-net-plugins/train-action-overview.md +815 -0
  118. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
  119. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
  120. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
  121. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
  122. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
  123. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
  124. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
  125. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +1092 -0
  126. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/plugins.md +117 -0
  127. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/quickstart.md +78 -0
  128. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/troubleshooting.md +519 -0
  129. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current.json +34 -0
  130. synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/footer.json +42 -0
  131. synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/navbar.json +18 -0
  132. synapse_sdk/devtools/docs/package-lock.json +18784 -0
  133. synapse_sdk/devtools/docs/package.json +48 -0
  134. synapse_sdk/devtools/docs/sidebars.ts +122 -0
  135. synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
  136. synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  137. synapse_sdk/devtools/docs/src/css/custom.css +30 -0
  138. synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
  139. synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
  140. synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
  141. synapse_sdk/devtools/docs/static/.nojekyll +0 -0
  142. synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
  143. synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
  144. synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
  145. synapse_sdk/devtools/docs/static/img/logo.png +0 -0
  146. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  147. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
  148. synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  149. synapse_sdk/devtools/docs/tsconfig.json +8 -0
  150. synapse_sdk/devtools/server.py +41 -0
  151. synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
  152. synapse_sdk/devtools/streamlit_app/app.py +128 -0
  153. synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
  154. synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
  155. synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
  156. synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
  157. synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
  158. synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
  159. synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
  160. synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
  161. synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
  162. synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
  163. synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
  164. synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
  165. synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
  166. synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
  167. synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
  168. synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
  169. synapse_sdk/devtools/streamlit_app.py +10 -0
  170. synapse_sdk/loggers.py +65 -7
  171. synapse_sdk/plugins/README.md +1340 -0
  172. synapse_sdk/plugins/categories/base.py +73 -11
  173. synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
  174. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
  175. synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
  176. synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
  177. synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
  178. synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
  179. synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
  180. synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
  181. synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
  182. synapse_sdk/plugins/categories/export/actions/{export.py → export/utils.py} +47 -82
  183. synapse_sdk/plugins/categories/export/templates/config.yaml +19 -1
  184. synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
  185. synapse_sdk/plugins/categories/export/templates/plugin/export.py +153 -129
  186. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +9 -62
  187. synapse_sdk/plugins/categories/neural_net/actions/train.py +1062 -32
  188. synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
  189. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +27 -5
  190. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
  191. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
  192. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
  193. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/action.py +10 -0
  194. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
  195. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +145 -0
  196. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
  197. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
  198. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
  199. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +97 -0
  200. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +250 -0
  201. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
  202. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
  203. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +287 -0
  204. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
  205. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
  206. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +87 -0
  207. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +127 -0
  208. synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
  209. synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +966 -0
  210. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
  211. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
  212. synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
  213. synapse_sdk/plugins/categories/upload/actions/upload/action.py +232 -0
  214. synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
  215. synapse_sdk/plugins/categories/upload/actions/upload/enums.py +471 -0
  216. synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
  217. synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
  218. synapse_sdk/plugins/categories/upload/actions/upload/models.py +203 -0
  219. synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
  220. synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
  221. synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
  222. synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
  223. synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
  224. synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
  225. synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
  226. synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +84 -0
  227. synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
  228. synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
  229. synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +203 -0
  230. synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +97 -0
  231. synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
  232. synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
  233. synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
  234. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
  235. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
  236. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
  237. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
  238. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +258 -0
  239. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +281 -0
  240. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
  241. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
  242. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
  243. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
  244. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
  245. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
  246. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
  247. synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
  248. synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
  249. synapse_sdk/plugins/categories/upload/templates/config.yaml +29 -2
  250. synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +294 -0
  251. synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +88 -30
  252. synapse_sdk/plugins/models.py +122 -16
  253. synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
  254. synapse_sdk/plugins/templates/schema.json +491 -0
  255. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
  256. synapse_sdk/plugins/utils/__init__.py +46 -0
  257. synapse_sdk/plugins/utils/actions.py +119 -0
  258. synapse_sdk/plugins/utils/config.py +203 -0
  259. synapse_sdk/plugins/{utils.py → utils/legacy.py} +26 -46
  260. synapse_sdk/plugins/utils/ray_gcs.py +66 -0
  261. synapse_sdk/plugins/utils/registry.py +58 -0
  262. synapse_sdk/shared/__init__.py +25 -0
  263. synapse_sdk/shared/enums.py +93 -0
  264. synapse_sdk/utils/converters/__init__.py +240 -0
  265. synapse_sdk/utils/converters/coco/__init__.py +0 -0
  266. synapse_sdk/utils/converters/coco/from_dm.py +322 -0
  267. synapse_sdk/utils/converters/coco/to_dm.py +215 -0
  268. synapse_sdk/utils/converters/dm/__init__.py +56 -0
  269. synapse_sdk/utils/converters/dm/from_v1.py +627 -0
  270. synapse_sdk/utils/converters/dm/to_v1.py +367 -0
  271. synapse_sdk/utils/converters/pascal/__init__.py +0 -0
  272. synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
  273. synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
  274. synapse_sdk/utils/converters/yolo/__init__.py +0 -0
  275. synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
  276. synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
  277. synapse_sdk/utils/dataset.py +46 -0
  278. synapse_sdk/utils/encryption.py +158 -0
  279. synapse_sdk/utils/file/__init__.py +39 -0
  280. synapse_sdk/utils/file/archive.py +32 -0
  281. synapse_sdk/utils/file/checksum.py +56 -0
  282. synapse_sdk/utils/file/chunking.py +31 -0
  283. synapse_sdk/utils/file/download.py +385 -0
  284. synapse_sdk/utils/file/encoding.py +40 -0
  285. synapse_sdk/utils/file/io.py +22 -0
  286. synapse_sdk/utils/file/video/__init__.py +29 -0
  287. synapse_sdk/utils/file/video/transcode.py +307 -0
  288. synapse_sdk/utils/{file.py → file.py.backup} +84 -2
  289. synapse_sdk/utils/http.py +138 -0
  290. synapse_sdk/utils/network.py +293 -0
  291. synapse_sdk/utils/storage/__init__.py +36 -2
  292. synapse_sdk/utils/storage/providers/__init__.py +141 -0
  293. synapse_sdk/utils/storage/providers/file_system.py +134 -0
  294. synapse_sdk/utils/storage/providers/http.py +190 -0
  295. synapse_sdk/utils/storage/providers/s3.py +54 -6
  296. synapse_sdk/utils/storage/providers/sftp.py +31 -0
  297. synapse_sdk/utils/storage/registry.py +6 -0
  298. synapse_sdk-2025.11.7.dist-info/METADATA +122 -0
  299. synapse_sdk-2025.11.7.dist-info/RECORD +386 -0
  300. {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/WHEEL +1 -1
  301. synapse_sdk/clients/backend/dataset.py +0 -102
  302. synapse_sdk/plugins/categories/upload/actions/upload.py +0 -293
  303. synapse_sdk-1.0.0a35.dist-info/METADATA +0 -47
  304. synapse_sdk-1.0.0a35.dist-info/RECORD +0 -137
  305. {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/entry_points.txt +0 -0
  306. {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/licenses/LICENSE +0 -0
  307. {synapse_sdk-1.0.0a35.dist-info → synapse_sdk-2025.11.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1340 @@
1
+ # Synapse SDK Plugin System - Developer Reference
2
+
3
+ This document provides comprehensive guidance for developers working on the Synapse SDK plugin system architecture, internal APIs, and core infrastructure.
4
+
5
+ ## Overview
6
+
7
+ The Synapse SDK plugin system is a modular framework that enables distributed execution of ML operations across different categories and execution methods. The system is built around the concept of **actions** - discrete operations that can be packaged, distributed, and executed in various environments.
8
+
9
+ ### Architecture
10
+
11
+ ```
12
+ synapse_sdk/plugins/
13
+ ├── categories/ # Plugin category implementations
14
+ │ ├── base.py # Action base class
15
+ │ ├── decorators.py # Registration decorators
16
+ │ ├── registry.py # Action registry
17
+ │ ├── neural_net/ # Neural network actions
18
+ │ ├── export/ # Data export actions
19
+ │ ├── upload/ # File upload actions
20
+ │ ├── smart_tool/ # AI-powered tools
21
+ │ ├── pre_annotation/ # Pre-processing actions
22
+ │ ├── post_annotation/ # Post-processing actions
23
+ │ └── data_validation/ # Validation actions
24
+ ├── templates/ # Cookiecutter templates
25
+ ├── utils/ # Utility functions
26
+ ├── models.py # Core plugin models
27
+ ├── enums.py # Plugin enums
28
+ └── exceptions.py # Plugin exceptions
29
+ ```
30
+
31
+ ### Key Features
32
+
33
+ - **🔌 Modular Architecture**: Self-contained plugins with isolated dependencies
34
+ - **⚡ Multiple Execution Methods**: Jobs, Tasks, and REST API endpoints
35
+ - **📦 Distributed Execution**: Ray-based scalable computing
36
+ - **🛠️ Template System**: Cookiecutter-based scaffolding
37
+ - **📊 Progress Tracking**: Built-in logging, metrics, and progress monitoring
38
+ - **🔄 Dynamic Loading**: Runtime plugin discovery and registration
39
+
40
+ ## Core Components
41
+
42
+ ### Action Base Class
43
+
44
+ The `Action` class (`synapse_sdk/plugins/categories/base.py`) provides the unified interface for all plugin actions:
45
+
46
+ ```python
47
+ class Action:
48
+ """Base class for all plugin actions.
49
+
50
+ Class Variables:
51
+ name (str): Action identifier
52
+ category (PluginCategory): Plugin category
53
+ method (RunMethod): Execution method
54
+ run_class (Run): Run management class
55
+ params_model (BaseModel): Parameter validation model
56
+ progress_categories (Dict): Progress tracking categories
57
+ metrics_categories (Dict): Metrics collection categories
58
+
59
+ Instance Variables:
60
+ params (Dict): Validated action parameters
61
+ plugin_config (Dict): Plugin configuration
62
+ plugin_release (PluginRelease): Plugin metadata
63
+ client: Backend API client
64
+ run (Run): Execution instance
65
+ """
66
+
67
+ # Class configuration
68
+ name = None
69
+ category = None
70
+ method = None
71
+ run_class = Run
72
+ params_model = None
73
+ progress_categories = None
74
+ metrics_categories = None
75
+
76
+ def start(self):
77
+ """Main action logic - implement in subclasses."""
78
+ raise NotImplementedError
79
+ ```
80
+
81
+ ### Plugin Categories
82
+
83
+ The system supports seven main categories defined in `enums.py`:
84
+
85
+ ```python
86
+ class PluginCategory(Enum):
87
+ NEURAL_NET = 'neural_net' # ML training and inference
88
+ EXPORT = 'export' # Data export operations
89
+ UPLOAD = 'upload' # File upload functionality
90
+ SMART_TOOL = 'smart_tool' # AI-powered automation
91
+ POST_ANNOTATION = 'post_annotation' # Post-processing
92
+ PRE_ANNOTATION = 'pre_annotation' # Pre-processing
93
+ DATA_VALIDATION = 'data_validation' # Quality checks
94
+ ```
95
+
96
+ ### Execution Methods
97
+
98
+ Three execution methods are supported:
99
+
100
+ ```python
101
+ class RunMethod(Enum):
102
+ JOB = 'job' # Long-running distributed tasks
103
+ TASK = 'task' # Simple operations
104
+ RESTAPI = 'restapi' # HTTP endpoints
105
+ ```
106
+
107
+ ### Run Management
108
+
109
+ The `Run` class (`models.py`) manages action execution:
110
+
111
+ ```python
112
+ class Run(BaseModel):
113
+ """Manages plugin execution lifecycle.
114
+
115
+ Key Methods:
116
+ log_message(message, context): Log execution messages
117
+ set_progress(current, total, category): Update progress
118
+ set_metrics(metrics, category): Record metrics
119
+ log(log_type, data): Structured logging
120
+ """
121
+
122
+ def log_message(self, message: str, context: str = 'INFO'):
123
+ """Log execution messages with context."""
124
+
125
+ def set_progress(self, current: int, total: int, category: str = None):
126
+ """Update progress tracking."""
127
+
128
+ def set_metrics(self, metrics: dict, category: str):
129
+ """Record execution metrics."""
130
+ ```
131
+
132
+ ## Creating Plugin Categories
133
+
134
+ ### 1. Define Category Structure
135
+
136
+ Create a new category directory:
137
+
138
+ ```
139
+ synapse_sdk/plugins/categories/my_category/
140
+ ├── __init__.py
141
+ ├── actions/
142
+ │ ├── __init__.py
143
+ │ └── my_action.py
144
+ └── templates/
145
+ └── plugin/
146
+ ├── __init__.py
147
+ └── my_action.py
148
+ ```
149
+
150
+ ### 2. Implement Base Action
151
+
152
+ ```python
153
+ # synapse_sdk/plugins/categories/my_category/actions/my_action.py
154
+ from synapse_sdk.plugins.categories.base import Action
155
+ from synapse_sdk.plugins.categories.decorators import register_action
156
+ from synapse_sdk.plugins.enums import PluginCategory, RunMethod
157
+ from pydantic import BaseModel
158
+
159
+ class MyActionParams(BaseModel):
160
+ """Parameter model for validation."""
161
+ input_path: str
162
+ output_path: str
163
+ config: dict = {}
164
+
165
+ @register_action
166
+ class MyAction(Action):
167
+ """Base implementation for my_category actions."""
168
+
169
+ name = 'my_action'
170
+ category = PluginCategory.MY_CATEGORY
171
+ method = RunMethod.JOB
172
+ params_model = MyActionParams
173
+
174
+ progress_categories = {
175
+ 'preprocessing': {'proportion': 20},
176
+ 'processing': {'proportion': 60},
177
+ 'postprocessing': {'proportion': 20}
178
+ }
179
+
180
+ metrics_categories = {
181
+ 'performance': {
182
+ 'throughput': 0,
183
+ 'latency': 0,
184
+ 'accuracy': 0
185
+ }
186
+ }
187
+
188
+ def start(self):
189
+ """Main execution logic."""
190
+ self.run.log_message("Starting my action...")
191
+
192
+ # Access validated parameters
193
+ input_path = self.params['input_path']
194
+ output_path = self.params['output_path']
195
+
196
+ # Update progress
197
+ self.run.set_progress(0, 100, 'preprocessing')
198
+
199
+ # Your implementation here
200
+ result = self.process_data(input_path, output_path)
201
+
202
+ # Record metrics
203
+ self.run.set_metrics({
204
+ 'throughput': result['throughput'],
205
+ 'items_processed': result['count']
206
+ }, 'performance')
207
+
208
+ self.run.log_message("Action completed successfully")
209
+ return result
210
+
211
+ def process_data(self, input_path, output_path):
212
+ """Implement category-specific logic."""
213
+ raise NotImplementedError("Subclasses must implement process_data")
214
+ ```
215
+
216
+ ### 3. Create Template
217
+
218
+ ```python
219
+ # synapse_sdk/plugins/categories/my_category/templates/plugin/my_action.py
220
+ from synapse_sdk.plugins.categories.my_category import MyAction as BaseMyAction
221
+
222
+ class MyAction(BaseMyAction):
223
+ """Custom implementation of my_action."""
224
+
225
+ def process_data(self, input_path, output_path):
226
+ """Custom data processing logic."""
227
+ # Plugin developer implements this
228
+ return {"status": "success", "items_processed": 100}
229
+ ```
230
+
231
+ ### 4. Register Category
232
+
233
+ Update `enums.py`:
234
+
235
+ ```python
236
+ class PluginCategory(Enum):
237
+ # ... existing categories
238
+ MY_CATEGORY = 'my_category'
239
+ ```
240
+
241
+ ## Action Implementation Examples
242
+
243
+ ### Upload Action Architecture
244
+
245
+ The upload action demonstrates modular action architecture:
246
+
247
+ ```
248
+ # Structure after SYN-5306 refactoring
249
+ synapse_sdk/plugins/categories/upload/actions/upload/
250
+ ├── __init__.py # Public API exports
251
+ ├── action.py # Main UploadAction class
252
+ ├── run.py # UploadRun execution management
253
+ ├── models.py # UploadParams validation
254
+ ├── enums.py # LogCode and LOG_MESSAGES
255
+ ├── exceptions.py # Custom exceptions
256
+ └── utils.py # Utility classes
257
+ ```
258
+
259
+ **Key Implementation Details:**
260
+
261
+ ```python
262
+ # upload/action.py
263
+ @register_action
264
+ class UploadAction(Action):
265
+ name = 'upload'
266
+ category = PluginCategory.UPLOAD
267
+ method = RunMethod.JOB
268
+ run_class = UploadRun
269
+
270
+ def start(self):
271
+ # Comprehensive upload workflow
272
+ storage_id = self.params.get('storage')
273
+ path = self.params.get('path')
274
+
275
+ # Setup and validation
276
+ storage = self.client.get_storage(storage_id)
277
+ pathlib_cwd = get_pathlib(storage, path)
278
+
279
+ # Excel metadata processing
280
+ excel_metadata = self._read_excel_metadata(pathlib_cwd)
281
+
282
+ # File organization and upload
283
+ file_specification = self._analyze_collection()
284
+ organized_files = self._organize_files(pathlib_cwd, file_specification, excel_metadata)
285
+
286
+ # Upload files
287
+ uploaded_files = self._upload_files(organized_files)
288
+
289
+ # Data unit generation
290
+ generated_data_units = self._generate_data_units(uploaded_files, batch_size)
291
+
292
+ return {
293
+ 'uploaded_files_count': len(uploaded_files),
294
+ 'generated_data_units_count': len(generated_data_units)
295
+ }
296
+ ```
297
+
298
+ ## Plugin Action Structure Guidelines
299
+
300
+ For complex actions that require multiple components, follow the modular structure pattern established by the refactored upload action. This approach improves maintainability, testability, and code organization.
301
+
302
+ ## Complex Action Refactoring Patterns
303
+
304
+ ### Overview
305
+
306
+ As plugin actions evolve and grow in complexity, they often become monolithic files with 900+ lines containing multiple responsibilities. The SYN-5398 UploadAction refactoring demonstrates how to break down complex actions using **Strategy** and **Facade** design patterns, transforming a 1,600+ line monolithic implementation into a maintainable, testable architecture.
307
+
308
+ ### When to Apply Complex Refactoring
309
+
310
+ Consider refactoring when your action exhibits:
311
+
312
+ - **Size**: 900+ lines in a single method or file
313
+ - **Multiple Responsibilities**: Handling validation, file processing, uploads, metadata extraction in one method
314
+ - **Conditional Complexity**: Multiple if/else branches for different processing strategies
315
+ - **Testing Difficulty**: Hard to unit test individual components
316
+ - **Maintenance Issues**: Changes require touching multiple unrelated sections
317
+
318
+ ### Strategy Pattern for Pluggable Behaviors
319
+
320
+ The Strategy pattern allows you to define a family of algorithms, encapsulate each one, and make them interchangeable. This is ideal for actions that need different implementations of the same behavior.
321
+
322
+ #### Core Strategy Types
323
+
324
+ Based on the UploadAction refactoring, identify these key strategy categories:
325
+
326
+ ```python
327
+ # 1. ValidationStrategy - Parameter and environment validation
328
+ class ValidationStrategy(ABC):
329
+ @abstractmethod
330
+ def validate(self, context: ActionContext) -> ValidationResult:
331
+ """Validate parameters and environment."""
332
+ pass
333
+
334
+ class UploadValidationStrategy(ValidationStrategy):
335
+ def validate(self, context: ActionContext) -> ValidationResult:
336
+ # Validate storage, collection, file paths
337
+ return ValidationResult(is_valid=True, errors=[])
338
+
339
+ # 2. FileDiscoveryStrategy - Different file discovery methods
340
+ class FileDiscoveryStrategy(ABC):
341
+ @abstractmethod
342
+ def discover_files(self, context: ActionContext) -> List[Path]:
343
+ """Discover files to process."""
344
+ pass
345
+
346
+ class RecursiveFileDiscoveryStrategy(FileDiscoveryStrategy):
347
+ def discover_files(self, context: ActionContext) -> List[Path]:
348
+ # Recursive directory traversal
349
+ return list(context.base_path.rglob("*"))
350
+
351
+ # 3. MetadataStrategy - Various metadata extraction approaches
352
+ class MetadataStrategy(ABC):
353
+ @abstractmethod
354
+ def extract_metadata(self, files: List[Path], context: ActionContext) -> Dict:
355
+ """Extract metadata from files."""
356
+ pass
357
+
358
+ class ExcelMetadataStrategy(MetadataStrategy):
359
+ def extract_metadata(self, files: List[Path], context: ActionContext) -> Dict:
360
+ # Excel-specific metadata extraction
361
+ return {"excel_sheets": [], "total_rows": 0}
362
+
363
+ # 4. UploadStrategy - Different upload implementations
364
+ class UploadStrategy(ABC):
365
+ @abstractmethod
366
+ def upload_files(self, files: List[Path], context: ActionContext) -> List[UploadResult]:
367
+ """Upload files using specific strategy."""
368
+ pass
369
+
370
+ class StandardUploadStrategy(UploadStrategy):
371
+ def upload_files(self, files: List[Path], context: ActionContext) -> List[UploadResult]:
372
+ # Standard upload implementation
373
+ return [self._upload_single(f) for f in files]
374
+
375
+ # 5. DataUnitStrategy - Different data unit generation methods
376
+ class DataUnitStrategy(ABC):
377
+ @abstractmethod
378
+ def generate_data_units(self, uploaded_files: List[UploadResult], context: ActionContext) -> List[DataUnit]:
379
+ """Generate data units from uploaded files."""
380
+ pass
381
+
382
+ class StandardDataUnitStrategy(DataUnitStrategy):
383
+ def generate_data_units(self, uploaded_files: List[UploadResult], context: ActionContext) -> List[DataUnit]:
384
+ # Standard data unit creation
385
+ return [DataUnit(file=file) for file in uploaded_files]
386
+ ```
387
+
388
+ #### Strategy Factory Pattern
389
+
390
+ Use a factory to create appropriate strategies based on configuration:
391
+
392
+ ```python
393
+ class StrategyFactory:
394
+ """Factory for creating action strategies based on context."""
395
+
396
+ @staticmethod
397
+ def create_validation_strategy(context: ActionContext) -> ValidationStrategy:
398
+ if context.params.get('strict_validation', False):
399
+ return StrictValidationStrategy()
400
+ return StandardValidationStrategy()
401
+
402
+ @staticmethod
403
+ def create_upload_strategy(context: ActionContext) -> UploadStrategy:
404
+ return StandardUploadStrategy()
405
+
406
+ @staticmethod
407
+ def create_file_discovery_strategy(context: ActionContext) -> FileDiscoveryStrategy:
408
+ if context.params.get('is_recursive', False):
409
+ return RecursiveFileDiscoveryStrategy()
410
+ return FlatFileDiscoveryStrategy()
411
+ ```
412
+
413
+ ### Facade Pattern with Orchestrator
414
+
415
+ The Facade pattern provides a simplified interface to a complex subsystem. The **Orchestrator** coordinates all strategies through a step-based workflow.
416
+
417
+ #### Orchestrator Implementation
418
+
419
+ ```python
420
+ class UploadOrchestrator:
421
+ """Facade that orchestrates the complete upload workflow."""
422
+
423
+ def __init__(self, context: ActionContext):
424
+ self.context = context
425
+ self.factory = StrategyFactory()
426
+ self.steps_completed = []
427
+
428
+ # Initialize strategies
429
+ self.validation_strategy = self.factory.create_validation_strategy(context)
430
+ self.file_discovery_strategy = self.factory.create_file_discovery_strategy(context)
431
+ self.metadata_strategy = self.factory.create_metadata_strategy(context)
432
+ self.upload_strategy = self.factory.create_upload_strategy(context)
433
+ self.data_unit_strategy = self.factory.create_data_unit_strategy(context)
434
+
435
+ def execute_workflow(self) -> UploadResult:
436
+ """Execute the complete upload workflow with rollback support."""
437
+ try:
438
+ # Step 1: Setup and validation
439
+ self._execute_step("setup_validation", self._setup_and_validate)
440
+
441
+ # Step 2: File discovery
442
+ files = self._execute_step("file_discovery", self._discover_files)
443
+
444
+ # Step 3: Excel metadata extraction
445
+ metadata = self._execute_step("metadata_extraction",
446
+ lambda: self._extract_metadata(files))
447
+
448
+ # Step 4: File organization
449
+ organized_files = self._execute_step("file_organization",
450
+ lambda: self._organize_files(files, metadata))
451
+
452
+ # Step 5: File upload
453
+ uploaded_files = self._execute_step("file_upload",
454
+ lambda: self._upload_files(organized_files))
455
+
456
+ # Step 6: Data unit generation
457
+ data_units = self._execute_step("data_unit_generation",
458
+ lambda: self._generate_data_units(uploaded_files))
459
+
460
+ # Step 7: Cleanup
461
+ self._execute_step("cleanup", self._cleanup_temp_files)
462
+
463
+ # Step 8: Result aggregation
464
+ return self._execute_step("result_aggregation",
465
+ lambda: self._aggregate_results(uploaded_files, data_units))
466
+
467
+ except Exception as e:
468
+ self._rollback_completed_steps()
469
+ raise UploadOrchestrationError(f"Workflow failed at step {len(self.steps_completed)}: {e}")
470
+
471
+ def _execute_step(self, step_name: str, step_func: callable):
472
+ """Execute a workflow step with error handling and progress tracking."""
473
+ self.context.logger.log_message_with_code(LogCode.STEP_STARTED, step_name)
474
+
475
+ try:
476
+ result = step_func()
477
+ self.steps_completed.append(step_name)
478
+ self.context.logger.log_message_with_code(LogCode.STEP_COMPLETED, step_name)
479
+ return result
480
+ except Exception as e:
481
+ self.context.logger.log_message_with_code(LogCode.STEP_FAILED, step_name, str(e))
482
+ raise
483
+
484
+ def _setup_and_validate(self):
485
+ """Step 1: Setup and validation using strategy."""
486
+ validation_result = self.validation_strategy.validate(self.context)
487
+ if not validation_result.is_valid:
488
+ raise ValidationError(f"Validation failed: {validation_result.errors}")
489
+
490
+ def _discover_files(self) -> List[Path]:
491
+ """Step 2: File discovery using strategy."""
492
+ return self.file_discovery_strategy.discover_files(self.context)
493
+
494
+ def _extract_metadata(self, files: List[Path]) -> Dict:
495
+ """Step 3: Metadata extraction using strategy."""
496
+ return self.metadata_strategy.extract_metadata(files, self.context)
497
+
498
+ def _upload_files(self, files: List[Path]) -> List[UploadResult]:
499
+ """Step 5: File upload using strategy."""
500
+ return self.upload_strategy.upload_files(files, self.context)
501
+
502
+ def _generate_data_units(self, uploaded_files: List[UploadResult]) -> List[DataUnit]:
503
+ """Step 6: Data unit generation using strategy."""
504
+ return self.data_unit_strategy.generate_data_units(uploaded_files, self.context)
505
+
506
+ def _rollback_completed_steps(self):
507
+ """Rollback completed steps in reverse order."""
508
+ for step in reversed(self.steps_completed):
509
+ try:
510
+ rollback_method = getattr(self, f"_rollback_{step}", None)
511
+ if rollback_method:
512
+ rollback_method()
513
+ except Exception as e:
514
+ self.context.logger.log_message_with_code(LogCode.ROLLBACK_FAILED, step, str(e))
515
+ ```
516
+
517
+ #### Transformed Action Implementation
518
+
519
+ The main action becomes dramatically simplified:
520
+
521
+ ```python
522
+ # Before: 900+ line monolithic start() method
523
+ class UploadAction(Action):
524
+ def start(self):
525
+ # 900+ lines of mixed responsibilities...
526
+
527
+ # After: Clean, orchestrated implementation (196 lines total)
528
+ class UploadAction(Action):
529
+ """Upload action using Strategy and Facade patterns."""
530
+
531
+ def __init__(self, *args, **kwargs):
532
+ super().__init__(*args, **kwargs)
533
+ self.context = ActionContext(
534
+ params=self.params,
535
+ client=self.client,
536
+ logger=self.run,
537
+ workspace=self.get_workspace_path()
538
+ )
539
+
540
+ def start(self) -> UploadResult:
541
+ """Main action execution using orchestrator facade."""
542
+ self.run.log_message_with_code(LogCode.UPLOAD_STARTED)
543
+
544
+ try:
545
+ # Create and execute orchestrator
546
+ orchestrator = UploadOrchestrator(self.context)
547
+ result = orchestrator.execute_workflow()
548
+
549
+ self.run.log_message_with_code(LogCode.UPLOAD_COMPLETED,
550
+ result.uploaded_files_count, result.data_units_count)
551
+ return result
552
+
553
+ except Exception as e:
554
+ self.run.log_message_with_code(LogCode.UPLOAD_FAILED, str(e))
555
+ raise ActionError(f"Upload action failed: {e}")
556
+ ```
557
+
558
+ ### Context Management
559
+
560
+ Use a shared context object to pass state between strategies and orchestrator:
561
+
562
+ ```python
563
+ @dataclass
564
+ class ActionContext:
565
+ """Shared context for action execution."""
566
+ params: Dict[str, Any]
567
+ client: Any
568
+ logger: Any
569
+ workspace: Path
570
+ temp_files: List[Path] = field(default_factory=list)
571
+ metrics: Dict[str, Any] = field(default_factory=dict)
572
+
573
+ def add_temp_file(self, file_path: Path):
574
+ """Track temporary files for cleanup."""
575
+ self.temp_files.append(file_path)
576
+
577
+ def update_metrics(self, category: str, metrics: Dict[str, Any]):
578
+ """Update execution metrics."""
579
+ if category not in self.metrics:
580
+ self.metrics[category] = {}
581
+ self.metrics[category].update(metrics)
582
+ ```
583
+
584
+ ### Migration Guide for Complex Actions
585
+
586
+ #### Step 1: Identify Strategy Boundaries
587
+
588
+ Analyze your monolithic action to identify:
589
+
590
+ 1. **Different algorithms** for the same operation (validation methods, file processing approaches)
591
+ 2. **Configurable behaviors** that change based on parameters
592
+ 3. **Testable units** that can be isolated
593
+
594
+ #### Step 2: Extract Strategies
595
+
596
+ ```python
597
+ # Before: Mixed responsibilities in one method
598
+ def start(self):
599
+ # validation logic (100 lines)
600
+ if self.params.get('strict_mode'):
601
+ # strict validation
602
+ else:
603
+ # standard validation
604
+
605
+ # file discovery logic (150 lines)
606
+ if self.params.get('recursive'):
607
+ # recursive discovery
608
+ else:
609
+ # flat discovery
610
+
611
+ # upload logic (200 lines)
612
+ # Direct upload implementation
613
+ uploaded_files = self._upload_files(organized_files)
614
+
615
+ # After: Separated into strategies
616
+ class StrictValidationStrategy(ValidationStrategy): ...
617
+ class StandardValidationStrategy(ValidationStrategy): ...
618
+ class RecursiveFileDiscoveryStrategy(FileDiscoveryStrategy): ...
619
+ class StandardUploadStrategy(UploadStrategy): ...
620
+ ```
621
+
622
+ #### Step 3: Create Orchestrator
623
+
624
+ Design your workflow steps:
625
+
626
+ 1. Define clear step boundaries
627
+ 2. Implement rollback for each step
628
+ 3. Add progress tracking and logging
629
+ 4. Handle errors gracefully
630
+
631
+ #### Step 4: Update Action Class
632
+
633
+ Transform the main action to use the orchestrator:
634
+
635
+ 1. Create context object
636
+ 2. Initialize orchestrator
637
+ 3. Execute workflow
638
+ 4. Handle results and errors
639
+
640
+ ### Benefits and Metrics
641
+
642
+ The SYN-5398 refactoring achieved:
643
+
644
+ #### **Code Reduction**
645
+
646
+ - Main action: 900+ lines → 196 lines (-78% reduction)
647
+ - Total codebase: 1,600+ lines → 1,400+ lines (better organized)
648
+ - Cyclomatic complexity: High → Low (single responsibility per class)
649
+
650
+ #### **Improved Testability**
651
+
652
+ - **89 passing tests** with individual strategy testing
653
+ - **Isolated unit tests** for each strategy component
654
+ - **Integration tests** for orchestrator workflow
655
+ - **Rollback testing** for error scenarios
656
+
657
+ #### **Better Maintainability**
658
+
659
+ - **Single responsibility** per strategy class
660
+ - **Clear separation** of concerns
661
+ - **Reusable strategies** across different actions
662
+ - **Easy to extend** with new strategy implementations
663
+
664
+ #### **Enhanced Flexibility**
665
+
666
+ - **Runtime strategy selection** based on parameters
667
+ - **Pluggable algorithms** without changing core logic
668
+ - **Configuration-driven** behavior changes
669
+ - **A/B testing support** through strategy switching
670
+
671
+ ### Example: Converting a Complex Action
672
+
673
+ ```python
674
+ # Before: Monolithic action (simplified example)
675
+ class ComplexAction(Action):
676
+ def start(self):
677
+ # 50 lines of validation
678
+ if self.params.get('validation_type') == 'strict':
679
+ # strict validation logic
680
+ else:
681
+ # standard validation logic
682
+
683
+ # 100 lines of data processing
684
+ if self.params.get('processing_method') == 'batch':
685
+ # batch processing logic
686
+ else:
687
+ # stream processing logic
688
+
689
+ # 80 lines of output generation
690
+ if self.params.get('output_format') == 'json':
691
+ # JSON output logic
692
+ else:
693
+ # CSV output logic
694
+
695
+ # After: Strategy-based action
696
+ class ComplexAction(Action):
697
+ def start(self):
698
+ context = ActionContext(params=self.params, logger=self.run)
699
+ orchestrator = ComplexActionOrchestrator(context)
700
+ return orchestrator.execute_workflow()
701
+
702
+ # Individual strategies (testable, reusable)
703
+ class StrictValidationStrategy(ValidationStrategy): ...
704
+ class BatchProcessingStrategy(ProcessingStrategy): ...
705
+ class JSONOutputStrategy(OutputStrategy): ...
706
+ ```
707
+
708
+ This pattern transformation makes complex actions maintainable, testable, and extensible while preserving all original functionality.
709
+
710
+ ### Recommended File Structure
711
+
712
+ ```
713
+ synapse_sdk/plugins/categories/{category}/actions/{action}/
714
+ ├── __init__.py # Public API exports
715
+ ├── action.py # Main action implementation
716
+ ├── run.py # Execution and logging management
717
+ ├── models.py # Pydantic parameter models
718
+ ├── enums.py # Enums and message constants
719
+ ├── exceptions.py # Custom exception classes
720
+ ├── utils.py # Helper utilities and configurations
721
+ └── README.md # Action-specific documentation
722
+ ```
723
+
724
+ ### Module Responsibilities
725
+
726
+ #### 1. `__init__.py` - Public API
727
+
728
+ Defines the public interface and maintains backward compatibility:
729
+
730
+ ```python
731
+ # Export all public classes for backward compatibility
732
+ from .action import UploadAction
733
+ from .enums import LogCode, LOG_MESSAGES, UploadStatus
734
+ from .exceptions import ExcelParsingError, ExcelSecurityError
735
+ from .models import UploadParams
736
+ from .run import UploadRun
737
+ from .utils import ExcelSecurityConfig, PathAwareJSONEncoder
738
+
739
+ __all__ = [
740
+ 'UploadAction',
741
+ 'UploadRun',
742
+ 'UploadParams',
743
+ 'UploadStatus',
744
+ 'LogCode',
745
+ 'LOG_MESSAGES',
746
+ 'ExcelSecurityError',
747
+ 'ExcelParsingError',
748
+ 'PathAwareJSONEncoder',
749
+ 'ExcelSecurityConfig',
750
+ ]
751
+ ```
752
+
753
+ #### 2. `action.py` - Main Implementation
754
+
755
+ Contains the core action logic, inheriting from the base `Action` class:
756
+
757
+ ```python
758
+ from synapse_sdk.plugins.categories.base import Action
759
+ from synapse_sdk.plugins.enums import PluginCategory, RunMethod
760
+
761
+ from .enums import LogCode
762
+ from .models import UploadParams
763
+ from .run import UploadRun
764
+
765
+ class UploadAction(Action):
766
+ """Main upload action implementation."""
767
+
768
+ name = 'upload'
769
+ category = PluginCategory.UPLOAD
770
+ method = RunMethod.JOB
771
+ run_class = UploadRun
772
+ params_model = UploadParams
773
+
774
+ def start(self):
775
+ """Main action logic."""
776
+ # Validate parameters
777
+ self.validate_params()
778
+
779
+ # Log start
780
+ self.run.log_message_with_code(LogCode.UPLOAD_STARTED)
781
+
782
+ # Execute main logic
783
+ result = self._process_upload()
784
+
785
+ # Log completion
786
+ self.run.log_message_with_code(LogCode.UPLOAD_COMPLETED)
787
+
788
+ return result
789
+ ```
790
+
791
+ #### 3. `models.py` - Parameter Validation
792
+
793
+ Defines Pydantic models for type-safe parameter validation:
794
+
795
+ ```python
796
+ from typing import Annotated
797
+ from pydantic import AfterValidator, BaseModel, field_validator
798
+ from synapse_sdk.utils.pydantic.validators import non_blank
799
+
800
+ class UploadParams(BaseModel):
801
+ """Upload action parameters with validation."""
802
+
803
+ name: Annotated[str, AfterValidator(non_blank)]
804
+ description: str | None = None
805
+ path: str
806
+ storage: int
807
+ collection: int
808
+ project: int | None = None
809
+ is_recursive: bool = False
810
+ max_file_size_mb: int = 50
811
+
812
+ @field_validator('storage', mode='before')
813
+ @classmethod
814
+ def check_storage_exists(cls, value: str, info) -> str:
815
+ """Validate storage exists via API."""
816
+ action = info.context['action']
817
+ client = action.client
818
+ try:
819
+ client.get_storage(value)
820
+ except ClientError:
821
+ raise PydanticCustomError('client_error', 'Storage not found')
822
+ return value
823
+ ```
824
+
825
+ #### 4. `enums.py` - Constants and Enums
826
+
827
+ Centralizes all enum definitions and constant values:
828
+
829
+ ```python
830
+ from enum import Enum
831
+ from synapse_sdk.plugins.enums import Context
832
+
833
+ class UploadStatus(str, Enum):
834
+ """Upload processing status."""
835
+ PENDING = 'pending'
836
+ PROCESSING = 'processing'
837
+ COMPLETED = 'completed'
838
+ FAILED = 'failed'
839
+
840
+ class LogCode(str, Enum):
841
+ """Type-safe logging codes."""
842
+ UPLOAD_STARTED = 'UPLOAD_STARTED'
843
+ VALIDATION_FAILED = 'VALIDATION_FAILED'
844
+ NO_FILES_FOUND = 'NO_FILES_FOUND'
845
+ UPLOAD_COMPLETED = 'UPLOAD_COMPLETED'
846
+ # ... additional codes
847
+
848
+ LOG_MESSAGES = {
849
+ LogCode.UPLOAD_STARTED: {
850
+ 'message': 'Upload process started.',
851
+ 'level': Context.INFO,
852
+ },
853
+ LogCode.VALIDATION_FAILED: {
854
+ 'message': 'Validation failed: {}',
855
+ 'level': Context.DANGER,
856
+ },
857
+ # ... message configurations
858
+ }
859
+ ```
860
+
861
+ #### 5. `run.py` - Execution Management
862
+
863
+ Handles execution flow, progress tracking, and specialized logging:
864
+
865
+ ```python
866
+ from typing import Optional
867
+ from synapse_sdk.plugins.models import Run
868
+ from synapse_sdk.plugins.enums import Context
869
+
870
+ from .enums import LogCode, LOG_MESSAGES
871
+
872
+ class UploadRun(Run):
873
+ """Specialized run management for upload actions."""
874
+
875
+ def log_message_with_code(self, code: LogCode, *args, level: Optional[Context] = None):
876
+ """Type-safe logging with predefined messages."""
877
+ if code not in LOG_MESSAGES:
878
+ self.log_message(f'Unknown log code: {code}')
879
+ return
880
+
881
+ log_config = LOG_MESSAGES[code]
882
+ message = log_config['message'].format(*args) if args else log_config['message']
883
+ log_level = level or log_config['level']
884
+
885
+ self.log_message(message, context=log_level.value)
886
+
887
+ def log_upload_event(self, code: LogCode, *args, level: Optional[Context] = None):
888
+ """Log upload-specific events with metrics."""
889
+ self.log_message_with_code(code, *args, level)
890
+ # Additional upload-specific logging logic
891
+ ```
892
+
893
+ #### 6. `exceptions.py` - Custom Exceptions
894
+
895
+ Defines action-specific exception classes:
896
+
897
+ ```python
898
+ class ExcelSecurityError(Exception):
899
+ """Raised when Excel file security validation fails."""
900
+ pass
901
+
902
+ class ExcelParsingError(Exception):
903
+ """Raised when Excel file parsing encounters errors."""
904
+ pass
905
+
906
+ class UploadValidationError(Exception):
907
+ """Raised when upload parameter validation fails."""
908
+ pass
909
+ ```
910
+
911
+ #### 7. `utils.py` - Helper Utilities
912
+
913
+ Contains utility classes and helper functions:
914
+
915
+ ```python
916
+ import json
917
+ import os
918
+ from pathlib import Path
919
+
920
+ class PathAwareJSONEncoder(json.JSONEncoder):
921
+ """JSON encoder that handles Path objects."""
922
+
923
+ def default(self, obj):
924
+ if hasattr(obj, '__fspath__') or hasattr(obj, 'as_posix'):
925
+ return str(obj)
926
+ elif hasattr(obj, 'isoformat'):
927
+ return obj.isoformat()
928
+ return super().default(obj)
929
+
930
+ class ExcelSecurityConfig:
931
+ """Configuration for Excel file security limits."""
932
+
933
+ def __init__(self):
934
+ self.MAX_FILE_SIZE_MB = int(os.getenv('EXCEL_MAX_FILE_SIZE_MB', '10'))
935
+ self.MAX_ROWS = int(os.getenv('EXCEL_MAX_ROWS', '10000'))
936
+ self.MAX_COLUMNS = int(os.getenv('EXCEL_MAX_COLUMNS', '50'))
937
+ ```
938
+
939
+ ### Migration Guide
940
+
941
+ #### From Monolithic to Modular Structure
942
+
943
+ 1. **Identify Components**: Break down the monolithic action into logical components
944
+ 2. **Extract Models**: Move parameter validation to `models.py`
945
+ 3. **Separate Enums**: Move constants and enums to `enums.py`
946
+ 4. **Create Utilities**: Extract helper functions to `utils.py`
947
+ 5. **Update Imports**: Ensure backward compatibility through `__init__.py`
948
+
949
+ #### Example Migration Steps
950
+
951
+ ```python
952
+ # Before: Single upload.py file (1362 lines)
953
+ class UploadAction(Action):
954
+ # All code in one file...
955
+
956
+ # After: Modular structure
957
+ # action.py - Main logic (546 lines)
958
+ # models.py - Parameter validation (98 lines)
959
+ # enums.py - Constants and logging codes (156 lines)
960
+ # run.py - Execution management (134 lines)
961
+ # utils.py - Helper utilities (89 lines)
962
+ # exceptions.py - Custom exceptions (6 lines)
963
+ # __init__.py - Public API (20 lines)
964
+ ```
965
+
966
+ ### Benefits of Modular Structure
967
+
968
+ - **Maintainability**: Each file has a single responsibility
969
+ - **Testability**: Individual components can be tested in isolation
970
+ - **Reusability**: Utilities and models can be shared across actions
971
+ - **Type Safety**: Enum-based logging and strong parameter validation
972
+ - **Backward Compatibility**: Public API remains unchanged
973
+
974
+ **Logging System with Enums:**
975
+
976
+ ```python
977
+ # upload/enums.py
978
+ class LogCode(str, Enum):
979
+ VALIDATION_FAILED = 'VALIDATION_FAILED'
980
+ NO_FILES_FOUND = 'NO_FILES_FOUND'
981
+ # ... 36 total log codes
982
+
983
+ LOG_MESSAGES = {
984
+ LogCode.VALIDATION_FAILED: {
985
+ 'message': 'Validation failed.',
986
+ 'level': Context.DANGER,
987
+ },
988
+ # ... message configurations
989
+ }
990
+
991
+ # upload/run.py
992
+ class UploadRun(Run):
993
+ def log_message_with_code(self, code: LogCode, *args, level: Optional[Context] = None):
994
+ """Type-safe logging with predefined messages."""
995
+ if code not in LOG_MESSAGES:
996
+ self.log_message(f'Unknown log code: {code}')
997
+ return
998
+
999
+ log_config = LOG_MESSAGES[code]
1000
+ message = log_config['message'].format(*args) if args else log_config['message']
1001
+ log_level = level or log_config['level'] or Context.INFO
1002
+
1003
+ if log_level == Context.INFO.value:
1004
+ self.log_message(message, context=log_level.value)
1005
+ else:
1006
+ self.log_upload_event(code, *args, level)
1007
+ ```
1008
+
1009
+ ## Development Workflow
1010
+
1011
+ ### 1. Local Development Setup
1012
+
1013
+ ```bash
1014
+ # Set up development environment
1015
+ cd synapse_sdk/plugins/categories/my_category
1016
+ python -m pip install -e .
1017
+
1018
+ # Create test plugin
1019
+ synapse plugin create --category my_category --debug
1020
+ ```
1021
+
1022
+ ### 2. Action Testing
1023
+
1024
+ ```python
1025
+ # Test action implementation
1026
+ from synapse_sdk.plugins.utils import get_action_class
1027
+
1028
+ # Get action class
1029
+ ActionClass = get_action_class("my_category", "my_action")
1030
+
1031
+ # Create test instance
1032
+ action = ActionClass(
1033
+ params={"input_path": "/test/data", "output_path": "/test/output"},
1034
+ plugin_config={"debug": True},
1035
+ envs={"TEST_MODE": "true"}
1036
+ )
1037
+
1038
+ # Run action
1039
+ result = action.run_action()
1040
+ assert result["status"] == "success"
1041
+ ```
1042
+
1043
+ ### 3. Integration Testing
1044
+
1045
+ ```python
1046
+ # Test with Ray backend
1047
+ import ray
1048
+ from synapse_sdk.clients.ray import RayClient
1049
+
1050
+ # Initialize Ray
1051
+ ray.init()
1052
+ client = RayClient()
1053
+
1054
+ # Test distributed execution
1055
+ job_result = client.submit_job(
1056
+ entrypoint="python action.py",
1057
+ runtime_env=action.get_runtime_env()
1058
+ )
1059
+ ```
1060
+
1061
+ ## Advanced Features
1062
+
1063
+ ### Custom Progress Categories
1064
+
1065
+ ```python
1066
+ class MyAction(Action):
1067
+ progress_categories = {
1068
+ 'data_loading': {
1069
+ 'proportion': 10,
1070
+ 'description': 'Loading input data'
1071
+ },
1072
+ 'feature_extraction': {
1073
+ 'proportion': 30,
1074
+ 'description': 'Extracting features'
1075
+ },
1076
+ 'model_training': {
1077
+ 'proportion': 50,
1078
+ 'description': 'Training model'
1079
+ },
1080
+ 'evaluation': {
1081
+ 'proportion': 10,
1082
+ 'description': 'Evaluating results'
1083
+ }
1084
+ }
1085
+
1086
+ def start(self):
1087
+ # Update specific progress categories
1088
+ self.run.set_progress(50, 100, 'data_loading')
1089
+ self.run.set_progress(25, 100, 'feature_extraction')
1090
+ ```
1091
+
1092
+ ### Runtime Environment Customization
1093
+
1094
+ ```python
1095
+ def get_runtime_env(self):
1096
+ """Customize execution environment."""
1097
+ env = super().get_runtime_env()
1098
+
1099
+ # Add custom packages
1100
+ env['pip']['packages'].extend([
1101
+ 'custom-ml-library==2.0.0',
1102
+ 'specialized-tool>=1.5.0'
1103
+ ])
1104
+
1105
+ # Set environment variables
1106
+ env['env_vars'].update({
1107
+ 'CUDA_VISIBLE_DEVICES': '0,1',
1108
+ 'OMP_NUM_THREADS': '8',
1109
+ 'CUSTOM_CONFIG_PATH': '/app/config'
1110
+ })
1111
+
1112
+ # Add working directory files
1113
+ env['working_dir_files'] = {
1114
+ 'config.yaml': 'path/to/local/config.yaml',
1115
+ 'model_weights.pth': 'path/to/weights.pth'
1116
+ }
1117
+
1118
+ return env
1119
+ ```
1120
+
1121
+ ### Parameter Validation Patterns
1122
+
1123
+ ```python
1124
+ from pydantic import BaseModel, validator, Field
1125
+ from typing import Literal, Optional, List
1126
+
1127
+ class AdvancedParams(BaseModel):
1128
+ """Advanced parameter validation."""
1129
+
1130
+ # Enum-like validation
1131
+ model_type: Literal["cnn", "transformer", "resnet"]
1132
+
1133
+ # Range validation
1134
+ learning_rate: float = Field(gt=0, le=1, default=0.001)
1135
+ batch_size: int = Field(ge=1, le=1024, default=32)
1136
+
1137
+ # File path validation
1138
+ data_path: str
1139
+ output_path: Optional[str] = None
1140
+
1141
+ # Complex validation
1142
+ layers: List[int] = Field(min_items=1, max_items=10)
1143
+
1144
+ @validator('data_path')
1145
+ def validate_data_path(cls, v):
1146
+ if not os.path.exists(v):
1147
+ raise ValueError(f'Data path does not exist: {v}')
1148
+ return v
1149
+
1150
+ @validator('output_path')
1151
+ def validate_output_path(cls, v, values):
1152
+ if v is None:
1153
+ # Auto-generate from data_path
1154
+ data_path = values.get('data_path', '')
1155
+ return f"{data_path}_output"
1156
+ return v
1157
+
1158
+ @validator('layers')
1159
+ def validate_layers(cls, v):
1160
+ if len(v) < 2:
1161
+ raise ValueError('Must specify at least 2 layers')
1162
+ if v[0] <= 0 or v[-1] <= 0:
1163
+ raise ValueError('Input and output layers must be positive')
1164
+ return v
1165
+ ```
1166
+
1167
+ ## Best Practices
1168
+
1169
+ ### 1. Action Design
1170
+
1171
+ - **Single Responsibility**: Each action should have one clear purpose
1172
+ - **Parameterization**: Make actions configurable through well-defined parameters
1173
+ - **Error Handling**: Implement comprehensive error handling and validation
1174
+ - **Progress Reporting**: Provide meaningful progress updates for long operations
1175
+
1176
+ ### 2. Code Organization
1177
+
1178
+ ```python
1179
+ # Good: Modular structure
1180
+ class UploadAction(Action):
1181
+ def start(self):
1182
+ self._validate_inputs()
1183
+ files = self._discover_files()
1184
+ processed_files = self._process_files(files)
1185
+ return self._generate_output(processed_files)
1186
+
1187
+ def _validate_inputs(self):
1188
+ """Separate validation logic."""
1189
+ pass
1190
+
1191
+ def _discover_files(self):
1192
+ """Separate file discovery logic."""
1193
+ pass
1194
+
1195
+ # Good: Use of enums for constants
1196
+ class LogCode(str, Enum):
1197
+ VALIDATION_FAILED = 'VALIDATION_FAILED'
1198
+ FILE_NOT_FOUND = 'FILE_NOT_FOUND'
1199
+
1200
+ # Good: Type hints and documentation
1201
+ def process_batch(self, items: List[Dict[str, Any]], batch_size: int = 100) -> List[Dict[str, Any]]:
1202
+ """Process items in batches for memory efficiency.
1203
+
1204
+ Args:
1205
+ items: List of items to process
1206
+ batch_size: Number of items per batch
1207
+
1208
+ Returns:
1209
+ List of processed items
1210
+ """
1211
+ ```
1212
+
1213
+ ### 3. Performance Optimization
1214
+
1215
+ ```python
1216
+ # Use simple sequential processing for file uploads
1217
+ def _upload_files(self, files: List[Path]) -> List[UploadResult]:
1218
+ results = []
1219
+ for file_path in files:
1220
+ result = self._upload_file(file_path)
1221
+ results.append(result)
1222
+ return results
1223
+
1224
+ # Use generators for memory efficiency
1225
+ def _process_large_dataset(self, data_source):
1226
+ """Process data in chunks to avoid memory issues."""
1227
+ for chunk in self._chunk_data(data_source, chunk_size=1000):
1228
+ processed_chunk = self._process_chunk(chunk)
1229
+ yield processed_chunk
1230
+
1231
+ # Update progress
1232
+ self.run.set_progress(self.processed_count, self.total_count, 'processing')
1233
+ ```
1234
+
1235
+ ### 4. Error Handling
1236
+
1237
+ ```python
1238
+ from synapse_sdk.plugins.exceptions import ActionError
1239
+
1240
+ class MyAction(Action):
1241
+ def start(self):
1242
+ try:
1243
+ return self._execute_main_logic()
1244
+ except ValidationError as e:
1245
+ self.run.log_message(f"Validation error: {e}", "ERROR")
1246
+ raise ActionError(f"Parameter validation failed: {e}")
1247
+ except FileNotFoundError as e:
1248
+ self.run.log_message(f"File not found: {e}", "ERROR")
1249
+ raise ActionError(f"Required file missing: {e}")
1250
+ except Exception as e:
1251
+ self.run.log_message(f"Unexpected error: {e}", "ERROR")
1252
+ raise ActionError(f"Action execution failed: {e}")
1253
+ ```
1254
+
1255
+ ### 5. Security Considerations
1256
+
1257
+ ```python
1258
+ # Good: Validate file paths
1259
+ def _validate_file_path(self, file_path: str) -> Path:
1260
+ """Validate and sanitize file paths."""
1261
+ path = Path(file_path).resolve()
1262
+
1263
+ # Prevent directory traversal
1264
+ if not str(path).startswith(str(self.workspace_root)):
1265
+ raise ActionError(f"File path outside workspace: {path}")
1266
+
1267
+ return path
1268
+
1269
+ # Good: Sanitize user inputs
1270
+ def _sanitize_filename(self, filename: str) -> str:
1271
+ """Remove unsafe characters from filename."""
1272
+ import re
1273
+ # Remove path separators and control characters
1274
+ safe_name = re.sub(r'[<>:"/\\|?*\x00-\x1f]', '_', filename)
1275
+ return safe_name[:255] # Limit length
1276
+
1277
+ # Good: Validate data sizes
1278
+ def _validate_data_size(self, data: bytes) -> None:
1279
+ """Check data size limits."""
1280
+ max_size = 100 * 1024 * 1024 # 100MB
1281
+ if len(data) > max_size:
1282
+ raise ActionError(f"Data too large: {len(data)} bytes (max: {max_size})")
1283
+ ```
1284
+
1285
+ ## API Reference
1286
+
1287
+ ### Core Classes
1288
+
1289
+ #### Action
1290
+
1291
+ Base class for all plugin actions.
1292
+
1293
+ **Methods:**
1294
+
1295
+ - `start()`: Main execution method (abstract)
1296
+ - `run_action()`: Execute action with error handling
1297
+ - `get_runtime_env()`: Get execution environment configuration
1298
+ - `validate_params()`: Validate action parameters
1299
+
1300
+ #### Run
1301
+
1302
+ Manages action execution lifecycle.
1303
+
1304
+ **Methods:**
1305
+
1306
+ - `log_message(message, context)`: Log execution messages
1307
+ - `set_progress(current, total, category)`: Update progress
1308
+ - `set_metrics(metrics, category)`: Record metrics
1309
+ - `log(log_type, data)`: Structured logging
1310
+
1311
+ #### PluginRelease
1312
+
1313
+ Manages plugin metadata and configuration.
1314
+
1315
+ **Attributes:**
1316
+
1317
+ - `code`: Plugin identifier
1318
+ - `name`: Human-readable name
1319
+ - `version`: Semantic version
1320
+ - `category`: Plugin category
1321
+ - `config`: Plugin configuration
1322
+
1323
+ ### Utility Functions
1324
+
1325
+ ```python
1326
+ # synapse_sdk/plugins/utils/
1327
+ from synapse_sdk.plugins.utils import (
1328
+ get_action_class, # Get action class by category/name
1329
+ load_plugin_config, # Load plugin configuration
1330
+ validate_plugin, # Validate plugin structure
1331
+ register_plugin, # Register plugin in system
1332
+ )
1333
+
1334
+ # Usage examples
1335
+ ActionClass = get_action_class("upload", "upload")
1336
+ config = load_plugin_config("/path/to/plugin")
1337
+ is_valid = validate_plugin("/path/to/plugin")
1338
+ ```
1339
+
1340
+ This README provides the foundation for developing and extending the Synapse SDK plugin system. For specific implementation examples, refer to the existing plugin categories and their respective documentation.