mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 19:54:13 +00:00
Fix edit team on ui (#8295)
* fix(columns.tsx): fix request logs team column to indicate the value is the alias not the id * fix(team_info.tsx): add edit team logic to team info page * fix(team_info.tsx): re-enable updating team settings on UI Fixes https://github.com/BerriAI/litellm/issues/8281 * fix(team_info.tsx): fix save changes on team update * fix(teams.tsx): allow edit button to still act as a quick action button -> drop user into settings page for team * test(config.yml): run dev ui during testing make sure no ui regressions are pushed on main * build: update ci/cd * ci(config.yml): fix test * ci: fix ci * ci: update * ci: fix * ci: another attempt to get nvm working in ci/cd * ci: fix ci * ci: test update * ci: test update 2 * ci: test 3 * fix(team_info.tsx): fix linting error
This commit is contained in:
parent
0038443b1e
commit
f66bcce2fa
4 changed files with 476 additions and 283 deletions
|
@ -132,6 +132,7 @@ const Team: React.FC<TeamProps> = ({
|
|||
null
|
||||
);
|
||||
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
|
||||
const [editTeam, setEditTeam] = useState<boolean>(false);
|
||||
|
||||
const [isTeamModalVisible, setIsTeamModalVisible] = useState(false);
|
||||
const [isAddMemberModalVisible, setIsAddMemberModalVisible] = useState(false);
|
||||
|
@ -168,183 +169,6 @@ const Team: React.FC<TeamProps> = ({
|
|||
fetchGuardrails();
|
||||
}, [accessToken]);
|
||||
|
||||
const EditTeamModal: React.FC<EditTeamModalProps> = ({
|
||||
visible,
|
||||
onCancel,
|
||||
team,
|
||||
onSubmit,
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// Extract existing guardrails from team metadata
|
||||
let existingGuardrails: string[] = [];
|
||||
try {
|
||||
existingGuardrails = team.metadata?.guardrails || [];
|
||||
} catch (error) {
|
||||
console.error("Error extracting guardrails:", error);
|
||||
}
|
||||
|
||||
const handleOk = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
const updatedValues = { ...values, team_id: team.team_id };
|
||||
onSubmit(updatedValues);
|
||||
form.resetFields();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Validation failed:", error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Edit Team"
|
||||
visible={visible}
|
||||
width={800}
|
||||
footer={null}
|
||||
onOk={handleOk}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleEditSubmit}
|
||||
initialValues={{
|
||||
...team,
|
||||
guardrails: existingGuardrails
|
||||
}}
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
labelAlign="left"
|
||||
>
|
||||
<>
|
||||
<Form.Item
|
||||
label="Team Name"
|
||||
name="team_alias"
|
||||
rules={[{ required: true, message: "Please input a team name" }]}
|
||||
>
|
||||
<TextInput />
|
||||
</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 &&
|
||||
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} width={200} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
className="mt-8"
|
||||
label="Reset Budget"
|
||||
name="budget_duration"
|
||||
>
|
||||
<Select2 defaultValue={null} placeholder="n/a">
|
||||
<Select2.Option value="24h">daily</Select2.Option>
|
||||
<Select2.Option value="7d">weekly</Select2.Option>
|
||||
<Select2.Option value="30d">monthly</Select2.Option>
|
||||
</Select2>
|
||||
</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="Requests per minute Limit (RPM)"
|
||||
name="team_id"
|
||||
hidden={true}
|
||||
></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"
|
||||
className="mt-8"
|
||||
help="Select existing guardrails or enter new ones"
|
||||
>
|
||||
<Select2
|
||||
mode="tags"
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select or enter guardrails"
|
||||
options={guardrailsList.map(name => ({ value: name, label: name }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
<div style={{ textAlign: "right", marginTop: "10px" }}>
|
||||
<Button2 htmlType="submit">Save</Button2>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const handleEditClick = (team: any) => {
|
||||
setSelectedTeam(team);
|
||||
setEditModalVisible(true);
|
||||
};
|
||||
|
||||
const handleEditCancel = () => {
|
||||
setEditModalVisible(false);
|
||||
setSelectedTeam(null);
|
||||
};
|
||||
|
||||
const handleEditSubmit = async (formValues: Record<string, any>) => {
|
||||
// Call API to update team with teamId and values
|
||||
const teamId = formValues.team_id; // get team_id
|
||||
|
||||
console.log("handleEditSubmit:", formValues);
|
||||
if (accessToken == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create metadata object with guardrails if they exist
|
||||
formValues.metadata = {
|
||||
...(formValues.metadata || {}),
|
||||
...(formValues.guardrails ? { guardrails: formValues.guardrails } : {})
|
||||
};
|
||||
|
||||
// Remove guardrails from top level since it's now in metadata
|
||||
delete formValues.guardrails;
|
||||
|
||||
let newTeamValues = await teamUpdateCall(accessToken, formValues);
|
||||
|
||||
// Update the teams state with the updated team data
|
||||
if (teams) {
|
||||
const updatedTeams = teams.map((team) =>
|
||||
team.team_id === teamId ? newTeamValues.data : team
|
||||
);
|
||||
setTeams(updatedTeams);
|
||||
}
|
||||
message.success("Team updated successfully");
|
||||
|
||||
setEditModalVisible(false);
|
||||
setSelectedTeam(null);
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
setIsTeamModalVisible(false);
|
||||
|
@ -526,10 +350,15 @@ const Team: React.FC<TeamProps> = ({
|
|||
{selectedTeamId ? (
|
||||
<TeamInfoView
|
||||
teamId={selectedTeamId}
|
||||
onClose={() => setSelectedTeamId(null)}
|
||||
onClose={() => {
|
||||
setSelectedTeamId(null);
|
||||
setEditTeam(false);
|
||||
}}
|
||||
accessToken={accessToken}
|
||||
is_team_admin={is_team_admin(teams?.find((team) => team.team_id === selectedTeamId))}
|
||||
is_proxy_admin={userRole == "Admin"}
|
||||
userModels={userModels}
|
||||
editTeam={editTeam}
|
||||
/>
|
||||
) : (
|
||||
<TabGroup className="gap-2 p-8 h-[75vh] w-full mt-2">
|
||||
|
@ -721,7 +550,10 @@ const Team: React.FC<TeamProps> = ({
|
|||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
size="sm"
|
||||
onClick={() => handleEditClick(team)}
|
||||
onClick={() => {
|
||||
setSelectedTeamId(team.team_id);
|
||||
setEditTeam(true);
|
||||
}}
|
||||
/>
|
||||
<Icon
|
||||
onClick={() => handleDelete(team.team_id)}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue