ui show spend per tag

This commit is contained in:
Ishaan Jaff 2024-04-04 16:57:45 -07:00
parent 12e5118367
commit ac5507bd84
4 changed files with 170 additions and 0 deletions

View file

@ -1,5 +1,6 @@
# Enterprise Proxy Util Endpoints
from litellm._logging import verbose_logger
import collections
async def get_spend_by_tags(start_date=None, end_date=None, prisma_client=None):
@ -17,6 +18,43 @@ async def get_spend_by_tags(start_date=None, end_date=None, prisma_client=None):
return response
async def ui_get_spend_by_tags(start_date=None, end_date=None, prisma_client=None):
response = await prisma_client.db.query_raw(
"""
SELECT
jsonb_array_elements_text(request_tags) AS individual_request_tag,
DATE(s."startTime") AS spend_date,
COUNT(*) AS log_count,
SUM(spend) AS total_spend
FROM "LiteLLM_SpendLogs" s
WHERE s."startTime" >= current_date - interval '30 days'
GROUP BY individual_request_tag, spend_date
ORDER BY spend_date;
"""
)
# print("tags - spend")
# print(response)
# Bar Chart 1 - Spend per tag - Top 10 tags by spend
total_spend_per_tag = collections.defaultdict(float)
for row in response:
tag_name = row["individual_request_tag"]
tag_spend = row["total_spend"]
total_spend_per_tag[tag_name] += tag_spend
# get top 10 tags
top_10_tags = sorted(total_spend_per_tag.items(), key=lambda x: x[1], reverse=True)[
:10
]
# convert to ui format
ui_top_10_tags = [{"name": tag[0], "value": tag[1]} for tag in top_10_tags]
# Bar Chart 2 - Daily Spend per tag
return {"top_10_tags": ui_top_10_tags, "daily_spend_per_tag": total_spend_per_tag}
async def view_spend_logs_from_clickhouse(
api_key=None, user_id=None, request_id=None, start_date=None, end_date=None
):

View file

@ -4753,6 +4753,73 @@ async def view_spend_tags(
)
@router.get(
"/global/spend/tags",
tags=["Budget & Spend Tracking"],
dependencies=[Depends(user_api_key_auth)],
include_in_schema=False,
responses={
200: {"model": List[LiteLLM_SpendLogs]},
},
)
async def global_view_spend_tags(
start_date: Optional[str] = fastapi.Query(
default=None,
description="Time from which to start viewing key spend",
),
end_date: Optional[str] = fastapi.Query(
default=None,
description="Time till which to view key spend",
),
):
"""
LiteLLM Enterprise - View Spend Per Request Tag. Used by LiteLLM UI
Example Request:
```
curl -X GET "http://0.0.0.0:8000/spend/tags" \
-H "Authorization: Bearer sk-1234"
```
Spend with Start Date and End Date
```
curl -X GET "http://0.0.0.0:8000/spend/tags?start_date=2022-01-01&end_date=2022-02-01" \
-H "Authorization: Bearer sk-1234"
```
"""
from enterprise.utils import ui_get_spend_by_tags
global prisma_client
try:
if prisma_client is None:
raise Exception(
f"Database not connected. Connect a database to your proxy - https://docs.litellm.ai/docs/simple_proxy#managing-auth---virtual-keys"
)
response = await ui_get_spend_by_tags(
start_date=start_date, end_date=end_date, prisma_client=prisma_client
)
return response
except Exception as e:
if isinstance(e, HTTPException):
raise ProxyException(
message=getattr(e, "detail", f"/spend/tags Error({str(e)})"),
type="internal_error",
param=getattr(e, "param", "None"),
code=getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR),
)
elif isinstance(e, ProxyException):
raise e
raise ProxyException(
message="/spend/tags Error" + str(e),
type="internal_error",
param=getattr(e, "param", "None"),
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
@router.post(
"/spend/calculate",
tags=["Budget & Spend Tracking"],

View file

@ -434,6 +434,36 @@ export const teamSpendLogsCall = async (accessToken: String) => {
}
};
export const tagsSpendLogsCall = async (accessToken: String) => {
try {
const url = proxyBaseUrl
? `${proxyBaseUrl}/global/spend/tags`
: `/global/spend/tags`;
console.log("in tagsSpendLogsCall:", url);
const response = await fetch(`${url}`, {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
if (!response.ok) {
const errorData = await response.text();
message.error(errorData);
throw new Error("Network response was not ok");
}
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error("Failed to create key:", error);
throw error;
}
};
export const userSpendLogsCall = async (
accessToken: String,
token: String,

View file

@ -11,6 +11,7 @@ import {
adminTopKeysCall,
adminTopModelsCall,
teamSpendLogsCall,
tagsSpendLogsCall
} from "./networking";
import { start } from "repl";
@ -139,6 +140,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
const [topModels, setTopModels] = useState<any[]>([]);
const [topUsers, setTopUsers] = useState<any[]>([]);
const [teamSpendData, setTeamSpendData] = useState<any[]>([]);
const [topTagsData, setTopTagsData] = useState<any[]>([]);
const [uniqueTeamIds, setUniqueTeamIds] = useState<any[]>([]);
const [totalSpendPerTeam, setTotalSpendPerTeam] = useState<any[]>([]);
@ -217,6 +219,10 @@ const UsagePage: React.FC<UsagePageProps> = ({
})
setTotalSpendPerTeam(total_spend_per_team);
//get top tags
const top_tags = await tagsSpendLogsCall(accessToken);
setTopTagsData(top_tags.top_10_tags);
} else if (userRole == "App Owner") {
await userSpendLogsCall(
accessToken,
@ -273,6 +279,7 @@ const UsagePage: React.FC<UsagePageProps> = ({
<TabList className="mt-2">
<Tab>All Up</Tab>
<Tab>Team Based Usage</Tab>
<Tab>Tag Based Usage</Tab>
</TabList>
<TabPanels>
<TabPanel>
@ -371,6 +378,34 @@ const UsagePage: React.FC<UsagePageProps> = ({
</Col>
</Grid>
</TabPanel>
<TabPanel>
<Grid numItems={2} className="gap-2 h-[75vh] w-full">
<Col numColSpan={2}>
<Card className="mb-2">
<Title>Total Spend Per Tag</Title>
<BarList
data={topTagsData}
/>
</Card>
<Card>
<Title>Daily Spend Per Tag</Title>
<BarChart
className="h-72"
data={teamSpendData}
showLegend={true}
index="date"
categories={uniqueTeamIds}
yAxisWidth={80}
stack={true}
/>
</Card>
</Col>
<Col numColSpan={2}>
</Col>
</Grid>
</TabPanel>
</TabPanels>
</TabGroup>
</div>