From 58eca8fb28587512e2b6ce9825298dc9b52a7274 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Tue, 27 Jan 2026 23:34:50 -0800 Subject: [PATCH] Spend logs setting modal --- .../useStoreRequestInSpendLogs.ts | 63 +++++++++++ .../SpendLogsSettingsModal.tsx | 100 ++++++++++++++++++ .../src/components/view_logs/index.tsx | 17 ++- 3 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/hooks/storeRequestInSpendLogs/useStoreRequestInSpendLogs.ts create mode 100644 ui/litellm-dashboard/src/components/view_logs/SpendLogsSettingsModal/SpendLogsSettingsModal.tsx diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/storeRequestInSpendLogs/useStoreRequestInSpendLogs.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/storeRequestInSpendLogs/useStoreRequestInSpendLogs.ts new file mode 100644 index 00000000000..9c6211c3086 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/storeRequestInSpendLogs/useStoreRequestInSpendLogs.ts @@ -0,0 +1,63 @@ +import { useMutation, UseMutationResult } from "@tanstack/react-query"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; +import useAuthorized from "../useAuthorized"; + +export interface StoreRequestInSpendLogsParams { + store_prompts_in_spend_logs: boolean; + maximum_spend_logs_retention_period?: string; +} + +export interface StoreRequestInSpendLogsResponse { + message: string; +} + +const performStoreRequestInSpendLogs = async ( + accessToken: string, + params: StoreRequestInSpendLogsParams +): Promise => { + const proxyBaseUrl = getProxyBaseUrl(); + const url = proxyBaseUrl ? `${proxyBaseUrl}/config/update` : `/config/update`; + + const response = await fetch(url, { + method: "POST", + headers: { + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + general_settings: { + store_prompts_in_spend_logs: params.store_prompts_in_spend_logs, + ...(params.maximum_spend_logs_retention_period && { + maximum_spend_logs_retention_period: params.maximum_spend_logs_retention_period, + }), + }, + }), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + const errorMessage = + errorData?.error?.message || errorData?.message || errorData?.detail || "Failed to update spend logs settings"; + throw new Error(errorMessage); + } + + const data = await response.json(); + return data; +}; + +export const useStoreRequestInSpendLogs = (): UseMutationResult< + StoreRequestInSpendLogsResponse, + Error, + StoreRequestInSpendLogsParams +> => { + const { accessToken } = useAuthorized(); + + return useMutation({ + mutationFn: async (params: StoreRequestInSpendLogsParams) => { + if (!accessToken) { + throw new Error("Access token is required"); + } + return await performStoreRequestInSpendLogs(accessToken, params); + }, + }); +}; diff --git a/ui/litellm-dashboard/src/components/view_logs/SpendLogsSettingsModal/SpendLogsSettingsModal.tsx b/ui/litellm-dashboard/src/components/view_logs/SpendLogsSettingsModal/SpendLogsSettingsModal.tsx new file mode 100644 index 00000000000..cf8e51be94b --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/SpendLogsSettingsModal/SpendLogsSettingsModal.tsx @@ -0,0 +1,100 @@ +"use client"; + +import { StoreRequestInSpendLogsParams, useStoreRequestInSpendLogs } from "@/app/(dashboard)/hooks/storeRequestInSpendLogs/useStoreRequestInSpendLogs"; +import NotificationsManager from "@/components/molecules/notifications_manager"; +import { parseErrorMessage } from "@/components/shared/errorUtils"; +import { ClockCircleOutlined } from "@ant-design/icons"; +import { Button, Form, Input, Modal, Space, Switch } from "antd"; +import React from "react"; + +interface SpendLogsSettingsModalProps { + isVisible: boolean; + onCancel: () => void; + onSuccess?: () => void; +} + +const SpendLogsSettingsModal: React.FC = ({ isVisible, onCancel, onSuccess }) => { + const [form] = Form.useForm(); + const { mutateAsync, isPending } = useStoreRequestInSpendLogs(); + const storePromptsValue = Form.useWatch('store_prompts_in_spend_logs', form); + + const handleFormSubmit = async (formValues: StoreRequestInSpendLogsParams) => { + try { + await mutateAsync(formValues, { + onSuccess: () => { + NotificationsManager.success("Spend logs settings updated successfully"); + form.resetFields(); + onSuccess?.(); + }, + onError: (error) => { + NotificationsManager.fromBackend("Failed to save spend logs settings: " + parseErrorMessage(error)); + }, + }); + } catch (error) { + NotificationsManager.fromBackend("Failed to save spend logs settings: " + parseErrorMessage(error)); + } + }; + + const handleCancel = () => { + form.resetFields(); + onCancel(); + }; + + return ( + + + + + } + onCancel={handleCancel} + > +
+ +
+ Store Prompts in Spend Logs + form.setFieldValue('store_prompts_in_spend_logs', checked)} /> +
+
+ + + + } + /> + +
+
+ ); +}; + +export default SpendLogsSettingsModal; diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index 904d052073a..e25925f13b8 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -12,7 +12,7 @@ import { RequestResponsePanel } from "./RequestResponsePanel"; import { ErrorViewer } from "./ErrorViewer"; import { internalUserRoles } from "../../utils/roles"; import { ConfigInfoMessage } from "./ConfigInfoMessage"; -import { Tooltip } from "antd"; +import { Button, Tooltip } from "antd"; import { KeyResponse, Team } from "../key_team_helpers/key_list"; import KeyInfoView from "../templates/key_info_view"; import { SessionView } from "./SessionView"; @@ -31,6 +31,8 @@ import { truncateString } from "@/utils/textUtils"; import DeletedKeysPage from "../DeletedKeysPage/DeletedKeysPage"; import DeletedTeamsPage from "../DeletedTeamsPage/DeletedTeamsPage"; import NewBadge from "../common_components/NewBadge"; +import SpendLogsSettingsModal from "./SpendLogsSettingsModal/SpendLogsSettingsModal"; +import { SettingOutlined } from "@ant-design/icons"; interface SpendLogsTableProps { accessToken: string | null; @@ -91,6 +93,7 @@ export default function SpendLogsTable({ const [expandedRequestId, setExpandedRequestId] = useState(null); const [selectedSessionId, setSelectedSessionId] = useState(null); + const [isSpendLogsSettingsModalVisible, setIsSpendLogsSettingsModalVisible] = useState(false); const queryClient = useQueryClient(); @@ -526,6 +529,13 @@ export default function SpendLogsTable({ "Request Logs" )} + {!selectedSessionId && ( +