feat(ui): add deployment-wide Aria visibility setting (#51506)

This commit is contained in:
tin-berri 2026-11-12 17:17:20 -08:00 • committed by GitHub
parent d6b3c549ee
commit 85afa9a1ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 191 additions and 34 deletions

View file

@ -377,6 +377,11 @@ class UISettings(LiteLLMBaseModel):
description="If true, shows the Chat page in the UI sidebar, letting users chat with an LLM and connect their own MCP server credentials via OAuth.",
)
disable_aria: bool = Field(
default=False,
description="Hides Aria for everyone on this deployment.",
)
moyai_url: str | None = Field(
default=None,
description="URL of a connected Moyai deployment. When set, the Moyai entry in the UI navigation opens this deployment instead of the Moyai landing page.",
@ -432,6 +437,7 @@ ALLOWED_UI_SETTINGS_FIELDS: Final = {
"disable_custom_api_keys",
"disable_key_generate_for_org_admin",
"enable_chat_ui",
"disable_aria",
"moyai_url",
TEAM_ADMIN_EDITABLE_TEAM_FIELDS_SETTING,
}

View file

@ -9,7 +9,7 @@ const { getSettings } = vi.hoisted(() => ({
getSettings: vi.fn().mockResolvedValue({ values: { enable_projects_ui: true } }),
}));
vi.mock("@/components/networking", () => ({ getUiSettings: getSettings }));
vi.mock("@/components/networking", () => ({ getUiSettings: getSettings, getProxyBaseUrl: () => "http://localhost" }));
vi.mock("@/components/leftnav", () => ({
default: ({ enableProjectsUI }: ComponentProps<typeof Sidebar>) => (
<nav>{enableProjectsUI && <a href="/projects">Projects</a>}</nav>

View file

@ -7,6 +7,7 @@ import { PTU_FLAG_REFRESH_MS, usePtuCostAttributionEnabled } from "./usePtuCostA
import { useUISettings } from "./useUISettings";
vi.mock("@/components/networking", () => ({
getProxyBaseUrl: () => "http://localhost",
getUiSettings: vi.fn(),
}));

View file

@ -7,6 +7,7 @@ import { useUISettings } from "./useUISettings";
// Mock the networking function
vi.mock("@/components/networking", () => ({
getProxyBaseUrl: () => "http://localhost",
getUiSettings: vi.fn(),
}));
@ -56,7 +57,7 @@ describe("useUISettings", () => {
expect(result.current.data).toEqual(mockUISettings);
expect(result.current.error).toBeNull();
expect(getUiSettings).toHaveBeenCalledWith();
expect(getUiSettings).toHaveBeenCalledWith("http://localhost");
expect(getUiSettings).toHaveBeenCalledTimes(1);
});
@ -80,7 +81,7 @@ describe("useUISettings", () => {
expect(result.current.error).toEqual(testError);
expect(result.current.data).toBeUndefined();
expect(getUiSettings).toHaveBeenCalledWith();
expect(getUiSettings).toHaveBeenCalledWith("http://localhost");
expect(getUiSettings).toHaveBeenCalledTimes(1);
});
@ -97,7 +98,7 @@ describe("useUISettings", () => {
});
expect(result.current.data).toEqual({});
expect(getUiSettings).toHaveBeenCalledWith();
expect(getUiSettings).toHaveBeenCalledWith("http://localhost");
});
it("should handle network timeout error", async () => {

View file

@ -1,4 +1,4 @@
import { getUiSettings } from "@/components/networking";
import { getProxyBaseUrl, getUiSettings } from "@/components/networking";
import { useQuery } from "@tanstack/react-query";
import { createQueryKeys } from "../common/queryKeysFactory";
@ -13,12 +13,14 @@ const uiSettingsKeys = createQueryKeys("uiSettings");
* cached copy stale; a screen that stays mounted and focused never refetches on its own,
* so a caller that needs to notice a change also has to poll.
*/
export const useUISettings = (options?: { staleTime?: number; refetchInterval?: number }) => {
export const useUISettings = (options?: { staleTime?: number; refetchInterval?: number; enabled?: boolean }) => {
const proxyBaseUrl = getProxyBaseUrl();
return useQuery<Record<string, any>>({
queryKey: uiSettingsKeys.list({}),
queryFn: async () => await getUiSettings(),
queryKey: [...uiSettingsKeys.list({}), proxyBaseUrl],
queryFn: async () => await getUiSettings(proxyBaseUrl),
staleTime: options?.staleTime ?? 60 * 60 * 1000, // 1 hour - data rarely changes
gcTime: 60 * 60 * 1000, // 1 hour - keep in cache for 1 hour
refetchInterval: options?.refetchInterval,
enabled: options?.enabled,
});
};

View file

@ -101,6 +101,31 @@ describe("UISettings", () => {
expect(screen.getByRole("switch", { name: "Require authentication for public AI Hub" })).toBeInTheDocument();
});
it.each([false, true])("saves the deployment Aria visibility toggle from %s", (disabled) => {
const mutate = vi.fn();
mockUseUISettings.mockReturnValue(buildSettingsResponse({ data: { values: { disable_aria: disabled } } }));
mockUseUpdateUISettings.mockReturnValue({ mutate, isPending: false, error: null });
render(<UISettings />);
const toggle = screen.getByRole("switch", { name: "Hide Aria Agent for everyone" });
if (disabled) expect(toggle).toBeChecked();
else expect(toggle).not.toBeChecked();
fireEvent.click(toggle);
expect(mutate).toHaveBeenCalledWith({ disable_aria: !disabled }, expect.any(Object));
});
it("keeps configuration-owned Aria visibility read-only", () => {
mockUseUISettings.mockReturnValue(
buildSettingsResponse({ data: { values: { disable_aria: true }, source: { disable_aria: "config" } } }),
);
render(<UISettings />);
expect(screen.getByRole("switch", { name: "Hide Aria Agent for everyone" })).toBeChecked();
expect(screen.getByRole("switch", { name: "Hide Aria Agent for everyone" })).toHaveAttribute(
"aria-disabled",
"true",
);
expect(screen.getByText("Managed by the deployment configuration.")).toBeInTheDocument();
});
it("should toggle setting and call update", () => {
const mutateMock = vi.fn((_settings, options) => {
options?.onSuccess?.();

View file

@ -81,6 +81,7 @@ export default function UISettings() {
relativeTimeOptions.find((option) => option.settingValue === values.usage_default_time_range)?.settingValue ??
"last_7_days";
const usageRangeConfigOwned = data?.source?.usage_default_time_range === "config";
const ariaConfigOwned = data?.source?.disable_aria === "config";
const moyaiUrl = (values.moyai_url as string | undefined) ?? null;
const isDisabledForInternalUsers = Boolean(values.disable_model_add_for_internal_users);
const isDisabledTeamAdminDeleteTeamUser = Boolean(values.disable_team_admin_delete_team_user);
@ -364,6 +365,29 @@ export default function UISettings() {
<p className="text-sm text-muted-foreground">{t("adminPanel.uiSettings.usageRange.managedByConfig")}</p>
)}
</div>
<div className="space-y-2">
<SettingRow
checked={values.disable_aria === true}
disabled={isUpdating || ariaConfigOwned}
onCheckedChange={(checked) =>
updateSettings(
{ disable_aria: checked },
{
onSuccess: () => toast.success(t("adminPanel.uiSettings.toasts.updated")),
onError: (error) => toast.fromError(error),
},
)
}
ariaLabel={t("adminPanel.uiSettings.settings.disableAria.label")}
label={t("adminPanel.uiSettings.settings.disableAria.label")}
description={t("adminPanel.uiSettings.settings.disableAria.description")}
/>
{ariaConfigOwned && (
<p className="text-sm text-muted-foreground">
{t("adminPanel.uiSettings.settings.disableAria.managedByConfig")}
</p>
)}
</div>
<SettingRow
checked={isDisabledForInternalUsers}
disabled={isUpdating}

View file

@ -148,6 +148,7 @@ interface GatewayOptions {
read?: () => Promise<Response>;
usage?: () => Promise<Response>;
settings?: { target: string; status: number } | ((request: Request) => Promise<Response>);
uiSettings?: () => Promise<Response>;
useSiTerminology?: boolean;
}
@ -191,6 +192,7 @@ function gateway(
if (typeof settings === "function") return settings(request);
return json({ PROXY_BASE_URL: MANAGEMENT, LITELLM_UI_API_DOC_BASE_URL: settings.target }, settings.status);
}
if (path.endsWith("/get/ui_settings")) return options.uiSettings ? options.uiSettings() : json({ values: {} });
if (path.endsWith("/model_group/info"))
return json({
data: options.models ?? [
@ -254,6 +256,57 @@ afterEach(() => {
});
describe("Aria Agent in the gateway", () => {
it.each([
["proxy_admin", true],
["proxy_admin", false],
["proxy_admin_viewer", true],
["internal_user", true],
["internal_user_viewer", false],
["org_admin", true],
] as const)("hides every Aria entry point for %s with premium=%s when disabled", async (role, premium) => {
session(role, "first-admin", { premium_user: premium });
const requests = gateway([], { uiSettings: async () => json({ values: { disable_aria: true } }) });
const { client } = renderWidget(undefined, false, true);
await screen.findByText("Session ready");
await waitFor(() => expect(requests.some((request) => request.url === `${MANAGEMENT}/get/ui_settings`)).toBe(true));
await waitFor(() => expect(client.isFetching()).toBe(0));
expect(screen.queryByRole("link", { name: "Aria Agent" })).not.toBeInTheDocument();
expect(screen.queryByRole("link", { name: "Ask Aria" })).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText("Ask Aria…")).not.toBeInTheDocument();
fireEvent.keyDown(document, { key: "j", metaKey: true });
fireEvent.keyDown(document, { key: "j", ctrlKey: true });
expect(push).not.toHaveBeenCalled();
expect(requests.some((request) => request.url.endsWith("/models"))).toBe(false);
});
it("waits for settings and does not show Aria when the initial read fails", async () => {
const settings = deferred<Response>();
gateway([], { uiSettings: () => settings.promise });
const { client } = renderWidget(undefined, false, true);
await screen.findByText("Session ready");
expect(screen.queryByRole("link", { name: "Aria Agent" })).not.toBeInTheDocument();
await act(async () => settings.resolve(json({ error: "Unavailable" }, 503)));
await waitFor(() => expect(client.isFetching()).toBe(0));
expect(screen.queryByRole("link", { name: "Ask Aria" })).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText("Ask Aria…")).not.toBeInTheDocument();
});
it("waits for the destination gateway's settings instead of reusing an enabled gateway", async () => {
const settings = deferred<Response>();
gateway([], {
uiSettings: async () =>
localStorage.getItem("litellm_worker_url") ? settings.promise : json({ values: { disable_aria: false } }),
});
const view = renderWidget(undefined, false, true, { showPage: false });
await screen.findByRole("link", { name: "Ask Aria" });
switchToWorkerUrl("https://other-gateway.test");
view.refresh();
expect(screen.queryByRole("link", { name: "Aria Agent" })).not.toBeInTheDocument();
await act(async () => settings.resolve(json({ values: { disable_aria: true } })));
await waitFor(() => expect(view.client.isFetching()).toBe(0));
expect(screen.queryByRole("link", { name: "Ask Aria" })).not.toBeInTheDocument();
});
it.each([true, false])(
"keeps the answer, draft and model when returning to Aria with premium=%s",
async (premium) => {
@ -643,7 +696,7 @@ describe("Aria Agent in the gateway", () => {
const { client } = renderWidget(undefined, true);
await screen.findByText("Session ready");
await waitFor(() => expect(client.isFetching()).toBe(0));
expect(screen.getByRole("link", { name: "Aria Agent" })).toBeVisible();
expect(await screen.findByRole("link", { name: "Aria Agent" })).toBeVisible();
expect(screen.queryByRole("region", { name: "Ask Aria Agent", hidden: true })).not.toBeInTheDocument();
await waitFor(() => expect(screen.getByPlaceholderText("Ask Aria…")).toBeEnabled());
act(() => send("Show my usage"));
@ -1040,19 +1093,20 @@ describe("Aria Agent in the gateway", () => {
});
it.each(
["account", "gateway", "destination", "permission"].flatMap((transition) =>
["account", "gateway", "destination", "permission", "visibility"].flatMap((transition) =>
["pending", "approval", "draft"].map((stage) => [transition, stage] as const),
),
)("should clear the conversation when %s changes during %s", async (transition, stage) => {
const pending = deferred<ModelReply>();
const settings = { target: INFERENCE, status: 200 };
const visibility = { disable_aria: false };
const replies = [
answer("Old answer."),
...(stage === "pending" ? [pending.promise] : []),
...(stage === "approval" ? [toolReply("key_create", keyArguments)] : []),
answer("Fresh session."),
];
const requests = gateway(replies, { settings });
const requests = gateway(replies, { settings, uiSettings: async () => json({ values: visibility }) });
const view = renderWidget(undefined, true);
await openWidget();
act(() => send("Old question"));
@ -1079,6 +1133,13 @@ describe("Aria Agent in the gateway", () => {
} else if (transition === "destination") {
settings.target = INFERENCE + "/updated";
await act(async () => view.client.invalidateQueries({ queryKey: ["proxySettings"] }));
} else if (transition === "visibility") {
visibility.disable_aria = true;
await act(async () => view.client.invalidateQueries({ queryKey: ["uiSettings"] }));
await waitFor(() => expect(screen.queryByRole("link", { name: "Aria Agent" })).not.toBeInTheDocument());
expect(screen.queryByPlaceholderText("Ask Aria…")).not.toBeInTheDocument();
visibility.disable_aria = false;
await act(async () => view.client.invalidateQueries({ queryKey: ["uiSettings"] }));
} else {
session("proxy_admin_viewer", "first-admin", { premium_user: true }, "sk-session-first-admin-viewer");
view.refresh();

View file

@ -22,6 +22,7 @@ import { RotateCcw } from "lucide-react";
import { z } from "zod";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useDisableLiteAdmin } from "@/app/(dashboard)/hooks/useDisableLiteAdmin";
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
import { useProxySettingsQuery } from "@/app/(dashboard)/hooks/proxySettings/useProxySettings";
import { ChatComposer } from "@/app/(dashboard)/playground/components/chat_ui/ChatComposer";
import { EndpointType, isModeCompatibleWithEndpoint } from "@/components/chat_ui/mode_endpoint_mapping";
@ -56,7 +57,9 @@ function useLiteAdminSession() {
const auth = useAuthorized();
const [disabled] = useDisableLiteAdmin(auth.userId);
const sessionReady = !auth.isLoading && auth.isAuthorized;
const { data: uiSettings } = useUISettings({ staleTime: 0, refetchInterval: 30_000, enabled: sessionReady });
const admin = auth.premiumUser === true && !auth.isViewOnly && isProxyAdminRole(auth.userRole);
if (!uiSettings || uiSettings.values?.disable_aria === true) return null;
if (!sessionReady || (admin && disabled)) return null;
if (!auth.token || !auth.accessToken || !auth.userId) return null;
const access: LiteAdminSession["access"] = admin

View file

@ -6802,8 +6802,7 @@ export const exchangeLoginCode = async (code: string, workerBaseUrl?: string | n
return data.token;
};
export const getUiSettings = async () => {
const proxyBaseUrl = getProxyBaseUrl();
export const getUiSettings = async (proxyBaseUrl = getProxyBaseUrl()) => {
const url = proxyBaseUrl ? `${proxyBaseUrl}/get/ui_settings` : `/get/ui_settings`;
const response = await fetch(url, {
method: "GET",

View file

@ -38,6 +38,8 @@ vi.mock("@/app/(dashboard)/hooks/keys/useResetKeySpend", () => ({
}));
vi.mock("../networking", () => ({
serverRootPath: "",
getProxyBaseUrl: vi.fn(() => ""),
getUiSettings: vi.fn().mockResolvedValue({ values: {} }),
keyDeleteCall: vi.fn().mockResolvedValue({}),
keyUpdateCall: vi.fn().mockResolvedValue({}),
getPolicyInfoWithGuardrails: vi.fn().mockResolvedValue({ resolved_guardrails: [] }),

View file

@ -98,6 +98,8 @@ import { useMCPToolsets } from "@/app/(dashboard)/hooks/mcpServers/useMCPToolset
vi.mock("../networking", () => ({
serverRootPath: "",
getProxyBaseUrl: vi.fn(() => ""),
getUiSettings: vi.fn().mockResolvedValue({ values: {} }),
keyDeleteCall: vi.fn().mockResolvedValue({}),
keyUpdateCall: vi.fn().mockResolvedValue({}),
regenerateKeyCall: vi.fn().mockResolvedValue({ key: "sk-regenerated", token: "regenerated-token" }),

View file

@ -5212,6 +5212,11 @@
"managedByConfig": "Managed by the deployment configuration."
},
"settings": {
"disableAria": {
"label": "Hide Aria Agent for everyone",
"description": "Hides Aria from navigation, Usage, and its workspace for all users, including admins. Active dashboards check for changes every 30 seconds.",
"managedByConfig": "Managed by the deployment configuration."
},
"disableModelAdd": {
"label": "Disable model add for internal users"
},

View file

@ -4663,9 +4663,20 @@ class TestMoyaiUrlSetting:
assert persisted["moyai_url"] is None
@pytest.mark.parametrize("preset", ["today", "last_7_days", "last_30_days", "month_to_date", "year_to_date"])
def test_usage_default_range_round_trips_without_overwriting_other_settings(
mock_auth, monkeypatch: pytest.MonkeyPatch, preset: str,
@pytest.mark.parametrize("setting,initial_value,submitted,default", (
("usage_default_time_range", None, "today", "last_7_days"),
("usage_default_time_range", None, "last_7_days", "last_7_days"),
("usage_default_time_range", None, "last_30_days", "last_7_days"),
("usage_default_time_range", None, "month_to_date", "last_7_days"),
("usage_default_time_range", None, "year_to_date", "last_7_days"),
("disable_aria", None, False, False),
("disable_aria", None, True, False),
("disable_aria", False, True, False),
("disable_aria", True, False, False),
))
def test_ui_setting_round_trips_without_overwriting_other_settings(
mock_auth: None, monkeypatch: pytest.MonkeyPatch, setting: str,
initial_value: str | bool | None, submitted: str | bool, default: str | bool,
) -> None:
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock
@ -4676,7 +4687,7 @@ def test_usage_default_range_round_trips_without_overwriting_other_settings(
prisma: Final = MagicMock()
prisma.db.litellm_uisettings.find_unique = AsyncMock(return_value=SimpleNamespace(
ui_settings={"disable_custom_api_keys": True},
ui_settings={"disable_custom_api_keys": True, **({} if initial_value is None else {setting: initial_value})},
))
prisma.db.litellm_uisettings.upsert = AsyncMock()
monkeypatch.setattr(proxy_server, "prisma_client", prisma)
@ -4686,22 +4697,28 @@ def test_usage_default_range_round_trips_without_overwriting_other_settings(
app.dependency_overrides[user_api_key_auth] = lambda: actor
try:
initial: Final = client.get("/get/ui_settings")
assert initial.json()["values"]["usage_default_time_range"] == "last_7_days"
response: Final = client.patch("/update/ui_settings", json={"usage_default_time_range": preset})
assert initial.status_code == 200, initial.text
assert initial.json()["values"][setting] == (default if initial_value is None else initial_value)
assert initial.json()["source"][setting] == ("default" if initial_value is None else "db")
response: Final = client.patch("/update/ui_settings", json={setting: submitted})
assert response.status_code == 200, response.text
stored: Final = json.loads(prisma.db.litellm_uisettings.upsert.call_args.kwargs["data"]["update"]["ui_settings"])
assert stored == {"disable_custom_api_keys": True, "usage_default_time_range": preset}
prisma.db.litellm_uisettings.find_unique.return_value = SimpleNamespace(ui_settings=stored)
assert stored == {"disable_custom_api_keys": True, setting: submitted}
prisma.db.litellm_uisettings.find_unique.return_value = SimpleNamespace(ui_settings=json.dumps(stored))
fetched: Final = client.get("/get/ui_settings")
assert fetched.json()["values"]["usage_default_time_range"] == preset
assert fetched.json()["source"]["usage_default_time_range"] == "db"
assert fetched.status_code == 200, fetched.text
assert fetched.json()["values"][setting] == submitted
assert fetched.json()["values"]["disable_custom_api_keys"] is True
assert fetched.json()["source"][setting] == "db"
assert "disable_aria" not in proxy_server.general_settings
finally:
app.dependency_overrides.clear()
@pytest.mark.parametrize("submitted", ["invalid", None, 7])
def test_usage_default_range_rejects_invalid_values_without_writing(
mock_auth, monkeypatch: pytest.MonkeyPatch, submitted: object,
@pytest.mark.parametrize("setting", ("usage_default_time_range", "disable_aria"))
def test_ui_setting_rejects_invalid_values_without_writing(
mock_auth: None, monkeypatch: pytest.MonkeyPatch, submitted: object, setting: str,
) -> None:
from unittest.mock import AsyncMock, MagicMock
from litellm.proxy import proxy_server
@ -4709,13 +4726,14 @@ def test_usage_default_range_rejects_invalid_values_without_writing(
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
prisma: Final = MagicMock()
prisma.db.litellm_uisettings.find_unique = AsyncMock(return_value=None)
prisma.db.litellm_uisettings.upsert = AsyncMock()
monkeypatch.setattr(proxy_server, "prisma_client", prisma)
monkeypatch.setattr(proxy_server, "store_model_in_db", True)
actor: Final = UserAPIKeyAuth(user_id="test-admin", user_role=LitellmUserRoles.PROXY_ADMIN)
app.dependency_overrides[user_api_key_auth] = lambda: actor
try:
response: Final = client.patch("/update/ui_settings", json={"usage_default_time_range": submitted})
response: Final = client.patch("/update/ui_settings", json={setting: submitted})
assert response.status_code == 422, response.text
prisma.db.litellm_uisettings.upsert.assert_not_called()
finally:
@ -4723,8 +4741,9 @@ def test_usage_default_range_rejects_invalid_values_without_writing(
@pytest.mark.parametrize("role", [LitellmUserRoles.INTERNAL_USER, LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY])
def test_usage_default_range_cannot_be_changed_by_non_admins(
mock_auth, monkeypatch: pytest.MonkeyPatch, role: LitellmUserRoles,
@pytest.mark.parametrize("setting,value", (("usage_default_time_range", "month_to_date"), ("disable_aria", True)))
def test_ui_setting_cannot_be_changed_by_non_admins(
mock_auth: None, monkeypatch: pytest.MonkeyPatch, role: LitellmUserRoles, setting: str, value: str | bool,
) -> None:
from unittest.mock import AsyncMock, MagicMock
from litellm.proxy import proxy_server
@ -4738,14 +4757,21 @@ def test_usage_default_range_cannot_be_changed_by_non_admins(
actor: Final = UserAPIKeyAuth(user_id="test-user", user_role=role)
app.dependency_overrides[user_api_key_auth] = lambda: actor
try:
response: Final = client.patch("/update/ui_settings", json={"usage_default_time_range": "month_to_date"})
response: Final = client.patch("/update/ui_settings", json={setting: value})
assert response.status_code == 403, response.text
prisma.db.litellm_uisettings.upsert.assert_not_called()
finally:
app.dependency_overrides.clear()
def test_usage_default_range_reports_config_precedence(mock_auth, monkeypatch: pytest.MonkeyPatch) -> None:
@pytest.mark.parametrize("setting,stored,configured", (
("usage_default_time_range", "today", "month_to_date"),
("disable_aria", False, True),
("disable_aria", True, False),
))
def test_ui_setting_reports_config_precedence(
mock_auth: None, monkeypatch: pytest.MonkeyPatch, setting: str, stored: str | bool, configured: str | bool,
) -> None:
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock
from litellm.proxy import proxy_server
@ -4753,16 +4779,16 @@ def test_usage_default_range_reports_config_precedence(mock_auth, monkeypatch: p
prisma: Final = MagicMock()
prisma.db.litellm_uisettings.find_unique = AsyncMock(return_value=SimpleNamespace(
ui_settings={"usage_default_time_range": "today"},
ui_settings={setting: stored},
))
monkeypatch.setattr(proxy_server, "prisma_client", prisma)
settings: Final = SettingsStore("general_settings")
settings.load_yaml({"usage_default_time_range": "month_to_date"})
settings.load_yaml({setting: configured})
monkeypatch.setattr(proxy_server.proxy_config, "settings", settings)
response: Final = client.get("/get/ui_settings")
assert response.status_code == 200, response.text
assert response.json()["values"]["usage_default_time_range"] == "month_to_date"
assert response.json()["source"]["usage_default_time_range"] == "config"
assert response.json()["values"][setting] == configured
assert response.json()["source"][setting] == "config"
@pytest.fixture