ml-dash 0.6.2__py3-none-any.whl → 0.6.2rc1__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/__init__.py +64 -36
- ml_dash/auth/token_storage.py +226 -267
- ml_dash/auto_start.py +15 -28
- ml_dash/cli.py +2 -16
- ml_dash/cli_commands/download.py +667 -757
- ml_dash/cli_commands/list.py +13 -146
- ml_dash/cli_commands/login.py +183 -190
- ml_dash/cli_commands/upload.py +1141 -1291
- ml_dash/client.py +6 -79
- ml_dash/config.py +119 -119
- ml_dash/experiment.py +1034 -1234
- ml_dash/files.py +224 -339
- ml_dash/log.py +7 -7
- ml_dash/metric.py +100 -359
- ml_dash/params.py +6 -6
- ml_dash/remote_auto_start.py +17 -20
- ml_dash/run.py +65 -211
- ml_dash/storage.py +1081 -1051
- {ml_dash-0.6.2.dist-info → ml_dash-0.6.2rc1.dist-info}/METADATA +14 -12
- ml_dash-0.6.2rc1.dist-info/RECORD +30 -0
- {ml_dash-0.6.2.dist-info → ml_dash-0.6.2rc1.dist-info}/WHEEL +1 -1
- ml_dash/cli_commands/api.py +0 -165
- ml_dash/cli_commands/profile.py +0 -92
- ml_dash/snowflake.py +0 -173
- ml_dash-0.6.2.dist-info/RECORD +0 -33
- {ml_dash-0.6.2.dist-info → ml_dash-0.6.2rc1.dist-info}/entry_points.txt +0 -0
ml_dash/client.py
CHANGED
|
@@ -84,7 +84,7 @@ class RemoteClient:
|
|
|
84
84
|
description: Optional[str] = None,
|
|
85
85
|
tags: Optional[List[str]] = None,
|
|
86
86
|
bindrs: Optional[List[str]] = None,
|
|
87
|
-
|
|
87
|
+
folder: Optional[str] = None,
|
|
88
88
|
write_protected: bool = False,
|
|
89
89
|
metadata: Optional[Dict[str, Any]] = None,
|
|
90
90
|
) -> Dict[str, Any]:
|
|
@@ -93,16 +93,16 @@ class RemoteClient:
|
|
|
93
93
|
|
|
94
94
|
Args:
|
|
95
95
|
project: Project name
|
|
96
|
-
name: Experiment name
|
|
96
|
+
name: Experiment name
|
|
97
97
|
description: Optional description
|
|
98
98
|
tags: Optional list of tags
|
|
99
99
|
bindrs: Optional list of bindrs
|
|
100
|
-
|
|
100
|
+
folder: Optional folder path
|
|
101
101
|
write_protected: If True, experiment becomes immutable
|
|
102
102
|
metadata: Optional metadata dict
|
|
103
103
|
|
|
104
104
|
Returns:
|
|
105
|
-
Response dict with experiment, project, and namespace data
|
|
105
|
+
Response dict with experiment, project, folder, and namespace data
|
|
106
106
|
|
|
107
107
|
Raises:
|
|
108
108
|
httpx.HTTPStatusError: If request fails
|
|
@@ -117,12 +117,12 @@ class RemoteClient:
|
|
|
117
117
|
payload["tags"] = tags
|
|
118
118
|
if bindrs is not None:
|
|
119
119
|
payload["bindrs"] = bindrs
|
|
120
|
+
if folder is not None:
|
|
121
|
+
payload["folder"] = folder
|
|
120
122
|
if write_protected:
|
|
121
123
|
payload["writeProtected"] = write_protected
|
|
122
124
|
if metadata is not None:
|
|
123
125
|
payload["metadata"] = metadata
|
|
124
|
-
if prefix is not None:
|
|
125
|
-
payload["prefix"] = prefix
|
|
126
126
|
|
|
127
127
|
response = self._client.post(
|
|
128
128
|
f"/projects/{project}/experiments",
|
|
@@ -728,9 +728,6 @@ class RemoteClient:
|
|
|
728
728
|
metadata
|
|
729
729
|
project {
|
|
730
730
|
slug
|
|
731
|
-
namespace {
|
|
732
|
-
slug
|
|
733
|
-
}
|
|
734
731
|
}
|
|
735
732
|
logMetadata {
|
|
736
733
|
totalLogs
|
|
@@ -795,9 +792,6 @@ class RemoteClient:
|
|
|
795
792
|
metadata
|
|
796
793
|
project {
|
|
797
794
|
slug
|
|
798
|
-
namespace {
|
|
799
|
-
slug
|
|
800
|
-
}
|
|
801
795
|
}
|
|
802
796
|
logMetadata {
|
|
803
797
|
totalLogs
|
|
@@ -834,73 +828,6 @@ class RemoteClient:
|
|
|
834
828
|
result = self.graphql_query(query, variables)
|
|
835
829
|
return result.get("experiment")
|
|
836
830
|
|
|
837
|
-
def search_experiments_graphql(self, pattern: str) -> List[Dict[str, Any]]:
|
|
838
|
-
"""
|
|
839
|
-
Search experiments using glob pattern via GraphQL.
|
|
840
|
-
|
|
841
|
-
Pattern format: namespace/project/experiment
|
|
842
|
-
Supports wildcards: *, ?, [0-9], [a-z], etc.
|
|
843
|
-
|
|
844
|
-
Args:
|
|
845
|
-
pattern: Glob pattern (e.g., "tom*/tutorials/*", "*/project-?/exp*")
|
|
846
|
-
|
|
847
|
-
Returns:
|
|
848
|
-
List of experiment dicts matching the pattern
|
|
849
|
-
|
|
850
|
-
Raises:
|
|
851
|
-
httpx.HTTPStatusError: If request fails
|
|
852
|
-
|
|
853
|
-
Examples:
|
|
854
|
-
>>> client.search_experiments_graphql("tom*/tutorials/*")
|
|
855
|
-
>>> client.search_experiments_graphql("*/my-project/baseline*")
|
|
856
|
-
"""
|
|
857
|
-
query = """
|
|
858
|
-
query SearchExperiments($pattern: String!) {
|
|
859
|
-
searchExperiments(pattern: $pattern) {
|
|
860
|
-
id
|
|
861
|
-
name
|
|
862
|
-
description
|
|
863
|
-
tags
|
|
864
|
-
status
|
|
865
|
-
startedAt
|
|
866
|
-
endedAt
|
|
867
|
-
metadata
|
|
868
|
-
project {
|
|
869
|
-
id
|
|
870
|
-
slug
|
|
871
|
-
name
|
|
872
|
-
namespace {
|
|
873
|
-
id
|
|
874
|
-
slug
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
logMetadata {
|
|
878
|
-
totalLogs
|
|
879
|
-
}
|
|
880
|
-
metrics {
|
|
881
|
-
name
|
|
882
|
-
metricMetadata {
|
|
883
|
-
totalDataPoints
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
files {
|
|
887
|
-
id
|
|
888
|
-
filename
|
|
889
|
-
path
|
|
890
|
-
contentType
|
|
891
|
-
sizeBytes
|
|
892
|
-
checksum
|
|
893
|
-
description
|
|
894
|
-
tags
|
|
895
|
-
metadata
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
"""
|
|
900
|
-
variables = {"pattern": pattern}
|
|
901
|
-
result = self.graphql_query(query, variables)
|
|
902
|
-
return result.get("searchExperiments", [])
|
|
903
|
-
|
|
904
831
|
def download_file_streaming(
|
|
905
832
|
self, experiment_id: str, file_id: str, dest_path: str
|
|
906
833
|
) -> str:
|
ml_dash/config.py
CHANGED
|
@@ -1,132 +1,132 @@
|
|
|
1
1
|
"""Configuration file management for ML-Dash CLI."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
from pathlib import Path
|
|
5
|
-
|
|
4
|
+
import json
|
|
5
|
+
from typing import Optional, Dict, Any
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Config:
|
|
9
|
-
"""
|
|
10
|
-
Manages ML-Dash CLI configuration file.
|
|
11
|
-
|
|
12
|
-
Configuration is stored in ~/.dash/config.json with structure:
|
|
13
|
-
{
|
|
14
|
-
"remote_url": "https://api.dash.ml",
|
|
15
|
-
"api_key": "token",
|
|
16
|
-
"default_batch_size": 100
|
|
17
|
-
}
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
DEFAULT_CONFIG_DIR = Path.home() / ".dash"
|
|
21
|
-
CONFIG_FILE = "config.json"
|
|
22
|
-
|
|
23
|
-
def __init__(self, config_dir: Optional[Path] = None):
|
|
24
|
-
"""
|
|
25
|
-
Initialize config manager.
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
config_dir: Config directory path (defaults to ~/.dash)
|
|
29
|
-
"""
|
|
30
|
-
self.config_dir = config_dir or self.DEFAULT_CONFIG_DIR
|
|
31
|
-
self.config_path = self.config_dir / self.CONFIG_FILE
|
|
32
|
-
self._data = self._load()
|
|
33
|
-
|
|
34
|
-
def _load(self) -> Dict[str, Any]:
|
|
35
|
-
"""Load config from file."""
|
|
36
|
-
if self.config_path.exists():
|
|
37
|
-
try:
|
|
38
|
-
with open(self.config_path, "r") as f:
|
|
39
|
-
return json.load(f)
|
|
40
|
-
except (json.JSONDecodeError, IOError):
|
|
41
|
-
# If config is corrupted, return empty dict
|
|
42
|
-
return {}
|
|
43
|
-
return {}
|
|
44
|
-
|
|
45
|
-
def save(self):
|
|
46
|
-
"""Save config to file."""
|
|
47
|
-
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
48
|
-
with open(self.config_path, "w") as f:
|
|
49
|
-
json.dump(self._data, f, indent=2)
|
|
50
|
-
|
|
51
|
-
def get(self, key: str, default: Any = None) -> Any:
|
|
52
|
-
"""
|
|
53
|
-
Get config value.
|
|
54
|
-
|
|
55
|
-
Args:
|
|
56
|
-
key: Config key
|
|
57
|
-
default: Default value if key not found
|
|
58
|
-
|
|
59
|
-
Returns:
|
|
60
|
-
Config value or default
|
|
61
|
-
"""
|
|
62
|
-
return self._data.get(key, default)
|
|
63
|
-
|
|
64
|
-
def set(self, key: str, value: Any):
|
|
65
9
|
"""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
10
|
+
Manages ML-Dash CLI configuration file.
|
|
11
|
+
|
|
12
|
+
Configuration is stored in ~/.ml-dash/config.json with structure:
|
|
13
|
+
{
|
|
14
|
+
"remote_url": "https://api.dash.ml",
|
|
15
|
+
"api_key": "token",
|
|
16
|
+
"default_batch_size": 100
|
|
17
|
+
}
|
|
71
18
|
"""
|
|
72
|
-
self._data[key] = value
|
|
73
|
-
self.save()
|
|
74
19
|
|
|
75
|
-
|
|
76
|
-
""
|
|
77
|
-
|
|
20
|
+
DEFAULT_CONFIG_DIR = Path.home() / ".ml-dash"
|
|
21
|
+
CONFIG_FILE = "config.json"
|
|
22
|
+
|
|
23
|
+
def __init__(self, config_dir: Optional[Path] = None):
|
|
24
|
+
"""
|
|
25
|
+
Initialize config manager.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
config_dir: Config directory path (defaults to ~/.ml-dash)
|
|
29
|
+
"""
|
|
30
|
+
self.config_dir = config_dir or self.DEFAULT_CONFIG_DIR
|
|
31
|
+
self.config_path = self.config_dir / self.CONFIG_FILE
|
|
32
|
+
self._data = self._load()
|
|
33
|
+
|
|
34
|
+
def _load(self) -> Dict[str, Any]:
|
|
35
|
+
"""Load config from file."""
|
|
36
|
+
if self.config_path.exists():
|
|
37
|
+
try:
|
|
38
|
+
with open(self.config_path, "r") as f:
|
|
39
|
+
return json.load(f)
|
|
40
|
+
except (json.JSONDecodeError, IOError):
|
|
41
|
+
# If config is corrupted, return empty dict
|
|
42
|
+
return {}
|
|
43
|
+
return {}
|
|
78
44
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
self
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
45
|
+
def save(self):
|
|
46
|
+
"""Save config to file."""
|
|
47
|
+
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
48
|
+
with open(self.config_path, "w") as f:
|
|
49
|
+
json.dump(self._data, f, indent=2)
|
|
50
|
+
|
|
51
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
52
|
+
"""
|
|
53
|
+
Get config value.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
key: Config key
|
|
57
|
+
default: Default value if key not found
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Config value or default
|
|
61
|
+
"""
|
|
62
|
+
return self._data.get(key, default)
|
|
63
|
+
|
|
64
|
+
def set(self, key: str, value: Any):
|
|
65
|
+
"""
|
|
66
|
+
Set config value and save.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
key: Config key
|
|
70
|
+
value: Config value
|
|
71
|
+
"""
|
|
72
|
+
self._data[key] = value
|
|
73
|
+
self.save()
|
|
74
|
+
|
|
75
|
+
def delete(self, key: str):
|
|
76
|
+
"""
|
|
77
|
+
Delete config key and save.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
key: Config key to delete
|
|
81
|
+
"""
|
|
82
|
+
if key in self._data:
|
|
83
|
+
del self._data[key]
|
|
84
|
+
self.save()
|
|
85
|
+
|
|
86
|
+
def clear(self):
|
|
87
|
+
"""Clear all config and save."""
|
|
88
|
+
self._data = {}
|
|
89
|
+
self.save()
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def remote_url(self) -> Optional[str]:
|
|
93
|
+
"""Get default remote URL."""
|
|
94
|
+
return self.get("remote_url", "https://api.dash.ml")
|
|
95
|
+
|
|
96
|
+
@remote_url.setter
|
|
97
|
+
def remote_url(self, url: str):
|
|
98
|
+
"""Set default remote URL."""
|
|
99
|
+
self.set("remote_url", url)
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def api_key(self) -> Optional[str]:
|
|
103
|
+
"""Get default API key."""
|
|
104
|
+
return self.get("api_key")
|
|
105
|
+
|
|
106
|
+
@api_key.setter
|
|
107
|
+
def api_key(self, key: str):
|
|
108
|
+
"""Set default API key."""
|
|
109
|
+
self.set("api_key", key)
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def batch_size(self) -> int:
|
|
113
|
+
"""Get default batch size for uploads."""
|
|
114
|
+
return self.get("default_batch_size", 100)
|
|
115
|
+
|
|
116
|
+
@batch_size.setter
|
|
117
|
+
def batch_size(self, size: int):
|
|
118
|
+
"""Set default batch size."""
|
|
119
|
+
self.set("default_batch_size", size)
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def device_secret(self) -> Optional[str]:
|
|
123
|
+
"""Get device secret for OAuth device flow."""
|
|
124
|
+
return self.get("device_secret")
|
|
125
|
+
|
|
126
|
+
@device_secret.setter
|
|
127
|
+
def device_secret(self, secret: str):
|
|
128
|
+
"""Set device secret."""
|
|
129
|
+
self.set("device_secret", secret)
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
# Global config instance
|