From 1f5ab2ee74179ef2bbbab7d4affa2561b9a5004e Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Sat, 12 Jul 2025 14:10:03 -0700 Subject: [PATCH] =?UTF-8?q?UI=20-=20Model=20Hub=20Page=20-=20minor=20fixes?= =?UTF-8?q?=20+=20improvements=20(+=20Make=20Model=20Hub=20OSS=20?= =?UTF-8?q?=F0=9F=9A=80)=20=20(#12553)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(model_hub_table.tsx): fix null link * fix(model_hub_table.tsx): add tooltip on disabled make public tell user how to enable button * feat(model_hub_table.tsx): make Model Hub OSS allow more teams to share available models via litellm * fix(model_hub_table_columns.tsx): make table view only model hub view, rename 'model' column to 'public model name', make model hub url copyable * fix(page.tsx): fix logo re-rendering * fix(public_model_hub.tsx): fix theme * style(public_model_hub.tsx): move filters into same card as model table filters are for table * fix(page.tsx): fix ui linting error --- .../src/app/model_hub/page.tsx | 4 +- ui/litellm-dashboard/src/app/page.tsx | 8 +- .../src/components/leftnav.tsx | 8 +- .../src/components/model_hub.tsx | 375 ------------------ .../src/components/model_hub_table.tsx | 63 +-- .../components/model_hub_table_columns.tsx | 2 +- .../src/components/navbar.tsx | 4 + .../src/components/public_model_hub.tsx | 97 +++-- 8 files changed, 90 insertions(+), 471 deletions(-) delete mode 100644 ui/litellm-dashboard/src/components/model_hub.tsx diff --git a/ui/litellm-dashboard/src/app/model_hub/page.tsx b/ui/litellm-dashboard/src/app/model_hub/page.tsx index 35abf9d12e..76e9fc342b 100644 --- a/ui/litellm-dashboard/src/app/model_hub/page.tsx +++ b/ui/litellm-dashboard/src/app/model_hub/page.tsx @@ -2,7 +2,7 @@ import React, { Suspense, useEffect, useState } from "react"; import { useSearchParams } from "next/navigation"; import { modelHubCall } from "@/components/networking"; -import ModelHub from "@/components/model_hub"; +import PublicModelHubPage from "@/components/public_model_hub"; export default function PublicModelHub() { const searchParams = useSearchParams()!; @@ -20,6 +20,6 @@ export default function PublicModelHub() { * */ return ( - + ); } diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 59b9e0214c..8d7fea8864 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -18,7 +18,6 @@ import GeneralSettings from "@/components/general_settings"; import PassThroughSettings from "@/components/pass_through_settings"; import BudgetPanel from "@/components/budgets/budget_panel"; import SpendLogsTable from "@/components/view_logs"; -import ModelHub from "@/components/model_hub"; import ModelHubTable from "@/components/model_hub_table"; import NewUsagePage from "@/components/new_usage"; import APIRef from "@/components/api_ref"; @@ -258,6 +257,7 @@ export default function CreateKeyPage() { setProxySettings={setProxySettings} proxySettings={proxySettings} accessToken={accessToken} + isPublicPage={false} />
@@ -370,12 +370,6 @@ export default function CreateKeyPage() { accessToken={accessToken} modelData={modelData} /> - ) : page == "model-hub" ? ( - ) : page == "model-hub-table" ? ( = ({ { key: "14", page: "api_ref", label: "API Reference", icon: }, { key: "16", - page: "model-hub", + page: "model-hub-table", label: "Model Hub", - icon: , - children: [ - { key: "16a", page: "model-hub", label: "Card View", icon: }, - { key: "16b", page: "model-hub-table", label: "Table View", icon: } - ] + icon: }, { key: "15", page: "logs", label: "Logs", icon: }, { key: "11", page: "guardrails", label: "Guardrails", icon: , roles: all_admin_roles }, diff --git a/ui/litellm-dashboard/src/components/model_hub.tsx b/ui/litellm-dashboard/src/components/model_hub.tsx deleted file mode 100644 index f86af8427a..0000000000 --- a/ui/litellm-dashboard/src/components/model_hub.tsx +++ /dev/null @@ -1,375 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useRouter, usePathname, useSearchParams } from "next/navigation"; - -import { modelHubCall } from "./networking"; -import { getConfigFieldSetting, updateConfigFieldSetting } from "./networking"; -import { - Card, - Text, - Title, - Grid, - Button, - Badge, - Tab, - TabGroup, - TabList, - TabPanel, - TabPanels, -} from "@tremor/react"; -import { RightOutlined, CopyOutlined } from "@ant-design/icons"; - -import { Modal, Tooltip, message } from "antd"; -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; - -interface ModelHubProps { - accessToken: string | null; - publicPage: boolean; - premiumUser: boolean; -} - -interface ModelInfo { - model_group: string; - mode: string; - supports_function_calling: boolean; - supports_vision: boolean; - max_input_tokens?: number; - max_output_tokens?: number; - input_cost_per_token?: number; - output_cost_per_token?: number; - supported_openai_params?: string[]; -} - -const ModelHub: React.FC = ({ - accessToken, - publicPage, - premiumUser, -}) => { - const [publicPageAllowed, setPublicPageAllowed] = useState(false); - const [modelHubData, setModelHubData] = useState(null); - const [isModalVisible, setIsModalVisible] = useState(false); - const [isPublicPageModalVisible, setIsPublicPageModalVisible] = - useState(false); - const [selectedModel, setSelectedModel] = useState(null); - const router = useRouter(); - - useEffect(() => { - if (!accessToken) { - return; - } - - const fetchData = async () => { - try { - const _modelHubData = await modelHubCall(accessToken); - - console.log("ModelHubData:", _modelHubData); - - setModelHubData(_modelHubData.data); - - getConfigFieldSetting(accessToken, "enable_public_model_hub") - .then((data) => { - console.log(`data: ${JSON.stringify(data)}`); - if (data.field_value == true) { - setPublicPageAllowed(true); - } - }) - .catch((error) => { - // do nothing - }); - } catch (error) { - console.error("There was an error fetching the model data", error); - } - }; - - fetchData(); - }, [accessToken, publicPage]); - - const showModal = (model: ModelInfo) => { - setSelectedModel(model); - - setIsModalVisible(true); - }; - - const goToPublicModelPage = () => { - router.replace(`/model_hub?key=${accessToken}`); - }; - const handleMakePublicPage = async () => { - if (!accessToken) { - return; - } - updateConfigFieldSetting(accessToken, "enable_public_model_hub", true).then( - (data) => { - setIsPublicPageModalVisible(true); - } - ); - }; - - const handleOk = () => { - setIsModalVisible(false); - setIsPublicPageModalVisible(false); - setSelectedModel(null); - }; - - const handleCancel = () => { - setIsModalVisible(false); - setIsPublicPageModalVisible(false); - setSelectedModel(null); - }; - - const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text); - }; - - return ( -
- {(publicPage && publicPageAllowed) || publicPage == false ? ( -
-
- -
- Model Hub - {publicPage == false ? ( - premiumUser ? ( - - ) : ( - - ) - ) : ( -
-

Filter by key:

- {`/ui/model_hub?key=`} -
- )} -
- -
- {modelHubData && - modelHubData.map((model: ModelInfo) => ( - -
-                    {model.model_group}
-                    
-                       copyToClipboard(model.model_group)}
-                        style={{ cursor: "pointer", marginRight: "10px" }}
-                      />
-                    
-                  
-
- - Max Input Tokens:{" "} - {model?.max_input_tokens - ? model?.max_input_tokens - : "Unknown"} - - - Max Output Tokens:{" "} - {model?.max_output_tokens - ? model?.max_output_tokens - : "Unknown"} - - - Input Cost Per 1M Tokens (USD):{" "} - {model?.input_cost_per_token - ? `$${(model.input_cost_per_token * 1_000_000).toFixed(2)}` - : "Unknown"} - - - Output Cost Per 1M Tokens (USD):{" "} - {model?.output_cost_per_token - ? `$${(model.output_cost_per_token * 1_000_000).toFixed(2)}` - : "Unknown"} - -
- -
- ))} -
-
- ) : ( - - - Public Model Hub not enabled. - -

- Ask your proxy admin to enable this on their Admin UI. -

-
- )} - - -
-
- Shareable Link: - {`/ui/model_hub?key=`} -
-
- -
-
-
- - {selectedModel && ( -
-

- Model Information & Usage -

- - - - Model Information - OpenAI Python SDK - Supported OpenAI Params - LlamaIndex - Langchain Py - - - - - Model Group: -
{JSON.stringify(selectedModel, null, 2)}
-
-
- - - {` -import openai -client = openai.OpenAI( - api_key="your_api_key", - base_url="http://0.0.0.0:4000" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys -) - -response = client.chat.completions.create( - model="${selectedModel.model_group}", # model to send to the proxy - messages = [ - { - "role": "user", - "content": "this is a test request, write a short poem" - } - ] -) - -print(response) - `} - - - - - {`${selectedModel.supported_openai_params?.map((param) => `${param}\n`).join("")}`} - - - - - {` -import os, dotenv - -from llama_index.llms import AzureOpenAI -from llama_index.embeddings import AzureOpenAIEmbedding -from llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext - -llm = AzureOpenAI( - engine="${selectedModel.model_group}", # model_name on litellm proxy - temperature=0.0, - azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint - api_key="sk-1234", # litellm proxy API Key - api_version="2023-07-01-preview", -) - -embed_model = AzureOpenAIEmbedding( - deployment_name="azure-embedding-model", - azure_endpoint="http://0.0.0.0:4000", - api_key="sk-1234", - api_version="2023-07-01-preview", -) - - -documents = SimpleDirectoryReader("llama_index_data").load_data() -service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model) -index = VectorStoreIndex.from_documents(documents, service_context=service_context) - -query_engine = index.as_query_engine() -response = query_engine.query("What did the author do growing up?") -print(response) - - `} - - - - - {` -from langchain.chat_models import ChatOpenAI -from langchain.prompts.chat import ( - ChatPromptTemplate, - HumanMessagePromptTemplate, - SystemMessagePromptTemplate, -) -from langchain.schema import HumanMessage, SystemMessage - -chat = ChatOpenAI( - openai_api_base="http://0.0.0.0:4000", - model = "${selectedModel.model_group}", - temperature=0.1 -) - -messages = [ - SystemMessage( - content="You are a helpful assistant that im using to make a test request to." - ), - HumanMessage( - content="test from litellm. tell me why it's amazing in 1 sentence" - ), -] -response = chat(messages) - -print(response) - - `} - - -
-
- - {/*

Additional Params: {JSON.stringify(selectedModel.litellm_params)}

*/} - - {/* Add other model details here */} -
- )} -
-
- ); -}; - -export default ModelHub; diff --git a/ui/litellm-dashboard/src/components/model_hub_table.tsx b/ui/litellm-dashboard/src/components/model_hub_table.tsx index c0a269c38f..b8952ecfff 100644 --- a/ui/litellm-dashboard/src/components/model_hub_table.tsx +++ b/ui/litellm-dashboard/src/components/model_hub_table.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState, useRef } from "react"; import { useRouter, useSearchParams } from "next/navigation"; -import { modelHubCall, makeModelGroupPublic, modelHubPublicModelsCall, proxyBaseUrl } from "./networking"; +import { modelHubCall, makeModelGroupPublic, modelHubPublicModelsCall, getProxyBaseUrl } from "./networking"; import { getConfigFieldSetting, updateConfigFieldSetting } from "./networking"; import { ModelDataTable } from "./model_dashboard/table"; import { modelHubColumns } from "./model_hub_table_columns"; @@ -13,9 +13,10 @@ import { Badge, Flex, } from "@tremor/react"; -import { Modal, message } from "antd"; +import { Modal, message, Tooltip } from "antd"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { Table as TableInstance } from '@tanstack/react-table'; +import { Copy } from "lucide-react"; interface ModelHubTableProps { accessToken: string | null; @@ -284,32 +285,38 @@ const ModelHubTable: React.FC = ({ {publicPage == false ? (
- Model Hub - Table View -
- Model Hub URL: - {`${proxyBaseUrl}/ui/model_hub_table`} +
+ Model Hub +

+ A list of all public model names personally available to you. +

+
+
+ Model Hub URL: +
+ {`${getProxyBaseUrl()}/ui/model_hub_table`} + +
- {publicPage == false ? ( - premiumUser ? ( - - ) : ( - - ) - ) : ( -
- Filter by key: - {`/ui/model_hub_table?key=`} -
+ {publicPage == false && ( + + + )}
@@ -432,7 +439,7 @@ const ModelHubTable: React.FC = ({
Shareable Link: - {`${proxyBaseUrl}/model_hub_table`} + {`${getProxyBaseUrl()}/ui/model_hub_table`}
diff --git a/ui/litellm-dashboard/src/components/model_hub_table_columns.tsx b/ui/litellm-dashboard/src/components/model_hub_table_columns.tsx index 76e443a691..04d45458d8 100644 --- a/ui/litellm-dashboard/src/components/model_hub_table_columns.tsx +++ b/ui/litellm-dashboard/src/components/model_hub_table_columns.tsx @@ -87,7 +87,7 @@ export const modelHubColumns = ( }, }, { - header: "Model", + header: "Public Model Name", accessorKey: "model_group", enableSorting: true, sortingFn: "alphanumeric", diff --git a/ui/litellm-dashboard/src/components/navbar.tsx b/ui/litellm-dashboard/src/components/navbar.tsx index dc7defb8e5..dd43c80da1 100644 --- a/ui/litellm-dashboard/src/components/navbar.tsx +++ b/ui/litellm-dashboard/src/components/navbar.tsx @@ -24,6 +24,7 @@ interface NavbarProps { setProxySettings: React.Dispatch>; proxySettings: any; accessToken: string | null; + isPublicPage: boolean; } const Navbar: React.FC = ({ @@ -34,6 +35,7 @@ const Navbar: React.FC = ({ proxySettings, setProxySettings, accessToken, + isPublicPage = false, }) => { const baseUrl = getProxyBaseUrl(); const imageUrl = baseUrl + "/get_image"; @@ -143,6 +145,7 @@ const Navbar: React.FC = ({ Docs + {!isPublicPage && ( = ({ + )}
diff --git a/ui/litellm-dashboard/src/components/public_model_hub.tsx b/ui/litellm-dashboard/src/components/public_model_hub.tsx index 5625ff3377..9145fec4b5 100644 --- a/ui/litellm-dashboard/src/components/public_model_hub.tsx +++ b/ui/litellm-dashboard/src/components/public_model_hub.tsx @@ -17,6 +17,7 @@ import { generateCodeSnippet } from "./chat_ui/CodeSnippets"; import { EndpointType, getEndpointType } from "./chat_ui/mode_endpoint_mapping"; import { MessageType } from "./chat_ui/types"; import { getProviderLogoAndName } from "./provider_info_helpers"; +import Navbar from "./navbar"; // Simple approach without react-markdown dependency interface ModelGroupInfo { @@ -54,6 +55,7 @@ const PublicModelHub: React.FC = ({ accessToken }) => { const [serviceStatus, setServiceStatus] = useState("I'm alive! ✓"); const [isModalVisible, setIsModalVisible] = useState(false); const [selectedModel, setSelectedModel] = useState(null); + const [proxySettings, setProxySettings] = useState({}); const tableRef = useRef>(null); useEffect(() => { @@ -474,22 +476,27 @@ const PublicModelHub: React.FC = ({ accessToken }) => { ]; return ( -
- {/* Header */} -
-
- {pageTitle} -
-
+
+ {/* Navigation */} +
{/* About Section */} - - About -

{customDocsDescription ? customDocsDescription : "Proxy Server to call 100+ LLMs in the OpenAI format."}

-
+ + About +

{customDocsDescription ? customDocsDescription : "Proxy Server to call 100+ LLMs in the OpenAI format."}

+
- 🔧 + 🔧 Built with litellm: v{litellmVersion}
@@ -497,17 +504,17 @@ const PublicModelHub: React.FC = ({ accessToken }) => { {/* Useful Links */} {usefulLinks && Object.keys(usefulLinks).length > 0 && ( - - Useful Links -
+ + Useful Links +
{Object.entries(usefulLinks || {}).map(([title, url]) => ( ))}
@@ -515,36 +522,41 @@ const PublicModelHub: React.FC = ({ accessToken }) => { )} {/* Health and Endpoint Status */} - - Health and Endpoint Status -
- Service status: {serviceStatus} + + Health and Endpoint Status +
+ Service status: {serviceStatus}
- {/* Filters */} - -
+ {/* Models Table */} + +
+ Available Models +
+ + {/* Filters */} +
- Search Models: + Search Models:
- + setSearchTerm(e.target.value)} - className="border rounded-lg pl-10 pr-4 py-3 w-full text-base focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" + className="border border-gray-300 rounded-lg pl-10 pr-4 py-2 w-full text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white" />
- Provider: + Provider:
- Mode: + Mode:
- Features: + Features:
-
- - {/* Models Table */} - -
- Available Models -
= ({ accessToken }) => { />
- + Showing {filteredData.length} of {modelHubData?.length || 0} models