diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 266c893521..c154bd8e97 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -8917,6 +8917,38 @@ async def global_spend_per_team(): } +@router.get( + "/global/all_end_users", + tags=["Budget & Spend Tracking"], + dependencies=[Depends(user_api_key_auth)], + include_in_schema=False, +) +async def global_view_all_end_users(): + """ + [BETA] This is a beta endpoint. It will change. + + Use this to just get all the unique `end_users` + """ + global prisma_client + + if prisma_client is None: + raise HTTPException(status_code=500, detail={"error": "No db connected"}) + + sql_query = """ + SELECT DISTINCT end_user FROM "LiteLLM_SpendLogs" + """ + + db_response = await prisma_client.db.query_raw(query=sql_query) + if db_response is None: + return [] + + _end_users = [] + for row in db_response: + _end_users.append(row["end_user"]) + + return {"end_users": _end_users} + + @router.post( "/global/spend/end_users", tags=["Budget & Spend Tracking"], @@ -11616,6 +11648,7 @@ async def model_metrics( startTime: Optional[datetime] = None, endTime: Optional[datetime] = None, api_key: Optional[str] = None, + customer: Optional[str] = None, ): global prisma_client, llm_router if prisma_client is None: @@ -11631,6 +11664,9 @@ async def model_metrics( if api_key is None or api_key == "undefined": api_key = "null" + if customer is None or customer == "undefined": + customer = "null" + sql_query = """ SELECT api_base, @@ -11641,7 +11677,7 @@ async def model_metrics( FROM "LiteLLM_SpendLogs" WHERE - "startTime" BETWEEN $2::timestamp AND $3::timestamp + "startTime" >= $2::timestamp AND "startTime" <= $3::timestamp AND "model_group" = $1 AND "cache_hit" != 'True' AND ( CASE @@ -11649,6 +11685,12 @@ async def model_metrics( ELSE TRUE END ) + AND ( + CASE + WHEN $5 != 'null' THEN "end_user" = $5 + ELSE TRUE + END + ) GROUP BY api_base, model_group, @@ -11661,7 +11703,7 @@ async def model_metrics( """ _all_api_bases = set() db_response = await prisma_client.db.query_raw( - sql_query, _selected_model_group, startTime, endTime, api_key + sql_query, _selected_model_group, startTime, endTime, api_key, customer ) _daily_entries: dict = {} # {"Jun 23": {"model1": 0.002, "model2": 0.003}} @@ -11721,6 +11763,7 @@ async def model_metrics_slow_responses( startTime: Optional[datetime] = None, endTime: Optional[datetime] = None, api_key: Optional[str] = None, + customer: Optional[str] = None, ): global prisma_client, llm_router, proxy_logging_obj if prisma_client is None: @@ -11733,6 +11776,9 @@ async def model_metrics_slow_responses( if api_key is None or api_key == "undefined": api_key = "null" + if customer is None or customer == "undefined": + customer = "null" + startTime = startTime or datetime.now() - timedelta(days=30) endTime = endTime or datetime.now() @@ -11762,6 +11808,12 @@ WHERE ELSE TRUE END ) + AND ( + CASE + WHEN $6 != 'null' THEN "end_user" = $6 + ELSE TRUE + END + ) GROUP BY api_base ORDER BY @@ -11775,6 +11827,7 @@ ORDER BY startTime, endTime, api_key, + customer, ) if db_response is not None: @@ -11799,6 +11852,7 @@ async def model_metrics_exceptions( startTime: Optional[datetime] = None, endTime: Optional[datetime] = None, api_key: Optional[str] = None, + customer: Optional[str] = None, ): global prisma_client, llm_router if prisma_client is None: diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index ea23f660a5..adb45346b7 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -51,6 +51,7 @@ import { modelSettingsCall, adminGlobalActivityExceptions, adminGlobalActivityExceptionsPerDeployment, + allEndUsersCall, } from "./networking"; import { BarChart, AreaChart } from "@tremor/react"; import { @@ -319,6 +320,9 @@ const ModelDashboard: React.FC = ({ const [showAdvancedFilters, setShowAdvancedFilters] = useState(false); const [selectedAPIKey, setSelectedAPIKey] = useState(null); + const [selectedCustomer, setSelectedCustomer] = useState(null); + + const [allEndUsers, setAllEndUsers] = useState([]); useEffect(() => { updateModelMetrics( @@ -326,7 +330,7 @@ const ModelDashboard: React.FC = ({ dateValue.from, dateValue.to ); - }, [selectedAPIKey]); + }, [selectedAPIKey, selectedCustomer]); function formatCreatedAt(createdAt: string | null) { if (createdAt) { @@ -628,7 +632,8 @@ const ModelDashboard: React.FC = ({ _initial_model_group, dateValue.from?.toISOString(), dateValue.to?.toISOString(), - selectedAPIKey?.token + selectedAPIKey?.token, + selectedCustomer ); console.log("Model metrics response:", modelMetricsResponse); @@ -657,7 +662,8 @@ const ModelDashboard: React.FC = ({ _initial_model_group, dateValue.from?.toISOString(), dateValue.to?.toISOString(), - selectedAPIKey?.token + selectedAPIKey?.token, + selectedCustomer ); console.log("Model exceptions response:", modelExceptionsResponse); setModelExceptions(modelExceptionsResponse.data); @@ -670,7 +676,8 @@ const ModelDashboard: React.FC = ({ _initial_model_group, dateValue.from?.toISOString(), dateValue.to?.toISOString(), - selectedAPIKey?.token + selectedAPIKey?.token, + selectedCustomer ); const dailyExceptions = await adminGlobalActivityExceptions( @@ -700,6 +707,10 @@ const ModelDashboard: React.FC = ({ setSlowResponsesData(slowResponses); + let all_end_users_data = await allEndUsersCall(accessToken); + + setAllEndUsers(all_end_users_data?.end_users); + const routerSettingsInfo = await getCallbacksCall( accessToken, userID, @@ -911,6 +922,21 @@ const ModelDashboard: React.FC = ({ selected_token = null; } + let selected_customer = selectedCustomer; + if (selected_customer === undefined) { + selected_customer = null; + } + + // make startTime and endTime to last hour of the day + startTime.setHours(0); + startTime.setMinutes(0); + startTime.setSeconds(0); + + endTime.setHours(23); + endTime.setMinutes(59); + endTime.setSeconds(59); + + try { const modelMetricsResponse = await modelMetricsCall( accessToken, @@ -919,7 +945,8 @@ const ModelDashboard: React.FC = ({ modelGroup, startTime.toISOString(), endTime.toISOString(), - selected_token + selected_token, + selected_customer ); console.log("Model metrics response:", modelMetricsResponse); @@ -947,7 +974,8 @@ const ModelDashboard: React.FC = ({ modelGroup, startTime.toISOString(), endTime.toISOString(), - selected_token + selected_token, + selected_customer ); console.log("Model exceptions response:", modelExceptionsResponse); setModelExceptions(modelExceptionsResponse.data); @@ -960,7 +988,8 @@ const ModelDashboard: React.FC = ({ modelGroup, startTime.toISOString(), endTime.toISOString(), - selected_token + selected_token, + selected_customer ); console.log("slowResponses:", slowResponses); @@ -999,9 +1028,11 @@ const ModelDashboard: React.FC = ({ const FilterByContent = (
Select API Key Name - - = ({ return null; // Add this line to handle the case when the condition is not met })} + + + + Select Customer Name + + + +
+ ): ( +
+ + + + + + Select Customer Name + + + + +
+ ) + } + + + @@ -1793,12 +1935,13 @@ const ModelDashboard: React.FC = ({ - + Select Time Range { setDateValue(value); updateModelMetrics( @@ -1809,7 +1952,7 @@ const ModelDashboard: React.FC = ({ }} /> - + Select Model Group