From 47b8e0ce12e3bd0b417a13d50188d8fc2e9dc73a Mon Sep 17 00:00:00 2001 From: Drifter4242 Date: Mon, 5 Jan 2026 11:27:14 +0000 Subject: [PATCH] feat: remember last launch settings (model, sharding, instance type) (#1028) ## Motivation Saves the last launch settings, so that the next time you run exo it will default to the same launch settings. This is just a small quality of life improvement. ## Changes When you launch it saves the settings to the web browser local storage. When it fills out the model list, it reads the settings and sets the default. I reviewed, tested and edited the code, but some of the code was written by Claude Opus. I hope that's ok. ## Why It Works See above ## Test Plan ### Manual Testing I have two Macbook Studio M3 Ultras, each with 512Gb ram, connected with Thunderbolt 5. I ran Kimi K2 Thinking with MLX Ring and Tensor Split. I ran exo multiple times to confirm that the default works. ### Automated Testing No changes to automated testing. --- dashboard/src/routes/+page.svelte | 67 +++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/dashboard/src/routes/+page.svelte b/dashboard/src/routes/+page.svelte index 9f75eac8..abc603ff 100644 --- a/dashboard/src/routes/+page.svelte +++ b/dashboard/src/routes/+page.svelte @@ -51,6 +51,59 @@ const sidebarVisible = $derived(chatSidebarVisible()); let selectedSharding = $state<'Pipeline' | 'Tensor'>('Pipeline'); type InstanceMeta = 'MlxRing' | 'MlxIbv' | 'MlxJaccl'; + // Launch defaults persistence + const LAUNCH_DEFAULTS_KEY = 'exo-launch-defaults'; + interface LaunchDefaults { + modelId: string | null; + sharding: 'Pipeline' | 'Tensor'; + instanceType: InstanceMeta; + minNodes: number; + } + + function saveLaunchDefaults(): void { + const defaults: LaunchDefaults = { + modelId: selectedPreviewModelId(), + sharding: selectedSharding, + instanceType: selectedInstanceType, + minNodes: selectedMinNodes, + }; + try { + localStorage.setItem(LAUNCH_DEFAULTS_KEY, JSON.stringify(defaults)); + } catch (e) { + console.warn('Failed to save launch defaults:', e); + } + } + + function loadLaunchDefaults(): LaunchDefaults | null { + try { + const stored = localStorage.getItem(LAUNCH_DEFAULTS_KEY); + if (!stored) return null; + return JSON.parse(stored) as LaunchDefaults; + } catch (e) { + console.warn('Failed to load launch defaults:', e); + return null; + } + } + + function applyLaunchDefaults(availableModels: Array<{id: string}>, maxNodes: number): void { + const defaults = loadLaunchDefaults(); + if (!defaults) return; + + // Apply sharding and instance type unconditionally + selectedSharding = defaults.sharding; + selectedInstanceType = defaults.instanceType; + + // Apply minNodes if valid (between 1 and maxNodes) + if (defaults.minNodes && defaults.minNodes >= 1 && defaults.minNodes <= maxNodes) { + selectedMinNodes = defaults.minNodes; + } + + // Only apply model if it exists in the available models + if (defaults.modelId && availableModels.some(m => m.id === defaults.modelId)) { + selectPreviewModel(defaults.modelId); + } + } + let selectedInstanceType = $state('MlxRing'); let selectedMinNodes = $state(1); let minNodesInitialized = $state(false); @@ -298,6 +351,9 @@ function toggleInstanceDownloadDetails(nodeId: string): void { const data = await response.json(); // API returns { data: [{ id, name }] } format models = data.data || []; + // Restore last launch defaults if available + const currentNodeCount = topologyData() ? Object.keys(topologyData()!.nodes).length : 1; + applyLaunchDefaults(models, currentNodeCount); } } catch (error) { console.error('Failed to fetch models:', error); @@ -988,6 +1044,7 @@ function toggleInstanceDownloadDetails(nodeId: string): void { function handleSliderMouseUp() { isDraggingSlider = false; + saveLaunchDefaults(); } // Handle touch events for mobile @@ -1007,6 +1064,7 @@ function toggleInstanceDownloadDetails(nodeId: string): void { function handleSliderTouchEnd() { isDraggingSlider = false; + saveLaunchDefaults(); } const nodeCount = $derived(data ? Object.keys(data.nodes).length : 0); @@ -1464,6 +1522,7 @@ function toggleInstanceDownloadDetails(nodeId: string): void { onclick={() => { if (modelCanFit) { selectPreviewModel(model.id); + saveLaunchDefaults(); isModelDropdownOpen = false; modelDropdownSearch = ''; } @@ -1497,7 +1556,7 @@ function toggleInstanceDownloadDetails(nodeId: string): void {
Sharding: