ml-dash 0.5.0__py3-none-any.whl → 0.5.1__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/client.py
CHANGED
|
@@ -35,6 +35,7 @@ class RemoteClient:
|
|
|
35
35
|
name: str,
|
|
36
36
|
description: Optional[str] = None,
|
|
37
37
|
tags: Optional[List[str]] = None,
|
|
38
|
+
bindrs: Optional[List[str]] = None,
|
|
38
39
|
folder: Optional[str] = None,
|
|
39
40
|
write_protected: bool = False,
|
|
40
41
|
metadata: Optional[Dict[str, Any]] = None,
|
|
@@ -47,6 +48,7 @@ class RemoteClient:
|
|
|
47
48
|
name: Experiment name
|
|
48
49
|
description: Optional description
|
|
49
50
|
tags: Optional list of tags
|
|
51
|
+
bindrs: Optional list of bindrs
|
|
50
52
|
folder: Optional folder path
|
|
51
53
|
write_protected: If True, experiment becomes immutable
|
|
52
54
|
metadata: Optional metadata dict
|
|
@@ -65,6 +67,8 @@ class RemoteClient:
|
|
|
65
67
|
payload["description"] = description
|
|
66
68
|
if tags is not None:
|
|
67
69
|
payload["tags"] = tags
|
|
70
|
+
if bindrs is not None:
|
|
71
|
+
payload["bindrs"] = bindrs
|
|
68
72
|
if folder is not None:
|
|
69
73
|
payload["folder"] = folder
|
|
70
74
|
if write_protected:
|
|
@@ -79,6 +83,35 @@ class RemoteClient:
|
|
|
79
83
|
response.raise_for_status()
|
|
80
84
|
return response.json()
|
|
81
85
|
|
|
86
|
+
def update_experiment_status(
|
|
87
|
+
self,
|
|
88
|
+
experiment_id: str,
|
|
89
|
+
status: str,
|
|
90
|
+
) -> Dict[str, Any]:
|
|
91
|
+
"""
|
|
92
|
+
Update experiment status.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
experiment_id: Experiment ID
|
|
96
|
+
status: Status value - "RUNNING" | "COMPLETED" | "FAILED" | "CANCELLED"
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Response dict with updated experiment data
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
httpx.HTTPStatusError: If request fails
|
|
103
|
+
"""
|
|
104
|
+
payload = {
|
|
105
|
+
"status": status,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
response = self._client.patch(
|
|
109
|
+
f"/experiments/{experiment_id}/status",
|
|
110
|
+
json=payload,
|
|
111
|
+
)
|
|
112
|
+
response.raise_for_status()
|
|
113
|
+
return response.json()
|
|
114
|
+
|
|
82
115
|
def create_log_entries(
|
|
83
116
|
self,
|
|
84
117
|
experiment_id: str,
|
ml_dash/experiment.py
CHANGED
|
@@ -65,6 +65,7 @@ class Experiment:
|
|
|
65
65
|
*,
|
|
66
66
|
description: Optional[str] = None,
|
|
67
67
|
tags: Optional[List[str]] = None,
|
|
68
|
+
bindrs: Optional[List[str]] = None,
|
|
68
69
|
folder: Optional[str] = None,
|
|
69
70
|
write_protected: bool = False,
|
|
70
71
|
metadata: Optional[Dict[str, Any]] = None,
|
|
@@ -82,6 +83,7 @@ class Experiment:
|
|
|
82
83
|
project: Project name
|
|
83
84
|
description: Optional experiment description
|
|
84
85
|
tags: Optional list of tags
|
|
86
|
+
bindrs: Optional list of bindrs
|
|
85
87
|
folder: Optional folder path (e.g., "/experiments/baseline")
|
|
86
88
|
write_protected: If True, experiment becomes immutable after creation
|
|
87
89
|
metadata: Optional metadata dict
|
|
@@ -94,6 +96,7 @@ class Experiment:
|
|
|
94
96
|
self.project = project
|
|
95
97
|
self.description = description
|
|
96
98
|
self.tags = tags
|
|
99
|
+
self.bindrs = bindrs
|
|
97
100
|
self.folder = folder
|
|
98
101
|
self.write_protected = write_protected
|
|
99
102
|
self.metadata = metadata
|
|
@@ -185,6 +188,7 @@ class Experiment:
|
|
|
185
188
|
name=self.name,
|
|
186
189
|
description=self.description,
|
|
187
190
|
tags=self.tags,
|
|
191
|
+
bindrs=self.bindrs,
|
|
188
192
|
folder=self.folder,
|
|
189
193
|
write_protected=self.write_protected,
|
|
190
194
|
metadata=self.metadata,
|
|
@@ -199,6 +203,7 @@ class Experiment:
|
|
|
199
203
|
name=self.name,
|
|
200
204
|
description=self.description,
|
|
201
205
|
tags=self.tags,
|
|
206
|
+
bindrs=self.bindrs,
|
|
202
207
|
folder=self.folder,
|
|
203
208
|
metadata=self.metadata,
|
|
204
209
|
)
|
|
@@ -206,8 +211,13 @@ class Experiment:
|
|
|
206
211
|
self._is_open = True
|
|
207
212
|
return self
|
|
208
213
|
|
|
209
|
-
def close(self):
|
|
210
|
-
"""
|
|
214
|
+
def close(self, status: str = "COMPLETED"):
|
|
215
|
+
"""
|
|
216
|
+
Close the experiment and update status.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
status: Status to set - "COMPLETED" (default), "FAILED", or "CANCELLED"
|
|
220
|
+
"""
|
|
211
221
|
if not self._is_open:
|
|
212
222
|
return
|
|
213
223
|
|
|
@@ -215,6 +225,17 @@ class Experiment:
|
|
|
215
225
|
if self._storage:
|
|
216
226
|
self._storage.flush()
|
|
217
227
|
|
|
228
|
+
# Update experiment status in remote mode
|
|
229
|
+
if self._client and self._experiment_id:
|
|
230
|
+
try:
|
|
231
|
+
self._client.update_experiment_status(
|
|
232
|
+
experiment_id=self._experiment_id,
|
|
233
|
+
status=status
|
|
234
|
+
)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
# Log error but don't fail the close operation
|
|
237
|
+
print(f"Warning: Failed to update experiment status: {e}")
|
|
238
|
+
|
|
218
239
|
self._is_open = False
|
|
219
240
|
|
|
220
241
|
def __enter__(self) -> "Experiment":
|
|
@@ -222,8 +243,10 @@ class Experiment:
|
|
|
222
243
|
return self.open()
|
|
223
244
|
|
|
224
245
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
225
|
-
"""Context manager exit."""
|
|
226
|
-
|
|
246
|
+
"""Context manager exit. Sets status to FAILED if exception occurred."""
|
|
247
|
+
# Determine status based on whether an exception occurred
|
|
248
|
+
status = "FAILED" if exc_type is not None else "COMPLETED"
|
|
249
|
+
self.close(status=status)
|
|
227
250
|
return False
|
|
228
251
|
|
|
229
252
|
def log(
|
ml_dash/storage.py
CHANGED
|
@@ -43,6 +43,7 @@ class LocalStorage:
|
|
|
43
43
|
name: str,
|
|
44
44
|
description: Optional[str] = None,
|
|
45
45
|
tags: Optional[List[str]] = None,
|
|
46
|
+
bindrs: Optional[List[str]] = None,
|
|
46
47
|
folder: Optional[str] = None,
|
|
47
48
|
metadata: Optional[Dict[str, Any]] = None,
|
|
48
49
|
) -> Path:
|
|
@@ -54,6 +55,7 @@ class LocalStorage:
|
|
|
54
55
|
name: Experiment name
|
|
55
56
|
description: Optional description
|
|
56
57
|
tags: Optional tags
|
|
58
|
+
bindrs: Optional bindrs
|
|
57
59
|
folder: Optional folder path (used for organization)
|
|
58
60
|
metadata: Optional metadata
|
|
59
61
|
|
|
@@ -79,6 +81,7 @@ class LocalStorage:
|
|
|
79
81
|
"project": project,
|
|
80
82
|
"description": description,
|
|
81
83
|
"tags": tags or [],
|
|
84
|
+
"bindrs": bindrs or [],
|
|
82
85
|
"folder": folder,
|
|
83
86
|
"metadata": metadata,
|
|
84
87
|
"created_at": datetime.utcnow().isoformat() + "Z",
|
|
@@ -99,6 +102,8 @@ class LocalStorage:
|
|
|
99
102
|
existing["description"] = description
|
|
100
103
|
if tags is not None:
|
|
101
104
|
existing["tags"] = tags
|
|
105
|
+
if bindrs is not None:
|
|
106
|
+
existing["bindrs"] = bindrs
|
|
102
107
|
if folder is not None:
|
|
103
108
|
existing["folder"] = folder
|
|
104
109
|
if metadata is not None:
|
|
@@ -288,7 +293,7 @@ class LocalStorage:
|
|
|
288
293
|
"""
|
|
289
294
|
Write file to local storage.
|
|
290
295
|
|
|
291
|
-
Copies file to: files/<file_id>/<filename>
|
|
296
|
+
Copies file to: files/<prefix>/<file_id>/<filename>
|
|
292
297
|
Updates .files_metadata.json with file metadata
|
|
293
298
|
|
|
294
299
|
Args:
|
|
@@ -317,8 +322,14 @@ class LocalStorage:
|
|
|
317
322
|
# Generate Snowflake ID for file
|
|
318
323
|
file_id = generate_snowflake_id()
|
|
319
324
|
|
|
320
|
-
#
|
|
321
|
-
|
|
325
|
+
# Normalize prefix (remove leading slashes to avoid absolute paths)
|
|
326
|
+
normalized_prefix = prefix.lstrip("/") if prefix else ""
|
|
327
|
+
|
|
328
|
+
# Create prefix directory, then file directory
|
|
329
|
+
prefix_dir = files_dir / normalized_prefix if normalized_prefix else files_dir
|
|
330
|
+
prefix_dir.mkdir(parents=True, exist_ok=True)
|
|
331
|
+
|
|
332
|
+
file_dir = prefix_dir / file_id
|
|
322
333
|
file_dir.mkdir(parents=True, exist_ok=True)
|
|
323
334
|
|
|
324
335
|
# Copy file
|
|
@@ -363,7 +374,11 @@ class LocalStorage:
|
|
|
363
374
|
if existing_index is not None:
|
|
364
375
|
# Overwrite: remove old file and update metadata
|
|
365
376
|
old_file = files_metadata["files"][existing_index]
|
|
366
|
-
|
|
377
|
+
old_prefix = old_file["path"].lstrip("/") if old_file["path"] else ""
|
|
378
|
+
if old_prefix:
|
|
379
|
+
old_file_dir = files_dir / old_prefix / old_file["id"]
|
|
380
|
+
else:
|
|
381
|
+
old_file_dir = files_dir / old_file["id"]
|
|
367
382
|
if old_file_dir.exists():
|
|
368
383
|
shutil.rmtree(old_file_dir)
|
|
369
384
|
files_metadata["files"][existing_index] = file_metadata
|
|
@@ -470,7 +485,11 @@ class LocalStorage:
|
|
|
470
485
|
raise FileNotFoundError(f"File {file_id} not found")
|
|
471
486
|
|
|
472
487
|
# Get source file
|
|
473
|
-
|
|
488
|
+
file_prefix = file_metadata["path"].lstrip("/") if file_metadata["path"] else ""
|
|
489
|
+
if file_prefix:
|
|
490
|
+
source_file = files_dir / file_prefix / file_id / file_metadata["filename"]
|
|
491
|
+
else:
|
|
492
|
+
source_file = files_dir / file_id / file_metadata["filename"]
|
|
474
493
|
if not source_file.exists():
|
|
475
494
|
raise FileNotFoundError(f"File {file_id} not found on disk")
|
|
476
495
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ml-dash
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: ML experiment metricing and data storage
|
|
5
5
|
Keywords: machine-learning,experiment-metricing,mlops,data-storage
|
|
6
6
|
Author: Ge Yang, Tom Tao
|
|
@@ -67,6 +67,9 @@ A simple and flexible SDK for ML experiment metricing and data storage.
|
|
|
67
67
|
- **Dual Operation Modes**: Remote (API server) or local (filesystem)
|
|
68
68
|
- **Auto-creation**: Automatically creates namespace, project, and folder hierarchy
|
|
69
69
|
- **Upsert Behavior**: Updates existing experiments or creates new ones
|
|
70
|
+
- **Experiment Lifecycle**: Automatic status tracking (RUNNING, COMPLETED, FAILED, CANCELLED)
|
|
71
|
+
- **Organized File Storage**: Prefix-based file organization with unique snowflake IDs
|
|
72
|
+
- **Rich Metadata**: Tags, bindrs, descriptions, and custom metadata support
|
|
70
73
|
- **Simple API**: Minimal configuration, maximum flexibility
|
|
71
74
|
|
|
72
75
|
## Installation
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
ml_dash/__init__.py,sha256=5tT0Lmf0SS3J7BOwJGVai8FOjdpjKGBJCEYL5nXnkLA,1384
|
|
2
|
-
ml_dash/client.py,sha256=
|
|
3
|
-
ml_dash/experiment.py,sha256=
|
|
2
|
+
ml_dash/client.py,sha256=vhWcS5o2n3o4apEjVeLmu7flCEzxBbBOoLSQNcAx_ew,17267
|
|
3
|
+
ml_dash/experiment.py,sha256=x1jtQD1QroNjNULOxZiGtX5oFLi3ZXDaFbGHWt0yMJU,28652
|
|
4
4
|
ml_dash/files.py,sha256=WKWbcug6XADwZruYQio1EdSstmfTsty9-2-t2KPWz38,9719
|
|
5
5
|
ml_dash/log.py,sha256=0yXaNnFwYeBI3tRLHX3kkqWRpg0MbSGwmgjnOfsElCk,5350
|
|
6
6
|
ml_dash/metric.py,sha256=PcEd6_HTLDpf-kBIDeQq2LlTRAS7xDx6EvSBpin5iuY,6456
|
|
7
7
|
ml_dash/params.py,sha256=W-JkY1Mz7KdmvDjQ0HFV2QnpBov7Gf4dl70fuBnXTdo,5974
|
|
8
8
|
ml_dash/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
ml_dash/storage.py,sha256=
|
|
10
|
-
ml_dash-0.5.
|
|
11
|
-
ml_dash-0.5.
|
|
12
|
-
ml_dash-0.5.
|
|
9
|
+
ml_dash/storage.py,sha256=UTuux2nfclLrrtlkC6TsOvDB_wIbSDvYGg8Gtbvk6mc,30471
|
|
10
|
+
ml_dash-0.5.1.dist-info/WHEEL,sha256=X16MKk8bp2DRsAuyteHJ-9qOjzmnY0x1aj0P1ftqqWA,78
|
|
11
|
+
ml_dash-0.5.1.dist-info/METADATA,sha256=TBTqi4lJNoFO2eyztYJZptQypUSVTDVUdbGS0EnQZ2k,6067
|
|
12
|
+
ml_dash-0.5.1.dist-info/RECORD,,
|
|
File without changes
|