Spend logs setting modal

This commit is contained in:
yuneng-jiang 2026-01-27 23:34:50 -08:00
parent ab655ef296
commit 58eca8fb28
3 changed files with 179 additions and 1 deletions

View file

@ -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<StoreRequestInSpendLogsResponse> => {
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<StoreRequestInSpendLogsResponse, Error, StoreRequestInSpendLogsParams>({
mutationFn: async (params: StoreRequestInSpendLogsParams) => {
if (!accessToken) {
throw new Error("Access token is required");
}
return await performStoreRequestInSpendLogs(accessToken, params);
},
});
};

View file

@ -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<SpendLogsSettingsModalProps> = ({ 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 (
<Modal
title="Spend Logs Settings"
open={isVisible}
width={600}
footer={
<Space>
<Button onClick={handleCancel} disabled={isPending}>
Cancel
</Button>
<Button type="primary" loading={isPending} onClick={() => form.submit()}>
{isPending ? "Saving..." : "Save Settings"}
</Button>
</Space>
}
onCancel={handleCancel}
>
<Form
form={form}
layout="horizontal"
labelCol={{ flex: "auto", style: { textAlign: "left" } }}
wrapperCol={{ flex: "auto", style: { textAlign: "right" } }}
onFinish={handleFormSubmit}
initialValues={{
store_prompts_in_spend_logs: false,
maximum_spend_logs_retention_period: undefined,
}}
>
<Form.Item
name="store_prompts_in_spend_logs"
tooltip="When enabled, prompts will be stored in spend logs for tracking and analysis purposes."
valuePropName="checked"
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>Store Prompts in Spend Logs</span>
<Switch checked={storePromptsValue ?? false} onChange={(checked) => form.setFieldValue('store_prompts_in_spend_logs', checked)} />
</div>
</Form.Item>
<Form.Item
label="Maximum Spend Logs Retention Period (Optional)"
name="maximum_spend_logs_retention_period"
tooltip="Set the maximum retention period for spend logs (e.g., '7d' for 7 days, '30d' for 30 days). Leave empty for no limit."
labelCol={{ flex: "auto", style: { textAlign: "left" } }}
wrapperCol={{ flex: "0 0 25%", style: { textAlign: "right" } }}
>
<Input
placeholder="e.g., 7d, 30d"
prefix={<ClockCircleOutlined />}
/>
</Form.Item>
</Form>
</Modal>
);
};
export default SpendLogsSettingsModal;

View file

@ -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<string | null>(null);
const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null);
const [isSpendLogsSettingsModalVisible, setIsSpendLogsSettingsModalVisible] = useState(false);
const queryClient = useQueryClient();
@ -526,6 +529,13 @@ export default function SpendLogsTable({
"Request Logs"
)}
</h1>
{!selectedSessionId && (
<Button
icon={<SettingOutlined />}
onClick={() => setIsSpendLogsSettingsModalVisible(true)}
title="Spend Logs Settings"
/>
)}
</div>
{selectedKeyInfo && selectedKeyIdInfoView && selectedKeyInfo.api_key === selectedKeyIdInfoView ? (
<KeyInfoView
@ -552,6 +562,11 @@ export default function SpendLogsTable({
onApplyFilters={handleFilterChange}
onResetFilters={handleFilterReset}
/>
<SpendLogsSettingsModal
isVisible={isSpendLogsSettingsModalVisible}
onCancel={() => setIsSpendLogsSettingsModalVisible(false)}
onSuccess={() => setIsSpendLogsSettingsModalVisible(false)}
/>
<div className="bg-white rounded-lg shadow w-full max-w-full box-border">
<div className="border-b px-6 py-4 w-full max-w-full box-border">
<div className="flex flex-col md:flex-row items-start md:items-center justify-between space-y-4 md:space-y-0 w-full max-w-full box-border">