diff --git a/app/frontend/src/App.jsx b/app/frontend/src/App.jsx index de89a38..dc92a01 100644 --- a/app/frontend/src/App.jsx +++ b/app/frontend/src/App.jsx @@ -155,6 +155,7 @@ function App() { defragThold: 0.1, batchSize: 512, ubatchSize: 512, + preferredBackend: "auto", }; }); diff --git a/app/frontend/src/components/Generator.jsx b/app/frontend/src/components/Generator.jsx index 2175ca2..856585e 100644 --- a/app/frontend/src/components/Generator.jsx +++ b/app/frontend/src/components/Generator.jsx @@ -115,7 +115,8 @@ function Generator({ await startLlm(targetModel, { threads: cores, contextSize: context, - gpuLayers: -1 + gpuLayers: -1, + preferredBackend: textSettings?.preferredBackend }); } diff --git a/app/frontend/src/components/ModelManager.jsx b/app/frontend/src/components/ModelManager.jsx index c1b386c..ed1be80 100644 --- a/app/frontend/src/components/ModelManager.jsx +++ b/app/frontend/src/components/ModelManager.jsx @@ -878,7 +878,8 @@ function ModelManager({ await startLlm(modelId, { threads: cores, contextSize: context, - gpuLayers: -1 + gpuLayers: -1, + preferredBackend: textSettings?.preferredBackend }); setActiveLlmModel(modelId); setLlmRunning(true); diff --git a/app/frontend/src/components/Settings.jsx b/app/frontend/src/components/Settings.jsx index f6135e3..c94d98d 100644 --- a/app/frontend/src/components/Settings.jsx +++ b/app/frontend/src/components/Settings.jsx @@ -257,6 +257,49 @@ function Settings({ error: null, }); + const [pendingTextSettings, setPendingTextSettings] = useState(textSettings || {}); + + useEffect(() => { + if (textSettings) { + setPendingTextSettings(textSettings); + } + }, [textSettings]); + + const EDITABLE_TEXT_KEYS = [ + "systemPrompt", "contextSize", "temperature", "responseTokenMode", + "maxTokens", "seed", "performanceProfile", "threads", + "gpuLayers", "batchSize", "cacheTypeK", "cacheTypeV", "preferredBackend" + ]; + + const hasPendingChanges = textSettings && pendingTextSettings && EDITABLE_TEXT_KEYS.some(key => { + return pendingTextSettings[key] !== textSettings[key]; + }); + + const handleSaveTextSettings = async () => { + try { + setTextSettings(pendingTextSettings); + if (llmStatus.ready) { + await stopLlm(); + setLlmStatus((prev) => ({ ...prev, ready: false, running: false })); + showAlert({ + title: "Settings Saved & Applied", + message: "Text settings saved successfully. The running text model has been unloaded to apply these changes." + }); + } else { + showAlert({ + title: "Settings Saved", + message: "Text settings saved successfully." + }); + } + } catch (err) { + showAlert({ + title: "Save Failed", + message: err.message || String(err), + danger: true + }); + } + }; + const [expandedSections, setExpandedSections] = useState(() => { return { appearance: localStorage.getItem("settings_section_appearance") === "true", @@ -380,7 +423,7 @@ function Settings({ }; const updateTextSetting = (key, value) => { - setTextSettings((prev) => ({ + setPendingTextSettings((prev) => ({ ...prev, [key]: value })); @@ -415,6 +458,7 @@ function Settings({ batchSize: settings?.batchSize, ubatchSize: settings?.ubatchSize, performanceProfile: settings?.performanceProfile, + preferredBackend: settings?.preferredBackend, }); const handleBenchmarkTextBackend = async () => { @@ -426,8 +470,8 @@ function Settings({ setBenchmarkBusy(true); try { const result = await benchmarkLlm(model, { - contextSize: Math.min(2048, Number(textSettings?.contextSize) || 2048), - gpuLayers: textSettings?.gpuLayers ?? -1, + contextSize: Math.min(2048, Number(pendingTextSettings?.contextSize) || 2048), + gpuLayers: pendingTextSettings?.gpuLayers ?? -1, includeCpu: true, }); const winner = result.winner; @@ -449,13 +493,14 @@ function Settings({ }; const handleThinkingToggle = async (enabled) => { - const nextSettings = { ...textSettings, enableThinking: enabled }; + const nextSettings = { ...pendingTextSettings, enableThinking: enabled }; let status = null; try { status = await getLlmStatus(); } catch (_) {} if (!status?.ready || !status?.settings?.model) { + setPendingTextSettings(nextSettings); setTextSettings(nextSettings); return; } @@ -469,6 +514,7 @@ function Settings({ if (!reload) return; try { + setPendingTextSettings(nextSettings); setTextSettings(nextSettings); await stopLlm(); await startLlm(status.settings.model, buildTextStartOptions(nextSettings)); @@ -899,7 +945,7 @@ function Settings({