flowyml 1.7.1__py3-none-any.whl → 1.8.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.
- flowyml/assets/base.py +15 -0
- flowyml/assets/dataset.py +570 -17
- flowyml/assets/metrics.py +5 -0
- flowyml/assets/model.py +1052 -15
- flowyml/cli/main.py +709 -0
- flowyml/cli/stack_cli.py +138 -25
- flowyml/core/__init__.py +17 -0
- flowyml/core/executor.py +231 -37
- flowyml/core/image_builder.py +129 -0
- flowyml/core/log_streamer.py +227 -0
- flowyml/core/orchestrator.py +59 -4
- flowyml/core/pipeline.py +65 -13
- flowyml/core/routing.py +558 -0
- flowyml/core/scheduler.py +88 -5
- flowyml/core/step.py +9 -1
- flowyml/core/step_grouping.py +49 -35
- flowyml/core/types.py +407 -0
- flowyml/integrations/keras.py +247 -82
- flowyml/monitoring/alerts.py +10 -0
- flowyml/monitoring/notifications.py +104 -25
- flowyml/monitoring/slack_blocks.py +323 -0
- flowyml/plugins/__init__.py +251 -0
- flowyml/plugins/alerters/__init__.py +1 -0
- flowyml/plugins/alerters/slack.py +168 -0
- flowyml/plugins/base.py +752 -0
- flowyml/plugins/config.py +478 -0
- flowyml/plugins/deployers/__init__.py +22 -0
- flowyml/plugins/deployers/gcp_cloud_run.py +200 -0
- flowyml/plugins/deployers/sagemaker.py +306 -0
- flowyml/plugins/deployers/vertex.py +290 -0
- flowyml/plugins/integration.py +369 -0
- flowyml/plugins/manager.py +510 -0
- flowyml/plugins/model_registries/__init__.py +22 -0
- flowyml/plugins/model_registries/mlflow.py +159 -0
- flowyml/plugins/model_registries/sagemaker.py +489 -0
- flowyml/plugins/model_registries/vertex.py +386 -0
- flowyml/plugins/orchestrators/__init__.py +13 -0
- flowyml/plugins/orchestrators/sagemaker.py +443 -0
- flowyml/plugins/orchestrators/vertex_ai.py +461 -0
- flowyml/plugins/registries/__init__.py +13 -0
- flowyml/plugins/registries/ecr.py +321 -0
- flowyml/plugins/registries/gcr.py +313 -0
- flowyml/plugins/registry.py +454 -0
- flowyml/plugins/stack.py +494 -0
- flowyml/plugins/stack_config.py +537 -0
- flowyml/plugins/stores/__init__.py +13 -0
- flowyml/plugins/stores/gcs.py +460 -0
- flowyml/plugins/stores/s3.py +453 -0
- flowyml/plugins/trackers/__init__.py +11 -0
- flowyml/plugins/trackers/mlflow.py +316 -0
- flowyml/plugins/validators/__init__.py +3 -0
- flowyml/plugins/validators/deepchecks.py +119 -0
- flowyml/registry/__init__.py +2 -1
- flowyml/registry/model_environment.py +109 -0
- flowyml/registry/model_registry.py +241 -96
- flowyml/serving/__init__.py +17 -0
- flowyml/serving/model_server.py +628 -0
- flowyml/stacks/__init__.py +60 -0
- flowyml/stacks/aws.py +93 -0
- flowyml/stacks/base.py +62 -0
- flowyml/stacks/components.py +12 -0
- flowyml/stacks/gcp.py +44 -9
- flowyml/stacks/plugins.py +115 -0
- flowyml/stacks/registry.py +2 -1
- flowyml/storage/sql.py +401 -12
- flowyml/tracking/experiment.py +8 -5
- flowyml/ui/backend/Dockerfile +87 -16
- flowyml/ui/backend/auth.py +12 -2
- flowyml/ui/backend/main.py +149 -5
- flowyml/ui/backend/routers/ai_context.py +226 -0
- flowyml/ui/backend/routers/assets.py +23 -4
- flowyml/ui/backend/routers/auth.py +96 -0
- flowyml/ui/backend/routers/deployments.py +660 -0
- flowyml/ui/backend/routers/model_explorer.py +597 -0
- flowyml/ui/backend/routers/plugins.py +103 -51
- flowyml/ui/backend/routers/projects.py +91 -8
- flowyml/ui/backend/routers/runs.py +132 -1
- flowyml/ui/backend/routers/schedules.py +54 -29
- flowyml/ui/backend/routers/templates.py +319 -0
- flowyml/ui/backend/routers/websocket.py +2 -2
- flowyml/ui/frontend/Dockerfile +55 -6
- flowyml/ui/frontend/dist/assets/index-B5AsPTSz.css +1 -0
- flowyml/ui/frontend/dist/assets/index-dFbZ8wD8.js +753 -0
- flowyml/ui/frontend/dist/index.html +2 -2
- flowyml/ui/frontend/dist/logo.png +0 -0
- flowyml/ui/frontend/nginx.conf +65 -4
- flowyml/ui/frontend/package-lock.json +1415 -74
- flowyml/ui/frontend/package.json +4 -0
- flowyml/ui/frontend/public/logo.png +0 -0
- flowyml/ui/frontend/src/App.jsx +10 -7
- flowyml/ui/frontend/src/app/assets/page.jsx +890 -321
- flowyml/ui/frontend/src/app/auth/Login.jsx +90 -0
- flowyml/ui/frontend/src/app/dashboard/page.jsx +8 -8
- flowyml/ui/frontend/src/app/deployments/page.jsx +786 -0
- flowyml/ui/frontend/src/app/model-explorer/page.jsx +1031 -0
- flowyml/ui/frontend/src/app/pipelines/page.jsx +12 -2
- flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectExperimentsList.jsx +19 -6
- flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectMetricsPanel.jsx +1 -1
- flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +601 -101
- flowyml/ui/frontend/src/app/runs/page.jsx +8 -2
- flowyml/ui/frontend/src/app/settings/page.jsx +267 -253
- flowyml/ui/frontend/src/components/ArtifactViewer.jsx +62 -2
- flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +424 -29
- flowyml/ui/frontend/src/components/AssetTreeHierarchy.jsx +119 -11
- flowyml/ui/frontend/src/components/DatasetViewer.jsx +753 -0
- flowyml/ui/frontend/src/components/Layout.jsx +6 -0
- flowyml/ui/frontend/src/components/PipelineGraph.jsx +79 -29
- flowyml/ui/frontend/src/components/RunDetailsPanel.jsx +36 -6
- flowyml/ui/frontend/src/components/RunMetaPanel.jsx +113 -0
- flowyml/ui/frontend/src/components/TrainingHistoryChart.jsx +514 -0
- flowyml/ui/frontend/src/components/TrainingMetricsPanel.jsx +175 -0
- flowyml/ui/frontend/src/components/ai/AIAssistantButton.jsx +71 -0
- flowyml/ui/frontend/src/components/ai/AIAssistantPanel.jsx +420 -0
- flowyml/ui/frontend/src/components/header/Header.jsx +22 -0
- flowyml/ui/frontend/src/components/plugins/PluginManager.jsx +4 -4
- flowyml/ui/frontend/src/components/plugins/{ZenMLIntegration.jsx → StackImport.jsx} +38 -12
- flowyml/ui/frontend/src/components/sidebar/Sidebar.jsx +36 -13
- flowyml/ui/frontend/src/contexts/AIAssistantContext.jsx +245 -0
- flowyml/ui/frontend/src/contexts/AuthContext.jsx +108 -0
- flowyml/ui/frontend/src/hooks/useAIContext.js +156 -0
- flowyml/ui/frontend/src/hooks/useWebGPU.js +54 -0
- flowyml/ui/frontend/src/layouts/MainLayout.jsx +6 -0
- flowyml/ui/frontend/src/router/index.jsx +47 -20
- flowyml/ui/frontend/src/services/pluginService.js +3 -1
- flowyml/ui/server_manager.py +5 -5
- flowyml/ui/utils.py +157 -39
- flowyml/utils/config.py +37 -15
- flowyml/utils/model_introspection.py +123 -0
- flowyml/utils/observability.py +30 -0
- flowyml-1.8.0.dist-info/METADATA +174 -0
- {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/RECORD +134 -73
- {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/WHEEL +1 -1
- flowyml/ui/frontend/dist/assets/index-BqDQvp63.js +0 -630
- flowyml/ui/frontend/dist/assets/index-By4trVyv.css +0 -1
- flowyml-1.7.1.dist-info/METADATA +0 -477
- {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/entry_points.txt +0 -0
- {flowyml-1.7.1.dist-info → flowyml-1.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -213,8 +213,77 @@ export function AssetTreeHierarchy({ projectId, onAssetSelect, compact = false }
|
|
|
213
213
|
);
|
|
214
214
|
};
|
|
215
215
|
|
|
216
|
-
const getArtifactsForRun = (runId) => {
|
|
217
|
-
|
|
216
|
+
const getArtifactsForRun = (runId, run = null) => {
|
|
217
|
+
// Match artifacts by run_id (exact match or partial match for truncated IDs)
|
|
218
|
+
let artifacts = data.artifacts.filter(a => {
|
|
219
|
+
// Exact match
|
|
220
|
+
if (a.run_id === runId) return true;
|
|
221
|
+
|
|
222
|
+
// Partial match (run_id might be truncated in display)
|
|
223
|
+
if (runId && a.run_id && (
|
|
224
|
+
a.run_id.startsWith(runId) ||
|
|
225
|
+
runId.startsWith(a.run_id) ||
|
|
226
|
+
a.run_id.includes(runId) ||
|
|
227
|
+
runId.includes(a.run_id)
|
|
228
|
+
)) return true;
|
|
229
|
+
|
|
230
|
+
// Match by artifact_id containing run_id
|
|
231
|
+
if (runId && a.artifact_id && a.artifact_id.includes(runId)) return true;
|
|
232
|
+
|
|
233
|
+
// Match by pipeline_name if available
|
|
234
|
+
if (run && a.pipeline_name && a.pipeline_name === run.pipeline_name) return true;
|
|
235
|
+
|
|
236
|
+
return false;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Apply asset type filter
|
|
240
|
+
if (assetTypeFilter !== 'all') {
|
|
241
|
+
artifacts = artifacts.filter(a => a.type?.toLowerCase() === assetTypeFilter);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return artifacts;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// Get all artifacts for a pipeline (regardless of run matching)
|
|
248
|
+
const getArtifactsForPipeline = (pipelineName, projectName) => {
|
|
249
|
+
let artifacts = data.artifacts.filter(a => {
|
|
250
|
+
// Match by pipeline_name
|
|
251
|
+
if (a.pipeline_name === pipelineName) return true;
|
|
252
|
+
|
|
253
|
+
// Match by project if pipeline_name is not set on artifact
|
|
254
|
+
if (!a.pipeline_name && a.project === projectName) return true;
|
|
255
|
+
|
|
256
|
+
return false;
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Apply asset type filter
|
|
260
|
+
if (assetTypeFilter !== 'all') {
|
|
261
|
+
artifacts = artifacts.filter(a => a.type?.toLowerCase() === assetTypeFilter);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return artifacts;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Get artifacts that belong to a project but couldn't be matched to a specific run
|
|
268
|
+
const getUnmatchedArtifactsForProject = (projectName, allMatchedRunIds) => {
|
|
269
|
+
let artifacts = data.artifacts.filter(a => {
|
|
270
|
+
// Only include artifacts for this project
|
|
271
|
+
if (a.project !== projectName) return false;
|
|
272
|
+
|
|
273
|
+
// Exclude if already matched to a run
|
|
274
|
+
if (allMatchedRunIds.has(a.run_id)) return false;
|
|
275
|
+
|
|
276
|
+
// Check partial matches
|
|
277
|
+
for (const runId of allMatchedRunIds) {
|
|
278
|
+
if (a.run_id && runId && (
|
|
279
|
+
a.run_id.startsWith(runId) ||
|
|
280
|
+
runId.startsWith(a.run_id) ||
|
|
281
|
+
a.artifact_id?.includes(runId)
|
|
282
|
+
)) return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return true;
|
|
286
|
+
});
|
|
218
287
|
|
|
219
288
|
// Apply asset type filter
|
|
220
289
|
if (assetTypeFilter !== 'all') {
|
|
@@ -236,7 +305,7 @@ export function AssetTreeHierarchy({ projectId, onAssetSelect, compact = false }
|
|
|
236
305
|
// Single project view or no projects - show pipelines directly
|
|
237
306
|
return data.pipelines.map((pipeline, idx) => {
|
|
238
307
|
const runs = getRunsForPipeline(pipeline.name, projectId);
|
|
239
|
-
return renderPipeline(pipeline, runs, 0, idx < 3); // Expand first 3 pipelines
|
|
308
|
+
return renderPipeline(pipeline, runs, 0, idx < 3, projectId); // Expand first 3 pipelines
|
|
240
309
|
});
|
|
241
310
|
} else {
|
|
242
311
|
// Multiple projects - group by project
|
|
@@ -258,7 +327,7 @@ export function AssetTreeHierarchy({ projectId, onAssetSelect, compact = false }
|
|
|
258
327
|
>
|
|
259
328
|
{pipelines.map((pipeline, idx) => {
|
|
260
329
|
const runs = getRunsForPipeline(pipeline.name, project.name);
|
|
261
|
-
return renderPipeline(pipeline, runs, 1, projIdx === 0 && idx < 2); // Expand first 2 pipelines of first project
|
|
330
|
+
return renderPipeline(pipeline, runs, 1, projIdx === 0 && idx < 2, project.name); // Expand first 2 pipelines of first project
|
|
262
331
|
})}
|
|
263
332
|
</TreeNode>
|
|
264
333
|
);
|
|
@@ -266,10 +335,32 @@ export function AssetTreeHierarchy({ projectId, onAssetSelect, compact = false }
|
|
|
266
335
|
}
|
|
267
336
|
};
|
|
268
337
|
|
|
269
|
-
const renderPipeline = (pipeline, runs, baseLevel, defaultExpanded = false) => {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
338
|
+
const renderPipeline = (pipeline, runs, baseLevel, defaultExpanded = false, projectName = null) => {
|
|
339
|
+
// Get all artifacts for this pipeline (by pipeline_name or project)
|
|
340
|
+
const pipelineArtifacts = getArtifactsForPipeline(pipeline.name, projectName);
|
|
341
|
+
|
|
342
|
+
// Get run IDs for this pipeline
|
|
343
|
+
const runIds = new Set(runs.map(r => r.run_id));
|
|
344
|
+
|
|
345
|
+
// Get unmatched artifacts (artifacts in pipeline that don't match any run)
|
|
346
|
+
const unmatchedArtifacts = pipelineArtifacts.filter(a => {
|
|
347
|
+
// Check exact match
|
|
348
|
+
if (runIds.has(a.run_id)) return false;
|
|
349
|
+
|
|
350
|
+
// Check partial matches
|
|
351
|
+
for (const runId of runIds) {
|
|
352
|
+
if (a.run_id && runId && (
|
|
353
|
+
a.run_id.startsWith(runId) ||
|
|
354
|
+
runId.startsWith(a.run_id) ||
|
|
355
|
+
a.artifact_id?.includes(runId)
|
|
356
|
+
)) return false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return true;
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const matchedArtifacts = pipelineArtifacts.length - unmatchedArtifacts.length;
|
|
363
|
+
const totalArtifacts = pipelineArtifacts.length;
|
|
273
364
|
|
|
274
365
|
return (
|
|
275
366
|
<TreeNode
|
|
@@ -277,7 +368,7 @@ export function AssetTreeHierarchy({ projectId, onAssetSelect, compact = false }
|
|
|
277
368
|
label={pipeline.name}
|
|
278
369
|
icon={Activity}
|
|
279
370
|
level={baseLevel}
|
|
280
|
-
defaultExpanded={defaultExpanded} //
|
|
371
|
+
defaultExpanded={defaultExpanded || unmatchedArtifacts.length > 0} // Expand if has unmatched artifacts
|
|
281
372
|
badge={
|
|
282
373
|
<div className="flex gap-1">
|
|
283
374
|
{totalArtifacts > 0 && (
|
|
@@ -297,16 +388,33 @@ export function AssetTreeHierarchy({ projectId, onAssetSelect, compact = false }
|
|
|
297
388
|
</Link>
|
|
298
389
|
}
|
|
299
390
|
>
|
|
300
|
-
{runs.length === 0 && (
|
|
391
|
+
{runs.length === 0 && unmatchedArtifacts.length === 0 && (
|
|
301
392
|
<div className="pl-12 py-2 text-xs text-slate-400 italic">No runs yet</div>
|
|
302
393
|
)}
|
|
303
394
|
{runs.map(run => renderRun(run, baseLevel + 1))}
|
|
395
|
+
{/* Show unmatched artifacts under "Other Artifacts" */}
|
|
396
|
+
{unmatchedArtifacts.length > 0 && (
|
|
397
|
+
<TreeNode
|
|
398
|
+
key={`${pipeline.name}-unmatched`}
|
|
399
|
+
label="Other Artifacts"
|
|
400
|
+
icon={FileBox}
|
|
401
|
+
level={baseLevel + 1}
|
|
402
|
+
defaultExpanded={true}
|
|
403
|
+
badge={
|
|
404
|
+
<span className="flex items-center gap-0.5 text-[10px] bg-amber-100 text-amber-600 dark:bg-amber-900/30 dark:text-amber-400 px-1.5 py-0.5 rounded-full">
|
|
405
|
+
<FileBox className="w-3 h-3" /> {unmatchedArtifacts.length}
|
|
406
|
+
</span>
|
|
407
|
+
}
|
|
408
|
+
>
|
|
409
|
+
{unmatchedArtifacts.map(artifact => renderArtifact(artifact, baseLevel + 2))}
|
|
410
|
+
</TreeNode>
|
|
411
|
+
)}
|
|
304
412
|
</TreeNode>
|
|
305
413
|
);
|
|
306
414
|
};
|
|
307
415
|
|
|
308
416
|
const renderRun = (run, baseLevel) => {
|
|
309
|
-
const artifacts = getArtifactsForRun(run.run_id);
|
|
417
|
+
const artifacts = getArtifactsForRun(run.run_id, run);
|
|
310
418
|
const modelCount = artifacts.filter(a => a.type?.toLowerCase() === 'model').length;
|
|
311
419
|
|
|
312
420
|
return (
|