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,766 @@
1
+ ---
2
+ id: upload-plugin-template
3
+ title: 업로드 플러그인 템플릿 개발
4
+ sidebar_position: 3
5
+ ---
6
+
7
+ # BaseUploader를 사용한 업로드 플러그인 템플릿 개발
8
+
9
+ 이 가이드는 BaseUploader 템플릿을 사용하여 사용자 정의 업로드 플러그인을 만들려는 플러그인 개발자를 위한 것입니다. BaseUploader는 업로드 플러그인 내에서 파일 처리 및 구성을 위한 워크플로우 기반의 기초를 제공합니다.
10
+
11
+ ## 개요
12
+
13
+ BaseUploader 템플릿 (`synapse_sdk.plugins.categories.upload.templates.plugin`)은 업로드 플러그인 구축을 위한 구조화된 접근 방식을 제공합니다. 메서드 재정의를 통해 사용자 정의를 허용하면서 일반적인 업로드 워크플로우를 처리합니다.
14
+
15
+ ### BaseUploader 워크플로우
16
+
17
+ BaseUploader는 6단계 워크플로우 파이프라인을 구현합니다:
18
+
19
+ ```
20
+ 1. setup_directories() # 사용자 정의 디렉토리 구조 생성
21
+ 2. organize_files() # 파일 구성 및 구조화
22
+ 3. before_process() # 전처리 후크
23
+ 4. process_files() # 주요 처리 로직 (필수)
24
+ 5. after_process() # 후처리 후크
25
+ 6. validate_files() # 최종 검증
26
+ ```
27
+
28
+ ## 시작하기
29
+
30
+ ### 템플릿 구조
31
+
32
+ 업로드 플러그인을 생성하면 다음과 같은 구조를 갖게 됩니다:
33
+
34
+ ```
35
+ synapse-{plugin-code}-plugin/
36
+ ├── config.yaml # 플러그인 메타데이터 및 구성
37
+ ├── plugin/ # 소스 코드 디렉토리
38
+ │ ├── __init__.py
39
+ │ └── upload.py # BaseUploader를 사용한 주요 업로드 구현
40
+ ├── requirements.txt # 파이썬 의존성
41
+ ├── pyproject.toml # 패키지 구성
42
+ └── README.md # 플러그인 문서
43
+ ```
44
+
45
+ ### 기본 플러그인 구현
46
+
47
+ ```python
48
+ # plugin/__init__.py
49
+ from pathlib import Path
50
+ from typing import Any, Dict, List
51
+
52
+ class BaseUploader:
53
+ """일반적인 업로드 기능을 가진 기본 클래스."""
54
+
55
+ def __init__(self, run, path: Path, file_specification: List = None,
56
+ organized_files: List = None, extra_params: Dict = None):
57
+ self.run = run
58
+ self.path = path
59
+ self.file_specification = file_specification or []
60
+ self.organized_files = organized_files or []
61
+ self.extra_params = extra_params or {}
62
+
63
+ # 재정의 가능한 핵심 워크플로우 메서드
64
+ def setup_directories(self) -> None:
65
+ """사용자 정의 디렉토리 설정 - 필요에 따라 재정의."""
66
+ pass
67
+
68
+ def organize_files(self, files: List) -> List:
69
+ """파일 구성 - 사용자 정의 로직을 위해 재정의."""
70
+ return files
71
+
72
+ def before_process(self, organized_files: List) -> List:
73
+ """전처리 후크 - 필요에 따라 재정의."""
74
+ return organized_files
75
+
76
+ def process_files(self, organized_files: List) -> List:
77
+ """주요 처리 - 반드시 재정의해야 함."""
78
+ return organized_files
79
+
80
+ def after_process(self, processed_files: List) -> List:
81
+ """후처리 후크 - 필요에 따라 재정의."""
82
+ return processed_files
83
+
84
+ def validate_files(self, files: List) -> List:
85
+ """검증 - 사용자 정의 검증을 위해 재정의."""
86
+ return self._filter_valid_files(files)
87
+
88
+ def handle_upload_files(self) -> List:
89
+ """주요 진입점 - 워크플로우를 실행합니다."""
90
+ self.setup_directories()
91
+ current_files = self.organized_files
92
+ current_files = self.organize_files(current_files)
93
+ current_files = self.before_process(current_files)
94
+ current_files = self.process_files(current_files)
95
+ current_files = self.after_process(current_files)
96
+ current_files = self.validate_files(current_files)
97
+ return current_files
98
+
99
+ # plugin/upload.py
100
+ from . import BaseUploader
101
+
102
+ class Uploader(BaseUploader):
103
+ """사용자 정의 업로드 플러그인 구현."""
104
+
105
+ def process_files(self, organized_files: List) -> List:
106
+ """필수: 파일 처리 로직을 구현하십시오."""
107
+ # 여기에 사용자 정의 처리 로직
108
+ return organized_files
109
+ ```
110
+
111
+ ## 핵심 메서드 참조
112
+
113
+ ### 필수 메서드
114
+
115
+ #### `process_files(organized_files: List) -> List`
116
+
117
+ **목적**: 모든 플러그인에서 구현해야 하는 주요 처리 메서드.
118
+
119
+ **사용 시기**: 항상 - 플러그인의 핵심 로직이 여기에 들어갑니다.
120
+
121
+ **예시**:
122
+
123
+ ```python
124
+ def process_files(self, organized_files: List) -> List:
125
+ """TIFF 이미지를 JPEG 형식으로 변환합니다."""
126
+ processed_files = []
127
+
128
+ for file_group in organized_files:
129
+ files_dict = file_group.get('files', {})
130
+ converted_files = {}
131
+
132
+ for spec_name, file_path in files_dict.items():
133
+ if file_path.suffix.lower() in ['.tif', '.tiff']:
134
+ # TIFF를 JPEG로 변환
135
+ jpeg_path = self.convert_tiff_to_jpeg(file_path)
136
+ converted_files[spec_name] = jpeg_path
137
+ self.run.log_message(f"{file_path}를 {jpeg_path}로 변환했습니다.")
138
+ else:
139
+ converted_files[spec_name] = file_path
140
+
141
+ file_group['files'] = converted_files
142
+ processed_files.append(file_group)
143
+
144
+ return processed_files
145
+ ```
146
+
147
+ ### 선택적 후크 메서드
148
+
149
+ #### `setup_directories() -> None`
150
+
151
+ **목적**: 처리가 시작되기 전에 사용자 정의 디렉토리 구조를 생성합니다.
152
+
153
+ **사용 시기**: 플러그인이 처리, 임시 파일 또는 출력을 위해 특정 디렉토리가 필요할 때.
154
+
155
+ **예시**:
156
+
157
+ ```python
158
+ def setup_directories(self):
159
+ """처리 디렉토리를 생성합니다."""
160
+ (self.path / 'temp').mkdir(exist_ok=True)
161
+ (self.path / 'processed').mkdir(exist_ok=True)
162
+ (self.path / 'thumbnails').mkdir(exist_ok=True)
163
+ self.run.log_message("처리 디렉토리를 생성했습니다.")
164
+ ```
165
+
166
+ #### `organize_files(files: List) -> List`
167
+
168
+ **목적**: 주요 처리 전에 파일을 재구성하고 구조화합니다.
169
+
170
+ **사용 시기**: 파일을 다르게 그룹화하거나, 기준으로 필터링하거나, 데이터를 재구성해야 할 때.
171
+
172
+ **예시**:
173
+
174
+ ```python
175
+ def organize_files(self, files: List) -> List:
176
+ """최적화된 처리를 위해 크기별로 파일을 그룹화합니다."""
177
+ large_files = []
178
+ small_files = []
179
+
180
+ for file_group in files:
181
+ total_size = sum(f.stat().st_size for f in file_group.get('files', {}).values())
182
+ if total_size > 100 * 1024 * 1024: # 100MB
183
+ large_files.append(file_group)
184
+ else:
185
+ small_files.append(file_group)
186
+
187
+ # 큰 파일 먼저 처리
188
+ return large_files + small_files
189
+ ```
190
+
191
+ #### `before_process(organized_files: List) -> List`
192
+
193
+ **목적**: 주요 처리 전 설정 작업을 위한 전처리 후크.
194
+
195
+ **사용 시기**: 검증, 준비 또는 초기화 작업에 사용합니다.
196
+
197
+ **예시**:
198
+
199
+ ```python
200
+ def before_process(self, organized_files: List) -> List:
201
+ """처리를 위해 파일을 검증하고 준비합니다."""
202
+ self.run.log_message(f"{len(organized_files)}개 파일 그룹의 처리를 시작합니다.")
203
+
204
+ # 사용 가능한 디스크 공간 확인
205
+ if not self.check_disk_space(organized_files):
206
+ raise Exception("처리에 필요한 디스크 공간이 부족합니다.")
207
+
208
+ return organized_files
209
+ ```
210
+
211
+ #### `after_process(processed_files: List) -> List`
212
+
213
+ **목적**: 정리 및 최종화를 위한 후처리 후크.
214
+
215
+ **사용 시기**: 정리, 최종 변환 또는 리소스 할당 해제에 사용합니다.
216
+
217
+ **예시**:
218
+
219
+ ```python
220
+ def after_process(self, processed_files: List) -> List:
221
+ """임시 파일을 정리하고 요약을 생성합니다."""
222
+ # 임시 파일 제거
223
+ temp_dir = self.path / 'temp'
224
+ if temp_dir.exists():
225
+ shutil.rmtree(temp_dir)
226
+
227
+ # 처리 요약 생성
228
+ summary = {
229
+ 'total_processed': len(processed_files),
230
+ 'processing_time': time.time() - self.start_time
231
+ }
232
+
233
+ self.run.log_message(f"처리가 완료되었습니다: {summary}")
234
+ return processed_files
235
+ ```
236
+
237
+ #### `validate_files(files: List) -> List`
238
+
239
+ **목적**: 유형 검사를 넘어서는 사용자 정의 검증 로직.
240
+
241
+ **사용 시기**: 내장 파일 유형 검증 외에 추가적인 검증 규칙이 필요할 때.
242
+
243
+ **예시**:
244
+
245
+ ```python
246
+ def validate_files(self, files: List) -> List:
247
+ """크기 및 형식 검사를 포함한 사용자 정의 검증."""
248
+ # 먼저 내장 검증 적용
249
+ validated_files = super().validate_files(files)
250
+
251
+ # 그런 다음 사용자 정의 검증 적용
252
+ final_files = []
253
+ for file_group in validated_files:
254
+ if self.validate_file_group(file_group):
255
+ final_files.append(file_group)
256
+ else:
257
+ self.run.log_message(f"파일 그룹이 검증에 실패했습니다: {file_group}")
258
+
259
+ return final_files
260
+ ```
261
+
262
+ #### `filter_files(organized_file: Dict[str, Any]) -> bool`
263
+
264
+ **목적**: 사용자 정의 기준에 따라 개별 파일을 필터링합니다.
265
+
266
+ **사용 시기**: 처리에서 특정 파일을 제외해야 할 때.
267
+
268
+ **예시**:
269
+
270
+ ```python
271
+ def filter_files(self, organized_file: Dict[str, Any]) -> bool:
272
+ """작은 파일을 필터링합니다."""
273
+ files_dict = organized_file.get('files', {})
274
+ total_size = sum(f.stat().st_size for f in files_dict.values())
275
+
276
+ if total_size < 1024: # 1KB보다 작은 파일 건너뛰기
277
+ self.run.log_message(f"작은 파일 그룹 건너뛰기: {total_size} 바이트")
278
+ return False
279
+
280
+ return True
281
+ ```
282
+
283
+ ## 파일 확장자 필터링
284
+
285
+ BaseUploader에는 파일 타입에 따라 자동으로 파일을 필터링하는 내장 확장자 필터링 시스템이 포함되어 있습니다. 이 기능은 워크플로우에 통합되어 있으며 검증 단계에서 자동으로 실행됩니다.
286
+
287
+ ### 작동 방식
288
+
289
+ 1. **자동 통합**: 확장자 필터링은 업로드 워크플로우의 `ValidateFilesStep`에서 자동으로 적용됩니다
290
+ 2. **대소문자 무시**: 확장자는 대소문자를 구분하지 않고 매칭됩니다 (`.mp4`는 `.MP4`, `.Mp4` 등과 매칭)
291
+ 3. **타입별 필터링**: 파일 타입(video, image, audio 등)별로 필터링이 수행됩니다
292
+ 4. **자동 로깅**: 필터링된 파일은 WARNING 레벨로 로깅되며 어떤 확장자가 필터링되었는지 표시됩니다
293
+
294
+ ### 기본 백엔드 구성
295
+
296
+ 시스템은 백엔드 파일 타입 제한과 일치하는 합리적인 기본값을 제공합니다:
297
+
298
+ ```python
299
+ def get_file_extensions_config(self) -> Dict[str, List[str]]:
300
+ """허용되는 파일 확장자 구성을 반환합니다.
301
+
302
+ 파일 타입별로 확장자를 제한하려면 이 메서드를 재정의하세요.
303
+ 확장자는 대소문자를 구분하지 않으며 점(.) 접두사를 포함해야 합니다.
304
+ """
305
+ return {
306
+ 'video': ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv'],
307
+ 'image': ['.jpg', '.jpeg', '.png'],
308
+ 'pcd': ['.pcd'],
309
+ 'text': ['.txt', '.html'],
310
+ 'audio': ['.mp3', '.wav'],
311
+ 'data': ['.xml', '.bin', '.json', '.fbx'],
312
+ }
313
+ ```
314
+
315
+ ### 확장자 필터링 커스터마이징
316
+
317
+ 플러그인의 확장자를 제한하려면 플러그인 템플릿 파일(`plugin/__init__.py`)에서 `get_file_extensions_config()` 메서드를 수정하기만 하면 됩니다:
318
+
319
+ #### 예제 1: MP4 비디오만 허용
320
+
321
+ ```python
322
+ def get_file_extensions_config(self) -> Dict[str, List[str]]:
323
+ """MP4 비디오만 허용합니다."""
324
+ return {
325
+ 'video': ['.mp4'], # MP4만 허용
326
+ 'image': ['.jpg', '.jpeg', '.png'],
327
+ 'pcd': ['.pcd'],
328
+ 'text': ['.txt', '.html'],
329
+ 'audio': ['.mp3', '.wav'],
330
+ 'data': ['.xml', '.bin', '.json', '.fbx'],
331
+ }
332
+ ```
333
+
334
+ **결과**: 업로드 시 `.avi`, `.mkv`, `.mov` 등의 확장자를 가진 파일은 자동으로 필터링되고 로그에 기록됩니다:
335
+
336
+ ```
337
+ WARNING: Filtered 3 video files with unavailable extensions: .avi, .mkv, .mov (allowed: .mp4)
338
+ ```
339
+
340
+ #### 예제 2: 추가 형식 지원
341
+
342
+ ```python
343
+ def get_file_extensions_config(self) -> Dict[str, List[str]]:
344
+ """추가 비디오 및 이미지 형식을 지원합니다."""
345
+ return {
346
+ 'video': ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv'],
347
+ 'image': ['.jpg', '.jpeg', '.png', '.tiff', '.bmp', '.gif'], # 형식 추가
348
+ 'pcd': ['.pcd'],
349
+ 'text': ['.txt', '.html', '.md', '.csv'], # .md 및 .csv 추가
350
+ 'audio': ['.mp3', '.wav', '.flac', '.aac'], # .flac 및 .aac 추가
351
+ 'data': ['.xml', '.bin', '.json', '.fbx', '.yaml'], # .yaml 추가
352
+ }
353
+ ```
354
+
355
+ #### 예제 3: 완전 커스텀 구성
356
+
357
+ ```python
358
+ def get_file_extensions_config(self) -> Dict[str, List[str]]:
359
+ """특정 프로젝트 요구사항에 맞는 커스텀 구성."""
360
+ return {
361
+ 'video': ['.mp4'], # 엄격한 비디오 형식
362
+ 'image': ['.jpg'], # 엄격한 이미지 형식
363
+ 'cad': ['.dwg', '.dxf', '.step'], # 커스텀 CAD 타입
364
+ 'document': ['.pdf', '.docx'], # 커스텀 문서 타입
365
+ }
366
+ ```
367
+
368
+ ### 확장자 필터링 워크플로우
369
+
370
+ ```
371
+ OrganizeFilesStep
372
+
373
+ ValidateFilesStep
374
+ ├─ Uploader.handle_upload_files()
375
+ │ └─ validate_files()
376
+ │ └─ validate_file_types() ← 확장자 필터링이 여기서 발생
377
+ │ ├─ get_file_extensions_config() 읽기
378
+ │ ├─ 타입별로 파일 필터링
379
+ │ └─ 필터링된 확장자 로깅
380
+ └─ Strategy 검증
381
+ ```
382
+
383
+ ## 실제 예제
384
+
385
+ ### 예제 1: 이미지 처리 플러그인
386
+
387
+ ```python
388
+ from pathlib import Path
389
+ from typing import List
390
+ from plugin import BaseUploader
391
+
392
+ class ImageProcessingUploader(BaseUploader):
393
+ """TIFF 이미지를 JPEG로 변환하고 썸네일을 생성합니다."""
394
+
395
+ def setup_directories(self):
396
+ """처리된 이미지를 위한 디렉토리를 생성합니다."""
397
+ (self.path / 'processed').mkdir(exist_ok=True)
398
+ (self.path / 'thumbnails').mkdir(exist_ok=True)
399
+
400
+ def process_files(self, organized_files: List) -> List:
401
+ """이미지를 변환하고 썸네일을 생성합니다."""
402
+ processed_files = []
403
+
404
+ for file_group in organized_files:
405
+ files_dict = file_group.get('files', {})
406
+ converted_files = {}
407
+
408
+ for spec_name, file_path in files_dict.items():
409
+ if file_path.suffix.lower() in ['.tif', '.tiff']:
410
+ # JPEG로 변환
411
+ jpeg_path = self.convert_to_jpeg(file_path)
412
+ converted_files[spec_name] = jpeg_path
413
+
414
+ # 썸네일 생성
415
+ thumbnail_path = self.generate_thumbnail(jpeg_path)
416
+ converted_files[f"{spec_name}_thumbnail"] = thumbnail_path
417
+
418
+ self.run.log_message(f"{file_path.name}을(를) 처리했습니다.")
419
+ else:
420
+ converted_files[spec_name] = file_path
421
+
422
+ file_group['files'] = converted_files
423
+ processed_files.append(file_group)
424
+
425
+ return processed_files
426
+
427
+ def convert_to_jpeg(self, tiff_path: Path) -> Path:
428
+ """PIL을 사용하여 TIFF를 JPEG로 변환합니다."""
429
+ from PIL import Image
430
+
431
+ output_path = self.path / 'processed' / f"{tiff_path.stem}.jpg"
432
+
433
+ with Image.open(tiff_path) as img:
434
+ if img.mode in ('RGBA', 'LA', 'P'):
435
+ img = img.convert('RGB')
436
+ img.save(output_path, 'JPEG', quality=95)
437
+
438
+ return output_path
439
+
440
+ def generate_thumbnail(self, image_path: Path) -> Path:
441
+ """썸네일을 생성합니다."""
442
+ from PIL import Image
443
+
444
+ thumbnail_path = self.path / 'thumbnails' / f"{image_path.stem}_thumb.jpg"
445
+
446
+ with Image.open(image_path) as img:
447
+ img.thumbnail((200, 200), Image.Resampling.LANCZOS)
448
+ img.save(thumbnail_path, 'JPEG', quality=85)
449
+
450
+ return thumbnail_path
451
+ ```
452
+
453
+ ### 예제 2: 데이터 검증 플러그인
454
+
455
+ ```python
456
+ class DataValidationUploader(BaseUploader):
457
+ """데이터 파일을 검증하고 품질 보고서를 생성합니다."""
458
+
459
+ def __init__(self, run, path, file_specification=None,
460
+ organized_files=None, extra_params=None):
461
+ super().__init__(run, path, file_specification, organized_files, extra_params)
462
+
463
+ # extra_params에서 초기화
464
+ self.validation_config = extra_params.get('validation_config', {})
465
+ self.strict_mode = extra_params.get('strict_validation', False)
466
+
467
+ def before_process(self, organized_files: List) -> List:
468
+ """검증 엔진을 초기화합니다."""
469
+ self.validation_results = []
470
+ self.run.log_message(f"{len(organized_files)}개 파일 그룹의 검증을 시작합니다.")
471
+ return organized_files
472
+
473
+ def process_files(self, organized_files: List) -> List:
474
+ """파일을 검증하고 품질 보고서를 생성합니다."""
475
+ processed_files = []
476
+
477
+ for file_group in organized_files:
478
+ validation_result = self.validate_file_group(file_group)
479
+
480
+ # 검증 메타데이터 추가
481
+ file_group['validation'] = validation_result
482
+ file_group['quality_score'] = validation_result['score']
483
+
484
+ # 검증 결과에 따라 파일 그룹 포함
485
+ if self.should_include_file_group(validation_result):
486
+ processed_files.append(file_group)
487
+ self.run.log_message(f"파일 그룹 통과: 점수 {validation_result['score']}")
488
+ else:
489
+ self.run.log_message(f"파일 그룹 실패: {validation_result['errors']}")
490
+
491
+ return processed_files
492
+
493
+ def validate_file_group(self, file_group: Dict) -> Dict:
494
+ """파일 그룹의 포괄적인 검증."""
495
+ files_dict = file_group.get('files', {})
496
+ errors = []
497
+ score = 100
498
+
499
+ for spec_name, file_path in files_dict.items():
500
+ # 파일 존재 여부
501
+ if not file_path.exists():
502
+ errors.append(f"파일을 찾을 수 없음: {file_path}")
503
+ score -= 50
504
+ continue
505
+
506
+ # 파일 크기 검증
507
+ file_size = file_path.stat().st_size
508
+ if file_size == 0:
509
+ errors.append(f"빈 파일: {file_path}")
510
+ score -= 40
511
+ elif file_size > 1024 * 1024 * 1024: # 1GB
512
+ score -= 10
513
+
514
+ return {
515
+ 'score': max(0, score),
516
+ 'errors': errors,
517
+ 'validated_at': datetime.now().isoformat()
518
+ }
519
+
520
+ def should_include_file_group(self, validation_result: Dict) -> bool:
521
+ """파일 그룹을 포함해야 하는지 결정합니다."""
522
+ if validation_result['errors'] and self.strict_mode:
523
+ return False
524
+
525
+ min_score = self.validation_config.get('min_score', 50)
526
+ return validation_result['score'] >= min_score
527
+ ```
528
+
529
+ ### 예제 3: 배치 처리 플러그인
530
+
531
+ ```python
532
+ class BatchProcessingUploader(BaseUploader):
533
+ """구성 가능한 배치로 파일을 처리합니다."""
534
+
535
+ def __init__(self, run, path, file_specification=None,
536
+ organized_files=None, extra_params=None):
537
+ super().__init__(run, path, file_specification, organized_files, extra_params)
538
+
539
+ self.batch_size = extra_params.get('batch_size', 10)
540
+ self.parallel_processing = extra_params.get('use_parallel', True)
541
+ self.max_workers = extra_params.get('max_workers', 4)
542
+
543
+ def organize_files(self, files: List) -> List:
544
+ """파일을 처리 배치로 구성합니다."""
545
+ batches = []
546
+ current_batch = []
547
+
548
+ for file_group in files:
549
+ current_batch.append(file_group)
550
+
551
+ if len(current_batch) >= self.batch_size:
552
+ batches.append({
553
+ 'batch_id': len(batches) + 1,
554
+ 'files': current_batch,
555
+ 'batch_size': len(current_batch)
556
+ })
557
+ current_batch = []
558
+
559
+ # 남은 파일 추가
560
+ if current_batch:
561
+ batches.append({
562
+ 'batch_id': len(batches) + 1,
563
+ 'files': current_batch,
564
+ 'batch_size': len(current_batch)
565
+ })
566
+
567
+ self.run.log_message(f"{len(batches)}개의 배치로 구성되었습니다.")
568
+ return batches
569
+
570
+ def process_files(self, organized_files: List) -> List:
571
+ """배치로 파일을 처리합니다."""
572
+ all_processed_files = []
573
+
574
+ if self.parallel_processing:
575
+ all_processed_files = self.process_batches_parallel(organized_files)
576
+ else:
577
+ all_processed_files = self.process_batches_sequential(organized_files)
578
+
579
+ return all_processed_files
580
+
581
+ def process_batches_sequential(self, batches: List) -> List:
582
+ """배치를 순차적으로 처리합니다."""
583
+ all_files = []
584
+
585
+ for i, batch in enumerate(batches, 1):
586
+ self.run.log_message(f"{i}/{len(batches)} 배치 처리 중")
587
+ processed_batch = self.process_single_batch(batch)
588
+ all_files.extend(processed_batch)
589
+
590
+ return all_files
591
+
592
+ def process_batches_parallel(self, batches: List) -> List:
593
+ """ThreadPoolExecutor를 사용하여 배치를 병렬로 처리합니다."""
594
+ from concurrent.futures import ThreadPoolExecutor, as_completed
595
+
596
+ all_files = []
597
+
598
+ with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
599
+ future_to_batch = {
600
+ executor.submit(self.process_single_batch, batch): batch
601
+ for batch in batches
602
+ }
603
+
604
+ for future in as_completed(future_to_batch):
605
+ batch = future_to_batch[future]
606
+ try:
607
+ processed_files = future.result()
608
+ all_files.extend(processed_files)
609
+ self.run.log_message(f"배치 {batch['batch_id']} 완료")
610
+ except Exception as e:
611
+ self.run.log_message(f"배치 {batch['batch_id']} 실패: {e}")
612
+
613
+ return all_files
614
+
615
+ def process_single_batch(self, batch: Dict) -> List:
616
+ """단일 파일 배치를 처리합니다."""
617
+ batch_files = batch['files']
618
+ processed_files = []
619
+
620
+ for file_group in batch_files:
621
+ # 배치 메타데이터 추가
622
+ file_group['batch_processed'] = True
623
+ file_group['batch_id'] = batch['batch_id']
624
+ processed_files.append(file_group)
625
+
626
+ return processed_files
627
+ ```
628
+
629
+ ## 모범 사례
630
+
631
+ ### 1. 코드 구성
632
+
633
+ - `process_files()`를 핵심 로직에 집중
634
+ - 설정, 정리 및 검증을 위해 후크 메서드 사용
635
+ - 헬퍼 메서드를 사용하여 관심사 분리
636
+
637
+ ### 2. 오류 처리
638
+
639
+ - 포괄적인 오류 처리 구현
640
+ - 컨텍스트 정보와 함께 오류 기록
641
+ - 가능하면 정상적으로 실패
642
+
643
+ ### 3. 성능
644
+
645
+ - 처리 로직 프로파일링
646
+ - 적절한 데이터 구조 사용
647
+ - 큰 파일에 대한 메모리 사용량 고려
648
+ - I/O 집약적인 작업에 대한 비동기 처리 구현
649
+
650
+ ### 4. 테스트
651
+
652
+ - 모든 메서드에 대한 단위 테스트 작성
653
+ - 실제 파일을 사용한 통합 테스트 포함
654
+ - 오류 조건 및 엣지 케이스 테스트
655
+
656
+ ### 5. 로깅
657
+
658
+ - 중요한 작업 및 마일스톤 기록
659
+ - 타이밍 정보 포함
660
+ - 분석을 위한 구조화된 로깅 사용
661
+
662
+ ### 6. 구성
663
+
664
+ - 플러그인 구성에 `extra_params` 사용
665
+ - 합리적인 기본값 제공
666
+ - 구성 매개변수 검증
667
+
668
+ ## 업로드 액션과의 통합
669
+
670
+ BaseUploader 플러그인은 업로드 액션 워크플로우와 통합됩니다:
671
+
672
+ 1. **파일 검색**: 업로드 액션이 파일을 검색하고 구성합니다.
673
+ 2. **플러그인 호출**: 구성된 파일과 함께 `handle_upload_files()`가 호출됩니다.
674
+ 3. **워크플로우 실행**: BaseUploader가 6단계 워크플로우를 실행합니다.
675
+ 4. **결과 반환**: 처리된 파일이 업로드 액션으로 반환됩니다.
676
+ 5. **업로드 및 데이터 단위 생성**: 업로드 액션이 업로드를 완료합니다.
677
+
678
+ ### 데이터 흐름
679
+
680
+ ```
681
+ 업로드 액션 (OrganizeFilesStep)
682
+ ↓ organized_files
683
+ BaseUploader.handle_upload_files()
684
+ ↓ setup_directories()
685
+ ↓ organize_files()
686
+ ↓ before_process()
687
+ ↓ process_files() ← 사용자 정의 로직
688
+ ↓ after_process()
689
+ ↓ validate_files()
690
+ ↓ processed_files
691
+ 업로드 액션 (UploadFilesStep, GenerateDataUnitsStep)
692
+ ```
693
+
694
+ ## 구성
695
+
696
+ ### 플러그인 구성 (config.yaml)
697
+
698
+ ```yaml
699
+ code: "my-upload-plugin"
700
+ name: "내 업로드 플러그인"
701
+ version: "1.0.0"
702
+ category: "upload"
703
+
704
+ package_manager: "pip"
705
+
706
+ actions:
707
+ upload:
708
+ entrypoint: "plugin.upload.Uploader"
709
+ method: "job"
710
+ ```
711
+
712
+ ### 의존성 (requirements.txt)
713
+
714
+ ```txt
715
+ synapse-sdk>=1.0.0
716
+ pillow>=10.0.0 # 이미지 처리용
717
+ pandas>=2.0.0 # 데이터 처리용
718
+ ```
719
+
720
+ ## 플러그인 테스트
721
+
722
+ ### 단위 테스트
723
+
724
+ ```python
725
+ import pytest
726
+ from unittest.mock import Mock
727
+ from pathlib import Path
728
+ from plugin.upload import Uploader
729
+
730
+ class TestUploader:
731
+
732
+ def setup_method(self):
733
+ self.mock_run = Mock()
734
+ self.test_path = Path('/tmp/test')
735
+ self.file_spec = [{'name': 'image_1', 'file_type': 'image'}]
736
+
737
+ def test_process_files(self):
738
+ """파일 처리 테스트."""
739
+ uploader = Uploader(
740
+ run=self.mock_run,
741
+ path=self.test_path,
742
+ file_specification=self.file_spec,
743
+ organized_files=[{'files': {}}]
744
+ )
745
+
746
+ result = uploader.process_files([{'files': {}}])
747
+ assert isinstance(result, list)
748
+ ```
749
+
750
+ ### 통합 테스트
751
+
752
+ ```bash
753
+ # 샘플 데이터로 테스트
754
+ synapse plugin run upload '{
755
+ "name": "테스트 업로드",
756
+ "use_single_path": true,
757
+ "path": "/test/data",
758
+ "storage": 1,
759
+ "data_collection": 5
760
+ }' --plugin my-upload-plugin --debug
761
+ ```
762
+
763
+ ## 참조
764
+
765
+ - [업로드 플러그인 개요](./upload-plugin-overview.md) - 사용자 가이드 및 구성 참조
766
+ - [업로드 액션 개발](./upload-plugin-action.md) - 액션 아키텍처 및 내부에 대한 SDK 개발자 가이드