From e6bee75d9120bb98369404e88175e6698dd555f8 Mon Sep 17 00:00:00 2001 From: ciaranbor Date: Fri, 20 Mar 2026 13:17:33 +0000 Subject: [PATCH] Use slider for storage limit --- dashboard/src/routes/downloads/+page.svelte | 78 +++++++++++++++++---- src/exo/master/placement.py | 5 +- src/exo/master/tests/test_placement.py | 14 ++-- 3 files changed, 76 insertions(+), 21 deletions(-) diff --git a/dashboard/src/routes/downloads/+page.svelte b/dashboard/src/routes/downloads/+page.svelte index ca3dc129..8086c139 100644 --- a/dashboard/src/routes/downloads/+page.svelte +++ b/dashboard/src/routes/downloads/+page.svelte @@ -225,6 +225,11 @@ let configPolicy = $state<"manual" | "auto-evict">("manual"); let configSaving = $state(false); let configApplyAll = $state(false); + let configDiskTotalGb = $derived( + storageConfigNode + ? Math.round((storageConfigNode.diskTotal ?? 0) / 1024 ** 3) + : 0, + ); function openStorageConfig(col: NodeColumn) { storageConfigNode = col; @@ -1074,31 +1079,52 @@ { + if (!configNoLimit && configMaxGb == null) { + configMaxGb = configDiskTotalGb || 50; + } + }} class="accent-exo-yellow w-4 h-4" /> Unlimited storage - -
- + +
+
+ + + {configMaxGb ?? 0} GB + +
+
+ 1 GB + {Math.max(configDiskTotalGb, configMaxGb ?? 1)} GB +
@@ -1167,4 +1193,30 @@ table { min-width: max-content; } + + .slider { + background: rgba(255, 255, 255, 0.1); + } + .slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: #f5c518; + cursor: pointer; + } + .slider::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + border: none; + background: #f5c518; + cursor: pointer; + } + .slider:disabled::-webkit-slider-thumb { + cursor: not-allowed; + } + .slider:disabled::-moz-range-thumb { + cursor: not-allowed; + } diff --git a/src/exo/master/placement.py b/src/exo/master/placement.py index c8a7f797..aaad8c9b 100644 --- a/src/exo/master/placement.py +++ b/src/exo/master/placement.py @@ -32,10 +32,11 @@ from exo.shared.types.memory import Memory from exo.shared.types.profiling import MemoryUsage, NodeNetworkInfo from exo.shared.types.tasks import Task, TaskId, TaskStatus from exo.shared.types.worker.downloads import ( - ModelModelDownloadFailed, + ModelDownloadFailed, ModelDownloading, ModelNotDownloading, ModelReady, + ModelRejected, ModelStatus, ) from exo.shared.types.worker.instances import ( @@ -87,6 +88,8 @@ def _get_node_download_fraction( return progress.downloaded.in_bytes / total if total > 0 else 0.0 case ModelDownloadFailed(): return 0.0 + case ModelRejected(): + return 0.0 return 0.0 diff --git a/src/exo/master/tests/test_placement.py b/src/exo/master/tests/test_placement.py index 3a8a36c0..bf01cbcb 100644 --- a/src/exo/master/tests/test_placement.py +++ b/src/exo/master/tests/test_placement.py @@ -26,10 +26,10 @@ from exo.shared.types.tasks import TaskId, TaskStatus, TextGeneration from exo.shared.types.text_generation import InputMessage, TextGenerationTaskParams from exo.shared.types.topology import Connection, SocketConnection from exo.shared.types.worker.downloads import ( - DownloadCompleted, - DownloadFailed, - DownloadOngoing, DownloadProgressData, + ModelDownloadFailed, + ModelDownloading, + ModelReady, ) from exo.shared.types.worker.instances import ( Instance, @@ -624,7 +624,7 @@ def test_placement_prefers_cycle_with_downloaded_model( # node_b has the model fully downloaded, node_a does not download_status = { node_b: [ - DownloadCompleted( + ModelReady( node_id=node_b, shard_metadata=shard_meta, total=model_card.storage_size, @@ -671,7 +671,7 @@ def test_placement_prefers_cycle_with_higher_download_progress( # node_a: 30% downloaded, node_b: 80% downloaded download_status = { node_a: [ - DownloadOngoing( + ModelDownloading( node_id=node_a, shard_metadata=shard_meta, download_progress=DownloadProgressData( @@ -687,7 +687,7 @@ def test_placement_prefers_cycle_with_higher_download_progress( ), ], node_b: [ - DownloadOngoing( + ModelDownloading( node_id=node_b, shard_metadata=shard_meta, download_progress=DownloadProgressData( @@ -744,7 +744,7 @@ def test_placement_does_not_prefer_cycle_with_failed_download( # node_b has a failed download — should not be preferred download_status = { node_b: [ - DownloadFailed( + ModelDownloadFailed( node_id=node_b, shard_metadata=shard_meta, error_message="connection reset",