From 36f18411d07a32ff2d0b242349e3285245c30b18 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Mon, 2 Jun 2025 15:13:49 -0700 Subject: [PATCH] fix(page.tsx): create pattern for loading in ui config before making network requests ensures requests are formatted correctly --- .../ui_discovery_endpoints.py | 2 +- .../src/app/onboarding/page.tsx | 12 +++++- ui/litellm-dashboard/src/app/page.tsx | 18 +++++---- .../src/components/networking.tsx | 37 +++++++++++++++++-- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py b/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py index 91a3c045676..2a6bec77bfb 100644 --- a/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py +++ b/litellm/proxy/discovery_endpoints/ui_discovery_endpoints.py @@ -8,7 +8,7 @@ from litellm.types.proxy.discovery_endpoints.ui_discovery_endpoints import ( router = APIRouter() -@router.get(".well-known/litellm-ui-config", response_model=UiDiscoveryEndpoints) +@router.get("/.well-known/litellm-ui-config", response_model=UiDiscoveryEndpoints) @router.get( "/litellm/.well-known/litellm-ui-config", response_model=UiDiscoveryEndpoints ) # if mounted at root path diff --git a/ui/litellm-dashboard/src/app/onboarding/page.tsx b/ui/litellm-dashboard/src/app/onboarding/page.tsx index 01b8f33084a..12730b79e2f 100644 --- a/ui/litellm-dashboard/src/app/onboarding/page.tsx +++ b/ui/litellm-dashboard/src/app/onboarding/page.tsx @@ -17,6 +17,7 @@ import { userUpdateUserCall, getOnboardingCredentials, claimOnboardingToken, + getUiConfig, } from "@/components/networking"; import { jwtDecode } from "jwt-decode"; import { Form, Button as Button2, message } from "antd"; @@ -33,11 +34,20 @@ export default function Onboarding() { const [userID, setUserID] = useState(null); const [loginUrl, setLoginUrl] = useState(""); const [jwtToken, setJwtToken] = useState(""); + const [getUiConfigLoading, setGetUiConfigLoading] = useState(true); useEffect(() => { - if (!inviteID) { + getUiConfig().then((data) => { // get the information for constructing the proxy base url, and then set the token and auth loading + console.log("ui config in onboarding.tsx:", data); + setGetUiConfigLoading(false); + }); + }, []); + + useEffect(() => { + if (!inviteID || getUiConfigLoading) { // wait for the ui config to be loaded return; } + getOnboardingCredentials(inviteID).then((data) => { const login_url = data.login_url; console.log("login_url:", login_url); diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 9509c812a01..8d9857d1a1c 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -26,7 +26,7 @@ import ChatUI from "@/components/chat_ui"; import Sidebar from "@/components/leftnav"; import Usage from "@/components/usage"; import CacheDashboard from "@/components/cache_dashboard"; -import { proxyBaseUrl, setGlobalLitellmHeaderName, updateProxyBaseUrl } from "@/components/networking"; +import { getUiConfig, proxyBaseUrl, setGlobalLitellmHeaderName } from "@/components/networking"; import { Organization } from "@/components/networking"; import GuardrailsPanel from "@/components/guardrails"; import TransformRequestPanel from "@/components/transform_request"; @@ -149,8 +149,11 @@ export default function CreateKeyPage() { useEffect(() => { const token = getCookie("token"); - setToken(token); - setAuthLoading(false); + getUiConfig().then((data) => { // get the information for constructing the proxy base url, and then set the token and auth loading + console.log("ui config in page.tsx:", data); + setToken(token); + setAuthLoading(false); + }); }, []); useEffect(() => { @@ -164,6 +167,7 @@ export default function CreateKeyPage() { return; } + const decoded = jwtDecode(token) as { [key: string]: any }; if (decoded) { // cast decoded to dictionary @@ -214,12 +218,12 @@ export default function CreateKeyPage() { if (decoded.user_id) { setUserID(decoded.user_id); } - - if (decoded.server_root_path) { - updateProxyBaseUrl(decoded.server_root_path); - } + } + + }, [token]); + useEffect(() => { if (accessToken && userID && userRole) { diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index d4506a8f603..98e0962ee38 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -14,9 +14,19 @@ if (isLocal != true) { console.log = function() {}; } -export const updateProxyBaseUrl = (serverRootPath: string) => { - proxyBaseUrl = isLocal ? "http://localhost:4000" : window.location.origin - proxyBaseUrl += serverRootPath; +const updateProxyBaseUrl = (serverRootPath: string, receivedProxyBaseUrl: string | null = null) => { + /** + * Special function for updating the proxy base url. Should only be called by getUiConfig. + */ + const defaultProxyBaseUrl = isLocal ? "http://localhost:4000" : window.location.origin; + let initialProxyBaseUrl = receivedProxyBaseUrl || defaultProxyBaseUrl; + console.log("proxyBaseUrl:", proxyBaseUrl); + console.log("serverRootPath:", serverRootPath); + if (serverRootPath.length > 0 && !initialProxyBaseUrl.endsWith(serverRootPath) && serverRootPath != "/") { + initialProxyBaseUrl += serverRootPath; + proxyBaseUrl = initialProxyBaseUrl; + } + console.log("Updated proxyBaseUrl:", proxyBaseUrl); }; export const getProxyBaseUrl = () => { @@ -72,6 +82,11 @@ export interface CredentialItem { }; } +export interface LiteLLMWellKnownUiConfig { + server_root_path: string; + proxy_base_url: string | null; +} + export interface CredentialsResponse { credentials: CredentialItem[]; } @@ -104,6 +119,20 @@ export function setGlobalLitellmHeaderName(headerName: string = "Authorization") globalLitellmHeaderName = headerName; } +export const getUiConfig = async () => { + console.log("Getting UI config"); + /**Special route to get the proxy base url and server root path */ + const url = `${proxyBaseUrl}/litellm/.well-known/litellm-ui-config`; + const response = await fetch(url); + const jsonData: LiteLLMWellKnownUiConfig = await response.json(); + /** + * Update the proxy base url and server root path + */ + console.log("jsonData in getUiConfig:", jsonData); + updateProxyBaseUrl(jsonData.server_root_path, jsonData.proxy_base_url); + return jsonData; +} + export const getOpenAPISchema = async () => { const url = proxyBaseUrl ? `${proxyBaseUrl}/openapi.json` : `/openapi.json`; const response = await fetch(url); @@ -4285,6 +4314,8 @@ export const getProxyUISettings = async ( * Get all the models user has access to */ try { + console.log("Getting proxy UI settings"); + console.log("proxyBaseUrl in getProxyUISettings:", proxyBaseUrl); let url = proxyBaseUrl ? `${proxyBaseUrl}/sso/get/ui_settings` : `/sso/get/ui_settings`;