From 03a0b274f7c8821a9b7b1893716e35f8dbb32a37 Mon Sep 17 00:00:00 2001 From: ishaan-jaff Date: Fri, 1 Mar 2024 09:28:22 -0800 Subject: [PATCH] (feat) predict spend --- enterprise/utils.py | 55 +++++++++++-------- litellm/proxy/proxy_server.py | 2 + .../src/components/networking.tsx | 39 +++++++++++++ .../src/components/view_key_spend_report.tsx | 21 ++++++- 4 files changed, 90 insertions(+), 27 deletions(-) diff --git a/enterprise/utils.py b/enterprise/utils.py index ba4c8d0a4..ad8148f38 100644 --- a/enterprise/utils.py +++ b/enterprise/utils.py @@ -251,27 +251,30 @@ def _forecast_daily_cost(data: list): import requests from datetime import datetime, timedelta - # Get the last entry in the data + first_entry = data[0] last_entry = data[-1] - # Parse the date from the last entry - last_entry_date = datetime.strptime(last_entry["date"], "%Y-%m-%d").date() - # print("Last Entry Date:", last_entry_date) + # get the date today + today_date = datetime.today().date() - # Get the month of the last entry - last_entry_month = last_entry_date.month - # print("Last Entry Month:", last_entry_month) + today_day_month = today_date.month + + # Parse the date from the first entry + first_entry_date = datetime.strptime(first_entry["date"], "%Y-%m-%d").date() + last_entry_date = datetime.strptime(last_entry["date"], "%Y-%m-%d") + + print("last entry date", last_entry_date) + + # Assuming today_date is a datetime object + today_date = datetime.now() # Calculate the last day of the month - last_day_of_month = ( - datetime(last_entry_date.year, last_entry_date.month % 12 + 1, 1) - - timedelta(days=1) - ).day - # print("Last Day of Month:", last_day_of_month) + last_day_of_todays_month = datetime( + today_date.year, today_date.month % 12 + 1, 1 + ) - timedelta(days=1) # Calculate the remaining days in the month - remaining_days = last_day_of_month - last_entry_date.day - # print("Remaining Days:", remaining_days) + remaining_days = (last_day_of_todays_month - last_entry_date).days + 1 series = {} for entry in data: @@ -279,6 +282,17 @@ def _forecast_daily_cost(data: list): spend = entry["spend"] series[date] = spend + if len(series) < 10: + num_items_to_fill = 11 - len(series) + + # avg spend for all days in series + avg_spend = sum(series.values()) / len(series) + for i in range(num_items_to_fill): + # go backwards from the first entry + date = first_entry_date - timedelta(days=i) + series[date.strftime("%Y-%m-%d")] = avg_spend + series[date.strftime("%Y-%m-%d")] = avg_spend + payload = {"series": series, "count": remaining_days} print("Prediction Data:", payload) @@ -291,6 +305,8 @@ def _forecast_daily_cost(data: list): json=payload, headers=headers, ) + # check the status code + response.raise_for_status() json_response = response.json() forecast_data = json_response["forecast"] @@ -314,15 +330,6 @@ def _forecast_daily_cost(data: list): # _forecast_daily_cost( # [ # {"date": "2022-01-01", "spend": 100}, -# {"date": "2022-01-02", "spend": 200}, -# {"date": "2022-01-03", "spend": 300}, -# {"date": "2022-01-04", "spend": 400}, -# {"date": "2022-01-05", "spend": 500}, -# {"date": "2022-01-06", "spend": 600}, -# {"date": "2022-01-07", "spend": 700}, -# {"date": "2022-01-08", "spend": 800}, -# {"date": "2022-01-09", "spend": 900}, -# {"date": "2022-01-10", "spend": 1000}, -# {"date": "2022-01-11", "spend": 50}, + # ] # ) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index f1656f770..e8745a071 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -786,6 +786,7 @@ async def user_api_key_auth( "/global/spend/logs", "/global/spend/keys", "/global/spend/models", + "/global/predict/spend/logs", ] # check if the current route startswith any of the allowed routes if ( @@ -4199,6 +4200,7 @@ async def global_predict_spend_logs(request: Request): from litellm.proxy.enterprise.utils import _forecast_daily_cost data = await request.json() + data = data.get("data") return _forecast_daily_cost(data) diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index a4c7dc976..c4bedb283 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -748,3 +748,42 @@ export const userUpdateUserCall = async ( throw error; } }; + + +export const PredictedSpendLogsCall = async (accessToken: string, requestData: any) => { + try { + let url = proxyBaseUrl + ? `${proxyBaseUrl}/global/predict/spend/logs` + : `/global/predict/spend/logs`; + + //message.info("Predicting spend logs request"); + + const response = await fetch(url, { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify( + { + data: requestData + } + ), + }); + + 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); + //message.success("Predicted Logs received"); + return data; + } catch (error) { + console.error("Failed to create key:", error); + throw error; + } +}; + diff --git a/ui/litellm-dashboard/src/components/view_key_spend_report.tsx b/ui/litellm-dashboard/src/components/view_key_spend_report.tsx index 9d4c95091..fb19fcdee 100644 --- a/ui/litellm-dashboard/src/components/view_key_spend_report.tsx +++ b/ui/litellm-dashboard/src/components/view_key_spend_report.tsx @@ -21,7 +21,7 @@ import { BarList, Metric, } from "@tremor/react"; -import { keySpendLogsCall } from "./networking"; +import { keySpendLogsCall, PredictedSpendLogsCall } from "./networking"; interface ViewKeySpendReportProps { token: string; @@ -48,6 +48,9 @@ const ViewKeySpendReport: React.FC = ({ const [data, setData] = useState<{ day: string; spend: number }[] | null>( null ); + const [predictedSpend, setPredictedSpend] = useState<{ day: string; spend: number }[] | null>( + null + ); const [userData, setUserData] = useState< { name: string; value: number }[] | null >(null); @@ -79,6 +82,18 @@ const ViewKeySpendReport: React.FC = ({ ); console.log("Response:", response); setData(response); + + // predict spend based on response + const predictedSpend = await PredictedSpendLogsCall(accessToken, response); + console.log("Response2:", predictedSpend); + + // append predictedSpend to data + const combinedData = [...response, ...predictedSpend]; + setData(combinedData); + + console.log("Combined Data:", combinedData); + // setPredictedSpend(predictedSpend); + } catch (error) { console.error("There was an error fetching the data", error); } @@ -110,9 +125,9 @@ const ViewKeySpendReport: React.FC = ({ )}