mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 19:54:13 +00:00
ui - add new cache hits page
This commit is contained in:
parent
7cba8f6de5
commit
1a22ee9152
4 changed files with 241 additions and 6 deletions
|
@ -15,6 +15,7 @@ import APIRef from "@/components/api_ref";
|
||||||
import ChatUI from "@/components/chat_ui";
|
import ChatUI from "@/components/chat_ui";
|
||||||
import Sidebar from "../components/leftnav";
|
import Sidebar from "../components/leftnav";
|
||||||
import Usage from "../components/usage";
|
import Usage from "../components/usage";
|
||||||
|
import CacheDashboard from "@/components/cache_dashboard";
|
||||||
import { jwtDecode } from "jwt-decode";
|
import { jwtDecode } from "jwt-decode";
|
||||||
import { Typography } from "antd";
|
import { Typography } from "antd";
|
||||||
|
|
||||||
|
@ -221,6 +222,14 @@ const CreateKeyPage = () => {
|
||||||
publicPage={false}
|
publicPage={false}
|
||||||
premiumUser={premiumUser}
|
premiumUser={premiumUser}
|
||||||
/>
|
/>
|
||||||
|
) : page == "caching" ? (
|
||||||
|
<CacheDashboard
|
||||||
|
userID={userID}
|
||||||
|
userRole={userRole}
|
||||||
|
token={token}
|
||||||
|
accessToken={accessToken}
|
||||||
|
premiumUser={premiumUser}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Usage
|
<Usage
|
||||||
userID={userID}
|
userID={userID}
|
||||||
|
|
182
ui/litellm-dashboard/src/components/cache_dashboard.tsx
Normal file
182
ui/litellm-dashboard/src/components/cache_dashboard.tsx
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Title,
|
||||||
|
BarChart,
|
||||||
|
Subtitle,
|
||||||
|
Grid,
|
||||||
|
Col,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
DateRangePicker,
|
||||||
|
DateRangePickerValue,
|
||||||
|
MultiSelect,
|
||||||
|
MultiSelectItem,
|
||||||
|
} from "@tremor/react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
adminGlobalCacheActivity,
|
||||||
|
} from "./networking";
|
||||||
|
|
||||||
|
const formatDateWithoutTZ = (date: Date | undefined) => {
|
||||||
|
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<CachePageProps> = ({
|
||||||
|
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<DateRangePickerValue>({
|
||||||
|
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 (
|
||||||
|
<Card>
|
||||||
|
<Title>API Activity Dashboard</Title>
|
||||||
|
<Subtitle>Cache hits vs API requests broken down by call type</Subtitle>
|
||||||
|
|
||||||
|
<Grid numItems={3} className="gap-4 mt-4">
|
||||||
|
<Col>
|
||||||
|
<MultiSelect
|
||||||
|
placeholder="Select API Keys"
|
||||||
|
value={selectedApiKeys}
|
||||||
|
onValueChange={setSelectedApiKeys}
|
||||||
|
>
|
||||||
|
{uniqueApiKeys.map((key) => (
|
||||||
|
<MultiSelectItem key={key} value={key}>
|
||||||
|
{key}
|
||||||
|
</MultiSelectItem>
|
||||||
|
))}
|
||||||
|
</MultiSelect>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<MultiSelect
|
||||||
|
placeholder="Select Models"
|
||||||
|
value={selectedModels}
|
||||||
|
onValueChange={setSelectedModels}
|
||||||
|
>
|
||||||
|
{uniqueModels.map((model) => (
|
||||||
|
<MultiSelectItem key={model} value={model}>
|
||||||
|
{model}
|
||||||
|
</MultiSelectItem>
|
||||||
|
))}
|
||||||
|
</MultiSelect>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<DateRangePicker
|
||||||
|
value={dateValue}
|
||||||
|
// onChange={setDateRange}
|
||||||
|
selectPlaceholder="Select date range"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<BarChart
|
||||||
|
className="mt-6"
|
||||||
|
data={filteredData}
|
||||||
|
index="name"
|
||||||
|
categories={["LLM API requests", "Cache hit"]}
|
||||||
|
colors={["blue", "teal"]}
|
||||||
|
yAxisWidth={48}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CacheDashboard;
|
|
@ -85,22 +85,25 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||||
<Text>Budgets</Text>
|
<Text>Budgets</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{userRole == "Admin" ? (
|
{userRole == "Admin" ? (
|
||||||
<Menu.Item key="10" onClick={() => setPage("general-settings")}>
|
<Menu.Item key="10" onClick={() => setPage("caching")}>
|
||||||
|
<Text>Caching</Text>
|
||||||
|
</Menu.Item>
|
||||||
|
) : null}
|
||||||
|
{userRole == "Admin" ? (
|
||||||
|
<Menu.Item key="11" onClick={() => setPage("general-settings")}>
|
||||||
<Text>Router Settings</Text>
|
<Text>Router Settings</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{userRole == "Admin" ? (
|
{userRole == "Admin" ? (
|
||||||
<Menu.Item key="11" onClick={() => setPage("admin-panel")}>
|
<Menu.Item key="12" onClick={() => setPage("admin-panel")}>
|
||||||
<Text>Admin</Text>
|
<Text>Admin</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
) : null}
|
) : null}
|
||||||
<Menu.Item key="12" onClick={() => setPage("api_ref")}>
|
<Menu.Item key="13" onClick={() => setPage("api_ref")}>
|
||||||
<Text>API Reference</Text>
|
<Text>API Reference</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="14" onClick={() => setPage("model-hub")}>
|
<Menu.Item key="15" onClick={() => setPage("model-hub")}>
|
||||||
<Text>Model Hub</Text>
|
<Text>Model Hub</Text>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
|
@ -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 (
|
export const adminGlobalActivityPerModel = async (
|
||||||
accessToken: String,
|
accessToken: String,
|
||||||
startTime: String | undefined,
|
startTime: String | undefined,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue