diff --git a/ui/litellm-dashboard/src/components/cache_dashboard.tsx b/ui/litellm-dashboard/src/components/cache_dashboard.tsx index 92a02fe1c7..f8bb2ac644 100644 --- a/ui/litellm-dashboard/src/components/cache_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/cache_dashboard.tsx @@ -12,10 +12,28 @@ import { DateRangePickerValue, MultiSelect, MultiSelectItem, + Button, + TabPanel, + TabPanels, + TabGroup, + TabList, + Tab, + TextInput, + Icon, + Text, } from "@tremor/react"; +import { + Button as Button2, + message, +} from "antd"; +import { + RefreshIcon, +} from "@heroicons/react/outline"; import { adminGlobalCacheActivity, + cachingHealthCheckCall, + healthCheckCall, } from "./networking"; const formatDateWithoutTZ = (date: Date | undefined) => { @@ -86,6 +104,8 @@ const CacheDashboard: React.FC = ({ to: new Date(), }); + const [lastRefreshed, setLastRefreshed] = useState(""); + const [healthCheckResponse, setHealthCheckResponse] = useState(""); useEffect(() => { if (!accessToken || !dateValue) { @@ -96,6 +116,9 @@ const CacheDashboard: React.FC = ({ setData(response); }; fetchData(); + + const currentDate = new Date(); + setLastRefreshed(currentDate.toLocaleString()); }, [accessToken]); const uniqueApiKeys = Array.from(new Set(data.map((item) => item?.api_key ?? ""))); @@ -208,8 +231,50 @@ const CacheDashboard: React.FC = ({ }, [selectedApiKeys, selectedModels, dateValue, data]); + +const handleRefreshClick = () => { + // Update the 'lastRefreshed' state to the current date and time + const currentDate = new Date(); + setLastRefreshed(currentDate.toLocaleString()); +}; + +const runCachingHealthCheck = async () => { + try { + message.info("Running cache health check..."); + setHealthCheckResponse(""); + const response = await cachingHealthCheckCall(accessToken !== null ? accessToken : ""); + console.log("CACHING HEALTH CHECK RESPONSE", response); + setHealthCheckResponse(response); + } catch (error) { + console.error("Error running health check:", error); + setHealthCheckResponse("Error running health check"); + } +}; + return ( - + + +
+ Cache Analytics + +
Cache Health
+
+
+ +
+ {lastRefreshed && Last Refreshed: {lastRefreshed}} + +
+
+ + + = ({
- +

Cache Hit Ratio

@@ -314,11 +379,28 @@ const CacheDashboard: React.FC = ({
+ + + + + Cache health will run a very small request through API /cache/ping + configured on litellm + - - - - + + {healthCheckResponse && ( +
+                  {JSON.stringify(healthCheckResponse, null, 2)}
+                
+ )} +
+
+ + ); }; diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index 4811f2d5b2..8de9bf1a2f 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -58,6 +58,8 @@ const menuItems: MenuItem[] = [ { key: "14", page: "api_ref", label: "API Reference", icon: }, { key: "16", page: "model-hub", label: "Model Hub", icon: }, { key: "15", page: "logs", label: "Logs", icon: , roles: all_admin_roles }, + + { key: "experimental", page: "experimental", @@ -68,6 +70,7 @@ const menuItems: MenuItem[] = [ { key: "9", page: "caching", label: "Caching", icon: , roles: all_admin_roles }, { key: "10", page: "budgets", label: "Budgets", icon: , roles: all_admin_roles }, { key: "11", page: "guardrails", label: "Guardrails", icon: , roles: all_admin_roles }, + ] }, { diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index f385423979..0a529ecf6c 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -3210,6 +3210,38 @@ export const healthCheckCall = async (accessToken: String) => { } }; +export const cachingHealthCheckCall = async (accessToken: String) => { + /** + * Get all the models user has access to + */ + try { + let url = proxyBaseUrl ? `${proxyBaseUrl}/cache/ping` : `/cache/ping`; + + //message.info("Requesting model data"); + const response = await fetch(url, { + method: "GET", + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorData = await response.text(); + handleError(errorData); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + //message.info("Received model data"); + return data; + // Handle success - you might want to update some state or UI based on the created key + } catch (error) { + console.error("Failed to call /cache/ping:", error); + throw error; + } +}; + export const getProxyUISettings = async ( accessToken: String, ) => {