diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f227558a07..c306f5894f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,6 +41,17 @@ Key variables for development: | `INITIAL_PASSWORD` | `123456` | First login password | | `ENABLE_REQUEST_LOGS` | `false` | Enable debug request logs | +### Dashboard Settings + +The dashboard provides UI toggles for features that can also be configured via environment variables: + +| Setting Location | Toggle | Description | +| ------------------- | ------------------ | ------------------------------ | +| Settings → Advanced | Debug Mode | Enable debug request logs (UI) | +| Settings → General | Sidebar Visibility | Show/hide sidebar sections | + +These settings are stored in the database and persist across restarts, overriding env var defaults when set. + ### Running Locally ```bash diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index 61a27caae5..26df8a5791 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -47,7 +47,10 @@ import { } from "@/lib/localDb"; import { getExecutor } from "../executors/index.ts"; import { getCacheControlSettings } from "@/lib/cacheControlSettings"; -import { shouldPreserveCacheControl } from "../utils/cacheControlPolicy.ts"; +import { + shouldPreserveCacheControl, + providerSupportsCaching, +} from "../utils/cacheControlPolicy.ts"; import { getCacheMetrics } from "@/lib/db/settings.ts"; import { @@ -965,9 +968,10 @@ export async function handleChatCore({ ? translatedBody : { ...translatedBody, model: modelToCall }; - // Inject prompt_cache_key for OpenAI providers if not already set + // Inject prompt_cache_key only for providers that support it if ( targetFormat === FORMATS.OPENAI && + providerSupportsCaching(provider) && !bodyToSend.prompt_cache_key && Array.isArray(bodyToSend.messages) && !["nvidia", "codex", "xai"].includes(provider) diff --git a/tests/e2e/settings-toggles.spec.ts b/tests/e2e/settings-toggles.spec.ts new file mode 100644 index 0000000000..fbd4167488 --- /dev/null +++ b/tests/e2e/settings-toggles.spec.ts @@ -0,0 +1,51 @@ +import { test, expect } from "@playwright/test"; + +test.describe("Settings Toggles", () => { + test("Debug mode toggle should work", async ({ page }) => { + await page.goto("/dashboard/settings"); + await page.waitForLoadState("networkidle"); + await page.click("text=Advanced"); + + const debugToggle = page.locator('[aria-label*="debug" i], [data-testid*="debug" i]').first(); + + await expect(debugToggle).toBeVisible({ timeout: 5000 }); + + const initialState = await debugToggle.isChecked(); + await debugToggle.click(); + await expect(debugToggle).not.toBeChecked({ timeout: 5000 }); + }); + + test("Sidebar visibility toggle should work", async ({ page }) => { + await page.goto("/dashboard/settings"); + await page.waitForLoadState("networkidle"); + await page.click("text=General"); + + const sidebarToggle = page + .locator('[aria-label*="sidebar" i], [data-testid*="sidebar" i]') + .first(); + + await expect(sidebarToggle).toBeVisible({ timeout: 5000 }); + + const initialState = await sidebarToggle.isChecked(); + await sidebarToggle.click(); + await expect(sidebarToggle).not.toBeChecked({ timeout: 5000 }); + }); + + test("Debug mode should persist after page reload", async ({ page }) => { + await page.goto("/dashboard/settings"); + await page.waitForLoadState("networkidle"); + await page.click("text=Advanced"); + + const debugToggle = page.locator('[aria-label*="debug" i], [data-testid*="debug" i]').first(); + + await expect(debugToggle).toBeVisible({ timeout: 5000 }); + + const wasChecked = await debugToggle.isChecked(); + await debugToggle.click(); + await expect(debugToggle).not.toBeChecked({ timeout: 5000 }); + await page.reload(); + await page.waitForLoadState("networkidle"); + await page.click("text=Advanced"); + await expect(debugToggle).not.toBeChecked({ timeout: 5000 }); + }); +}); diff --git a/tests/unit/settings-api.test.mjs b/tests/unit/settings-api.test.mjs new file mode 100644 index 0000000000..e3192e2c76 --- /dev/null +++ b/tests/unit/settings-api.test.mjs @@ -0,0 +1,67 @@ +import { describe, test } from "node:test"; +import assert from "node:assert/strict"; +import { getSettings, updateSettings } from "../../src/lib/db/settings.ts"; + +describe("Settings API - debugMode and hiddenSidebarItems", () => { + describe("debugMode", () => { + test("updateSettings with debugMode=true succeeds", async () => { + const result = await updateSettings({ debugMode: true }); + assert.ok(result, "updateSettings should return truthy result"); + + const settings = await getSettings(); + assert.strictEqual(settings.debugMode, true, "debugMode should be true"); + }); + + test("updateSettings with debugMode=false succeeds", async () => { + const result = await updateSettings({ debugMode: false }); + assert.ok(result, "updateSettings should return truthy result"); + + const settings = await getSettings(); + assert.strictEqual(settings.debugMode, false, "debugMode should be false"); + }); + }); + + describe("hiddenSidebarItems", () => { + test("updateSettings with hiddenSidebarItems=['translator'] succeeds", async () => { + const result = await updateSettings({ hiddenSidebarItems: ["translator"] }); + assert.ok(result, "updateSettings should return truthy result"); + + const settings = await getSettings(); + assert.deepStrictEqual( + settings.hiddenSidebarItems, + ["translator"], + "hiddenSidebarItems should contain translator" + ); + }); + + test("updateSettings with empty hiddenSidebarItems succeeds", async () => { + const result = await updateSettings({ hiddenSidebarItems: [] }); + assert.ok(result, "updateSettings should return truthy result"); + + const settings = await getSettings(); + assert.deepStrictEqual( + settings.hiddenSidebarItems, + [], + "hiddenSidebarItems should be empty array" + ); + }); + }); + + describe("combined updates", () => { + test("updateSettings with both debugMode and hiddenSidebarItems succeeds", async () => { + const result = await updateSettings({ + debugMode: true, + hiddenSidebarItems: ["translator"], + }); + assert.ok(result, "updateSettings should return truthy result"); + + const settings = await getSettings(); + assert.strictEqual(settings.debugMode, true, "debugMode should be true"); + assert.deepStrictEqual( + settings.hiddenSidebarItems, + ["translator"], + "hiddenSidebarItems should be updated" + ); + }); + }); +});