synapse-sdk 1.0.0a13__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.
- synapse_sdk/__init__.py +24 -0
- synapse_sdk/cli/__init__.py +310 -5
- synapse_sdk/cli/alias/__init__.py +22 -0
- synapse_sdk/cli/alias/create.py +36 -0
- synapse_sdk/cli/alias/dataclass.py +31 -0
- synapse_sdk/cli/alias/default.py +16 -0
- synapse_sdk/cli/alias/delete.py +15 -0
- synapse_sdk/cli/alias/list.py +19 -0
- synapse_sdk/cli/alias/read.py +15 -0
- synapse_sdk/cli/alias/update.py +17 -0
- synapse_sdk/cli/alias/utils.py +61 -0
- synapse_sdk/cli/code_server.py +687 -0
- synapse_sdk/cli/config.py +440 -0
- synapse_sdk/cli/devtools.py +90 -0
- synapse_sdk/cli/plugin/__init__.py +33 -0
- synapse_sdk/cli/{create_plugin.py → plugin/create.py} +2 -2
- synapse_sdk/cli/plugin/publish.py +45 -0
- synapse_sdk/{plugins/cli → cli/plugin}/run.py +12 -5
- synapse_sdk/clients/agent/__init__.py +9 -3
- synapse_sdk/clients/agent/container.py +133 -0
- synapse_sdk/clients/agent/core.py +19 -0
- synapse_sdk/clients/agent/ray.py +298 -9
- synapse_sdk/clients/backend/__init__.py +41 -12
- synapse_sdk/clients/backend/annotation.py +13 -5
- synapse_sdk/clients/backend/core.py +59 -0
- synapse_sdk/clients/backend/data_collection.py +186 -0
- synapse_sdk/clients/backend/hitl.py +17 -0
- synapse_sdk/clients/backend/integration.py +19 -4
- synapse_sdk/clients/backend/ml.py +10 -7
- synapse_sdk/clients/backend/models.py +78 -0
- synapse_sdk/clients/base.py +381 -34
- synapse_sdk/clients/ray/serve.py +2 -0
- synapse_sdk/clients/validators/collections.py +31 -0
- synapse_sdk/devtools/config.py +94 -0
- synapse_sdk/devtools/docs/.gitignore +20 -0
- synapse_sdk/devtools/docs/README.md +41 -0
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +29 -0
- synapse_sdk/devtools/docs/blog/authors.yml +25 -0
- synapse_sdk/devtools/docs/blog/tags.yml +19 -0
- synapse_sdk/devtools/docs/docs/api/clients/agent.md +43 -0
- synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
- synapse_sdk/devtools/docs/docs/api/clients/backend.md +420 -0
- synapse_sdk/devtools/docs/docs/api/clients/base.md +257 -0
- synapse_sdk/devtools/docs/docs/api/clients/core-mixin.md +477 -0
- synapse_sdk/devtools/docs/docs/api/clients/data-collection-mixin.md +422 -0
- synapse_sdk/devtools/docs/docs/api/clients/hitl-mixin.md +554 -0
- synapse_sdk/devtools/docs/docs/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/docs/api/clients/integration-mixin.md +571 -0
- synapse_sdk/devtools/docs/docs/api/clients/ml-mixin.md +578 -0
- synapse_sdk/devtools/docs/docs/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/docs/api/index.md +52 -0
- synapse_sdk/devtools/docs/docs/api/plugins/categories.md +43 -0
- synapse_sdk/devtools/docs/docs/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/docs/api/plugins/utils.md +328 -0
- synapse_sdk/devtools/docs/docs/categories.md +0 -0
- synapse_sdk/devtools/docs/docs/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/docs/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/docs/configuration.md +83 -0
- synapse_sdk/devtools/docs/docs/contributing.md +306 -0
- synapse_sdk/devtools/docs/docs/examples/index.md +29 -0
- synapse_sdk/devtools/docs/docs/faq.md +179 -0
- synapse_sdk/devtools/docs/docs/features/converters/index.md +455 -0
- synapse_sdk/devtools/docs/docs/features/index.md +24 -0
- synapse_sdk/devtools/docs/docs/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/docs/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/docs/features/utils/storage.md +57 -0
- synapse_sdk/devtools/docs/docs/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/docs/installation.md +94 -0
- synapse_sdk/devtools/docs/docs/introduction.md +47 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/neural-net-plugins/train-action-overview.md +814 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/docs/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/docs/plugins/plugins.md +852 -0
- synapse_sdk/devtools/docs/docs/quickstart.md +78 -0
- synapse_sdk/devtools/docs/docs/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/_category_.json +8 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/congratulations.md +23 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-document.md +57 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/create-a-page.md +43 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
- synapse_sdk/devtools/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/_category_.json +7 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
- synapse_sdk/devtools/docs/docs/tutorial-extras/translate-your-site.md +88 -0
- synapse_sdk/devtools/docs/docusaurus.config.ts +148 -0
- synapse_sdk/devtools/docs/i18n/ko/code.json +325 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/agent.md +43 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/annotation-mixin.md +289 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/backend.md +420 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/base.md +257 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/core-mixin.md +417 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/data-collection-mixin.md +356 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/hitl-mixin.md +192 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/index.md +391 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/integration-mixin.md +479 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ml-mixin.md +284 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ray.md +342 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/index.md +52 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/plugins/models.md +114 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/categories.md +0 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/cli-usage.md +280 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/concepts/index.md +38 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/configuration.md +83 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/contributing.md +306 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/examples/index.md +29 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/faq.md +179 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/converters/index.md +30 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/index.md +24 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/file.md +415 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/network.md +378 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/storage.md +60 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/features/utils/types.md +51 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/installation.md +94 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/introduction.md +47 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/neural-net-plugins/train-action-overview.md +815 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/pre-annotation-plugin-overview.md +198 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-action-development.md +1645 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-overview.md +717 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/pre-annotation-plugins/to-task-template-development.md +1380 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-action.md +948 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-overview.md +544 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/categories/upload-plugins/upload-plugin-template.md +766 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +1092 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/plugins.md +117 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/quickstart.md +78 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/troubleshooting.md +519 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current.json +34 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/footer.json +42 -0
- synapse_sdk/devtools/docs/i18n/ko/docusaurus-theme-classic/navbar.json +18 -0
- synapse_sdk/devtools/docs/package-lock.json +18784 -0
- synapse_sdk/devtools/docs/package.json +48 -0
- synapse_sdk/devtools/docs/sidebars.ts +122 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- synapse_sdk/devtools/docs/src/css/custom.css +30 -0
- synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
- synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
- synapse_sdk/devtools/docs/static/.nojekyll +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
- synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
- synapse_sdk/devtools/docs/static/img/logo.png +0 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- synapse_sdk/devtools/docs/tsconfig.json +8 -0
- synapse_sdk/devtools/server.py +41 -0
- synapse_sdk/devtools/streamlit_app/__init__.py +5 -0
- synapse_sdk/devtools/streamlit_app/app.py +128 -0
- synapse_sdk/devtools/streamlit_app/services/__init__.py +11 -0
- synapse_sdk/devtools/streamlit_app/services/job_service.py +233 -0
- synapse_sdk/devtools/streamlit_app/services/plugin_service.py +236 -0
- synapse_sdk/devtools/streamlit_app/services/serve_service.py +95 -0
- synapse_sdk/devtools/streamlit_app/ui/__init__.py +15 -0
- synapse_sdk/devtools/streamlit_app/ui/config_tab.py +76 -0
- synapse_sdk/devtools/streamlit_app/ui/deployment_tab.py +66 -0
- synapse_sdk/devtools/streamlit_app/ui/http_tab.py +125 -0
- synapse_sdk/devtools/streamlit_app/ui/jobs_tab.py +573 -0
- synapse_sdk/devtools/streamlit_app/ui/serve_tab.py +346 -0
- synapse_sdk/devtools/streamlit_app/ui/status_bar.py +118 -0
- synapse_sdk/devtools/streamlit_app/utils/__init__.py +40 -0
- synapse_sdk/devtools/streamlit_app/utils/json_viewer.py +197 -0
- synapse_sdk/devtools/streamlit_app/utils/log_formatter.py +38 -0
- synapse_sdk/devtools/streamlit_app/utils/styles.py +241 -0
- synapse_sdk/devtools/streamlit_app/utils/ui_components.py +289 -0
- synapse_sdk/devtools/streamlit_app.py +10 -0
- synapse_sdk/loggers.py +74 -9
- synapse_sdk/plugins/README.md +1340 -0
- synapse_sdk/plugins/__init__.py +0 -13
- synapse_sdk/plugins/categories/base.py +145 -30
- synapse_sdk/plugins/categories/data_validation/actions/validation.py +72 -0
- synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +33 -5
- synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
- synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
- synapse_sdk/plugins/categories/export/actions/export/action.py +165 -0
- synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
- synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
- synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
- synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
- synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
- synapse_sdk/plugins/categories/export/templates/config.yaml +21 -0
- synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +390 -0
- synapse_sdk/plugins/categories/export/templates/plugin/export.py +160 -0
- synapse_sdk/plugins/categories/neural_net/actions/deployment.py +29 -14
- synapse_sdk/plugins/categories/neural_net/actions/inference.py +13 -1
- synapse_sdk/plugins/categories/neural_net/actions/train.py +1084 -38
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +534 -0
- synapse_sdk/plugins/categories/neural_net/base/__init__.py +0 -0
- synapse_sdk/plugins/categories/neural_net/base/inference.py +37 -0
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +30 -5
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
- synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +4 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation/__init__.py +3 -0
- synapse_sdk/plugins/categories/{export/actions/export.py → pre_annotation/actions/pre_annotation/action.py} +4 -4
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/__init__.py +28 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py +145 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py +269 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/exceptions.py +14 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py +76 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py +97 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py +250 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/run.py +64 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/__init__.py +17 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py +287 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py +170 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/extraction.py +83 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/metrics.py +87 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/preprocessor.py +127 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py +143 -0
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +966 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +19 -0
- synapse_sdk/plugins/categories/pre_annotation/templates/plugin/to_task.py +40 -0
- synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +5 -2
- synapse_sdk/plugins/categories/upload/__init__.py +0 -0
- synapse_sdk/plugins/categories/upload/actions/__init__.py +0 -0
- synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +19 -0
- synapse_sdk/plugins/categories/upload/actions/upload/action.py +232 -0
- synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
- synapse_sdk/plugins/categories/upload/actions/upload/enums.py +471 -0
- synapse_sdk/plugins/categories/upload/actions/upload/exceptions.py +36 -0
- synapse_sdk/plugins/categories/upload/actions/upload/factory.py +138 -0
- synapse_sdk/plugins/categories/upload/actions/upload/models.py +203 -0
- synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +183 -0
- synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
- synapse_sdk/plugins/categories/upload/actions/upload/run.py +179 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +107 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +63 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +235 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +203 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +97 -0
- synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +71 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +82 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +29 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +258 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +281 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +84 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
- synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +60 -0
- synapse_sdk/plugins/categories/upload/actions/upload/utils.py +250 -0
- synapse_sdk/plugins/categories/upload/templates/README.md +470 -0
- synapse_sdk/plugins/categories/upload/templates/config.yaml +33 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +294 -0
- synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +102 -0
- synapse_sdk/plugins/enums.py +3 -1
- synapse_sdk/plugins/models.py +140 -16
- synapse_sdk/plugins/templates/plugin-config-schema.json +406 -0
- synapse_sdk/plugins/templates/schema.json +491 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +1 -0
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +1 -1
- synapse_sdk/plugins/utils/__init__.py +46 -0
- synapse_sdk/plugins/utils/actions.py +119 -0
- synapse_sdk/plugins/utils/config.py +203 -0
- synapse_sdk/plugins/utils/legacy.py +95 -0
- synapse_sdk/plugins/utils/ray_gcs.py +66 -0
- synapse_sdk/plugins/utils/registry.py +58 -0
- synapse_sdk/shared/__init__.py +25 -0
- synapse_sdk/shared/enums.py +93 -0
- synapse_sdk/types.py +19 -0
- synapse_sdk/utils/converters/__init__.py +240 -0
- synapse_sdk/utils/converters/coco/__init__.py +0 -0
- synapse_sdk/utils/converters/coco/from_dm.py +322 -0
- synapse_sdk/utils/converters/coco/to_dm.py +215 -0
- synapse_sdk/utils/converters/dm/__init__.py +56 -0
- synapse_sdk/utils/converters/dm/from_v1.py +627 -0
- synapse_sdk/utils/converters/dm/to_v1.py +367 -0
- synapse_sdk/utils/converters/pascal/__init__.py +0 -0
- synapse_sdk/utils/converters/pascal/from_dm.py +244 -0
- synapse_sdk/utils/converters/pascal/to_dm.py +214 -0
- synapse_sdk/utils/converters/yolo/__init__.py +0 -0
- synapse_sdk/utils/converters/yolo/from_dm.py +384 -0
- synapse_sdk/utils/converters/yolo/to_dm.py +267 -0
- synapse_sdk/utils/dataset.py +46 -0
- synapse_sdk/utils/encryption.py +158 -0
- synapse_sdk/utils/file/__init__.py +39 -0
- synapse_sdk/utils/file/archive.py +32 -0
- synapse_sdk/utils/file/checksum.py +56 -0
- synapse_sdk/utils/file/chunking.py +31 -0
- synapse_sdk/utils/file/download.py +385 -0
- synapse_sdk/utils/file/encoding.py +40 -0
- synapse_sdk/utils/file/io.py +22 -0
- synapse_sdk/utils/file/video/__init__.py +29 -0
- synapse_sdk/utils/file/video/transcode.py +307 -0
- synapse_sdk/utils/file.py.backup +301 -0
- synapse_sdk/utils/http.py +138 -0
- synapse_sdk/utils/network.py +309 -0
- synapse_sdk/utils/storage/__init__.py +72 -0
- synapse_sdk/utils/storage/providers/__init__.py +183 -0
- synapse_sdk/utils/storage/providers/file_system.py +134 -0
- synapse_sdk/utils/storage/providers/gcp.py +13 -0
- synapse_sdk/utils/storage/providers/http.py +190 -0
- synapse_sdk/utils/storage/providers/s3.py +91 -0
- synapse_sdk/utils/storage/providers/sftp.py +47 -0
- synapse_sdk/utils/storage/registry.py +17 -0
- synapse_sdk-2025.11.7.dist-info/METADATA +122 -0
- synapse_sdk-2025.11.7.dist-info/RECORD +386 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info}/WHEEL +1 -1
- synapse_sdk/clients/backend/dataset.py +0 -51
- synapse_sdk/plugins/categories/import/actions/import.py +0 -10
- synapse_sdk/plugins/cli/__init__.py +0 -21
- synapse_sdk/plugins/cli/publish.py +0 -37
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
- synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
- synapse_sdk/plugins/utils.py +0 -50
- synapse_sdk/utils/file.py +0 -87
- synapse_sdk/utils/storage.py +0 -91
- synapse_sdk-1.0.0a13.dist-info/METADATA +0 -43
- synapse_sdk-1.0.0a13.dist-info/RECORD +0 -111
- /synapse_sdk/{plugins/categories/import → clients/validators}/__init__.py +0 -0
- /synapse_sdk/{plugins/categories/import/actions → devtools}/__init__.py +0 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info/licenses}/LICENSE +0 -0
- {synapse_sdk-1.0.0a13.dist-info → synapse_sdk-2025.11.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1645 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: to-task-action-development
|
|
3
|
+
title: ToTask Action - SDK Developer Guide
|
|
4
|
+
sidebar_position: 3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ToTask Action - SDK Developer Guide
|
|
8
|
+
|
|
9
|
+
:::info Audience
|
|
10
|
+
This guide is for **SDK core developers** working on the ToTaskAction framework, orchestrator, strategies, and workflow engine.
|
|
11
|
+
|
|
12
|
+
If you're a **plugin developer** creating custom pre-annotation plugins using the AnnotationToTask template, see the [ToTask Template Development](./to-task-template-development.md) guide instead.
|
|
13
|
+
:::
|
|
14
|
+
|
|
15
|
+
This guide provides comprehensive technical documentation for the ToTask action architecture, including design patterns, workflow execution, and extension points for custom strategy implementations.
|
|
16
|
+
|
|
17
|
+
## Architecture Overview
|
|
18
|
+
|
|
19
|
+
The ToTask action is built on a modern, maintainable architecture using four key design patterns:
|
|
20
|
+
|
|
21
|
+
### Design Patterns
|
|
22
|
+
|
|
23
|
+
#### 1. Strategy Pattern
|
|
24
|
+
|
|
25
|
+
Enables pluggable algorithms for validation, annotation, metrics, and data extraction. Different strategies can be selected at runtime based on configuration.
|
|
26
|
+
|
|
27
|
+
#### 2. Facade Pattern
|
|
28
|
+
|
|
29
|
+
`ToTaskOrchestrator` provides a simplified interface to the complex 7-stage workflow, handling orchestration, error management, and rollback.
|
|
30
|
+
|
|
31
|
+
#### 3. Factory Pattern
|
|
32
|
+
|
|
33
|
+
`ToTaskStrategyFactory` creates appropriate strategy instances based on runtime parameters, decoupling strategy creation from usage.
|
|
34
|
+
|
|
35
|
+
#### 4. Context Pattern
|
|
36
|
+
|
|
37
|
+
`ToTaskContext` maintains shared state and communication channels between workflow components throughout execution.
|
|
38
|
+
|
|
39
|
+
## Core Components
|
|
40
|
+
|
|
41
|
+
### Component Diagram
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
classDiagram
|
|
45
|
+
class ToTaskAction {
|
|
46
|
+
+name: str = "to_task"
|
|
47
|
+
+category: PluginCategory.PRE_ANNOTATION
|
|
48
|
+
+method: RunMethod.JOB
|
|
49
|
+
+run_class: ToTaskRun
|
|
50
|
+
+params_model: ToTaskParams
|
|
51
|
+
+progress_categories: dict
|
|
52
|
+
+metrics_categories: dict
|
|
53
|
+
|
|
54
|
+
+start() Dict
|
|
55
|
+
+get_context() ToTaskContext
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class ToTaskOrchestrator {
|
|
59
|
+
+context: ToTaskContext
|
|
60
|
+
+factory: ToTaskStrategyFactory
|
|
61
|
+
+steps_completed: List[str]
|
|
62
|
+
|
|
63
|
+
+execute_workflow() Dict
|
|
64
|
+
+_execute_step(step_name, step_func)
|
|
65
|
+
+_validate_project()
|
|
66
|
+
+_validate_tasks()
|
|
67
|
+
+_determine_annotation_method()
|
|
68
|
+
+_validate_annotation_method()
|
|
69
|
+
+_initialize_processing()
|
|
70
|
+
+_process_all_tasks()
|
|
71
|
+
+_finalize_processing()
|
|
72
|
+
+_rollback_completed_steps()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
class ToTaskContext {
|
|
76
|
+
+params: Dict
|
|
77
|
+
+client: BackendClient
|
|
78
|
+
+logger: Any
|
|
79
|
+
+project: Dict
|
|
80
|
+
+data_collection: Dict
|
|
81
|
+
+task_ids: List[int]
|
|
82
|
+
+metrics: MetricsRecord
|
|
83
|
+
+annotation_method: AnnotationMethod
|
|
84
|
+
+temp_files: List[str]
|
|
85
|
+
+rollback_actions: List[callable]
|
|
86
|
+
|
|
87
|
+
+add_temp_file(file_path)
|
|
88
|
+
+add_rollback_action(action)
|
|
89
|
+
+update_metrics(success, failed, total)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class ToTaskStrategyFactory {
|
|
93
|
+
+create_validation_strategy(type) ValidationStrategy
|
|
94
|
+
+create_annotation_strategy(method) AnnotationStrategy
|
|
95
|
+
+create_metrics_strategy() MetricsStrategy
|
|
96
|
+
+create_extraction_strategy() DataExtractionStrategy
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class ValidationStrategy {
|
|
100
|
+
<<abstract>>
|
|
101
|
+
+validate(context) Dict
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class AnnotationStrategy {
|
|
105
|
+
<<abstract>>
|
|
106
|
+
+process_task(context, task_id, task_data) Dict
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class MetricsStrategy {
|
|
110
|
+
<<abstract>>
|
|
111
|
+
+update_progress(context, current, total)
|
|
112
|
+
+record_task_result(context, task_id, success, error)
|
|
113
|
+
+update_metrics(context, total, success, failed)
|
|
114
|
+
+finalize_metrics(context)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class DataExtractionStrategy {
|
|
118
|
+
<<abstract>>
|
|
119
|
+
+extract_data(context, task_data) Tuple
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
ToTaskAction --> ToTaskOrchestrator : creates
|
|
123
|
+
ToTaskAction --> ToTaskContext : creates
|
|
124
|
+
ToTaskOrchestrator --> ToTaskContext : uses
|
|
125
|
+
ToTaskOrchestrator --> ToTaskStrategyFactory : uses
|
|
126
|
+
ToTaskStrategyFactory --> ValidationStrategy : creates
|
|
127
|
+
ToTaskStrategyFactory --> AnnotationStrategy : creates
|
|
128
|
+
ToTaskStrategyFactory --> MetricsStrategy : creates
|
|
129
|
+
ToTaskStrategyFactory --> DataExtractionStrategy : creates
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### ToTaskAction
|
|
133
|
+
|
|
134
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py`
|
|
135
|
+
|
|
136
|
+
Entry point for the annotation action.
|
|
137
|
+
|
|
138
|
+
**Responsibilities:**
|
|
139
|
+
|
|
140
|
+
- Parameter validation using `ToTaskParams` model
|
|
141
|
+
- Execution context creation
|
|
142
|
+
- Orchestrator instantiation and execution
|
|
143
|
+
- Top-level error handling
|
|
144
|
+
|
|
145
|
+
**Key Attributes:**
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
class ToTaskAction(BaseAction):
|
|
149
|
+
name = 'to_task'
|
|
150
|
+
category = PluginCategory.PRE_ANNOTATION
|
|
151
|
+
method = RunMethod.JOB
|
|
152
|
+
run_class = ToTaskRun
|
|
153
|
+
params_model = ToTaskParams
|
|
154
|
+
|
|
155
|
+
progress_categories = {
|
|
156
|
+
'annotate_task_data': {
|
|
157
|
+
'label': 'Annotating Task Data',
|
|
158
|
+
'weight': 1.0
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
metrics_categories = {
|
|
163
|
+
'annotate_task_data': {
|
|
164
|
+
'label': 'Task Annotation Metrics',
|
|
165
|
+
'metrics': ['success', 'failed', 'stand_by']
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Main Method:**
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
def start(self) -> Dict[str, Any]:
|
|
174
|
+
"""Execute the ToTask action workflow"""
|
|
175
|
+
try:
|
|
176
|
+
# Create execution context
|
|
177
|
+
context = self.get_context()
|
|
178
|
+
|
|
179
|
+
# Create and execute orchestrator
|
|
180
|
+
orchestrator = ToTaskOrchestrator(context)
|
|
181
|
+
result = orchestrator.execute_workflow()
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
'status': JobStatus.SUCCEEDED,
|
|
185
|
+
'message': f'Successfully processed {result["total"]} tasks'
|
|
186
|
+
}
|
|
187
|
+
except Exception as e:
|
|
188
|
+
return {
|
|
189
|
+
'status': JobStatus.FAILED,
|
|
190
|
+
'message': str(e)
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### ToTaskOrchestrator
|
|
195
|
+
|
|
196
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py`
|
|
197
|
+
|
|
198
|
+
Facade that orchestrates the 7-stage workflow.
|
|
199
|
+
|
|
200
|
+
**Responsibilities:**
|
|
201
|
+
|
|
202
|
+
- Execute workflow stages in sequence
|
|
203
|
+
- Track completed steps for rollback
|
|
204
|
+
- Handle errors and trigger rollback
|
|
205
|
+
- Manage strategy instances
|
|
206
|
+
|
|
207
|
+
**Key Methods:**
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
class ToTaskOrchestrator:
|
|
211
|
+
def __init__(self, context: ToTaskContext):
|
|
212
|
+
self.context = context
|
|
213
|
+
self.factory = ToTaskStrategyFactory()
|
|
214
|
+
self.steps_completed = []
|
|
215
|
+
|
|
216
|
+
# Initialize validation strategies
|
|
217
|
+
self.project_validation = self.factory.create_validation_strategy('project')
|
|
218
|
+
self.task_validation = self.factory.create_validation_strategy('task')
|
|
219
|
+
self.target_spec_validation = self.factory.create_validation_strategy('target_spec')
|
|
220
|
+
|
|
221
|
+
# Initialize metrics strategy
|
|
222
|
+
self.metrics_strategy = self.factory.create_metrics_strategy()
|
|
223
|
+
|
|
224
|
+
def execute_workflow(self) -> Dict[str, Any]:
|
|
225
|
+
"""Execute the complete 7-stage workflow"""
|
|
226
|
+
try:
|
|
227
|
+
# Stage 1: Validate project
|
|
228
|
+
self._execute_step('project_validation', self._validate_project)
|
|
229
|
+
|
|
230
|
+
# Stage 2: Validate tasks
|
|
231
|
+
self._execute_step('task_validation', self._validate_tasks)
|
|
232
|
+
|
|
233
|
+
# Stage 3: Determine annotation method
|
|
234
|
+
self._execute_step('method_determination', self._determine_annotation_method)
|
|
235
|
+
|
|
236
|
+
# Stage 4: Validate annotation method
|
|
237
|
+
self._execute_step('method_validation', self._validate_annotation_method)
|
|
238
|
+
|
|
239
|
+
# Stage 5: Initialize processing
|
|
240
|
+
self._execute_step('processing_initialization', self._initialize_processing)
|
|
241
|
+
|
|
242
|
+
# Stage 6: Process all tasks
|
|
243
|
+
self._execute_step('task_processing', self._process_all_tasks)
|
|
244
|
+
|
|
245
|
+
# Stage 7: Finalize
|
|
246
|
+
self._execute_step('finalization', self._finalize_processing)
|
|
247
|
+
|
|
248
|
+
return self.context.metrics
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
# Rollback on any error
|
|
252
|
+
self._rollback_completed_steps()
|
|
253
|
+
raise
|
|
254
|
+
|
|
255
|
+
def _execute_step(self, step_name: str, step_func: callable):
|
|
256
|
+
"""Execute a workflow step and track completion"""
|
|
257
|
+
step_func()
|
|
258
|
+
self.steps_completed.append(step_name)
|
|
259
|
+
self.context.logger.log_message_with_code(
|
|
260
|
+
LogCode.STEP_COMPLETED, step_name
|
|
261
|
+
)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### ToTaskContext
|
|
265
|
+
|
|
266
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py`
|
|
267
|
+
|
|
268
|
+
Shared execution context for workflow components.
|
|
269
|
+
|
|
270
|
+
**Key Attributes:**
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
class ToTaskContext:
|
|
274
|
+
def __init__(self, params: Dict, client: Any, logger: Any):
|
|
275
|
+
# Configuration
|
|
276
|
+
self.params = params
|
|
277
|
+
self.client = client
|
|
278
|
+
self.logger = logger
|
|
279
|
+
|
|
280
|
+
# Project data
|
|
281
|
+
self.project: Optional[Dict] = None
|
|
282
|
+
self.data_collection: Optional[Dict] = None
|
|
283
|
+
|
|
284
|
+
# Task data
|
|
285
|
+
self.task_ids: List[int] = []
|
|
286
|
+
|
|
287
|
+
# Execution state
|
|
288
|
+
self.annotation_method: Optional[AnnotationMethod] = None
|
|
289
|
+
self.metrics = MetricsRecord(success=0, failed=0, total=0)
|
|
290
|
+
|
|
291
|
+
# Cleanup tracking
|
|
292
|
+
self.temp_files: List[str] = []
|
|
293
|
+
self.rollback_actions: List[callable] = []
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Helper Methods:**
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
def add_temp_file(self, file_path: str):
|
|
300
|
+
"""Register a temporary file for cleanup"""
|
|
301
|
+
self.temp_files.append(file_path)
|
|
302
|
+
|
|
303
|
+
def add_rollback_action(self, action: callable):
|
|
304
|
+
"""Register a rollback action"""
|
|
305
|
+
self.rollback_actions.append(action)
|
|
306
|
+
|
|
307
|
+
def update_metrics(self, success_count: int, failed_count: int, total_count: int):
|
|
308
|
+
"""Update metrics counters"""
|
|
309
|
+
self.metrics.success = success_count
|
|
310
|
+
self.metrics.failed = failed_count
|
|
311
|
+
self.metrics.total = total_count
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### ToTaskStrategyFactory
|
|
315
|
+
|
|
316
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/factory.py`
|
|
317
|
+
|
|
318
|
+
Factory for creating strategy instances.
|
|
319
|
+
|
|
320
|
+
**Implementation:**
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
class ToTaskStrategyFactory:
|
|
324
|
+
def create_validation_strategy(
|
|
325
|
+
self, strategy_type: str
|
|
326
|
+
) -> ValidationStrategy:
|
|
327
|
+
"""Create validation strategy by type"""
|
|
328
|
+
strategies = {
|
|
329
|
+
'project': ProjectValidationStrategy,
|
|
330
|
+
'task': TaskValidationStrategy,
|
|
331
|
+
'target_spec': TargetSpecValidationStrategy
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
strategy_class = strategies.get(strategy_type)
|
|
335
|
+
if not strategy_class:
|
|
336
|
+
raise ValueError(f'Unknown validation strategy: {strategy_type}')
|
|
337
|
+
|
|
338
|
+
return strategy_class()
|
|
339
|
+
|
|
340
|
+
def create_annotation_strategy(
|
|
341
|
+
self, method: AnnotationMethod
|
|
342
|
+
) -> AnnotationStrategy:
|
|
343
|
+
"""Create annotation strategy by method"""
|
|
344
|
+
if method == AnnotationMethod.FILE:
|
|
345
|
+
return FileAnnotationStrategy()
|
|
346
|
+
elif method == AnnotationMethod.INFERENCE:
|
|
347
|
+
return InferenceAnnotationStrategy()
|
|
348
|
+
else:
|
|
349
|
+
raise ValueError(f'Unknown annotation method: {method}')
|
|
350
|
+
|
|
351
|
+
def create_metrics_strategy(self) -> MetricsStrategy:
|
|
352
|
+
"""Create metrics strategy"""
|
|
353
|
+
return ToTaskMetricsStrategy()
|
|
354
|
+
|
|
355
|
+
def create_extraction_strategy(self) -> DataExtractionStrategy:
|
|
356
|
+
"""Create data extraction strategy"""
|
|
357
|
+
return FileDataExtractionStrategy()
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Strategy Architecture
|
|
361
|
+
|
|
362
|
+
### Strategy Base Classes
|
|
363
|
+
|
|
364
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/base.py`
|
|
365
|
+
|
|
366
|
+
#### ValidationStrategy
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
class ValidationStrategy(ABC):
|
|
370
|
+
"""Base class for validation strategies"""
|
|
371
|
+
|
|
372
|
+
@abstractmethod
|
|
373
|
+
def validate(self, context: ToTaskContext) -> Dict[str, Any]:
|
|
374
|
+
"""
|
|
375
|
+
Perform validation
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
context: Execution context
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
Dict with 'success' (bool) and optional 'error' (str)
|
|
382
|
+
"""
|
|
383
|
+
pass
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### AnnotationStrategy
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
class AnnotationStrategy(ABC):
|
|
390
|
+
"""Base class for annotation strategies"""
|
|
391
|
+
|
|
392
|
+
@abstractmethod
|
|
393
|
+
def process_task(
|
|
394
|
+
self,
|
|
395
|
+
context: ToTaskContext,
|
|
396
|
+
task_id: int,
|
|
397
|
+
task_data: Dict[str, Any],
|
|
398
|
+
**kwargs
|
|
399
|
+
) -> Dict[str, Any]:
|
|
400
|
+
"""
|
|
401
|
+
Process a single task annotation
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
context: Execution context
|
|
405
|
+
task_id: Task ID
|
|
406
|
+
task_data: Task data from API
|
|
407
|
+
**kwargs: Additional parameters
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
Dict with 'success' (bool) and optional 'error' (str)
|
|
411
|
+
"""
|
|
412
|
+
pass
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
#### MetricsStrategy
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
class MetricsStrategy(ABC):
|
|
419
|
+
"""Base class for metrics tracking"""
|
|
420
|
+
|
|
421
|
+
@abstractmethod
|
|
422
|
+
def update_progress(self, context: ToTaskContext, current: int, total: int):
|
|
423
|
+
"""Update progress percentage"""
|
|
424
|
+
pass
|
|
425
|
+
|
|
426
|
+
@abstractmethod
|
|
427
|
+
def record_task_result(
|
|
428
|
+
self,
|
|
429
|
+
context: ToTaskContext,
|
|
430
|
+
task_id: int,
|
|
431
|
+
success: bool,
|
|
432
|
+
error: Optional[str] = None
|
|
433
|
+
):
|
|
434
|
+
"""Record individual task result"""
|
|
435
|
+
pass
|
|
436
|
+
|
|
437
|
+
@abstractmethod
|
|
438
|
+
def update_metrics(
|
|
439
|
+
self,
|
|
440
|
+
context: ToTaskContext,
|
|
441
|
+
total: int,
|
|
442
|
+
success: int,
|
|
443
|
+
failed: int
|
|
444
|
+
):
|
|
445
|
+
"""Update aggregate metrics"""
|
|
446
|
+
pass
|
|
447
|
+
|
|
448
|
+
@abstractmethod
|
|
449
|
+
def finalize_metrics(self, context: ToTaskContext):
|
|
450
|
+
"""Finalize and log final metrics"""
|
|
451
|
+
pass
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
#### DataExtractionStrategy
|
|
455
|
+
|
|
456
|
+
```python
|
|
457
|
+
class DataExtractionStrategy(ABC):
|
|
458
|
+
"""Base class for data extraction"""
|
|
459
|
+
|
|
460
|
+
@abstractmethod
|
|
461
|
+
def extract_data(
|
|
462
|
+
self,
|
|
463
|
+
context: ToTaskContext,
|
|
464
|
+
task_data: Dict[str, Any]
|
|
465
|
+
) -> Tuple[Optional[str], Optional[str]]:
|
|
466
|
+
"""
|
|
467
|
+
Extract required data from task
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
context: Execution context
|
|
471
|
+
task_data: Task data from API
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
Tuple of (data_url, original_name)
|
|
475
|
+
"""
|
|
476
|
+
pass
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Validation Strategy Implementations
|
|
480
|
+
|
|
481
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/validation.py`
|
|
482
|
+
|
|
483
|
+
#### ProjectValidationStrategy
|
|
484
|
+
|
|
485
|
+
```python
|
|
486
|
+
class ProjectValidationStrategy(ValidationStrategy):
|
|
487
|
+
"""Validates project and data collection"""
|
|
488
|
+
|
|
489
|
+
def validate(self, context: ToTaskContext) -> Dict[str, Any]:
|
|
490
|
+
try:
|
|
491
|
+
# Get project
|
|
492
|
+
project_id = context.params['project']
|
|
493
|
+
project = context.client.get_project(project_id)
|
|
494
|
+
|
|
495
|
+
if not project:
|
|
496
|
+
return {
|
|
497
|
+
'success': False,
|
|
498
|
+
'error': f'Project {project_id} not found'
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
context.project = project
|
|
502
|
+
|
|
503
|
+
# Check data collection
|
|
504
|
+
data_collection_id = project.get('data_collection')
|
|
505
|
+
if not data_collection_id:
|
|
506
|
+
return {
|
|
507
|
+
'success': False,
|
|
508
|
+
'error': 'Project has no data collection'
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
# Get data collection details
|
|
512
|
+
data_collection = context.client.get_data_collection(
|
|
513
|
+
data_collection_id
|
|
514
|
+
)
|
|
515
|
+
context.data_collection = data_collection
|
|
516
|
+
|
|
517
|
+
context.logger.log_message_with_code(
|
|
518
|
+
LogCode.PROJECT_VALIDATED,
|
|
519
|
+
project_id,
|
|
520
|
+
data_collection_id
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
return {'success': True}
|
|
524
|
+
|
|
525
|
+
except Exception as e:
|
|
526
|
+
return {
|
|
527
|
+
'success': False,
|
|
528
|
+
'error': f'Project validation failed: {str(e)}'
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
#### TaskValidationStrategy
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
class TaskValidationStrategy(ValidationStrategy):
|
|
536
|
+
"""Validates and retrieves tasks"""
|
|
537
|
+
|
|
538
|
+
def validate(self, context: ToTaskContext) -> Dict[str, Any]:
|
|
539
|
+
try:
|
|
540
|
+
# Get tasks matching filters
|
|
541
|
+
project_id = context.params['project']
|
|
542
|
+
task_filters = context.params['task_filters']
|
|
543
|
+
|
|
544
|
+
tasks = context.client.list_tasks(
|
|
545
|
+
project=project_id,
|
|
546
|
+
**task_filters
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
if not tasks:
|
|
550
|
+
return {
|
|
551
|
+
'success': False,
|
|
552
|
+
'error': 'No tasks found matching filters'
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
# Store task IDs
|
|
556
|
+
context.task_ids = [task['id'] for task in tasks]
|
|
557
|
+
|
|
558
|
+
context.logger.log_message_with_code(
|
|
559
|
+
LogCode.TASKS_VALIDATED,
|
|
560
|
+
len(context.task_ids)
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
return {'success': True}
|
|
564
|
+
|
|
565
|
+
except Exception as e:
|
|
566
|
+
return {
|
|
567
|
+
'success': False,
|
|
568
|
+
'error': f'Task validation failed: {str(e)}'
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### TargetSpecValidationStrategy
|
|
573
|
+
|
|
574
|
+
```python
|
|
575
|
+
class TargetSpecValidationStrategy(ValidationStrategy):
|
|
576
|
+
"""Validates target specification name exists"""
|
|
577
|
+
|
|
578
|
+
def validate(self, context: ToTaskContext) -> Dict[str, Any]:
|
|
579
|
+
try:
|
|
580
|
+
target_spec_name = context.params.get('target_specification_name')
|
|
581
|
+
|
|
582
|
+
if not target_spec_name:
|
|
583
|
+
return {
|
|
584
|
+
'success': False,
|
|
585
|
+
'error': 'target_specification_name is required for file method'
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
# Get data collection file specifications
|
|
589
|
+
file_specs = context.data_collection.get('file_specifications', [])
|
|
590
|
+
spec_names = [spec['name'] for spec in file_specs]
|
|
591
|
+
|
|
592
|
+
if target_spec_name not in spec_names:
|
|
593
|
+
return {
|
|
594
|
+
'success': False,
|
|
595
|
+
'error': f'Target specification "{target_spec_name}" not found'
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
context.logger.log_message_with_code(
|
|
599
|
+
LogCode.TARGET_SPEC_VALIDATED,
|
|
600
|
+
target_spec_name
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
return {'success': True}
|
|
604
|
+
|
|
605
|
+
except Exception as e:
|
|
606
|
+
return {
|
|
607
|
+
'success': False,
|
|
608
|
+
'error': f'Target spec validation failed: {str(e)}'
|
|
609
|
+
}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Annotation Strategy Implementations
|
|
613
|
+
|
|
614
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/annotation.py`
|
|
615
|
+
|
|
616
|
+
#### FileAnnotationStrategy
|
|
617
|
+
|
|
618
|
+
```python
|
|
619
|
+
class FileAnnotationStrategy(AnnotationStrategy):
|
|
620
|
+
"""Annotates tasks using JSON files"""
|
|
621
|
+
|
|
622
|
+
def process_task(
|
|
623
|
+
self,
|
|
624
|
+
context: ToTaskContext,
|
|
625
|
+
task_id: int,
|
|
626
|
+
task_data: Dict[str, Any],
|
|
627
|
+
**kwargs
|
|
628
|
+
) -> Dict[str, Any]:
|
|
629
|
+
try:
|
|
630
|
+
# Extract target file URL
|
|
631
|
+
extraction_strategy = FileDataExtractionStrategy()
|
|
632
|
+
url, original_name = extraction_strategy.extract_data(
|
|
633
|
+
context, task_data
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
if not url:
|
|
637
|
+
return {
|
|
638
|
+
'success': False,
|
|
639
|
+
'error': 'No file URL found for target specification'
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
# Download JSON data
|
|
643
|
+
json_data = self._download_json_data(url)
|
|
644
|
+
|
|
645
|
+
# Convert to task object
|
|
646
|
+
task_object = self._convert_to_task_object(json_data)
|
|
647
|
+
|
|
648
|
+
# Update task
|
|
649
|
+
result = context.client.update_task(
|
|
650
|
+
task_id,
|
|
651
|
+
data={'data': task_object}
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
if result:
|
|
655
|
+
context.logger.log_message_with_code(
|
|
656
|
+
LogCode.ANNOTATION_COMPLETED,
|
|
657
|
+
task_id
|
|
658
|
+
)
|
|
659
|
+
return {'success': True}
|
|
660
|
+
else:
|
|
661
|
+
return {
|
|
662
|
+
'success': False,
|
|
663
|
+
'error': 'Failed to update task'
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
except Exception as e:
|
|
667
|
+
return {
|
|
668
|
+
'success': False,
|
|
669
|
+
'error': f'File annotation failed: {str(e)}'
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
def _download_json_data(self, url: str) -> Dict[str, Any]:
|
|
673
|
+
"""Download and parse JSON from URL"""
|
|
674
|
+
import requests
|
|
675
|
+
response = requests.get(url, timeout=30)
|
|
676
|
+
response.raise_for_status()
|
|
677
|
+
return response.json()
|
|
678
|
+
|
|
679
|
+
def _convert_to_task_object(self, json_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
680
|
+
"""Convert JSON data to task object format"""
|
|
681
|
+
# Validate and transform as needed
|
|
682
|
+
return json_data
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
#### InferenceAnnotationStrategy
|
|
686
|
+
|
|
687
|
+
```python
|
|
688
|
+
class InferenceAnnotationStrategy(AnnotationStrategy):
|
|
689
|
+
"""Annotates tasks using model inference"""
|
|
690
|
+
|
|
691
|
+
def process_task(
|
|
692
|
+
self,
|
|
693
|
+
context: ToTaskContext,
|
|
694
|
+
task_id: int,
|
|
695
|
+
task_data: Dict[str, Any],
|
|
696
|
+
**kwargs
|
|
697
|
+
) -> Dict[str, Any]:
|
|
698
|
+
try:
|
|
699
|
+
# Get preprocessor info
|
|
700
|
+
preprocessor_id = context.params['pre_processor']
|
|
701
|
+
preprocessor_info = self._get_preprocessor_info(
|
|
702
|
+
context, preprocessor_id
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
# Ensure preprocessor is running
|
|
706
|
+
self._ensure_preprocessor_running(
|
|
707
|
+
context, preprocessor_info['code']
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
# Extract primary image URL
|
|
711
|
+
image_url = self._extract_primary_image_url(task_data)
|
|
712
|
+
|
|
713
|
+
if not image_url:
|
|
714
|
+
return {
|
|
715
|
+
'success': False,
|
|
716
|
+
'error': 'No primary image found'
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
# Call preprocessor API
|
|
720
|
+
inference_params = context.params.get('pre_processor_params', {})
|
|
721
|
+
result = self._call_preprocessor_api(
|
|
722
|
+
preprocessor_info,
|
|
723
|
+
image_url,
|
|
724
|
+
inference_params
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
# Convert inference result to task object
|
|
728
|
+
task_object = self._convert_inference_to_task_object(result)
|
|
729
|
+
|
|
730
|
+
# Update task
|
|
731
|
+
update_result = context.client.update_task(
|
|
732
|
+
task_id,
|
|
733
|
+
data={'data': task_object}
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
if update_result:
|
|
737
|
+
context.logger.log_message_with_code(
|
|
738
|
+
LogCode.INFERENCE_COMPLETED,
|
|
739
|
+
task_id
|
|
740
|
+
)
|
|
741
|
+
return {'success': True}
|
|
742
|
+
else:
|
|
743
|
+
return {
|
|
744
|
+
'success': False,
|
|
745
|
+
'error': 'Failed to update task with inference results'
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
except Exception as e:
|
|
749
|
+
return {
|
|
750
|
+
'success': False,
|
|
751
|
+
'error': f'Inference annotation failed: {str(e)}'
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
def _get_preprocessor_info(
|
|
755
|
+
self, context: ToTaskContext, preprocessor_id: int
|
|
756
|
+
) -> Dict[str, Any]:
|
|
757
|
+
"""Get preprocessor details"""
|
|
758
|
+
return context.client.get_plugin_release(preprocessor_id)
|
|
759
|
+
|
|
760
|
+
def _ensure_preprocessor_running(
|
|
761
|
+
self, context: ToTaskContext, preprocessor_code: str
|
|
762
|
+
):
|
|
763
|
+
"""Ensure preprocessor is deployed and running"""
|
|
764
|
+
# Check Ray Serve applications
|
|
765
|
+
apps = context.client.list_serve_applications(
|
|
766
|
+
agent=context.params['agent']
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
# Find app by code
|
|
770
|
+
app = next(
|
|
771
|
+
(a for a in apps if a['code'] == preprocessor_code),
|
|
772
|
+
None
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
if not app or app['status'] != 'RUNNING':
|
|
776
|
+
# Deploy preprocessor
|
|
777
|
+
context.logger.log_message_with_code(
|
|
778
|
+
LogCode.DEPLOYING_PREPROCESSOR,
|
|
779
|
+
preprocessor_code
|
|
780
|
+
)
|
|
781
|
+
# Deployment logic here...
|
|
782
|
+
|
|
783
|
+
def _extract_primary_image_url(self, task_data: Dict[str, Any]) -> Optional[str]:
|
|
784
|
+
"""Extract primary image URL from task data"""
|
|
785
|
+
data_unit = task_data.get('data_unit', {})
|
|
786
|
+
files = data_unit.get('files', [])
|
|
787
|
+
|
|
788
|
+
for file_info in files:
|
|
789
|
+
if file_info.get('is_primary'):
|
|
790
|
+
return file_info.get('url')
|
|
791
|
+
|
|
792
|
+
return None
|
|
793
|
+
|
|
794
|
+
def _call_preprocessor_api(
|
|
795
|
+
self,
|
|
796
|
+
preprocessor_info: Dict[str, Any],
|
|
797
|
+
image_url: str,
|
|
798
|
+
params: Dict[str, Any]
|
|
799
|
+
) -> Dict[str, Any]:
|
|
800
|
+
"""Call preprocessor API for inference"""
|
|
801
|
+
import requests
|
|
802
|
+
|
|
803
|
+
api_url = preprocessor_info['api_endpoint']
|
|
804
|
+
payload = {
|
|
805
|
+
'image_url': image_url,
|
|
806
|
+
**params
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
response = requests.post(api_url, json=payload, timeout=60)
|
|
810
|
+
response.raise_for_status()
|
|
811
|
+
return response.json()
|
|
812
|
+
|
|
813
|
+
def _convert_inference_to_task_object(
|
|
814
|
+
self, result: Dict[str, Any]
|
|
815
|
+
) -> Dict[str, Any]:
|
|
816
|
+
"""Convert inference results to task object format"""
|
|
817
|
+
# Transform model output to task object structure
|
|
818
|
+
return result
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
## Workflow Execution
|
|
822
|
+
|
|
823
|
+
### 7-Stage Workflow
|
|
824
|
+
|
|
825
|
+
```mermaid
|
|
826
|
+
flowchart TD
|
|
827
|
+
A[ToTask Action Start] --> B[Parameter & Run Validation]
|
|
828
|
+
B --> C[Create ToTaskContext]
|
|
829
|
+
C --> D[Create ToTaskOrchestrator]
|
|
830
|
+
D --> E[Execute Workflow]
|
|
831
|
+
|
|
832
|
+
E --> F[Stage 1: Project Validation]
|
|
833
|
+
F -->|Success| G[Stage 2: Task Validation]
|
|
834
|
+
F -->|Failure| Z[Rollback & Error]
|
|
835
|
+
|
|
836
|
+
G -->|Success| H[Stage 3: Method Determination]
|
|
837
|
+
G -->|Failure| Z
|
|
838
|
+
|
|
839
|
+
H --> I{Method Type?}
|
|
840
|
+
I -->|File| J[Stage 4: Target Spec Validation]
|
|
841
|
+
I -->|Inference| K[Stage 4: Preprocessor Validation]
|
|
842
|
+
|
|
843
|
+
J -->|Success| L[Stage 5: Initialize Processing]
|
|
844
|
+
K -->|Success| L
|
|
845
|
+
J -->|Failure| Z
|
|
846
|
+
K -->|Failure| Z
|
|
847
|
+
|
|
848
|
+
L --> M[Stage 6: Task Processing Loop]
|
|
849
|
+
|
|
850
|
+
M --> N{More Tasks?}
|
|
851
|
+
N -->|Yes| O[Fetch Task Data]
|
|
852
|
+
N -->|No| T[Stage 7: Finalization]
|
|
853
|
+
|
|
854
|
+
O --> P{Annotation Method?}
|
|
855
|
+
P -->|File| Q[File Annotation Strategy]
|
|
856
|
+
P -->|Inference| R[Inference Annotation Strategy]
|
|
857
|
+
|
|
858
|
+
Q --> S{Success?}
|
|
859
|
+
R --> S
|
|
860
|
+
|
|
861
|
+
S -->|Yes| U[Increment Success Count]
|
|
862
|
+
S -->|No| V[Increment Failed Count]
|
|
863
|
+
S -->|Critical| Z
|
|
864
|
+
|
|
865
|
+
U --> W[Update Progress & Metrics]
|
|
866
|
+
V --> W
|
|
867
|
+
|
|
868
|
+
W --> N
|
|
869
|
+
|
|
870
|
+
T --> X[Aggregate Final Metrics]
|
|
871
|
+
X --> Y[Return Success Result]
|
|
872
|
+
|
|
873
|
+
Z --> ZA[Rollback Completed Steps]
|
|
874
|
+
ZA --> ZB[Cleanup Temp Files]
|
|
875
|
+
ZB --> ZC[Raise Error Exception]
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
### Stage Details
|
|
879
|
+
|
|
880
|
+
#### Stage 1: Project Validation
|
|
881
|
+
|
|
882
|
+
```python
|
|
883
|
+
def _validate_project(self):
|
|
884
|
+
"""Validate project and data collection"""
|
|
885
|
+
result = self.project_validation.validate(self.context)
|
|
886
|
+
|
|
887
|
+
if not result['success']:
|
|
888
|
+
raise PreAnnotationToTaskFailed(result.get('error'))
|
|
889
|
+
|
|
890
|
+
# Project and data collection now available in context
|
|
891
|
+
self.context.logger.log_message_with_code(
|
|
892
|
+
LogCode.PROJECT_VALIDATION_COMPLETE
|
|
893
|
+
)
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
#### Stage 2: Task Validation
|
|
897
|
+
|
|
898
|
+
```python
|
|
899
|
+
def _validate_tasks(self):
|
|
900
|
+
"""Validate and retrieve tasks"""
|
|
901
|
+
result = self.task_validation.validate(self.context)
|
|
902
|
+
|
|
903
|
+
if not result['success']:
|
|
904
|
+
raise PreAnnotationToTaskFailed(result.get('error'))
|
|
905
|
+
|
|
906
|
+
# Task IDs now available in context.task_ids
|
|
907
|
+
task_count = len(self.context.task_ids)
|
|
908
|
+
self.context.logger.log_message_with_code(
|
|
909
|
+
LogCode.TASKS_FOUND,
|
|
910
|
+
task_count
|
|
911
|
+
)
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
#### Stage 3: Method Determination
|
|
915
|
+
|
|
916
|
+
```python
|
|
917
|
+
def _determine_annotation_method(self):
|
|
918
|
+
"""Determine annotation method from parameters"""
|
|
919
|
+
method = self.context.params.get('method')
|
|
920
|
+
|
|
921
|
+
if method == 'file':
|
|
922
|
+
self.context.annotation_method = AnnotationMethod.FILE
|
|
923
|
+
elif method == 'inference':
|
|
924
|
+
self.context.annotation_method = AnnotationMethod.INFERENCE
|
|
925
|
+
else:
|
|
926
|
+
raise PreAnnotationToTaskFailed(
|
|
927
|
+
f'Unsupported annotation method: {method}'
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
self.context.logger.log_message_with_code(
|
|
931
|
+
LogCode.METHOD_DETERMINED,
|
|
932
|
+
self.context.annotation_method
|
|
933
|
+
)
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
#### Stage 4: Method Validation
|
|
937
|
+
|
|
938
|
+
```python
|
|
939
|
+
def _validate_annotation_method(self):
|
|
940
|
+
"""Validate method-specific requirements"""
|
|
941
|
+
if self.context.annotation_method == AnnotationMethod.FILE:
|
|
942
|
+
# Validate target specification
|
|
943
|
+
result = self.target_spec_validation.validate(self.context)
|
|
944
|
+
|
|
945
|
+
if not result['success']:
|
|
946
|
+
raise PreAnnotationToTaskFailed(result.get('error'))
|
|
947
|
+
|
|
948
|
+
elif self.context.annotation_method == AnnotationMethod.INFERENCE:
|
|
949
|
+
# Validate preprocessor
|
|
950
|
+
preprocessor_id = self.context.params.get('pre_processor')
|
|
951
|
+
|
|
952
|
+
if not preprocessor_id:
|
|
953
|
+
raise PreAnnotationToTaskFailed(
|
|
954
|
+
'pre_processor is required for inference method'
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
# Additional preprocessor validation...
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
#### Stage 5: Processing Initialization
|
|
961
|
+
|
|
962
|
+
```python
|
|
963
|
+
def _initialize_processing(self):
|
|
964
|
+
"""Initialize metrics and progress tracking"""
|
|
965
|
+
total_tasks = len(self.context.task_ids)
|
|
966
|
+
|
|
967
|
+
# Reset metrics
|
|
968
|
+
self.context.update_metrics(
|
|
969
|
+
success_count=0,
|
|
970
|
+
failed_count=0,
|
|
971
|
+
total_count=total_tasks
|
|
972
|
+
)
|
|
973
|
+
|
|
974
|
+
# Initialize progress
|
|
975
|
+
self.metrics_strategy.update_progress(
|
|
976
|
+
self.context,
|
|
977
|
+
current=0,
|
|
978
|
+
total=total_tasks
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
self.context.logger.log_message_with_code(
|
|
982
|
+
LogCode.PROCESSING_INITIALIZED,
|
|
983
|
+
total_tasks
|
|
984
|
+
)
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
#### Stage 6: Task Processing
|
|
988
|
+
|
|
989
|
+
```python
|
|
990
|
+
def _process_all_tasks(self):
|
|
991
|
+
"""Process all tasks with annotation strategy"""
|
|
992
|
+
# Create annotation strategy
|
|
993
|
+
annotation_strategy = self.factory.create_annotation_strategy(
|
|
994
|
+
self.context.annotation_method
|
|
995
|
+
)
|
|
996
|
+
|
|
997
|
+
success_count = 0
|
|
998
|
+
failed_count = 0
|
|
999
|
+
total_tasks = len(self.context.task_ids)
|
|
1000
|
+
|
|
1001
|
+
# Task query parameters
|
|
1002
|
+
task_params = {
|
|
1003
|
+
'expand': 'data_unit,data_unit.files'
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
# Process each task
|
|
1007
|
+
for index, task_id in enumerate(self.context.task_ids, start=1):
|
|
1008
|
+
try:
|
|
1009
|
+
# Fetch task data
|
|
1010
|
+
task_data = self.context.client.get_task(
|
|
1011
|
+
task_id,
|
|
1012
|
+
params=task_params
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
# Execute annotation strategy
|
|
1016
|
+
result = annotation_strategy.process_task(
|
|
1017
|
+
self.context,
|
|
1018
|
+
task_id,
|
|
1019
|
+
task_data,
|
|
1020
|
+
target_specification_name=self.context.params.get(
|
|
1021
|
+
'target_specification_name'
|
|
1022
|
+
)
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
# Update counters
|
|
1026
|
+
if result['success']:
|
|
1027
|
+
success_count += 1
|
|
1028
|
+
self.metrics_strategy.record_task_result(
|
|
1029
|
+
self.context,
|
|
1030
|
+
task_id,
|
|
1031
|
+
success=True
|
|
1032
|
+
)
|
|
1033
|
+
else:
|
|
1034
|
+
failed_count += 1
|
|
1035
|
+
self.metrics_strategy.record_task_result(
|
|
1036
|
+
self.context,
|
|
1037
|
+
task_id,
|
|
1038
|
+
success=False,
|
|
1039
|
+
error=result.get('error')
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
except CriticalError as e:
|
|
1043
|
+
# Critical errors stop processing
|
|
1044
|
+
raise PreAnnotationToTaskFailed(
|
|
1045
|
+
f'Critical error processing task {task_id}: {str(e)}'
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
except Exception as e:
|
|
1049
|
+
# Task-level errors continue processing
|
|
1050
|
+
failed_count += 1
|
|
1051
|
+
self.context.logger.log_message_with_code(
|
|
1052
|
+
LogCode.TASK_PROCESSING_FAILED,
|
|
1053
|
+
task_id,
|
|
1054
|
+
str(e)
|
|
1055
|
+
)
|
|
1056
|
+
|
|
1057
|
+
# Update progress and metrics
|
|
1058
|
+
self.metrics_strategy.update_progress(
|
|
1059
|
+
self.context,
|
|
1060
|
+
current=index,
|
|
1061
|
+
total=total_tasks
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
self.context.update_metrics(
|
|
1065
|
+
success_count=success_count,
|
|
1066
|
+
failed_count=failed_count,
|
|
1067
|
+
total_count=total_tasks
|
|
1068
|
+
)
|
|
1069
|
+
|
|
1070
|
+
self.metrics_strategy.update_metrics(
|
|
1071
|
+
self.context,
|
|
1072
|
+
total=total_tasks,
|
|
1073
|
+
success=success_count,
|
|
1074
|
+
failed=failed_count
|
|
1075
|
+
)
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
#### Stage 7: Finalization
|
|
1079
|
+
|
|
1080
|
+
```python
|
|
1081
|
+
def _finalize_processing(self):
|
|
1082
|
+
"""Finalize metrics and cleanup"""
|
|
1083
|
+
self.metrics_strategy.finalize_metrics(self.context)
|
|
1084
|
+
|
|
1085
|
+
# Log completion
|
|
1086
|
+
self.context.logger.log_message_with_code(
|
|
1087
|
+
LogCode.TO_TASK_COMPLETED,
|
|
1088
|
+
self.context.metrics.success,
|
|
1089
|
+
self.context.metrics.failed
|
|
1090
|
+
)
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
## Error Handling and Rollback
|
|
1094
|
+
|
|
1095
|
+
### Rollback Mechanism
|
|
1096
|
+
|
|
1097
|
+
```python
|
|
1098
|
+
def _rollback_completed_steps(self):
|
|
1099
|
+
"""Rollback all completed workflow steps"""
|
|
1100
|
+
self.context.logger.log_message_with_code(
|
|
1101
|
+
LogCode.ROLLBACK_STARTED,
|
|
1102
|
+
len(self.steps_completed)
|
|
1103
|
+
)
|
|
1104
|
+
|
|
1105
|
+
# Rollback steps in reverse order
|
|
1106
|
+
for step in reversed(self.steps_completed):
|
|
1107
|
+
try:
|
|
1108
|
+
rollback_method = getattr(self, f'_rollback_{step}', None)
|
|
1109
|
+
if rollback_method:
|
|
1110
|
+
rollback_method()
|
|
1111
|
+
self.context.logger.log_message_with_code(
|
|
1112
|
+
LogCode.STEP_ROLLED_BACK,
|
|
1113
|
+
step
|
|
1114
|
+
)
|
|
1115
|
+
except Exception as e:
|
|
1116
|
+
self.context.logger.log_message_with_code(
|
|
1117
|
+
LogCode.ROLLBACK_FAILED,
|
|
1118
|
+
step,
|
|
1119
|
+
str(e)
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
# Execute custom rollback actions
|
|
1123
|
+
for action in reversed(self.context.rollback_actions):
|
|
1124
|
+
try:
|
|
1125
|
+
action()
|
|
1126
|
+
except Exception as e:
|
|
1127
|
+
self.context.logger.log_message_with_code(
|
|
1128
|
+
LogCode.ROLLBACK_ACTION_FAILED,
|
|
1129
|
+
str(e)
|
|
1130
|
+
)
|
|
1131
|
+
|
|
1132
|
+
# Cleanup temp files
|
|
1133
|
+
self._cleanup_temp_files()
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
### Step-Specific Rollback Methods
|
|
1137
|
+
|
|
1138
|
+
```python
|
|
1139
|
+
def _rollback_project_validation(self):
|
|
1140
|
+
"""Rollback project validation"""
|
|
1141
|
+
self.context.project = None
|
|
1142
|
+
self.context.data_collection = None
|
|
1143
|
+
|
|
1144
|
+
def _rollback_task_validation(self):
|
|
1145
|
+
"""Rollback task validation"""
|
|
1146
|
+
self.context.task_ids = []
|
|
1147
|
+
|
|
1148
|
+
def _rollback_processing_initialization(self):
|
|
1149
|
+
"""Rollback processing initialization"""
|
|
1150
|
+
self.context.metrics = MetricsRecord(success=0, failed=0, total=0)
|
|
1151
|
+
|
|
1152
|
+
def _rollback_task_processing(self):
|
|
1153
|
+
"""Rollback task processing"""
|
|
1154
|
+
# Cleanup is handled by temp files
|
|
1155
|
+
pass
|
|
1156
|
+
|
|
1157
|
+
def _cleanup_temp_files(self):
|
|
1158
|
+
"""Remove all temporary files"""
|
|
1159
|
+
for file_path in self.context.temp_files:
|
|
1160
|
+
try:
|
|
1161
|
+
if os.path.exists(file_path):
|
|
1162
|
+
os.remove(file_path)
|
|
1163
|
+
except Exception as e:
|
|
1164
|
+
self.context.logger.log_message_with_code(
|
|
1165
|
+
LogCode.TEMP_FILE_CLEANUP_FAILED,
|
|
1166
|
+
file_path,
|
|
1167
|
+
str(e)
|
|
1168
|
+
)
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
### Error Levels
|
|
1172
|
+
|
|
1173
|
+
**Task-Level Errors:**
|
|
1174
|
+
|
|
1175
|
+
- Individual task processing failures
|
|
1176
|
+
- Logged and counted in metrics
|
|
1177
|
+
- Workflow continues with remaining tasks
|
|
1178
|
+
|
|
1179
|
+
**Critical Errors:**
|
|
1180
|
+
|
|
1181
|
+
- System-level failures
|
|
1182
|
+
- Trigger immediate workflow termination
|
|
1183
|
+
- Initiate rollback process
|
|
1184
|
+
|
|
1185
|
+
**Workflow Errors:**
|
|
1186
|
+
|
|
1187
|
+
- Stage validation failures
|
|
1188
|
+
- Rollback completed stages
|
|
1189
|
+
- Propagate exception to caller
|
|
1190
|
+
|
|
1191
|
+
## Extending the ToTask Action
|
|
1192
|
+
|
|
1193
|
+
### Creating Custom Validation Strategies
|
|
1194
|
+
|
|
1195
|
+
```python
|
|
1196
|
+
from synapse_sdk.plugins.categories.pre_annotation.actions.to_task.strategies.base import ValidationStrategy
|
|
1197
|
+
|
|
1198
|
+
class CustomBusinessRuleValidationStrategy(ValidationStrategy):
|
|
1199
|
+
"""Custom validation for business-specific rules"""
|
|
1200
|
+
|
|
1201
|
+
def validate(self, context: ToTaskContext) -> Dict[str, Any]:
|
|
1202
|
+
"""Validate custom business rules"""
|
|
1203
|
+
try:
|
|
1204
|
+
# Get tasks
|
|
1205
|
+
tasks = context.client.list_tasks(
|
|
1206
|
+
project=context.params['project'],
|
|
1207
|
+
**context.params['task_filters']
|
|
1208
|
+
)
|
|
1209
|
+
|
|
1210
|
+
# Custom validation logic
|
|
1211
|
+
for task in tasks:
|
|
1212
|
+
# Check business rules
|
|
1213
|
+
if not self._meets_business_rules(task):
|
|
1214
|
+
return {
|
|
1215
|
+
'success': False,
|
|
1216
|
+
'error': f'Task {task["id"]} violates business rules'
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
# Store task IDs
|
|
1220
|
+
context.task_ids = [task['id'] for task in tasks]
|
|
1221
|
+
|
|
1222
|
+
return {'success': True}
|
|
1223
|
+
|
|
1224
|
+
except Exception as e:
|
|
1225
|
+
return {
|
|
1226
|
+
'success': False,
|
|
1227
|
+
'error': f'Custom validation failed: {str(e)}'
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
def _meets_business_rules(self, task: Dict[str, Any]) -> bool:
|
|
1231
|
+
"""Check if task meets business rules"""
|
|
1232
|
+
# Implement your business logic
|
|
1233
|
+
return True
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
### Creating Custom Annotation Strategies
|
|
1237
|
+
|
|
1238
|
+
```python
|
|
1239
|
+
from synapse_sdk.plugins.categories.pre_annotation.actions.to_task.strategies.base import AnnotationStrategy
|
|
1240
|
+
|
|
1241
|
+
class ExternalAPIAnnotationStrategy(AnnotationStrategy):
|
|
1242
|
+
"""Annotate using external API service"""
|
|
1243
|
+
|
|
1244
|
+
def process_task(
|
|
1245
|
+
self,
|
|
1246
|
+
context: ToTaskContext,
|
|
1247
|
+
task_id: int,
|
|
1248
|
+
task_data: Dict[str, Any],
|
|
1249
|
+
**kwargs
|
|
1250
|
+
) -> Dict[str, Any]:
|
|
1251
|
+
"""Process task using external API"""
|
|
1252
|
+
try:
|
|
1253
|
+
# Extract image URL
|
|
1254
|
+
image_url = self._extract_image_url(task_data)
|
|
1255
|
+
|
|
1256
|
+
# Call external API
|
|
1257
|
+
api_url = context.params.get('external_api_url')
|
|
1258
|
+
api_key = context.params.get('external_api_key')
|
|
1259
|
+
|
|
1260
|
+
annotations = self._call_external_api(
|
|
1261
|
+
api_url,
|
|
1262
|
+
api_key,
|
|
1263
|
+
image_url
|
|
1264
|
+
)
|
|
1265
|
+
|
|
1266
|
+
# Convert to task object
|
|
1267
|
+
task_object = self._convert_annotations(annotations)
|
|
1268
|
+
|
|
1269
|
+
# Update task
|
|
1270
|
+
result = context.client.update_task(
|
|
1271
|
+
task_id,
|
|
1272
|
+
data={'data': task_object}
|
|
1273
|
+
)
|
|
1274
|
+
|
|
1275
|
+
if result:
|
|
1276
|
+
return {'success': True}
|
|
1277
|
+
else:
|
|
1278
|
+
return {
|
|
1279
|
+
'success': False,
|
|
1280
|
+
'error': 'Failed to update task'
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
except Exception as e:
|
|
1284
|
+
return {
|
|
1285
|
+
'success': False,
|
|
1286
|
+
'error': f'External API annotation failed: {str(e)}'
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
def _extract_image_url(self, task_data: Dict[str, Any]) -> str:
|
|
1290
|
+
"""Extract image URL from task data"""
|
|
1291
|
+
# Implementation...
|
|
1292
|
+
pass
|
|
1293
|
+
|
|
1294
|
+
def _call_external_api(
|
|
1295
|
+
self, api_url: str, api_key: str, image_url: str
|
|
1296
|
+
) -> Dict[str, Any]:
|
|
1297
|
+
"""Call external annotation API"""
|
|
1298
|
+
import requests
|
|
1299
|
+
|
|
1300
|
+
response = requests.post(
|
|
1301
|
+
api_url,
|
|
1302
|
+
headers={'Authorization': f'Bearer {api_key}'},
|
|
1303
|
+
json={'image_url': image_url},
|
|
1304
|
+
timeout=30
|
|
1305
|
+
)
|
|
1306
|
+
response.raise_for_status()
|
|
1307
|
+
return response.json()
|
|
1308
|
+
|
|
1309
|
+
def _convert_annotations(self, annotations: Dict[str, Any]) -> Dict[str, Any]:
|
|
1310
|
+
"""Convert external format to task object"""
|
|
1311
|
+
# Implementation...
|
|
1312
|
+
pass
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
### Integrating Custom Strategies
|
|
1316
|
+
|
|
1317
|
+
Update the factory to support custom strategies:
|
|
1318
|
+
|
|
1319
|
+
```python
|
|
1320
|
+
class CustomToTaskStrategyFactory(ToTaskStrategyFactory):
|
|
1321
|
+
"""Extended factory with custom strategies"""
|
|
1322
|
+
|
|
1323
|
+
def create_validation_strategy(
|
|
1324
|
+
self, strategy_type: str
|
|
1325
|
+
) -> ValidationStrategy:
|
|
1326
|
+
"""Create validation strategy including custom types"""
|
|
1327
|
+
strategies = {
|
|
1328
|
+
'project': ProjectValidationStrategy,
|
|
1329
|
+
'task': TaskValidationStrategy,
|
|
1330
|
+
'target_spec': TargetSpecValidationStrategy,
|
|
1331
|
+
'business_rules': CustomBusinessRuleValidationStrategy, # Custom
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
strategy_class = strategies.get(strategy_type)
|
|
1335
|
+
if not strategy_class:
|
|
1336
|
+
raise ValueError(f'Unknown validation strategy: {strategy_type}')
|
|
1337
|
+
|
|
1338
|
+
return strategy_class()
|
|
1339
|
+
|
|
1340
|
+
def create_annotation_strategy(
|
|
1341
|
+
self, method: AnnotationMethod
|
|
1342
|
+
) -> AnnotationStrategy:
|
|
1343
|
+
"""Create annotation strategy including custom methods"""
|
|
1344
|
+
if method == AnnotationMethod.FILE:
|
|
1345
|
+
return FileAnnotationStrategy()
|
|
1346
|
+
elif method == AnnotationMethod.INFERENCE:
|
|
1347
|
+
return InferenceAnnotationStrategy()
|
|
1348
|
+
elif method == 'external_api': # Custom
|
|
1349
|
+
return ExternalAPIAnnotationStrategy()
|
|
1350
|
+
else:
|
|
1351
|
+
raise ValueError(f'Unknown annotation method: {method}')
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
## API Reference
|
|
1355
|
+
|
|
1356
|
+
### Models and Enums
|
|
1357
|
+
|
|
1358
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py`
|
|
1359
|
+
|
|
1360
|
+
#### ToTaskParams
|
|
1361
|
+
|
|
1362
|
+
```python
|
|
1363
|
+
class ToTaskParams(BaseModel):
|
|
1364
|
+
"""Parameters for ToTask action"""
|
|
1365
|
+
|
|
1366
|
+
name: str = Field(..., description="Action name (no whitespace)")
|
|
1367
|
+
description: Optional[str] = Field(None, description="Action description")
|
|
1368
|
+
project: int = Field(..., description="Project ID")
|
|
1369
|
+
agent: int = Field(..., description="Agent ID")
|
|
1370
|
+
task_filters: Dict[str, Any] = Field(..., description="Task filter criteria")
|
|
1371
|
+
method: str = Field(..., description="Annotation method: 'file' or 'inference'")
|
|
1372
|
+
target_specification_name: Optional[str] = Field(
|
|
1373
|
+
None,
|
|
1374
|
+
description="File specification name (required for file method)"
|
|
1375
|
+
)
|
|
1376
|
+
model: Optional[int] = Field(None, description="Model ID")
|
|
1377
|
+
pre_processor: Optional[int] = Field(
|
|
1378
|
+
None,
|
|
1379
|
+
description="Pre-processor ID (required for inference method)"
|
|
1380
|
+
)
|
|
1381
|
+
pre_processor_params: Dict[str, Any] = Field(
|
|
1382
|
+
default_factory=dict,
|
|
1383
|
+
description="Pre-processor configuration parameters"
|
|
1384
|
+
)
|
|
1385
|
+
|
|
1386
|
+
@validator('name')
|
|
1387
|
+
def validate_name(cls, v):
|
|
1388
|
+
"""Validate name has no whitespace"""
|
|
1389
|
+
if ' ' in v:
|
|
1390
|
+
raise ValueError('Name must not contain whitespace')
|
|
1391
|
+
return v
|
|
1392
|
+
|
|
1393
|
+
@validator('method')
|
|
1394
|
+
def validate_method(cls, v):
|
|
1395
|
+
"""Validate method is supported"""
|
|
1396
|
+
if v not in ['file', 'inference']:
|
|
1397
|
+
raise ValueError('Method must be "file" or "inference"')
|
|
1398
|
+
return v
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
#### ToTaskResult
|
|
1402
|
+
|
|
1403
|
+
```python
|
|
1404
|
+
class ToTaskResult(BaseModel):
|
|
1405
|
+
"""Result from ToTask action"""
|
|
1406
|
+
|
|
1407
|
+
status: JobStatus = Field(..., description="Job status")
|
|
1408
|
+
message: str = Field(..., description="Result message")
|
|
1409
|
+
```
|
|
1410
|
+
|
|
1411
|
+
#### MetricsRecord
|
|
1412
|
+
|
|
1413
|
+
```python
|
|
1414
|
+
class MetricsRecord(BaseModel):
|
|
1415
|
+
"""Metrics tracking record"""
|
|
1416
|
+
|
|
1417
|
+
success: int = Field(0, description="Successfully processed count")
|
|
1418
|
+
failed: int = Field(0, description="Failed processing count")
|
|
1419
|
+
total: int = Field(0, description="Total tasks count")
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
**File:** `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/enums.py`
|
|
1423
|
+
|
|
1424
|
+
#### AnnotationMethod
|
|
1425
|
+
|
|
1426
|
+
```python
|
|
1427
|
+
class AnnotationMethod(str, Enum):
|
|
1428
|
+
"""Supported annotation methods"""
|
|
1429
|
+
|
|
1430
|
+
FILE = 'file'
|
|
1431
|
+
INFERENCE = 'inference'
|
|
1432
|
+
```
|
|
1433
|
+
|
|
1434
|
+
#### LogCode
|
|
1435
|
+
|
|
1436
|
+
```python
|
|
1437
|
+
class LogCode(str, Enum):
|
|
1438
|
+
"""Type-safe logging codes"""
|
|
1439
|
+
|
|
1440
|
+
# Workflow codes
|
|
1441
|
+
TO_TASK_STARTED = 'to_task_started'
|
|
1442
|
+
STEP_COMPLETED = 'step_completed'
|
|
1443
|
+
TO_TASK_COMPLETED = 'to_task_completed'
|
|
1444
|
+
|
|
1445
|
+
# Validation codes
|
|
1446
|
+
PROJECT_VALIDATED = 'project_validated'
|
|
1447
|
+
TASKS_VALIDATED = 'tasks_validated'
|
|
1448
|
+
TARGET_SPEC_VALIDATED = 'target_spec_validated'
|
|
1449
|
+
INVALID_PROJECT_RESPONSE = 'invalid_project_response'
|
|
1450
|
+
NO_DATA_COLLECTION = 'no_data_collection'
|
|
1451
|
+
TARGET_SPEC_NOT_FOUND = 'target_spec_not_found'
|
|
1452
|
+
|
|
1453
|
+
# Processing codes
|
|
1454
|
+
PROCESSING_INITIALIZED = 'processing_initialized'
|
|
1455
|
+
ANNOTATING_DATA = 'annotating_data'
|
|
1456
|
+
ANNOTATION_COMPLETED = 'annotation_completed'
|
|
1457
|
+
TASK_PROCESSING_FAILED = 'task_processing_failed'
|
|
1458
|
+
|
|
1459
|
+
# Inference codes
|
|
1460
|
+
ANNOTATING_INFERENCE_DATA = 'annotating_inference_data'
|
|
1461
|
+
DEPLOYING_PREPROCESSOR = 'deploying_preprocessor'
|
|
1462
|
+
INFERENCE_COMPLETED = 'inference_completed'
|
|
1463
|
+
INFERENCE_PROCESSING_FAILED = 'inference_processing_failed'
|
|
1464
|
+
|
|
1465
|
+
# Rollback codes
|
|
1466
|
+
ROLLBACK_STARTED = 'rollback_started'
|
|
1467
|
+
STEP_ROLLED_BACK = 'step_rolled_back'
|
|
1468
|
+
ROLLBACK_FAILED = 'rollback_failed'
|
|
1469
|
+
ROLLBACK_ACTION_FAILED = 'rollback_action_failed'
|
|
1470
|
+
|
|
1471
|
+
# Metrics codes
|
|
1472
|
+
PROGRESS_UPDATE_FAILED = 'progress_update_failed'
|
|
1473
|
+
METRICS_RECORDING_FAILED = 'metrics_recording_failed'
|
|
1474
|
+
METRICS_UPDATE_FAILED = 'metrics_update_failed'
|
|
1475
|
+
METRICS_FINALIZATION_FAILED = 'metrics_finalization_failed'
|
|
1476
|
+
```
|
|
1477
|
+
|
|
1478
|
+
## Testing
|
|
1479
|
+
|
|
1480
|
+
### Unit Testing Strategies
|
|
1481
|
+
|
|
1482
|
+
```python
|
|
1483
|
+
import pytest
|
|
1484
|
+
from unittest.mock import Mock, patch
|
|
1485
|
+
from synapse_sdk.plugins.categories.pre_annotation.actions.to_task.strategies.validation import ProjectValidationStrategy
|
|
1486
|
+
|
|
1487
|
+
class TestProjectValidationStrategy:
|
|
1488
|
+
"""Test project validation strategy"""
|
|
1489
|
+
|
|
1490
|
+
def test_validate_success(self):
|
|
1491
|
+
"""Test successful validation"""
|
|
1492
|
+
# Setup
|
|
1493
|
+
context = Mock()
|
|
1494
|
+
context.params = {'project': 123}
|
|
1495
|
+
context.client.get_project.return_value = {
|
|
1496
|
+
'id': 123,
|
|
1497
|
+
'data_collection': 456
|
|
1498
|
+
}
|
|
1499
|
+
context.client.get_data_collection.return_value = {
|
|
1500
|
+
'id': 456
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
# Execute
|
|
1504
|
+
strategy = ProjectValidationStrategy()
|
|
1505
|
+
result = strategy.validate(context)
|
|
1506
|
+
|
|
1507
|
+
# Assert
|
|
1508
|
+
assert result['success'] is True
|
|
1509
|
+
assert context.project == {'id': 123, 'data_collection': 456}
|
|
1510
|
+
assert context.data_collection == {'id': 456}
|
|
1511
|
+
|
|
1512
|
+
def test_validate_no_project(self):
|
|
1513
|
+
"""Test validation fails when project not found"""
|
|
1514
|
+
# Setup
|
|
1515
|
+
context = Mock()
|
|
1516
|
+
context.params = {'project': 999}
|
|
1517
|
+
context.client.get_project.return_value = None
|
|
1518
|
+
|
|
1519
|
+
# Execute
|
|
1520
|
+
strategy = ProjectValidationStrategy()
|
|
1521
|
+
result = strategy.validate(context)
|
|
1522
|
+
|
|
1523
|
+
# Assert
|
|
1524
|
+
assert result['success'] is False
|
|
1525
|
+
assert 'not found' in result['error']
|
|
1526
|
+
|
|
1527
|
+
def test_validate_no_data_collection(self):
|
|
1528
|
+
"""Test validation fails when no data collection"""
|
|
1529
|
+
# Setup
|
|
1530
|
+
context = Mock()
|
|
1531
|
+
context.params = {'project': 123}
|
|
1532
|
+
context.client.get_project.return_value = {
|
|
1533
|
+
'id': 123,
|
|
1534
|
+
'data_collection': None
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
# Execute
|
|
1538
|
+
strategy = ProjectValidationStrategy()
|
|
1539
|
+
result = strategy.validate(context)
|
|
1540
|
+
|
|
1541
|
+
# Assert
|
|
1542
|
+
assert result['success'] is False
|
|
1543
|
+
assert 'no data collection' in result['error']
|
|
1544
|
+
```
|
|
1545
|
+
|
|
1546
|
+
### Integration Testing
|
|
1547
|
+
|
|
1548
|
+
```python
|
|
1549
|
+
class TestToTaskIntegration:
|
|
1550
|
+
"""Integration tests for ToTask workflow"""
|
|
1551
|
+
|
|
1552
|
+
@pytest.fixture
|
|
1553
|
+
def mock_context(self):
|
|
1554
|
+
"""Create mock context"""
|
|
1555
|
+
context = Mock(spec=ToTaskContext)
|
|
1556
|
+
context.params = {
|
|
1557
|
+
'project': 123,
|
|
1558
|
+
'agent': 1,
|
|
1559
|
+
'task_filters': {'status': 'pending'},
|
|
1560
|
+
'method': 'file',
|
|
1561
|
+
'target_specification_name': 'annotations'
|
|
1562
|
+
}
|
|
1563
|
+
context.task_ids = [1, 2, 3]
|
|
1564
|
+
return context
|
|
1565
|
+
|
|
1566
|
+
def test_full_workflow_file_method(self, mock_context):
|
|
1567
|
+
"""Test complete workflow with file method"""
|
|
1568
|
+
# Setup orchestrator
|
|
1569
|
+
orchestrator = ToTaskOrchestrator(mock_context)
|
|
1570
|
+
|
|
1571
|
+
# Mock validation responses
|
|
1572
|
+
mock_context.client.get_project.return_value = {
|
|
1573
|
+
'id': 123,
|
|
1574
|
+
'data_collection': 456
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
# Execute workflow
|
|
1578
|
+
result = orchestrator.execute_workflow()
|
|
1579
|
+
|
|
1580
|
+
# Verify all stages completed
|
|
1581
|
+
assert 'project_validation' in orchestrator.steps_completed
|
|
1582
|
+
assert 'task_validation' in orchestrator.steps_completed
|
|
1583
|
+
assert 'finalization' in orchestrator.steps_completed
|
|
1584
|
+
|
|
1585
|
+
# Verify metrics
|
|
1586
|
+
assert result.total == 3
|
|
1587
|
+
```
|
|
1588
|
+
|
|
1589
|
+
## Migration Guide
|
|
1590
|
+
|
|
1591
|
+
### From Legacy Implementation
|
|
1592
|
+
|
|
1593
|
+
If migrating from a legacy ToTask implementation:
|
|
1594
|
+
|
|
1595
|
+
**Key Changes:**
|
|
1596
|
+
|
|
1597
|
+
1. **Strategy-based architecture** - Replace monolithic logic with strategies
|
|
1598
|
+
2. **Orchestrated workflow** - 7 defined stages instead of ad-hoc execution
|
|
1599
|
+
3. **Automatic rollback** - Built-in error recovery
|
|
1600
|
+
4. **Type-safe logging** - LogCode enum instead of string messages
|
|
1601
|
+
|
|
1602
|
+
**Migration Steps:**
|
|
1603
|
+
|
|
1604
|
+
1. Update parameter validation to use `ToTaskParams` model
|
|
1605
|
+
2. Replace custom validation logic with validation strategies
|
|
1606
|
+
3. Migrate annotation logic to annotation strategies
|
|
1607
|
+
4. Update error handling to use orchestrator rollback
|
|
1608
|
+
5. Replace logging with LogCode-based messages
|
|
1609
|
+
|
|
1610
|
+
## Best Practices
|
|
1611
|
+
|
|
1612
|
+
### Performance
|
|
1613
|
+
|
|
1614
|
+
1. **Use appropriate batch sizes** for inference
|
|
1615
|
+
2. **Filter tasks efficiently** to reduce processing overhead
|
|
1616
|
+
3. **Implement caching** in custom strategies when appropriate
|
|
1617
|
+
4. **Monitor memory usage** for large task sets
|
|
1618
|
+
|
|
1619
|
+
### Reliability
|
|
1620
|
+
|
|
1621
|
+
1. **Validate all inputs** before processing
|
|
1622
|
+
2. **Implement proper error handling** in custom strategies
|
|
1623
|
+
3. **Use rollback actions** for cleanup operations
|
|
1624
|
+
4. **Log detailed error messages** for debugging
|
|
1625
|
+
|
|
1626
|
+
### Maintainability
|
|
1627
|
+
|
|
1628
|
+
1. **Follow strategy pattern** for new functionality
|
|
1629
|
+
2. **Use type hints** consistently
|
|
1630
|
+
3. **Document custom strategies** thoroughly
|
|
1631
|
+
4. **Write comprehensive tests** for all strategies
|
|
1632
|
+
|
|
1633
|
+
## Related Documentation
|
|
1634
|
+
|
|
1635
|
+
- [ToTask Overview](./to-task-overview.md) - User guide
|
|
1636
|
+
- [Pre-annotation Plugin Overview](./pre-annotation-plugin-overview.md) - Category overview
|
|
1637
|
+
- Plugin Development Guide - General plugin development
|
|
1638
|
+
- Strategy Pattern - Design pattern details
|
|
1639
|
+
|
|
1640
|
+
## Source Code References
|
|
1641
|
+
|
|
1642
|
+
- Action: `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/action.py`
|
|
1643
|
+
- Orchestrator: `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/orchestrator.py`
|
|
1644
|
+
- Strategies: `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/strategies/`
|
|
1645
|
+
- Models: `synapse_sdk/plugins/categories/pre_annotation/actions/to_task/models.py`
|