flowyml 1.2.0__py3-none-any.whl → 1.3.0__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.
Files changed (91) hide show
  1. flowyml/__init__.py +3 -0
  2. flowyml/assets/base.py +10 -0
  3. flowyml/assets/metrics.py +6 -0
  4. flowyml/cli/main.py +108 -2
  5. flowyml/cli/run.py +9 -2
  6. flowyml/core/execution_status.py +52 -0
  7. flowyml/core/hooks.py +106 -0
  8. flowyml/core/observability.py +210 -0
  9. flowyml/core/orchestrator.py +274 -0
  10. flowyml/core/pipeline.py +193 -231
  11. flowyml/core/project.py +34 -2
  12. flowyml/core/remote_orchestrator.py +109 -0
  13. flowyml/core/resources.py +22 -5
  14. flowyml/core/retry_policy.py +80 -0
  15. flowyml/core/step.py +18 -1
  16. flowyml/core/submission_result.py +53 -0
  17. flowyml/integrations/keras.py +95 -22
  18. flowyml/monitoring/alerts.py +2 -2
  19. flowyml/stacks/__init__.py +15 -0
  20. flowyml/stacks/aws.py +599 -0
  21. flowyml/stacks/azure.py +295 -0
  22. flowyml/stacks/components.py +24 -2
  23. flowyml/stacks/gcp.py +158 -11
  24. flowyml/stacks/local.py +5 -0
  25. flowyml/storage/artifacts.py +15 -5
  26. flowyml/storage/materializers/__init__.py +2 -0
  27. flowyml/storage/materializers/cloudpickle.py +74 -0
  28. flowyml/storage/metadata.py +166 -5
  29. flowyml/ui/backend/main.py +41 -1
  30. flowyml/ui/backend/routers/assets.py +356 -15
  31. flowyml/ui/backend/routers/client.py +46 -0
  32. flowyml/ui/backend/routers/execution.py +13 -2
  33. flowyml/ui/backend/routers/experiments.py +48 -12
  34. flowyml/ui/backend/routers/metrics.py +213 -0
  35. flowyml/ui/backend/routers/pipelines.py +63 -7
  36. flowyml/ui/backend/routers/projects.py +33 -7
  37. flowyml/ui/backend/routers/runs.py +150 -8
  38. flowyml/ui/frontend/dist/assets/index-DcYwrn2j.css +1 -0
  39. flowyml/ui/frontend/dist/assets/index-Dlz_ygOL.js +592 -0
  40. flowyml/ui/frontend/dist/index.html +2 -2
  41. flowyml/ui/frontend/src/App.jsx +4 -1
  42. flowyml/ui/frontend/src/app/assets/page.jsx +260 -230
  43. flowyml/ui/frontend/src/app/dashboard/page.jsx +38 -7
  44. flowyml/ui/frontend/src/app/experiments/page.jsx +61 -314
  45. flowyml/ui/frontend/src/app/observability/page.jsx +277 -0
  46. flowyml/ui/frontend/src/app/pipelines/page.jsx +79 -402
  47. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectArtifactsList.jsx +151 -0
  48. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectExperimentsList.jsx +145 -0
  49. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectHeader.jsx +45 -0
  50. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectHierarchy.jsx +467 -0
  51. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectMetricsPanel.jsx +253 -0
  52. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectPipelinesList.jsx +105 -0
  53. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectRelations.jsx +189 -0
  54. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectRunsList.jsx +136 -0
  55. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectTabs.jsx +95 -0
  56. flowyml/ui/frontend/src/app/projects/[projectId]/page.jsx +326 -0
  57. flowyml/ui/frontend/src/app/projects/page.jsx +13 -3
  58. flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +79 -10
  59. flowyml/ui/frontend/src/app/runs/page.jsx +82 -424
  60. flowyml/ui/frontend/src/app/settings/page.jsx +1 -0
  61. flowyml/ui/frontend/src/app/tokens/page.jsx +62 -16
  62. flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +373 -0
  63. flowyml/ui/frontend/src/components/AssetLineageGraph.jsx +291 -0
  64. flowyml/ui/frontend/src/components/AssetStatsDashboard.jsx +302 -0
  65. flowyml/ui/frontend/src/components/AssetTreeHierarchy.jsx +477 -0
  66. flowyml/ui/frontend/src/components/ExperimentDetailsPanel.jsx +227 -0
  67. flowyml/ui/frontend/src/components/NavigationTree.jsx +401 -0
  68. flowyml/ui/frontend/src/components/PipelineDetailsPanel.jsx +239 -0
  69. flowyml/ui/frontend/src/components/PipelineGraph.jsx +67 -3
  70. flowyml/ui/frontend/src/components/ProjectSelector.jsx +115 -0
  71. flowyml/ui/frontend/src/components/RunDetailsPanel.jsx +298 -0
  72. flowyml/ui/frontend/src/components/header/Header.jsx +48 -1
  73. flowyml/ui/frontend/src/components/plugins/ZenMLIntegration.jsx +106 -0
  74. flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +52 -26
  75. flowyml/ui/frontend/src/components/ui/DataView.jsx +35 -17
  76. flowyml/ui/frontend/src/components/ui/ErrorBoundary.jsx +118 -0
  77. flowyml/ui/frontend/src/contexts/ProjectContext.jsx +2 -2
  78. flowyml/ui/frontend/src/contexts/ToastContext.jsx +116 -0
  79. flowyml/ui/frontend/src/layouts/MainLayout.jsx +5 -1
  80. flowyml/ui/frontend/src/router/index.jsx +4 -0
  81. flowyml/ui/frontend/src/utils/date.js +10 -0
  82. flowyml/ui/frontend/src/utils/downloads.js +11 -0
  83. flowyml/utils/config.py +6 -0
  84. flowyml/utils/stack_config.py +45 -3
  85. {flowyml-1.2.0.dist-info → flowyml-1.3.0.dist-info}/METADATA +42 -4
  86. {flowyml-1.2.0.dist-info → flowyml-1.3.0.dist-info}/RECORD +89 -52
  87. {flowyml-1.2.0.dist-info → flowyml-1.3.0.dist-info}/licenses/LICENSE +1 -1
  88. flowyml/ui/frontend/dist/assets/index-DFNQnrUj.js +0 -448
  89. flowyml/ui/frontend/dist/assets/index-pWI271rZ.css +0 -1
  90. {flowyml-1.2.0.dist-info → flowyml-1.3.0.dist-info}/WHEEL +0 -0
  91. {flowyml-1.2.0.dist-info → flowyml-1.3.0.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,8 @@
1
1
  import React, { useEffect, useState, useMemo } from 'react';
2
2
  import { fetchApi } from '../../../utils/api';
3
+ import { downloadArtifactById } from '../../../utils/downloads';
3
4
  import { useParams, Link } from 'react-router-dom';
4
- import { CheckCircle, XCircle, Clock, Calendar, Package, ArrowRight, BarChart2, FileText, Database, Box, ChevronRight, Activity, Layers, Code2, Terminal, Info, X, Maximize2, TrendingUp, Download, ArrowDownCircle, ArrowUpCircle, Tag, Zap, AlertCircle, FolderPlus } from 'lucide-react';
5
+ import { CheckCircle, XCircle, Clock, Calendar, Package, ArrowRight, BarChart2, FileText, Database, Box, ChevronRight, Activity, Layers, Code2, Terminal, Info, X, Maximize2, TrendingUp, Download, ArrowDownCircle, ArrowUpCircle, Tag, Zap, AlertCircle, FolderPlus, Cloud, Server } from 'lucide-react';
5
6
  import { Card } from '../../../components/ui/Card';
6
7
  import { Badge } from '../../../components/ui/Badge';
7
8
  import { Button } from '../../../components/ui/Button';
@@ -19,6 +20,43 @@ export function RunDetails() {
19
20
  const [selectedStep, setSelectedStep] = useState(null);
20
21
  const [activeTab, setActiveTab] = useState('overview');
21
22
  const [selectedArtifact, setSelectedArtifact] = useState(null);
23
+ const [cloudStatus, setCloudStatus] = useState(null);
24
+ const [isPolling, setIsPolling] = useState(false);
25
+
26
+ // Fetch cloud status for remote runs
27
+ useEffect(() => {
28
+ if (!run) return;
29
+
30
+ const fetchCloudStatus = async () => {
31
+ try {
32
+ const res = await fetchApi(`/api/runs/${runId}/cloud-status`);
33
+ const data = await res.json();
34
+ setCloudStatus(data);
35
+
36
+ // Continue polling if run is remote and not finished
37
+ if (data.is_remote && data.cloud_status && !data.cloud_status.is_finished) {
38
+ setIsPolling(true);
39
+ } else {
40
+ setIsPolling(false);
41
+ }
42
+ } catch (error) {
43
+ console.error('Failed to fetch cloud status:', error);
44
+ setIsPolling(false);
45
+ }
46
+ };
47
+
48
+ fetchCloudStatus();
49
+
50
+ // Poll every 5 seconds if remote and not finished
51
+ let interval;
52
+ if (isPolling) {
53
+ interval = setInterval(fetchCloudStatus, 5000);
54
+ }
55
+
56
+ return () => {
57
+ if (interval) clearInterval(interval);
58
+ };
59
+ }, [runId, run, isPolling]);
22
60
 
23
61
  useEffect(() => {
24
62
  const fetchData = async () => {
@@ -94,13 +132,25 @@ export function RunDetails() {
94
132
  <p className="text-slate-500 mt-1 flex items-center gap-2">
95
133
  <Layers size={16} />
96
134
  Pipeline: <span className="font-medium text-slate-700">{run.pipeline_name}</span>
135
+ {cloudStatus?.is_remote && (
136
+ <Badge variant="secondary" className="text-xs bg-blue-50 text-blue-600 flex items-center gap-1">
137
+ <Cloud size={12} />
138
+ {cloudStatus.orchestrator_type}
139
+ </Badge>
140
+ )}
141
+ {cloudStatus?.is_remote && isPolling && (
142
+ <span className="text-xs text-amber-600 flex items-center gap-1 animate-pulse">
143
+ <Activity size={12} />
144
+ Live
145
+ </span>
146
+ )}
97
147
  </p>
98
148
  </div>
99
149
  <div className="flex flex-col items-end gap-2">
100
150
  <div className="flex items-center gap-2">
101
151
  <SimpleProjectSelector runId={run.run_id} currentProject={run.project} />
102
152
  <Badge variant={statusVariant} className="text-sm px-4 py-1.5 uppercase tracking-wide shadow-sm">
103
- {run.status}
153
+ {cloudStatus?.cloud_status?.status || run.status}
104
154
  </Badge>
105
155
  </div>
106
156
  <span className="text-xs text-slate-400 font-mono">ID: {run.run_id}</span>
@@ -480,6 +530,16 @@ function ArtifactsTab({ artifacts, onArtifactClick }) {
480
530
  <p className="text-xs text-slate-500 truncate">{art.type}</p>
481
531
  </div>
482
532
  <div className="flex items-center gap-2">
533
+ <button
534
+ className="p-2 rounded-lg hover:bg-white transition-colors text-slate-400 hover:text-primary-600 disabled:opacity-40"
535
+ onClick={(e) => {
536
+ e.stopPropagation();
537
+ downloadArtifactById(art.artifact_id);
538
+ }}
539
+ disabled={!art.artifact_id}
540
+ >
541
+ <Download size={14} />
542
+ </button>
483
543
  <Maximize2 size={14} className="text-slate-300 group-hover:text-primary-400 transition-colors" />
484
544
  <ArrowRight size={14} className="text-slate-300 group-hover:text-primary-400 opacity-0 group-hover:opacity-100 transition-all" />
485
545
  </div>
@@ -526,12 +586,21 @@ function ArtifactModal({ artifact, onClose }) {
526
586
  <p className="text-sm text-slate-500">{artifact.type}</p>
527
587
  </div>
528
588
  </div>
529
- <button
530
- onClick={onClose}
531
- className="p-2 hover:bg-white rounded-lg transition-colors"
532
- >
533
- <X size={20} className="text-slate-400" />
534
- </button>
589
+ <div className="flex items-center gap-2">
590
+ <button
591
+ onClick={() => downloadArtifactById(artifact.artifact_id)}
592
+ className="inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-primary-600 text-white text-sm font-semibold hover:bg-primary-500 transition-colors disabled:opacity-50"
593
+ disabled={!artifact.artifact_id}
594
+ >
595
+ <Download size={16} /> Download
596
+ </button>
597
+ <button
598
+ onClick={onClose}
599
+ className="p-2 hover:bg-white rounded-lg transition-colors"
600
+ >
601
+ <X size={20} className="text-slate-400" />
602
+ </button>
603
+ </div>
535
604
  </div>
536
605
 
537
606
  {/* Content */}
@@ -663,8 +732,8 @@ function SimpleProjectSelector({ runId, currentProject }) {
663
732
  onClick={() => handleSelectProject(p.name)}
664
733
  disabled={updating}
665
734
  className={`w-full text-left px-3 py-2 text-sm rounded-lg transition-colors ${p.name === currentProject
666
- ? 'bg-primary-50 dark:bg-primary-900/20 text-primary-700 dark:text-primary-300 font-medium'
667
- : 'text-slate-700 dark:text-slate-200 hover:bg-slate-50 dark:hover:bg-slate-700'
735
+ ? 'bg-primary-50 dark:bg-primary-900/20 text-primary-700 dark:text-primary-300 font-medium'
736
+ : 'text-slate-700 dark:text-slate-200 hover:bg-slate-50 dark:hover:bg-slate-700'
668
737
  }`}
669
738
  >
670
739
  {p.name} {p.name === currentProject && '✓'}