Skip to content

Commit 34110a2

Browse files
[CLI] Show RESOURCES only in verbose mode, otherwise only show GPU. Also move (spot) to `PRICE.
1 parent bd10c19 commit 34110a2

3 files changed

Lines changed: 52 additions & 12 deletions

File tree

src/dstack/_internal/cli/utils/run.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def _get_run_status_style(status: RunStatus, status_message: Optional[str] = Non
189189
RunStatus.SUBMITTED: "grey",
190190
RunStatus.PROVISIONING: "deep_sky_blue1",
191191
RunStatus.RUNNING: "sea_green3",
192-
RunStatus.TERMINATING: "white",
192+
RunStatus.TERMINATING: "deep_sky_blue1",
193193
RunStatus.TERMINATED: "grey",
194194
RunStatus.FAILED: "indian_red1",
195195
RunStatus.DONE: "grey",
@@ -218,7 +218,7 @@ def _get_job_status_style(status_message: str, job_status: JobStatus) -> str:
218218
JobStatus.PROVISIONING: "deep_sky_blue1",
219219
JobStatus.PULLING: "sea_green3",
220220
JobStatus.RUNNING: "sea_green3",
221-
JobStatus.TERMINATING: "white",
221+
JobStatus.TERMINATING: "deep_sky_blue1",
222222
JobStatus.TERMINATED: "grey",
223223
JobStatus.ABORTED: "gold1",
224224
JobStatus.FAILED: "indian_red1",
@@ -237,9 +237,12 @@ def get_runs_table(
237237
table = Table(box=None, expand=shutil.get_terminal_size(fallback=(120, 40)).columns <= 110)
238238
table.add_column("NAME", style="bold", no_wrap=True, ratio=2)
239239
table.add_column("BACKEND", style="grey58", ratio=2)
240-
table.add_column("RESOURCES", ratio=3 if not verbose else 2)
241240
if verbose:
242-
table.add_column("INSTANCE TYPE", no_wrap=True, ratio=1)
241+
table.add_column("RESOURCES", style="grey58", ratio=3)
242+
else:
243+
table.add_column("GPU", ratio=2)
244+
if verbose:
245+
table.add_column("INSTANCE TYPE", style="grey58", no_wrap=True, ratio=1)
243246
table.add_column("PRICE", style="grey58", ratio=1)
244247
table.add_column("STATUS", no_wrap=True, ratio=1)
245248
if verbose or any(
@@ -270,11 +273,14 @@ def get_runs_table(
270273
run.status, status_message=status_text if run.status.is_finished() else status_text
271274
)
272275

276+
resource_key = "RESOURCES" if verbose else "GPU"
273277
run_row: Dict[Union[str, int], Any] = {
274278
"NAME": run.run_spec.run_name
275279
+ (f" [secondary]deployment={run.deployment_num}[/]" if show_deployment_num else ""),
276280
"SUBMITTED": format_date(run.submitted_at),
277281
"STATUS": f"[{status_style}]{status_text}[/]",
282+
resource_key: "-", # Default value when no provisioning data
283+
"PRICE": "-", # Default value when no provisioning data
278284
}
279285
if run.error:
280286
run_row["ERROR"] = run.error
@@ -303,6 +309,8 @@ def get_runs_table(
303309
),
304310
"SUBMITTED": format_date(latest_job_submission.submitted_at),
305311
"ERROR": latest_job_submission.error,
312+
resource_key: "-", # Initialize with default, will be updated if provisioning data exists
313+
"PRICE": "-", # Initialize with default, will be updated if provisioning data exists
306314
}
307315
jpd = latest_job_submission.job_provisioning_data
308316
if jpd is not None:
@@ -315,16 +323,28 @@ def get_runs_table(
315323
instance_type += f" ({jrd.offer.blocks}/{jrd.offer.total_blocks})"
316324
if jpd.reservation:
317325
instance_type += f" ({jpd.reservation})"
318-
job_row.update(
319-
{
320-
"BACKEND": f"{jpd.backend.value.replace('remote', 'ssh')} ({jpd.region})",
321-
"RESOURCES": resources.pretty_format(include_spot=True),
322-
"INSTANCE TYPE": instance_type,
323-
"PRICE": f"${jpd.price:.4f}".rstrip("0").rstrip("."),
324-
}
326+
resource_value = (
327+
resources.pretty_format(include_spot=False)
328+
if verbose
329+
else resources.pretty_format(gpu_only=True, include_spot=False)
325330
)
331+
price_str = f"${jpd.price:.4f}".rstrip("0").rstrip(".")
332+
if resources.spot:
333+
price_str += " (spot)"
334+
update_dict: Dict[Union[str, int], Any] = {
335+
"BACKEND": f"{jpd.backend.value.replace('remote', 'ssh')} ({jpd.region})",
336+
resource_key: resource_value,
337+
"INSTANCE TYPE": instance_type,
338+
"PRICE": price_str,
339+
}
340+
job_row.update(update_dict)
326341
if merge_job_rows:
342+
# Merge run_row into job_row, but preserve job_row's resource_key if it was set
343+
resource_value = job_row.get(resource_key, "-")
344+
price_value = job_row.get("PRICE", "")
327345
job_row.update(run_row)
346+
job_row[resource_key] = resource_value # Restore job-specific resource value
347+
job_row["PRICE"] = price_value # Restore job-specific price value
328348
job_row["STATUS"] = run_row["STATUS"]
329349
add_row_from_dict(table, job_row, style="secondary" if len(run.jobs) != 1 else None)
330350

src/dstack/_internal/core/models/instances.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,23 @@ def _pretty_format(
8383
gpus: List[Gpu],
8484
spot: bool,
8585
include_spot: bool = False,
86+
gpu_only: bool = False,
8687
) -> str:
88+
if gpu_only:
89+
if not gpus:
90+
return "-"
91+
gpu = gpus[0]
92+
gpu_resources = {
93+
"gpu_name": gpu.name,
94+
"gpu_count": len(gpus),
95+
}
96+
if gpu.memory_mib > 0:
97+
gpu_resources["gpu_memory"] = f"{gpu.memory_mib / 1024:.0f}GB"
98+
output = pretty_resources(**gpu_resources)
99+
if include_spot and spot:
100+
output += " (spot)"
101+
return output
102+
87103
resources = {}
88104
if cpus > 0:
89105
resources["cpus"] = cpus
@@ -103,7 +119,7 @@ def _pretty_format(
103119
output += " (spot)"
104120
return output
105121

106-
def pretty_format(self, include_spot: bool = False) -> str:
122+
def pretty_format(self, include_spot: bool = False, gpu_only: bool = False) -> str:
107123
return Resources._pretty_format(
108124
self.cpus,
109125
self.cpu_arch,
@@ -112,6 +128,7 @@ def pretty_format(self, include_spot: bool = False) -> str:
112128
self.gpus,
113129
self.spot,
114130
include_spot,
131+
gpu_only,
115132
)
116133

117134

src/tests/_internal/cli/utils/test_run.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ async def create_run_with_job(
114114
run_status = RunStatus.FAILED
115115
elif job_status in [JobStatus.TERMINATED, JobStatus.ABORTED]:
116116
run_status = RunStatus.TERMINATED
117+
elif job_status == JobStatus.TERMINATING:
118+
run_status = RunStatus.TERMINATING
117119
elif job_status == JobStatus.PROVISIONING:
118120
run_status = RunStatus.PROVISIONING
119121
elif job_status == JobStatus.PULLING:
@@ -268,6 +270,7 @@ async def test_simple_run(self, session: AsyncSession):
268270
(JobStatus.RUNNING, None, None, "running", "bold sea_green3"),
269271
(JobStatus.PROVISIONING, None, None, "provisioning", "bold deep_sky_blue1"),
270272
(JobStatus.PULLING, None, None, "pulling", "bold sea_green3"),
273+
(JobStatus.TERMINATING, None, None, "terminating", "bold deep_sky_blue1"),
271274
],
272275
)
273276
async def test_status_messages(

0 commit comments

Comments
 (0)