From 910ed3f7e619f217cb3ee90fe4eff9a4a52e8b23 Mon Sep 17 00:00:00 2001 From: "Gan, Jimmy" Date: Mon, 2 Mar 2026 03:50:56 +0800 Subject: [PATCH] feat: add LiteLLM health page to dashboard Expose LiteLLM operational status in the NAS Dashboard UI so auth-required and outage states are visible without calling backend APIs directly. Co-Authored-By: Claude Opus 4.6 (1M context) --- dashboard/frontend/src/App.svelte | 3 + .../frontend/src/components/Sidebar.svelte | 3 + dashboard/frontend/src/lib/api.js | 4 + dashboard/frontend/src/routes/LiteLLM.svelte | 108 ++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 dashboard/frontend/src/routes/LiteLLM.svelte diff --git a/dashboard/frontend/src/App.svelte b/dashboard/frontend/src/App.svelte index 4ac539d..16a0870 100644 --- a/dashboard/frontend/src/App.svelte +++ b/dashboard/frontend/src/App.svelte @@ -9,6 +9,7 @@ import Settings from "./routes/Settings.svelte"; import ChatSummary from "./routes/ChatSummary.svelte"; import Security from "./routes/Security.svelte"; + import LiteLLM from "./routes/LiteLLM.svelte"; import Login from "./routes/Login.svelte"; import { onMount } from "svelte"; import { getToken, checkAuth, setToken, setRefreshToken, currentUser, setCurrentUser, getPreferences, savePreferences } from "./lib/api.js"; @@ -153,6 +154,8 @@ {:else if page === "security" && hasPageAccess("security")} + {:else if page === "litellm" && hasPageAccess("dashboard")} + {:else} {/if} diff --git a/dashboard/frontend/src/components/Sidebar.svelte b/dashboard/frontend/src/components/Sidebar.svelte index 7242961..bb69cf4 100644 --- a/dashboard/frontend/src/components/Sidebar.svelte +++ b/dashboard/frontend/src/components/Sidebar.svelte @@ -16,6 +16,7 @@ const defaultLinks = [ { id: "dashboard", label: "Overview", icon: "grid" }, + { id: "litellm", label: "LiteLLM", icon: "bolt" }, { id: "docker", label: "Docker", icon: "box" }, { id: "files", label: "Files", icon: "folder" }, { id: "terminal", label: "Terminal", icon: "terminal" }, @@ -228,6 +229,8 @@ {:else if icon === "box"} + {:else if icon === "bolt"} + {:else if icon === "folder"} {:else if icon === "terminal"} diff --git a/dashboard/frontend/src/lib/api.js b/dashboard/frontend/src/lib/api.js index 316e0c2..39242aa 100644 --- a/dashboard/frontend/src/lib/api.js +++ b/dashboard/frontend/src/lib/api.js @@ -139,6 +139,10 @@ export function savePreferences(data) { return request("/auth/preferences", { method: "PUT", json: data }); } +export function getLiteLLMHealth() { + return get("/litellm/health"); +} + export function put(path, data) { return request(path, { method: "PUT", json: data }); } diff --git a/dashboard/frontend/src/routes/LiteLLM.svelte b/dashboard/frontend/src/routes/LiteLLM.svelte new file mode 100644 index 0000000..d60a5ef --- /dev/null +++ b/dashboard/frontend/src/routes/LiteLLM.svelte @@ -0,0 +1,108 @@ + + +
+
+
+

LiteLLM

+

Gateway health and connectivity status

+
+ up + auth_required + down +
+
+ +
+ + {#if requestError} +
+

Failed to fetch LiteLLM health

+

{requestError}

+
+ {/if} + +
+ {#if loading && !health} +
+
+
+ {#each Array(6) as _} +
+ {/each} +
+
+ {:else if health} +
+

Health Status

+ {valueOrDash(health.status)} +
+ +
+
+

OK

+

{String(Boolean(health.ok))}

+
+ +
+

Latency

+

{health.latency_ms === null || health.latency_ms === undefined ? "—" : `${health.latency_ms} ms`}

+
+ +
+

Endpoint

+

{valueOrDash(health.endpoint)}

+
+ +
+

HTTP Status

+

{valueOrDash(health.http_status)}

+
+ +
+

Error

+

{valueOrDash(health.error)}

+
+
+ {:else} +

No health data available.

+ {/if} +
+