truss 0.10.11rc2__py3-none-any.whl → 0.10.12__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.

Potentially problematic release.


This version of truss might be problematic. Click here for more details.

truss/cli/train/common.py CHANGED
@@ -5,6 +5,12 @@ import rich_click as click
5
5
  from truss.cli.utils.output import console
6
6
  from truss.remote.baseten import BasetenRemote
7
7
 
8
+ # Byte size constants
9
+ BYTES_PER_KB = 1000
10
+ BYTES_PER_MB = BYTES_PER_KB * 1000
11
+ BYTES_PER_GB = BYTES_PER_MB * 1000
12
+ BYTES_PER_TB = BYTES_PER_GB * 1000
13
+
8
14
 
9
15
  def get_most_recent_job(
10
16
  remote_provider: BasetenRemote, project_id: Optional[str], job_id: Optional[str]
@@ -27,3 +33,16 @@ def get_most_recent_job(
27
33
  project_id = cast(str, job["training_project"]["id"])
28
34
  job_id = cast(str, job["id"])
29
35
  return project_id, job_id
36
+
37
+
38
+ def format_bytes_to_human_readable(bytes: int) -> str:
39
+ if bytes > BYTES_PER_TB:
40
+ return f"{bytes / BYTES_PER_TB:.2f} TB"
41
+ if bytes > BYTES_PER_GB:
42
+ return f"{bytes / BYTES_PER_GB:.2f} GB"
43
+ elif bytes > BYTES_PER_MB:
44
+ return f"{bytes / BYTES_PER_MB:.2f} MB"
45
+ elif bytes > BYTES_PER_KB:
46
+ return f"{bytes / BYTES_PER_KB:.2f} KB"
47
+ else:
48
+ return f"{bytes} B"
truss/cli/train/core.py CHANGED
@@ -20,6 +20,14 @@ from truss.remote.baseten.remote import BasetenRemote
20
20
  from truss_train import loader
21
21
  from truss_train.definitions import DeployCheckpointsConfig
22
22
 
23
+ SORT_BY_FILEPATH = "filepath"
24
+ SORT_BY_SIZE = "size"
25
+ SORT_BY_MODIFIED = "modified"
26
+ SORT_BY_TYPE = "type"
27
+ SORT_BY_PERMISSIONS = "permissions"
28
+ SORT_ORDER_ASC = "asc"
29
+ SORT_ORDER_DESC = "desc"
30
+
23
31
  ACTIVE_JOB_STATUSES = [
24
32
  "TRAINING_JOB_RUNNING",
25
33
  "TRAINING_JOB_CREATED",
@@ -401,3 +409,131 @@ def download_checkpoint_artifacts(
401
409
 
402
410
  def status_page_url(remote_url: str, training_job_id: str) -> str:
403
411
  return f"{remote_url}/training/jobs/{training_job_id}"
412
+
413
+
414
+ def fetch_project_by_name_or_id(
415
+ remote_provider: BasetenRemote, project_identifier: str
416
+ ) -> dict:
417
+ """Fetch a training project by name or ID.
418
+
419
+ Args:
420
+ remote_provider: The remote provider instance
421
+ project_identifier: Either a project ID or project name
422
+
423
+ Returns:
424
+ The project object as a dictionary
425
+
426
+ Raises:
427
+ click.ClickException: If the project is not found
428
+ """
429
+ try:
430
+ projects = remote_provider.api.list_training_projects()
431
+ projects_by_name = {project.get("name"): project for project in projects}
432
+ projects_by_id = {project.get("id"): project for project in projects}
433
+ if project_identifier in projects_by_id:
434
+ return projects_by_id[project_identifier]
435
+ if project_identifier in projects_by_name:
436
+ return projects_by_name[project_identifier]
437
+ valid_project_ids_and_names = ", ".join(
438
+ [f"{project.get('id')} ({project.get('name')})" for project in projects]
439
+ )
440
+ raise click.ClickException(
441
+ f"Project '{project_identifier}' not found. Valid project IDs and names: {valid_project_ids_and_names}"
442
+ )
443
+ except click.ClickException:
444
+ raise
445
+ except Exception as e:
446
+ raise click.ClickException(f"Error fetching project: {str(e)}")
447
+
448
+
449
+ def view_cache_summary(
450
+ remote_provider: BasetenRemote,
451
+ project_id: str,
452
+ sort_by: str = SORT_BY_FILEPATH,
453
+ order: str = SORT_ORDER_ASC,
454
+ ):
455
+ """View cache summary for a training project."""
456
+ try:
457
+ cache_data = remote_provider.api.get_cache_summary(project_id)
458
+
459
+ if not cache_data:
460
+ console.print("No cache summary found for this project.", style="yellow")
461
+ return
462
+
463
+ table = rich.table.Table(title=f"Cache summary for project: {project_id}")
464
+ table.add_column("File Path", style="cyan")
465
+ table.add_column("Size", style="green")
466
+ table.add_column("Modified", style="yellow")
467
+ table.add_column("Type")
468
+ table.add_column("Permissions", style="magenta")
469
+
470
+ files = cache_data.get("file_summaries", [])
471
+ if not files:
472
+ console.print("No files found in cache.", style="yellow")
473
+ return
474
+
475
+ reverse = order == SORT_ORDER_DESC
476
+
477
+ if sort_by == SORT_BY_FILEPATH:
478
+ files.sort(key=lambda x: x.get("path", ""), reverse=reverse)
479
+ elif sort_by == SORT_BY_SIZE:
480
+ files.sort(key=lambda x: x.get("size_bytes", 0), reverse=reverse)
481
+ elif sort_by == SORT_BY_MODIFIED:
482
+ files.sort(key=lambda x: x.get("modified", ""), reverse=reverse)
483
+ elif sort_by == SORT_BY_TYPE:
484
+ files.sort(key=lambda x: x.get("file_type", ""), reverse=reverse)
485
+ elif sort_by == SORT_BY_PERMISSIONS:
486
+ files.sort(key=lambda x: x.get("permissions", ""), reverse=reverse)
487
+
488
+ total_size = 0
489
+ for file_info in files:
490
+ total_size += file_info.get("size_bytes", 0)
491
+
492
+ total_size_str = common.format_bytes_to_human_readable(total_size)
493
+
494
+ console.print(
495
+ f"📅 Cache captured at: {cache_data.get('timestamp', 'Unknown')}",
496
+ style="bold blue",
497
+ )
498
+ console.print(
499
+ f"📁 Project ID: {cache_data.get('project_id', 'Unknown')}",
500
+ style="bold blue",
501
+ )
502
+ console.print()
503
+ console.print(f"📊 Total files: {len(files)}", style="bold green")
504
+ console.print(f"💾 Total size: {total_size_str}", style="bold green")
505
+ console.print()
506
+
507
+ for file_info in files:
508
+ size_bytes = file_info.get("size_bytes", 0)
509
+
510
+ size_str = cli_common.format_bytes_to_human_readable(int(size_bytes))
511
+
512
+ modified_str = cli_common.format_localized_time(
513
+ file_info.get("modified", "Unknown")
514
+ )
515
+
516
+ table.add_row(
517
+ file_info.get("path", "Unknown"),
518
+ size_str,
519
+ modified_str,
520
+ file_info.get("file_type", "Unknown"),
521
+ file_info.get("permissions", "Unknown"),
522
+ )
523
+
524
+ console.print(table)
525
+
526
+ except Exception as e:
527
+ console.print(f"Error fetching cache summary: {str(e)}", style="red")
528
+ raise
529
+
530
+
531
+ def view_cache_summary_by_project(
532
+ remote_provider: BasetenRemote,
533
+ project_identifier: str,
534
+ sort_by: str = SORT_BY_FILEPATH,
535
+ order: str = SORT_ORDER_ASC,
536
+ ):
537
+ """View cache summary for a training project by ID or name."""
538
+ project = fetch_project_by_name_or_id(remote_provider, project_identifier)
539
+ view_cache_summary(remote_provider, project["id"], sort_by, order)
@@ -35,18 +35,17 @@ class MetricsWatcher(TrainingPollerMixin):
35
35
 
36
36
  def _format_bytes(self, bytes_val: float) -> Tuple[str, str]:
37
37
  """Convert bytes to human readable format"""
38
+ default_color = "green"
38
39
  color_map = {"MB": "green", "GB": "cyan", "TB": "magenta"}
39
- unit = "MB"
40
- if bytes_val > 1024 * 1024 * 1024 * 1024:
40
+ unit = "B"
41
+ if bytes_val > 1000 * 1000 * 1000 * 1000:
41
42
  unit = "TB"
42
- elif bytes_val > 1024 * 1024 * 1024:
43
+ elif bytes_val > 1000 * 1000 * 1000:
43
44
  unit = "GB"
44
-
45
- if unit == "MB":
46
- return f"{bytes_val / (1024 * 1024):.2f} MB", color_map[unit]
47
- elif unit == "GB":
48
- return f"{bytes_val / (1024 * 1024 * 1024):.2f} GB", color_map[unit]
49
- return f"{bytes_val:.2f} bytes", color_map[unit]
45
+ elif bytes_val > 1000 * 1000:
46
+ unit = "MB"
47
+ color = color_map.get(unit, default_color)
48
+ return (common.format_bytes_to_human_readable(int(bytes_val)), color)
50
49
 
51
50
  def _format_storage_utilization(self, utilization: float) -> Tuple[str, str]:
52
51
  percent = round(utilization * 100, 4)
@@ -11,11 +11,21 @@ from truss.cli.logs import utils as cli_log_utils
11
11
  from truss.cli.logs.training_log_watcher import TrainingLogWatcher
12
12
  from truss.cli.train import common as train_common
13
13
  from truss.cli.train import core
14
+ from truss.cli.train.core import (
15
+ SORT_BY_FILEPATH,
16
+ SORT_BY_MODIFIED,
17
+ SORT_BY_PERMISSIONS,
18
+ SORT_BY_SIZE,
19
+ SORT_BY_TYPE,
20
+ SORT_ORDER_ASC,
21
+ SORT_ORDER_DESC,
22
+ )
14
23
  from truss.cli.utils import common
15
24
  from truss.cli.utils.output import console, error_console
16
25
  from truss.remote.baseten.core import get_training_job_logs_with_pagination
17
26
  from truss.remote.baseten.remote import BasetenRemote
18
27
  from truss.remote.remote_factory import RemoteFactory
28
+ from truss_train import TrainingJob
19
29
 
20
30
 
21
31
  @click.group()
@@ -27,15 +37,30 @@ truss_cli.add_command(train)
27
37
 
28
38
 
29
39
  def _print_training_job_success_message(
30
- job_id: str, remote_provider: BasetenRemote
40
+ job_id: str,
41
+ project_name: str,
42
+ job_object: TrainingJob,
43
+ remote_provider: BasetenRemote,
31
44
  ) -> None:
32
45
  """Print success message and helpful commands for a training job."""
33
46
  console.print("✨ Training job successfully created!", style="green")
47
+ should_print_cache_summary = (
48
+ job_object.runtime.enable_cache
49
+ or job_object.runtime.cache_config
50
+ and job_object.runtime.cache_config.enabled
51
+ )
52
+ cache_summary_snippet = ""
53
+ if should_print_cache_summary:
54
+ cache_summary_snippet = (
55
+ f"📁 View cache summary via "
56
+ f"[cyan]'truss train cache summarize --project {project_name}'[/cyan]\n"
57
+ )
34
58
  console.print(
35
59
  f"🪵 View logs for your job via "
36
60
  f"[cyan]'truss train logs --job-id {job_id} --tail'[/cyan]\n"
37
61
  f"🔍 View metrics for your job via "
38
62
  f"[cyan]'truss train metrics --job-id {job_id}'[/cyan]\n"
63
+ f"{cache_summary_snippet}"
39
64
  f"🌐 Status page: {common.format_link(core.status_page_url(remote_provider.remote_url, job_id))}"
40
65
  )
41
66
 
@@ -44,6 +69,7 @@ def _handle_post_create_logic(
44
69
  job_resp: dict, remote_provider: BasetenRemote, tail: bool
45
70
  ) -> None:
46
71
  project_id, job_id = job_resp["training_project"]["id"], job_resp["id"]
72
+ project_name = job_resp["training_project"]["name"]
47
73
 
48
74
  if job_resp.get("current_status", None) == "TRAINING_JOB_QUEUED":
49
75
  console.print(
@@ -51,7 +77,9 @@ def _handle_post_create_logic(
51
77
  style="green",
52
78
  )
53
79
  else:
54
- _print_training_job_success_message(job_id, remote_provider)
80
+ _print_training_job_success_message(
81
+ job_id, project_name, job_resp["job_object"], remote_provider
82
+ )
55
83
 
56
84
  if tail:
57
85
  watcher = TrainingLogWatcher(remote_provider.api, project_id, job_id)
@@ -351,3 +379,46 @@ def download_checkpoint_artifacts(job_id: Optional[str], remote: Optional[str])
351
379
  except Exception as e:
352
380
  error_console.print(f"Failed to download checkpoint artifacts data: {str(e)}")
353
381
  sys.exit(1)
382
+
383
+
384
+ @train.group(name="cache")
385
+ def cache():
386
+ """Cache-related subcommands for truss train"""
387
+
388
+
389
+ @cache.command(name="summarize")
390
+ @click.option(
391
+ "--project", type=str, required=True, help="Project ID or name to view cache for."
392
+ )
393
+ @click.option("--remote", type=str, required=False, help="Remote to use")
394
+ @click.option(
395
+ "--sort",
396
+ type=click.Choice(
397
+ [
398
+ SORT_BY_FILEPATH,
399
+ SORT_BY_SIZE,
400
+ SORT_BY_MODIFIED,
401
+ SORT_BY_TYPE,
402
+ SORT_BY_PERMISSIONS,
403
+ ]
404
+ ),
405
+ default=SORT_BY_FILEPATH,
406
+ help="Sort files by filepath, size, modified date, file type, or permissions.",
407
+ )
408
+ @click.option(
409
+ "--order",
410
+ type=click.Choice([SORT_ORDER_ASC, SORT_ORDER_DESC]),
411
+ default=SORT_ORDER_ASC,
412
+ help="Sort order: ascending or descending.",
413
+ )
414
+ @common.common_options()
415
+ def view_cache_summary(project: str, remote: Optional[str], sort: str, order: str):
416
+ """View cache summary for a training project"""
417
+ if not remote:
418
+ remote = remote_cli.inquire_remote_name()
419
+
420
+ remote_provider: BasetenRemote = cast(
421
+ BasetenRemote, RemoteFactory.create(remote=remote)
422
+ )
423
+
424
+ train_cli.view_cache_summary_by_project(remote_provider, project, sort, order)
truss/cli/utils/common.py CHANGED
@@ -186,4 +186,17 @@ def format_localized_time(iso_timestamp: str) -> str:
186
186
  iso_timestamp = iso_timestamp.replace("Z", "+00:00")
187
187
  utc_time = datetime.datetime.fromisoformat(iso_timestamp)
188
188
  local_time = utc_time.astimezone()
189
- return local_time.strftime("%Y-%m-%d %H:%M")
189
+ return local_time.strftime("%Y-%m-%d %H:%M:%S")
190
+
191
+
192
+ def format_bytes_to_human_readable(bytes: int) -> str:
193
+ if bytes > 1000 * 1000 * 1000 * 1000:
194
+ return f"{bytes / (1000 * 1000 * 1000 * 1000):.2f} TB"
195
+ if bytes > 1000 * 1000 * 1000:
196
+ return f"{bytes / (1000 * 1000 * 1000):.2f} GB"
197
+ elif bytes > 1000 * 1000:
198
+ return f"{bytes / (1000 * 1000):.2f} MB"
199
+ elif bytes > 1000:
200
+ return f"{bytes / 1000:.2f} KB"
201
+ else:
202
+ return f"{bytes} B"
@@ -669,6 +669,13 @@ class BasetenApi:
669
669
  # NB(nikhil): reverse order so latest logs are at the end
670
670
  return resp_json["logs"][::-1]
671
671
 
672
+ def get_cache_summary(self, project_id: str):
673
+ """Get cache summary for a training project."""
674
+ resp_json = self._rest_api_client.get(
675
+ f"v1/training_projects/{project_id}/cache/summary"
676
+ )
677
+ return resp_json
678
+
672
679
  def _fetch_log_batch(
673
680
  self, project_id: str, job_id: str, query_params: Dict[str, Any]
674
681
  ) -> List[Any]:
@@ -120,3 +120,31 @@ class TrussUserEnv(pydantic.BaseModel):
120
120
  class BlobType(Enum):
121
121
  MODEL = "model"
122
122
  TRAIN = "train"
123
+
124
+
125
+ class FileSummary(pydantic.BaseModel):
126
+ """Information about a file in the cache."""
127
+
128
+ path: str = pydantic.Field(description="Relative path of the file in the cache")
129
+ size_bytes: int = pydantic.Field(description="Size of the file in bytes")
130
+ modified: str = pydantic.Field(description="Last modification time of the file")
131
+ file_type: Optional[str] = pydantic.Field(
132
+ default=None,
133
+ description="Type of the file (e.g., 'file', 'directory', 'symlink')",
134
+ )
135
+ permissions: Optional[str] = pydantic.Field(
136
+ default=None,
137
+ description="File permissions in Unix symbolic format (e.g., 'drwxr-xr-x', '-rw-r--r--')",
138
+ )
139
+
140
+
141
+ class GetCacheSummaryResponseV1(pydantic.BaseModel):
142
+ """Response for getting cache summary."""
143
+
144
+ timestamp: str = pydantic.Field(
145
+ description="Timestamp when the cache summary was captured"
146
+ )
147
+ project_id: str = pydantic.Field(description="Project ID associated with the cache")
148
+ file_summaries: list[FileSummary] = pydantic.Field(
149
+ description="List of files in the cache"
150
+ )
@@ -0,0 +1,712 @@
1
+ from unittest.mock import Mock
2
+
3
+ import click
4
+ import pytest
5
+
6
+ from truss.cli.train.core import (
7
+ SORT_BY_FILEPATH,
8
+ SORT_BY_MODIFIED,
9
+ SORT_BY_PERMISSIONS,
10
+ SORT_BY_SIZE,
11
+ SORT_BY_TYPE,
12
+ SORT_ORDER_ASC,
13
+ SORT_ORDER_DESC,
14
+ view_cache_summary,
15
+ view_cache_summary_by_project,
16
+ )
17
+ from truss.remote.baseten.remote import BasetenRemote
18
+
19
+
20
+ def test_view_cache_summary_success(capsys):
21
+ """Test successful cache summary viewing."""
22
+ mock_api = Mock()
23
+ mock_remote = Mock(spec=BasetenRemote)
24
+ mock_remote.api = mock_api
25
+
26
+ mock_api.get_cache_summary.return_value = {
27
+ "timestamp": "2024-01-01T12:00:00Z",
28
+ "project_id": "proj123",
29
+ "file_summaries": [
30
+ {
31
+ "path": "model/weights.bin",
32
+ "size_bytes": 1024 * 1024 * 100,
33
+ "modified": "2024-01-01T10:00:00Z",
34
+ "file_type": "file",
35
+ "permissions": "-rw-r--r--",
36
+ },
37
+ {
38
+ "path": "config.json",
39
+ "size_bytes": 1024,
40
+ "modified": "2024-01-01T09:00:00Z",
41
+ "file_type": "file",
42
+ "permissions": "-rw-r--r--",
43
+ },
44
+ {
45
+ "path": "model/",
46
+ "size_bytes": 0,
47
+ "modified": "2024-01-01T08:00:00Z",
48
+ "file_type": "directory",
49
+ "permissions": "drwxr-xr-x",
50
+ },
51
+ ],
52
+ }
53
+ mock_api.list_training_projects.return_value = [
54
+ {"id": "proj123", "name": "test-project"}
55
+ ]
56
+
57
+ view_cache_summary(mock_remote, "proj123", SORT_BY_FILEPATH, SORT_ORDER_ASC)
58
+
59
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
60
+
61
+ captured = capsys.readouterr()
62
+ assert "Cache summary for project: proj123" in captured.out
63
+ assert "model/weights.bin" in captured.out
64
+ assert "config.json" in captured.out
65
+ assert "104.86 MB" in captured.out
66
+ assert "1.02 KB" in captured.out
67
+
68
+
69
+ def test_view_cache_summary_no_cache(capsys):
70
+ """Test when no cache summary is found."""
71
+ mock_api = Mock()
72
+ mock_remote = Mock(spec=BasetenRemote)
73
+ mock_remote.api = mock_api
74
+
75
+ mock_api.get_cache_summary.return_value = {}
76
+
77
+ mock_api.list_training_projects.return_value = [
78
+ {"id": "proj123", "name": "test-project"}
79
+ ]
80
+
81
+ view_cache_summary(mock_remote, "proj123", SORT_BY_FILEPATH, SORT_ORDER_ASC)
82
+
83
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
84
+
85
+ captured = capsys.readouterr()
86
+ assert "No cache summary found for this project." in captured.out
87
+
88
+
89
+ def test_view_cache_summary_empty_files(capsys):
90
+ """Test when cache summary exists but has no files."""
91
+ mock_api = Mock()
92
+ mock_remote = Mock(spec=BasetenRemote)
93
+ mock_remote.api = mock_api
94
+
95
+ mock_api.list_training_projects.return_value = [
96
+ {"id": "proj123", "name": "test-project"}
97
+ ]
98
+ mock_api.get_cache_summary.return_value = {
99
+ "timestamp": "2024-01-01T12:00:00Z",
100
+ "project_id": "proj123",
101
+ "file_summaries": [],
102
+ }
103
+
104
+ view_cache_summary(mock_remote, "proj123", SORT_BY_FILEPATH, SORT_ORDER_ASC)
105
+
106
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
107
+
108
+ captured = capsys.readouterr()
109
+ assert "No files found in cache." in captured.out
110
+
111
+
112
+ def test_view_cache_summary_api_error(capsys):
113
+ """Test when API call fails."""
114
+ mock_api = Mock()
115
+ mock_remote = Mock(spec=BasetenRemote)
116
+ mock_remote.api = mock_api
117
+
118
+ mock_api.list_training_projects.return_value = [
119
+ {"id": "proj123", "name": "test-project"}
120
+ ]
121
+ mock_api.get_cache_summary.side_effect = Exception("API Error")
122
+
123
+ with pytest.raises(Exception, match="API Error"):
124
+ view_cache_summary(mock_remote, "proj123", SORT_BY_FILEPATH, SORT_ORDER_ASC)
125
+
126
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
127
+
128
+ captured = capsys.readouterr()
129
+ assert "Error fetching cache summary: API Error" in captured.out
130
+
131
+
132
+ def test_view_cache_summary_sort_by_size_asc(capsys):
133
+ """Test sorting by size in ascending order."""
134
+ mock_api = Mock()
135
+ mock_remote = Mock(spec=BasetenRemote)
136
+ mock_remote.api = mock_api
137
+
138
+ mock_api.list_training_projects.return_value = [
139
+ {"id": "proj123", "name": "test-project"}
140
+ ]
141
+ mock_api.get_cache_summary.return_value = {
142
+ "timestamp": "2024-01-01T12:00:00Z",
143
+ "project_id": "proj123",
144
+ "file_summaries": [
145
+ {
146
+ "path": "large_file.bin",
147
+ "size_bytes": 1024 * 1024 * 100,
148
+ "modified": "2024-01-01T10:00:00Z",
149
+ "file_type": "file",
150
+ "permissions": "-rw-r--r--",
151
+ },
152
+ {
153
+ "path": "small_file.txt",
154
+ "size_bytes": 1024,
155
+ "modified": "2024-01-01T09:00:00Z",
156
+ "file_type": "file",
157
+ "permissions": "-rw-r--r--",
158
+ },
159
+ {
160
+ "path": "medium_file.dat",
161
+ "size_bytes": 1024 * 1024,
162
+ "modified": "2024-01-01T11:00:00Z",
163
+ "file_type": "file",
164
+ "permissions": "-rw-r--r--",
165
+ },
166
+ ],
167
+ }
168
+
169
+ view_cache_summary(mock_remote, "proj123", SORT_BY_SIZE, SORT_ORDER_ASC)
170
+
171
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
172
+
173
+ captured = capsys.readouterr()
174
+ output_lines = captured.out.split("\n")
175
+
176
+ table_start = -1
177
+ for i, line in enumerate(output_lines):
178
+ if "small_file.txt" in line:
179
+ table_start = i
180
+ break
181
+
182
+ small_file_line = None
183
+ medium_file_line = None
184
+ large_file_line = None
185
+
186
+ for line in output_lines[table_start:]:
187
+ if "small_file.txt" in line:
188
+ small_file_line = line
189
+ elif "medium_file.dat" in line:
190
+ medium_file_line = line
191
+ elif "large_file.bin" in line:
192
+ large_file_line = line
193
+
194
+ assert small_file_line is not None
195
+ assert medium_file_line is not None
196
+ assert large_file_line is not None
197
+
198
+ small_pos = captured.out.find("small_file.txt")
199
+ medium_pos = captured.out.find("medium_file.dat")
200
+ large_pos = captured.out.find("large_file.bin")
201
+
202
+ assert small_pos < medium_pos < large_pos
203
+
204
+
205
+ def test_view_cache_summary_sort_by_size_desc(capsys):
206
+ """Test sorting by size in descending order."""
207
+ mock_api = Mock()
208
+ mock_remote = Mock(spec=BasetenRemote)
209
+ mock_remote.api = mock_api
210
+
211
+ mock_api.list_training_projects.return_value = [
212
+ {"id": "proj123", "name": "test-project"}
213
+ ]
214
+ mock_api.get_cache_summary.return_value = {
215
+ "timestamp": "2024-01-01T12:00:00Z",
216
+ "project_id": "proj123",
217
+ "file_summaries": [
218
+ {
219
+ "path": "large_file.bin",
220
+ "size_bytes": 1024 * 1024 * 100,
221
+ "modified": "2024-01-01T10:00:00Z",
222
+ "file_type": "file",
223
+ "permissions": "-rw-r--r--",
224
+ },
225
+ {
226
+ "path": "small_file.txt",
227
+ "size_bytes": 1024,
228
+ "modified": "2024-01-01T09:00:00Z",
229
+ "file_type": "file",
230
+ "permissions": "-rw-r--r--",
231
+ },
232
+ {
233
+ "path": "medium_file.dat",
234
+ "size_bytes": 1024 * 1024,
235
+ "modified": "2024-01-01T11:00:00Z",
236
+ "file_type": "file",
237
+ "permissions": "-rw-r--r--",
238
+ },
239
+ ],
240
+ }
241
+
242
+ view_cache_summary(mock_remote, "proj123", SORT_BY_SIZE, SORT_ORDER_DESC)
243
+
244
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
245
+
246
+ captured = capsys.readouterr()
247
+
248
+ small_pos = captured.out.find("small_file.txt")
249
+ medium_pos = captured.out.find("medium_file.dat")
250
+ large_pos = captured.out.find("large_file.bin")
251
+
252
+ assert large_pos < medium_pos < small_pos
253
+
254
+
255
+ def test_view_cache_summary_sort_by_modified_asc(capsys):
256
+ """Test sorting by modified date in ascending order."""
257
+ mock_api = Mock()
258
+ mock_remote = Mock(spec=BasetenRemote)
259
+ mock_remote.api = mock_api
260
+
261
+ mock_api.list_training_projects.return_value = [
262
+ {"id": "proj123", "name": "test-project"}
263
+ ]
264
+ mock_api.get_cache_summary.return_value = {
265
+ "timestamp": "2024-01-01T12:00:00Z",
266
+ "project_id": "proj123",
267
+ "file_summaries": [
268
+ {
269
+ "path": "old_file.txt",
270
+ "size_bytes": 1024,
271
+ "modified": "2024-01-01T08:00:00Z",
272
+ "file_type": "file",
273
+ "permissions": "-rw-r--r--",
274
+ },
275
+ {
276
+ "path": "new_file.txt",
277
+ "size_bytes": 1024,
278
+ "modified": "2024-01-01T12:00:00Z",
279
+ "file_type": "file",
280
+ "permissions": "-rw-r--r--",
281
+ },
282
+ {
283
+ "path": "middle_file.txt",
284
+ "size_bytes": 1024,
285
+ "modified": "2024-01-01T10:00:00Z",
286
+ "file_type": "file",
287
+ "permissions": "-rw-r--r--",
288
+ },
289
+ ],
290
+ }
291
+
292
+ view_cache_summary(mock_remote, "proj123", SORT_BY_MODIFIED, SORT_ORDER_ASC)
293
+
294
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
295
+
296
+ captured = capsys.readouterr()
297
+
298
+ old_pos = captured.out.find("old_file.txt")
299
+ middle_pos = captured.out.find("middle_file.txt")
300
+ new_pos = captured.out.find("new_file.txt")
301
+
302
+ assert old_pos < middle_pos < new_pos
303
+
304
+
305
+ def test_view_cache_summary_sort_by_filepath_desc(capsys):
306
+ """Test sorting by filepath in descending order."""
307
+ mock_api = Mock()
308
+ mock_remote = Mock(spec=BasetenRemote)
309
+ mock_remote.api = mock_api
310
+
311
+ mock_api.get_cache_summary.return_value = {
312
+ "timestamp": "2024-01-01T12:00:00Z",
313
+ "project_id": "proj123",
314
+ "file_summaries": [
315
+ {
316
+ "path": "a_file.txt",
317
+ "size_bytes": 1024,
318
+ "modified": "2024-01-01T10:00:00Z",
319
+ "file_type": "file",
320
+ "permissions": "-rw-r--r--",
321
+ },
322
+ {
323
+ "path": "z_file.txt",
324
+ "size_bytes": 1024,
325
+ "modified": "2024-01-01T10:00:00Z",
326
+ "file_type": "file",
327
+ "permissions": "-rw-r--r--",
328
+ },
329
+ {
330
+ "path": "m_file.txt",
331
+ "size_bytes": 1024,
332
+ "modified": "2024-01-01T10:00:00Z",
333
+ "file_type": "file",
334
+ "permissions": "-rw-r--r--",
335
+ },
336
+ ],
337
+ }
338
+
339
+ view_cache_summary(mock_remote, "proj123", SORT_BY_FILEPATH, SORT_ORDER_DESC)
340
+
341
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
342
+
343
+ captured = capsys.readouterr()
344
+
345
+ a_pos = captured.out.find("a_file.txt")
346
+ m_pos = captured.out.find("m_file.txt")
347
+ z_pos = captured.out.find("z_file.txt")
348
+
349
+ assert z_pos < m_pos < a_pos
350
+
351
+
352
+ def test_view_cache_summary_by_project_name_success(capsys):
353
+ """Test successful cache summary viewing by project name."""
354
+ mock_api = Mock()
355
+ mock_remote = Mock(spec=BasetenRemote)
356
+ mock_remote.api = mock_api
357
+
358
+ # Mock the get_cache_summary response for successful project ID lookup
359
+ mock_api.get_cache_summary.return_value = {
360
+ "timestamp": "2024-01-01T12:00:00Z",
361
+ "project_id": "proj123",
362
+ "file_summaries": [
363
+ {
364
+ "path": "model/weights.bin",
365
+ "size_bytes": 1024 * 1024 * 100,
366
+ "modified": "2024-01-01T10:00:00Z",
367
+ "file_type": "file",
368
+ "permissions": "-rw-r--r--",
369
+ }
370
+ ],
371
+ }
372
+
373
+ # Mock the list_training_projects response
374
+ mock_api.list_training_projects.return_value = [
375
+ {"id": "proj123", "name": "test-project"},
376
+ {"id": "proj456", "name": "another-project"},
377
+ ]
378
+
379
+ view_cache_summary_by_project(
380
+ mock_remote, "test-project", SORT_BY_FILEPATH, SORT_ORDER_ASC
381
+ )
382
+
383
+ assert mock_api.get_cache_summary.call_count == 1
384
+ assert mock_api.list_training_projects.call_count == 1
385
+
386
+ captured = capsys.readouterr()
387
+ assert "Cache summary for project: proj123" in captured.out
388
+ assert "model/weights.bin" in captured.out
389
+
390
+
391
+ def test_view_cache_summary_by_project_name_not_found(capsys):
392
+ """Test when project name is not found."""
393
+ mock_api = Mock()
394
+ mock_remote = Mock(spec=BasetenRemote)
395
+ mock_remote.api = mock_api
396
+
397
+ mock_api.list_training_projects.return_value = [
398
+ {"id": "proj123", "name": "test-project"},
399
+ {"id": "proj456", "name": "another-project"},
400
+ ]
401
+
402
+ with pytest.raises(
403
+ click.ClickException, match="Project 'nonexistent-project' not found"
404
+ ):
405
+ view_cache_summary_by_project(
406
+ mock_remote, "nonexistent-project", SORT_BY_FILEPATH, SORT_ORDER_ASC
407
+ )
408
+
409
+ mock_api.list_training_projects.assert_called_once()
410
+
411
+
412
+ def test_view_cache_summary_by_project_id_direct(capsys):
413
+ """Test that project ID is used directly."""
414
+ mock_api = Mock()
415
+ mock_remote = Mock(spec=BasetenRemote)
416
+ mock_remote.api = mock_api
417
+
418
+ mock_api.get_cache_summary.return_value = {
419
+ "timestamp": "2024-01-01T12:00:00Z",
420
+ "project_id": "proj123",
421
+ "file_summaries": [
422
+ {
423
+ "path": "model/weights.bin",
424
+ "size_bytes": 1024 * 1024 * 100,
425
+ "modified": "2024-01-01T10:00:00Z",
426
+ "file_type": "file",
427
+ "permissions": "-rw-r--r--",
428
+ }
429
+ ],
430
+ }
431
+
432
+ mock_api.list_training_projects.return_value = [
433
+ {"id": "proj123", "name": "test-project"},
434
+ {"id": "proj456", "name": "another-project"},
435
+ ]
436
+
437
+ project_id = "proj123"
438
+ view_cache_summary_by_project(
439
+ mock_remote, project_id, SORT_BY_FILEPATH, SORT_ORDER_ASC
440
+ )
441
+
442
+ assert mock_api.get_cache_summary.call_count == 1
443
+ assert mock_api.list_training_projects.call_count == 1
444
+
445
+ captured = capsys.readouterr()
446
+ assert "Cache summary for project: proj123" in captured.out
447
+
448
+
449
+ def test_view_cache_summary_by_project_other_error():
450
+ """Test that other errors (not 404) are re-raised."""
451
+ mock_api = Mock()
452
+ mock_remote = Mock(spec=BasetenRemote)
453
+ mock_remote.api = mock_api
454
+
455
+ mock_api.list_training_projects.return_value = [
456
+ {"id": "proj123", "name": "some-project"}
457
+ ]
458
+ mock_api.get_cache_summary.side_effect = Exception("Network error")
459
+
460
+ with pytest.raises(Exception, match="Network error"):
461
+ view_cache_summary_by_project(
462
+ mock_remote, "some-project", SORT_BY_FILEPATH, SORT_ORDER_ASC
463
+ )
464
+
465
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
466
+
467
+
468
+ def test_view_cache_summary_by_project_list_error(capsys):
469
+ """Test when listing projects fails after 404."""
470
+ mock_api = Mock()
471
+ mock_remote = Mock(spec=BasetenRemote)
472
+ mock_remote.api = mock_api
473
+
474
+ mock_api.list_training_projects.side_effect = Exception("API error")
475
+
476
+ with pytest.raises(click.ClickException, match="Error fetching project: API error"):
477
+ view_cache_summary_by_project(
478
+ mock_remote, "nonexistent-project", SORT_BY_FILEPATH, SORT_ORDER_ASC
479
+ )
480
+
481
+ mock_api.list_training_projects.assert_called_once()
482
+
483
+
484
+ def test_view_cache_summary_sort_by_type_asc(capsys):
485
+ """Test sorting by file type in ascending order."""
486
+ mock_api = Mock()
487
+ mock_remote = Mock(spec=BasetenRemote)
488
+ mock_remote.api = mock_api
489
+
490
+ mock_api.list_training_projects.return_value = [
491
+ {"id": "proj123", "name": "test-project"}
492
+ ]
493
+ mock_api.get_cache_summary.return_value = {
494
+ "timestamp": "2024-01-01T12:00:00Z",
495
+ "project_id": "proj123",
496
+ "file_summaries": [
497
+ {
498
+ "path": "config.json",
499
+ "size_bytes": 1024,
500
+ "modified": "2024-01-01T09:00:00Z",
501
+ "file_type": "file",
502
+ "permissions": "-rw-r--r--",
503
+ },
504
+ {
505
+ "path": "model/",
506
+ "size_bytes": 0,
507
+ "modified": "2024-01-01T08:00:00Z",
508
+ "file_type": "directory",
509
+ "permissions": "drwxr-xr-x",
510
+ },
511
+ {
512
+ "path": "data.txt",
513
+ "size_bytes": 2048,
514
+ "modified": "2024-01-01T10:00:00Z",
515
+ "file_type": "file",
516
+ "permissions": "-rw-r--r--",
517
+ },
518
+ ],
519
+ }
520
+
521
+ view_cache_summary(mock_remote, "proj123", SORT_BY_TYPE, SORT_ORDER_ASC)
522
+
523
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
524
+
525
+ captured = capsys.readouterr()
526
+ output_lines = captured.out.split("\n")
527
+
528
+ table_start = -1
529
+ for i, line in enumerate(output_lines):
530
+ if "config.json" in line or "data.txt" in line or "model/" in line:
531
+ table_start = i
532
+ break
533
+
534
+ directory_line = None
535
+ file_lines = []
536
+
537
+ for line in output_lines[table_start:]:
538
+ if "model/" in line:
539
+ directory_line = line
540
+ elif "config.json" in line or "data.txt" in line:
541
+ file_lines.append(line)
542
+
543
+ assert directory_line is not None
544
+ assert len(file_lines) == 2
545
+
546
+ directory_pos = captured.out.find("model/")
547
+ config_pos = captured.out.find("config.json")
548
+ data_pos = captured.out.find("data.txt")
549
+
550
+ assert directory_pos < config_pos
551
+ assert directory_pos < data_pos
552
+
553
+
554
+ def test_view_cache_summary_sort_by_type_desc(capsys):
555
+ """Test sorting by file type in descending order."""
556
+ mock_api = Mock()
557
+ mock_remote = Mock(spec=BasetenRemote)
558
+ mock_remote.api = mock_api
559
+
560
+ mock_api.list_training_projects.return_value = [
561
+ {"id": "proj123", "name": "test-project"}
562
+ ]
563
+ mock_api.get_cache_summary.return_value = {
564
+ "timestamp": "2024-01-01T12:00:00Z",
565
+ "project_id": "proj123",
566
+ "file_summaries": [
567
+ {
568
+ "path": "config.json",
569
+ "size_bytes": 1024,
570
+ "modified": "2024-01-01T09:00:00Z",
571
+ "file_type": "file",
572
+ "permissions": "-rw-r--r--",
573
+ },
574
+ {
575
+ "path": "model/",
576
+ "size_bytes": 0,
577
+ "modified": "2024-01-01T08:00:00Z",
578
+ "file_type": "directory",
579
+ "permissions": "drwxr-xr-x",
580
+ },
581
+ {
582
+ "path": "data.txt",
583
+ "size_bytes": 2048,
584
+ "modified": "2024-01-01T10:00:00Z",
585
+ "file_type": "file",
586
+ "permissions": "-rw-r--r--",
587
+ },
588
+ ],
589
+ }
590
+
591
+ view_cache_summary(mock_remote, "proj123", SORT_BY_TYPE, SORT_ORDER_DESC)
592
+
593
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
594
+
595
+ captured = capsys.readouterr()
596
+
597
+ config_pos = captured.out.find("config.json")
598
+ data_pos = captured.out.find("data.txt")
599
+ directory_pos = captured.out.find("model/")
600
+
601
+ assert config_pos < directory_pos
602
+ assert data_pos < directory_pos
603
+
604
+
605
+ def test_view_cache_summary_sort_by_permissions_asc(capsys):
606
+ """Test sorting by permissions in ascending order."""
607
+ mock_api = Mock()
608
+ mock_remote = Mock(spec=BasetenRemote)
609
+ mock_remote.api = mock_api
610
+
611
+ mock_api.list_training_projects.return_value = [
612
+ {"id": "proj123", "name": "test-project"}
613
+ ]
614
+ mock_api.get_cache_summary.return_value = {
615
+ "timestamp": "2024-01-01T12:00:00Z",
616
+ "project_id": "proj123",
617
+ "file_summaries": [
618
+ {
619
+ "path": "config.json",
620
+ "size_bytes": 1024,
621
+ "modified": "2024-01-01T09:00:00Z",
622
+ "file_type": "file",
623
+ "permissions": "-rw-r--r--",
624
+ },
625
+ {
626
+ "path": "model/",
627
+ "size_bytes": 0,
628
+ "modified": "2024-01-01T08:00:00Z",
629
+ "file_type": "directory",
630
+ "permissions": "drwxr-xr-x",
631
+ },
632
+ {
633
+ "path": "script.sh",
634
+ "size_bytes": 2048,
635
+ "modified": "2024-01-01T10:00:00Z",
636
+ "file_type": "file",
637
+ "permissions": "-rwxr-xr-x",
638
+ },
639
+ ],
640
+ }
641
+
642
+ view_cache_summary(mock_remote, "proj123", SORT_BY_PERMISSIONS, SORT_ORDER_ASC)
643
+
644
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
645
+
646
+ captured = capsys.readouterr()
647
+
648
+ config_pos = captured.out.find("config.json")
649
+ script_pos = captured.out.find("script.sh")
650
+ directory_pos = captured.out.find("model/")
651
+
652
+ assert config_pos != -1, "Config file not found in output"
653
+ assert script_pos != -1, "Script file not found in output"
654
+ assert directory_pos != -1, "Directory not found in output"
655
+
656
+ assert config_pos < script_pos
657
+ assert script_pos < directory_pos
658
+
659
+
660
+ def test_view_cache_summary_sort_by_permissions_desc(capsys):
661
+ """Test sorting by permissions in descending order."""
662
+ mock_api = Mock()
663
+ mock_remote = Mock(spec=BasetenRemote)
664
+ mock_remote.api = mock_api
665
+
666
+ mock_api.list_training_projects.return_value = [
667
+ {"id": "proj123", "name": "test-project"}
668
+ ]
669
+ mock_api.get_cache_summary.return_value = {
670
+ "timestamp": "2024-01-01T12:00:00Z",
671
+ "project_id": "proj123",
672
+ "file_summaries": [
673
+ {
674
+ "path": "config.json",
675
+ "size_bytes": 1024,
676
+ "modified": "2024-01-01T09:00:00Z",
677
+ "file_type": "file",
678
+ "permissions": "-rw-r--r--",
679
+ },
680
+ {
681
+ "path": "model/",
682
+ "size_bytes": 0,
683
+ "modified": "2024-01-01T08:00:00Z",
684
+ "file_type": "directory",
685
+ "permissions": "drwxr-xr-x",
686
+ },
687
+ {
688
+ "path": "script.sh",
689
+ "size_bytes": 2048,
690
+ "modified": "2024-01-01T10:00:00Z",
691
+ "file_type": "file",
692
+ "permissions": "-rwxr-xr-x",
693
+ },
694
+ ],
695
+ }
696
+
697
+ view_cache_summary(mock_remote, "proj123", SORT_BY_PERMISSIONS, SORT_ORDER_DESC)
698
+
699
+ mock_api.get_cache_summary.assert_called_once_with("proj123")
700
+
701
+ captured = capsys.readouterr()
702
+
703
+ directory_pos = captured.out.find("model/")
704
+ script_pos = captured.out.find("script.sh")
705
+ config_pos = captured.out.find("config.json")
706
+
707
+ assert directory_pos != -1, "Directory not found in output"
708
+ assert script_pos != -1, "Script file not found in output"
709
+ assert config_pos != -1, "Config file not found in output"
710
+
711
+ assert directory_pos < script_pos
712
+ assert script_pos < config_pos
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: truss
3
- Version: 0.10.11rc2
3
+ Version: 0.10.12
4
4
  Summary: A seamless bridge from model development to model delivery
5
5
  Project-URL: Repository, https://github.com/basetenlabs/truss
6
6
  Project-URL: Homepage, https://truss.baseten.co
@@ -11,16 +11,16 @@ truss/base/truss_spec.py,sha256=jFVF79CXoEEspl2kXBAPyi-rwISReIGTdobGpaIhwJw,5979
11
11
  truss/cli/chains_commands.py,sha256=y6pdIAGCcKOPG9bPuCXPfSA0onQm5x-tT_3blSBfPYg,16971
12
12
  truss/cli/cli.py,sha256=PaMkuwXZflkU7sa1tEoT_Zmy-iBkEZs1m4IVqcieaeo,30367
13
13
  truss/cli/remote_cli.py,sha256=G_xCKRXzgkCmkiZJhUFfsv5YSVgde1jLA5LPQitpZgI,1905
14
- truss/cli/train_commands.py,sha256=P9bdnpq1SgEGXBaVf9joKdsaCDX2v29P4MhLMuz-jYw,12344
14
+ truss/cli/train_commands.py,sha256=eFVx8Lba4ILYA4FCjRjMmABH-uUR_i7h4Io1mn7reZM,14423
15
15
  truss/cli/logs/base_watcher.py,sha256=KKyd7lIrdaEeDVt8EtjMioSPGVpLyOcF0ewyzE_GGdQ,2785
16
16
  truss/cli/logs/model_log_watcher.py,sha256=NACcP-wkcaroYa2Cb9BZC7Yr0554WZa_FSM2LXOf4A8,1263
17
17
  truss/cli/logs/training_log_watcher.py,sha256=r6HRqrLnz-PiKTUXiDYYxg4ZnP8vYcXlEX1YmgHhzlo,1173
18
18
  truss/cli/logs/utils.py,sha256=z-U_FG4BUzdZLbE3BnXb4DZQ0zt3LSZ3PiQpLaDuc3o,1031
19
- truss/cli/train/common.py,sha256=Es1yllSYxjM9x2uBzTGbYwyd8ML66cqqge0XO8_G_X0,992
20
- truss/cli/train/core.py,sha256=MBOhPSVYOU7wVh09uWQrJDEVOhJQug_2Odv3u6tCVTA,13855
19
+ truss/cli/train/common.py,sha256=xTR41U5FeSndXfNBBHF9wF5XwZH1sOIVFlv-XHjsKIU,1547
20
+ truss/cli/train/core.py,sha256=gfFRqxCxGsanbMNgJBJ0WFqVxZm4SwOEM2iyLS3K8Ns,18687
21
21
  truss/cli/train/deploy_from_checkpoint_config.yml,sha256=mktaVrfhN8Kjx1UveC4xr-gTW-kjwbHvq6bx_LpO-Wg,371
22
22
  truss/cli/train/deploy_from_checkpoint_config_whisper.yml,sha256=6GbOorYC8ml0UyOUvuBpFO_fuYtYE646JqsalR-D4oY,406
23
- truss/cli/train/metrics_watcher.py,sha256=ftrLQ5m7V1lAqcAvdGbMv5r0aF4D0lypfKjokCBQvLw,12798
23
+ truss/cli/train/metrics_watcher.py,sha256=smz-zrEsBj_-wJHI0pAZ-EAPrvfCWzq1eQjGiFNM-Mk,12755
24
24
  truss/cli/train/poller.py,sha256=TGRzELxsicga0bEXewSX1ujw6lfPmDnHd6nr8zvOFO8,3550
25
25
  truss/cli/train/types.py,sha256=alGtr4Q71GeB65PpGMhsoKygw4k_ncR6MKIP1ioP8rI,951
26
26
  truss/cli/train/deploy_checkpoints/__init__.py,sha256=wL-M2yu8PxO2tFvjwshXAfPnB-5TlvsBp2v_bdzimRU,99
@@ -29,7 +29,7 @@ truss/cli/train/deploy_checkpoints/deploy_checkpoints_helpers.py,sha256=6x5nS_Hn
29
29
  truss/cli/train/deploy_checkpoints/deploy_full_checkpoints.py,sha256=FYRG5KTMlxEMZS-RA_m2gp1wuqWbSpqt2RhdQfLibhA,3968
30
30
  truss/cli/train/deploy_checkpoints/deploy_lora_checkpoints.py,sha256=P91dIAzuhl2GlzmrWwCcYI7uCMT1Lm7C79JQHM_exN4,4442
31
31
  truss/cli/train/deploy_checkpoints/deploy_whisper_checkpoints.py,sha256=NSo2kEn-CxawGvUhn-xE81noxoTJ0cCv8X9fkrMxAsM,2617
32
- truss/cli/utils/common.py,sha256=aWnla4qMSEz57dRMTl7R-EaScsuEpnQUeziGUaIeqeU,6149
32
+ truss/cli/utils/common.py,sha256=ink9ZE0MsOv6PCFK_Ra5k1aHm281TXTnMpnLjf2PtUM,6585
33
33
  truss/cli/utils/output.py,sha256=GNjU85ZAMp5BI6Yij5wYXcaAvpm_kmHV0nHNmdkMxb0,646
34
34
  truss/cli/utils/self_upgrade.py,sha256=eTJZA4Wc8uUp4Qh6viRQp6bZm--wnQp7KWe5KRRpPtg,5427
35
35
  truss/contexts/docker_build_setup.py,sha256=cF4ExZgtYvrWxvyCAaUZUvV_DB_7__MqVomUDpalvKo,3925
@@ -52,10 +52,10 @@ truss/patch/truss_dir_patch_applier.py,sha256=ALnaVnu96g0kF2UmGuBFTua3lrXpwAy4sG
52
52
  truss/remote/remote_factory.py,sha256=-0gLh_yIyNDgD48Q6sR8Yo5dOMQg84lrHRvn_XR0n4s,3585
53
53
  truss/remote/truss_remote.py,sha256=TEe6h6by5-JLy7PMFsDN2QxIY5FmdIYN3bKvHHl02xM,8440
54
54
  truss/remote/baseten/__init__.py,sha256=XNqJW1zyp143XQc6-7XVwsUA_Q_ZJv_ausn1_Ohtw9Y,176
55
- truss/remote/baseten/api.py,sha256=6Nie4hv4z5I62boeCQvP3tGA0Pwu96bMgz1vp5Tkxao,24447
55
+ truss/remote/baseten/api.py,sha256=lJOt2i3tu0ZeCh4B_-hpfpjcZKgTHVnkxraooK7TUHw,24699
56
56
  truss/remote/baseten/auth.py,sha256=tI7s6cI2EZgzpMIzrdbILHyGwiHDnmoKf_JBhJXT55E,776
57
57
  truss/remote/baseten/core.py,sha256=uxtmBI9RAVHu1glIEJb5Q4ccJYLeZM1Cp5Svb9W68Yw,21965
58
- truss/remote/baseten/custom_types.py,sha256=g7MwgYaeqIxF-e170G5iEVLWiw5jgAnqXztIUqVkdyc,3227
58
+ truss/remote/baseten/custom_types.py,sha256=gUG7EkTeXzqcqznbKxz1SGTtHavNKGm1UoTFiln3LmQ,4309
59
59
  truss/remote/baseten/error.py,sha256=3TNTwwPqZnr4NRd9Sl6SfLUQR2fz9l6akDPpOntTpzA,578
60
60
  truss/remote/baseten/remote.py,sha256=Se8AES5mk8jxa8S9fN2DSG7wnsaV7ftRjJ4Uwc_w_S0,22544
61
61
  truss/remote/baseten/rest_client.py,sha256=_t3CWsWARt2u0C0fDsF4rtvkkHe-lH7KXoPxWXAkKd4,1185
@@ -139,6 +139,7 @@ truss/tests/test_truss_gatherer.py,sha256=bn288OEkC49YY0mhly4cAl410ktZPfElNdWwZy
139
139
  truss/tests/test_truss_handle.py,sha256=-xz9VXkecXDTslmQZ-dmUmQLnvD0uumRqHS2uvGlMBA,30750
140
140
  truss/tests/test_util.py,sha256=hs1bNMkXKEdoPRx4Nw-NAEdoibR92OubZuADGmbiYsQ,1344
141
141
  truss/tests/cli/test_cli.py,sha256=yfbVS5u1hnAmmA8mJ539vj3lhH-JVGUvC4Q_Mbort44,787
142
+ truss/tests/cli/train/test_cache_view.py,sha256=aVRCh3atRpFbJqyYgq7N-vAW0DiKMftQ7ajUqO2ClOg,22606
142
143
  truss/tests/cli/train/test_deploy_checkpoints.py,sha256=wQZ3DPLPAyXE3iaQiyHJTBO15v_gXN44eDk1StYkKmM,44764
143
144
  truss/tests/cli/train/test_train_cli_core.py,sha256=T1Xa6-NRk2nTJGX6sXaA8x4qCwL3Ini72PBI2gW7rYM,7879
144
145
  truss/tests/cli/train/resources/test_deploy_from_checkpoint_config.yml,sha256=GF7r9l0KaeXiUYCPSBpeMPd2QG6PeWWyI12NdbqLOgc,1930
@@ -360,11 +361,11 @@ truss_chains/remote_chainlet/stub.py,sha256=Y2gDUzMY9WRaQNHIz-o4dfLUfFyYV9dUhIRQ
360
361
  truss_chains/remote_chainlet/utils.py,sha256=O_5P-VAUvg0cegEW1uKCOf5EBwD8rEGYVoGMivOmc7k,22374
361
362
  truss_train/__init__.py,sha256=7hE6j6-u6UGzCGaNp3CsCN0kAVjBus1Ekups-Bk0fi4,837
362
363
  truss_train/definitions.py,sha256=V985HhY4rdXL10DZxpFEpze9ScxzWErMht4WwaPknGU,6789
363
- truss_train/deployment.py,sha256=fDYRfzFRtVKMRVG0bKXYPmx6HXwLE0ukSQ0f81hG8kk,3020
364
+ truss_train/deployment.py,sha256=lWWANSuzBWu2M4oK4qD7n-oVR1JKdmw2Pn5BJQHg-Ck,3074
364
365
  truss_train/loader.py,sha256=0o66EjBaHc2YY4syxxHVR4ordJWs13lNXnKjKq2wq0U,1630
365
366
  truss_train/public_api.py,sha256=9N_NstiUlmBuLUwH_fNG_1x7OhGCytZLNvqKXBlStrM,1220
366
- truss-0.10.11rc2.dist-info/METADATA,sha256=rEMcJZISmCLDeN8M9pcdUFXyiTxeERavaJszW3ZlDz0,6673
367
- truss-0.10.11rc2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
368
- truss-0.10.11rc2.dist-info/entry_points.txt,sha256=-MwKfHHQHQ6j0HqIgvxrz3CehCmczDLTD-OsRHnjjuU,130
369
- truss-0.10.11rc2.dist-info/licenses/LICENSE,sha256=FTqGzu85i-uw1Gi8E_o0oD60bH9yQ_XIGtZbA1QUYiw,1064
370
- truss-0.10.11rc2.dist-info/RECORD,,
367
+ truss-0.10.12.dist-info/METADATA,sha256=D39-4SrfGGxR5CjGVA9z4w8f9yB4JODhNM1sowDnnmw,6670
368
+ truss-0.10.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
369
+ truss-0.10.12.dist-info/entry_points.txt,sha256=-MwKfHHQHQ6j0HqIgvxrz3CehCmczDLTD-OsRHnjjuU,130
370
+ truss-0.10.12.dist-info/licenses/LICENSE,sha256=FTqGzu85i-uw1Gi8E_o0oD60bH9yQ_XIGtZbA1QUYiw,1064
371
+ truss-0.10.12.dist-info/RECORD,,
truss_train/deployment.py CHANGED
@@ -83,4 +83,5 @@ def create_training_job_from_file(
83
83
  training_project=training_project,
84
84
  config=config,
85
85
  )
86
+ job_resp["job_object"] = training_project.job
86
87
  return job_resp