diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 3b70ef0aca6..ed9c98df7c6 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -15,6 +15,7 @@ import APIRef from "@/components/api_ref"; import ChatUI from "@/components/chat_ui"; import Sidebar from "../components/leftnav"; import Usage from "../components/usage"; +import CacheDashboard from "@/components/cache_dashboard"; import { jwtDecode } from "jwt-decode"; import { Typography } from "antd"; @@ -221,6 +222,14 @@ const CreateKeyPage = () => { publicPage={false} premiumUser={premiumUser} /> + ) : page == "caching" ? ( + ) : ( { + if (!date) return undefined; + return date.toISOString().split('T')[0]; + }; + +interface CachePageProps { + accessToken: string | null; + token: string | null; + userRole: string | null; + userID: string | null; + premiumUser: boolean; +} + + +const CacheDashboard: React.FC = ({ + accessToken, + token, + userRole, + userID, + premiumUser, +}) => { + const [filteredData, setFilteredData] = useState([]); + const [selectedApiKeys, setSelectedApiKeys] = useState([]); + const [selectedModels, setSelectedModels] = useState([]); + const [data, setData] = useState([]); + + const [dateValue, setDateValue] = useState({ + from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), + to: new Date(), + }); + + + useEffect(() => { + if (!accessToken || !dateValue) { + return; + } + const fetchData = async () => { + const response = await adminGlobalCacheActivity(accessToken, formatDateWithoutTZ(dateValue.from), formatDateWithoutTZ(dateValue.to)); + setData(response); + }; + fetchData(); + }, [accessToken]); + + const uniqueApiKeys = [...new Set(data.map((item) => item.api_key))]; + const uniqueModels = [...new Set(data.map((item) => item.model))]; + const uniqueCallTypes = [...new Set(data.map((item) => item.call_type))]; + + useEffect(() => { + console.log("DATA IN CACHE DASHBOARD", data); + let newData = data; + if (selectedApiKeys.length > 0) { + newData = newData.filter((item) => selectedApiKeys.includes(item.api_key)); + } + + if (selectedModels.length > 0) { + newData = newData.filter((item) => selectedModels.includes(item.model)); + } + + /* + Data looks like this + [{"api_key":"147dba2181f28914eea90eb484926c293cdcf7f5b5c9c3dd6a004d9e0f9fdb21","call_type":"acompletion","model":"llama3-8b-8192","total_rows":13,"cache_hit_true_rows":0}, + {"api_key":"8c23f021d0535c2e59abb7d83d0e03ccfb8db1b90e231ff082949d95df419e86","call_type":"None","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0}, + {"api_key":"88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b","call_type":"acompletion","model":"gpt-3.5-turbo","total_rows":19,"cache_hit_true_rows":0}, + {"api_key":"88dc28d0f030c55ed4ab77ed8faf098196cb1c05df778539800c9f1243fe6b4b","call_type":"aimage_generation","model":"","total_rows":3,"cache_hit_true_rows":0}, + {"api_key":"0ad4b3c03dcb6de0b5b8f761db798c6a8ae80be3fd1e2ea30c07ce6d5e3bf870","call_type":"None","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0}, + {"api_key":"034224b36e9769bc50e2190634abc3f97cad789b17ca80ac43b82f46cd5579b3","call_type":"","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0}, + {"api_key":"4f9c71cce0a2bb9a0b62ce6f0ebb3245b682702a8851d26932fa7e3b8ebfc755","call_type":"","model":"chatgpt-v-2","total_rows":1,"cache_hit_true_rows":0}, + */ + + // What data we need for bar chat + // ui_data = [ + // { + // name: "Call Type", + // Cache hit: 20, + // LLM API requests: 10, + // } + // ] + + console.log("before processed data in cache dashboard", newData); + + const processedData = newData.reduce((acc, item) => { + console.log("Processing item:", item); + + if (!item.call_type) { + console.log("Item has no call_type:", item); + item.call_type = "Unknown"; + } + + const existingItem = acc.find(i => i.name === item.call_type); + if (existingItem) { + existingItem["LLM API requests"] += (item.total_rows || 0) - (item.cache_hit_true_rows || 0); + existingItem["Cache hit"] += item.cache_hit_true_rows || 0; + } else { + acc.push({ + name: item.call_type, + "LLM API requests": (item.total_rows || 0) - (item.cache_hit_true_rows || 0), + "Cache hit": item.cache_hit_true_rows || 0, + }); + } + return acc; + }, []); + + setFilteredData(processedData); + + console.log("PROCESSED DATA IN CACHE DASHBOARD", processedData); + + }, [selectedApiKeys, selectedModels, dateValue, data]); + + return ( + + API Activity Dashboard + Cache hits vs API requests broken down by call type + + + + + {uniqueApiKeys.map((key) => ( + + {key} + + ))} + + + + + {uniqueModels.map((model) => ( + + {model} + + ))} + + + + + + + + + + ); +}; + +export default CacheDashboard; \ No newline at end of file diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index aa774088596..7a86fbcea62 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -85,22 +85,25 @@ const Sidebar: React.FC = ({ Budgets ) : null} - {userRole == "Admin" ? ( - setPage("general-settings")}> + setPage("caching")}> + Caching + + ) : null} + {userRole == "Admin" ? ( + setPage("general-settings")}> Router Settings ) : null} - {userRole == "Admin" ? ( - setPage("admin-panel")}> + setPage("admin-panel")}> Admin ) : null} - setPage("api_ref")}> + setPage("api_ref")}> API Reference - setPage("model-hub")}> + setPage("model-hub")}> Model Hub diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index e5371f22e4b..9060c339e13 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -1373,6 +1373,47 @@ export const adminGlobalActivity = async ( } }; +export const adminGlobalCacheActivity = async ( + accessToken: String, + startTime: String | undefined, + endTime: String | undefined +) => { + try { + let url = proxyBaseUrl + ? `${proxyBaseUrl}/global/activity/cache_hits` + : `/global/activity/cache_hits`; + + if (startTime && endTime) { + url += `?start_date=${startTime}&end_date=${endTime}`; + } + + const requestOptions: { + method: string; + headers: { + Authorization: string; + }; + } = { + method: "GET", + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }; + + const response = await fetch(url, requestOptions); + + if (!response.ok) { + const errorData = await response.text(); + throw new Error("Network response was not ok"); + } + const data = await response.json(); + console.log(data); + return data; + } catch (error) { + console.error("Failed to fetch spend data:", error); + throw error; + } +}; + export const adminGlobalActivityPerModel = async ( accessToken: String, startTime: String | undefined,