mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
[Feature] UI - Add LiteLLM Params to Edit Model (#16496)
* Add LiteLLM Params to Edit Model * Fixed tests
This commit is contained in:
parent
7dd76bc4e3
commit
b9759b4bfa
6 changed files with 99 additions and 18 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { fireEvent, render, waitFor } from "@testing-library/react";
|
||||
import { act, fireEvent, render, waitFor } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import AdvancedSettings from "./advanced_settings";
|
||||
|
||||
|
|
@ -31,4 +31,21 @@ describe("AdvancedSettings", () => {
|
|||
expect(getByText("Tags")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should render the litellm params", async () => {
|
||||
const { getByText } = render(
|
||||
<AdvancedSettings
|
||||
showAdvancedSettings={true}
|
||||
setShowAdvancedSettings={() => {}}
|
||||
guardrailsList={[]}
|
||||
tagsList={{}}
|
||||
/>,
|
||||
);
|
||||
act(() => {
|
||||
fireEvent.click(getByText("Advanced Settings"));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(getByText("LiteLLM Params")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { InfoCircleOutlined } from "@ant-design/icons";
|
|||
import { Team } from "../key_team_helpers/key_list";
|
||||
import CacheControlSettings from "./cache_control_settings";
|
||||
import { Tag } from "../tag_management/types";
|
||||
import { formItemValidateJSON } from "../../utils/textUtils";
|
||||
const { Link } = Typography;
|
||||
|
||||
interface AdvancedSettingsProps {
|
||||
|
|
@ -40,18 +41,6 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const validateJSON = (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
return Promise.reject("Please enter valid JSON");
|
||||
}
|
||||
};
|
||||
|
||||
// Handle custom pricing changes
|
||||
const handleCustomPricingChange = (checked: boolean) => {
|
||||
setCustomPricing(checked);
|
||||
|
|
@ -233,7 +222,7 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
name="litellm_extra_params"
|
||||
tooltip="Optional litellm params used for making a litellm.completion() call."
|
||||
className="mb-4 mt-4"
|
||||
rules={[{ validator: validateJSON }]}
|
||||
rules={[{ validator: formItemValidateJSON }]}
|
||||
>
|
||||
<TextArea
|
||||
rows={4}
|
||||
|
|
@ -260,7 +249,7 @@ const AdvancedSettings: React.FC<AdvancedSettingsProps> = ({
|
|||
name="model_info_params"
|
||||
tooltip="Optional model info params. Returned when calling `/model/info` endpoint."
|
||||
className="mb-0"
|
||||
rules={[{ validator: validateJSON }]}
|
||||
rules={[{ validator: formItemValidateJSON }]}
|
||||
>
|
||||
<TextArea
|
||||
rows={4}
|
||||
|
|
|
|||
|
|
@ -175,6 +175,13 @@ describe("ModelInfoView", () => {
|
|||
expect(getByText("Tags")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should render the litellm params in the edit model", async () => {
|
||||
const { getByText } = render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} />);
|
||||
await waitFor(() => {
|
||||
expect(getByText("LiteLLM Params")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should render a test connection button", async () => {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { Button, Form, Input, Modal, Select, Tooltip } from "antd";
|
|||
import { CheckIcon, CopyIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { copyToClipboard as utilCopyToClipboard } from "../utils/dataUtils";
|
||||
import { truncateString } from "../utils/textUtils";
|
||||
import { formItemValidateJSON, truncateString } from "../utils/textUtils";
|
||||
import CacheControlSettings from "./add_model/cache_control_settings";
|
||||
import EditAutoRouterModal from "./edit_auto_router/edit_auto_router_modal";
|
||||
import ReuseCredentialsModal from "./model_add/reuse_credentials";
|
||||
|
|
@ -178,8 +178,19 @@ export default function ModelInfoView({
|
|||
|
||||
console.log("values.model_name, ", values.model_name);
|
||||
|
||||
// Parse LiteLLM extra params from JSON text area
|
||||
let parsedExtraParams: Record<string, any> = {};
|
||||
try {
|
||||
parsedExtraParams = values.litellm_extra_params ? JSON.parse(values.litellm_extra_params) : {};
|
||||
} catch (e) {
|
||||
NotificationsManager.fromBackend("Invalid JSON in LiteLLM Params");
|
||||
setIsSaving(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let updatedLitellmParams = {
|
||||
...localModelData.litellm_params,
|
||||
...values.litellm_params,
|
||||
...parsedExtraParams,
|
||||
model: values.litellm_model_name,
|
||||
api_base: values.api_base,
|
||||
custom_llm_provider: values.custom_llm_provider,
|
||||
|
|
@ -537,6 +548,7 @@ export default function ModelInfoView({
|
|||
? localModelData.litellm_params.guardrails
|
||||
: [],
|
||||
tags: Array.isArray(localModelData.litellm_params?.tags) ? localModelData.litellm_params.tags : [],
|
||||
litellm_extra_params: JSON.stringify(localModelData.litellm_params || {}, null, 2),
|
||||
}}
|
||||
layout="vertical"
|
||||
onValuesChange={() => setIsDirty(true)}
|
||||
|
|
@ -909,6 +921,39 @@ export default function ModelInfoView({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">
|
||||
LiteLLM Params
|
||||
<Tooltip title="Optional litellm params used for making a litellm.completion() call. Some params are automatically added by LiteLLM.">
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/completion/input"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<InfoCircleOutlined style={{ marginLeft: "4px" }} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Text>
|
||||
{isEditing ? (
|
||||
<Form.Item name="litellm_extra_params" rules={[{ validator: formItemValidateJSON }]}>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder='{
|
||||
"rpm": 100,
|
||||
"timeout": 0,
|
||||
"stream_timeout": 0
|
||||
}'
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div className="mt-1 p-2 bg-gray-50 rounded">
|
||||
<pre className="bg-gray-100 p-2 rounded text-xs overflow-auto mt-1">
|
||||
{JSON.stringify(localModelData.litellm_params, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Text className="font-medium">Team ID</Text>
|
||||
<div className="mt-1 p-2 bg-gray-50 rounded">{modelData.model_info.team_id || "Not Set"}</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { formatLabel, truncateString } from "./textUtils";
|
||||
import { formatLabel, formItemValidateJSON, truncateString } from "./textUtils";
|
||||
|
||||
describe("formatLabel", () => {
|
||||
it("should format label", () => {
|
||||
|
|
@ -16,3 +16,14 @@ describe("truncateString", () => {
|
|||
expect(truncateString("Hello, world!", 20)).toBe("Hello, world!");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formItemValidateJSON", () => {
|
||||
it("should resolve for a valid JSON", async () => {
|
||||
const validObj = { a: 1, b: "x", c: true, d: [1, 2], e: { f: "y" } };
|
||||
await expect(formItemValidateJSON({}, JSON.stringify(validObj))).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should reject with an error message for invalid JSON", async () => {
|
||||
await expect(formItemValidateJSON({}, "invalid JSON")).rejects.toBe("Please enter valid JSON");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,3 +10,15 @@ export const formatLabel = (text: string): string => {
|
|||
export function truncateString(str: string, maxLength: number) {
|
||||
return str.length > maxLength ? str.substring(0, maxLength) + "..." : str;
|
||||
}
|
||||
|
||||
export const formItemValidateJSON = (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
return Promise.reject("Please enter valid JSON");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue