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 29eb8e0019b..b9df46f0c93 100644 --- a/ui/litellm-dashboard/src/components/model_info_view.test.tsx +++ b/ui/litellm-dashboard/src/components/model_info_view.test.tsx @@ -579,6 +579,46 @@ describe("ModelInfoView", () => { expect(updatePayload.litellm_params).not.toHaveProperty("vector_store_ids"); }); + it("should not leak vector_store_ids from JSON textarea when model already has them", async () => { + const modelWithVectorStores = { + ...defaultModelData, + litellm_params: { + ...defaultModelData.litellm_params, + vector_store_ids: ["vs_abc123"], + }, + }; + + mockUseModelsInfo.mockReturnValue({ + data: { + data: [modelWithVectorStores], + }, + isLoading: false, + error: null, + }); + + const user = userEvent.setup(); + render(, { wrapper }); + + await waitFor(() => { + expect(screen.getByRole("button", { name: /edit settings/i })).toBeInTheDocument(); + }); + + await user.click(screen.getByRole("button", { name: /edit settings/i })); + + const litellmParamsInput = screen + .getAllByRole("textbox") + .find( + (input) => + input.tagName === "TEXTAREA" && + (input as HTMLTextAreaElement).value.includes('"custom_llm_provider"'), + ); + expect(litellmParamsInput).toBeDefined(); + if (!litellmParamsInput) { + return; + } + expect((litellmParamsInput as HTMLTextAreaElement).value).not.toContain("vector_store_ids"); + }); + it("should not include input_cost_per_token or output_cost_per_token in update payload when user does not touch cost fields", async () => { // Regression: editing a model without touching cost fields used to inject // input_cost_per_token: 0 and output_cost_per_token: 0 into litellm_params, diff --git a/ui/litellm-dashboard/src/components/model_info_view.tsx b/ui/litellm-dashboard/src/components/model_info_view.tsx index ea8af2fcd62..d53668cd501 100644 --- a/ui/litellm-dashboard/src/components/model_info_view.tsx +++ b/ui/litellm-dashboard/src/components/model_info_view.tsx @@ -235,6 +235,9 @@ export default function ModelInfoView({ try { parsedExtraParams = values.litellm_extra_params ? JSON.parse(values.litellm_extra_params) : {}; delete parsedExtraParams.litellm_credential_name; + delete parsedExtraParams.vector_store_ids; + delete parsedExtraParams.guardrails; + delete parsedExtraParams.tags; } catch (e) { NotificationsManager.fromBackend("Invalid JSON in LiteLLM Params"); setIsSaving(false); @@ -651,7 +654,7 @@ export default function ModelInfoView({ litellm_extra_params: JSON.stringify( Object.fromEntries( Object.entries(localModelData.litellm_params || {}).filter( - ([key]) => key !== "litellm_credential_name", + ([key]) => !["litellm_credential_name", "vector_store_ids", "guardrails", "tags"].includes(key), ), ), null,