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
Original file line number Diff line number Diff line change
@@ -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);
},
});
};
32 changes: 27 additions & 5 deletions ui/litellm-dashboard/src/components/page_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ import { describe, it, expect } from "vitest";
import { getAvailablePages } from "./page_utils";
import { menuGroups } from "./leftnav";
import { pageDescriptions } from "./page_metadata";
import { internalUserRoles } from "@/utils/roles";

/**
* Check if a page is accessible to internal users
* A page is accessible if:
* 1. It has no role restrictions, OR
* 2. Its roles include at least one internal user role
*/
const isPageAccessibleToInternalUsers = (pageRoles?: string[]): boolean => {
if (!pageRoles || pageRoles.length === 0) {
return true; // No role restrictions
}

// Check if any of the page's roles match internal user roles
return pageRoles.some(role => internalUserRoles.includes(role));
};

describe("Page Utils - LeftNav Sync", () => {
it("should return all pages from leftnav configuration", () => {
Expand All @@ -32,26 +48,32 @@ describe("Page Utils - LeftNav Sync", () => {
const availablePages = getAvailablePages();
const availablePageKeys = availablePages.map((p) => p.page);

// Collect all page keys from menuGroups (excluding parent containers)
// Collect all page keys from menuGroups (excluding parent containers and pages not accessible to internal users)
const menuPageKeys: string[] = [];
const excludedParents = ["tools", "experimental", "settings"];

menuGroups.forEach((group) => {
group.items.forEach((item) => {
if (item.page && !excludedParents.includes(item.page)) {
if (
item.page &&
!excludedParents.includes(item.page) &&
isPageAccessibleToInternalUsers(item.roles)
) {
menuPageKeys.push(item.page);
}

// Add children
// Add children (only if accessible to internal users)
if (item.children) {
item.children.forEach((child) => {
menuPageKeys.push(child.page);
if (isPageAccessibleToInternalUsers(child.roles)) {
menuPageKeys.push(child.page);
}
});
}
});
});

// Every menu page should be in available pages
// Every menu page accessible to internal users should be in available pages
menuPageKeys.forEach((pageKey) => {
expect(
availablePageKeys,
Expand Down
Loading
Loading