From aa7d2afa66e925e0d7dcd35e8d43e8466c8e6141 Mon Sep 17 00:00:00 2001 From: Martin Collado Date: Thu, 15 Oct 2026 21:10:50 +0200 Subject: [PATCH] fix(ui): keep runtime backend discovery under the UI prefix --- .../src/components/networking.test.ts | 23 +++++++++++++++++++ .../src/components/networking.tsx | 5 +++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/networking.test.ts b/ui/litellm-dashboard/src/components/networking.test.ts index dc582da6833..0b718bfae36 100644 --- a/ui/litellm-dashboard/src/components/networking.test.ts +++ b/ui/litellm-dashboard/src/components/networking.test.ts @@ -1038,6 +1038,29 @@ describe("getUiConfig runtime backend discovery", () => { return { networking, mockFetch }; }; + it("loads static runtime configuration from the UI prefix independently of the selected worker", async () => { + const mockFetch = vi + .fn() + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ backend_url: "https://backend.example.com" }), + } as Response) + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ ...discoveryConfig(), server_root_path: "/services/llm" }), + } as Response); + vi.stubGlobal("fetch", mockFetch); + const networking = await loadFreshNetworking(); + const { setServerRootPath } = await import("@/lib/serverRootPath"); + setServerRootPath("/services/llm"); + networking.switchToWorkerUrl("https://worker.example.com/worker-mount"); + + await networking.getUiConfig(); + + expect(mockFetch).toHaveBeenNthCalledWith(1, "/services/llm/ui/litellm-runtime-config.json", { cache: "no-store" }); + expect(mockFetch.mock.calls[1][0]).toBe("https://backend.example.com/litellm/.well-known/litellm-ui-config"); + }); + it("uses a normalized runtime backend URL for discovery and the management fallback", async () => { const mockFetch = vi .fn() diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 2a2ce213216..9e68f1ad9a1 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -566,7 +566,10 @@ export const getUiConfig = async () => { : await (async (): Promise => { try { // eslint-disable-next-line no-restricted-syntax -- read the UI static config before the API origin is known - const response = await fetch("/ui/litellm-runtime-config.json", { cache: "no-store" }); + const response = await fetch( + `${serverRootPath === "/" ? "" : serverRootPath}/ui/litellm-runtime-config.json`, + { cache: "no-store" }, + ); if (!response.ok) return null; const runtimeConfig: unknown = await response.json(); if (