flowyml 1.2.0__tar.gz → 1.4.0__tar.gz
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.
- {flowyml-1.2.0 → flowyml-1.4.0}/LICENSE +1 -1
- {flowyml-1.2.0 → flowyml-1.4.0}/PKG-INFO +44 -4
- {flowyml-1.2.0 → flowyml-1.4.0}/README.md +37 -3
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/__init__.py +3 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/base.py +10 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/metrics.py +6 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/cli/main.py +108 -2
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/cli/run.py +9 -2
- flowyml-1.4.0/flowyml/core/execution_status.py +52 -0
- flowyml-1.4.0/flowyml/core/hooks.py +106 -0
- flowyml-1.4.0/flowyml/core/observability.py +210 -0
- flowyml-1.4.0/flowyml/core/orchestrator.py +274 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/pipeline.py +193 -231
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/project.py +34 -2
- flowyml-1.4.0/flowyml/core/remote_orchestrator.py +109 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/resources.py +34 -17
- flowyml-1.4.0/flowyml/core/retry_policy.py +80 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/scheduler.py +9 -9
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/scheduler_config.py +2 -3
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/step.py +18 -1
- flowyml-1.4.0/flowyml/core/submission_result.py +53 -0
- flowyml-1.4.0/flowyml/integrations/keras.py +207 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/monitoring/alerts.py +2 -2
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/__init__.py +15 -0
- flowyml-1.4.0/flowyml/stacks/aws.py +599 -0
- flowyml-1.4.0/flowyml/stacks/azure.py +295 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/bridge.py +9 -9
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/components.py +24 -2
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/gcp.py +158 -11
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/local.py +5 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/plugins.py +2 -2
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/registry.py +21 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/artifacts.py +15 -5
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/__init__.py +2 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/base.py +33 -0
- flowyml-1.4.0/flowyml/storage/materializers/cloudpickle.py +74 -0
- flowyml-1.4.0/flowyml/storage/metadata.py +53 -0
- flowyml-1.4.0/flowyml/storage/remote.py +590 -0
- flowyml-1.4.0/flowyml/storage/sql.py +911 -0
- flowyml-1.4.0/flowyml/ui/backend/dependencies.py +28 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/main.py +43 -80
- flowyml-1.4.0/flowyml/ui/backend/routers/assets.py +511 -0
- flowyml-1.4.0/flowyml/ui/backend/routers/client.py +46 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/execution.py +13 -2
- flowyml-1.4.0/flowyml/ui/backend/routers/experiments.py +132 -0
- flowyml-1.4.0/flowyml/ui/backend/routers/metrics.py +168 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/pipelines.py +77 -12
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/projects.py +33 -7
- flowyml-1.4.0/flowyml/ui/backend/routers/runs.py +275 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/schedules.py +5 -21
- flowyml-1.4.0/flowyml/ui/backend/routers/stats.py +14 -0
- flowyml-1.4.0/flowyml/ui/backend/routers/traces.py +68 -0
- flowyml-1.4.0/flowyml/ui/frontend/dist/assets/index-DcYwrn2j.css +1 -0
- flowyml-1.4.0/flowyml/ui/frontend/dist/assets/index-Dlz_ygOL.js +592 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/dist/index.html +2 -2
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/App.jsx +4 -1
- flowyml-1.4.0/flowyml/ui/frontend/src/app/assets/page.jsx +427 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/dashboard/page.jsx +38 -7
- flowyml-1.4.0/flowyml/ui/frontend/src/app/experiments/page.jsx +107 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/observability/page.jsx +277 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/pipelines/page.jsx +131 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectArtifactsList.jsx +151 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectExperimentsList.jsx +145 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectHeader.jsx +45 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectHierarchy.jsx +467 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectMetricsPanel.jsx +253 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectPipelinesList.jsx +105 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectRelations.jsx +189 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectRunsList.jsx +136 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectTabs.jsx +95 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/app/projects/[projectId]/page.jsx +326 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/projects/page.jsx +13 -3
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +79 -10
- flowyml-1.4.0/flowyml/ui/frontend/src/app/runs/page.jsx +128 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/settings/page.jsx +1 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/tokens/page.jsx +62 -16
- flowyml-1.4.0/flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +373 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/components/AssetLineageGraph.jsx +291 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/components/AssetStatsDashboard.jsx +302 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/components/AssetTreeHierarchy.jsx +477 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/components/ExperimentDetailsPanel.jsx +227 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/components/NavigationTree.jsx +401 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/components/PipelineDetailsPanel.jsx +239 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/PipelineGraph.jsx +67 -3
- flowyml-1.4.0/flowyml/ui/frontend/src/components/ProjectSelector.jsx +115 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/components/RunDetailsPanel.jsx +298 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/header/Header.jsx +48 -1
- flowyml-1.4.0/flowyml/ui/frontend/src/components/plugins/ZenMLIntegration.jsx +106 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +52 -26
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/DataView.jsx +35 -17
- flowyml-1.4.0/flowyml/ui/frontend/src/components/ui/ErrorBoundary.jsx +118 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/contexts/ProjectContext.jsx +2 -2
- flowyml-1.4.0/flowyml/ui/frontend/src/contexts/ToastContext.jsx +116 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/layouts/MainLayout.jsx +5 -1
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/router/index.jsx +4 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/utils/date.js +10 -0
- flowyml-1.4.0/flowyml/ui/frontend/src/utils/downloads.js +11 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/config.py +6 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/stack_config.py +45 -3
- {flowyml-1.2.0 → flowyml-1.4.0}/pyproject.toml +11 -1
- flowyml-1.2.0/flowyml/integrations/keras.py +0 -134
- flowyml-1.2.0/flowyml/storage/metadata.py +0 -931
- flowyml-1.2.0/flowyml/ui/backend/routers/assets.py +0 -45
- flowyml-1.2.0/flowyml/ui/backend/routers/experiments.py +0 -49
- flowyml-1.2.0/flowyml/ui/backend/routers/runs.py +0 -66
- flowyml-1.2.0/flowyml/ui/backend/routers/traces.py +0 -84
- flowyml-1.2.0/flowyml/ui/frontend/dist/assets/index-DFNQnrUj.js +0 -448
- flowyml-1.2.0/flowyml/ui/frontend/dist/assets/index-pWI271rZ.css +0 -1
- flowyml-1.2.0/flowyml/ui/frontend/src/app/assets/page.jsx +0 -397
- flowyml-1.2.0/flowyml/ui/frontend/src/app/experiments/page.jsx +0 -360
- flowyml-1.2.0/flowyml/ui/frontend/src/app/pipelines/page.jsx +0 -454
- flowyml-1.2.0/flowyml/ui/frontend/src/app/runs/page.jsx +0 -470
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/artifact.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/dataset.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/featureset.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/model.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/registry.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/assets/report.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/cli/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/cli/experiment.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/cli/init.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/cli/stack_cli.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/cli/ui.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/advanced_cache.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/approval.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/cache.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/checkpoint.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/conditional.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/context.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/error_handling.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/executor.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/graph.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/parallel.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/step_grouping.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/templates.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/core/versioning.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/integrations/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/monitoring/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/monitoring/data.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/monitoring/llm.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/monitoring/monitor.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/monitoring/notifications.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/registry/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/registry/model_registry.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/registry/pipeline_registry.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/base.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/migration.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/stacks/plugin_config.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/keras.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/numpy.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/pandas.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/pytorch.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/sklearn.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/storage/materializers/tensorflow.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/tracking/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/tracking/experiment.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/tracking/leaderboard.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/tracking/runs.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/Dockerfile +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/auth.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/leaderboard.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/notifications.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/backend/routers/plugins.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/Dockerfile +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/README.md +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/index.html +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/nginx.conf +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/package-lock.json +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/package.json +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/postcss.config.js +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/experiments/[experimentId]/page.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/leaderboard/page.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/plugins/page.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/schedules/page.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/app/traces/page.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/Layout.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/plugins/AddPluginDialog.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/plugins/InstalledPlugins.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/plugins/PluginBrowser.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/Badge.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/Button.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/Card.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/CodeSnippet.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/CollapsibleCard.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/EmptyState.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/ExecutionStatus.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/KeyValue.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/components/ui/ProjectSelector.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/contexts/ThemeContext.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/index.css +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/main.jsx +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/services/pluginService.js +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/utils/api.js +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/src/utils/cn.js +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/tailwind.config.js +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/frontend/vite.config.js +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/ui/utils.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/__init__.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/debug.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/environment.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/git.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/logging.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/performance.py +0 -0
- {flowyml-1.2.0 → flowyml-1.4.0}/flowyml/utils/validation.py +0 -0
|
@@ -2,7 +2,7 @@ Apache License
|
|
|
2
2
|
Version 2.0, January 2004
|
|
3
3
|
http://www.apache.org/licenses/
|
|
4
4
|
|
|
5
|
-
Copyright 2024
|
|
5
|
+
Copyright 2024 UnicoLab.ai
|
|
6
6
|
|
|
7
7
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
8
|
you may not use this file except in compliance with the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowyml
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Next-Generation ML Pipeline Framework
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -19,24 +19,30 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
20
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
21
|
Provides-Extra: all
|
|
22
|
+
Provides-Extra: aws
|
|
23
|
+
Provides-Extra: azure
|
|
22
24
|
Provides-Extra: gcp
|
|
23
25
|
Provides-Extra: pytorch
|
|
24
26
|
Provides-Extra: sklearn
|
|
25
27
|
Provides-Extra: tensorflow
|
|
26
28
|
Provides-Extra: ui
|
|
27
29
|
Requires-Dist: click (>=8.0.0)
|
|
30
|
+
Requires-Dist: cloudpickle (>=2.0.0)
|
|
28
31
|
Requires-Dist: croniter (>=2.0.1,<3.0.0)
|
|
29
32
|
Requires-Dist: fastapi (>=0.122.0,<0.123.0) ; extra == "ui"
|
|
30
33
|
Requires-Dist: google-cloud-aiplatform (>=1.35.0) ; extra == "gcp" or extra == "all"
|
|
31
34
|
Requires-Dist: google-cloud-storage (>=2.10.0) ; extra == "gcp" or extra == "all"
|
|
35
|
+
Requires-Dist: httpx (>=0.24,<0.28)
|
|
32
36
|
Requires-Dist: loguru (>=0.7.3,<0.8.0)
|
|
33
37
|
Requires-Dist: numpy (>=1.20.0)
|
|
34
38
|
Requires-Dist: pandas (>=1.3.0)
|
|
39
|
+
Requires-Dist: psycopg2-binary (>=2.9.0)
|
|
35
40
|
Requires-Dist: pydantic (>=2.0.0)
|
|
36
41
|
Requires-Dist: python-multipart (>=0.0.6) ; extra == "ui" or extra == "all"
|
|
37
42
|
Requires-Dist: pytz (>=2024.1,<2025.0)
|
|
38
43
|
Requires-Dist: pyyaml (>=6.0)
|
|
39
44
|
Requires-Dist: scikit-learn (>=1.0.0) ; extra == "sklearn" or extra == "all"
|
|
45
|
+
Requires-Dist: sqlalchemy (>=2.0.0)
|
|
40
46
|
Requires-Dist: tensorflow (>=2.12.0) ; extra == "tensorflow" or extra == "all"
|
|
41
47
|
Requires-Dist: toml (>=0.10.2)
|
|
42
48
|
Requires-Dist: torch (>=2.0.0) ; extra == "pytorch" or extra == "all"
|
|
@@ -304,7 +310,41 @@ Ready for the enterprise. Run locally per project or deploy as a centralized ent
|
|
|
304
310
|
- **Cloud Providers**: AWS, GCP, Azure (via plugins).
|
|
305
311
|
- **Tools**: MLflow, Weights & Biases, Great Expectations.
|
|
306
312
|
|
|
307
|
-
### 20.
|
|
313
|
+
### 20. 📊 Automatic Training History Tracking
|
|
314
|
+
FlowyML automatically captures and visualizes your model training progress with zero manual intervention.
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
from flowyml.integrations.keras import FlowymlKerasCallback
|
|
318
|
+
|
|
319
|
+
# Just add the callback - that's it!
|
|
320
|
+
callback = FlowymlKerasCallback(
|
|
321
|
+
experiment_name="my-experiment",
|
|
322
|
+
project="my-project"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
model.fit(
|
|
326
|
+
x_train, y_train,
|
|
327
|
+
validation_data=(x_val, y_val),
|
|
328
|
+
epochs=50,
|
|
329
|
+
callbacks=[callback] # Auto-tracks all metrics!
|
|
330
|
+
)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**What gets captured automatically:**
|
|
334
|
+
- ✅ Loss (train & validation) per epoch
|
|
335
|
+
- ✅ Accuracy (train & validation) per epoch
|
|
336
|
+
- ✅ All custom metrics (F1, precision, recall, etc.)
|
|
337
|
+
- ✅ Model architecture and parameters
|
|
338
|
+
- ✅ Interactive charts in the UI
|
|
339
|
+
|
|
340
|
+
**View beautiful training graphs in the UI:**
|
|
341
|
+
1. Navigate to your project's Structure tab
|
|
342
|
+
2. Click on any model artifact
|
|
343
|
+
3. See interactive loss/accuracy charts over epochs!
|
|
344
|
+
|
|
345
|
+
No external tools needed - all visualization built into FlowyML.
|
|
346
|
+
|
|
347
|
+
### 21. 📂 Project-Based Organization
|
|
308
348
|
Built-in multi-tenancy for managing multiple teams and initiatives.
|
|
309
349
|
|
|
310
350
|
```python
|
|
@@ -318,7 +358,7 @@ runs = project.list_runs()
|
|
|
318
358
|
stats = project.get_stats()
|
|
319
359
|
```
|
|
320
360
|
|
|
321
|
-
###
|
|
361
|
+
### 22. 📝 Pipeline Templates
|
|
322
362
|
Stop reinventing the wheel. Use pre-built templates for common ML patterns.
|
|
323
363
|
|
|
324
364
|
```python
|
|
@@ -382,7 +422,7 @@ print(f"Run ID: {result.run_id}")
|
|
|
382
422
|
print(f"Model Score: {result.outputs['model'].score}")
|
|
383
423
|
```
|
|
384
424
|
|
|
385
|
-
###
|
|
425
|
+
### 23. 🌐 Pipeline Versioning
|
|
386
426
|
Git-like versioning for pipelines. Track changes, compare, rollback.
|
|
387
427
|
|
|
388
428
|
```python
|
|
@@ -255,7 +255,41 @@ Ready for the enterprise. Run locally per project or deploy as a centralized ent
|
|
|
255
255
|
- **Cloud Providers**: AWS, GCP, Azure (via plugins).
|
|
256
256
|
- **Tools**: MLflow, Weights & Biases, Great Expectations.
|
|
257
257
|
|
|
258
|
-
### 20.
|
|
258
|
+
### 20. 📊 Automatic Training History Tracking
|
|
259
|
+
FlowyML automatically captures and visualizes your model training progress with zero manual intervention.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from flowyml.integrations.keras import FlowymlKerasCallback
|
|
263
|
+
|
|
264
|
+
# Just add the callback - that's it!
|
|
265
|
+
callback = FlowymlKerasCallback(
|
|
266
|
+
experiment_name="my-experiment",
|
|
267
|
+
project="my-project"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
model.fit(
|
|
271
|
+
x_train, y_train,
|
|
272
|
+
validation_data=(x_val, y_val),
|
|
273
|
+
epochs=50,
|
|
274
|
+
callbacks=[callback] # Auto-tracks all metrics!
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**What gets captured automatically:**
|
|
279
|
+
- ✅ Loss (train & validation) per epoch
|
|
280
|
+
- ✅ Accuracy (train & validation) per epoch
|
|
281
|
+
- ✅ All custom metrics (F1, precision, recall, etc.)
|
|
282
|
+
- ✅ Model architecture and parameters
|
|
283
|
+
- ✅ Interactive charts in the UI
|
|
284
|
+
|
|
285
|
+
**View beautiful training graphs in the UI:**
|
|
286
|
+
1. Navigate to your project's Structure tab
|
|
287
|
+
2. Click on any model artifact
|
|
288
|
+
3. See interactive loss/accuracy charts over epochs!
|
|
289
|
+
|
|
290
|
+
No external tools needed - all visualization built into FlowyML.
|
|
291
|
+
|
|
292
|
+
### 21. 📂 Project-Based Organization
|
|
259
293
|
Built-in multi-tenancy for managing multiple teams and initiatives.
|
|
260
294
|
|
|
261
295
|
```python
|
|
@@ -269,7 +303,7 @@ runs = project.list_runs()
|
|
|
269
303
|
stats = project.get_stats()
|
|
270
304
|
```
|
|
271
305
|
|
|
272
|
-
###
|
|
306
|
+
### 22. 📝 Pipeline Templates
|
|
273
307
|
Stop reinventing the wheel. Use pre-built templates for common ML patterns.
|
|
274
308
|
|
|
275
309
|
```python
|
|
@@ -333,7 +367,7 @@ print(f"Run ID: {result.run_id}")
|
|
|
333
367
|
print(f"Model Score: {result.outputs['model'].score}")
|
|
334
368
|
```
|
|
335
369
|
|
|
336
|
-
###
|
|
370
|
+
### 23. 🌐 Pipeline Versioning
|
|
337
371
|
Git-like versioning for pipelines. Track changes, compare, rollback.
|
|
338
372
|
|
|
339
373
|
```python
|
|
@@ -38,6 +38,7 @@ from flowyml.assets.registry import AssetRegistry
|
|
|
38
38
|
# Stack imports
|
|
39
39
|
from flowyml.stacks.base import Stack
|
|
40
40
|
from flowyml.stacks.local import LocalStack
|
|
41
|
+
from flowyml.stacks.components import ResourceConfig, DockerConfig
|
|
41
42
|
|
|
42
43
|
# Tracking imports
|
|
43
44
|
from flowyml.tracking.experiment import Experiment
|
|
@@ -140,6 +141,8 @@ __all__ = [
|
|
|
140
141
|
# Stacks
|
|
141
142
|
"Stack",
|
|
142
143
|
"LocalStack",
|
|
144
|
+
"ResourceConfig",
|
|
145
|
+
"DockerConfig",
|
|
143
146
|
# Tracking
|
|
144
147
|
"Experiment",
|
|
145
148
|
"Run",
|
|
@@ -77,6 +77,16 @@ class Asset:
|
|
|
77
77
|
if parent:
|
|
78
78
|
parent.children.append(self)
|
|
79
79
|
|
|
80
|
+
@property
|
|
81
|
+
def properties(self) -> dict[str, Any]:
|
|
82
|
+
"""Expose mutable properties stored in metadata."""
|
|
83
|
+
return self.metadata.properties
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def tags(self) -> dict[str, str]:
|
|
87
|
+
"""Expose mutable tags stored in metadata."""
|
|
88
|
+
return self.metadata.tags
|
|
89
|
+
|
|
80
90
|
@classmethod
|
|
81
91
|
def create(
|
|
82
92
|
cls,
|
|
@@ -89,7 +89,10 @@ class Metrics(Asset):
|
|
|
89
89
|
def create(
|
|
90
90
|
cls,
|
|
91
91
|
name: str | None = None,
|
|
92
|
+
version: str | None = None,
|
|
92
93
|
parent: Asset | None = None,
|
|
94
|
+
tags: dict[str, str] | None = None,
|
|
95
|
+
properties: dict[str, Any] | None = None,
|
|
93
96
|
**metrics,
|
|
94
97
|
) -> "Metrics":
|
|
95
98
|
"""Factory method to create metrics.
|
|
@@ -99,6 +102,9 @@ class Metrics(Asset):
|
|
|
99
102
|
"""
|
|
100
103
|
return cls(
|
|
101
104
|
name=name or "metrics",
|
|
105
|
+
version=version,
|
|
102
106
|
data=metrics,
|
|
103
107
|
parent=parent,
|
|
108
|
+
tags=tags,
|
|
109
|
+
properties=properties,
|
|
104
110
|
)
|
|
@@ -48,9 +48,11 @@ def init(name: str, template: str, directory: str) -> None:
|
|
|
48
48
|
@click.option("--stack", default="local", help="Stack to use for execution")
|
|
49
49
|
@click.option("--context", "-c", multiple=True, help="Context parameters (key=value)")
|
|
50
50
|
@click.option("--debug", is_flag=True, help="Enable debug mode")
|
|
51
|
-
|
|
51
|
+
@click.option("--retry", type=int, help="Number of retries for the pipeline")
|
|
52
|
+
def run(pipeline_name: str, stack: str, context: tuple, debug: bool, retry: int | None) -> None:
|
|
52
53
|
"""Run a pipeline."""
|
|
53
54
|
from flowyml.cli.run import run_pipeline
|
|
55
|
+
from flowyml.core.retry_policy import OrchestratorRetryPolicy
|
|
54
56
|
|
|
55
57
|
# Parse context parameters
|
|
56
58
|
ctx_params = {}
|
|
@@ -60,8 +62,13 @@ def run(pipeline_name: str, stack: str, context: tuple, debug: bool) -> None:
|
|
|
60
62
|
|
|
61
63
|
click.echo(f"Running pipeline '{pipeline_name}' on stack '{stack}'...")
|
|
62
64
|
|
|
65
|
+
kwargs = {}
|
|
66
|
+
if retry:
|
|
67
|
+
kwargs["retry_policy"] = OrchestratorRetryPolicy(max_attempts=retry)
|
|
68
|
+
click.echo(f" Retry policy enabled: max_attempts={retry}")
|
|
69
|
+
|
|
63
70
|
try:
|
|
64
|
-
result = run_pipeline(pipeline_name, stack, ctx_params, debug)
|
|
71
|
+
result = run_pipeline(pipeline_name, stack, ctx_params, debug, **kwargs)
|
|
65
72
|
click.echo("✓ Pipeline completed successfully")
|
|
66
73
|
click.echo(f" Run ID: {result.get('run_id', 'N/A')}")
|
|
67
74
|
click.echo(f" Duration: {result.get('duration', 'N/A')}")
|
|
@@ -70,6 +77,105 @@ def run(pipeline_name: str, stack: str, context: tuple, debug: bool) -> None:
|
|
|
70
77
|
raise click.Abort()
|
|
71
78
|
|
|
72
79
|
|
|
80
|
+
@cli.group()
|
|
81
|
+
def schedule() -> None:
|
|
82
|
+
"""Schedule management commands."""
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@schedule.command("create")
|
|
87
|
+
@click.argument("pipeline_name")
|
|
88
|
+
@click.argument("schedule_type", type=click.Choice(["cron", "interval", "daily", "hourly"]))
|
|
89
|
+
@click.argument("value")
|
|
90
|
+
@click.option("--stack", default="local", help="Stack to use for execution")
|
|
91
|
+
def create_schedule(pipeline_name: str, schedule_type: str, value: str, stack: str) -> None:
|
|
92
|
+
"""Create a new schedule for a pipeline.
|
|
93
|
+
|
|
94
|
+
VALUE format depends on SCHEDULE_TYPE:
|
|
95
|
+
- cron: "*/5 * * * *"
|
|
96
|
+
- interval: seconds (e.g. 60)
|
|
97
|
+
- daily: "HH:MM" (e.g. 14:30)
|
|
98
|
+
- hourly: minute (e.g. 30)
|
|
99
|
+
"""
|
|
100
|
+
from flowyml.core.scheduler import PipelineScheduler
|
|
101
|
+
from flowyml.cli.run import run_pipeline
|
|
102
|
+
|
|
103
|
+
# We need a callable for the scheduler.
|
|
104
|
+
# Since CLI is stateless, we wrap the run_pipeline command.
|
|
105
|
+
# Note: In a real distributed system, this would submit to a scheduler service.
|
|
106
|
+
# Here we are just registering it in the local scheduler DB.
|
|
107
|
+
|
|
108
|
+
# For now, we'll just use the scheduler API to register the definition
|
|
109
|
+
scheduler = PipelineScheduler()
|
|
110
|
+
|
|
111
|
+
# Define a wrapper that runs the pipeline via CLI logic
|
|
112
|
+
def job_func():
|
|
113
|
+
run_pipeline(pipeline_name, stack, {}, False)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
if schedule_type == "cron":
|
|
117
|
+
scheduler.schedule_cron(pipeline_name, job_func, value)
|
|
118
|
+
elif schedule_type == "interval":
|
|
119
|
+
scheduler.schedule_interval(pipeline_name, job_func, seconds=int(value))
|
|
120
|
+
elif schedule_type == "daily":
|
|
121
|
+
if ":" in value:
|
|
122
|
+
h, m = map(int, value.split(":"))
|
|
123
|
+
scheduler.schedule_daily(pipeline_name, job_func, hour=h, minute=m)
|
|
124
|
+
else:
|
|
125
|
+
raise ValueError("Daily value must be HH:MM")
|
|
126
|
+
elif schedule_type == "hourly":
|
|
127
|
+
scheduler.schedule_hourly(pipeline_name, job_func, minute=int(value))
|
|
128
|
+
|
|
129
|
+
click.echo(f"✓ Schedule created for '{pipeline_name}' ({schedule_type}={value})")
|
|
130
|
+
click.echo(" Note: Ensure the scheduler service is running to execute this schedule.")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
click.echo(f"✗ Error creating schedule: {e}", err=True)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@schedule.command("list")
|
|
136
|
+
def list_schedules() -> None:
|
|
137
|
+
"""List all active schedules."""
|
|
138
|
+
from flowyml.core.scheduler import PipelineScheduler
|
|
139
|
+
|
|
140
|
+
scheduler = PipelineScheduler()
|
|
141
|
+
jobs = scheduler.get_jobs()
|
|
142
|
+
|
|
143
|
+
if not jobs:
|
|
144
|
+
click.echo("No active schedules found.")
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
click.echo(f"Found {len(jobs)} schedules:\n")
|
|
148
|
+
for job in jobs:
|
|
149
|
+
click.echo(f" {job.id} - {job.name}")
|
|
150
|
+
click.echo(f" Next run: {job.next_run_time}")
|
|
151
|
+
click.echo()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@schedule.command("start")
|
|
155
|
+
def start_scheduler() -> None:
|
|
156
|
+
"""Start the scheduler service (blocking)."""
|
|
157
|
+
from flowyml.core.scheduler import PipelineScheduler
|
|
158
|
+
import time
|
|
159
|
+
|
|
160
|
+
click.echo("🚀 Starting Scheduler Service...")
|
|
161
|
+
scheduler = PipelineScheduler()
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
# In a real app, this would load definitions from DB and register them
|
|
165
|
+
# For now, it just runs the scheduler loop for existing in-memory jobs
|
|
166
|
+
# (which might be empty if we restarted).
|
|
167
|
+
# To make this persistent, we'd need to serialize job definitions to DB.
|
|
168
|
+
# The current Scheduler implementation supports SQLite persistence for job state,
|
|
169
|
+
# but we need to re-register jobs on startup.
|
|
170
|
+
|
|
171
|
+
click.echo(" Scheduler running. Press Ctrl+C to stop.")
|
|
172
|
+
while True:
|
|
173
|
+
scheduler.run_pending()
|
|
174
|
+
time.sleep(1)
|
|
175
|
+
except KeyboardInterrupt:
|
|
176
|
+
click.echo("\n🛑 Scheduler stopped.")
|
|
177
|
+
|
|
178
|
+
|
|
73
179
|
@cli.group()
|
|
74
180
|
def ui() -> None:
|
|
75
181
|
"""UI server commands."""
|
|
@@ -6,7 +6,13 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def run_pipeline(
|
|
9
|
+
def run_pipeline(
|
|
10
|
+
pipeline_name: str,
|
|
11
|
+
stack: str,
|
|
12
|
+
context_params: dict[str, Any],
|
|
13
|
+
debug: bool,
|
|
14
|
+
**kwargs,
|
|
15
|
+
) -> dict[str, Any]:
|
|
10
16
|
"""Run a pipeline by name.
|
|
11
17
|
|
|
12
18
|
Args:
|
|
@@ -14,6 +20,7 @@ def run_pipeline(pipeline_name: str, stack: str, context_params: dict[str, Any],
|
|
|
14
20
|
stack: Stack to use for execution
|
|
15
21
|
context_params: Context parameters to override
|
|
16
22
|
debug: Enable debug mode
|
|
23
|
+
**kwargs: Additional arguments passed to pipeline.run
|
|
17
24
|
|
|
18
25
|
Returns:
|
|
19
26
|
Dictionary with run results
|
|
@@ -65,7 +72,7 @@ def run_pipeline(pipeline_name: str, stack: str, context_params: dict[str, Any],
|
|
|
65
72
|
pipeline.set_stack(stack)
|
|
66
73
|
|
|
67
74
|
# Run pipeline
|
|
68
|
-
result = pipeline.run(debug=debug)
|
|
75
|
+
result = pipeline.run(debug=debug, **kwargs)
|
|
69
76
|
|
|
70
77
|
return {
|
|
71
78
|
"run_id": result.run_id,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Execution status tracking for pipeline runs."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ExecutionStatus(str, Enum):
|
|
7
|
+
"""Status of a pipeline or step execution."""
|
|
8
|
+
|
|
9
|
+
# Pre-execution states
|
|
10
|
+
INITIALIZING = "initializing"
|
|
11
|
+
PROVISIONING = "provisioning"
|
|
12
|
+
|
|
13
|
+
# Active execution states
|
|
14
|
+
RUNNING = "running"
|
|
15
|
+
|
|
16
|
+
# Terminal success states
|
|
17
|
+
COMPLETED = "completed"
|
|
18
|
+
CACHED = "cached"
|
|
19
|
+
|
|
20
|
+
# Terminal failure states
|
|
21
|
+
FAILED = "failed"
|
|
22
|
+
STOPPED = "stopped"
|
|
23
|
+
CANCELLED = "cancelled"
|
|
24
|
+
|
|
25
|
+
# Intermediate states
|
|
26
|
+
STOPPING = "stopping"
|
|
27
|
+
CANCELLING = "cancelling"
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def is_finished(self) -> bool:
|
|
31
|
+
"""Check if execution is in a terminal state."""
|
|
32
|
+
return self in {
|
|
33
|
+
ExecutionStatus.COMPLETED,
|
|
34
|
+
ExecutionStatus.CACHED,
|
|
35
|
+
ExecutionStatus.FAILED,
|
|
36
|
+
ExecutionStatus.STOPPED,
|
|
37
|
+
ExecutionStatus.CANCELLED,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def is_successful(self) -> bool:
|
|
42
|
+
"""Check if execution completed successfully."""
|
|
43
|
+
return self in {ExecutionStatus.COMPLETED, ExecutionStatus.CACHED}
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def is_failed(self) -> bool:
|
|
47
|
+
"""Check if execution failed."""
|
|
48
|
+
return self in {
|
|
49
|
+
ExecutionStatus.FAILED,
|
|
50
|
+
ExecutionStatus.STOPPED,
|
|
51
|
+
ExecutionStatus.CANCELLED,
|
|
52
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Lifecycle hooks for pipelines and steps."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, TYPE_CHECKING
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from flowyml.core.pipeline import Pipeline, PipelineResult
|
|
9
|
+
from flowyml.core.step import Step
|
|
10
|
+
from flowyml.core.executor import ExecutionResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class HookRegistry:
|
|
15
|
+
"""Registry for pipeline and step lifecycle hooks."""
|
|
16
|
+
|
|
17
|
+
# Pipeline-level hooks
|
|
18
|
+
on_pipeline_start: list[Callable[["Pipeline"], None]] = field(default_factory=list)
|
|
19
|
+
on_pipeline_end: list[Callable[["Pipeline", "PipelineResult"], None]] = field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
# Step-level hooks
|
|
22
|
+
on_step_start: list[Callable[["Step", dict[str, Any]], None]] = field(default_factory=list)
|
|
23
|
+
on_step_end: list[Callable[["Step", "ExecutionResult"], None]] = field(default_factory=list)
|
|
24
|
+
|
|
25
|
+
def register_pipeline_start_hook(self, hook: Callable[["Pipeline"], None]) -> None:
|
|
26
|
+
"""Register a hook to run at pipeline start."""
|
|
27
|
+
self.on_pipeline_start.append(hook)
|
|
28
|
+
|
|
29
|
+
def register_pipeline_end_hook(self, hook: Callable[["Pipeline", "PipelineResult"], None]) -> None:
|
|
30
|
+
"""Register a hook to run at pipeline end."""
|
|
31
|
+
self.on_pipeline_end.append(hook)
|
|
32
|
+
|
|
33
|
+
def register_step_start_hook(self, hook: Callable[["Step", dict[str, Any]], None]) -> None:
|
|
34
|
+
"""Register a hook to run before step execution."""
|
|
35
|
+
self.on_step_start.append(hook)
|
|
36
|
+
|
|
37
|
+
def register_step_end_hook(self, hook: Callable[["Step", "ExecutionResult"], None]) -> None:
|
|
38
|
+
"""Register a hook to run after step execution."""
|
|
39
|
+
self.on_step_end.append(hook)
|
|
40
|
+
|
|
41
|
+
def run_pipeline_start_hooks(self, pipeline: "Pipeline") -> None:
|
|
42
|
+
"""Execute all pipeline start hooks."""
|
|
43
|
+
for hook in self.on_pipeline_start:
|
|
44
|
+
try:
|
|
45
|
+
hook(pipeline)
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print(f"Warning: Pipeline start hook failed: {e}")
|
|
48
|
+
|
|
49
|
+
def run_pipeline_end_hooks(self, pipeline: "Pipeline", result: "PipelineResult") -> None:
|
|
50
|
+
"""Execute all pipeline end hooks."""
|
|
51
|
+
for hook in self.on_pipeline_end:
|
|
52
|
+
try:
|
|
53
|
+
hook(pipeline, result)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(f"Warning: Pipeline end hook failed: {e}")
|
|
56
|
+
|
|
57
|
+
def run_step_start_hooks(self, step: "Step", inputs: dict[str, Any]) -> None:
|
|
58
|
+
"""Execute all step start hooks."""
|
|
59
|
+
for hook in self.on_step_start:
|
|
60
|
+
try:
|
|
61
|
+
hook(step, inputs)
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"Warning: Step start hook failed: {e}")
|
|
64
|
+
|
|
65
|
+
def run_step_end_hooks(self, step: "Step", result: "ExecutionResult") -> None:
|
|
66
|
+
"""Execute all step end hooks."""
|
|
67
|
+
for hook in self.on_step_end:
|
|
68
|
+
try:
|
|
69
|
+
hook(step, result)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
print(f"Warning: Step end hook failed: {e}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Global hook registry
|
|
75
|
+
_global_hooks = HookRegistry()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_global_hooks() -> HookRegistry:
|
|
79
|
+
"""Get the global hook registry."""
|
|
80
|
+
return _global_hooks
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def on_pipeline_start(func: Callable[["Pipeline"], None]) -> Callable[["Pipeline"], None]:
|
|
84
|
+
"""Decorator to register a pipeline start hook."""
|
|
85
|
+
_global_hooks.register_pipeline_start_hook(func)
|
|
86
|
+
return func
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def on_pipeline_end(
|
|
90
|
+
func: Callable[["Pipeline", "PipelineResult"], None],
|
|
91
|
+
) -> Callable[["Pipeline", "PipelineResult"], None]:
|
|
92
|
+
"""Decorator to register a pipeline end hook."""
|
|
93
|
+
_global_hooks.register_pipeline_end_hook(func)
|
|
94
|
+
return func
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def on_step_start(func: Callable[["Step", dict[str, Any]], None]) -> Callable[["Step", dict[str, Any]], None]:
|
|
98
|
+
"""Decorator to register a step start hook."""
|
|
99
|
+
_global_hooks.register_step_start_hook(func)
|
|
100
|
+
return func
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def on_step_end(func: Callable[["Step", "ExecutionResult"], None]) -> Callable[["Step", "ExecutionResult"], None]:
|
|
104
|
+
"""Decorator to register a step end hook."""
|
|
105
|
+
_global_hooks.register_step_end_hook(func)
|
|
106
|
+
return func
|