mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 11:43:54 +00:00
(ui) create user
This commit is contained in:
parent
c656eaf7a4
commit
02f7b902db
4 changed files with 221 additions and 11 deletions
|
@ -79,14 +79,12 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
|||
name="key_alias"
|
||||
>
|
||||
<Input />
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional</p>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Team ID"
|
||||
name="team_id"
|
||||
>
|
||||
<Input placeholder="ai_team" />
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional</p>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Models"
|
||||
|
@ -103,44 +101,36 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
|||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional, defaults to all models</p>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
<Form.Item
|
||||
label="Max Budget (USD)"
|
||||
name="max_budget"
|
||||
>
|
||||
<InputNumber step={0.01} precision={2} width={200}/>
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional, defaults Unlimited Budget </p>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Tokens per minute Limit (TPM)"
|
||||
name="tpm_limit"
|
||||
>
|
||||
<InputNumber step={1} width={400}/>
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional, defaults to Unlimited TPM</p>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Requests per minute Limit (RPM)"
|
||||
name="rpm_limit"
|
||||
>
|
||||
<InputNumber step={1} width={400}/>
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional, defaults to Unlimited RPM</p>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Duration (eg: 30s, 30h, 30d)"
|
||||
name="duration"
|
||||
>
|
||||
<Input />
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional, defaults to never expiring key</p>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Metadata"
|
||||
name="metadata"
|
||||
>
|
||||
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
|
||||
<p style={{ fontStyle: 'italic', color: 'gray' }}>Optional, defaults to null</p>
|
||||
</Form.Item>
|
||||
</>
|
||||
) : (
|
||||
|
|
148
ui/litellm-dashboard/src/components/create_user_button.tsx
Normal file
148
ui/litellm-dashboard/src/components/create_user_button.tsx
Normal file
|
@ -0,0 +1,148 @@
|
|||
import React, { useState } from "react";
|
||||
import { Button, Modal, Form, Input, message, Select, InputNumber } from "antd";
|
||||
import { Button as Button2 } from "@tremor/react";
|
||||
import { userCreateCall } from "./networking";
|
||||
|
||||
interface CreateuserProps {
|
||||
userID: string;
|
||||
accessToken: string;
|
||||
}
|
||||
|
||||
const Createuser: React.FC<CreateuserProps> = ({ userID, accessToken }) => {
|
||||
const [form] = Form.useForm();
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [apiuser, setApiuser] = useState<string | null>(null);
|
||||
|
||||
const handleOk = () => {
|
||||
setIsModalVisible(false);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setIsModalVisible(false);
|
||||
setApiuser(null);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
const handleCreate = async (formValues: { user_id: string }) => {
|
||||
try {
|
||||
message.info("Making API Call");
|
||||
setIsModalVisible(true);
|
||||
console.log("formValues in create user:", formValues);
|
||||
const response = await userCreateCall(accessToken, userID, formValues);
|
||||
console.log("user create Response:", response);
|
||||
setApiuser(response["key"]);
|
||||
message.success("API user Created");
|
||||
form.resetFields();
|
||||
localStorage.removeItem("userData" + userID);
|
||||
} catch (error) {
|
||||
console.error("Error creating the user:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button2 className="mx-auto" onClick={() => setIsModalVisible(true)}>
|
||||
+ Create New User
|
||||
</Button2>
|
||||
<Modal
|
||||
title="Create User"
|
||||
visible={isModalVisible}
|
||||
width={800}
|
||||
footer={null}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Form form={form} onFinish={handleCreate} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} labelAlign="left">
|
||||
<Form.Item
|
||||
label="User ID"
|
||||
name="user_id"
|
||||
>
|
||||
<Input placeholder="Enter User ID" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Team ID"
|
||||
name="team_id"
|
||||
>
|
||||
<Input placeholder="ai_team" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Models"
|
||||
name="models"
|
||||
>
|
||||
{/* <Select
|
||||
mode="multiple"
|
||||
placeholder="Select models"
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{userModels.map((model) => (
|
||||
<Option key={model} value={model}>
|
||||
{model}
|
||||
</Option>
|
||||
))}
|
||||
</Select> */}
|
||||
</Form.Item>
|
||||
|
||||
|
||||
<Form.Item
|
||||
label="Max Budget (USD)"
|
||||
name="max_budget"
|
||||
>
|
||||
<InputNumber step={0.01} precision={2} width={200}/>
|
||||
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Tokens per minute Limit (TPM)"
|
||||
name="tpm_limit"
|
||||
>
|
||||
<InputNumber step={1} width={400}/>
|
||||
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Requests per minute Limit (RPM)"
|
||||
name="rpm_limit"
|
||||
>
|
||||
<InputNumber step={1} width={400}/>
|
||||
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Duration (eg: 30s, 30h, 30d)"
|
||||
name="duration"
|
||||
>
|
||||
<Input />
|
||||
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Metadata"
|
||||
name="metadata"
|
||||
>
|
||||
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
|
||||
|
||||
</Form.Item>
|
||||
<Button htmlType="submit">
|
||||
Create User
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal>
|
||||
{apiuser && (
|
||||
<Modal
|
||||
title="Save Your User"
|
||||
visible={isModalVisible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
footer={null}
|
||||
>
|
||||
<p>
|
||||
Please save this secret user somewhere safe and accessible. For
|
||||
security reasons, <b>you will not be able to view it again</b> through
|
||||
your LiteLLM account. If you lose this secret user, you will need to
|
||||
generate a new one.
|
||||
</p>
|
||||
<p>{apiuser != null ? `API user: ${apiuser}` : "User being created, this might take 30s"}</p>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Createuser;
|
|
@ -69,6 +69,71 @@ export const keyCreateCall = async (
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
export const userCreateCall = async (
|
||||
accessToken: string,
|
||||
userID: string,
|
||||
formValues: Record<string, any> // Assuming formValues is an object
|
||||
) => {
|
||||
try {
|
||||
console.log("Form Values in keyCreateCall:", formValues); // Log the form values before making the API call
|
||||
|
||||
// check if formValues.description is not undefined, make it a string and add it to formValues.metadata
|
||||
if (formValues.description) {
|
||||
// add to formValues.metadata
|
||||
if (!formValues.metadata) {
|
||||
formValues.metadata = {};
|
||||
}
|
||||
// value needs to be in "", valid JSON
|
||||
formValues.metadata.description = formValues.description;
|
||||
// remove descrption from formValues
|
||||
delete formValues.description;
|
||||
formValues.metadata = JSON.stringify(formValues.metadata);
|
||||
}
|
||||
// if formValues.metadata is not undefined, make it a valid dict
|
||||
if (formValues.metadata) {
|
||||
console.log("formValues.metadata:", formValues.metadata);
|
||||
// if there's an exception JSON.parse, show it in the message
|
||||
try {
|
||||
formValues.metadata = JSON.parse(formValues.metadata);
|
||||
} catch (error) {
|
||||
message.error("Failed to parse metadata: " + error);
|
||||
throw new Error("Failed to parse metadata: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Form Values after check:", formValues);
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/user/new` : `/user/new`;
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: userID,
|
||||
...formValues, // Include formValues in the request body
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
message.error("Failed to create key: " + errorData);
|
||||
console.error("Error response from the server:", errorData);
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("API Response:", data);
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const keyDeleteCall = async (accessToken: String, user_key: String) => {
|
||||
try {
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/key/delete` : `/key/delete`;
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Card, Title, Subtitle, Table, TableHead, TableRow, TableCell, TableBody
|
|||
import { userInfoCall } from "./networking";
|
||||
import { Badge, BadgeDelta, Button } from '@tremor/react';
|
||||
import RequestAccess from "./request_model_access";
|
||||
import CreateUser from "./create_user_button";
|
||||
|
||||
interface ViewUserDashboardProps {
|
||||
accessToken: string | null;
|
||||
|
@ -55,6 +56,12 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
|||
return (
|
||||
<div style={{ width: "100%" }}>
|
||||
<Grid className="gap-2 p-10 h-[75vh] w-full">
|
||||
<CreateUser
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
userModels={["litellm-proxy-budget"]}
|
||||
accessToken={accessToken}
|
||||
/>
|
||||
<Card>
|
||||
<Table className="mt-5">
|
||||
<TableHead>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue