Merge pull request #3197 from BerriAI/litellm_ui_flow_for_non_admins

[UI] - simplify "Create Key" for non admins
This commit is contained in:
Ishaan Jaff 2024-04-20 16:31:32 -07:00 committed by GitHub
commit cc4e66d04a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 154 additions and 17 deletions

View file

@ -2,7 +2,7 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { Button, TextInput, Grid, Col } from "@tremor/react"; import { Button, TextInput, Grid, Col } from "@tremor/react";
import { Card, Metric, Text, Title, Subtitle } from "@tremor/react"; import { Card, Metric, Text, Title, Subtitle, Accordion, AccordionHeader, AccordionBody, } from "@tremor/react";
import { CopyToClipboard } from 'react-copy-to-clipboard'; import { CopyToClipboard } from 'react-copy-to-clipboard';
import { import {
Button as Button2, Button as Button2,
@ -116,7 +116,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
wrapperCol={{ span: 16 }} wrapperCol={{ span: 16 }}
labelAlign="left" labelAlign="left"
> >
{userRole === "App Owner" || userRole === "Admin" || userRole === "App User" ? ( {userRole === "App Owner" || userRole === "Admin" ? (
<> <>
<Form.Item <Form.Item
label="Key Name" label="Key Name"
@ -248,16 +248,143 @@ const CreateKey: React.FC<CreateKeyProps> = ({
</> </>
) : ( ) : (
<> <>
<Form.Item label="Key Name" name="key_alias"> <Form.Item
label="Key Name"
name="key_alias"
rules={[{ required: true, message: 'Please input a key name' }]}
help="required"
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label="Team ID (Contact Group)" name="team_id"> <Form.Item
<Input placeholder="default team (create a new team)" /> label="Team ID"
name="team_id"
hidden={true}
initialValue={team ? team["team_id"] : null}
valuePropName="team_id"
className="mt-8"
>
<Input value={team ? team["team_alias"] : ""} disabled />
</Form.Item> </Form.Item>
<Form.Item label="Description" name="description"> <Form.Item
<Input.TextArea placeholder="Enter description" rows={4} /> label="Models"
name="models"
rules={[{ required: true, message: 'Please select a model' }]}
help="required"
>
<Select
mode="multiple"
placeholder="Select models"
style={{ width: "100%" }}
>
<Option key="all-team-models" value="all-team-models">
All Team Models
</Option>
{team && team.models ? (
team.models.includes("all-proxy-models") ? (
userModels.map((model: string) => (
(
<Option key={model} value={model}>
{model}
</Option>
)
))
) : (
team.models.map((model: string) => (
<Option key={model} value={model}>
{model}
</Option>
))
)
) : (
userModels.map((model: string) => (
<Option key={model} value={model}>
{model}
</Option>
))
)}
</Select>
</Form.Item> </Form.Item>
<Accordion className="mt-8">
<AccordionHeader>
<b>Optional Settings</b>
</AccordionHeader>
<AccordionBody>
<Form.Item
className="mt-8"
label="Max Budget (USD)"
name="max_budget"
help={`Budget cannot exceed team max budget: $${team?.max_budget !== null && team?.max_budget !== undefined ? team?.max_budget : 'unlimited'}`}
rules={[
{
validator: async (_, value) => {
if (value && team && team.max_budget !== null && value > team.max_budget) {
throw new Error(`Budget cannot exceed team max budget: $${team.max_budget}`);
}
},
},
]}
>
<InputNumber step={0.01} precision={2} width={200} />
</Form.Item>
<Form.Item
className="mt-8"
label="Reset Budget"
name="budget_duration"
help={`Team Reset Budget: ${team?.budget_duration !== null && team?.budget_duration !== undefined ? team?.budget_duration : 'None'}`}
>
<Select defaultValue={null} placeholder="n/a">
<Select.Option value="24h">daily</Select.Option>
<Select.Option value="30d">monthly</Select.Option>
</Select>
</Form.Item>
<Form.Item
className="mt-8"
label="Tokens per minute Limit (TPM)"
name="tpm_limit"
help={`TPM cannot exceed team TPM limit: ${team?.tpm_limit !== null && team?.tpm_limit !== undefined ? team?.tpm_limit : 'unlimited'}`}
rules={[
{
validator: async (_, value) => {
if (value && team && team.tpm_limit !== null && value > team.tpm_limit) {
throw new Error(`TPM limit cannot exceed team TPM limit: ${team.tpm_limit}`);
}
},
},
]}
>
<InputNumber step={1} width={400} />
</Form.Item>
<Form.Item
className="mt-8"
label="Requests per minute Limit (RPM)"
name="rpm_limit"
help={`RPM cannot exceed team RPM limit: ${team?.rpm_limit !== null && team?.rpm_limit !== undefined ? team?.rpm_limit : 'unlimited'}`}
rules={[
{
validator: async (_, value) => {
if (value && team && team.rpm_limit !== null && value > team.rpm_limit) {
throw new Error(`RPM limit cannot exceed team RPM limit: ${team.rpm_limit}`);
}
},
},
]}
>
<InputNumber step={1} width={400} />
</Form.Item>
<Form.Item label="Expire Key (eg: 30s, 30h, 30d)" name="duration" className="mt-8">
<Input />
</Form.Item>
<Form.Item label="Metadata" name="metadata">
<Input.TextArea rows={4} placeholder="Enter metadata as JSON" />
</Form.Item>
</AccordionBody>
</Accordion>
</> </>
)} )}
<div style={{ textAlign: "right", marginTop: "10px" }}> <div style={{ textAlign: "right", marginTop: "10px" }}>

View file

@ -4,6 +4,7 @@ import { Select, SelectItem, Text, Title } from "@tremor/react";
interface DashboardTeamProps { interface DashboardTeamProps {
teams: Object[] | null; teams: Object[] | null;
setSelectedTeam: React.Dispatch<React.SetStateAction<any | null>>; setSelectedTeam: React.Dispatch<React.SetStateAction<any | null>>;
userRole: string | null;
} }
type TeamInterface = { type TeamInterface = {
@ -15,6 +16,7 @@ type TeamInterface = {
const DashboardTeam: React.FC<DashboardTeamProps> = ({ const DashboardTeam: React.FC<DashboardTeamProps> = ({
teams, teams,
setSelectedTeam, setSelectedTeam,
userRole,
}) => { }) => {
const defaultTeam: TeamInterface = { const defaultTeam: TeamInterface = {
models: [], models: [],
@ -25,19 +27,27 @@ const DashboardTeam: React.FC<DashboardTeamProps> = ({
const [value, setValue] = useState(defaultTeam); const [value, setValue] = useState(defaultTeam);
const updatedTeams = teams ? [...teams, defaultTeam] : [defaultTeam]; let updatedTeams;
if (userRole === "App User") {
// Non-Admin SSO users should only see their own team - they should not see "Default Team"
updatedTeams = teams;
} else {
updatedTeams = teams ? [...teams, defaultTeam] : [defaultTeam];
}
return ( return (
<div className="mt-5 mb-5"> <div className="mt-5 mb-5">
<Title>Select Team</Title> <Title>Select Team</Title>
{userRole !== "App User" && (
<>
<Text> <Text>
If you belong to multiple teams, this setting controls which team is If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys.
used by default when creating new API Keys.
</Text> </Text>
<Text className="mt-3 mb-3"> <Text className="mt-3 mb-3">
<b>Default Team:</b> If no team_id is set for a key, it will be grouped under here. <b>Default Team:</b> If no team_id is set for a key, it will be grouped under here.
</Text> </Text>
</>
)}
{updatedTeams && updatedTeams.length > 0 ? ( {updatedTeams && updatedTeams.length > 0 ? (
<Select defaultValue="0"> <Select defaultValue="0">
{updatedTeams.map((team: any, index) => ( {updatedTeams.map((team: any, index) => (

View file

@ -257,7 +257,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
data={keys} data={keys}
setData={setKeys} setData={setKeys}
/> />
<DashboardTeam teams={teams} setSelectedTeam={setSelectedTeam} /> <DashboardTeam teams={teams} setSelectedTeam={setSelectedTeam} userRole={userRole}/>
</Col> </Col>
</Grid> </Grid>
</div> </div>