Persist engine URLs and custom cards server-side (survive browser restarts)
Engine URL inputs (Ollama, vLLM, faster-whisper, etc.), custom engine cards, and the refinement/conversation LLM URLs were stored only in localStorage and lost on browser data clear. All four are now synced to settings.json via _patchSettings() with localStorage as fast initial fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
916123f3b4
commit
239bf0dad6
10
server.py
10
server.py
@ -326,6 +326,8 @@ _SETTINGS_KEYS = {
|
||||
"captures_default_voice",
|
||||
"client_voice_bindings",
|
||||
"llm_url",
|
||||
# Browser-persistent UI state
|
||||
"engine_local_urls", "custom_engine_cards", "refine_llm_url", "conv_llm_url",
|
||||
}
|
||||
|
||||
|
||||
@ -488,6 +490,10 @@ def _normalize_settings(s: dict) -> dict:
|
||||
s["output_dir"] = str(scan_dir / "active_voices")
|
||||
if s.get("tts_stream_mode") not in {"auto", "streaming", "buffered"}:
|
||||
s["tts_stream_mode"] = "auto"
|
||||
if not isinstance(s.get("engine_local_urls"), dict):
|
||||
s["engine_local_urls"] = {}
|
||||
if not isinstance(s.get("custom_engine_cards"), list):
|
||||
s["custom_engine_cards"] = []
|
||||
return s
|
||||
|
||||
|
||||
@ -532,6 +538,10 @@ def _load_settings() -> dict:
|
||||
"captures_default_voice": "",
|
||||
"client_voice_bindings": {},
|
||||
"llm_url": "http://localhost:11434/v1",
|
||||
"engine_local_urls": {},
|
||||
"custom_engine_cards": [],
|
||||
"refine_llm_url": "",
|
||||
"conv_llm_url": "",
|
||||
}
|
||||
if CONFIG_FILE.exists():
|
||||
try:
|
||||
|
||||
@ -1955,6 +1955,23 @@ async function refreshTtsBackendAvailability(selected = '') {
|
||||
return _ttsBackends;
|
||||
}
|
||||
|
||||
async function _patchSettings(patch) {
|
||||
try {
|
||||
await fetch('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(patch) });
|
||||
} catch (e) { console.warn('[settings] patch failed', e); }
|
||||
}
|
||||
|
||||
let _engineUrlSaveTimer = null;
|
||||
function _saveEngineLocalUrls() {
|
||||
clearTimeout(_engineUrlSaveTimer);
|
||||
_engineUrlSaveTimer = setTimeout(() => {
|
||||
const urls = {};
|
||||
document.querySelectorAll('[data-llm-local-key]').forEach(i => { if (i.value) urls[i.dataset.llmLocalKey] = i.value; });
|
||||
_patchSettings({ engine_local_urls: urls });
|
||||
if (_appSettings) _appSettings.engine_local_urls = urls;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
const s = await fetch('/api/settings').then(r => r.json());
|
||||
$('s-whisper-url').value = s.whisper_url || '';
|
||||
@ -1968,6 +1985,19 @@ async function loadSettings() {
|
||||
$('s-xtts-url').value = s.xtts_url || '';
|
||||
const llmUrlEl = $('s-llm-url'); if (llmUrlEl) llmUrlEl.value = s.llm_url || '';
|
||||
_appSettings = s;
|
||||
|
||||
// Restore engine URL inputs from server (overrides localStorage fallback)
|
||||
const savedEngineUrls = s.engine_local_urls || {};
|
||||
document.querySelectorAll('[data-llm-local-key]').forEach(inp => {
|
||||
const v = savedEngineUrls[inp.dataset.llmLocalKey];
|
||||
if (v) inp.value = v;
|
||||
});
|
||||
|
||||
// Restore LLM URLs for refinement and conversation panels
|
||||
const refineInp = $('refine-llm-url');
|
||||
if (refineInp && s.refine_llm_url) refineInp.value = s.refine_llm_url;
|
||||
const convInp = $('conv-llm-url');
|
||||
if (convInp && s.conv_llm_url) convInp.value = s.conv_llm_url;
|
||||
$('s-tts-stream-url').value = s.tts_stream_url || '';
|
||||
$('s-customvoice-url').value = s.customvoice_url || 'http://host.docker.internal:8022';
|
||||
$('s-nvidia-router-url').value = s.nvidia_router_url || 'http://host.docker.internal:8090';
|
||||
@ -6842,11 +6872,15 @@ loadLocalContainers();
|
||||
|
||||
// ── Custom engine card storage helpers ────────────────────────────────────
|
||||
function loadCustomEngineCards() {
|
||||
if (_appSettings && Array.isArray(_appSettings.custom_engine_cards) && _appSettings.custom_engine_cards.length)
|
||||
return _appSettings.custom_engine_cards;
|
||||
try { return JSON.parse(localStorage.getItem('engines-custom-cards') || '[]'); }
|
||||
catch (e) { return []; }
|
||||
}
|
||||
function saveCustomEngineCards(cards) {
|
||||
localStorage.setItem('engines-custom-cards', JSON.stringify(cards));
|
||||
if (_appSettings) _appSettings.custom_engine_cards = cards;
|
||||
_patchSettings({ custom_engine_cards: cards });
|
||||
}
|
||||
|
||||
// Settings key per docker container name; role fallback for custom cards
|
||||
@ -7303,9 +7337,9 @@ document.querySelectorAll('.dc-refresh-btn').forEach(b => b.addEventListener('cl
|
||||
if (!card) return;
|
||||
const btn = card.querySelector('.llm-local-ping');
|
||||
|
||||
const savedUrl = localStorage.getItem('llm-local-url-' + key);
|
||||
const savedUrl = (_appSettings && _appSettings.engine_local_urls && _appSettings.engine_local_urls[key]) || localStorage.getItem('llm-local-url-' + key);
|
||||
if (savedUrl) inp.value = savedUrl;
|
||||
inp.addEventListener('input', () => { localStorage.setItem('llm-local-url-' + key, inp.value); });
|
||||
inp.addEventListener('input', () => { localStorage.setItem('llm-local-url-' + key, inp.value); _saveEngineLocalUrls(); });
|
||||
|
||||
if (localStorage.getItem('llm-local-con-' + key) === '1') applyCardState(card, key, true, false);
|
||||
|
||||
@ -7979,10 +8013,15 @@ let _refineOriginal = null;
|
||||
(function initLlmRefinement() {
|
||||
const inp = $('refine-llm-url');
|
||||
if (!inp) return;
|
||||
const saved = localStorage.getItem('refine-llm-url');
|
||||
// loadSettings() will overwrite with the server value; localStorage is the fast initial fallback
|
||||
const saved = (_appSettings && _appSettings.refine_llm_url) || localStorage.getItem('refine-llm-url');
|
||||
if (saved) inp.value = saved;
|
||||
else inp.value = localStorage.getItem('llm-local-url-ollama') || 'http://localhost:11434/v1';
|
||||
inp.addEventListener('input', () => localStorage.setItem('refine-llm-url', inp.value));
|
||||
else inp.value = (_appSettings && _appSettings.engine_local_urls && _appSettings.engine_local_urls['ollama']) || localStorage.getItem('llm-local-url-ollama') || 'http://localhost:11434/v1';
|
||||
inp.addEventListener('input', () => {
|
||||
localStorage.setItem('refine-llm-url', inp.value);
|
||||
_patchSettings({ refine_llm_url: inp.value });
|
||||
if (_appSettings) _appSettings.refine_llm_url = inp.value;
|
||||
});
|
||||
})();
|
||||
|
||||
function updateRefineButtonState() {
|
||||
@ -8149,6 +8188,13 @@ $('s-import-voices-file')?.addEventListener('change', async function () {
|
||||
const turnHistory = $('conv-turn-history');
|
||||
if (!chatWindow || !micBtn) return;
|
||||
|
||||
// Restore conv LLM URL from server settings
|
||||
if (llmUrlInp && _appSettings && _appSettings.conv_llm_url) llmUrlInp.value = _appSettings.conv_llm_url;
|
||||
if (llmUrlInp) llmUrlInp.addEventListener('input', () => {
|
||||
_patchSettings({ conv_llm_url: llmUrlInp.value });
|
||||
if (_appSettings) _appSettings.conv_llm_url = llmUrlInp.value;
|
||||
});
|
||||
|
||||
let mediaRecorder = null;
|
||||
let recChunks = [];
|
||||
let recTimerInterval = null;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user