diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 2f719a87b15..647599a88d5 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -687,6 +687,8 @@ async def user_api_key_auth( elif route == "/model/info": # /model/info just shows models user has access to pass + elif route == "/user/request_model": + pass # this allows any user to request a model through the UI elif allow_user_auth == True and route == "/key/generate": pass elif allow_user_auth == True and route == "/key/delete": diff --git a/ui/litellm-dashboard/src/components/model_dashboard.tsx b/ui/litellm-dashboard/src/components/model_dashboard.tsx index 1cc2d98a83a..dbd5c7c94d0 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard.tsx @@ -1,7 +1,8 @@ import React, { useState, useEffect } from "react"; import { Card, Title, Subtitle, Table, TableHead, TableRow, TableCell, TableBody, Metric, Grid } from "@tremor/react"; import { modelInfoCall } from "./networking"; -import { Badge, BadgeDelta } from '@tremor/react'; +import { Badge, BadgeDelta, Button } from '@tremor/react'; +import RequestAccess from "./request_model_access"; interface ModelDashboardProps { accessToken: string | null; @@ -41,6 +42,7 @@ const ModelDashboard: React.FC = ({ if (!modelData) { return
Loading...
; } + let all_models_on_proxy: any[] = []; // loop through model data and edit each row for (let i = 0; i < modelData.data.length; i++) { @@ -82,9 +84,12 @@ const ModelDashboard: React.FC = ({ modelData.data[i].output_cost = output_cost modelData.data[i].max_tokens = max_tokens + all_models_on_proxy.push(curr_model.model_name); + console.log(modelData.data[i]); } + // when users click request access show pop up to allow them to request access return (
@@ -109,7 +114,7 @@ const ModelDashboard: React.FC = ({ {model.provider} - {model.user_access ? Yes : Request Access} + {model.user_access ? Yes : } {model.input_cost} diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 3a223504112..ccdc279d233 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -292,3 +292,38 @@ export const spendUsersCall = async (accessToken: String, userID: String) => { throw error; } }; + + + + +export const userRequestModelCall = async (accessToken: String, model: String, UserID: String, justification: String) => { + try { + const url = proxyBaseUrl ? `${proxyBaseUrl}/user/request_model` : `user/request_model`; + const response = await fetch(url, { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + models: [model], + user_id: UserID, + justification: justification, + }), + }); + + if (!response.ok) { + const errorData = await response.text(); + message.error("Failed to delete key: " + errorData); + throw new Error("Network response was not ok"); + } + const data = await response.json(); + console.log(data); + message.success(""); + return data; + // Handle success - you might want to update some state or UI based on the created key + } catch (error) { + console.error("Failed to create key:", error); + throw error; + } +}; \ No newline at end of file diff --git a/ui/litellm-dashboard/src/components/request_model_access.tsx b/ui/litellm-dashboard/src/components/request_model_access.tsx new file mode 100644 index 00000000000..c884d03b2c6 --- /dev/null +++ b/ui/litellm-dashboard/src/components/request_model_access.tsx @@ -0,0 +1,96 @@ +"use client"; + +import React, { useState, useEffect, useRef } from "react"; +import { Modal, Form, Input, Select, InputNumber, message } from "antd"; +import { Button } from "@tremor/react"; +import { userRequestModelCall } from "./networking"; + +const { Option } = Select; + +interface RequestAccessProps { + userModels: string[]; + accessToken: string; + userID: string; +} + +function onRequestAccess(formData: Record): void { + // This function does nothing for now + } + +const RequestAccess: React.FC = ({ userModels, accessToken, userID }) => { + const [form] = Form.useForm(); + const [isModalVisible, setIsModalVisible] = useState(false); + + const handleOk = () => { + setIsModalVisible(false); + form.resetFields(); + }; + + const handleCancel = () => { + setIsModalVisible(false); + form.resetFields(); + }; + + const handleRequestAccess = async (formValues: Record) => { + try { + message.info("Requesting access"); + // Extract form values + const { selectedModel, accessReason } = formValues; + + // Call userRequestModelCall + const response = await userRequestModelCall( + accessToken, // You need to have accessToken available + selectedModel, + userID, // You need to have UserID available + accessReason + ); + + onRequestAccess(formValues); + setIsModalVisible(true); + } catch (error) { + console.error("Error requesting access:", error); + } + }; + + return ( +
+ + +
+ + + + + + +
+ +
+
+
+
+ ); +}; + +export default RequestAccess; diff --git a/ui/litellm-dashboard/src/components/user_dashboard.tsx b/ui/litellm-dashboard/src/components/user_dashboard.tsx index f3f24e4449c..89dc377b9c1 100644 --- a/ui/litellm-dashboard/src/components/user_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/user_dashboard.tsx @@ -129,7 +129,7 @@ const UserDashboard: React.FC = ({ const model_info = await modelInfoCall(accessToken, userID, userRole); console.log("model_info:", model_info); // loop through model_info["data"] and create an array of element.model_name - let available_model_names = model_info["data"].map((element: { model_name: string; }) => element.model_name); + let available_model_names = model_info["data"].filter((element: { model_name: string; user_access: boolean }) => element.user_access === true).map((element: { model_name: string; }) => element.model_name); console.log("available_model_names:", available_model_names); setUserModels(available_model_names);