flowyml 1.1.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 (159) hide show
  1. flowyml/__init__.py +207 -0
  2. flowyml/assets/__init__.py +22 -0
  3. flowyml/assets/artifact.py +40 -0
  4. flowyml/assets/base.py +209 -0
  5. flowyml/assets/dataset.py +100 -0
  6. flowyml/assets/featureset.py +301 -0
  7. flowyml/assets/metrics.py +104 -0
  8. flowyml/assets/model.py +82 -0
  9. flowyml/assets/registry.py +157 -0
  10. flowyml/assets/report.py +315 -0
  11. flowyml/cli/__init__.py +5 -0
  12. flowyml/cli/experiment.py +232 -0
  13. flowyml/cli/init.py +256 -0
  14. flowyml/cli/main.py +327 -0
  15. flowyml/cli/run.py +75 -0
  16. flowyml/cli/stack_cli.py +532 -0
  17. flowyml/cli/ui.py +33 -0
  18. flowyml/core/__init__.py +68 -0
  19. flowyml/core/advanced_cache.py +274 -0
  20. flowyml/core/approval.py +64 -0
  21. flowyml/core/cache.py +203 -0
  22. flowyml/core/checkpoint.py +148 -0
  23. flowyml/core/conditional.py +373 -0
  24. flowyml/core/context.py +155 -0
  25. flowyml/core/error_handling.py +419 -0
  26. flowyml/core/executor.py +354 -0
  27. flowyml/core/graph.py +185 -0
  28. flowyml/core/parallel.py +452 -0
  29. flowyml/core/pipeline.py +764 -0
  30. flowyml/core/project.py +253 -0
  31. flowyml/core/resources.py +424 -0
  32. flowyml/core/scheduler.py +630 -0
  33. flowyml/core/scheduler_config.py +32 -0
  34. flowyml/core/step.py +201 -0
  35. flowyml/core/step_grouping.py +292 -0
  36. flowyml/core/templates.py +226 -0
  37. flowyml/core/versioning.py +217 -0
  38. flowyml/integrations/__init__.py +1 -0
  39. flowyml/integrations/keras.py +134 -0
  40. flowyml/monitoring/__init__.py +1 -0
  41. flowyml/monitoring/alerts.py +57 -0
  42. flowyml/monitoring/data.py +102 -0
  43. flowyml/monitoring/llm.py +160 -0
  44. flowyml/monitoring/monitor.py +57 -0
  45. flowyml/monitoring/notifications.py +246 -0
  46. flowyml/registry/__init__.py +5 -0
  47. flowyml/registry/model_registry.py +491 -0
  48. flowyml/registry/pipeline_registry.py +55 -0
  49. flowyml/stacks/__init__.py +27 -0
  50. flowyml/stacks/base.py +77 -0
  51. flowyml/stacks/bridge.py +288 -0
  52. flowyml/stacks/components.py +155 -0
  53. flowyml/stacks/gcp.py +499 -0
  54. flowyml/stacks/local.py +112 -0
  55. flowyml/stacks/migration.py +97 -0
  56. flowyml/stacks/plugin_config.py +78 -0
  57. flowyml/stacks/plugins.py +401 -0
  58. flowyml/stacks/registry.py +226 -0
  59. flowyml/storage/__init__.py +26 -0
  60. flowyml/storage/artifacts.py +246 -0
  61. flowyml/storage/materializers/__init__.py +20 -0
  62. flowyml/storage/materializers/base.py +133 -0
  63. flowyml/storage/materializers/keras.py +185 -0
  64. flowyml/storage/materializers/numpy.py +94 -0
  65. flowyml/storage/materializers/pandas.py +142 -0
  66. flowyml/storage/materializers/pytorch.py +135 -0
  67. flowyml/storage/materializers/sklearn.py +110 -0
  68. flowyml/storage/materializers/tensorflow.py +152 -0
  69. flowyml/storage/metadata.py +931 -0
  70. flowyml/tracking/__init__.py +1 -0
  71. flowyml/tracking/experiment.py +211 -0
  72. flowyml/tracking/leaderboard.py +191 -0
  73. flowyml/tracking/runs.py +145 -0
  74. flowyml/ui/__init__.py +15 -0
  75. flowyml/ui/backend/Dockerfile +31 -0
  76. flowyml/ui/backend/__init__.py +0 -0
  77. flowyml/ui/backend/auth.py +163 -0
  78. flowyml/ui/backend/main.py +187 -0
  79. flowyml/ui/backend/routers/__init__.py +0 -0
  80. flowyml/ui/backend/routers/assets.py +45 -0
  81. flowyml/ui/backend/routers/execution.py +179 -0
  82. flowyml/ui/backend/routers/experiments.py +49 -0
  83. flowyml/ui/backend/routers/leaderboard.py +118 -0
  84. flowyml/ui/backend/routers/notifications.py +72 -0
  85. flowyml/ui/backend/routers/pipelines.py +110 -0
  86. flowyml/ui/backend/routers/plugins.py +192 -0
  87. flowyml/ui/backend/routers/projects.py +85 -0
  88. flowyml/ui/backend/routers/runs.py +66 -0
  89. flowyml/ui/backend/routers/schedules.py +222 -0
  90. flowyml/ui/backend/routers/traces.py +84 -0
  91. flowyml/ui/frontend/Dockerfile +20 -0
  92. flowyml/ui/frontend/README.md +315 -0
  93. flowyml/ui/frontend/dist/assets/index-DFNQnrUj.js +448 -0
  94. flowyml/ui/frontend/dist/assets/index-pWI271rZ.css +1 -0
  95. flowyml/ui/frontend/dist/index.html +16 -0
  96. flowyml/ui/frontend/index.html +15 -0
  97. flowyml/ui/frontend/nginx.conf +26 -0
  98. flowyml/ui/frontend/package-lock.json +3545 -0
  99. flowyml/ui/frontend/package.json +33 -0
  100. flowyml/ui/frontend/postcss.config.js +6 -0
  101. flowyml/ui/frontend/src/App.jsx +21 -0
  102. flowyml/ui/frontend/src/app/assets/page.jsx +397 -0
  103. flowyml/ui/frontend/src/app/dashboard/page.jsx +295 -0
  104. flowyml/ui/frontend/src/app/experiments/[experimentId]/page.jsx +255 -0
  105. flowyml/ui/frontend/src/app/experiments/page.jsx +360 -0
  106. flowyml/ui/frontend/src/app/leaderboard/page.jsx +133 -0
  107. flowyml/ui/frontend/src/app/pipelines/page.jsx +454 -0
  108. flowyml/ui/frontend/src/app/plugins/page.jsx +48 -0
  109. flowyml/ui/frontend/src/app/projects/page.jsx +292 -0
  110. flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +682 -0
  111. flowyml/ui/frontend/src/app/runs/page.jsx +470 -0
  112. flowyml/ui/frontend/src/app/schedules/page.jsx +585 -0
  113. flowyml/ui/frontend/src/app/settings/page.jsx +314 -0
  114. flowyml/ui/frontend/src/app/tokens/page.jsx +456 -0
  115. flowyml/ui/frontend/src/app/traces/page.jsx +246 -0
  116. flowyml/ui/frontend/src/components/Layout.jsx +108 -0
  117. flowyml/ui/frontend/src/components/PipelineGraph.jsx +295 -0
  118. flowyml/ui/frontend/src/components/header/Header.jsx +72 -0
  119. flowyml/ui/frontend/src/components/plugins/AddPluginDialog.jsx +121 -0
  120. flowyml/ui/frontend/src/components/plugins/InstalledPlugins.jsx +124 -0
  121. flowyml/ui/frontend/src/components/plugins/PluginBrowser.jsx +167 -0
  122. flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +60 -0
  123. flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +145 -0
  124. flowyml/ui/frontend/src/components/ui/Badge.jsx +26 -0
  125. flowyml/ui/frontend/src/components/ui/Button.jsx +34 -0
  126. flowyml/ui/frontend/src/components/ui/Card.jsx +44 -0
  127. flowyml/ui/frontend/src/components/ui/CodeSnippet.jsx +38 -0
  128. flowyml/ui/frontend/src/components/ui/CollapsibleCard.jsx +53 -0
  129. flowyml/ui/frontend/src/components/ui/DataView.jsx +175 -0
  130. flowyml/ui/frontend/src/components/ui/EmptyState.jsx +49 -0
  131. flowyml/ui/frontend/src/components/ui/ExecutionStatus.jsx +122 -0
  132. flowyml/ui/frontend/src/components/ui/KeyValue.jsx +25 -0
  133. flowyml/ui/frontend/src/components/ui/ProjectSelector.jsx +134 -0
  134. flowyml/ui/frontend/src/contexts/ProjectContext.jsx +79 -0
  135. flowyml/ui/frontend/src/contexts/ThemeContext.jsx +54 -0
  136. flowyml/ui/frontend/src/index.css +11 -0
  137. flowyml/ui/frontend/src/layouts/MainLayout.jsx +23 -0
  138. flowyml/ui/frontend/src/main.jsx +10 -0
  139. flowyml/ui/frontend/src/router/index.jsx +39 -0
  140. flowyml/ui/frontend/src/services/pluginService.js +90 -0
  141. flowyml/ui/frontend/src/utils/api.js +47 -0
  142. flowyml/ui/frontend/src/utils/cn.js +6 -0
  143. flowyml/ui/frontend/tailwind.config.js +31 -0
  144. flowyml/ui/frontend/vite.config.js +21 -0
  145. flowyml/ui/utils.py +77 -0
  146. flowyml/utils/__init__.py +67 -0
  147. flowyml/utils/config.py +308 -0
  148. flowyml/utils/debug.py +240 -0
  149. flowyml/utils/environment.py +346 -0
  150. flowyml/utils/git.py +319 -0
  151. flowyml/utils/logging.py +61 -0
  152. flowyml/utils/performance.py +314 -0
  153. flowyml/utils/stack_config.py +296 -0
  154. flowyml/utils/validation.py +270 -0
  155. flowyml-1.1.0.dist-info/METADATA +372 -0
  156. flowyml-1.1.0.dist-info/RECORD +159 -0
  157. flowyml-1.1.0.dist-info/WHEEL +4 -0
  158. flowyml-1.1.0.dist-info/entry_points.txt +3 -0
  159. flowyml-1.1.0.dist-info/licenses/LICENSE +17 -0
@@ -0,0 +1,292 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { fetchApi } from '../../utils/api';
3
+ import { Link } from 'react-router-dom';
4
+ import { Folder, Plus, Trash2, Activity, Database, Clock } from 'lucide-react';
5
+ import { format } from 'date-fns';
6
+ import { DataView } from '../../components/ui/DataView';
7
+ import { Button } from '../../components/ui/Button';
8
+ import { useProject } from '../../contexts/ProjectContext';
9
+
10
+ export function Projects() {
11
+ const [projects, setProjects] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [showCreateModal, setShowCreateModal] = useState(false);
14
+ const [newProjectName, setNewProjectName] = useState('');
15
+ const [newProjectDesc, setNewProjectDesc] = useState('');
16
+ const [projectStats, setProjectStats] = useState({});
17
+ const { setSelectedProject } = useProject();
18
+
19
+ useEffect(() => {
20
+ fetchProjects();
21
+ }, []);
22
+
23
+ const fetchProjects = async () => {
24
+ try {
25
+ const response = await fetchApi('/api/projects/');
26
+ const data = await response.json();
27
+ // setProjects(data); // This line will be replaced/modified
28
+
29
+ // Fetch stats for each project
30
+ const projectsList = Array.isArray(data) ? data : (data.projects || []);
31
+ const projectsWithStats = await Promise.all(projectsList.map(async (project) => {
32
+ try {
33
+ // Fetch runs for this project
34
+ const runsRes = await fetch(`/api/runs?project=${encodeURIComponent(project.name)}`);
35
+ const runsData = await runsRes.json();
36
+
37
+ // Fetch pipelines - we need to count unique pipelines from runs
38
+ const pipelineNames = new Set((runsData.runs || []).map(r => r.pipeline_name));
39
+
40
+ // Fetch artifacts for this project
41
+ const artifactsRes = await fetch(`/api/assets?project=${encodeURIComponent(project.name)}`);
42
+ const artifactsData = await artifactsRes.json();
43
+
44
+ return {
45
+ ...project,
46
+ runs: (runsData.runs || []).length,
47
+ pipelines: pipelineNames.size,
48
+ artifacts: (artifactsData.artifacts || []).length
49
+ };
50
+ } catch (err) {
51
+ console.error(`Failed to fetch stats for project ${project.name}:`, err);
52
+ return {
53
+ ...project,
54
+ runs: 0,
55
+ pipelines: 0,
56
+ artifacts: 0
57
+ };
58
+ }
59
+ }));
60
+
61
+ setProjects(projectsWithStats);
62
+ } catch (error) {
63
+ console.error('Failed to fetch projects:', error);
64
+ } finally {
65
+ setLoading(false);
66
+ }
67
+ };
68
+
69
+ const createProject = async (e) => {
70
+ e.preventDefault();
71
+ try {
72
+ const response = await fetchApi('/api/projects/', {
73
+ method: 'POST',
74
+ headers: { 'Content-Type': 'application/json' },
75
+ body: JSON.stringify({
76
+ name: newProjectName,
77
+ description: newProjectDesc
78
+ })
79
+ });
80
+
81
+ if (response.ok) {
82
+ setShowCreateModal(false);
83
+ setNewProjectName('');
84
+ setNewProjectDesc('');
85
+ fetchProjects();
86
+ }
87
+ } catch (error) {
88
+ console.error('Failed to create project:', error);
89
+ }
90
+ };
91
+
92
+ const deleteProject = async (name) => {
93
+ if (!confirm(`Are you sure you want to delete project "${name}"?`)) return;
94
+
95
+ try {
96
+ const response = await fetchApi(`/api/projects/${name}`, {
97
+ method: 'DELETE'
98
+ });
99
+
100
+ if (response.ok) {
101
+ fetchProjects();
102
+ }
103
+ } catch (error) {
104
+ console.error('Failed to delete project:', error);
105
+ }
106
+ };
107
+
108
+ const columns = [
109
+ {
110
+ header: 'Project Name',
111
+ key: 'name',
112
+ sortable: true,
113
+ render: (project) => (
114
+ <div className="flex items-center gap-3">
115
+ <div className="p-2 bg-blue-500/10 rounded-lg">
116
+ <Folder className="w-5 h-5 text-blue-500" />
117
+ </div>
118
+ <div>
119
+ <div className="font-medium text-slate-900 dark:text-white">{project.name}</div>
120
+ <div className="text-xs text-slate-500">Created {format(new Date(project.created_at), 'MMM d, yyyy')}</div>
121
+ </div>
122
+ </div>
123
+ )
124
+ },
125
+ {
126
+ header: 'Description',
127
+ key: 'description',
128
+ render: (project) => (
129
+ <span className="text-slate-500 dark:text-slate-400 truncate max-w-xs block">
130
+ {project.description || "No description"}
131
+ </span>
132
+ )
133
+ },
134
+ {
135
+ header: 'Stats',
136
+ key: 'stats',
137
+ render: (project) => {
138
+ const stats = projectStats[project.name] || { runs: 0, pipelines: 0, artifacts: 0 };
139
+ return (
140
+ <div className="flex gap-4 text-sm text-slate-500">
141
+ <span className="flex items-center gap-1"><Activity size={14} /> {stats.pipelines || 0}</span>
142
+ <span className="flex items-center gap-1"><Clock size={14} /> {stats.runs || 0}</span>
143
+ <span className="flex items-center gap-1"><Database size={14} /> {stats.artifacts || 0}</span>
144
+ </div>
145
+ );
146
+ }
147
+ },
148
+ {
149
+ header: 'Actions',
150
+ key: 'actions',
151
+ render: (project) => (
152
+ <button
153
+ onClick={(e) => { e.stopPropagation(); deleteProject(project.name); }}
154
+ className="p-2 text-slate-400 hover:text-red-500 transition-colors"
155
+ >
156
+ <Trash2 size={16} />
157
+ </button>
158
+ )
159
+ }
160
+ ];
161
+
162
+ const renderGrid = (project) => {
163
+ const stats = projectStats[project.name] || { runs: 0, pipelines: 0, artifacts: 0 };
164
+
165
+ return (
166
+ <Link
167
+ to={`/runs?project=${encodeURIComponent(project.name)}`}
168
+ onClick={() => setSelectedProject(project.name)}
169
+ className="block"
170
+ >
171
+ <div className="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-6 hover:border-blue-500/50 hover:shadow-md transition-all group cursor-pointer">
172
+ <div className="flex justify-between items-start mb-4">
173
+ <div className="flex items-center gap-3">
174
+ <div className="p-3 bg-blue-500/10 rounded-xl group-hover:bg-blue-500/20 transition-colors">
175
+ <Folder className="w-6 h-6 text-blue-600 dark:text-blue-400" />
176
+ </div>
177
+ <div>
178
+ <h3 className="font-bold text-lg text-slate-900 dark:text-white">{project.name}</h3>
179
+ <p className="text-xs text-slate-500">
180
+ Created {format(new Date(project.created_at), 'MMM d, yyyy')}
181
+ </p>
182
+ </div>
183
+ </div>
184
+ <button
185
+ onClick={(e) => { e.preventDefault(); e.stopPropagation(); deleteProject(project.name); }}
186
+ className="text-slate-400 hover:text-red-500 transition-colors opacity-0 group-hover:opacity-100"
187
+ >
188
+ <Trash2 className="w-4 h-4" />
189
+ </button>
190
+ </div>
191
+
192
+ <p className="text-slate-500 dark:text-slate-400 mb-6 h-10 overflow-hidden text-sm line-clamp-2">
193
+ {project.description || "No description provided."}
194
+ </p>
195
+
196
+ <div className="grid grid-cols-3 gap-4 border-t border-slate-100 dark:border-slate-700 pt-4">
197
+ <div className="text-center">
198
+ <div className="flex items-center justify-center gap-1 text-slate-400 text-xs mb-1">
199
+ <Activity className="w-3 h-3" /> Pipelines
200
+ </div>
201
+ <span className="font-bold text-slate-700 dark:text-slate-200">{stats.pipelines || 0}</span>
202
+ </div>
203
+ <div className="text-center">
204
+ <div className="flex items-center justify-center gap-1 text-slate-400 text-xs mb-1">
205
+ <Clock className="w-3 h-3" /> Runs
206
+ </div>
207
+ <span className="font-bold text-slate-700 dark:text-slate-200">{stats.runs || 0}</span>
208
+ </div>
209
+ <div className="text-center">
210
+ <div className="flex items-center justify-center gap-1 text-slate-400 text-xs mb-1">
211
+ <Database className="w-3 h-3" /> Artifacts
212
+ </div>
213
+ <span className="font-bold text-slate-700 dark:text-slate-200">{stats.artifacts || 0}</span>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ </Link>
218
+ );
219
+ };
220
+
221
+ return (
222
+ <div className="p-6 max-w-7xl mx-auto">
223
+ <DataView
224
+ title="Projects"
225
+ subtitle="Manage your ML projects and workspaces"
226
+ items={projects}
227
+ loading={loading}
228
+ columns={columns}
229
+ renderGrid={renderGrid}
230
+ actions={
231
+ <Button onClick={() => setShowCreateModal(true)} className="flex items-center gap-2">
232
+ <Plus size={18} />
233
+ New Project
234
+ </Button>
235
+ }
236
+ emptyState={
237
+ <div className="text-center py-12 bg-slate-50 dark:bg-slate-800/30 rounded-xl border-2 border-dashed border-slate-200 dark:border-slate-700">
238
+ <Folder className="w-12 h-12 mx-auto text-slate-400 mb-4" />
239
+ <h3 className="text-lg font-medium text-slate-900 dark:text-white">No projects found</h3>
240
+ <p className="text-slate-500 mb-6">Get started by creating your first project.</p>
241
+ <Button onClick={() => setShowCreateModal(true)}>
242
+ Create Project
243
+ </Button>
244
+ </div>
245
+ }
246
+ />
247
+
248
+ {showCreateModal && (
249
+ <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 animate-in fade-in duration-200">
250
+ <div className="bg-white dark:bg-slate-800 p-6 rounded-xl w-full max-w-md border border-slate-200 dark:border-slate-700 shadow-xl">
251
+ <h2 className="text-xl font-bold mb-4 text-slate-900 dark:text-white">Create New Project</h2>
252
+ <form onSubmit={createProject}>
253
+ <div className="mb-4">
254
+ <label className="block text-sm font-medium mb-1 text-slate-700 dark:text-slate-300">Project Name</label>
255
+ <input
256
+ type="text"
257
+ value={newProjectName}
258
+ onChange={(e) => setNewProjectName(e.target.value)}
259
+ className="w-full px-3 py-2 bg-slate-50 dark:bg-slate-700 rounded-lg border border-slate-200 dark:border-slate-600 focus:border-blue-500 outline-none text-slate-900 dark:text-white"
260
+ required
261
+ placeholder="e.g., recommendation-system"
262
+ />
263
+ </div>
264
+ <div className="mb-6">
265
+ <label className="block text-sm font-medium mb-1 text-slate-700 dark:text-slate-300">Description</label>
266
+ <textarea
267
+ value={newProjectDesc}
268
+ onChange={(e) => setNewProjectDesc(e.target.value)}
269
+ className="w-full px-3 py-2 bg-slate-50 dark:bg-slate-700 rounded-lg border border-slate-200 dark:border-slate-600 focus:border-blue-500 outline-none text-slate-900 dark:text-white"
270
+ rows="3"
271
+ placeholder="Brief description of the project..."
272
+ />
273
+ </div>
274
+ <div className="flex justify-end gap-3">
275
+ <button
276
+ type="button"
277
+ onClick={() => setShowCreateModal(false)}
278
+ className="px-4 py-2 text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg transition-colors"
279
+ >
280
+ Cancel
281
+ </button>
282
+ <Button type="submit">
283
+ Create Project
284
+ </Button>
285
+ </div>
286
+ </form>
287
+ </div>
288
+ </div>
289
+ )}
290
+ </div>
291
+ );
292
+ }