ml-dash 0.6.7__py3-none-any.whl → 0.6.9__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.
@@ -1,6 +1,7 @@
1
1
  """Profile command for ml-dash CLI - shows current user and configuration."""
2
2
 
3
3
  import json
4
+ import time
4
5
 
5
6
  from rich.console import Console
6
7
  from rich.panel import Panel
@@ -22,6 +23,83 @@ def add_parser(subparsers):
22
23
  action="store_true",
23
24
  help="Output as JSON",
24
25
  )
26
+ parser.add_argument(
27
+ "--refresh",
28
+ action="store_true",
29
+ help="Fetch fresh profile from server (not from cached token)",
30
+ )
31
+
32
+
33
+ def _fetch_fresh_profile(remote_url: str, token: str) -> dict:
34
+ """Fetch fresh user profile from the API server.
35
+
36
+ Args:
37
+ remote_url: API server URL
38
+ token: JWT authentication token
39
+
40
+ Returns:
41
+ User profile dict with username, email, name, etc.
42
+ """
43
+ try:
44
+ from ml_dash.client import RemoteClient
45
+
46
+ client = RemoteClient(remote_url, api_key=token)
47
+
48
+ # Query for full user profile
49
+ query = """
50
+ query GetUserProfile {
51
+ me {
52
+ id
53
+ username
54
+ name
55
+ email
56
+ }
57
+ }
58
+ """
59
+
60
+ result = client.graphql_query(query)
61
+ me = result.get("me", {})
62
+
63
+ if me:
64
+ return {
65
+ "sub": me.get("id"),
66
+ "username": me.get("username"),
67
+ "name": me.get("name"),
68
+ "email": me.get("email"),
69
+ }
70
+ except Exception as e:
71
+ # If API call fails, return None to fall back to token decoding
72
+ return None
73
+
74
+ return None
75
+
76
+
77
+ def _check_token_expiration(token_payload: dict) -> tuple[bool, str]:
78
+ """Check if token is expired or close to expiring.
79
+
80
+ Args:
81
+ token_payload: Decoded JWT payload
82
+
83
+ Returns:
84
+ Tuple of (is_expired, message)
85
+ """
86
+ exp = token_payload.get("exp")
87
+ if not exp:
88
+ return False, None
89
+
90
+ current_time = int(time.time())
91
+ time_left = exp - current_time
92
+
93
+ if time_left < 0:
94
+ return True, "[red]Token expired[/red]"
95
+ elif time_left < 86400: # Less than 1 day
96
+ hours_left = time_left // 3600
97
+ return False, f"[yellow]Token expires in {hours_left} hours[/yellow]"
98
+ else:
99
+ days_left = time_left // 86400
100
+ return False, f"Expires in {days_left} days"
101
+
102
+ return False, None
25
103
 
26
104
 
27
105
  def cmd_profile(args) -> int:
@@ -42,7 +120,33 @@ def cmd_profile(args) -> int:
42
120
 
43
121
  if token:
44
122
  info["authenticated"] = True
45
- info["user"] = decode_jwt_payload(token)
123
+
124
+ # Decode token payload for initial data and expiration check
125
+ token_payload = decode_jwt_payload(token)
126
+
127
+ # Check token expiration
128
+ is_expired, expiry_message = _check_token_expiration(token_payload)
129
+
130
+ if is_expired:
131
+ info["authenticated"] = False
132
+ info["error"] = "Token expired. Please run 'ml-dash login' to re-authenticate."
133
+ else:
134
+ # Fetch fresh profile from server if requested, or fall back to token
135
+ if args.refresh:
136
+ fresh_profile = _fetch_fresh_profile(config.remote_url, token)
137
+ if fresh_profile:
138
+ info["user"] = fresh_profile
139
+ info["source"] = "server"
140
+ else:
141
+ info["user"] = token_payload
142
+ info["source"] = "token"
143
+ info["warning"] = "Could not fetch fresh profile from server, using cached token data"
144
+ else:
145
+ info["user"] = token_payload
146
+ info["source"] = "token"
147
+
148
+ if expiry_message:
149
+ info["token_status"] = expiry_message
46
150
 
47
151
  if args.json:
48
152
  console.print_json(json.dumps(info))
@@ -50,10 +154,11 @@ def cmd_profile(args) -> int:
50
154
 
51
155
  # Rich display
52
156
  if not info["authenticated"]:
157
+ error_msg = info.get("error", "Not authenticated")
53
158
  console.print(
54
159
  Panel(
55
160
  f"[bold cyan]OS Username:[/bold cyan] {info.get('local_user')}\n\n"
56
- "[yellow]Not authenticated[/yellow]\n\n"
161
+ f"[yellow]{error_msg}[/yellow]\n\n"
57
162
  "Run [cyan]ml-dash login[/cyan] to authenticate.",
58
163
  title="[bold]ML-Dash Info[/bold]",
59
164
  border_style="yellow",
@@ -66,7 +171,6 @@ def cmd_profile(args) -> int:
66
171
  table.add_column("Key", style="bold cyan")
67
172
  table.add_column("Value")
68
173
 
69
- # table.add_row("OS Username", info.get("local_user"))
70
174
  user = info.get("user", {})
71
175
  if user.get("username"):
72
176
  table.add_row("Username", user["username"])
@@ -78,12 +182,43 @@ def cmd_profile(args) -> int:
78
182
  if user.get("email"):
79
183
  table.add_row("Email", user["email"])
80
184
  table.add_row("Remote", info.get("remote_url") or "https://api.dash.ml")
81
- if info.get("token_expires"):
82
- table.add_row("Token Expires", info["token_expires"])
185
+
186
+ # Show token status (expiration)
187
+ if info.get("token_status"):
188
+ table.add_row("Token Status", info["token_status"])
189
+
190
+ # Show data source
191
+ source = info.get("source", "token")
192
+ if source == "server":
193
+ table.add_row("Data Source", "[green]Server (Fresh)[/green]")
194
+ else:
195
+ table.add_row("Data Source", "[yellow]Token (Cached)[/yellow]")
196
+
197
+ # Show warning if any
198
+ warning_text = None
199
+ if info.get("warning"):
200
+ warning_text = f"\n[yellow]⚠ {info['warning']}[/yellow]"
201
+
202
+ # Show tip for refreshing
203
+ if source == "token":
204
+ tip_text = "\n[dim]Tip: Use --refresh to fetch fresh data from server[/dim]"
205
+ else:
206
+ tip_text = None
207
+
208
+ # Build panel content
209
+ panel_content = table
210
+ if warning_text or tip_text:
211
+ from rich.console import Group
212
+ items = [table]
213
+ if warning_text:
214
+ items.append(warning_text)
215
+ if tip_text:
216
+ items.append(tip_text)
217
+ panel_content = Group(*items)
83
218
 
84
219
  console.print(
85
220
  Panel(
86
- table,
221
+ panel_content,
87
222
  title="[bold green]✓ Authenticated[/bold green]",
88
223
  border_style="green",
89
224
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ml-dash
3
- Version: 0.6.7
3
+ Version: 0.6.9
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
@@ -68,10 +68,11 @@ Description-Content-Type: text/markdown
68
68
 
69
69
  # ML-Dash
70
70
 
71
- A simple and flexible SDK for ML experiment tracking and data storage.
71
+ A simple and flexible SDK for ML experiment tracking and data storage with background buffering for high-performance training.
72
72
 
73
73
  ## Features
74
74
 
75
+ ### Core Features
75
76
  - **Three Usage Styles**: Pre-configured singleton (dxp), context manager, or direct instantiation
76
77
  - **Dual Operation Modes**: Remote (API server) or local (filesystem)
77
78
  - **OAuth2 Authentication**: Secure device flow authentication for CLI and SDK
@@ -82,6 +83,13 @@ A simple and flexible SDK for ML experiment tracking and data storage.
82
83
  - **Rich Metadata**: Tags, bindrs, descriptions, and custom metadata support
83
84
  - **Simple API**: Minimal configuration, maximum flexibility
84
85
 
86
+ ### Performance Features (New in 0.6.7)
87
+ - **Background Buffering**: Non-blocking I/O operations eliminate training interruptions
88
+ - **Automatic Batching**: Time-based (5s) and size-based (100 items) flush triggers
89
+ - **Track API**: Time-series data tracking for robotics, RL, and sequential experiments
90
+ - **Numpy Image Support**: Direct saving of numpy arrays as PNG/JPEG images
91
+ - **Parallel Uploads**: ThreadPoolExecutor for efficient file uploads
92
+
85
93
  ## Installation
86
94
 
87
95
  <table>
@@ -93,14 +101,14 @@ A simple and flexible SDK for ML experiment tracking and data storage.
93
101
  <td>
94
102
 
95
103
  ```bash
96
- uv add ml-dash==0.6.2rc1
104
+ uv add ml-dash
97
105
  ```
98
106
 
99
107
  </td>
100
108
  <td>
101
109
 
102
110
  ```bash
103
- pip install ml-dash==0.6.2rc1
111
+ pip install ml-dash
104
112
  ```
105
113
 
106
114
  </td>
@@ -159,7 +167,75 @@ with Experiment(
159
167
 
160
168
  ```
161
169
 
162
- See [docs/getting-started.md](docs/getting-started.md) for more examples.
170
+ ## New Features in 0.6.7
171
+
172
+ ### 🚀 Background Buffering (Non-blocking I/O)
173
+
174
+ All write operations are now buffered and executed in background threads:
175
+
176
+ ```python
177
+ with Experiment("my-project/exp").run as experiment:
178
+ for i in range(10000):
179
+ # Non-blocking! Returns immediately
180
+ experiment.log(f"Step {i}")
181
+ experiment.metrics("train").log(loss=loss, accuracy=acc)
182
+ experiment.files("frames").save_image(frame, to=f"frame_{i}.jpg")
183
+
184
+ # All data automatically flushed when context exits
185
+ ```
186
+
187
+ Configure buffering via environment variables:
188
+ ```bash
189
+ export ML_DASH_BUFFER_ENABLED=true
190
+ export ML_DASH_FLUSH_INTERVAL=5.0
191
+ export ML_DASH_LOG_BATCH_SIZE=100
192
+ ```
193
+
194
+ ### 📊 Track API (Time-Series Data)
195
+
196
+ Perfect for robotics, RL, and sequential experiments:
197
+
198
+ ```python
199
+ with Experiment("robotics/training").run as experiment:
200
+ for step in range(1000):
201
+ # Track robot position over time
202
+ experiment.track("robot/position").append({
203
+ "step": step,
204
+ "x": position[0],
205
+ "y": position[1],
206
+ "z": position[2]
207
+ })
208
+
209
+ # Track control signals
210
+ experiment.track("robot/control").append({
211
+ "step": step,
212
+ "motor1": ctrl[0],
213
+ "motor2": ctrl[1]
214
+ })
215
+ ```
216
+
217
+ ### 🖼️ Numpy Image Support
218
+
219
+ Save numpy arrays directly as images (PNG/JPEG):
220
+
221
+ ```python
222
+ import numpy as np
223
+
224
+ with Experiment("vision/training").run as experiment:
225
+ # From MuJoCo, OpenCV, PIL, etc.
226
+ pixels = renderer.render() # numpy array
227
+
228
+ # Save as PNG (lossless)
229
+ experiment.files("frames").save_image(pixels, to="frame.png")
230
+
231
+ # Save as JPEG with quality control
232
+ experiment.files("frames").save_image(pixels, to="frame.jpg", quality=85)
233
+
234
+ # Auto-detection also works
235
+ experiment.files("frames").save(pixels, to="frame.jpg")
236
+ ```
237
+
238
+ See [CHANGELOG.md](CHANGELOG.md) for complete release notes.
163
239
 
164
240
  ## Development Setup
165
241
 
@@ -15,7 +15,7 @@ ml_dash/cli_commands/download.py,sha256=Jw-ZeVH8SL9t2yRNCwSmQ0qUOzI4_iWGWt7SwAfM
15
15
  ml_dash/cli_commands/list.py,sha256=H442wOAcWYtDwq6BS7lpZbkKhqfTXBCHGctbw8zT1Zw,20841
16
16
  ml_dash/cli_commands/login.py,sha256=zX-urtUrfzg2qOGtKNYQgj6UloN9kzj4zEO6h_xwuNs,6782
17
17
  ml_dash/cli_commands/logout.py,sha256=lTUUNyRXqvo61qNkCd4KBrPUujDAHnNqsHkU6bHie0U,1332
18
- ml_dash/cli_commands/profile.py,sha256=BaSM6BAN3YM4tw95iKV_nypKZxwsB3PoAAejQcYip5E,2351
18
+ ml_dash/cli_commands/profile.py,sha256=AsdwAA2bmSTVnYyJ31iddkSwYJfYqJ60V1UGjFpa3YI,5869
19
19
  ml_dash/cli_commands/upload.py,sha256=SSUfXC3qoNpoFvPM_ia-ing_N50LNiAvMy9op6FM9Ew,49664
20
20
  ml_dash/client.py,sha256=L-FZysVAuIovJJZ1MlDMPhOPJFyL77pMuBxtucG88x8,62005
21
21
  ml_dash/config.py,sha256=oz2xvoBh2X_xUXWr92cPD5nFxXMT5LxVNypv5B5O0fA,3116
@@ -30,7 +30,7 @@ ml_dash/run.py,sha256=_a1l5qswwMgrhvajx_lI-I5IXhfHDeH3hfLAJ7Uk_Gs,8847
30
30
  ml_dash/snowflake.py,sha256=14rEpRU5YltsmmmZW0EMUy_hdv5S5ME9gWVtmdmwfiU,4917
31
31
  ml_dash/storage.py,sha256=x1W-dK6wQY36-LVOJ4kA8Dn07ObNQuIErQWJ3b0PoGY,44910
32
32
  ml_dash/track.py,sha256=Dfg1ZnmKZ_FlE5ZfG8Qld_wN4RIMs3nrOxrxwf3thiY,8164
33
- ml_dash-0.6.7.dist-info/WHEEL,sha256=z-mOpxbJHqy3cq6SvUThBZdaLGFZzdZPtgWLcP2NKjQ,79
34
- ml_dash-0.6.7.dist-info/entry_points.txt,sha256=dYs2EHX1uRNO7AQGNnVaJJpgiy0Z9q7tiy4fHSyaf3Q,46
35
- ml_dash-0.6.7.dist-info/METADATA,sha256=u-694DRG0lHYIXcTLMas7jixUBQW6-HP9jkc11RwKuc,7203
36
- ml_dash-0.6.7.dist-info/RECORD,,
33
+ ml_dash-0.6.9.dist-info/WHEEL,sha256=z-mOpxbJHqy3cq6SvUThBZdaLGFZzdZPtgWLcP2NKjQ,79
34
+ ml_dash-0.6.9.dist-info/entry_points.txt,sha256=dYs2EHX1uRNO7AQGNnVaJJpgiy0Z9q7tiy4fHSyaf3Q,46
35
+ ml_dash-0.6.9.dist-info/METADATA,sha256=f2YU7ukCO9VmIQ1zrctXaRBOA5D6t5caT89JLMD6Sxw,9535
36
+ ml_dash-0.6.9.dist-info/RECORD,,