11import json
2+ import os
23import tarfile
34import tempfile
45from dataclasses import dataclass
1617from truss .cli .train .types import PrepareCheckpointArgs , PrepareCheckpointResult
1718from truss .cli .utils import common as cli_common
1819from truss .cli .utils .output import console
20+ from truss .remote .baseten .custom_types import (
21+ FileSummary ,
22+ FileSummaryWithTotalSize ,
23+ GetCacheSummaryResponseV1 ,
24+ )
1925from truss .remote .baseten .remote import BasetenRemote
2026from truss_train import loader
2127from truss_train .definitions import DeployCheckpointsConfig
@@ -446,6 +452,44 @@ def fetch_project_by_name_or_id(
446452 raise click .ClickException (f"Error fetching project: { str (e )} " )
447453
448454
455+ def create_file_summary_with_directory_sizes (
456+ files : list [FileSummary ],
457+ ) -> list [FileSummaryWithTotalSize ]:
458+ directory_sizes = calculate_directory_sizes (files )
459+ return [
460+ FileSummaryWithTotalSize (
461+ file_summary = file_info ,
462+ total_size = directory_sizes .get (file_info .path , file_info .size_bytes ),
463+ )
464+ for file_info in files
465+ ]
466+
467+
468+ def calculate_directory_sizes (
469+ files : list [FileSummary ], max_depth : int = 100
470+ ) -> dict [str , int ]:
471+ directory_sizes = {}
472+
473+ for file_info in files :
474+ if file_info .file_type == "directory" :
475+ directory_sizes [file_info .path ] = 0
476+
477+ for file_info in files :
478+ current_path = file_info .path
479+ for i in range (max_depth ):
480+ if current_path is None :
481+ break
482+ if current_path in directory_sizes :
483+ directory_sizes [current_path ] += file_info .size_bytes
484+ # Move to parent directory
485+ parent = os .path .dirname (current_path )
486+ if parent == current_path : # Reached root
487+ break
488+ current_path = parent
489+
490+ return directory_sizes
491+
492+
449493def view_cache_summary (
450494 remote_provider : BasetenRemote ,
451495 project_id : str ,
@@ -454,71 +498,63 @@ def view_cache_summary(
454498):
455499 """View cache summary for a training project."""
456500 try :
457- cache_data = remote_provider .api .get_cache_summary (project_id )
501+ raw_cache_data = remote_provider .api .get_cache_summary (project_id )
458502
459- if not cache_data :
503+ if not raw_cache_data :
460504 console .print ("No cache summary found for this project." , style = "yellow" )
461505 return
462506
507+ cache_data = GetCacheSummaryResponseV1 .model_validate (raw_cache_data )
508+
463509 table = rich .table .Table (title = f"Cache summary for project: { project_id } " )
464510 table .add_column ("File Path" , style = "cyan" )
465511 table .add_column ("Size" , style = "green" )
466512 table .add_column ("Modified" , style = "yellow" )
467513 table .add_column ("Type" )
468514 table .add_column ("Permissions" , style = "magenta" )
469515
470- files = cache_data .get ( " file_summaries" , [])
516+ files = cache_data .file_summaries
471517 if not files :
472518 console .print ("No files found in cache." , style = "yellow" )
473519 return
474520
475- reverse = order == SORT_ORDER_DESC
521+ files_with_total_sizes = create_file_summary_with_directory_sizes ( files )
476522
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 )
523+ reverse = order == SORT_ORDER_DESC
524+ sort_key = _get_sort_key (sort_by )
525+ files_with_total_sizes .sort (key = sort_key , reverse = reverse )
491526
527+ total_size = sum (
528+ file_info .file_summary .size_bytes for file_info in files_with_total_sizes
529+ )
492530 total_size_str = common .format_bytes_to_human_readable (total_size )
493531
494532 console .print (
495- f"📅 Cache captured at: { cache_data .get ('timestamp' , 'Unknown' )} " ,
496- style = "bold blue" ,
533+ f"📅 Cache captured at: { cache_data .timestamp } " , style = "bold blue"
497534 )
535+ console .print (f"📁 Project ID: { cache_data .project_id } " , style = "bold blue" )
536+ console .print ()
498537 console .print (
499- f"📁 Project ID: { cache_data .get ('project_id' , 'Unknown' )} " ,
500- style = "bold blue" ,
538+ f"📊 Total files: { len (files_with_total_sizes )} " , style = "bold green"
501539 )
502- console .print ()
503- console .print (f"📊 Total files: { len (files )} " , style = "bold green" )
504540 console .print (f"💾 Total size: { total_size_str } " , style = "bold green" )
505541 console .print ()
506542
507- for file_info in files :
508- size_bytes = file_info .get ( "size_bytes" , 0 )
543+ for file_info in files_with_total_sizes :
544+ total_size = file_info .total_size
509545
510- size_str = cli_common .format_bytes_to_human_readable (int (size_bytes ))
546+ size_str = cli_common .format_bytes_to_human_readable (int (total_size ))
511547
512548 modified_str = cli_common .format_localized_time (
513- file_info .get ( " modified" , "Unknown" )
549+ file_info .file_summary . modified
514550 )
515551
516552 table .add_row (
517- file_info .get ( " path" , "Unknown" ) ,
553+ file_info .file_summary . path ,
518554 size_str ,
519555 modified_str ,
520- file_info .get ( " file_type" , "Unknown" ) ,
521- file_info .get ( " permissions" , "Unknown" ) ,
556+ file_info .file_summary . file_type or "Unknown" ,
557+ file_info .file_summary . permissions or "Unknown" ,
522558 )
523559
524560 console .print (table )
@@ -528,6 +564,21 @@ def view_cache_summary(
528564 raise
529565
530566
567+ def _get_sort_key (sort_by : str ) -> Callable [[FileSummaryWithTotalSize ], Any ]:
568+ if sort_by == SORT_BY_FILEPATH :
569+ return lambda x : x .file_summary .path
570+ elif sort_by == SORT_BY_SIZE :
571+ return lambda x : x .total_size
572+ elif sort_by == SORT_BY_MODIFIED :
573+ return lambda x : x .file_summary .modified
574+ elif sort_by == SORT_BY_TYPE :
575+ return lambda x : x .file_summary .file_type or ""
576+ elif sort_by == SORT_BY_PERMISSIONS :
577+ return lambda x : x .file_summary .permissions or ""
578+ else :
579+ raise ValueError (f"Invalid --sort argument: { sort_by } " )
580+
581+
531582def view_cache_summary_by_project (
532583 remote_provider : BasetenRemote ,
533584 project_identifier : str ,
0 commit comments