mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
feat: replace JSON textarea with model dropdown editor for team_member_model_max_budget
This commit is contained in:
parent
e5c33ca695
commit
f844ff664e
1 changed files with 125 additions and 17 deletions
|
|
@ -5,6 +5,7 @@ import TeamSSOSettings from "@/components/TeamSSOSettings";
|
|||
import { isProxyAdminRole } from "@/utils/roles";
|
||||
import {
|
||||
InfoCircleOutlined,
|
||||
MinusCircleOutlined,
|
||||
PlusOutlined,
|
||||
TeamOutlined,
|
||||
ReloadOutlined,
|
||||
|
|
@ -21,6 +22,7 @@ import {
|
|||
Flex,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Layout,
|
||||
Modal,
|
||||
Pagination,
|
||||
|
|
@ -172,6 +174,127 @@ const getOrganizationAlias = (
|
|||
return organization?.organization_alias || organizationId;
|
||||
};
|
||||
|
||||
const BUDGET_DURATION_OPTIONS = [
|
||||
{ label: "No reset", value: "" },
|
||||
{ label: "Daily", value: "24h" },
|
||||
{ label: "Weekly", value: "7d" },
|
||||
{ label: "Monthly", value: "30d" },
|
||||
{ label: "Yearly", value: "365d" },
|
||||
];
|
||||
|
||||
interface ModelBudgetRow {
|
||||
id: string;
|
||||
model: string;
|
||||
max_budget: number | null;
|
||||
budget_duration: string;
|
||||
}
|
||||
|
||||
type ModelBudgetValue = Record<string, { max_budget: number; budget_duration?: string }>;
|
||||
|
||||
interface ModelBudgetEditorProps {
|
||||
value?: ModelBudgetValue;
|
||||
onChange?: (val: ModelBudgetValue | undefined) => void;
|
||||
availableModels: string[];
|
||||
}
|
||||
|
||||
const ModelBudgetEditor: React.FC<ModelBudgetEditorProps> = ({ value, onChange, availableModels }) => {
|
||||
const [rows, setRows] = useState<ModelBudgetRow[]>(() => {
|
||||
if (!value || Object.keys(value).length === 0) return [];
|
||||
return Object.entries(value).map(([model, cfg]) => ({
|
||||
id: Math.random().toString(36).slice(2),
|
||||
model,
|
||||
max_budget: cfg.max_budget ?? null,
|
||||
budget_duration: cfg.budget_duration ?? "",
|
||||
}));
|
||||
});
|
||||
|
||||
const notifyChange = (newRows: ModelBudgetRow[]) => {
|
||||
if (!onChange) return;
|
||||
const filled = newRows.filter((r) => r.model);
|
||||
if (filled.length === 0) {
|
||||
onChange(undefined);
|
||||
return;
|
||||
}
|
||||
const obj: ModelBudgetValue = {};
|
||||
for (const r of filled) {
|
||||
obj[r.model] = {
|
||||
max_budget: r.max_budget ?? 0,
|
||||
...(r.budget_duration ? { budget_duration: r.budget_duration } : {}),
|
||||
};
|
||||
}
|
||||
onChange(obj);
|
||||
};
|
||||
|
||||
const addRow = () => {
|
||||
const newRows = [
|
||||
...rows,
|
||||
{ id: Math.random().toString(36).slice(2), model: "", max_budget: null, budget_duration: "" },
|
||||
];
|
||||
setRows(newRows);
|
||||
};
|
||||
|
||||
const updateRow = (id: string, field: keyof ModelBudgetRow, val: any) => {
|
||||
const newRows = rows.map((r) => (r.id === id ? { ...r, [field]: val } : r));
|
||||
setRows(newRows);
|
||||
notifyChange(newRows);
|
||||
};
|
||||
|
||||
const removeRow = (id: string) => {
|
||||
const newRows = rows.filter((r) => r.id !== id);
|
||||
setRows(newRows);
|
||||
notifyChange(newRows);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
|
||||
{rows.map((row) => (
|
||||
<div key={row.id} style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
|
||||
<Select
|
||||
style={{ width: 200 }}
|
||||
placeholder="Select model"
|
||||
value={row.model || undefined}
|
||||
onChange={(val) => updateRow(row.id, "model", val)}
|
||||
showSearch
|
||||
options={availableModels.map((m) => ({ label: m, value: m }))}
|
||||
filterOption={(input, opt) =>
|
||||
(opt?.label as string ?? "").toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
/>
|
||||
<InputNumber
|
||||
style={{ width: 140 }}
|
||||
placeholder="Max budget"
|
||||
min={0}
|
||||
precision={4}
|
||||
prefix="$"
|
||||
value={row.max_budget}
|
||||
onChange={(val) => updateRow(row.id, "max_budget", val)}
|
||||
/>
|
||||
<Select
|
||||
style={{ width: 130 }}
|
||||
value={row.budget_duration}
|
||||
onChange={(val) => updateRow(row.id, "budget_duration", val)}
|
||||
options={BUDGET_DURATION_OPTIONS}
|
||||
/>
|
||||
<Button
|
||||
type="text"
|
||||
danger
|
||||
icon={<MinusCircleOutlined />}
|
||||
onClick={() => removeRow(row.id)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={addRow}
|
||||
icon={<PlusOutlined />}
|
||||
style={{ width: "fit-content" }}
|
||||
>
|
||||
Add model limit
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// @deprecated
|
||||
const Teams: React.FC<TeamProps> = ({
|
||||
teams,
|
||||
|
|
@ -563,21 +686,6 @@ const Teams: React.FC<TeamProps> = ({
|
|||
delete formValues.allowed_agents_and_groups;
|
||||
}
|
||||
|
||||
// Parse team_member_model_max_budget from JSON string
|
||||
if (formValues.team_member_model_max_budget) {
|
||||
if (typeof formValues.team_member_model_max_budget === "string") {
|
||||
if (formValues.team_member_model_max_budget.trim() === "") {
|
||||
delete formValues.team_member_model_max_budget;
|
||||
} else {
|
||||
try {
|
||||
formValues.team_member_model_max_budget = JSON.parse(formValues.team_member_model_max_budget);
|
||||
} catch (e) {
|
||||
throw new Error("Failed to parse team member model budget: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add model_aliases if any are defined
|
||||
if (Object.keys(modelAliases).length > 0) {
|
||||
formValues.model_aliases = modelAliases;
|
||||
|
|
@ -1242,9 +1350,9 @@ const Teams: React.FC<TeamProps> = ({
|
|||
<Form.Item
|
||||
label="Default Member Model Budget"
|
||||
name="team_member_model_max_budget"
|
||||
tooltip='Optional. Set independent spend limits per model for each team member. Each member gets their own independent budget per model. Format: {"model-name": {"max_budget": 10.0}}'
|
||||
tooltip="Optional. Set per-model spend limits for every team member. Each member gets their own independent budget per model."
|
||||
>
|
||||
<TextInput placeholder='{"gpt-3.5-turbo": {"max_budget": 10.0}}' />
|
||||
<ModelBudgetEditor availableModels={userModels} />
|
||||
</Form.Item>
|
||||
|
||||
<Accordion
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue