diff --git a/ui/litellm-dashboard/src/components/model_info_view.test.tsx b/ui/litellm-dashboard/src/components/model_info_view.test.tsx index 5c7aa3c82fc..99b6d90998b 100644 --- a/ui/litellm-dashboard/src/components/model_info_view.test.tsx +++ b/ui/litellm-dashboard/src/components/model_info_view.test.tsx @@ -127,53 +127,120 @@ describe("ModelInfoView", () => { supported_openai_params: ["temperature", "max_tokens", "top_p", "frequency_penalty", "presence_penalty"], }; - it("should render the model info view", async () => { - const { getByText } = render( - {}} - modelData={modelData} - accessToken="123" - userID="123" - userRole="Admin" - editModel={false} - setEditModalVisible={() => {}} - setSelectedModel={() => {}} - onModelUpdate={() => {}} - modelAccessGroups={[]} - />, - ); - await waitFor(() => { - expect(getByText("Model Settings")).toBeInTheDocument(); + describe("Edit Model", () => { + it("should render the model info view", async () => { + const { getByText } = render( + {}} + modelData={modelData} + accessToken="123" + userID="123" + userRole="Admin" + editModel={false} + setEditModalVisible={() => {}} + setSelectedModel={() => {}} + onModelUpdate={() => {}} + modelAccessGroups={[]} + />, + ); + await waitFor(() => { + expect(getByText("Model Settings")).toBeInTheDocument(); + }); + }); + + it("should not render an edit model button if the model is not a DB model", async () => { + const nonDbModelData = { + ...modelData, + model_info: { + ...modelData.model_info, + db_model: false, + }, + }; + + const { queryByText } = render( + {}} + modelData={nonDbModelData} + accessToken="123" + userID="123" + userRole="Admin" + editModel={false} + setEditModalVisible={() => {}} + setSelectedModel={() => {}} + onModelUpdate={() => {}} + modelAccessGroups={[]} + />, + ); + await waitFor(() => { + expect(queryByText("Edit Model")).not.toBeInTheDocument(); + }); + }); + + it("should render tags in the edit model", async () => { + const { getByText } = render( + {}} + modelData={modelData} + accessToken="123" + userID="123" + userRole="Admin" + editModel={true} + setEditModalVisible={() => {}} + setSelectedModel={() => {}} + onModelUpdate={() => {}} + modelAccessGroups={[]} + />, + ); + await waitFor(() => { + expect(getByText("Tags")).toBeInTheDocument(); + }); }); }); - it("should not render an edit model button if the model is not a DB model", async () => { - const nonDbModelData = { - ...modelData, - model_info: { - ...modelData.model_info, - db_model: false, - }, - }; + describe("View Model", () => { + it("should render the model info view", async () => { + const { getByText } = render( + {}} + modelData={modelData} + accessToken="123" + userID="123" + userRole="Admin" + editModel={false} + setEditModalVisible={() => {}} + setSelectedModel={() => {}} + onModelUpdate={() => {}} + modelAccessGroups={[]} + />, + ); + await waitFor(() => { + expect(getByText("Model Settings")).toBeInTheDocument(); + }); + }); - const { queryByText } = render( - {}} - modelData={nonDbModelData} - accessToken="123" - userID="123" - userRole="Admin" - editModel={false} - setEditModalVisible={() => {}} - setSelectedModel={() => {}} - onModelUpdate={() => {}} - modelAccessGroups={[]} - />, - ); - await waitFor(() => { - expect(queryByText("Edit Model")).not.toBeInTheDocument(); + it("should render tags in the view model", async () => { + const { getByText } = render( + {}} + modelData={modelData} + accessToken="123" + userID="123" + userRole="Admin" + editModel={false} + setEditModalVisible={() => {}} + setSelectedModel={() => {}} + onModelUpdate={() => {}} + modelAccessGroups={[]} + />, + ); + await waitFor(() => { + expect(getByText("Tags")).toBeInTheDocument(); + }); }); }); }); diff --git a/ui/litellm-dashboard/src/components/model_info_view.tsx b/ui/litellm-dashboard/src/components/model_info_view.tsx index 23c8f0a9644..df5292b52e0 100644 --- a/ui/litellm-dashboard/src/components/model_info_view.tsx +++ b/ui/litellm-dashboard/src/components/model_info_view.tsx @@ -22,6 +22,7 @@ import { modelInfoV1Call, modelPatchUpdateCall, getGuardrailsList, + tagListCall, } from "./networking"; import { Button, Form, Input, Select, Modal, Tooltip } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; @@ -33,6 +34,7 @@ import { CheckIcon, CopyIcon } from "lucide-react"; import { copyToClipboard as utilCopyToClipboard } from "../utils/dataUtils"; import EditAutoRouterModal from "./edit_auto_router/edit_auto_router_modal"; import NotificationsManager from "./molecules/notifications_manager"; +import { Tag } from "./tag_management/types"; interface ModelInfoViewProps { modelId: string; @@ -73,6 +75,7 @@ export default function ModelInfoView({ const [copiedStates, setCopiedStates] = useState>({}); const [isAutoRouterModalOpen, setIsAutoRouterModalOpen] = useState(false); const [guardrailsList, setGuardrailsList] = useState([]); + const [tagsList, setTagsList] = useState>({}); const canEditModel = (userRole === "Admin" || modelData?.model_info?.created_by === userID) && modelData?.model_info?.db_model; const isAdmin = userRole === "Admin"; @@ -84,6 +87,8 @@ export default function ModelInfoView({ console.log("usingExistingCredential, ", usingExistingCredential); console.log("modelData.litellm_params.litellm_credential_name, ", modelData?.litellm_params?.litellm_credential_name); + console.log("tagsList, ", modelData.litellm_params?.tags); + useEffect(() => { const getExistingCredential = async () => { console.log("accessToken, ", accessToken); @@ -132,9 +137,20 @@ export default function ModelInfoView({ } }; + const fetchTags = async () => { + if (!accessToken) return; + try { + const response = await tagListCall(accessToken); + setTagsList(response); + } catch (error) { + console.error("Failed to fetch tags:", error); + } + }; + getExistingCredential(); getModelInfo(); fetchGuardrails(); + fetchTags(); }, [accessToken, modelId]); const handleReuseCredential = async (values: any) => { @@ -173,6 +189,7 @@ export default function ModelInfoView({ stream_timeout: values.stream_timeout, input_cost_per_token: values.input_cost / 1_000_000, output_cost_per_token: values.output_cost / 1_000_000, + tags: values.tags, }; if (values.guardrails) { updatedLitellmParams.guardrails = values.guardrails; @@ -476,6 +493,7 @@ export default function ModelInfoView({ guardrails: Array.isArray(localModelData.litellm_params?.guardrails) ? localModelData.litellm_params.guardrails : [], + tags: Array.isArray(localModelData.litellm_params?.tags) ? localModelData.litellm_params.tags : [], }} layout="vertical" onValuesChange={() => setIsDirty(true)} @@ -691,7 +709,7 @@ export default function ModelInfoView({
- Guardrails{" "} + Guardrails +
+ Tags + {isEditing ? ( + + = ({ onChange, value, className, a title: tag.description || tag.name, }))} optionFilterProp="label" - showSearch + tokenSeparators={[","]} + maxTagCount="responsive" + allowClear style={{ width: "100%" }} /> );