From abdc5b357fd5e3da7b98a80242d8fd7da2d15cd1 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Mon, 27 May 2024 17:45:45 -0700 Subject: [PATCH] feat(model_hub.tsx): enable admin to expose a public model hub --- litellm/proxy/_types.py | 9 + litellm/proxy/proxy_server.py | 8 +- .../src/app/model_hub/page.tsx | 4 +- ui/litellm-dashboard/src/app/page.tsx | 5 +- .../src/components/model_hub.tsx | 210 ++++++++++++------ .../src/components/networking.tsx | 32 +++ 6 files changed, 193 insertions(+), 75 deletions(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index e85f116f73..0893427b72 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -928,6 +928,10 @@ class ConfigGeneralSettings(LiteLLMBase): allowed_routes: Optional[List] = Field( None, description="Proxy API Endpoints you want users to be able to access" ) + enable_public_model_hub: bool = Field( + default=False, + description="Public model hub for users to see what models they have access to, supported openai params, etc.", + ) class ConfigYAML(LiteLLMBase): @@ -1154,3 +1158,8 @@ class WebhookEvent(CallInfo): class SpecialModelNames(enum.Enum): all_team_models = "all-team-models" all_proxy_models = "all-proxy-models" + + +class ConfigFieldInfo(LiteLLMBase): + field_name: str + field_value: Any diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 1bdb5edba3..3bc7ccbc59 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -11061,6 +11061,7 @@ async def update_config_general_settings( "/config/field/info", tags=["config.yaml"], dependencies=[Depends(user_api_key_auth)], + response_model=ConfigFieldInfo, ) async def get_config_general_settings( field_name: str, @@ -11107,10 +11108,9 @@ async def get_config_general_settings( general_settings = dict(db_general_settings.param_value) if field_name in general_settings: - return { - "field_name": field_name, - "field_value": general_settings[field_name], - } + return ConfigFieldInfo( + field_name=field_name, field_value=general_settings[field_name] + ) else: raise HTTPException( status_code=400, diff --git a/ui/litellm-dashboard/src/app/model_hub/page.tsx b/ui/litellm-dashboard/src/app/model_hub/page.tsx index a038a69747..cca9f877b7 100644 --- a/ui/litellm-dashboard/src/app/model_hub/page.tsx +++ b/ui/litellm-dashboard/src/app/model_hub/page.tsx @@ -19,5 +19,7 @@ export default function PublicModelHub() { * populate navbar * */ - return ; + return ( + + ); } diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 8d2b1fac2b..96bfa80300 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -196,11 +196,8 @@ const CreateKeyPage = () => { /> ) : page == "model-hub" ? ( ) : ( diff --git a/ui/litellm-dashboard/src/components/model_hub.tsx b/ui/litellm-dashboard/src/components/model_hub.tsx index 14b3cfd1f7..5a141d154d 100644 --- a/ui/litellm-dashboard/src/components/model_hub.tsx +++ b/ui/litellm-dashboard/src/components/model_hub.tsx @@ -1,7 +1,8 @@ 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, @@ -15,15 +16,15 @@ import { TabPanel, TabPanels, } from "@tremor/react"; - import { RightOutlined, CopyOutlined } from "@ant-design/icons"; -import { Modal, Tooltip } from "antd"; +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 { @@ -36,10 +37,18 @@ interface ModelInfo { supported_openai_params?: string[]; } -const ModelHub: React.FC = ({ accessToken, publicPage }) => { +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) { @@ -53,13 +62,24 @@ const ModelHub: React.FC = ({ accessToken, publicPage }) => { 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]); + }, [accessToken, publicPage]); const showModal = (model: ModelInfo) => { setSelectedModel(model); @@ -67,15 +87,29 @@ const ModelHub: React.FC = ({ accessToken, publicPage }) => { 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); }; @@ -85,68 +119,112 @@ const ModelHub: React.FC = ({ accessToken, publicPage }) => { return (
-
-
+ {(publicPage && publicPageAllowed) || publicPage == false ? ( +
+
-
- Model Hub - {publicPage == false && ( - - )} -
- -
- {modelHubData && - modelHubData.map((model: ModelInfo) => ( - -
-                  {model.model_group}
-                  
-                     copyToClipboard(model.model_group)}
-                      style={{ cursor: "pointer", marginRight: "10px" }}
-                    />
-                  
-                
-
- Mode: {model.mode} - - Supports Function Calling:{" "} - {model?.supports_function_calling == true ? "Yes" : "No"} - - - Supports Vision:{" "} - {model?.supports_vision == true ? "Yes" : "No"} - - - Max Input Tokens:{" "} - {model?.max_input_tokens ? model?.max_input_tokens : "N/A"} - - - Max Output Tokens:{" "} - {model?.max_output_tokens - ? model?.max_output_tokens - : "N/A"} - -
- -
+ + ) + ) : ( +
+

Filter by key:

+ {`/ui/model_hub?key=`} +
+ )} +
+
+ {modelHubData && + modelHubData.map((model: ModelInfo) => ( + +
+                    {model.model_group}
+                    
+                       copyToClipboard(model.model_group)}
+                        style={{ cursor: "pointer", marginRight: "10px" }}
+                      />
+                    
+                  
+
+ Mode: {model.mode} + + Supports Function Calling:{" "} + {model?.supports_function_calling == true ? "Yes" : "No"} + + + Supports Vision:{" "} + {model?.supports_vision == true ? "Yes" : "No"} + + + Max Input Tokens:{" "} + {model?.max_input_tokens + ? model?.max_input_tokens + : "N/A"} + + + Max Output Tokens:{" "} + {model?.max_output_tokens + ? model?.max_output_tokens + : "N/A"} + +
+ +
+ ))} +
+
+ ) : ( + + + Public Model Hub not enabled. + +

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

+
+ )} + + +
+
+ Shareable Link: + {`/ui/model_hub?key=`} +
+
+ +
+
+
{ } }; +export const getConfigFieldSetting = async ( + accessToken: String, + fieldName: string +) => { + try { + let url = proxyBaseUrl + ? `${proxyBaseUrl}/config/field/info?field_name=${fieldName}` + : `/config/field/info?field_name=${fieldName}`; + + //message.info("Requesting model data"); + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + return data; + // Handle success - you might want to update some state or UI based on the created key + } catch (error) { + console.error("Failed to set callbacks:", error); + throw error; + } +}; + export const updateConfigFieldSetting = async ( accessToken: String, fieldName: string,