mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 19:54:13 +00:00
Merge pull request #3609 from BerriAI/litellm_send_daily_spend_report
[Feat] send weekly spend reports by Team/Tag
This commit is contained in:
commit
471cb148cb
3 changed files with 175 additions and 1 deletions
|
@ -873,3 +873,99 @@ Model Info:
|
||||||
) # shuffle to prevent collisions
|
) # shuffle to prevent collisions
|
||||||
await asyncio.sleep(interval)
|
await asyncio.sleep(interval)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
async def send_weekly_spend_report(self):
|
||||||
|
""" """
|
||||||
|
try:
|
||||||
|
from litellm.proxy.proxy_server import _get_spend_report_for_time_range
|
||||||
|
|
||||||
|
todays_date = datetime.datetime.now().date()
|
||||||
|
week_before = todays_date - datetime.timedelta(days=7)
|
||||||
|
|
||||||
|
weekly_spend_per_team, weekly_spend_per_tag = (
|
||||||
|
await _get_spend_report_for_time_range(
|
||||||
|
start_date=week_before.strftime("%Y-%m-%d"),
|
||||||
|
end_date=todays_date.strftime("%Y-%m-%d"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
_weekly_spend_message = f"*💸 Weekly Spend Report for `{week_before.strftime('%m-%d-%Y')} - {todays_date.strftime('%m-%d-%Y')}` *\n"
|
||||||
|
|
||||||
|
if weekly_spend_per_team is not None:
|
||||||
|
_weekly_spend_message += "\n*Team Spend Report:*\n"
|
||||||
|
for spend in weekly_spend_per_team:
|
||||||
|
_team_spend = spend["total_spend"]
|
||||||
|
_team_spend = float(_team_spend)
|
||||||
|
# round to 4 decimal places
|
||||||
|
_team_spend = round(_team_spend, 4)
|
||||||
|
_weekly_spend_message += (
|
||||||
|
f"Team: `{spend['team_alias']}` | Spend: `${_team_spend}`\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
if weekly_spend_per_tag is not None:
|
||||||
|
_weekly_spend_message += "\n*Tag Spend Report:*\n"
|
||||||
|
for spend in weekly_spend_per_tag:
|
||||||
|
_tag_spend = spend["total_spend"]
|
||||||
|
_tag_spend = float(_tag_spend)
|
||||||
|
# round to 4 decimal places
|
||||||
|
_tag_spend = round(_tag_spend, 4)
|
||||||
|
_weekly_spend_message += f"Tag: `{spend['individual_request_tag']}` | Spend: `${_tag_spend}`\n"
|
||||||
|
|
||||||
|
await self.send_alert(
|
||||||
|
message=_weekly_spend_message,
|
||||||
|
level="Low",
|
||||||
|
alert_type="daily_reports",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
verbose_proxy_logger.error("Error sending weekly spend report", e)
|
||||||
|
|
||||||
|
async def send_monthly_spend_report(self):
|
||||||
|
""" """
|
||||||
|
try:
|
||||||
|
from calendar import monthrange
|
||||||
|
|
||||||
|
from litellm.proxy.proxy_server import _get_spend_report_for_time_range
|
||||||
|
|
||||||
|
todays_date = datetime.datetime.now().date()
|
||||||
|
first_day_of_month = todays_date.replace(day=1)
|
||||||
|
_, last_day_of_month = monthrange(todays_date.year, todays_date.month)
|
||||||
|
last_day_of_month = first_day_of_month + datetime.timedelta(
|
||||||
|
days=last_day_of_month - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
monthly_spend_per_team, monthly_spend_per_tag = (
|
||||||
|
await _get_spend_report_for_time_range(
|
||||||
|
start_date=first_day_of_month.strftime("%Y-%m-%d"),
|
||||||
|
end_date=last_day_of_month.strftime("%Y-%m-%d"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
_spend_message = f"*💸 Monthly Spend Report for `{first_day_of_month.strftime('%m-%d-%Y')} - {last_day_of_month.strftime('%m-%d-%Y')}` *\n"
|
||||||
|
|
||||||
|
if monthly_spend_per_team is not None:
|
||||||
|
_spend_message += "\n*Team Spend Report:*\n"
|
||||||
|
for spend in monthly_spend_per_team:
|
||||||
|
_team_spend = spend["total_spend"]
|
||||||
|
_team_spend = float(_team_spend)
|
||||||
|
# round to 4 decimal places
|
||||||
|
_team_spend = round(_team_spend, 4)
|
||||||
|
_spend_message += (
|
||||||
|
f"Team: `{spend['team_alias']}` | Spend: `${_team_spend}`\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
if monthly_spend_per_tag is not None:
|
||||||
|
_spend_message += "\n*Tag Spend Report:*\n"
|
||||||
|
for spend in monthly_spend_per_tag:
|
||||||
|
_tag_spend = spend["total_spend"]
|
||||||
|
_tag_spend = float(_tag_spend)
|
||||||
|
# round to 4 decimal places
|
||||||
|
_tag_spend = round(_tag_spend, 4)
|
||||||
|
_spend_message += f"Tag: `{spend['individual_request_tag']}` | Spend: `${_tag_spend}`\n"
|
||||||
|
|
||||||
|
await self.send_alert(
|
||||||
|
message=_spend_message,
|
||||||
|
level="Low",
|
||||||
|
alert_type="daily_reports",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
verbose_proxy_logger.error("Error sending weekly spend report", e)
|
||||||
|
|
|
@ -3479,6 +3479,26 @@ async def startup_event():
|
||||||
await proxy_config.add_deployment(
|
await proxy_config.add_deployment(
|
||||||
prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj
|
prisma_client=prisma_client, proxy_logging_obj=proxy_logging_obj
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
proxy_logging_obj is not None
|
||||||
|
and proxy_logging_obj.slack_alerting_instance is not None
|
||||||
|
and prisma_client is not None
|
||||||
|
):
|
||||||
|
print("Alerting: Initializing Weekly/Monthly Spend Reports") # noqa
|
||||||
|
### Schedule weekly/monhtly spend reports ###
|
||||||
|
scheduler.add_job(
|
||||||
|
proxy_logging_obj.slack_alerting_instance.send_weekly_spend_report,
|
||||||
|
"cron",
|
||||||
|
day_of_week="mon",
|
||||||
|
)
|
||||||
|
|
||||||
|
scheduler.add_job(
|
||||||
|
proxy_logging_obj.slack_alerting_instance.send_monthly_spend_report,
|
||||||
|
"cron",
|
||||||
|
day=1,
|
||||||
|
)
|
||||||
|
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
|
|
||||||
|
|
||||||
|
@ -5431,6 +5451,55 @@ async def global_view_spend_tags(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _get_spend_report_for_time_range(
|
||||||
|
start_date: str,
|
||||||
|
end_date: str,
|
||||||
|
):
|
||||||
|
global prisma_client
|
||||||
|
if prisma_client is None:
|
||||||
|
verbose_proxy_logger.error(
|
||||||
|
f"Database not connected. Connect a database to your proxy for weekly, monthly spend reports"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
sql_query = """
|
||||||
|
SELECT
|
||||||
|
t.team_alias,
|
||||||
|
SUM(s.spend) AS total_spend
|
||||||
|
FROM
|
||||||
|
"LiteLLM_SpendLogs" s
|
||||||
|
LEFT JOIN
|
||||||
|
"LiteLLM_TeamTable" t ON s.team_id = t.team_id
|
||||||
|
WHERE
|
||||||
|
s."startTime"::DATE >= $1::date AND s."startTime"::DATE <= $2::date
|
||||||
|
GROUP BY
|
||||||
|
t.team_alias
|
||||||
|
ORDER BY
|
||||||
|
total_spend DESC;
|
||||||
|
"""
|
||||||
|
response = await prisma_client.db.query_raw(sql_query, start_date, end_date)
|
||||||
|
|
||||||
|
# get spend per tag for today
|
||||||
|
sql_query = """
|
||||||
|
SELECT
|
||||||
|
jsonb_array_elements_text(request_tags) AS individual_request_tag,
|
||||||
|
SUM(spend) AS total_spend
|
||||||
|
FROM "LiteLLM_SpendLogs"
|
||||||
|
WHERE "startTime"::DATE >= $1::date AND "startTime"::DATE <= $2::date
|
||||||
|
GROUP BY individual_request_tag
|
||||||
|
ORDER BY total_spend DESC;
|
||||||
|
"""
|
||||||
|
|
||||||
|
spend_per_tag = await prisma_client.db.query_raw(
|
||||||
|
sql_query, start_date, end_date
|
||||||
|
)
|
||||||
|
|
||||||
|
return response, spend_per_tag
|
||||||
|
except Exception as e:
|
||||||
|
verbose_proxy_logger.error("Exception in _get_daily_spend_reports", e) # noqa
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/spend/calculate",
|
"/spend/calculate",
|
||||||
tags=["Budget & Spend Tracking"],
|
tags=["Budget & Spend Tracking"],
|
||||||
|
@ -5818,7 +5887,7 @@ async def global_spend_keys(
|
||||||
tags=["Budget & Spend Tracking"],
|
tags=["Budget & Spend Tracking"],
|
||||||
dependencies=[Depends(user_api_key_auth)],
|
dependencies=[Depends(user_api_key_auth)],
|
||||||
)
|
)
|
||||||
async def global_spend_per_tea():
|
async def global_spend_per_team():
|
||||||
"""
|
"""
|
||||||
[BETA] This is a beta endpoint. It will change.
|
[BETA] This is a beta endpoint. It will change.
|
||||||
|
|
||||||
|
@ -9503,6 +9572,14 @@ async def health_services_endpoint(
|
||||||
level="Low",
|
level="Low",
|
||||||
alert_type="budget_alerts",
|
alert_type="budget_alerts",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if prisma_client is not None:
|
||||||
|
asyncio.create_task(
|
||||||
|
proxy_logging_obj.slack_alerting_instance.send_monthly_spend_report()
|
||||||
|
)
|
||||||
|
asyncio.create_task(
|
||||||
|
proxy_logging_obj.slack_alerting_instance.send_weekly_spend_report()
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"message": "Mock Slack Alert sent, verify Slack Alert Received on your channel",
|
"message": "Mock Slack Alert sent, verify Slack Alert Received on your channel",
|
||||||
|
|
|
@ -109,6 +109,7 @@ const Settings: React.FC<SettingsPageProps> = ({
|
||||||
"llm_requests_hanging": "LLM Requests Hanging",
|
"llm_requests_hanging": "LLM Requests Hanging",
|
||||||
"budget_alerts": "Budget Alerts (API Keys, Users)",
|
"budget_alerts": "Budget Alerts (API Keys, Users)",
|
||||||
"db_exceptions": "Database Exceptions (Read/Write)",
|
"db_exceptions": "Database Exceptions (Read/Write)",
|
||||||
|
"daily_reports": "Weekly/Monthly Spend Reports",
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue