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,307 @@
1
+ import asyncio
2
+ import shutil
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import Callable, Optional
6
+
7
+ import ffmpeg
8
+
9
+
10
+ # Exception classes
11
+ class VideoTranscodeError(Exception):
12
+ """Base exception for video transcoding errors."""
13
+
14
+ pass
15
+
16
+
17
+ class UnsupportedFormatError(VideoTranscodeError):
18
+ """Raised when input format is not supported."""
19
+
20
+ pass
21
+
22
+
23
+ class FFmpegNotFoundError(VideoTranscodeError):
24
+ """Raised when FFmpeg is not installed or not in PATH."""
25
+
26
+ pass
27
+
28
+
29
+ class TranscodingFailedError(VideoTranscodeError):
30
+ """Raised when FFmpeg transcoding process fails."""
31
+
32
+ pass
33
+
34
+
35
+ @dataclass
36
+ class TranscodeConfig:
37
+ """Video transcoding configuration."""
38
+
39
+ vcodec: str = 'libx264' # Video codec
40
+ preset: str = 'medium' # Encoding preset (ultrafast to veryslow)
41
+ crf: int = 28 # Constant Rate Factor (0-51, lower=better quality)
42
+ acodec: str = 'aac' # Audio codec
43
+ audio_bitrate: str = '128k' # Audio bitrate
44
+ movflags: str = '+faststart' # MP4 optimization flags
45
+ resolution: Optional[str] = None # Target resolution (e.g., '1920x1080')
46
+ fps: Optional[int] = None # Target frame rate
47
+ start_time: Optional[float] = None # Trim start time in seconds
48
+ duration: Optional[float] = None # Trim duration in seconds
49
+
50
+
51
+ # Supported input formats
52
+ SUPPORTED_FORMATS = {'.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv', '.mpeg', '.mpg', '.m4v', '.3gp', '.ogv'}
53
+
54
+
55
+ def _check_ffmpeg_available():
56
+ """Check if FFmpeg is available in PATH."""
57
+ if not shutil.which('ffmpeg'):
58
+ raise FFmpegNotFoundError(
59
+ 'FFmpeg is not installed or not found in PATH. Please install FFmpeg to use video transcoding features.'
60
+ )
61
+
62
+
63
+ def validate_video_format(video_path: str | Path) -> bool:
64
+ """
65
+ Check if video format is supported for transcoding.
66
+
67
+ Args:
68
+ video_path (str | Path): Path to the video file
69
+
70
+ Returns:
71
+ bool: True if format is supported, False otherwise
72
+ """
73
+ path = Path(video_path)
74
+ return path.suffix.lower() in SUPPORTED_FORMATS
75
+
76
+
77
+ def get_video_info(video_path: str | Path) -> dict:
78
+ """
79
+ Extract video metadata (resolution, duration, codecs, etc.).
80
+
81
+ Args:
82
+ video_path (str | Path): Path to the video file
83
+
84
+ Returns:
85
+ dict: Video metadata information
86
+
87
+ Raises:
88
+ VideoTranscodeError: If unable to probe video file
89
+ """
90
+ _check_ffmpeg_available()
91
+
92
+ try:
93
+ probe = ffmpeg.probe(str(video_path))
94
+
95
+ video_info = {}
96
+
97
+ # Get format information
98
+ if 'format' in probe:
99
+ format_info = probe['format']
100
+ video_info['duration'] = float(format_info.get('duration', 0))
101
+ video_info['size'] = int(format_info.get('size', 0))
102
+ video_info['bitrate'] = int(format_info.get('bit_rate', 0))
103
+
104
+ # Get stream information
105
+ video_streams = [stream for stream in probe['streams'] if stream['codec_type'] == 'video']
106
+ audio_streams = [stream for stream in probe['streams'] if stream['codec_type'] == 'audio']
107
+
108
+ if video_streams:
109
+ video_stream = video_streams[0]
110
+ video_info['width'] = int(video_stream.get('width', 0))
111
+ video_info['height'] = int(video_stream.get('height', 0))
112
+ video_info['video_codec'] = video_stream.get('codec_name', '')
113
+ video_info['fps'] = eval(video_stream.get('r_frame_rate', '0/1'))
114
+
115
+ if audio_streams:
116
+ audio_stream = audio_streams[0]
117
+ video_info['audio_codec'] = audio_stream.get('codec_name', '')
118
+ video_info['channels'] = int(audio_stream.get('channels', 0))
119
+ video_info['sample_rate'] = int(audio_stream.get('sample_rate', 0))
120
+
121
+ return video_info
122
+
123
+ except Exception as e:
124
+ raise VideoTranscodeError(f'Failed to probe video file: {str(e)}')
125
+
126
+
127
+ def _build_ffmpeg_stream(input_path: str | Path, output_path: str | Path, config: TranscodeConfig):
128
+ """Build FFmpeg stream with configuration."""
129
+ stream = ffmpeg.input(str(input_path))
130
+
131
+ # Apply start time and duration trimming
132
+ if config.start_time is not None or config.duration is not None:
133
+ kwargs = {}
134
+ if config.start_time is not None:
135
+ kwargs['ss'] = config.start_time
136
+ if config.duration is not None:
137
+ kwargs['t'] = config.duration
138
+ stream = ffmpeg.input(str(input_path), **kwargs)
139
+
140
+ # Apply video filters
141
+ if config.resolution or config.fps:
142
+ if config.resolution:
143
+ width, height = config.resolution.split('x')
144
+ stream = ffmpeg.filter(stream, 'scale', width, height)
145
+ if config.fps:
146
+ stream = ffmpeg.filter(stream, 'fps', fps=config.fps)
147
+
148
+ # Build output with encoding parameters
149
+ output_kwargs = {
150
+ 'vcodec': config.vcodec,
151
+ 'preset': config.preset,
152
+ 'crf': config.crf,
153
+ 'acodec': config.acodec,
154
+ 'audio_bitrate': config.audio_bitrate,
155
+ 'movflags': config.movflags,
156
+ }
157
+
158
+ return ffmpeg.output(stream, str(output_path), **output_kwargs)
159
+
160
+
161
+ def transcode_video(
162
+ input_path: str | Path,
163
+ output_path: str | Path,
164
+ config: Optional[TranscodeConfig] = None,
165
+ progress_callback: Optional[Callable[[float], None]] = None,
166
+ ) -> Path:
167
+ """
168
+ Transcode video with specified configuration.
169
+
170
+ Args:
171
+ input_path (str | Path): Path to input video file
172
+ output_path (str | Path): Path to output video file
173
+ config (Optional[TranscodeConfig]): Transcoding configuration
174
+ progress_callback (Optional[Callable[[float], None]]): Progress callback function
175
+
176
+ Returns:
177
+ Path: Path to the transcoded video file
178
+
179
+ Raises:
180
+ UnsupportedFormatError: If input format is not supported
181
+ FFmpegNotFoundError: If FFmpeg is not available
182
+ TranscodingFailedError: If transcoding fails
183
+ """
184
+ _check_ffmpeg_available()
185
+
186
+ input_path = Path(input_path)
187
+ output_path = Path(output_path)
188
+
189
+ if not validate_video_format(input_path):
190
+ raise UnsupportedFormatError(f'Unsupported video format: {input_path.suffix}')
191
+
192
+ if config is None:
193
+ config = TranscodeConfig()
194
+
195
+ # Ensure output directory exists
196
+ output_path.parent.mkdir(parents=True, exist_ok=True)
197
+
198
+ try:
199
+ # Build FFmpeg command
200
+ stream = _build_ffmpeg_stream(input_path, output_path, config)
201
+
202
+ # Run FFmpeg
203
+ if progress_callback:
204
+ # Get video duration for progress calculation
205
+ video_info = get_video_info(input_path)
206
+ total_duration = video_info.get('duration', 0)
207
+
208
+ # Run with progress monitoring
209
+ process = ffmpeg.run_async(stream, pipe_stderr=True, overwrite_output=True)
210
+
211
+ while True:
212
+ output = process.stderr.readline()
213
+ if output == b'' and process.poll() is not None:
214
+ break
215
+ if output:
216
+ line = output.decode('utf-8')
217
+ # Parse progress from FFmpeg output
218
+ if 'time=' in line and total_duration > 0:
219
+ try:
220
+ time_str = line.split('time=')[1].split()[0]
221
+ hours, minutes, seconds = time_str.split(':')
222
+ current_time = int(hours) * 3600 + int(minutes) * 60 + float(seconds)
223
+ progress = min(current_time / total_duration, 1.0)
224
+ progress_callback(progress)
225
+ except (ValueError, IndexError):
226
+ pass
227
+
228
+ if process.returncode != 0:
229
+ raise TranscodingFailedError('FFmpeg process failed')
230
+ else:
231
+ # Run without progress monitoring
232
+ ffmpeg.run(stream, overwrite_output=True, quiet=True)
233
+
234
+ return output_path
235
+
236
+ except ffmpeg.Error as e:
237
+ error_message = e.stderr.decode('utf-8') if e.stderr else str(e)
238
+ raise TranscodingFailedError(f'Transcoding failed: {error_message}')
239
+ except Exception as e:
240
+ raise VideoTranscodeError(f'Unexpected error during transcoding: {str(e)}')
241
+
242
+
243
+ def optimize_for_web(video_path: str | Path, output_path: str | Path) -> Path:
244
+ """
245
+ Quick optimization for web streaming with default settings.
246
+
247
+ Args:
248
+ video_path (str | Path): Path to input video file
249
+ output_path (str | Path): Path to output video file
250
+
251
+ Returns:
252
+ Path: Path to the optimized video file
253
+ """
254
+ config = TranscodeConfig(
255
+ preset='fast', # Faster encoding for web optimization
256
+ crf=23, # Better quality for web
257
+ movflags='+faststart+frag_keyframe+empty_moov', # Advanced web optimization
258
+ )
259
+ return transcode_video(video_path, output_path, config)
260
+
261
+
262
+ async def atranscode_video(
263
+ input_path: str | Path, output_path: str | Path, config: Optional[TranscodeConfig] = None
264
+ ) -> Path:
265
+ """
266
+ Async version of transcode_video.
267
+
268
+ Args:
269
+ input_path (str | Path): Path to input video file
270
+ output_path (str | Path): Path to output video file
271
+ config (Optional[TranscodeConfig]): Transcoding configuration
272
+
273
+ Returns:
274
+ Path: Path to the transcoded video file
275
+ """
276
+ loop = asyncio.get_event_loop()
277
+ return await loop.run_in_executor(None, transcode_video, input_path, output_path, config)
278
+
279
+
280
+ def transcode_batch(
281
+ video_paths: list[Path], output_dir: Path, config: Optional[TranscodeConfig] = None, max_workers: int = 4
282
+ ) -> list[Path]:
283
+ """
284
+ Process multiple videos concurrently.
285
+
286
+ Args:
287
+ video_paths (list[Path]): List of input video file paths
288
+ output_dir (Path): Directory for output files
289
+ config (Optional[TranscodeConfig]): Transcoding configuration
290
+ max_workers (int): Maximum number of concurrent workers
291
+
292
+ Returns:
293
+ list[Path]: List of paths to transcoded video files
294
+ """
295
+ import concurrent.futures
296
+
297
+ output_dir = Path(output_dir)
298
+ output_dir.mkdir(parents=True, exist_ok=True)
299
+
300
+ def process_video(video_path):
301
+ output_path = output_dir / f'{video_path.stem}_transcoded.mp4'
302
+ return transcode_video(video_path, output_path, config)
303
+
304
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
305
+ results = list(executor.map(process_video, video_paths))
306
+
307
+ return results
@@ -7,6 +7,7 @@ import operator
7
7
  import zipfile
8
8
  from functools import reduce
9
9
  from pathlib import Path
10
+ from typing import IO, Any, Callable
10
11
 
11
12
  import aiohttp
12
13
  import requests
@@ -16,6 +17,39 @@ from synapse_sdk.utils.network import clean_url
16
17
  from synapse_sdk.utils.string import hash_text
17
18
 
18
19
 
20
+ def read_file_in_chunks(file_path, chunk_size=1024 * 1024 * 50):
21
+ """
22
+ Read a file in chunks for efficient memory usage during file processing.
23
+
24
+ This function is particularly useful for large files or when you need to process
25
+ files in chunks, such as for uploading or hashing.
26
+
27
+ Args:
28
+ file_path (str | Path): Path to the file to read
29
+ chunk_size (int, optional): Size of each chunk in bytes. Defaults to 50MB (1024 * 1024 * 50)
30
+
31
+ Yields:
32
+ bytes: File content chunks
33
+
34
+ Raises:
35
+ FileNotFoundError: If the file doesn't exist
36
+ PermissionError: If the file can't be read due to permissions
37
+ OSError: If there's an OS-level error reading the file
38
+
39
+ Example:
40
+ ```python
41
+ from synapse_sdk.utils.file import read_file_in_chunks
42
+
43
+ # Read a file in 10MB chunks
44
+ for chunk in read_file_in_chunks('large_file.bin', chunk_size=1024*1024*10):
45
+ process_chunk(chunk)
46
+ ```
47
+ """
48
+ with open(file_path, 'rb') as file:
49
+ while chunk := file.read(chunk_size):
50
+ yield chunk
51
+
52
+
19
53
  def download_file(url, path_download, name=None, coerce=None, use_cached=True):
20
54
  chunk_size = 1024 * 1024 * 50
21
55
  cleaned_url = clean_url(url) # remove query params and fragment
@@ -150,11 +184,55 @@ def calculate_checksum(file_path, prefix=''):
150
184
  return checksum
151
185
 
152
186
 
153
- def archive(input_path, output_path):
187
+ def get_checksum_from_file(file: IO[Any], digest_mod: Callable[[], Any] = hashlib.sha1) -> str:
188
+ """
189
+ Calculate checksum for a file-like object.
190
+
191
+ Args:
192
+ file (IO[Any]): File-like object with read() method that supports reading in chunks
193
+ digest_mod (Callable[[], Any]): Hash algorithm from hashlib (defaults to hashlib.sha1)
194
+
195
+ Returns:
196
+ str: Hexadecimal digest of the file contents
197
+
198
+ Example:
199
+ ```python
200
+ import hashlib
201
+ from io import BytesIO
202
+ from synapse_sdk.utils.file import get_checksum_from_file
203
+
204
+ # With BytesIO
205
+ data = BytesIO(b'Hello, world!')
206
+ checksum = get_checksum_from_file(data)
207
+
208
+ # With different hash algorithm
209
+ checksum = get_checksum_from_file(data, digest_mod=hashlib.sha256)
210
+ ```
211
+ """
212
+ digest = digest_mod()
213
+ chunk_size = 4096
214
+
215
+ # Reset file pointer to beginning if possible
216
+ if hasattr(file, 'seek'):
217
+ file.seek(0)
218
+
219
+ while True:
220
+ chunk = file.read(chunk_size)
221
+ if not chunk:
222
+ break
223
+ if isinstance(chunk, str):
224
+ chunk = chunk.encode('utf-8')
225
+ digest.update(chunk)
226
+
227
+ return digest.hexdigest()
228
+
229
+
230
+ def archive(input_path, output_path, append=False):
154
231
  input_path = Path(input_path)
155
232
  output_path = Path(output_path)
156
233
 
157
- with zipfile.ZipFile(output_path, mode='w', compression=zipfile.ZIP_DEFLATED) as zipf:
234
+ mode = 'a' if append and output_path.exists() else 'w'
235
+ with zipfile.ZipFile(output_path, mode=mode, compression=zipfile.ZIP_DEFLATED) as zipf:
158
236
  if input_path.is_file():
159
237
  zipf.write(input_path, input_path.name)
160
238
  else:
@@ -196,6 +274,10 @@ def convert_file_to_base64(file_path):
196
274
  Returns:
197
275
  str: Base64 encoded string of the file contents
198
276
  """
277
+ # FIXME base64 is sent sometimes.
278
+ if file_path.startswith('data:'):
279
+ return file_path
280
+
199
281
  # Convert string path to Path object
200
282
  path = Path(file_path)
201
283
 
@@ -0,0 +1,138 @@
1
+ import logging
2
+ import os
3
+ import tempfile
4
+ import threading
5
+ import time
6
+ import uuid
7
+ from contextlib import contextmanager
8
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
9
+
10
+ import requests
11
+
12
+ from synapse_sdk.utils.network import get_available_ports_host
13
+
14
+
15
+ class SingleFileHttpServer(SimpleHTTPRequestHandler):
16
+ """
17
+ Custom HTTP request handler that serves a single specified file
18
+ regardless of the request path.
19
+ """
20
+
21
+ def __init__(self, *args, file_path=None, content_type=None, random_path=None, **kwargs):
22
+ self.file_path = file_path
23
+ self.content_type = content_type
24
+ self.random_path = random_path
25
+ super().__init__(*args, **kwargs)
26
+
27
+ def do_GET(self):
28
+ """Handle GET requests by serving the single file."""
29
+ try:
30
+ # Check if the path matches our random path
31
+ if self.random_path and self.path == f'/{self.random_path}':
32
+ self.send_response(200)
33
+ if self.content_type:
34
+ self.send_header('Content-type', self.content_type)
35
+ self.send_header('Content-Length', str(os.path.getsize(self.file_path)))
36
+ self.end_headers()
37
+
38
+ with open(self.file_path, 'rb') as file:
39
+ self.wfile.write(file.read())
40
+ elif self.path == '/':
41
+ # Redirect root to the random path
42
+ self.send_response(302)
43
+ self.send_header('Location', f'/{self.random_path}')
44
+ self.end_headers()
45
+ else:
46
+ self.send_error(404, 'File not found')
47
+
48
+ except Exception as e:
49
+ self.send_error(500, str(e))
50
+
51
+
52
+ @contextmanager
53
+ def temp_file_server(image=None, file_path=None, format='JPEG', host='0.0.0.0', port=None, content_type=None):
54
+ """
55
+ Context manager that serves a file temporarily via HTTP.
56
+
57
+ Args:
58
+ image: A PIL Image object to serve (optional)
59
+ file_path: Path to an existing file to serve (optional - used if image not provided)
60
+ format: Image format when saving a PIL Image (default: "JPEG")
61
+ host: Host to serve on (default: "0.0.0.0")
62
+ port: Port to serve on (default: auto-selected free port)
63
+ content_type: Content type header (default: auto-detected based on format)
64
+
65
+ Returns:
66
+ URL where the file is being served
67
+
68
+ Usage:
69
+ with temp_file_serve(image=my_pillow_img) as url:
70
+ # use url to access the image
71
+ print(f"Image available at: {url}")
72
+ """
73
+ if image is None and file_path is None:
74
+ raise ValueError('Either image or file_path must be provided')
75
+
76
+ # Use a free port if none specified
77
+ if port is None:
78
+ port = get_available_ports_host(start_port=8991, end_port=8999)
79
+
80
+ temp_dir = None
81
+ temp_file_path = None
82
+
83
+ try:
84
+ random_filename = f'{uuid.uuid4().hex}'
85
+
86
+ if image is not None:
87
+ temp_dir = tempfile.mkdtemp()
88
+ ext_map = {'JPEG': '.jpg', 'PNG': '.png', 'GIF': '.gif', 'WEBP': '.webp'}
89
+ content_type_map = {'JPEG': 'image/jpeg', 'PNG': 'image/png', 'GIF': 'image/gif', 'WEBP': 'image/webp'}
90
+
91
+ ext = ext_map.get(format, '.jpg')
92
+ if content_type is None:
93
+ content_type = content_type_map.get(format, 'image/jpeg')
94
+
95
+ temp_file_path = os.path.join(temp_dir, f'temp_image{ext}')
96
+ image.save(temp_file_path, format=format)
97
+ file_path = temp_file_path
98
+ random_filename += ext
99
+ else:
100
+ _, ext = os.path.splitext(file_path)
101
+ random_filename += ext
102
+
103
+ def handler(*args, **kwargs):
104
+ return SingleFileHttpServer(
105
+ *args, file_path=file_path, content_type=content_type, random_path=random_filename, **kwargs
106
+ )
107
+
108
+ server = HTTPServer((host, port), handler)
109
+
110
+ server_thread = threading.Thread(target=server.serve_forever)
111
+ server_thread.daemon = True
112
+ server_thread.start()
113
+
114
+ url = f'http://localhost:{port}/{random_filename}'
115
+
116
+ while True:
117
+ try:
118
+ response = requests.get(url)
119
+ if response.status_code == 200:
120
+ break
121
+ except requests.exceptions.ConnectionError:
122
+ pass
123
+ time.sleep(0.01)
124
+
125
+ try:
126
+ yield url
127
+ finally:
128
+ server.shutdown()
129
+ server.server_close()
130
+
131
+ finally:
132
+ if temp_dir:
133
+ try:
134
+ if temp_file_path and os.path.exists(temp_file_path):
135
+ os.unlink(temp_file_path)
136
+ os.rmdir(temp_dir)
137
+ except Exception as e:
138
+ logging.warning(f'Error cleaning up temporary files: {e}')