diff --git a/client/src/components/settings/LocalLlmRuntimesView.jsx b/client/src/components/settings/LocalLlmRuntimesView.jsx index bfd9a5fbff..104e1df936 100644 --- a/client/src/components/settings/LocalLlmRuntimesView.jsx +++ b/client/src/components/settings/LocalLlmRuntimesView.jsx @@ -322,6 +322,13 @@ export default function LocalLlmRuntimesView() { ? `${idleRuntimeLabel[runtime] || runtime} will stay loaded while idle` : `${idleRuntimeLabel[runtime] || runtime} releases its model after ${minutes} idle minute${minutes === 1 ? '' : 's'}` ).then(runtime === 'llama' ? loadLlamaStatus : runtime === 'slotstream' ? loadSlotstreamStatus : loadMtplxStatus); + const toggleKeepLoaded = (runtime, nextPinned) => runAction( + `runtime-keep-loaded-${runtime}`, + () => patchSettingsSlice(`localLlm.${runtime}`, { keepLoaded: nextPinned }), + nextPinned + ? `${idleRuntimeLabel[runtime] || runtime} is pinned and will stay loaded` + : `${idleRuntimeLabel[runtime] || runtime} will follow idle and pressure release policies` + ).then(runtime === 'llama' ? loadLlamaStatus : runtime === 'slotstream' ? loadSlotstreamStatus : loadMtplxStatus); const runtimeInstallSlotstream = () => runAction( 'runtime-install-slotstream', () => installSlotstream(), @@ -656,6 +663,7 @@ export default function LocalLlmRuntimesView() { onStopSlotstream={runtimeStopSlotstream} onSaveStartup={saveRuntimeStartup} onSaveIdleWindow={saveIdleWindow} + onToggleKeepLoaded={toggleKeepLoaded} /> {/* Backends — model catalog, default marker, cross-backend import */} diff --git a/client/src/components/settings/RuntimeServersCard.jsx b/client/src/components/settings/RuntimeServersCard.jsx index 46622d0165..0156c52c43 100644 --- a/client/src/components/settings/RuntimeServersCard.jsx +++ b/client/src/components/settings/RuntimeServersCard.jsx @@ -77,6 +77,7 @@ function pm2Row({ id, label, icon, status, platformReason, onStart, onStop, onIn icon, state, endpoint: status?.endpoint || null, + releaseReason: !status?.running && status?.releaseReason ? status.releaseReason : null, detail: platformReason || detail || null, pm2: true, runAtStartup: status?.runAtStartup ?? null, @@ -190,6 +191,11 @@ function ServerRow({ row, busy, actionInProgress, children }) { starts at boot )} + {row.releaseReason && ( + + {row.releaseReason} + + )} {row.detail && {row.detail}}
@@ -296,6 +302,7 @@ export default function RuntimeServersCard({ onStopSlotstream, onSaveStartup, onSaveIdleWindow, + onToggleKeepLoaded, }) { // Read off each daemon's own status payload — the same place `runAtStartup` // and Ollama's `disabled` come from — so there is no second settings fetch on @@ -305,6 +312,11 @@ export default function RuntimeServersCard({ mtplx: mtplxStatus?.idleMinutes ?? 0, slotstream: slotstreamStatus?.idleMinutes ?? 0, }; + const keepLoaded = { + llama: Boolean(llamaStatus?.keepLoaded), + mtplx: Boolean(mtplxStatus?.keepLoaded), + slotstream: Boolean(slotstreamStatus?.keepLoaded), + }; const ollamaService = status?.ollama?.service; const ollamaRunsAtStartup = Boolean(ollamaService?.runAtStartup); @@ -424,17 +436,32 @@ export default function RuntimeServersCard({ )} {(row.id === 'llama' || row.id === 'mtplx' || row.id === 'slotstream') && row.state !== 'unsupported' && row.state !== 'missing' && ( - onSaveIdleWindow?.(row.id, minutes)} - note={row.id === 'llama' - ? 'Minutes of PortOS inactivity after which llama.cpp unloads the model in place and reloads it on the next request. 0 = keep it resident. Applies from the next start.' - : row.id === 'slotstream' - ? 'Minutes of PortOS inactivity after which Slotstream is stopped. The next PortOS request starts it again on the same checkpoint and memory cap. 0 = keep it running.' - : 'Minutes of PortOS inactivity after which MTPLX is stopped. The next PortOS request starts it again on the same checkpoint. 0 = keep it running.'} - /> +
+ onSaveIdleWindow?.(row.id, minutes)} + note={keepLoaded[row.id] + ? `${row.label} is pinned to keep loaded and is exempt from idle release and memory pressure eviction.` + : row.id === 'llama' + ? 'Minutes of PortOS inactivity after which llama.cpp unloads the model in place and reloads it on the next request. 0 = keep it resident. Applies from the next start.' + : row.id === 'slotstream' + ? 'Minutes of PortOS inactivity after which Slotstream is stopped. The next PortOS request starts it again on the same checkpoint and memory cap. 0 = keep it running.' + : 'Minutes of PortOS inactivity after which MTPLX is stopped. The next PortOS request starts it again on the same checkpoint. 0 = keep it running.'} + /> + +
)} {(row.id === 'llama' || row.id === 'mtplx' || row.id === 'slotstream') && row.state !== 'unsupported' && (