diff --git a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx
index 35434830d98..d7dfb8151fc 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.integration.test.tsx
@@ -80,6 +80,42 @@ describe("GeneralSettings General tab", () => {
vi.mocked(deleteConfigFieldSetting).mockClear();
});
+ it.each([
+ ["maximum_spend_logs_retention_period", null, "7d"],
+ ["maximum_health_check_retention_period", "30d", "14d"],
+ ["maximum_daily_tag_spend_retention_period", undefined, "90d"],
+ ["maximum_spend_logs_cleanup_run_budget", "5m", "10m"],
+ ["maximum_spend_logs_cleanup_batch_timeout", "", "45s"],
+ ["mcp_client_id_header", "x-client-id", "X-MCP-Client.ID"],
+ ])("displays, updates and resets the String setting %s", async (fieldName, initialValue, updatedValue) => {
+ vi.mocked(getGeneralSettingsCall).mockResolvedValue([
+ ...SETTINGS_FIXTURE,
+ {
+ field_name: fieldName,
+ field_type: "String",
+ field_value: initialValue,
+ field_description: "string setting",
+ stored_in_db: initialValue != null,
+ field_default_value: null,
+ },
+ ]);
+ const user = userEvent.setup();
+ renderWithProviders();
+ await user.click(screen.getByRole("tab", { name: "General" }));
+ const input = await screen.findByRole("textbox", { name: fieldName });
+ expect(input).toHaveValue(initialValue ?? "");
+ fireEvent.change(input, { target: { value: updatedValue } });
+ const row = screen.getByRole("row", { name: new RegExp(fieldName) });
+ await user.click(within(row).getByRole("button", { name: "Update" }));
+ expect(vi.mocked(updateConfigFieldSetting).mock.calls).toEqual([["token", fieldName, updatedValue]]);
+ expect(within(row).getByText("In DB")).toBeInTheDocument();
+ const resetIcon = row.querySelectorAll("td")[3].querySelector("svg");
+ expect(resetIcon).not.toBeNull();
+ await user.click(resetIcon as unknown as Element);
+ expect(vi.mocked(deleteConfigFieldSetting).mock.calls).toEqual([["token", fieldName]]);
+ expect(input).toHaveValue("");
+ });
+
it("updates max_ui_session_budget with its own value, not the value at its filtered index", async () => {
const user = userEvent.setup();
renderWithProviders();
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx
index 5d28adb7ef3..67712d78a13 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx
@@ -68,6 +68,15 @@ const SettingValueEditor: React.FC<{
/>
);
}
+ if (setting.field_type === "String") {
+ return (
+ onChange(setting.field_name, event.target.value)}
+ />
+ );
+ }
if (setting.field_type === "Integer") {
return (