From b6b8f46b36715c1519b3873d1dc4dc8809f1d402 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Fri, 21 Nov 2025 15:59:03 -0800 Subject: [PATCH] Change Public Model Hub to use proxyBaseUrl (#16892) --- .../src/components/public_model_hub.test.tsx | 68 +++++++++ .../src/components/public_model_hub.tsx | 132 ++++++++++-------- 2 files changed, 140 insertions(+), 60 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/public_model_hub.test.tsx diff --git a/ui/litellm-dashboard/src/components/public_model_hub.test.tsx b/ui/litellm-dashboard/src/components/public_model_hub.test.tsx new file mode 100644 index 00000000000..8ff57b49e08 --- /dev/null +++ b/ui/litellm-dashboard/src/components/public_model_hub.test.tsx @@ -0,0 +1,68 @@ +import { describe, it, expect, vi, beforeAll, beforeEach } from "vitest"; +import { render } from "@testing-library/react"; +import PublicModelHub from "./public_model_hub"; +import { FeatureFlagsProvider } from "@/hooks/useFeatureFlags"; + +vi.mock("next/navigation", () => ({ + useRouter: vi.fn(() => ({ + replace: vi.fn(), + push: vi.fn(), + refresh: vi.fn(), + })), +})); + +vi.mock("./networking", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + modelHubPublicModelsCall: vi.fn().mockResolvedValue([]), + getPublicModelHubInfo: vi.fn().mockResolvedValue({ + docs_title: "LiteLLM Gateway", + custom_docs_description: null, + litellm_version: "1.0.0", + useful_links: {}, + }), + agentHubPublicModelsCall: vi.fn().mockResolvedValue([]), + mcpHubPublicServersCall: vi.fn().mockResolvedValue([]), + getUiConfig: vi.fn().mockResolvedValue({}), + }; +}); + +beforeAll(() => { + Object.defineProperty(window, "matchMedia", { + writable: true, + value: (query: string) => ({ + matches: false, + media: query, + onchange: null, + addListener: () => {}, + removeListener: () => {}, + addEventListener: () => {}, + removeEventListener: () => {}, + dispatchEvent: () => false, + }), + }); +}); + +beforeEach(() => { + Storage.prototype.getItem = vi.fn(() => "false"); + Storage.prototype.setItem = vi.fn(); + Object.defineProperty(window, "location", { + writable: true, + value: { + pathname: "/", + origin: "http://localhost:3000", + }, + }); +}); + +describe("PublicModelHub", () => { + it("renders", () => { + const { container } = render( + + + , + ); + expect(container).toBeInTheDocument(); + }); +}); diff --git a/ui/litellm-dashboard/src/components/public_model_hub.tsx b/ui/litellm-dashboard/src/components/public_model_hub.tsx index b7339aab6cd..6235a0c66f6 100644 --- a/ui/litellm-dashboard/src/components/public_model_hub.tsx +++ b/ui/litellm-dashboard/src/components/public_model_hub.tsx @@ -1,5 +1,11 @@ import React, { useEffect, useState, useRef, useMemo } from "react"; -import { modelHubPublicModelsCall, getPublicModelHubInfo, agentHubPublicModelsCall, mcpHubPublicServersCall } from "./networking"; +import { + modelHubPublicModelsCall, + getPublicModelHubInfo, + agentHubPublicModelsCall, + mcpHubPublicServersCall, + getUiConfig, +} from "./networking"; import { ModelDataTable } from "./model_dashboard/table"; import { ColumnDef } from "@tanstack/react-table"; import { Card, Text, Title, Button } from "@tremor/react"; @@ -117,60 +123,72 @@ const PublicModelHub: React.FC = ({ accessToken, isEmbedded const mcpTableRef = useRef>(null); useEffect(() => { - const fetchPublicData = async () => { + const initializeAndFetch = async () => { + // Initialize proxyBaseUrl first to ensure it includes the server root path try { - setLoading(true); - const _modelHubData = await modelHubPublicModelsCall(); - console.log("ModelHubData:", _modelHubData); - setModelHubData(_modelHubData); + await getUiConfig(); } catch (error) { - console.error("There was an error fetching the public model data", error); - setServiceStatus("Service unavailable"); - } finally { - setLoading(false); + console.error("Failed to get UI config:", error); + // Continue anyway - might work with default proxyBaseUrl } + + const fetchPublicData = async () => { + try { + setLoading(true); + const _modelHubData = await modelHubPublicModelsCall(); + console.log("ModelHubData:", _modelHubData); + setModelHubData(_modelHubData); + } catch (error) { + console.error("There was an error fetching the public model data", error); + setServiceStatus("Service unavailable"); + } finally { + setLoading(false); + } + }; + + const fetchAgentData = async () => { + try { + setAgentLoading(true); + const _agentHubData = await agentHubPublicModelsCall(); + console.log("AgentHubData:", _agentHubData); + setAgentHubData(_agentHubData); + } catch (error) { + console.error("There was an error fetching the public agent data", error); + } finally { + setAgentLoading(false); + } + }; + + const fetchMcpData = async () => { + try { + setMcpLoading(true); + const _mcpHubData = await mcpHubPublicServersCall(); + console.log("MCPHubData:", _mcpHubData); + setMcpHubData(_mcpHubData); + } catch (error) { + console.error("There was an error fetching the public MCP server data", error); + } finally { + setMcpLoading(false); + } + }; + + const fetchPublicModelHubInfo = async () => { + const publicModelHubInfo = await getPublicModelHubInfo(); + console.log("Public Model Hub Info:", publicModelHubInfo); + setPageTitle(publicModelHubInfo.docs_title); + setCustomDocsDescription(publicModelHubInfo.custom_docs_description); + setLitellmVersion(publicModelHubInfo.litellm_version); + setUsefulLinks(publicModelHubInfo.useful_links || {}); + }; + + fetchPublicModelHubInfo(); + + fetchPublicData(); + fetchAgentData(); + fetchMcpData(); }; - const fetchAgentData = async () => { - try { - setAgentLoading(true); - const _agentHubData = await agentHubPublicModelsCall(); - console.log("AgentHubData:", _agentHubData); - setAgentHubData(_agentHubData); - } catch (error) { - console.error("There was an error fetching the public agent data", error); - } finally { - setAgentLoading(false); - } - }; - - const fetchMcpData = async () => { - try { - setMcpLoading(true); - const _mcpHubData = await mcpHubPublicServersCall(); - console.log("MCPHubData:", _mcpHubData); - setMcpHubData(_mcpHubData); - } catch (error) { - console.error("There was an error fetching the public MCP server data", error); - } finally { - setMcpLoading(false); - } - }; - - const fetchPublicModelHubInfo = async () => { - const publicModelHubInfo = await getPublicModelHubInfo(); - console.log("Public Model Hub Info:", publicModelHubInfo); - setPageTitle(publicModelHubInfo.docs_title); - setCustomDocsDescription(publicModelHubInfo.custom_docs_description); - setLitellmVersion(publicModelHubInfo.litellm_version); - setUsefulLinks(publicModelHubInfo.useful_links || {}); - }; - - fetchPublicModelHubInfo(); - - fetchPublicData(); - fetchAgentData(); - fetchMcpData(); + initializeAndFetch(); }, []); // Clear filters when filter values change to avoid confusion @@ -400,8 +418,7 @@ const PublicModelHub: React.FC = ({ accessToken, isEmbedded // Apply transport filters return searchResults.filter((server) => { - const matchesTransport = - selectedMcpTransports.length === 0 || selectedMcpTransports.includes(server.transport); + const matchesTransport = selectedMcpTransports.length === 0 || selectedMcpTransports.includes(server.transport); return matchesTransport; }); @@ -1183,10 +1200,7 @@ const PublicModelHub: React.FC = ({ accessToken, isEmbedded
Search MCP Servers: - +
@@ -1842,9 +1856,7 @@ print(response.model_dump(mode='json', exclude_none=True))`;
Additional Information
-
-                      {JSON.stringify(selectedMcpServer.mcp_info, null, 2)}
-                    
+
{JSON.stringify(selectedMcpServer.mcp_info, null, 2)}
)} @@ -1854,7 +1866,7 @@ print(response.model_dump(mode='json', exclude_none=True))`; Usage Example
-{`# Using MCP Server with Python FastMCP
+                    {`# Using MCP Server with Python FastMCP
 
 from fastmcp import Client
 import asyncio