flowyml 1.7.1__py3-none-any.whl → 1.7.2__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 (30) hide show
  1. flowyml/assets/dataset.py +570 -17
  2. flowyml/assets/model.py +1052 -15
  3. flowyml/core/executor.py +70 -11
  4. flowyml/core/orchestrator.py +37 -2
  5. flowyml/core/pipeline.py +32 -4
  6. flowyml/core/scheduler.py +88 -5
  7. flowyml/integrations/keras.py +247 -82
  8. flowyml/ui/backend/routers/runs.py +112 -0
  9. flowyml/ui/backend/routers/schedules.py +35 -15
  10. flowyml/ui/frontend/dist/assets/index-B40RsQDq.css +1 -0
  11. flowyml/ui/frontend/dist/assets/index-CjI0zKCn.js +685 -0
  12. flowyml/ui/frontend/dist/index.html +2 -2
  13. flowyml/ui/frontend/package-lock.json +11 -0
  14. flowyml/ui/frontend/package.json +1 -0
  15. flowyml/ui/frontend/src/app/assets/page.jsx +890 -321
  16. flowyml/ui/frontend/src/app/projects/[projectId]/_components/ProjectMetricsPanel.jsx +1 -1
  17. flowyml/ui/frontend/src/app/runs/[runId]/page.jsx +589 -101
  18. flowyml/ui/frontend/src/components/ArtifactViewer.jsx +62 -2
  19. flowyml/ui/frontend/src/components/AssetDetailsPanel.jsx +401 -28
  20. flowyml/ui/frontend/src/components/AssetTreeHierarchy.jsx +119 -11
  21. flowyml/ui/frontend/src/components/DatasetViewer.jsx +753 -0
  22. flowyml/ui/frontend/src/components/TrainingHistoryChart.jsx +514 -0
  23. flowyml/ui/frontend/src/components/TrainingMetricsPanel.jsx +175 -0
  24. {flowyml-1.7.1.dist-info → flowyml-1.7.2.dist-info}/METADATA +1 -1
  25. {flowyml-1.7.1.dist-info → flowyml-1.7.2.dist-info}/RECORD +28 -25
  26. flowyml/ui/frontend/dist/assets/index-BqDQvp63.js +0 -630
  27. flowyml/ui/frontend/dist/assets/index-By4trVyv.css +0 -1
  28. {flowyml-1.7.1.dist-info → flowyml-1.7.2.dist-info}/WHEEL +0 -0
  29. {flowyml-1.7.1.dist-info → flowyml-1.7.2.dist-info}/entry_points.txt +0 -0
  30. {flowyml-1.7.1.dist-info → flowyml-1.7.2.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
- let artifacts = data.artifacts.filter(a => a.run_id === runId);
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
- const totalArtifacts = runs.reduce((sum, run) => {
271
- return sum + getArtifactsForRun(run.run_id).length;
272
- }, 0);
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} // Use the parameter
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 (