Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/litellm-dashboard/eslint-metrics.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"@typescript-eslint/no-explicit-any": 1991,
"@typescript-eslint/no-explicit-any": 1990,
"complexity": 128,
"max-depth": 59,
"no-console": 15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@
mockUseStoreRequestInSpendLogs.mockReturnValue({
mutate: mockMutate,
isPending: false,
} as any);

Check warning on line 48 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
mockUseDeleteProxyConfigField.mockReturnValue({
mutate: mockDeleteField,
isPending: false,
} as any);

Check warning on line 52 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
mockUseProxyConfig.mockReturnValue({
data: [],
isLoading: false,
refetch: mockRefetch,
} as any);

Check warning on line 57 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
mockParseErrorMessage.mockImplementation((error: any) => error?.message || String(error));

Check warning on line 58 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type
});

it("should render the card with title and form fields", () => {
Expand Down Expand Up @@ -193,7 +193,7 @@
mockUseStoreRequestInSpendLogs.mockReturnValue({
mutate: mockMutate,
isPending: true,
} as any);

Check warning on line 196 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type

renderWithProviders(<LoggingSettings />);

Expand All @@ -206,7 +206,7 @@
mockUseDeleteProxyConfigField.mockReturnValue({
mutate: mockDeleteField,
isPending: true,
} as any);

Check warning on line 209 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type

renderWithProviders(<LoggingSettings />);

Expand All @@ -215,19 +215,6 @@
expect(saveButton.className).toContain("ant-btn-loading");
});

it("should disable save button while config is loading", () => {
mockUseProxyConfig.mockReturnValue({
data: undefined,
isLoading: true,
refetch: mockRefetch,
} as any);

renderWithProviders(<LoggingSettings />);

const saveButton = screen.getByRole("button", { name: "Save Settings" });
expect(saveButton).toBeDisabled();
});

it("should render form with initial values from config data", () => {
mockUseProxyConfig.mockReturnValue({
data: [
Expand All @@ -250,7 +237,7 @@
],
isLoading: false,
refetch: mockRefetch,
} as any);

Check warning on line 240 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type

renderWithProviders(<LoggingSettings />);

Expand All @@ -261,17 +248,60 @@
expect(retentionInput).toHaveValue("30d");
});

it("should reflect persisted values that arrive after the initial loading render", async () => {
mockUseProxyConfig.mockReturnValue({
data: undefined,
isLoading: true,
refetch: mockRefetch,
} as unknown as ReturnType<typeof useProxyConfig>);

const { rerender } = renderWithProviders(<LoggingSettings />);

expect(screen.queryByRole("switch")).not.toBeInTheDocument();

mockUseProxyConfig.mockReturnValue({
data: [
{
field_name: "store_prompts_in_spend_logs",
field_type: "bool",
field_description: "Store prompts in spend logs",
field_value: true,
stored_in_db: true,
field_default_value: false,
},
{
field_name: "maximum_spend_logs_retention_period",
field_type: "string",
field_description: "Maximum retention period",
field_value: "30d",
stored_in_db: true,
field_default_value: undefined,
},
],
isLoading: false,
refetch: mockRefetch,
} as unknown as ReturnType<typeof useProxyConfig>);

rerender(<LoggingSettings />);

await waitFor(() => {
expect(screen.getByRole("switch")).toBeChecked();
});
expect(screen.getByPlaceholderText("e.g., 7d, 30d")).toHaveValue("30d");
});

it("should show skeleton loaders when config is loading", () => {
mockUseProxyConfig.mockReturnValue({
data: undefined,
isLoading: true,
refetch: mockRefetch,
} as any);

Check warning on line 298 in ui/litellm-dashboard/src/components/Settings/AdminSettings/LoggingSettings/LoggingSettings.test.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Unexpected any. Specify a different type

renderWithProviders(<LoggingSettings />);

expect(screen.queryByRole("switch")).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText("e.g., 7d, 30d")).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Save Settings" })).not.toBeInTheDocument();

const skeletons = document.querySelectorAll(".ant-skeleton");
expect(skeletons.length).toBeGreaterThan(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const LoggingSettings: React.FC = () => {
const { mutate, isPending } = useStoreRequestInSpendLogs();
const { mutate: deleteField, isPending: isDeletingField } = useDeleteProxyConfigField();
const { data: proxyConfigData, isLoading: isLoadingConfig } = useProxyConfig(ConfigType.GENERAL_SETTINGS);
const storePromptsValue = Form.useWatch("store_prompts_in_spend_logs", form);

const initialValues = useMemo(() => {
if (!proxyConfigData) {
Expand Down Expand Up @@ -82,53 +81,41 @@ const LoggingSettings: React.FC = () => {
Proxy-wide settings that control how request and response data are written to spend logs.
</Typography.Paragraph>

<Form
key={proxyConfigData ? JSON.stringify(initialValues) : "loading"}
form={form}
layout="vertical"
onFinish={handleFormSubmit}
initialValues={initialValues}
>
<Form.Item
label="Store Prompts in Spend Logs"
name="store_prompts_in_spend_logs"
tooltip={
proxyConfigData?.find((f) => f.field_name === "store_prompts_in_spend_logs")?.field_description ||
"When enabled, prompts will be stored in spend logs for tracking and analysis purposes."
}
valuePropName="checked"
>
{isLoadingConfig ? (
<Skeleton.Input active block />
) : (
<Switch
checked={storePromptsValue ?? false}
onChange={(checked) => form.setFieldValue("store_prompts_in_spend_logs", checked)}
/>
)}
</Form.Item>
{isLoadingConfig ? (
<Skeleton active paragraph={{ rows: 4 }} />
) : (
<Form form={form} layout="vertical" onFinish={handleFormSubmit} initialValues={initialValues}>
<Form.Item
label="Store Prompts in Spend Logs"
name="store_prompts_in_spend_logs"
tooltip={
proxyConfigData?.find((f) => f.field_name === "store_prompts_in_spend_logs")?.field_description ||
"When enabled, prompts will be stored in spend logs for tracking and analysis purposes."
}
valuePropName="checked"
>
<Switch />
</Form.Item>

<Form.Item
label="Maximum Spend Logs Retention Period (Optional)"
name="maximum_spend_logs_retention_period"
tooltip={
proxyConfigData?.find((f) => f.field_name === "maximum_spend_logs_retention_period")?.field_description ||
"Set the maximum retention period for spend logs (e.g., '7d' for 7 days, '30d' for 30 days). Leave empty for no limit."
}
>
{isLoadingConfig ? (
<Skeleton.Input active block />
) : (
<Form.Item
label="Maximum Spend Logs Retention Period (Optional)"
name="maximum_spend_logs_retention_period"
tooltip={
proxyConfigData?.find((f) => f.field_name === "maximum_spend_logs_retention_period")
?.field_description ||
"Set the maximum retention period for spend logs (e.g., '7d' for 7 days, '30d' for 30 days). Leave empty for no limit."
}
>
<Input placeholder="e.g., 7d, 30d" prefix={<ClockCircleOutlined />} />
)}
</Form.Item>
</Form.Item>

<Form.Item>
<Button type="primary" htmlType="submit" loading={isPending || isDeletingField} disabled={isLoadingConfig}>
{isPending || isDeletingField ? "Saving..." : "Save Settings"}
</Button>
</Form.Item>
</Form>
<Form.Item>
<Button type="primary" htmlType="submit" loading={isPending || isDeletingField}>
{isPending || isDeletingField ? "Saving..." : "Save Settings"}
</Button>
</Form.Item>
</Form>
)}
</Space>
</Card>
);
Expand Down
Loading