Skip to content

Commit fffba52

Browse files
committed
fix: persist Parakeet model selection across restarts
- Fix validation logic to check ParakeetManager for Parakeet models - Previously only checked WhisperManager, causing valid Parakeet models to be cleared - Remove redundant selectedModel state from useModelManagement hook - Use settings.current_model as single source of truth for model selection - Update OnboardingDesktop to use settings directly Fixes model persistence issue where Parakeet models would reset to "none" on dev server restart
1 parent 33c3913 commit fffba52

File tree

3 files changed

+43
-28
lines changed

3 files changed

+43
-28
lines changed

src-tauri/src/lib.rs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,23 +1628,45 @@ async fn perform_startup_checks(app: tauri::AppHandle) {
16281628
}
16291629
}
16301630

1631-
// Check current model is still available
1631+
// Check current model is still available based on engine type
16321632
let mut _model_available = false;
16331633
if let Some(current_model) = store
16341634
.get("current_model")
16351635
.and_then(|v| v.as_str().map(|s| s.to_string()))
16361636
{
16371637
if !current_model.is_empty() {
1638-
if let Some(whisper_manager) =
1639-
app.try_state::<AsyncRwLock<whisper::manager::WhisperManager>>()
1640-
{
1641-
let downloaded = whisper_manager.read().await.get_downloaded_model_names();
1642-
_model_available = downloaded.contains(&current_model);
1643-
if !_model_available {
1644-
log::warn!("Current model '{}' no longer available", current_model);
1645-
// Clear the selection
1646-
store.set("current_model", serde_json::Value::String(String::new()));
1647-
let _ = store.save();
1638+
// Get the engine type to determine which manager to check
1639+
let engine = store
1640+
.get("current_model_engine")
1641+
.and_then(|v| v.as_str().map(|s| s.to_string()))
1642+
.unwrap_or_else(|| "whisper".to_string());
1643+
1644+
if engine == "parakeet" {
1645+
// Check ParakeetManager for Parakeet models
1646+
if let Some(parakeet_manager) = app.try_state::<parakeet::ParakeetManager>() {
1647+
let models = parakeet_manager.list_models();
1648+
_model_available = models.iter().any(|m| m.name == current_model && m.downloaded);
1649+
if !_model_available {
1650+
log::warn!("Current Parakeet model '{}' no longer available", current_model);
1651+
// Clear the selection
1652+
store.set("current_model", serde_json::Value::String(String::new()));
1653+
store.set("current_model_engine", serde_json::Value::String("whisper".to_string()));
1654+
let _ = store.save();
1655+
}
1656+
}
1657+
} else {
1658+
// Check WhisperManager for Whisper models (default)
1659+
if let Some(whisper_manager) =
1660+
app.try_state::<AsyncRwLock<whisper::manager::WhisperManager>>()
1661+
{
1662+
let downloaded = whisper_manager.read().await.get_downloaded_model_names();
1663+
_model_available = downloaded.contains(&current_model);
1664+
if !_model_available {
1665+
log::warn!("Current Whisper model '{}' no longer available", current_model);
1666+
// Clear the selection
1667+
store.set("current_model", serde_json::Value::String(String::new()));
1668+
let _ = store.save();
1669+
}
16481670
}
16491671
}
16501672
}

src/components/onboarding/OnboardingDesktop.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ export const OnboardingDesktop = function OnboardingDesktop({
6767
modelOrder,
6868
downloadProgress,
6969
verifyingModels,
70-
selectedModel,
71-
setSelectedModel,
7270
loadModels,
7371
downloadModel,
7472
cancelDownload,
@@ -114,14 +112,14 @@ export const OnboardingDesktop = function OnboardingDesktop({
114112

115113
useEffect(() => {
116114
// Only auto-select if no model is selected yet
117-
if (!selectedModel) {
115+
if (!settings?.current_model) {
118116
// Find a downloaded model
119117
const downloadedModel = Object.entries(models).find(([_, m]) => m.downloaded);
120118
if (downloadedModel) {
121-
setSelectedModel(downloadedModel[0]);
119+
updateSettings({ current_model: downloadedModel[0] });
122120
}
123121
}
124-
}, [models]); // Only depend on models, not selectedModel to avoid loops
122+
}, [models, settings?.current_model]); // Depend on both to react to changes
125123

126124
const checkPermissions = async () => {
127125
// Use the hook methods to check permissions
@@ -184,7 +182,7 @@ export const OnboardingDesktop = function OnboardingDesktop({
184182

185183
await updateSettings({
186184
hotkey: hotkey,
187-
current_model: selectedModel || "",
185+
current_model: settings?.current_model || "",
188186
current_model_engine: 'whisper',
189187
onboarding_completed: true
190188
});
@@ -242,7 +240,7 @@ export const OnboardingDesktop = function OnboardingDesktop({
242240
// automation check removed for now
243241
case "models":
244242
// User can proceed if they have selected a model that is downloaded
245-
return selectedModel !== null && models[selectedModel]?.downloaded === true;
243+
return settings?.current_model !== null && settings?.current_model !== "" && models[settings.current_model]?.downloaded === true;
246244
default:
247245
return true;
248246
}
@@ -448,9 +446,9 @@ export const OnboardingDesktop = function OnboardingDesktop({
448446
model={model}
449447
downloadProgress={progress}
450448
isVerifying={verifyingModels.has(name)}
451-
isSelected={selectedModel === name}
449+
isSelected={settings?.current_model === name}
452450
onDownload={downloadModel}
453-
onSelect={setSelectedModel}
451+
onSelect={(modelName) => updateSettings({ current_model: modelName })}
454452
onCancelDownload={cancelDownload}
455453
showSelectButton={model.downloaded}
456454
/>

src/hooks/useModelManagement.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function useModelManagement(options: UseModelManagementOptions = {}) {
4848
const [downloadProgress, setDownloadProgress] = useState<Record<string, number>>({});
4949
const [verifyingModels, setVerifyingModels] = useState<Set<string>>(new Set());
5050

51-
const [selectedModel, setSelectedModel] = useState<string | null>(null);
51+
// Removed selectedModel state - now using settings.current_model directly
5252
const [isLoading, setIsLoading] = useState(false);
5353

5454
// Load models from backend
@@ -164,17 +164,14 @@ export function useModelManagement(options: UseModelManagementOptions = {}) {
164164
// Refresh model status
165165
await loadModels();
166166

167-
// If deleted model was the current one, clear selection
168-
if (selectedModel === modelName) {
169-
setSelectedModel(null);
170-
}
167+
// Model selection clearing is handled by the component via settings
171168
} catch (error) {
172169
console.error("Failed to delete model:", error);
173170
if (showToasts) {
174171
toast.error(`Failed to delete model: ${error}`);
175172
}
176173
}
177-
}, [selectedModel, loadModels, showToasts]);
174+
}, [loadModels, showToasts]);
178175

179176
// Setup event listeners BEFORE any other effects
180177
useEffect(() => {
@@ -319,11 +316,9 @@ export function useModelManagement(options: UseModelManagementOptions = {}) {
319316
modelOrder,
320317
downloadProgress,
321318
verifyingModels,
322-
selectedModel,
323319
isLoading,
324320

325321
// Actions
326-
setSelectedModel,
327322
loadModels,
328323
downloadModel,
329324
cancelDownload,

0 commit comments

Comments
 (0)