ui : resolve the layout probes through the providers api url

The key check reads the providers api url and waits for the server
capabilities, and the model status feed survives tab switches instead
of tearing down on every one. Split from the shell polish: the layout
depends on the providers layer.

Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
This commit is contained in:
Aleksander Grygier
2026-10-09 11:09:40 +02:00
committed by GitHub
parent 2f5ea674d3
commit f5a5b93b28
+11 -26
View File
@@ -2,7 +2,6 @@
import '../app.css'; import '../app.css';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/state'; import { page } from '$app/state';
import { SidebarNavigation } from '$lib/components/app'; import { SidebarNavigation } from '$lib/components/app';
import { PwaMetaTags, PwaRefreshAlert } from '$lib/components/pwa'; import { PwaMetaTags, PwaRefreshAlert } from '$lib/components/pwa';
@@ -31,6 +30,7 @@
versionStore versionStore
} from '$lib/stores'; } from '$lib/stores';
import { initStores } from '$lib/stores/init'; import { initStores } from '$lib/stores/init';
import { apiUrl } from '$lib/utils/api-base';
import { ModeWatcher } from 'mode-watcher'; import { ModeWatcher } from 'mode-watcher';
import { untrack } from 'svelte'; import { untrack } from 'svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
@@ -147,6 +147,11 @@
}); });
function checkApiKey() { function checkApiKey() {
// the stored key authenticates the llama.cpp server serving this UI
if (!serverStore.capabilities.props) {
return;
}
const apiKey = settingsStore.config.apiKey; const apiKey = settingsStore.config.apiKey;
// Without a stored key there is nothing to re-validate here; the keyless // Without a stored key there is nothing to re-validate here; the keyless
@@ -167,7 +172,7 @@
[HEADERS.AUTHORIZATION]: `${HEADERS.BEARER}${apiKey.trim()}` [HEADERS.AUTHORIZATION]: `${HEADERS.BEARER}${apiKey.trim()}`
}; };
fetch(`${base}/props`, { headers }) fetch(apiUrl('/props'), { headers })
.then((response) => { .then((response) => {
if (response.status === 401 || response.status === 403) { if (response.status === 401 || response.status === 403) {
window.location.reload(); window.location.reload();
@@ -230,37 +235,17 @@
}); });
} }
// Fetch router models when in router mode (for status and modalities) // Live model status and load progress via the /models/sse feed (router mode).
// Wait for models to be loaded first, run only once // The feed is kept for the session: switching to an external backend and back
let routerModelsFetched = false; // must not tear it down and reconnect on every tab switch.
$effect(() => {
const isRouter = serverStore.isRouterMode;
const modelsCount = modelsStore.models.length;
// Only fetch router models once when we have models loaded and in router mode
if (isRouter && modelsCount > 0 && !routerModelsFetched) {
routerModelsFetched = true;
untrack(() => {
modelsStore.fetchRouterModels();
});
}
});
// Live model status and load progress via the /models/sse feed (router mode)
$effect(() => { $effect(() => {
if (!browser) return; if (!browser) return;
if (!serverStore.isRouterMode) return; if (!serverStore.localIsRouter) return;
untrack(() => { untrack(() => {
modelsStore.status.subscribe(); modelsStore.status.subscribe();
}); });
return () => {
modelsStore.status.unsubscribe();
};
}); });
// Background MCP server health checks on app load. // Background MCP server health checks on app load.