From ab68af4ff55721337ea8dac40860568230f2f2af Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Wed, 23 Apr 2025 22:02:02 -0700 Subject: [PATCH] Litellm multi admin fixes (#10259) * fix(create_user_button.tsx): do not set 'no-default-models' when user is a proxy admin * fix(user_info_view.tsx): show all user personal models * feat(user_info_view.tsx): allow giving users more personal models * feat(user_edit_view.tsx): allow proxy admin to edit user role, available models, etc. --- .../src/components/create_user_button.tsx | 5 +- .../src/components/user_edit_view.tsx | 168 +++++++++++++ .../src/components/view_users.tsx | 1 + .../src/components/view_users/table.tsx | 3 + .../components/view_users/user_info_view.tsx | 233 ++++++++++++------ 5 files changed, 336 insertions(+), 74 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/user_edit_view.tsx diff --git a/ui/litellm-dashboard/src/components/create_user_button.tsx b/ui/litellm-dashboard/src/components/create_user_button.tsx index 65a20823f1..eeed238d85 100644 --- a/ui/litellm-dashboard/src/components/create_user_button.tsx +++ b/ui/litellm-dashboard/src/components/create_user_button.tsx @@ -117,13 +117,14 @@ const Createuser: React.FC = ({ form.resetFields(); }; - const handleCreate = async (formValues: { user_id: string, models?: string[] }) => { + const handleCreate = async (formValues: { user_id: string, models?: string[], user_role: string }) => { try { message.info("Making API Call"); if (!isEmbedded) { setIsModalVisible(true); } - if (!formValues.models || formValues.models.length === 0) { + if ((!formValues.models || formValues.models.length === 0) && formValues.user_role !== "proxy_admin") { + console.log("formValues.user_role", formValues.user_role) // If models is empty or undefined, set it to "no-default-models" formValues.models = ["no-default-models"]; } diff --git a/ui/litellm-dashboard/src/components/user_edit_view.tsx b/ui/litellm-dashboard/src/components/user_edit_view.tsx new file mode 100644 index 0000000000..9c0fcbced9 --- /dev/null +++ b/ui/litellm-dashboard/src/components/user_edit_view.tsx @@ -0,0 +1,168 @@ +import React from "react"; +import { Form, InputNumber, Select, Tooltip } from "antd"; +import { TextInput, Textarea, SelectItem } from "@tremor/react"; +import { Button } from "@tremor/react"; +import { getModelDisplayName } from "./key_team_helpers/fetch_available_models_team_key"; +import { all_admin_roles } from "../utils/roles"; +import { InfoCircleOutlined } from "@ant-design/icons"; +interface UserEditViewProps { + userData: any; + onCancel: () => void; + onSubmit: (values: any) => void; + teams: any[] | null; + accessToken: string | null; + userID: string | null; + userRole: string | null; + userModels: string[]; + possibleUIRoles: Record> | null; +} + +export function UserEditView({ + userData, + onCancel, + onSubmit, + teams, + accessToken, + userID, + userRole, + userModels, + possibleUIRoles, +}: UserEditViewProps) { + const [form] = Form.useForm(); + + // Set initial form values + React.useEffect(() => { + form.setFieldsValue({ + user_id: userData.user_id, + user_email: userData.user_info?.user_email, + user_role: userData.user_info?.user_role, + models: userData.user_info?.models || [], + max_budget: userData.user_info?.max_budget, + metadata: userData.user_info?.metadata ? JSON.stringify(userData.user_info.metadata, null, 2) : undefined, + }); + }, [userData, form]); + + const handleSubmit = (values: any) => { + // Convert metadata back to an object if it exists and is a string + if (values.metadata && typeof values.metadata === "string") { + try { + values.metadata = JSON.parse(values.metadata); + } catch (error) { + console.error("Error parsing metadata JSON:", error); + return; + } + } + + onSubmit(values); + }; + + return ( +
+ + + + + + + + + + Global Proxy Role{' '} + + + + + } + name="user_role"> + + + + + Personal Models{' '} + + + + + } + name="models" + > + + + + + + + + +