Litellm user daily activity allow non admin usage (#9695)

* feat(internal_user_endpoints.py): allow non-admin to view their own usage via `/user/daily/activity` route

* fix(leftnav.tsx): allow users to view their own usage via new_usage.tsx

allows internal users to see their usage via new api

Handles 1m+ spend logs scenario

* fix(leftnav.tsx): allow all users to see new usage tab
This commit is contained in:
Krish Dholakia 2025-04-01 22:27:26 -07:00 committed by GitHub
parent 4f9805c9aa
commit 5a3eab0247
3 changed files with 68 additions and 46 deletions

View file

@ -432,6 +432,7 @@ class LiteLLMRoutes(enum.Enum):
"/model/new", "/model/new",
"/model/update", "/model/update",
"/model/delete", "/model/delete",
"/user/daily/activity",
] # routes that manage their own allowed/disallowed logic ] # routes that manage their own allowed/disallowed logic
## Org Admin Routes ## ## Org Admin Routes ##

View file

@ -1480,6 +1480,14 @@ async def get_user_daily_activity(
if api_key: if api_key:
where_conditions["api_key"] = 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 # Get total count for pagination
total_count = await prisma_client.db.litellm_dailyuserspend.count( total_count = await prisma_client.db.litellm_dailyuserspend.count(
where=where_conditions where=where_conditions

View file

@ -23,7 +23,7 @@ import {
LockOutlined, LockOutlined,
ToolOutlined, ToolOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { old_admin_roles, v2_admin_role_names, all_admin_roles, rolesAllowedToSeeUsage, rolesWithWriteAccess } from '../utils/roles'; import { old_admin_roles, v2_admin_role_names, all_admin_roles, rolesAllowedToSeeUsage, rolesWithWriteAccess, internalUserRoles } from '../utils/roles';
const { Sider } = Layout; const { Sider } = Layout;
@ -44,8 +44,15 @@ interface MenuItem {
icon?: React.ReactNode; icon?: React.ReactNode;
} }
// Note: If a menu item does not have a role, it is visible to all roles.
const menuItems: MenuItem[] = [
const Sidebar: React.FC<SidebarProps> = ({
setPage,
userRole,
defaultSelectedKey,
}) => {
// Note: If a menu item does not have a role, it is visible to all roles.
const menuItems: MenuItem[] = [
{ key: "1", page: "api-keys", label: "Virtual Keys", icon: <KeyOutlined /> }, { key: "1", page: "api-keys", label: "Virtual Keys", icon: <KeyOutlined /> },
{ key: "3", page: "llm-playground", label: "Test Key", icon: <PlayCircleOutlined />, roles: rolesWithWriteAccess }, { key: "3", page: "llm-playground", label: "Test Key", icon: <PlayCircleOutlined />, roles: rolesWithWriteAccess },
{ key: "2", page: "models", label: "Models", icon: <BlockOutlined />, roles: rolesWithWriteAccess }, { key: "2", page: "models", label: "Models", icon: <BlockOutlined />, roles: rolesWithWriteAccess },
@ -64,12 +71,11 @@ const menuItems: MenuItem[] = [
page: "experimental", page: "experimental",
label: "Experimental", label: "Experimental",
icon: <ExperimentOutlined />, icon: <ExperimentOutlined />,
roles: all_admin_roles,
children: [ children: [
{ key: "9", page: "caching", label: "Caching", icon: <DatabaseOutlined />, roles: all_admin_roles }, { key: "9", page: "caching", label: "Caching", icon: <DatabaseOutlined />, roles: all_admin_roles },
{ key: "10", page: "budgets", label: "Budgets", icon: <BankOutlined />, roles: all_admin_roles }, { key: "10", page: "budgets", label: "Budgets", icon: <BankOutlined />, roles: all_admin_roles },
{ key: "11", page: "guardrails", label: "Guardrails", icon: <SafetyOutlined />, roles: all_admin_roles }, { key: "11", page: "guardrails", label: "Guardrails", icon: <SafetyOutlined />, roles: all_admin_roles },
{ key: "12", page: "new_usage", label: "New Usage", icon: <BarChartOutlined />, roles: all_admin_roles }, { key: "12", page: "new_usage", label: "New Usage", icon: <BarChartOutlined />, roles: [...all_admin_roles, ...internalUserRoles] },
{ key: "18", page: "mcp-tools", label: "MCP Tools", icon: <ToolOutlined />, roles: all_admin_roles }, { key: "18", page: "mcp-tools", label: "MCP Tools", icon: <ToolOutlined />, roles: all_admin_roles },
] ]
}, },
@ -86,13 +92,7 @@ const menuItems: MenuItem[] = [
{ key: "13", page: "admin-panel", label: "Admin Settings", icon: <SettingOutlined />, roles: all_admin_roles }, { key: "13", page: "admin-panel", label: "Admin Settings", icon: <SettingOutlined />, roles: all_admin_roles },
] ]
} }
]; ];
const Sidebar: React.FC<SidebarProps> = ({
setPage,
userRole,
defaultSelectedKey,
}) => {
// Find the menu item that matches the default page, including in submenus // Find the menu item that matches the default page, including in submenus
const findMenuItemKey = (page: string): string => { const findMenuItemKey = (page: string): string => {
// Check top-level items // Check top-level items
@ -111,9 +111,22 @@ const Sidebar: React.FC<SidebarProps> = ({
const selectedMenuKey = findMenuItemKey(defaultSelectedKey); const selectedMenuKey = findMenuItemKey(defaultSelectedKey);
const filteredMenuItems = menuItems.filter(item => const filteredMenuItems = menuItems.filter(item => {
!item.roles || item.roles.includes(userRole) // Check if parent item has roles and user has access
const hasParentAccess = !item.roles || item.roles.includes(userRole);
if (!hasParentAccess) return false;
// Filter children if they exist
if (item.children) {
item.children = item.children.filter(child =>
!child.roles || child.roles.includes(userRole)
); );
}
return true;
});
return ( return (
<Layout style={{ minHeight: "100vh" }}> <Layout style={{ minHeight: "100vh" }}>