ml-dash 0.5.4__py3-none-any.whl → 0.5.6__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.
ml_dash/experiment.py
CHANGED
|
@@ -460,7 +460,7 @@ class Experiment:
|
|
|
460
460
|
timestamp=log_entry["timestamp"]
|
|
461
461
|
)
|
|
462
462
|
|
|
463
|
-
def
|
|
463
|
+
def files(self, **kwargs) -> FileBuilder:
|
|
464
464
|
"""
|
|
465
465
|
Get a FileBuilder for fluent file operations.
|
|
466
466
|
|
|
@@ -472,17 +472,17 @@ class Experiment:
|
|
|
472
472
|
|
|
473
473
|
Examples:
|
|
474
474
|
# Upload file
|
|
475
|
-
experiment.
|
|
475
|
+
experiment.files(file_path="./model.pt", prefix="/models").save()
|
|
476
476
|
|
|
477
477
|
# List files
|
|
478
|
-
files = experiment.
|
|
479
|
-
files = experiment.
|
|
478
|
+
files = experiment.files().list()
|
|
479
|
+
files = experiment.files(prefix="/models").list()
|
|
480
480
|
|
|
481
481
|
# Download file
|
|
482
|
-
experiment.
|
|
482
|
+
experiment.files(file_id="123").download()
|
|
483
483
|
|
|
484
484
|
# Delete file
|
|
485
|
-
experiment.
|
|
485
|
+
experiment.files(file_id="123").delete()
|
|
486
486
|
"""
|
|
487
487
|
if not self._is_open:
|
|
488
488
|
raise RuntimeError("Experiment not open. Use experiment.open() or context manager.")
|
ml_dash/files.py
CHANGED
|
@@ -6,7 +6,7 @@ Provides fluent API for file upload, download, list, and delete operations.
|
|
|
6
6
|
|
|
7
7
|
import hashlib
|
|
8
8
|
import mimetypes
|
|
9
|
-
from typing import Dict, Any, List, Optional, TYPE_CHECKING
|
|
9
|
+
from typing import Dict, Any, List, Optional, Union, TYPE_CHECKING
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
@@ -19,18 +19,18 @@ class FileBuilder:
|
|
|
19
19
|
|
|
20
20
|
Usage:
|
|
21
21
|
# Upload file
|
|
22
|
-
experiment.
|
|
22
|
+
experiment.files(file_path="./model.pt", prefix="/models").save()
|
|
23
23
|
|
|
24
24
|
# List files
|
|
25
|
-
files = experiment.
|
|
26
|
-
files = experiment.
|
|
25
|
+
files = experiment.files().list()
|
|
26
|
+
files = experiment.files(prefix="/models").list()
|
|
27
27
|
|
|
28
28
|
# Download file
|
|
29
|
-
experiment.
|
|
30
|
-
experiment.
|
|
29
|
+
experiment.files(file_id="123").download()
|
|
30
|
+
experiment.files(file_id="123", dest_path="./model.pt").download()
|
|
31
31
|
|
|
32
32
|
# Delete file
|
|
33
|
-
experiment.
|
|
33
|
+
experiment.files(file_id="123").delete()
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
36
|
def __init__(self, experiment: 'Experiment', **kwargs):
|
|
@@ -72,7 +72,7 @@ class FileBuilder:
|
|
|
72
72
|
ValueError: If file size exceeds 5GB limit
|
|
73
73
|
|
|
74
74
|
Examples:
|
|
75
|
-
result = experiment.
|
|
75
|
+
result = experiment.files(file_path="./model.pt", prefix="/models").save()
|
|
76
76
|
# Returns: {"id": "123", "path": "/models", "filename": "model.pt", ...}
|
|
77
77
|
"""
|
|
78
78
|
if not self._experiment._is_open:
|
|
@@ -130,9 +130,9 @@ class FileBuilder:
|
|
|
130
130
|
RuntimeError: If experiment is not open
|
|
131
131
|
|
|
132
132
|
Examples:
|
|
133
|
-
files = experiment.
|
|
134
|
-
files = experiment.
|
|
135
|
-
files = experiment.
|
|
133
|
+
files = experiment.files().list() # All files
|
|
134
|
+
files = experiment.files(prefix="/models").list() # Filter by prefix
|
|
135
|
+
files = experiment.files(tags=["checkpoint"]).list() # Filter by tags
|
|
136
136
|
"""
|
|
137
137
|
if not self._experiment._is_open:
|
|
138
138
|
raise RuntimeError("Experiment not open. Use experiment.open() or context manager.")
|
|
@@ -158,10 +158,10 @@ class FileBuilder:
|
|
|
158
158
|
|
|
159
159
|
Examples:
|
|
160
160
|
# Download to current directory with original filename
|
|
161
|
-
path = experiment.
|
|
161
|
+
path = experiment.files(file_id="123").download()
|
|
162
162
|
|
|
163
163
|
# Download to custom path
|
|
164
|
-
path = experiment.
|
|
164
|
+
path = experiment.files(file_id="123", dest_path="./model.pt").download()
|
|
165
165
|
"""
|
|
166
166
|
if not self._experiment._is_open:
|
|
167
167
|
raise RuntimeError("Experiment not open. Use experiment.open() or context manager.")
|
|
@@ -186,7 +186,7 @@ class FileBuilder:
|
|
|
186
186
|
ValueError: If file_id not provided
|
|
187
187
|
|
|
188
188
|
Examples:
|
|
189
|
-
result = experiment.
|
|
189
|
+
result = experiment.files(file_id="123").delete()
|
|
190
190
|
"""
|
|
191
191
|
if not self._experiment._is_open:
|
|
192
192
|
raise RuntimeError("Experiment not open. Use experiment.open() or context manager.")
|
|
@@ -211,7 +211,7 @@ class FileBuilder:
|
|
|
211
211
|
ValueError: If file_id not provided
|
|
212
212
|
|
|
213
213
|
Examples:
|
|
214
|
-
result = experiment.
|
|
214
|
+
result = experiment.files(
|
|
215
215
|
file_id="123",
|
|
216
216
|
description="Updated description",
|
|
217
217
|
tags=["new", "tags"],
|
|
@@ -251,7 +251,7 @@ class FileBuilder:
|
|
|
251
251
|
|
|
252
252
|
Examples:
|
|
253
253
|
config = {"model": "resnet50", "lr": 0.001}
|
|
254
|
-
result = experiment.
|
|
254
|
+
result = experiment.files(prefix="/configs").save_json(config, "config.json")
|
|
255
255
|
"""
|
|
256
256
|
import json
|
|
257
257
|
import tempfile
|
|
@@ -309,10 +309,10 @@ class FileBuilder:
|
|
|
309
309
|
Examples:
|
|
310
310
|
import torch
|
|
311
311
|
model = torch.nn.Linear(10, 5)
|
|
312
|
-
result = experiment.
|
|
312
|
+
result = experiment.files(prefix="/models").save_torch(model, "model.pt")
|
|
313
313
|
|
|
314
314
|
# Or save state dict
|
|
315
|
-
result = experiment.
|
|
315
|
+
result = experiment.files(prefix="/models").save_torch(model.state_dict(), "model.pth")
|
|
316
316
|
"""
|
|
317
317
|
import tempfile
|
|
318
318
|
import os
|
|
@@ -372,10 +372,10 @@ class FileBuilder:
|
|
|
372
372
|
|
|
373
373
|
Examples:
|
|
374
374
|
data = {"model": "resnet50", "weights": np.array([1, 2, 3])}
|
|
375
|
-
result = experiment.
|
|
375
|
+
result = experiment.files(prefix="/data").save_pkl(data, "data.pkl")
|
|
376
376
|
|
|
377
377
|
# Or save any Python object
|
|
378
|
-
result = experiment.
|
|
378
|
+
result = experiment.files(prefix="/models").save_pkl(trained_model, "model.pickle")
|
|
379
379
|
"""
|
|
380
380
|
import pickle
|
|
381
381
|
import tempfile
|
|
@@ -414,6 +414,199 @@ class FileBuilder:
|
|
|
414
414
|
except Exception:
|
|
415
415
|
pass
|
|
416
416
|
|
|
417
|
+
def save_fig(self, fig: Optional[Any] = None, file_name: str = "figure.png", **kwargs) -> Dict[str, Any]:
|
|
418
|
+
"""
|
|
419
|
+
Save matplotlib figure to a file.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
fig: Matplotlib figure object. If None, uses plt.gcf() (current figure)
|
|
423
|
+
file_name: Name of file to create (extension determines format: .png, .pdf, .svg, .jpg)
|
|
424
|
+
**kwargs: Additional arguments passed to fig.savefig():
|
|
425
|
+
- dpi: Resolution (int or 'figure')
|
|
426
|
+
- transparent: Make background transparent (bool)
|
|
427
|
+
- bbox_inches: 'tight' to auto-crop (str or Bbox)
|
|
428
|
+
- quality: JPEG quality 0-100 (int)
|
|
429
|
+
- format: Override format detection (str)
|
|
430
|
+
|
|
431
|
+
Returns:
|
|
432
|
+
File metadata dict with id, path, filename, checksum, etc.
|
|
433
|
+
|
|
434
|
+
Raises:
|
|
435
|
+
RuntimeError: If experiment not open or write-protected
|
|
436
|
+
ImportError: If matplotlib not installed
|
|
437
|
+
|
|
438
|
+
Examples:
|
|
439
|
+
import matplotlib.pyplot as plt
|
|
440
|
+
|
|
441
|
+
# Use current figure
|
|
442
|
+
plt.plot([1, 2, 3], [1, 4, 9])
|
|
443
|
+
result = experiment.files(prefix="/plots").save_fig(file_name="plot.png")
|
|
444
|
+
|
|
445
|
+
# Specify figure explicitly
|
|
446
|
+
fig, ax = plt.subplots()
|
|
447
|
+
ax.plot([1, 2, 3])
|
|
448
|
+
result = experiment.files(prefix="/plots").save_fig(fig=fig, file_name="plot.pdf", dpi=150)
|
|
449
|
+
"""
|
|
450
|
+
import tempfile
|
|
451
|
+
import os
|
|
452
|
+
|
|
453
|
+
try:
|
|
454
|
+
import matplotlib.pyplot as plt
|
|
455
|
+
except ImportError:
|
|
456
|
+
raise ImportError("Matplotlib is not installed. Install it with: pip install matplotlib")
|
|
457
|
+
|
|
458
|
+
if not self._experiment._is_open:
|
|
459
|
+
raise RuntimeError("Experiment not open. Use experiment.run.start() or context manager.")
|
|
460
|
+
|
|
461
|
+
if self._experiment._write_protected:
|
|
462
|
+
raise RuntimeError("Experiment is write-protected and cannot be modified.")
|
|
463
|
+
|
|
464
|
+
# Get figure
|
|
465
|
+
if fig is None:
|
|
466
|
+
fig = plt.gcf()
|
|
467
|
+
|
|
468
|
+
# Create temporary file with desired filename
|
|
469
|
+
temp_dir = tempfile.mkdtemp()
|
|
470
|
+
temp_path = os.path.join(temp_dir, file_name)
|
|
471
|
+
|
|
472
|
+
try:
|
|
473
|
+
# Save figure to temp file
|
|
474
|
+
fig.savefig(temp_path, **kwargs)
|
|
475
|
+
|
|
476
|
+
# Close figure to prevent memory leaks
|
|
477
|
+
plt.close(fig)
|
|
478
|
+
|
|
479
|
+
# Save using existing save() method
|
|
480
|
+
original_file_path = self._file_path
|
|
481
|
+
self._file_path = temp_path
|
|
482
|
+
|
|
483
|
+
# Upload and get result
|
|
484
|
+
result = self.save()
|
|
485
|
+
|
|
486
|
+
# Restore original file_path
|
|
487
|
+
self._file_path = original_file_path
|
|
488
|
+
|
|
489
|
+
return result
|
|
490
|
+
finally:
|
|
491
|
+
# Clean up temp file and directory
|
|
492
|
+
try:
|
|
493
|
+
os.unlink(temp_path)
|
|
494
|
+
os.rmdir(temp_dir)
|
|
495
|
+
except Exception:
|
|
496
|
+
pass
|
|
497
|
+
|
|
498
|
+
def save_video(
|
|
499
|
+
self,
|
|
500
|
+
frame_stack: Union[List, Any],
|
|
501
|
+
file_name: str,
|
|
502
|
+
fps: int = 20,
|
|
503
|
+
**imageio_kwargs
|
|
504
|
+
) -> Dict[str, Any]:
|
|
505
|
+
"""
|
|
506
|
+
Save video frame stack to a file.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
frame_stack: List of numpy arrays or stacked array (shape: [N, H, W] or [N, H, W, C])
|
|
510
|
+
file_name: Name of file to create (extension determines format: .mp4, .gif, .avi, .webm)
|
|
511
|
+
fps: Frames per second (default: 20)
|
|
512
|
+
**imageio_kwargs: Additional arguments passed to imageio.v3.imwrite():
|
|
513
|
+
- codec: Video codec (e.g., 'libx264', 'h264')
|
|
514
|
+
- quality: Quality level (int, higher is better)
|
|
515
|
+
- pixelformat: Pixel format (e.g., 'yuv420p')
|
|
516
|
+
- macro_block_size: Macro block size for encoding
|
|
517
|
+
|
|
518
|
+
Returns:
|
|
519
|
+
File metadata dict with id, path, filename, checksum, etc.
|
|
520
|
+
|
|
521
|
+
Raises:
|
|
522
|
+
RuntimeError: If experiment not open or write-protected
|
|
523
|
+
ImportError: If imageio or scikit-image not installed
|
|
524
|
+
ValueError: If frame_stack is empty or invalid format
|
|
525
|
+
|
|
526
|
+
Examples:
|
|
527
|
+
import numpy as np
|
|
528
|
+
|
|
529
|
+
# Grayscale frames (float values 0-1)
|
|
530
|
+
frames = [np.random.rand(480, 640) for _ in range(30)]
|
|
531
|
+
result = experiment.files(prefix="/videos").save_video(frames, "output.mp4")
|
|
532
|
+
|
|
533
|
+
# RGB frames with custom FPS
|
|
534
|
+
frames = [np.random.rand(480, 640, 3) for _ in range(60)]
|
|
535
|
+
result = experiment.files(prefix="/videos").save_video(frames, "output.mp4", fps=30)
|
|
536
|
+
|
|
537
|
+
# Save as GIF
|
|
538
|
+
frames = [np.random.rand(200, 200) for _ in range(20)]
|
|
539
|
+
result = experiment.files(prefix="/videos").save_video(frames, "animation.gif")
|
|
540
|
+
|
|
541
|
+
# With custom codec and quality
|
|
542
|
+
result = experiment.files(prefix="/videos").save_video(
|
|
543
|
+
frames, "output.mp4", fps=30, codec='libx264', quality=8
|
|
544
|
+
)
|
|
545
|
+
"""
|
|
546
|
+
import tempfile
|
|
547
|
+
import os
|
|
548
|
+
|
|
549
|
+
try:
|
|
550
|
+
import imageio.v3 as iio
|
|
551
|
+
except ImportError:
|
|
552
|
+
raise ImportError("imageio is not installed. Install it with: pip install imageio imageio-ffmpeg")
|
|
553
|
+
|
|
554
|
+
try:
|
|
555
|
+
from skimage import img_as_ubyte
|
|
556
|
+
except ImportError:
|
|
557
|
+
raise ImportError("scikit-image is not installed. Install it with: pip install scikit-image")
|
|
558
|
+
|
|
559
|
+
if not self._experiment._is_open:
|
|
560
|
+
raise RuntimeError("Experiment not open. Use experiment.run.start() or context manager.")
|
|
561
|
+
|
|
562
|
+
if self._experiment._write_protected:
|
|
563
|
+
raise RuntimeError("Experiment is write-protected and cannot be modified.")
|
|
564
|
+
|
|
565
|
+
# Validate frame_stack
|
|
566
|
+
try:
|
|
567
|
+
# Handle both list and numpy array
|
|
568
|
+
if len(frame_stack) == 0:
|
|
569
|
+
raise ValueError("frame_stack is empty")
|
|
570
|
+
except TypeError:
|
|
571
|
+
raise ValueError("frame_stack must be a list or numpy array")
|
|
572
|
+
|
|
573
|
+
# Create temporary file with desired filename
|
|
574
|
+
temp_dir = tempfile.mkdtemp()
|
|
575
|
+
temp_path = os.path.join(temp_dir, file_name)
|
|
576
|
+
|
|
577
|
+
try:
|
|
578
|
+
# Convert frames to uint8 format (handles float32/64, grayscale, RGB, etc.)
|
|
579
|
+
# img_as_ubyte automatically scales [0.0, 1.0] floats to [0, 255] uint8
|
|
580
|
+
frames_ubyte = img_as_ubyte(frame_stack)
|
|
581
|
+
|
|
582
|
+
# Encode video to temp file
|
|
583
|
+
try:
|
|
584
|
+
iio.imwrite(temp_path, frames_ubyte, fps=fps, **imageio_kwargs)
|
|
585
|
+
except iio.core.NeedDownloadError:
|
|
586
|
+
# Auto-download FFmpeg if not available
|
|
587
|
+
import imageio.plugins.ffmpeg
|
|
588
|
+
imageio.plugins.ffmpeg.download()
|
|
589
|
+
iio.imwrite(temp_path, frames_ubyte, fps=fps, **imageio_kwargs)
|
|
590
|
+
|
|
591
|
+
# Save using existing save() method
|
|
592
|
+
original_file_path = self._file_path
|
|
593
|
+
self._file_path = temp_path
|
|
594
|
+
|
|
595
|
+
# Upload and get result
|
|
596
|
+
result = self.save()
|
|
597
|
+
|
|
598
|
+
# Restore original file_path
|
|
599
|
+
self._file_path = original_file_path
|
|
600
|
+
|
|
601
|
+
return result
|
|
602
|
+
finally:
|
|
603
|
+
# Clean up temp file and directory
|
|
604
|
+
try:
|
|
605
|
+
os.unlink(temp_path)
|
|
606
|
+
os.rmdir(temp_dir)
|
|
607
|
+
except Exception:
|
|
608
|
+
pass
|
|
609
|
+
|
|
417
610
|
|
|
418
611
|
def compute_sha256(file_path: str) -> str:
|
|
419
612
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ml-dash
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.6
|
|
4
4
|
Summary: ML experiment tracking and data storage
|
|
5
5
|
Keywords: machine-learning,experiment-tracking,mlops,data-storage
|
|
6
6
|
Author: Ge Yang, Tom Tao
|
|
@@ -38,6 +38,9 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
38
38
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
39
39
|
Requires-Dist: httpx>=0.27.0
|
|
40
40
|
Requires-Dist: pyjwt>=2.8.0
|
|
41
|
+
Requires-Dist: imageio>=2.31.0
|
|
42
|
+
Requires-Dist: imageio-ffmpeg>=0.4.9
|
|
43
|
+
Requires-Dist: scikit-image>=0.21.0
|
|
41
44
|
Requires-Dist: pytest>=8.0.0 ; extra == 'dev'
|
|
42
45
|
Requires-Dist: pytest-asyncio>=0.23.0 ; extra == 'dev'
|
|
43
46
|
Requires-Dist: sphinx>=7.2.0 ; extra == 'dev'
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
ml_dash/__init__.py,sha256=o_LrWVJBY_VkUGhSBs5wdb_NqEsHD1AK9HGsjZGxHxQ,1414
|
|
2
2
|
ml_dash/auto_start.py,sha256=c3XcXFpZdvjtWauEoK5043Gw9k0L_5IDq4fdiB2ha88,959
|
|
3
3
|
ml_dash/client.py,sha256=vhWcS5o2n3o4apEjVeLmu7flCEzxBbBOoLSQNcAx_ew,17267
|
|
4
|
-
ml_dash/experiment.py,sha256=
|
|
5
|
-
ml_dash/files.py,sha256=
|
|
4
|
+
ml_dash/experiment.py,sha256=K36HkHJb_O2-vdaPPOCq74_2nZtfiLaS0o7qhTntD8Q,30646
|
|
5
|
+
ml_dash/files.py,sha256=ZY9BwPm_Fym-yg0I3lBtZwi1c7ioKRmhp_86f2u06ww,23014
|
|
6
6
|
ml_dash/log.py,sha256=0yXaNnFwYeBI3tRLHX3kkqWRpg0MbSGwmgjnOfsElCk,5350
|
|
7
7
|
ml_dash/metric.py,sha256=c0Zl0wEufmQuVfwIMvrORLwqe92Iaf0PfKRgmlgQWzQ,10343
|
|
8
8
|
ml_dash/params.py,sha256=xaByDSVar4D1pZqxTANkMPeZTL5-V7ewJe5TXfPLhMQ,5980
|
|
9
9
|
ml_dash/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
ml_dash/storage.py,sha256=8lyT5ZdvhS2nEyrEgMnFAT0LzV5ne1v8tkI3w1PUHJ4,30793
|
|
11
|
-
ml_dash-0.5.
|
|
12
|
-
ml_dash-0.5.
|
|
13
|
-
ml_dash-0.5.
|
|
11
|
+
ml_dash-0.5.6.dist-info/WHEEL,sha256=X16MKk8bp2DRsAuyteHJ-9qOjzmnY0x1aj0P1ftqqWA,78
|
|
12
|
+
ml_dash-0.5.6.dist-info/METADATA,sha256=l2nzY_SRqgzZFeS6_KKHtzgsOSdZ40Mc4XAaGkggEaU,6147
|
|
13
|
+
ml_dash-0.5.6.dist-info/RECORD,,
|
|
File without changes
|