(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:
Ishaan Jaff 2025-01-24 18:49:32 -08:00 • committed by GitHub
parent bf46ae7346
commit c9a32ebf76
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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>