ml-dash 0.6.1__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 CHANGED
@@ -51,6 +51,7 @@ from .client import RemoteClient
51
51
  from .storage import LocalStorage
52
52
  from .log import LogLevel, LogBuilder
53
53
  from .params import ParametersBuilder
54
+ from .run import RUN
54
55
  from .auto_start import dxp
55
56
 
56
57
  __version__ = "0.1.0"
@@ -65,6 +66,7 @@ __all__ = [
65
66
  "LogLevel",
66
67
  "LogBuilder",
67
68
  "ParametersBuilder",
69
+ "RUN",
68
70
  "dxp",
69
71
  ]
70
72
 
ml_dash/experiment.py CHANGED
@@ -17,7 +17,8 @@ from .client import RemoteClient
17
17
  from .storage import LocalStorage
18
18
  from .log import LogLevel, LogBuilder
19
19
  from .params import ParametersBuilder
20
- from .files import FileBuilder
20
+ from .files import FilesAccessor, BindrsBuilder
21
+ from .run import RUN
21
22
 
22
23
 
23
24
  class OperationMode(Enum):
@@ -121,28 +122,13 @@ class RunManager:
121
122
  "Set folder before calling start() or entering 'with' block."
122
123
  )
123
124
 
124
- # Process template variables if present
125
+ # Check if this is a template (contains {RUN.) or static folder
125
126
  if value and '{RUN.' in value:
126
- # Generate unique run ID (timestamp-based)
127
- from datetime import datetime
128
- run_timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
129
-
130
- # Simple string replacement for template variables
131
- # Supports: {RUN.name}, {RUN.project}, {RUN.id}, {RUN.timestamp}
132
- replacements = {
133
- '{RUN.name}': f"{self._experiment.name}_{run_timestamp}", # Unique name with timestamp
134
- '{RUN.project}': self._experiment.project,
135
- '{RUN.id}': run_timestamp, # Just the timestamp
136
- '{RUN.timestamp}': run_timestamp, # Alias for id
137
- }
138
-
139
- # Replace all template variables
140
- for template, replacement in replacements.items():
141
- if template in value:
142
- value = value.replace(template, replacement)
143
-
144
- # Update the folder on the experiment
145
- self._experiment.folder = value
127
+ # Store the template - it will be formatted when the run starts
128
+ self._experiment._folder_template = value
129
+ else:
130
+ # Static folder - set directly
131
+ self._experiment.folder = value
146
132
 
147
133
  def __enter__(self) -> "Experiment":
148
134
  """Context manager entry - starts the experiment."""
@@ -240,7 +226,7 @@ class Experiment:
240
226
  self.project = project
241
227
  self.description = description
242
228
  self.tags = tags
243
- self.bindrs = bindrs
229
+ self._bindrs_list = bindrs
244
230
  self.folder = folder
245
231
  self._write_protected = _write_protected
246
232
  self.metadata = metadata
@@ -264,6 +250,7 @@ class Experiment:
264
250
  self._experiment_data: Optional[Dict[str, Any]] = None
265
251
  self._is_open = False
266
252
  self._metrics_manager: Optional['MetricsManager'] = None # Cached metrics manager
253
+ self._folder_template: Optional[str] = None # Template for folder path
267
254
 
268
255
  if self.mode in (OperationMode.REMOTE, OperationMode.HYBRID):
269
256
  # api_key can be None - RemoteClient will auto-load from storage
@@ -284,6 +271,16 @@ class Experiment:
284
271
  if self._is_open:
285
272
  return self
286
273
 
274
+ # Initialize RUN with experiment values
275
+ RUN.name = self.name
276
+ RUN.project = self.project
277
+ RUN.description = self.description
278
+ RUN._init_run() # Generate id and timestamp
279
+
280
+ # Format folder template if present
281
+ if self._folder_template:
282
+ self.folder = RUN._format(self._folder_template)
283
+
287
284
  if self._client:
288
285
  # Remote mode: create/update experiment via API
289
286
  response = self._client.create_or_update_experiment(
@@ -291,7 +288,7 @@ class Experiment:
291
288
  name=self.name,
292
289
  description=self.description,
293
290
  tags=self.tags,
294
- bindrs=self.bindrs,
291
+ bindrs=self._bindrs_list,
295
292
  folder=self.folder,
296
293
  write_protected=self._write_protected,
297
294
  metadata=self.metadata,
@@ -306,7 +303,7 @@ class Experiment:
306
303
  name=self.name,
307
304
  description=self.description,
308
305
  tags=self.tags,
309
- bindrs=self.bindrs,
306
+ bindrs=self._bindrs_list,
310
307
  folder=self.folder,
311
308
  metadata=self.metadata,
312
309
  )
@@ -341,6 +338,9 @@ class Experiment:
341
338
 
342
339
  self._is_open = False
343
340
 
341
+ # Reset RUN for next experiment
342
+ RUN._reset()
343
+
344
344
  @property
345
345
  def run(self) -> RunManager:
346
346
  """
@@ -545,39 +545,86 @@ class Experiment:
545
545
  else:
546
546
  print(formatted_message, file=sys.stdout)
547
547
 
548
- def files(self, **kwargs) -> FileBuilder:
548
+ @property
549
+ def files(self) -> FilesAccessor:
549
550
  """
550
- Get a FileBuilder for fluent file operations.
551
+ Get a FilesAccessor for fluent file operations.
551
552
 
552
553
  Returns:
553
- FileBuilder instance for chaining
554
+ FilesAccessor instance for chaining
554
555
 
555
556
  Raises:
556
557
  RuntimeError: If experiment is not open
557
558
 
558
559
  Examples:
559
560
  # Upload file
560
- experiment.files(file_path="./model.pt", prefix="/models").save()
561
+ experiment.files("checkpoints").save(net, to="checkpoint.pt")
561
562
 
562
563
  # List files
563
- files = experiment.files().list()
564
- files = experiment.files(prefix="/models").list()
564
+ files = experiment.files("/some/location").list()
565
+ files = experiment.files("/models").list()
565
566
 
566
567
  # Download file
567
- experiment.files(file_id="123").download()
568
+ experiment.files("some.text").download()
569
+ experiment.files("some.text").download(to="./model.pt")
570
+
571
+ # Download Files via Glob Pattern
572
+ file_paths = experiment.files("images").list("*.png")
573
+ experiment.files("images").download("*.png")
574
+
575
+ # This is equivalent to downloading to a directory
576
+ experiment.files.download("images/*.png", to="local_images")
577
+
578
+ # Delete files
579
+ experiment.files("some.text").delete()
580
+ experiment.files.delete("some.text")
581
+
582
+ # Specific File Types
583
+ dxp.files.save_text("content", to="view.yaml")
584
+ dxp.files.save_json(dict(hey="yo"), to="config.json")
585
+ dxp.files.save_blob(b"xxx", to="data.bin")
586
+ """
587
+ if not self._is_open:
588
+ raise RuntimeError(
589
+ "Experiment not started. Use 'with experiment.run:' or call experiment.run.start() first.\n"
590
+ "Example:\n"
591
+ " with dxp.run:\n"
592
+ " dxp.files('path').save()"
593
+ )
594
+
595
+ return FilesAccessor(self)
596
+
597
+ def bindrs(self, bindr_name: str) -> BindrsBuilder:
598
+ """
599
+ Get a BindrsBuilder for working with file collections (bindrs).
600
+
601
+ Bindrs are collections of files that can span multiple prefixes.
602
+
603
+ Args:
604
+ bindr_name: Name of the bindr (collection)
605
+
606
+ Returns:
607
+ BindrsBuilder instance for chaining
608
+
609
+ Raises:
610
+ RuntimeError: If experiment is not open
611
+
612
+ Examples:
613
+ # List files in a bindr
614
+ file_paths = experiment.bindrs("some-bindr").list()
568
615
 
569
- # Delete file
570
- experiment.files(file_id="123").delete()
616
+ Note:
617
+ This is a placeholder for future bindr functionality.
571
618
  """
572
619
  if not self._is_open:
573
620
  raise RuntimeError(
574
621
  "Experiment not started. Use 'with experiment.run:' or call experiment.run.start() first.\n"
575
622
  "Example:\n"
576
623
  " with dxp.run:\n"
577
- " dxp.files().save()"
624
+ " files = dxp.bindrs('my-bindr').list()"
578
625
  )
579
626
 
580
- return FileBuilder(self, **kwargs)
627
+ return BindrsBuilder(self, bindr_name)
581
628
 
582
629
  def _upload_file(
583
630
  self,