mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 03:34:10 +00:00
UI Fixes and Improvements (02/14/2025) p1 (#8546)
* fix(user_dashboard.tsx): add bounding height to keys table ui prevents table from exceeding page height * fix(create_key_button.tsx): do not require team to be selected when user creating keys - allow personal key creation * fix(team_info.tsx): allow proxy admin to edit/delete team members even when not specifically team admins
This commit is contained in:
parent
08aa201e15
commit
3deefc8e2f
5 changed files with 16 additions and 183 deletions
|
@ -277,7 +277,7 @@ export function AllKeysTable({
|
|||
];
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full h-full overflow-hidden">
|
||||
{selectedKeyId ? (
|
||||
<KeyInfoView
|
||||
keyId={selectedKeyId}
|
||||
|
@ -289,7 +289,7 @@ export function AllKeysTable({
|
|||
teams={teams}
|
||||
/>
|
||||
) : (
|
||||
<div className="border-b py-4">
|
||||
<div className="border-b py-4 flex-1 overflow-hidden">
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<TeamFilter
|
||||
teams={teams}
|
||||
|
@ -324,6 +324,7 @@ export function AllKeysTable({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[32rem] overflow-auto">
|
||||
<DataTable
|
||||
columns={columns.filter(col => col.id !== 'expander')}
|
||||
data={keys}
|
||||
|
@ -332,7 +333,7 @@ export function AllKeysTable({
|
|||
renderSubComponent={() => <></>}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
|
|
@ -289,7 +289,6 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
|||
name="team_id"
|
||||
initialValue={team ? team.team_id : null}
|
||||
className="mt-8"
|
||||
rules={[{ required: true, message: 'Please select a team' }]}
|
||||
>
|
||||
<Select
|
||||
showSearch
|
||||
|
|
|
@ -2545,8 +2545,8 @@ export const teamMemberDeleteCall = async (
|
|||
},
|
||||
body: JSON.stringify({
|
||||
team_id: teamId,
|
||||
...(formValues.user_email && { user_email: formValues.user_email }),
|
||||
...(formValues.user_id && { user_id: formValues.user_id })
|
||||
...(formValues.user_email !== undefined && { user_email: formValues.user_email }),
|
||||
...(formValues.user_id !== undefined && { user_id: formValues.user_id })
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
@ -90,7 +90,6 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
|||
|
||||
console.log("userModels in team info", userModels);
|
||||
|
||||
const canManageMembers = is_team_admin || is_proxy_admin;
|
||||
const canEditTeam = is_team_admin || is_proxy_admin;
|
||||
|
||||
const fetchTeamInfo = async () => {
|
||||
|
@ -202,172 +201,6 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const renderSettingsPanel = () => {
|
||||
if (!teamData?.team_info) return null;
|
||||
const info = teamData.team_info;
|
||||
|
||||
// Extract existing guardrails from team metadata
|
||||
let existingGuardrails: string[] = [];
|
||||
try {
|
||||
existingGuardrails = info.metadata?.guardrails || [];
|
||||
} catch (error) {
|
||||
console.error("Error extracting guardrails:", error);
|
||||
}
|
||||
|
||||
if (!isEditing) {
|
||||
return (
|
||||
<Card>
|
||||
<div className="flex justify-between">
|
||||
<Title>Team Settings</Title>
|
||||
{canEditTeam && (
|
||||
<Button type="primary" onClick={() => setIsEditing(true)}>
|
||||
Edit Settings
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 space-y-4">
|
||||
<div>
|
||||
<Text className="font-medium">Team Name</Text>
|
||||
<Text>{info.team_alias}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Team ID</Text>
|
||||
<Text className="font-mono">{info.team_id}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Created At</Text>
|
||||
<Text>{new Date(info.created_at).toLocaleString()}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Models</Text>
|
||||
<div className="flex flex-wrap gap-2 mt-1">
|
||||
{info.models.map((model, index) => (
|
||||
<Badge key={index} color="red">
|
||||
{model}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Rate Limits</Text>
|
||||
<Text>TPM: {info.tpm_limit || 'Unlimited'}</Text>
|
||||
<Text>RPM: {info.rpm_limit || 'Unlimited'}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Budget</Text>
|
||||
<Text>Max: ${info.max_budget || 'Unlimited'}</Text>
|
||||
<Text>Reset: {info.budget_duration || 'Never'}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Status</Text>
|
||||
<Badge color={info.blocked ? 'red' : 'green'}>
|
||||
{info.blocked ? 'Blocked' : 'Active'}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Title>Edit Team Settings</Title>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleTeamUpdate}
|
||||
initialValues={{
|
||||
...info,
|
||||
guardrails: existingGuardrails
|
||||
}}
|
||||
layout="vertical"
|
||||
className="mt-4"
|
||||
>
|
||||
<Form.Item
|
||||
label="Team Name"
|
||||
name="team_alias"
|
||||
rules={[{ required: true, message: "Please input a team name" }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Models" name="models">
|
||||
<Select2
|
||||
mode="multiple"
|
||||
placeholder="Select models"
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
<Select2.Option
|
||||
key="all-proxy-models"
|
||||
value="all-proxy-models"
|
||||
>
|
||||
All Proxy Models
|
||||
</Select2.Option>
|
||||
{userModels.map((model) => (
|
||||
<Select2.Option key={model} value={model}>
|
||||
{getModelDisplayName(model)}
|
||||
</Select2.Option>
|
||||
))}
|
||||
</Select2>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Max Budget (USD)" name="max_budget">
|
||||
<InputNumber step={0.01} precision={2} style={{ width: 200 }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Reset Budget" name="budget_duration">
|
||||
<Select placeholder="n/a">
|
||||
<Select.Option value="24h">daily</Select.Option>
|
||||
<Select.Option value="7d">weekly</Select.Option>
|
||||
<Select.Option value="30d">monthly</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Tokens per minute Limit (TPM)" name="tpm_limit">
|
||||
<InputNumber step={1} style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Requests per minute Limit (RPM)" name="rpm_limit">
|
||||
<InputNumber step={1} style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
Guardrails{' '}
|
||||
<Tooltip title="Setup your first guardrail">
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/guardrails/quick_start"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<InfoCircleOutlined style={{ marginLeft: '4px' }} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="guardrails"
|
||||
help="Select existing guardrails or enter new ones"
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder="Select or enter guardrails"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<div className="flex justify-end gap-2 mt-6">
|
||||
<Button onClick={() => setIsEditing(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <div className="p-4">Loading...</div>;
|
||||
}
|
||||
|
@ -461,7 +294,7 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
|||
<Text className="font-mono">{member.role}</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{is_team_admin && (
|
||||
{canEditTeam && (
|
||||
<>
|
||||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
|
|
|
@ -335,8 +335,8 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
|
|||
|
||||
console.log("inside user dashboard, selected team", selectedTeam);
|
||||
return (
|
||||
<div className="w-full mx-4">
|
||||
<Grid numItems={1} className="gap-2 p-8 h-[75vh] w-full mt-2">
|
||||
<div className="w-full mx-4 overflow-hidden h-[75vh]">
|
||||
<Grid numItems={1} className="gap-2 p-8 w-full mt-2">
|
||||
<Col numColSpan={1} className="flex flex-col gap-2">
|
||||
<CreateKey
|
||||
key={selectedTeam ? selectedTeam.team_id : null}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue