mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
(UI) - Usage page show days when spend is 0 and round spend figures on charts to 2 sig figs (#7991)
* ui with cyan * ui format usage tab better * fix activity page * ui fix activity page * ui show correct usage * ui usage page
This commit is contained in:
parent
bf46ae7346
commit
c9a32ebf76
1 changed files with 182 additions and 40 deletions
|
|
@ -76,15 +76,11 @@ const customTooltip = (props: CustomTooltipTypeBar) => {
|
|||
const value = payload[0].payload;
|
||||
const date = value["startTime"];
|
||||
const model_values = value["models"];
|
||||
// Convert the object into an array of key-value pairs
|
||||
const entries: [string, number][] = Object.entries(model_values).map(
|
||||
([key, value]) => [key, value as number]
|
||||
); // Type assertion to specify the value as number
|
||||
);
|
||||
|
||||
// Sort the array based on the float value in descending order
|
||||
entries.sort((a, b) => b[1] - a[1]);
|
||||
|
||||
// Get the top 5 key-value pairs
|
||||
const topEntries = entries.slice(0, 5);
|
||||
|
||||
return (
|
||||
|
|
@ -98,7 +94,7 @@ const customTooltip = (props: CustomTooltipTypeBar) => {
|
|||
{":"}
|
||||
<span className="text-xs text-tremor-content-emphasis">
|
||||
{" "}
|
||||
{value ? (value < 0.01 ? "<$0.01" : value.toFixed(2)) : ""}
|
||||
{value ? `$${value.toFixed(2)}` : ""}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -169,6 +165,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
to: new Date(),
|
||||
});
|
||||
const [proxySettings, setProxySettings] = useState<ProxySettings | null>(null);
|
||||
const [totalMonthlySpend, setTotalMonthlySpend] = useState<number>(0);
|
||||
|
||||
const firstDay = new Date(
|
||||
currentDate.getFullYear(),
|
||||
|
|
@ -285,7 +282,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
console.log(`End date is ${endTime}`);
|
||||
|
||||
const valueFormatter = (number: number) =>
|
||||
`$ ${new Intl.NumberFormat("us").format(number).toString()}`;
|
||||
`$ ${number.toFixed(2)}`;
|
||||
|
||||
const fetchAndSetData = async (
|
||||
fetchFunction: () => Promise<any>,
|
||||
|
|
@ -301,11 +298,94 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const fetchOverallSpend = () => fetchAndSetData(
|
||||
() => accessToken ? adminSpendLogsCall(accessToken) : Promise.reject("No access token"),
|
||||
setKeySpendData,
|
||||
"Error fetching overall spend"
|
||||
);
|
||||
// Update the fillMissingDates function to handle different date formats
|
||||
const fillMissingDates = (data: any[], startDate: Date, endDate: Date, categories: string[]) => {
|
||||
const filledData = [];
|
||||
const currentDate = new Date(startDate);
|
||||
|
||||
// Helper function to standardize date format
|
||||
const standardizeDate = (dateStr: string) => {
|
||||
if (dateStr.includes('-')) {
|
||||
// Already in YYYY-MM-DD format
|
||||
return dateStr;
|
||||
} else {
|
||||
// Convert "Jan 06" format
|
||||
const [month, day] = dateStr.split(' ');
|
||||
const year = new Date().getFullYear();
|
||||
const monthIndex = new Date(`${month} 01 2024`).getMonth();
|
||||
const fullDate = new Date(year, monthIndex, parseInt(day));
|
||||
return fullDate.toISOString().split('T')[0];
|
||||
}
|
||||
};
|
||||
|
||||
// Create a map of existing dates for quick lookup
|
||||
const existingDates = new Map(
|
||||
data.map(item => {
|
||||
const standardizedDate = standardizeDate(item.date);
|
||||
return [standardizedDate, {
|
||||
...item,
|
||||
date: standardizedDate // Store standardized date format
|
||||
}];
|
||||
})
|
||||
);
|
||||
|
||||
// Iterate through each date in the range
|
||||
while (currentDate <= endDate) {
|
||||
const dateStr = currentDate.toISOString().split('T')[0];
|
||||
|
||||
if (existingDates.has(dateStr)) {
|
||||
// Use existing data if we have it
|
||||
filledData.push(existingDates.get(dateStr));
|
||||
} else {
|
||||
// Create an entry with zero values
|
||||
const emptyEntry: any = {
|
||||
date: dateStr,
|
||||
api_requests: 0,
|
||||
total_tokens: 0
|
||||
};
|
||||
|
||||
// Add zero values for each model/team if needed
|
||||
categories.forEach(category => {
|
||||
if (!emptyEntry[category]) {
|
||||
emptyEntry[category] = 0;
|
||||
}
|
||||
});
|
||||
|
||||
filledData.push(emptyEntry);
|
||||
}
|
||||
|
||||
// Move to next day
|
||||
currentDate.setDate(currentDate.getDate() + 1);
|
||||
}
|
||||
|
||||
return filledData;
|
||||
};
|
||||
|
||||
// Update the fetchOverallSpend function
|
||||
const fetchOverallSpend = async () => {
|
||||
if (!accessToken) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await adminSpendLogsCall(accessToken);
|
||||
|
||||
// Get the first and last day of the current month
|
||||
const now = new Date();
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
// Fill in missing dates
|
||||
const filledData = fillMissingDates(data, firstDay, lastDay, []);
|
||||
|
||||
// Calculate total spend for the month and round to 2 decimal places
|
||||
const monthlyTotal = Number(filledData.reduce((sum, day) => sum + (day.spend || 0), 0).toFixed(2));
|
||||
setTotalMonthlySpend(monthlyTotal);
|
||||
|
||||
setKeySpendData(filledData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching overall spend:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchProviderSpend = () => fetchAndSetData(
|
||||
() => accessToken && token ? adminspendByProvider(accessToken, token, startTime, endTime) : Promise.reject("No access token or token"),
|
||||
|
|
@ -320,7 +400,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
const top_keys = await adminTopKeysCall(accessToken);
|
||||
return top_keys.map((k: any) => ({
|
||||
key: (k["key_alias"] || k["key_name"] || k["api_key"]).substring(0, 10),
|
||||
spend: k["total_spend"],
|
||||
spend: Number(k["total_spend"].toFixed(2)),
|
||||
}));
|
||||
},
|
||||
setTopKeys,
|
||||
|
|
@ -335,7 +415,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
const top_models = await adminTopModelsCall(accessToken);
|
||||
return top_models.map((k: any) => ({
|
||||
key: k["model"],
|
||||
spend: k["total_spend"],
|
||||
spend: Number(k["total_spend"].toFixed(2)),
|
||||
}));
|
||||
},
|
||||
setTopModels,
|
||||
|
|
@ -343,16 +423,31 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
// Update the fetchTeamSpend function
|
||||
const fetchTeamSpend = async () => {
|
||||
if (!accessToken) return;
|
||||
await fetchAndSetData(
|
||||
async () => {
|
||||
const teamSpend = await teamSpendLogsCall(accessToken);
|
||||
setTeamSpendData(teamSpend.daily_spend);
|
||||
|
||||
// Get the first and last day of the current month
|
||||
const now = new Date();
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
// Fill in missing dates with zero values for all teams
|
||||
const filledData = fillMissingDates(
|
||||
teamSpend.daily_spend,
|
||||
firstDay,
|
||||
lastDay,
|
||||
teamSpend.teams
|
||||
);
|
||||
|
||||
setTeamSpendData(filledData);
|
||||
setUniqueTeamIds(teamSpend.teams);
|
||||
return teamSpend.total_spend_per_team.map((tspt: any) => ({
|
||||
name: tspt["team_id"] || "",
|
||||
value: (tspt["total_spend"] || 0).toFixed(2),
|
||||
value: Number(tspt["total_spend"] || 0).toFixed(2),
|
||||
}));
|
||||
},
|
||||
setTotalSpendPerTeam,
|
||||
|
|
@ -390,22 +485,60 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
const fetchGlobalActivity = () => {
|
||||
// Update the fetchGlobalActivity function
|
||||
const fetchGlobalActivity = async () => {
|
||||
if (!accessToken) return;
|
||||
fetchAndSetData(
|
||||
() => adminGlobalActivity(accessToken, startTime, endTime),
|
||||
setGlobalActivity,
|
||||
"Error fetching global activity"
|
||||
);
|
||||
try {
|
||||
const data = await adminGlobalActivity(accessToken, startTime, endTime);
|
||||
|
||||
// Get the date range from the current month
|
||||
const now = new Date();
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
// Fill in missing dates for daily_data
|
||||
const filledDailyData = fillMissingDates(
|
||||
data.daily_data || [],
|
||||
firstDay,
|
||||
lastDay,
|
||||
['api_requests', 'total_tokens']
|
||||
);
|
||||
|
||||
setGlobalActivity({
|
||||
...data,
|
||||
daily_data: filledDailyData
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching global activity:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchGlobalActivityPerModel = () => {
|
||||
// Update the fetchGlobalActivityPerModel function
|
||||
const fetchGlobalActivityPerModel = async () => {
|
||||
if (!accessToken) return;
|
||||
fetchAndSetData(
|
||||
() => adminGlobalActivityPerModel(accessToken, startTime, endTime),
|
||||
setGlobalActivityPerModel,
|
||||
"Error fetching global activity per model"
|
||||
);
|
||||
try {
|
||||
const data = await adminGlobalActivityPerModel(accessToken, startTime, endTime);
|
||||
|
||||
// Get the date range from the current month
|
||||
const now = new Date();
|
||||
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
// Fill in missing dates for each model's daily data
|
||||
const filledModelData = data.map((modelData: any) => ({
|
||||
...modelData,
|
||||
daily_data: fillMissingDates(
|
||||
modelData.daily_data || [],
|
||||
firstDay,
|
||||
lastDay,
|
||||
['api_requests', 'total_tokens']
|
||||
)
|
||||
}));
|
||||
|
||||
setGlobalActivityPerModel(filledModelData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching global activity per model:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -492,14 +625,19 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
<TabPanels>
|
||||
<TabPanel>
|
||||
<Grid numItems={2} className="gap-2 h-[100vh] w-full">
|
||||
<ViewUserSpend
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
accessToken={accessToken}
|
||||
userSpend={null}
|
||||
selectedTeam={null}
|
||||
userMaxBudget={null}
|
||||
/>
|
||||
<Col numColSpan={2}>
|
||||
<Text className="text-tremor-default text-tremor-content dark:text-dark-tremor-content mb-2 mt-2 text-lg">
|
||||
Project Spend {new Date().toLocaleString('default', { month: 'long' })} 1 - {new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0).getDate()}
|
||||
</Text>
|
||||
<ViewUserSpend
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
accessToken={accessToken}
|
||||
userSpend={totalMonthlySpend}
|
||||
selectedTeam={null}
|
||||
userMaxBudget={null}
|
||||
/>
|
||||
</Col>
|
||||
<Col numColSpan={2}>
|
||||
<Card>
|
||||
<Title>Monthly Spend</Title>
|
||||
|
|
@ -507,7 +645,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
data={keySpendData}
|
||||
index="date"
|
||||
categories={["spend"]}
|
||||
colors={["blue"]}
|
||||
colors={["cyan"]}
|
||||
valueFormatter={valueFormatter}
|
||||
yAxisWidth={100}
|
||||
tickGap={5}
|
||||
|
|
@ -523,12 +661,13 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
data={topKeys}
|
||||
index="key"
|
||||
categories={["spend"]}
|
||||
colors={["blue"]}
|
||||
colors={["cyan"]}
|
||||
yAxisWidth={80}
|
||||
tickGap={5}
|
||||
layout="vertical"
|
||||
showXAxis={false}
|
||||
showLegend={false}
|
||||
valueFormatter={(value) => `$${value.toFixed(2)}`}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
@ -540,11 +679,12 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
data={topModels}
|
||||
index="key"
|
||||
categories={["spend"]}
|
||||
colors={["blue"]}
|
||||
colors={["cyan"]}
|
||||
yAxisWidth={200}
|
||||
layout="vertical"
|
||||
showXAxis={false}
|
||||
showLegend={false}
|
||||
valueFormatter={(value) => `$${value.toFixed(2)}`}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
|
|
@ -564,6 +704,8 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
data={spendByProvider}
|
||||
index="provider"
|
||||
category="spend"
|
||||
colors={["cyan"]}
|
||||
valueFormatter={(value) => `$${value.toFixed(2)}`}
|
||||
/>
|
||||
</Col>
|
||||
<Col numColSpan={1}>
|
||||
|
|
@ -880,7 +1022,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
|
|||
data={topTagsData}
|
||||
index="name"
|
||||
categories={["spend"]}
|
||||
colors={["blue"]}
|
||||
colors={["cyan"]}
|
||||
>
|
||||
|
||||
</BarChart>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue