diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 4877ad0a36..097f798de2 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -433,14 +433,13 @@ class LiteLLMProxyRequestSetup: ) -> Optional[List[str]]: tags = None - if llm_router and llm_router.enable_tag_filtering is True: - # Check request headers for tags - if "x-litellm-tags" in headers: - if isinstance(headers["x-litellm-tags"], str): - _tags = headers["x-litellm-tags"].split(",") - tags = [tag.strip() for tag in _tags] - elif isinstance(headers["x-litellm-tags"], list): - tags = headers["x-litellm-tags"] + # Check request headers for tags + if "x-litellm-tags" in headers: + if isinstance(headers["x-litellm-tags"], str): + _tags = headers["x-litellm-tags"].split(",") + tags = [tag.strip() for tag in _tags] + elif isinstance(headers["x-litellm-tags"], list): + tags = headers["x-litellm-tags"] # Check request body for tags if "tags" in data and isinstance(data["tags"], list): tags = data["tags"] diff --git a/litellm/proxy/management_endpoints/common_daily_activity.py b/litellm/proxy/management_endpoints/common_daily_activity.py index 9fb1168621..d5de85311b 100644 --- a/litellm/proxy/management_endpoints/common_daily_activity.py +++ b/litellm/proxy/management_endpoints/common_daily_activity.py @@ -75,7 +75,8 @@ def update_breakdown_metrics( metadata=KeyMetadata( key_alias=api_key_metadata.get(record.api_key, {}).get( "key_alias", None - ) + ), + team_id=api_key_metadata.get(record.api_key, {}).get("team_id", None), ), # Add any api_key-specific metadata here ) breakdown.api_keys[record.api_key].metrics = update_metrics( @@ -112,6 +113,7 @@ async def get_daily_activity( api_key: Optional[str], page: int, page_size: int, + exclude_entity_ids: Optional[List[str]] = None, ) -> SpendAnalyticsPaginatedResponse: """Common function to get daily activity for any entity type.""" if prisma_client is None: @@ -144,6 +146,10 @@ async def get_daily_activity( where_conditions[entity_id_field] = {"in": entity_id} else: where_conditions[entity_id_field] = entity_id + if exclude_entity_ids: + where_conditions.setdefault(entity_id_field, {})["not"] = { + "in": exclude_entity_ids + } # Get total count for pagination total_count = await getattr(prisma_client.db, table_name).count( @@ -175,7 +181,10 @@ async def get_daily_activity( where={"token": {"in": list(api_keys)}} ) api_key_metadata.update( - {k.token: {"key_alias": k.key_alias} for k in key_records} + { + k.token: {"key_alias": k.key_alias, "team_id": k.team_id} + for k in key_records + } ) # Process results diff --git a/litellm/proxy/management_endpoints/internal_user_endpoints.py b/litellm/proxy/management_endpoints/internal_user_endpoints.py index a91bc2dc62..e2f1077095 100644 --- a/litellm/proxy/management_endpoints/internal_user_endpoints.py +++ b/litellm/proxy/management_endpoints/internal_user_endpoints.py @@ -25,6 +25,8 @@ from litellm._logging import verbose_proxy_logger from litellm.litellm_core_utils.duration_parser import duration_in_seconds from litellm.proxy._types import * from litellm.proxy.auth.user_api_key_auth import user_api_key_auth +from litellm.proxy.management_endpoints.common_daily_activity import get_daily_activity +from litellm.proxy.management_endpoints.common_utils import _user_has_admin_view from litellm.proxy.management_endpoints.key_management_endpoints import ( generate_key_helper_fn, prepare_metadata_fields, @@ -1382,136 +1384,22 @@ async def get_user_daily_activity( ) try: - # Build filter conditions - where_conditions: Dict[str, Any] = { - "date": { - "gte": start_date, - "lte": end_date, - } - } + entity_id: Optional[str] = None + if not _user_has_admin_view(user_api_key_dict): + entity_id = user_api_key_dict.user_id - if model: - where_conditions["model"] = model - if api_key: - where_conditions["api_key"] = api_key - - if ( - user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN - and user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY - ): - where_conditions[ - "user_id" - ] = user_api_key_dict.user_id # only allow access to own data - - # Get total count for pagination - total_count = await prisma_client.db.litellm_dailyuserspend.count( - where=where_conditions - ) - - # Fetch paginated results - daily_spend_data = await prisma_client.db.litellm_dailyuserspend.find_many( - where=where_conditions, - order=[ - {"date": "desc"}, - ], - skip=(page - 1) * page_size, - take=page_size, - ) - - daily_spend_data_pydantic_list = [ - LiteLLM_DailyUserSpend(**record.model_dump()) for record in daily_spend_data - ] - - # Get all unique API keys from the spend data - api_keys = set() - for record in daily_spend_data_pydantic_list: - if record.api_key: - api_keys.add(record.api_key) - - # Fetch key aliases in bulk - - api_key_metadata: Dict[str, Dict[str, Any]] = {} - model_metadata: Dict[str, Dict[str, Any]] = {} - provider_metadata: Dict[str, Dict[str, Any]] = {} - if api_keys: - key_records = await prisma_client.db.litellm_verificationtoken.find_many( - where={"token": {"in": list(api_keys)}} - ) - api_key_metadata.update( - {k.token: {"key_alias": k.key_alias} for k in key_records} - ) - # Process results - results = [] - total_metrics = SpendMetrics() - - # Group data by date and other dimensions - - grouped_data: Dict[str, Dict[str, Any]] = {} - for record in daily_spend_data_pydantic_list: - date_str = record.date - if date_str not in grouped_data: - grouped_data[date_str] = { - "metrics": SpendMetrics(), - "breakdown": BreakdownMetrics(), - } - - # Update metrics - grouped_data[date_str]["metrics"] = update_metrics( - grouped_data[date_str]["metrics"], record - ) - # Update breakdowns - grouped_data[date_str]["breakdown"] = update_breakdown_metrics( - grouped_data[date_str]["breakdown"], - record, - model_metadata, - provider_metadata, - api_key_metadata, - ) - - # Update total metrics - total_metrics.spend += record.spend - total_metrics.prompt_tokens += record.prompt_tokens - total_metrics.completion_tokens += record.completion_tokens - total_metrics.total_tokens += ( - record.prompt_tokens + record.completion_tokens - ) - total_metrics.cache_read_input_tokens += record.cache_read_input_tokens - total_metrics.cache_creation_input_tokens += ( - record.cache_creation_input_tokens - ) - total_metrics.api_requests += record.api_requests - total_metrics.successful_requests += record.successful_requests - total_metrics.failed_requests += record.failed_requests - - # Convert grouped data to response format - for date_str, data in grouped_data.items(): - results.append( - DailySpendData( - date=datetime.strptime(date_str, "%Y-%m-%d").date(), - metrics=data["metrics"], - breakdown=data["breakdown"], - ) - ) - - # Sort results by date - results.sort(key=lambda x: x.date, reverse=True) - - return SpendAnalyticsPaginatedResponse( - results=results, - metadata=DailySpendMetadata( - total_spend=total_metrics.spend, - total_prompt_tokens=total_metrics.prompt_tokens, - total_completion_tokens=total_metrics.completion_tokens, - total_tokens=total_metrics.total_tokens, - total_api_requests=total_metrics.api_requests, - total_successful_requests=total_metrics.successful_requests, - total_failed_requests=total_metrics.failed_requests, - total_cache_read_input_tokens=total_metrics.cache_read_input_tokens, - total_cache_creation_input_tokens=total_metrics.cache_creation_input_tokens, - page=page, - total_pages=-(-total_count // page_size), # Ceiling division - has_more=(page * page_size) < total_count, - ), + return await get_daily_activity( + prisma_client=prisma_client, + table_name="litellm_dailyuserspend", + entity_id_field="user_id", + entity_id=entity_id, + entity_metadata_field=None, + start_date=start_date, + end_date=end_date, + model=model, + api_key=api_key, + page=page, + page_size=page_size, ) except Exception as e: diff --git a/litellm/proxy/management_endpoints/tag_management_endpoints.py b/litellm/proxy/management_endpoints/tag_management_endpoints.py index 23e2d3c9ee..7c731400fb 100644 --- a/litellm/proxy/management_endpoints/tag_management_endpoints.py +++ b/litellm/proxy/management_endpoints/tag_management_endpoints.py @@ -12,7 +12,7 @@ All /tag management endpoints import datetime import json -from typing import Dict, Optional +from typing import Dict, List, Optional from fastapi import APIRouter, Depends, HTTPException @@ -25,6 +25,7 @@ from litellm.proxy.management_endpoints.common_daily_activity import ( get_daily_activity, ) from litellm.types.tag_management import ( + LiteLLM_DailyTagSpendTable, TagConfig, TagDeleteRequest, TagInfoRequest, @@ -301,6 +302,7 @@ async def info_tag( "/tag/list", tags=["tag management"], dependencies=[Depends(user_api_key_auth)], + response_model=List[TagConfig], ) async def list_tags( user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), @@ -314,9 +316,33 @@ async def list_tags( raise HTTPException(status_code=500, detail="Database not connected") try: + ## QUERY STORED TAGS ## tags_config = await _get_tags_config(prisma_client) list_of_tags = list(tags_config.values()) - return list_of_tags + + ## QUERY DYNAMIC TAGS ## + dynamic_tags = await prisma_client.db.litellm_dailytagspend.find_many( + distinct=["tag"], + ) + + dynamic_tags_list = [ + LiteLLM_DailyTagSpendTable(**dynamic_tag.model_dump()) + for dynamic_tag in dynamic_tags + ] + + dynamic_tag_config = [ + TagConfig( + name=tag.tag, + description="This is just a spend tag that was passed dynamically in a request. It does not control any LLM models.", + models=None, + created_at=tag.created_at.isoformat(), + updated_at=tag.updated_at.isoformat(), + ) + for tag in dynamic_tags_list + if tag.tag not in tags_config + ] + + return list_of_tags + dynamic_tag_config except Exception as e: raise HTTPException(status_code=500, detail=str(e)) diff --git a/litellm/proxy/management_endpoints/team_endpoints.py b/litellm/proxy/management_endpoints/team_endpoints.py index 1874405fba..1f23a5401c 100644 --- a/litellm/proxy/management_endpoints/team_endpoints.py +++ b/litellm/proxy/management_endpoints/team_endpoints.py @@ -2102,6 +2102,7 @@ async def get_team_daily_activity( api_key: Optional[str] = None, page: int = 1, page_size: int = 10, + exclude_team_ids: Optional[str] = None, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), ): """ @@ -2115,7 +2116,7 @@ async def get_team_daily_activity( api_key (Optional[str]): Filter by API key. page (int): Page number for pagination. page_size (int): Number of items per page. - + exclude_team_ids (Optional[str]): Comma-separated list of team IDs to exclude. Returns: SpendAnalyticsPaginatedResponse: Paginated response containing daily activity data. """ @@ -2133,6 +2134,12 @@ async def get_team_daily_activity( # Convert comma-separated tags string to list if provided team_ids_list = team_ids.split(",") if team_ids else None + exclude_team_ids_list: Optional[List[str]] = None + + if exclude_team_ids: + exclude_team_ids_list = ( + exclude_team_ids.split(",") if exclude_team_ids else None + ) if not _user_has_admin_view(user_api_key_dict): user_info = await get_user_object( @@ -2182,6 +2189,7 @@ async def get_team_daily_activity( entity_id_field="team_id", entity_id=team_ids_list, entity_metadata_field=team_alias_metadata, + exclude_entity_ids=exclude_team_ids_list, start_date=start_date, end_date=end_date, model=model, diff --git a/litellm/types/proxy/management_endpoints/common_daily_activity.py b/litellm/types/proxy/management_endpoints/common_daily_activity.py index 9408035746..6213087f64 100644 --- a/litellm/types/proxy/management_endpoints/common_daily_activity.py +++ b/litellm/types/proxy/management_endpoints/common_daily_activity.py @@ -39,6 +39,7 @@ class KeyMetadata(BaseModel): """Metadata for a key""" key_alias: Optional[str] = None + team_id: Optional[str] = None class KeyMetricWithMetadata(MetricBase): diff --git a/litellm/types/tag_management.py b/litellm/types/tag_management.py index e530b37cab..a3615b658c 100644 --- a/litellm/types/tag_management.py +++ b/litellm/types/tag_management.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import Dict, List, Optional from pydantic import BaseModel @@ -30,3 +31,23 @@ class TagDeleteRequest(BaseModel): class TagInfoRequest(BaseModel): names: List[str] + + +class LiteLLM_DailyTagSpendTable(BaseModel): + id: str + tag: str + date: str + api_key: str + model: str + model_group: Optional[str] + custom_llm_provider: Optional[str] + prompt_tokens: int + completion_tokens: int + cache_read_input_tokens: int + cache_creation_input_tokens: int + spend: float + api_requests: int + successful_requests: int + failed_requests: int + created_at: datetime + updated_at: datetime diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 6790f9dc61..4e7269640c 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -370,6 +370,7 @@ export default function CreateKeyPage() { userID={userID} userRole={userRole} accessToken={accessToken} + teams={teams} /> ) : ( diff --git a/ui/litellm-dashboard/src/components/activity_metrics.tsx b/ui/litellm-dashboard/src/components/activity_metrics.tsx index df54561b16..32cba95d9e 100644 --- a/ui/litellm-dashboard/src/components/activity_metrics.tsx +++ b/ui/litellm-dashboard/src/components/activity_metrics.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Card, Grid, Text, Title, Accordion, AccordionHeader, AccordionBody } from '@tremor/react'; import { AreaChart, BarChart } from '@tremor/react'; -import { SpendMetrics, DailyData, ModelActivityData } from './usage/types'; +import { SpendMetrics, DailyData, ModelActivityData, MetricWithMetadata, KeyMetricWithMetadata } from './usage/types'; import { Collapse } from 'antd'; interface ActivityMetricsProps { @@ -224,7 +224,7 @@ export const ActivityMetrics: React.FC = ({ modelMetrics } key={modelName} header={
- {modelName || 'Unknown Model'} + {modelMetrics[modelName].label || 'Unknown Item'}
${modelMetrics[modelName].total_spend.toFixed(2)} {modelMetrics[modelName].total_requests.toLocaleString()} requests @@ -243,14 +243,24 @@ export const ActivityMetrics: React.FC = ({ modelMetrics } ); }; +// Helper function to format key label +const formatKeyLabel = (modelData: KeyMetricWithMetadata, model: string): string => { + const keyAlias = modelData.metadata.key_alias || `key-hash-${model}`; + const teamId = modelData.metadata.team_id; + return teamId ? `${keyAlias} (team_id: ${teamId})` : keyAlias; +}; + // Process data function -export const processActivityData = (dailyActivity: { results: DailyData[] }): Record => { +export const processActivityData = (dailyActivity: { results: DailyData[] }, key: "models" | "api_keys"): Record => { const modelMetrics: Record = {}; dailyActivity.results.forEach((day) => { - Object.entries(day.breakdown.models || {}).forEach(([model, modelData]) => { + Object.entries(day.breakdown[key] || {}).forEach(([model, modelData]) => { if (!modelMetrics[model]) { modelMetrics[model] = { + label: key === 'api_keys' + ? formatKeyLabel(modelData as KeyMetricWithMetadata, model) + : model, total_requests: 0, total_successful_requests: 0, total_failed_requests: 0, diff --git a/ui/litellm-dashboard/src/components/entity_usage.tsx b/ui/litellm-dashboard/src/components/entity_usage.tsx index 713e7b9807..a9a13b3847 100644 --- a/ui/litellm-dashboard/src/components/entity_usage.tsx +++ b/ui/litellm-dashboard/src/components/entity_usage.tsx @@ -49,12 +49,18 @@ interface EntitySpendData { }; } +export interface EntityList { + label: string; + value: string; +} + interface EntityUsageProps { accessToken: string | null; entityType: 'tag' | 'team'; entityId?: string | null; userID: string | null; userRole: string | null; + entityList: EntityList[] | null; } const EntityUsage: React.FC = ({ @@ -62,7 +68,8 @@ const EntityUsage: React.FC = ({ entityType, entityId, userID, - userRole + userRole, + entityList }) => { const [spendData, setSpendData] = useState({ results: [], @@ -75,8 +82,8 @@ const EntityUsage: React.FC = ({ } }); - const modelMetrics = processActivityData(spendData); - + const modelMetrics = processActivityData(spendData, "models"); + const keyMetrics = processActivityData(spendData, "api_keys"); const [selectedTags, setSelectedTags] = useState([]); const [dateValue, setDateValue] = useState({ from: new Date(Date.now() - 28 * 24 * 60 * 60 * 1000), @@ -225,16 +232,9 @@ const EntityUsage: React.FC = ({ }; const getAllTags = () => { - const tags = new Set(); - spendData.results.forEach(day => { - Object.keys(day.breakdown.entities || {}).forEach(tag => { - tags.add(tag); - }); - }); - return Array.from(tags).map(tag => ({ - label: tag, - value: tag - })); + if (entityList) { + return entityList; + } }; const filterDataByTags = (data: EntityMetricWithMetadata[]) => { @@ -292,9 +292,10 @@ const EntityUsage: React.FC = ({ onValueChange={setDateValue} /> - - Filter by {entityType === 'tag' ? 'Tags' : 'Teams'} - = ({ options={getAllTags()} className="mt-2" allowClear - /> - + /> + + )} Cost - Activity + Model Activity + Key Activity @@ -355,20 +358,45 @@ const EntityUsage: React.FC = ({ {/* Daily Spend Chart */} - - Daily Spend - - new Date(a.date).getTime() - new Date(b.date).getTime() - )} - index="date" - categories={["metrics.spend"]} - colors={["cyan"]} - valueFormatter={(value) => `$${value.toFixed(2)}`} - yAxisWidth={100} - showLegend={false} - /> - + + Daily Spend + + new Date(a.date).getTime() - new Date(b.date).getTime() + )} + index="date" + categories={["metrics.spend"]} + colors={["cyan"]} + valueFormatter={(value) => `$${value.toFixed(2)}`} + yAxisWidth={100} + showLegend={false} + customTooltip={({ payload, active }) => { + if (!active || !payload?.[0]) return null; + const data = payload[0].payload; + return ( +
+

{data.date}

+

Total Spend: ${data.metrics.spend.toFixed(2)}

+

Total Requests: {data.metrics.api_requests}

+

Successful: {data.metrics.successful_requests}

+

Failed: {data.metrics.failed_requests}

+

Total Tokens: {data.metrics.total_tokens}

+
+

Spend by {entityType === 'tag' ? 'Tag' : 'Team'}:

+ {Object.entries(data.breakdown.entities || {}).map(([entity, entityData]) => { + const metrics = entityData as EntityMetrics; + return ( +

+ {metrics.metadata.team_alias || entity}: ${metrics.metrics.spend.toFixed(2)} +

+ ); + })} +
+
+ ); + }} + /> +
{/* Entity Breakdown Section */} @@ -392,7 +420,7 @@ const EntityUsage: React.FC = ({ index="metadata.alias" categories={["metrics.spend"]} colors={["cyan"]} - valueFormatter={(value) => `$${value.toFixed(7)}`} + valueFormatter={(value) => `$${value.toFixed(4)}`} layout="vertical" showLegend={false} yAxisWidth={100} @@ -415,7 +443,7 @@ const EntityUsage: React.FC = ({ .map((entity) => ( {entity.metadata.alias} - ${entity.metrics.spend.toFixed(7)} + ${entity.metrics.spend.toFixed(4)} {entity.metrics.successful_requests.toLocaleString()} @@ -521,6 +549,9 @@ const EntityUsage: React.FC = ({ + + +
diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index b3cf7018ac..879660ee40 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -57,7 +57,7 @@ const Sidebar: React.FC = ({ { key: "1", page: "api-keys", label: "Virtual Keys", icon: }, { key: "3", page: "llm-playground", label: "Test Key", icon: , roles: rolesWithWriteAccess }, { key: "2", page: "models", label: "Models", icon: , roles: rolesWithWriteAccess }, - { key: "4", page: "usage", label: "Usage", icon: }, + { key: "12", page: "new_usage", label: "Usage", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, { key: "6", page: "teams", label: "Teams", icon: }, { key: "17", page: "organizations", label: "Organizations", icon: , roles: all_admin_roles }, { key: "5", page: "users", label: "Internal Users", icon: , roles: all_admin_roles }, @@ -73,7 +73,7 @@ const Sidebar: React.FC = ({ { key: "9", page: "caching", label: "Caching", icon: , roles: all_admin_roles }, { key: "10", page: "budgets", label: "Budgets", icon: , roles: all_admin_roles }, { key: "11", page: "guardrails", label: "Guardrails", icon: , roles: all_admin_roles }, - { key: "12", page: "new_usage", label: "New Usage", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, + { key: "4", page: "usage", label: "Old Usage", icon: }, { key: "20", page: "transform-request", label: "API Playground", icon: , roles: [...all_admin_roles, ...internalUserRoles] }, { key: "18", page: "mcp-tools", label: "MCP Tools", icon: , roles: all_admin_roles }, { key: "19", page: "tag-management", label: "Tag Management", icon: , roles: all_admin_roles }, diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index f14bf0b774..f16aaf30fa 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -1200,6 +1200,7 @@ export const teamDailyActivityCall = async (accessToken: String, startTime: Date if (teamIds) { queryParams.append('team_ids', teamIds.join(',')); } + queryParams.append('exclude_team_ids', 'litellm-dashboard'); const queryString = queryParams.toString(); if (queryString) { url += `?${queryString}`; diff --git a/ui/litellm-dashboard/src/components/new_usage.tsx b/ui/litellm-dashboard/src/components/new_usage.tsx index 5fca09c3c8..b6378cd9cc 100644 --- a/ui/litellm-dashboard/src/components/new_usage.tsx +++ b/ui/litellm-dashboard/src/components/new_usage.tsx @@ -17,24 +17,29 @@ import { } from "@tremor/react"; import { AreaChart } from "@tremor/react"; -import { userDailyActivityCall } from "./networking"; +import { userDailyActivityCall, tagListCall } from "./networking"; +import { Tag } from "./tag_management/types"; import ViewUserSpend from "./view_user_spend"; import TopKeyView from "./top_key_view"; import { ActivityMetrics, processActivityData } from './activity_metrics'; import { SpendMetrics, DailyData, ModelActivityData, MetricWithMetadata, KeyMetricWithMetadata } from './usage/types'; import EntityUsage from './entity_usage'; import { old_admin_roles, v2_admin_role_names, all_admin_roles, rolesAllowedToSeeUsage, rolesWithWriteAccess, internalUserRoles } from '../utils/roles'; +import { Team } from "./key_team_helpers/key_list"; +import { EntityList } from "./entity_usage"; interface NewUsagePageProps { accessToken: string | null; userRole: string | null; userID: string | null; + teams: Team[]; } const NewUsagePage: React.FC = ({ accessToken, userRole, userID, + teams }) => { const [userSpendData, setUserSpendData] = useState<{ results: DailyData[]; @@ -47,6 +52,23 @@ const NewUsagePage: React.FC = ({ to: new Date(), }); + const [allTags, setAllTags] = useState([]); + + const getAllTags = async () => { + if (!accessToken) { + return; + } + const tags = await tagListCall(accessToken); + setAllTags(Object.values(tags).map((tag: Tag) => ({ + label: tag.name, + value: tag.name + }))); + }; + + useEffect(() => { + getAllTags(); + }, [accessToken]); + // Derived states from userSpendData const totalSpend = userSpendData.metadata?.total_spend || 0; @@ -228,16 +250,19 @@ const NewUsagePage: React.FC = ({ fetchUserSpendData(); }, [accessToken, dateValue]); - const modelMetrics = processActivityData(userSpendData); + const modelMetrics = processActivityData(userSpendData, "models"); + const keyMetrics = processActivityData(userSpendData, "api_keys"); return (
- Usage Analytics Dashboard + + This is the new usage dashboard.
You may see empty data, as these use new aggregate tables to allow UI to work at 1M+ spend logs. To access the old dashboard, go to Experimental {'>'} Old Usage. +
- Your Usage + {all_admin_roles.includes(userRole || "") ? Global Usage : Your Usage} Team Usage - {all_admin_roles.includes(userRole || "") && Tag Usage} + {all_admin_roles.includes(userRole || "") ? Tag Usage : <>} {/* Your Usage Panel */} @@ -257,7 +282,8 @@ const NewUsagePage: React.FC = ({ Cost - Activity + Model Activity + Key Activity {/* Cost Panel */} @@ -460,6 +486,9 @@ const NewUsagePage: React.FC = ({ + + + @@ -471,6 +500,10 @@ const NewUsagePage: React.FC = ({ entityType="team" userID={userID} userRole={userRole} + entityList={teams?.map(team => ({ + label: team.team_alias, + value: team.team_id + })) || null} /> @@ -481,6 +514,7 @@ const NewUsagePage: React.FC = ({ entityType="tag" userID={userID} userRole={userRole} + entityList={allTags} /> diff --git a/ui/litellm-dashboard/src/components/usage/types.ts b/ui/litellm-dashboard/src/components/usage/types.ts index 8c2a493391..46d96b4aa1 100644 --- a/ui/litellm-dashboard/src/components/usage/types.ts +++ b/ui/litellm-dashboard/src/components/usage/types.ts @@ -31,10 +31,12 @@ export interface KeyMetricWithMetadata { metrics: SpendMetrics; metadata: { key_alias: string | null; + team_id?: string | null; }; } export interface ModelActivityData { + label: string; total_requests: number; total_successful_requests: number; total_failed_requests: number; @@ -62,11 +64,7 @@ export interface ModelActivityData { export interface KeyMetadata { key_alias: string | null; -} - -export interface KeyMetricWithMetadata { - metrics: SpendMetrics; - metadata: KeyMetadata; + team_id: string | null; } export interface EntityMetadata {