mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
Merge 7d89bc59a4 into bb4f7211d7
This commit is contained in:
commit
fb5ff7f4cd
17 changed files with 334 additions and 39 deletions
|
|
@ -5,6 +5,7 @@ import prettier from "eslint-config-prettier/flat";
|
|||
import unusedImports from "eslint-plugin-unused-imports";
|
||||
import testingLibrary from "eslint-plugin-testing-library";
|
||||
import jestDom from "eslint-plugin-jest-dom";
|
||||
import pluginQuery from "@tanstack/eslint-plugin-query";
|
||||
import local from "./scripts/eslint-rules/index.mjs";
|
||||
|
||||
const eslintConfig = [
|
||||
|
|
@ -14,10 +15,12 @@ const eslintConfig = [
|
|||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
...nextCoreWebVitals,
|
||||
...pluginQuery.configs["flat/recommended"],
|
||||
prettier,
|
||||
{
|
||||
plugins: { "unused-imports": unusedImports, local },
|
||||
rules: {
|
||||
"@tanstack/query/exhaustive-deps": ["error", { allowlist: { variables: ["accessToken", "apiClient", "demo"] } }],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"local/no-large-inline-object-arg": "warn",
|
||||
"local/no-long-condition-chain": "warn",
|
||||
|
|
|
|||
178
ui/litellm-dashboard/package-lock.json
generated
178
ui/litellm-dashboard/package-lock.json
generated
|
|
@ -60,6 +60,7 @@
|
|||
"@eslint/js": "9.39.2",
|
||||
"@tailwindcss/forms": "0.5.11",
|
||||
"@tailwindcss/postcss": "4.3.2",
|
||||
"@tanstack/eslint-plugin-query": "5.104.0",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "6.9.1",
|
||||
"@testing-library/react": "16.3.2",
|
||||
|
|
@ -3440,6 +3441,183 @@
|
|||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query": {
|
||||
"version": "5.104.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.104.0.tgz",
|
||||
"integrity": "sha512-WHStlNTKraESaAjeuZ0zJr+t3ZigZgAJ/FQX1sdLGSF+U0vXlHgcAoPqm27VixMRyXaFHCx9xBJiUmPNcxMpjQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/utils": "^8.70.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": "^5.6.0 || ^6.0.0 || ^7.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.71.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.0.tgz",
|
||||
"integrity": "sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.71.0",
|
||||
"@typescript-eslint/types": "^8.71.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.71.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.0.tgz",
|
||||
"integrity": "sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.0",
|
||||
"@typescript-eslint/visitor-keys": "8.71.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.71.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.0.tgz",
|
||||
"integrity": "sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/types": {
|
||||
"version": "8.71.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.0.tgz",
|
||||
"integrity": "sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.71.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.0.tgz",
|
||||
"integrity": "sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.71.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.71.0",
|
||||
"@typescript-eslint/types": "8.71.0",
|
||||
"@typescript-eslint/visitor-keys": "8.71.0",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.71.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.0.tgz",
|
||||
"integrity": "sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.71.0",
|
||||
"@typescript-eslint/types": "8.71.0",
|
||||
"@typescript-eslint/typescript-estree": "8.71.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.71.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.0.tgz",
|
||||
"integrity": "sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.0",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/eslint-visitor-keys": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
|
||||
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/pacer": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/pacer/-/pacer-0.21.1.tgz",
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@
|
|||
"@eslint/js": "9.39.2",
|
||||
"@tailwindcss/forms": "0.5.11",
|
||||
"@tailwindcss/postcss": "4.3.2",
|
||||
"@tanstack/eslint-plugin-query": "5.104.0",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "6.9.1",
|
||||
"@testing-library/react": "16.3.2",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start
|
|||
error: detailError,
|
||||
} = useGuardrailsUsageDetail(guardrailId, { accessToken, startDate, endDate });
|
||||
const { data: logsData, isLoading: logsLoading } = useQuery({
|
||||
queryKey: ["guardrails-usage-logs", guardrailId, logsPage, logsPageSize],
|
||||
queryKey: ["guardrails-usage-logs", guardrailId, logsPage, logsPageSize, startDate, endDate],
|
||||
queryFn: () =>
|
||||
getGuardrailsUsageLogs(accessToken!, {
|
||||
guardrailId,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,14 @@ export const useMCPServerHealth = () => {
|
|||
);
|
||||
|
||||
return {
|
||||
...query,
|
||||
data: query.data,
|
||||
error: query.error,
|
||||
isError: query.isError,
|
||||
isFetching: query.isFetching,
|
||||
isLoading: query.isLoading,
|
||||
isSuccess: query.isSuccess,
|
||||
refetch: query.refetch,
|
||||
status: query.status,
|
||||
recheckServerHealth,
|
||||
recheckingServerIds,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -621,6 +621,30 @@ describe("useSelectedTeamModels", () => {
|
|||
expect(modelAvailableCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps team model results in separate cache entries", async () => {
|
||||
const teamOneResponse = { data: [{ ...mockProxyModel, id: "team-1-model" }] };
|
||||
const teamTwoResponse = { data: [{ ...mockProxyModel, id: "team-2-model" }] };
|
||||
(modelAvailableCall as any).mockResolvedValueOnce(teamOneResponse).mockResolvedValueOnce(teamTwoResponse);
|
||||
|
||||
const { result, rerender } = renderHook(({ teamID }: { teamID: string }) => useSelectedTeamModels(teamID), {
|
||||
wrapper,
|
||||
initialProps: { teamID: "team-1" },
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(teamOneResponse));
|
||||
|
||||
rerender({ teamID: "team-2" });
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(teamTwoResponse));
|
||||
expect(modelAvailableCall).toHaveBeenCalledTimes(2);
|
||||
expect(
|
||||
queryClient
|
||||
.getQueriesData<AllProxyModelsResponse>({ queryKey: ["selectedTeamModels", "list"] })
|
||||
.map(([, data]) => data?.data[0]?.id)
|
||||
.sort(),
|
||||
).toEqual(["team-1-model", "team-2-model"]);
|
||||
});
|
||||
|
||||
it("should handle error when modelAvailableCall fails", async () => {
|
||||
const errorMessage = "Failed to fetch team models";
|
||||
const testError = new Error(errorMessage);
|
||||
|
|
|
|||
|
|
@ -247,7 +247,12 @@ export const useModelHub = () => {
|
|||
export const useAllProxyModels = () => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<AllProxyModelsResponse>({
|
||||
queryKey: allProxyModelsKeys.list({}),
|
||||
queryKey: allProxyModelsKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true, null, true, false, "expand"),
|
||||
enabled: Boolean(accessToken && userId && userRole),
|
||||
});
|
||||
|
|
@ -256,7 +261,12 @@ export const useAllProxyModels = () => {
|
|||
export const useUserModels = (): UseQueryResult<string[]> => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<string[]>({
|
||||
queryKey: userModelsKeys.list({}),
|
||||
queryKey: userModelsKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => {
|
||||
const response = await modelAvailableCall(accessToken!, userId!, userRole!);
|
||||
return response["data"].map((model: { id: string }) => model.id);
|
||||
|
|
@ -268,7 +278,13 @@ export const useUserModels = (): UseQueryResult<string[]> => {
|
|||
export const useSelectedTeamModels = (teamID: string | null) => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<AllProxyModelsResponse>({
|
||||
queryKey: selectedTeamModelsKeys.list({}),
|
||||
queryKey: selectedTeamModelsKeys.list({
|
||||
filters: {
|
||||
teamID: teamID ?? "",
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true, teamID!),
|
||||
enabled: Boolean(accessToken && userId && userRole && teamID),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -116,6 +116,36 @@ describe("useTeams", () => {
|
|||
expect(fetchTeams).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps team list results scoped to the authorized user and role", async () => {
|
||||
const otherUserTeams = [mockTeams[1]];
|
||||
vi.mocked(fetchTeams).mockResolvedValueOnce(mockTeams).mockResolvedValueOnce(otherUserTeams);
|
||||
|
||||
const { result, rerender } = renderHook(() => useTeams(), { wrapper });
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(mockTeams));
|
||||
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "another-user-id",
|
||||
userRole: "Internal User",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
rerender();
|
||||
|
||||
await waitFor(() => expect(result.current.data).toEqual(otherUserTeams));
|
||||
expect(fetchTeams).toHaveBeenCalledTimes(2);
|
||||
expect(
|
||||
queryClient
|
||||
.getQueriesData<Team[]>({ queryKey: ["teams", "list"] })
|
||||
.map(([, teams]) => teams?.[0]?.team_id)
|
||||
.sort(),
|
||||
).toEqual(["team-1", "team-2"]);
|
||||
});
|
||||
|
||||
it("should handle error when fetchTeams fails", async () => {
|
||||
const errorMessage = "Failed to fetch teams";
|
||||
const testError = new Error(errorMessage);
|
||||
|
|
@ -398,7 +428,10 @@ describe("useTeam", () => {
|
|||
});
|
||||
|
||||
it("should use initialData from teams list cache when available", async () => {
|
||||
queryClient.setQueryData(["teams", "list", { params: {} }], mockTeams);
|
||||
queryClient.setQueryData(
|
||||
["teams", "list", { params: { filters: { userId: "test-user-id", userRole: "Admin" } } }],
|
||||
mockTeams,
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useTeam("team-1"), { wrapper });
|
||||
|
||||
|
|
@ -413,7 +446,10 @@ describe("useTeam", () => {
|
|||
});
|
||||
|
||||
it("should return undefined initialData when teamId is not in cache", () => {
|
||||
queryClient.setQueryData(["teams", "list", { params: {} }], mockTeams);
|
||||
queryClient.setQueryData(
|
||||
["teams", "list", { params: { filters: { userId: "test-user-id", userRole: "Admin" } } }],
|
||||
mockTeams,
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useTeam("non-existent-team"), { wrapper });
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,12 @@ export const teamKeys = createQueryKeys("teams");
|
|||
export const useTeams = (): UseQueryResult<Team[]> => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery<Team[]>({
|
||||
queryKey: teamKeys.list({}),
|
||||
queryKey: teamKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
queryFn: async () => await fetchTeams(accessToken!, userId, userRole, null),
|
||||
enabled: Boolean(accessToken),
|
||||
});
|
||||
|
|
@ -159,7 +164,7 @@ export const useAllTeams = (): UseQueryResult<Team[]> => {
|
|||
};
|
||||
|
||||
export const useTeam = (teamId?: string) => {
|
||||
const { accessToken } = useAuthorized();
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery<Team>({
|
||||
queryKey: teamKeys.detail(teamId!),
|
||||
|
|
@ -177,7 +182,14 @@ export const useTeam = (teamId?: string) => {
|
|||
initialData: () => {
|
||||
if (!teamId) return undefined;
|
||||
|
||||
const teams = queryClient.getQueryData<Team[]>(teamKeys.list({}));
|
||||
const teams = queryClient.getQueryData<Team[]>(
|
||||
teamKeys.list({
|
||||
filters: {
|
||||
userId: userId ?? "",
|
||||
userRole: userRole ?? "",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return teams?.find((team) => team.team_id === teamId);
|
||||
},
|
||||
|
|
@ -202,6 +214,7 @@ export const useInfiniteTeams = (pageSize: number = 50, search?: string, organiz
|
|||
...(search && { search }),
|
||||
...(organizationId && { organizationId }),
|
||||
...(userId && { userId }),
|
||||
isAdmin: isAdmin ? "true" : "false",
|
||||
},
|
||||
}),
|
||||
queryFn: async ({ pageParam }) => {
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ const MCPToolsViewer = ({
|
|||
error: mcpToolsError,
|
||||
refetch: refetchTools,
|
||||
} = useQuery({
|
||||
queryKey: ["mcpTools", serverId, passthroughHeaders, oauthToken],
|
||||
queryKey: ["mcpTools", serverId, passthroughHeaders, oauthToken, userID],
|
||||
queryFn: async () => {
|
||||
if (!accessToken) throw new Error("Access Token required");
|
||||
const result = await listMCPTools(accessToken, serverId, buildCustomHeaders());
|
||||
|
|
|
|||
|
|
@ -244,17 +244,19 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
|
|||
|
||||
return await userListCall(
|
||||
accessToken,
|
||||
userIdFilter ? [userIdFilter] : null,
|
||||
pagination.pageIndex + 1,
|
||||
pagination.pageSize,
|
||||
userListQueryFilters.userId ? [userListQueryFilters.userId] : null,
|
||||
userListQueryFilters.page,
|
||||
userListQueryFilters.pageSize,
|
||||
null,
|
||||
userRoleFilter ?? null,
|
||||
teamFilter ?? null,
|
||||
ssoUserIdFilter ?? null,
|
||||
sortBy,
|
||||
sortOrder,
|
||||
orgAdminOrgIds ? orgAdminOrgIds.map((o) => o.organization_id) : null,
|
||||
searchFilter,
|
||||
userListQueryFilters.role ?? null,
|
||||
userListQueryFilters.team ?? null,
|
||||
userListQueryFilters.ssoUserId ?? null,
|
||||
userListQueryFilters.sortBy,
|
||||
userListQueryFilters.sortOrder,
|
||||
userListQueryFilters.orgAdminOrgIds
|
||||
? userListQueryFilters.orgAdminOrgIds.map((organization) => organization.organization_id)
|
||||
: null,
|
||||
userListQueryFilters.search,
|
||||
);
|
||||
},
|
||||
enabled: Boolean(accessToken && token && userRole && userID),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@ export const useAutoRouterAvailability = (accessToken: string, body: Request, en
|
|||
queryFn: ({ signal }) =>
|
||||
apiClient.post<Availability>("/auto_router/availability", {
|
||||
accessToken,
|
||||
body: { ...body, complexity_router_config: JSON.parse(debounced) },
|
||||
body: {
|
||||
team_id: body.team_id,
|
||||
saved_model_id: body.saved_model_id,
|
||||
complexity_router_config: JSON.parse(debounced),
|
||||
},
|
||||
signal,
|
||||
}),
|
||||
enabled: enabled && Boolean(accessToken),
|
||||
|
|
@ -53,8 +57,10 @@ export const useAutoRouterAvailability = (accessToken: string, body: Request, en
|
|||
return query.data.error ?? null;
|
||||
};
|
||||
return {
|
||||
...query,
|
||||
data: query.data,
|
||||
isError: query.isError,
|
||||
isPending: query.isPending || (query.isFetching && !query.isFetchedAfterMount),
|
||||
refetch: query.refetch,
|
||||
isChecking,
|
||||
saveBlockedReason: saveBlockedReason(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -246,20 +246,19 @@ const LogsPanel: React.FC<Props> = ({ accessToken, userId }) => {
|
|||
const [page, setPage] = useState(1);
|
||||
const [selectedLog, setSelectedLog] = useState<LogRow | null>(null);
|
||||
|
||||
const startDate = getStartMoment(timeRange).utc().format("YYYY-MM-DD HH:mm:ss");
|
||||
const endDate = moment().utc().format("YYYY-MM-DD HH:mm:ss");
|
||||
|
||||
const logsCallOptions = {
|
||||
accessToken,
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
page,
|
||||
page_size: PAGE_SIZE,
|
||||
params: { user_id: userId, sort_by: "startTime", sort_order: "desc" as const },
|
||||
};
|
||||
const logsQueryOptions = {
|
||||
queryKey: [LOGS_QUERY_KEY, accessToken, userId, timeRange, page],
|
||||
queryFn: () => uiSpendLogsCall(logsCallOptions),
|
||||
queryFn: () => {
|
||||
const logsCallOptions = {
|
||||
accessToken,
|
||||
start_date: getStartMoment(timeRange).utc().format("YYYY-MM-DD HH:mm:ss"),
|
||||
end_date: moment().utc().format("YYYY-MM-DD HH:mm:ss"),
|
||||
page,
|
||||
page_size: PAGE_SIZE,
|
||||
params: { user_id: userId, sort_by: "startTime", sort_order: "desc" as const },
|
||||
};
|
||||
return uiSpendLogsCall(logsCallOptions);
|
||||
},
|
||||
enabled: !!accessToken && !!userId,
|
||||
placeholderData: keepPreviousData,
|
||||
};
|
||||
|
|
@ -270,10 +269,14 @@ const LogsPanel: React.FC<Props> = ({ accessToken, userId }) => {
|
|||
const totalPages = logs?.total_pages ?? 0;
|
||||
const total = logs?.total ?? 0;
|
||||
|
||||
const detailStartDate = selectedLog ? moment(selectedLog.startTime).utc().format("YYYY-MM-DD HH:mm:ss") : "";
|
||||
const { data: detailData, isLoading: isDetailLoading } = useQuery({
|
||||
queryKey: [LOGS_QUERY_KEY, "detail", accessToken, selectedLog?.request_id, selectedLog?.startTime],
|
||||
queryFn: () => uiSpendLogDetailsCall(accessToken, selectedLog!.request_id, detailStartDate),
|
||||
queryFn: () =>
|
||||
uiSpendLogDetailsCall(
|
||||
accessToken,
|
||||
selectedLog!.request_id,
|
||||
moment(selectedLog!.startTime).utc().format("YYYY-MM-DD HH:mm:ss"),
|
||||
),
|
||||
enabled: !!accessToken && !!selectedLog,
|
||||
});
|
||||
const details = detailData as LogDetails | undefined;
|
||||
|
|
|
|||
|
|
@ -91,11 +91,11 @@ const TIME_RANGE_OPTIONS: { value: TimeRange; label: string }[] = [
|
|||
|
||||
const UsagePanel: React.FC<Props> = ({ accessToken, userId }) => {
|
||||
const [timeRange, setTimeRange] = useState<TimeRange>("30d");
|
||||
const { start, end } = getDateRange(timeRange);
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: [USAGE_QUERY_KEY, accessToken, userId, timeRange],
|
||||
queryFn: () => {
|
||||
const { start, end } = getDateRange(timeRange);
|
||||
const request = {
|
||||
accessToken,
|
||||
startTime: start,
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ export default function RequestLogsPanel({ accessToken, token, userRole, userID,
|
|||
const { data: selectedKeyInfo } = useQuery(keyInfoQueryOptions);
|
||||
|
||||
const urlLogQueryOptions: UseQueryOptions<LogEntry | null> = {
|
||||
queryKey: ["logs", "byId", urlLogId, accessToken],
|
||||
queryKey: ["logs", "byId", urlLogId, accessToken, startTime, endTime, isCustomDate],
|
||||
queryFn: async () => {
|
||||
if (urlLogId === null) return null;
|
||||
const window = formatLogsWindow(startTime, endTime, isCustomDate);
|
||||
|
|
|
|||
|
|
@ -161,6 +161,9 @@ describe("useLogFilterLogic", () => {
|
|||
["startTime", { startTime: "2025-02-02T00:00:00" }],
|
||||
["columnFilters", { columnFilters: [{ id: LOG_FILTER_IDS.TEAM_ID, value: "team-2" }] }],
|
||||
["excludeInternalHealthChecks", { excludeInternalHealthChecks: true }],
|
||||
["token", { token: "other-token" }],
|
||||
["userRole", { userRole: "Internal User" }],
|
||||
["userID", { userID: "user-2" }],
|
||||
])("refetches when %s changes", async (_label, nextProps) => {
|
||||
const { rerender } = renderHook((props: HookOverrides) => useLogFilterLogic({ ...defaultProps, ...props }), {
|
||||
wrapper,
|
||||
|
|
|
|||
|
|
@ -155,6 +155,9 @@ export function useLogFilterLogic({
|
|||
sortOrder,
|
||||
excludeInternalHealthChecks,
|
||||
sessionCursor,
|
||||
token,
|
||||
userRole,
|
||||
userID,
|
||||
],
|
||||
queryFn: async () => {
|
||||
if (!accessToken || !token || !userRole || !userID) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue