feat(dashboard): add tool-source diagnostics settings toggle (#5978)

* feat(dashboard): add tool-source diagnostics settings toggle

Adds a Settings > Advanced card (cloned from DebugModeCard) that lets
operators flip the existing `logToolSources` flag from the UI instead
of editing the DB row directly. The backend gate (chatCore.ts) and DB
default were already present but had no toggle. Also adds
`logToolSources` to the /api/settings Zod PATCH schema (it is `.strict()`,
so the key was previously rejected) and en-only i18n strings.

Co-authored-by: DuyPrX <93126969+DuyPrX@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/pull/1825

* chore(changelog): restore release entries + add tool-source toggle bullet

---------

Co-authored-by: DuyPrX <93126969+DuyPrX@users.noreply.github.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-03 00:14:32 -03:00
committed by GitHub
parent aac5ebcde5
commit cbe2ec1244
6 changed files with 95 additions and 0 deletions

View File

@@ -16,6 +16,7 @@
- **feat(cli-tools):** add Crush CLI tool to the dashboard with one-click configuration. (thanks @dopaemon)
- **feat(dashboard):** suggest HuggingFace Hub media models in the media provider view. (thanks @yicone)
- **feat(dashboard):** collapse quota rows and sort by remaining quota in the usage view. (thanks @j2-cuong)
- **feat(dashboard):** add a settings toggle for tool-source diagnostics logging. (thanks @DuyPrX)
### 🔧 Bug Fixes

View File

@@ -1,6 +1,7 @@
"use client";
import DebugModeCard from "../components/DebugModeCard";
import LogToolSourcesCard from "../components/LogToolSourcesCard";
import PayloadRulesTab from "../components/PayloadRulesTab";
import RequestLimitsTab from "../components/RequestLimitsTab";
import CliproxyapiSettingsTab from "../components/CliproxyapiSettingsTab";
@@ -9,6 +10,7 @@ export default function SettingsAdvancedPage() {
return (
<div className="space-y-6">
<DebugModeCard />
<LogToolSourcesCard />
<PayloadRulesTab />
<RequestLimitsTab />
<CliproxyapiSettingsTab />

View File

@@ -0,0 +1,70 @@
"use client";
import { useEffect, useState } from "react";
import { Card, Toggle } from "@/shared/components";
import { useTranslations } from "next-intl";
export default function LogToolSourcesCard() {
const [logToolSources, setLogToolSources] = useState(false);
const [loading, setLoading] = useState(true);
const t = useTranslations("settings");
useEffect(() => {
let mounted = true;
async function loadSettings() {
setLoading(true);
try {
const res = await fetch("/api/settings", { cache: "no-store" });
if (res.ok && mounted) {
const data = await res.json();
setLogToolSources(data.logToolSources === true);
}
} catch {
// Leave the current switch state in place if settings cannot be loaded.
} finally {
if (mounted) setLoading(false);
}
}
loadSettings();
return () => {
mounted = false;
};
}, []);
const updateLogToolSources = async (value: boolean) => {
const previousValue = logToolSources;
setLogToolSources(value);
try {
const res = await fetch("/api/settings", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ logToolSources: value }),
});
if (!res.ok) setLogToolSources(previousValue);
} catch (err) {
setLogToolSources(previousValue);
console.error("Failed to update logToolSources:", err);
}
};
return (
<Card>
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-primary/10 text-primary">
<span className="material-symbols-outlined text-[20px]" aria-hidden="true">
troubleshoot
</span>
</div>
<div>
<h3 className="text-lg font-semibold">{t("logToolSourcesToggle")}</h3>
<p className="text-sm text-muted-foreground">{t("logToolSourcesDescription")}</p>
</div>
</div>
<Toggle checked={logToolSources} onChange={updateLogToolSources} disabled={loading} />
</div>
</Card>
);
}

View File

@@ -4995,6 +4995,8 @@
"modelsDevInfoOrder": "User Override → models.dev → LiteLLM → Hardcoded Default",
"systemTheme": "System Theme",
"debugToggle": "Enable Debug Mode",
"logToolSourcesToggle": "Log Tool Sources",
"logToolSourcesDescription": "Emit a diagnostic log line per request summarizing tool count and MCP/hosted/client source breakdown.",
"homePinProviderQuotaToHome": "Pin Information to Home Page",
"homeProviderQuotaLimits": "Provider Quota Limits",
"homeProviderQuotaLimitsDesc": "Pin the Provider Quota status container (with Refresh All button) to the top of the Home page.",

View File

@@ -144,6 +144,7 @@ export const updateSettingsSchema = z.object({
.optional(),
customBannedSignals: z.array(z.string().max(200)).optional(),
debugMode: z.boolean().optional(),
logToolSources: z.boolean().optional(),
hiddenSidebarItems: z.array(z.enum(HIDEABLE_SIDEBAR_ITEM_IDS)).optional(),
hiddenSidebarGroupLabels: z.array(z.enum(HIDEABLE_SIDEBAR_GROUP_IDS)).optional(),
sidebarSectionOrder: z

View File

@@ -59,6 +59,25 @@ test("Debug mode moved to the top of Advanced settings", () => {
assertInOrder(advancedPage, ["<DebugModeCard", "<PayloadRulesTab"]);
});
test("Log Tool Sources toggle is mounted in Advanced settings next to Debug mode", () => {
const advancedPage = readSrc("src/app/(dashboard)/dashboard/settings/advanced/page.tsx");
assertInOrder(advancedPage, ["<DebugModeCard", "<LogToolSourcesCard", "<PayloadRulesTab"]);
const card = readSrc(
"src/app/(dashboard)/dashboard/settings/components/LogToolSourcesCard.tsx"
);
assert.match(card, /t\("logToolSourcesToggle"\)/);
assert.match(card, /t\("logToolSourcesDescription"\)/);
assert.match(card, /logToolSources: value/);
const schema = readSrc("src/shared/validation/settingsSchemas.ts");
assert.match(schema, /logToolSources: z\.boolean\(\)\.optional\(\)/);
const en = readSrc("src/i18n/messages/en.json");
assert.match(en, /"logToolSourcesToggle": "Log Tool Sources"/);
});
test("Proxy Logs table uses the same blue row hover emphasis as Logs", () => {
const proxyLogger = readSrc("src/shared/components/ProxyLogger.tsx");
const requestLogger = readSrc("src/shared/components/RequestLoggerV2.tsx");