mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-17 04:26:20 +00:00
Paginate All Models Tab
This commit is contained in:
@@ -0,0 +1,627 @@
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
import React, { ReactNode } from "react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
useModelsInfo,
|
||||
useModelHub,
|
||||
useAllProxyModels,
|
||||
useSelectedTeamModels,
|
||||
type ProxyModel,
|
||||
type AllProxyModelsResponse,
|
||||
type PaginatedModelInfoResponse,
|
||||
} from "./useModels";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
modelInfoCall: vi.fn(),
|
||||
modelHubCall: vi.fn(),
|
||||
modelAvailableCall: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockUseAuthorized = vi.fn();
|
||||
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
|
||||
default: () => mockUseAuthorized(),
|
||||
}));
|
||||
|
||||
import { modelInfoCall, modelHubCall, modelAvailableCall } from "@/components/networking";
|
||||
|
||||
const mockProxyModel: ProxyModel = {
|
||||
id: "model-1",
|
||||
object: "model",
|
||||
created: 1234567890,
|
||||
owned_by: "openai",
|
||||
};
|
||||
|
||||
const mockPaginatedModelInfoResponse: PaginatedModelInfoResponse = {
|
||||
data: [{ id: "model-1", name: "Test Model" }],
|
||||
total_count: 1,
|
||||
current_page: 1,
|
||||
total_pages: 1,
|
||||
size: 50,
|
||||
};
|
||||
|
||||
const mockAllProxyModelsResponse: AllProxyModelsResponse = {
|
||||
data: [mockProxyModel],
|
||||
};
|
||||
|
||||
describe("useModelsInfo", () => {
|
||||
let queryClient: QueryClient;
|
||||
|
||||
beforeEach(() => {
|
||||
queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) =>
|
||||
React.createElement(QueryClientProvider, { client: queryClient }, children);
|
||||
|
||||
it("should render without crashing", () => {
|
||||
(modelInfoCall as any).mockResolvedValue(mockPaginatedModelInfoResponse);
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(), { wrapper });
|
||||
|
||||
expect(result.current).toBeDefined();
|
||||
});
|
||||
|
||||
it("should return models data when query is successful", async () => {
|
||||
(modelInfoCall as any).mockResolvedValue(mockPaginatedModelInfoResponse);
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockPaginatedModelInfoResponse);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(modelInfoCall).toHaveBeenCalledWith(
|
||||
"test-access-token",
|
||||
"test-user-id",
|
||||
"Admin",
|
||||
1,
|
||||
50
|
||||
);
|
||||
expect(modelInfoCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should use custom page and size parameters", async () => {
|
||||
(modelInfoCall as any).mockResolvedValue(mockPaginatedModelInfoResponse);
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(2, 25), { wrapper });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
expect(modelInfoCall).toHaveBeenCalledWith(
|
||||
"test-access-token",
|
||||
"test-user-id",
|
||||
"Admin",
|
||||
2,
|
||||
25
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle error when modelInfoCall fails", async () => {
|
||||
const errorMessage = "Failed to fetch models";
|
||||
const testError = new Error(errorMessage);
|
||||
|
||||
(modelInfoCall as any).mockRejectedValue(testError);
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.error).toEqual(testError);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(modelInfoCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not execute query when accessToken is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: null,
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: null,
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelInfoCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when userId is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: null,
|
||||
userRole: "Admin",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelInfoCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when userRole is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "test-user-id",
|
||||
userRole: null,
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelInfoCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when all required auth values are missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: null,
|
||||
userId: null,
|
||||
userRole: null,
|
||||
token: null,
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useModelsInfo(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelInfoCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("useModelHub", () => {
|
||||
let queryClient: QueryClient;
|
||||
|
||||
beforeEach(() => {
|
||||
queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) =>
|
||||
React.createElement(QueryClientProvider, { client: queryClient }, children);
|
||||
|
||||
it("should render without crashing", () => {
|
||||
(modelHubCall as any).mockResolvedValue({ data: [] });
|
||||
|
||||
const { result } = renderHook(() => useModelHub(), { wrapper });
|
||||
|
||||
expect(result.current).toBeDefined();
|
||||
});
|
||||
|
||||
it("should return model hub data when query is successful", async () => {
|
||||
const mockHubData = { data: [{ id: "hub-1", name: "Test Hub" }] };
|
||||
(modelHubCall as any).mockResolvedValue(mockHubData);
|
||||
|
||||
const { result } = renderHook(() => useModelHub(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockHubData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(modelHubCall).toHaveBeenCalledWith("test-access-token");
|
||||
expect(modelHubCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should handle error when modelHubCall fails", async () => {
|
||||
const errorMessage = "Failed to fetch model hub";
|
||||
const testError = new Error(errorMessage);
|
||||
|
||||
(modelHubCall as any).mockRejectedValue(testError);
|
||||
|
||||
const { result } = renderHook(() => useModelHub(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.error).toEqual(testError);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(modelHubCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not execute query when accessToken is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: null,
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: null,
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useModelHub(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelHubCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("useAllProxyModels", () => {
|
||||
let queryClient: QueryClient;
|
||||
|
||||
beforeEach(() => {
|
||||
queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) =>
|
||||
React.createElement(QueryClientProvider, { client: queryClient }, children);
|
||||
|
||||
it("should render without crashing", () => {
|
||||
(modelAvailableCall as any).mockResolvedValue(mockAllProxyModelsResponse);
|
||||
|
||||
const { result } = renderHook(() => useAllProxyModels(), { wrapper });
|
||||
|
||||
expect(result.current).toBeDefined();
|
||||
});
|
||||
|
||||
it("should return all proxy models data when query is successful", async () => {
|
||||
(modelAvailableCall as any).mockResolvedValue(mockAllProxyModelsResponse);
|
||||
|
||||
const { result } = renderHook(() => useAllProxyModels(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockAllProxyModelsResponse);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(modelAvailableCall).toHaveBeenCalledWith(
|
||||
"test-access-token",
|
||||
"test-user-id",
|
||||
"Admin",
|
||||
true
|
||||
);
|
||||
expect(modelAvailableCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should handle error when modelAvailableCall fails", async () => {
|
||||
const errorMessage = "Failed to fetch proxy models";
|
||||
const testError = new Error(errorMessage);
|
||||
|
||||
(modelAvailableCall as any).mockRejectedValue(testError);
|
||||
|
||||
const { result } = renderHook(() => useAllProxyModels(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.error).toEqual(testError);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(modelAvailableCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not execute query when accessToken is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: null,
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: null,
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useAllProxyModels(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when userId is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: null,
|
||||
userRole: "Admin",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useAllProxyModels(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when userRole is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "test-user-id",
|
||||
userRole: null,
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useAllProxyModels(), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("useSelectedTeamModels", () => {
|
||||
let queryClient: QueryClient;
|
||||
|
||||
beforeEach(() => {
|
||||
queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) =>
|
||||
React.createElement(QueryClientProvider, { client: queryClient }, children);
|
||||
|
||||
it("should render without crashing", () => {
|
||||
(modelAvailableCall as any).mockResolvedValue(mockAllProxyModelsResponse);
|
||||
|
||||
const { result } = renderHook(() => useSelectedTeamModels("team-1"), { wrapper });
|
||||
|
||||
expect(result.current).toBeDefined();
|
||||
});
|
||||
|
||||
it("should return team models data when query is successful", async () => {
|
||||
(modelAvailableCall as any).mockResolvedValue(mockAllProxyModelsResponse);
|
||||
|
||||
const { result } = renderHook(() => useSelectedTeamModels("team-1"), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockAllProxyModelsResponse);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(modelAvailableCall).toHaveBeenCalledWith(
|
||||
"test-access-token",
|
||||
"test-user-id",
|
||||
"Admin",
|
||||
true,
|
||||
"team-1"
|
||||
);
|
||||
expect(modelAvailableCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should handle error when modelAvailableCall fails", async () => {
|
||||
const errorMessage = "Failed to fetch team models";
|
||||
const testError = new Error(errorMessage);
|
||||
|
||||
(modelAvailableCall as any).mockRejectedValue(testError);
|
||||
|
||||
const { result } = renderHook(() => useSelectedTeamModels("team-1"), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.error).toEqual(testError);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(modelAvailableCall).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not execute query when teamID is null", () => {
|
||||
const { result } = renderHook(() => useSelectedTeamModels(null), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when accessToken is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: null,
|
||||
userId: "test-user-id",
|
||||
userRole: "Admin",
|
||||
token: null,
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSelectedTeamModels("team-1"), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when userId is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: null,
|
||||
userRole: "Admin",
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSelectedTeamModels("team-1"), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when userRole is missing", () => {
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "test-access-token",
|
||||
userId: "test-user-id",
|
||||
userRole: null,
|
||||
token: "test-token",
|
||||
userEmail: "test@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useSelectedTeamModels("team-1"), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not execute query when teamID is missing and other auth values are present", () => {
|
||||
const { result } = renderHook(() => useSelectedTeamModels(null), { wrapper });
|
||||
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
expect(modelAvailableCall).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -14,21 +14,31 @@ export interface AllProxyModelsResponse {
|
||||
data: ProxyModel[];
|
||||
}
|
||||
|
||||
export interface PaginatedModelInfoResponse {
|
||||
data: any[];
|
||||
total_count: number;
|
||||
current_page: number;
|
||||
total_pages: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
const modelKeys = createQueryKeys("models");
|
||||
const modelHubKeys = createQueryKeys("modelHub");
|
||||
const allProxyModelsKeys = createQueryKeys("allProxyModels");
|
||||
const selectedTeamModelsKeys = createQueryKeys("selectedTeamModels");
|
||||
|
||||
export const useModelsInfo = () => {
|
||||
export const useModelsInfo = (page: number = 1, size: number = 50) => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
return useQuery({
|
||||
return useQuery<PaginatedModelInfoResponse>({
|
||||
queryKey: modelKeys.list({
|
||||
filters: {
|
||||
...(userId && { userId }),
|
||||
...(userRole && { userRole }),
|
||||
page,
|
||||
size,
|
||||
},
|
||||
}),
|
||||
queryFn: async () => await modelInfoCall(accessToken!, userId!, userRole!),
|
||||
queryFn: async () => await modelInfoCall(accessToken!, userId!, userRole!, page, size),
|
||||
enabled: Boolean(accessToken && userId && userRole),
|
||||
});
|
||||
};
|
||||
|
||||
+2
-1
@@ -94,7 +94,8 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({ premiumUser, te
|
||||
}, [modelDataResponse?.data]);
|
||||
|
||||
const allModelsOnProxy = useMemo<string[]>(() => {
|
||||
return modelDataResponse?.data?.map((model: any) => model.model_name);
|
||||
if (!modelDataResponse?.data) return [];
|
||||
return modelDataResponse.data.map((model: any) => model.model_name);
|
||||
}, [modelDataResponse?.data]);
|
||||
|
||||
const getProviderFromModel = (model: string) => {
|
||||
|
||||
+234
-114
@@ -1,13 +1,18 @@
|
||||
import * as useAuthorizedModule from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import AllModelsTab from "./AllModelsTab";
|
||||
|
||||
// Mock the useModelsInfo hook
|
||||
const mockUseModelsInfo = vi.fn(() => ({ data: { data: [] } })) as any;
|
||||
const mockUseModelsInfo = vi.fn(() => ({
|
||||
data: { data: [], total_count: 0, current_page: 1, total_pages: 1, size: 50 },
|
||||
isLoading: false,
|
||||
error: null,
|
||||
})) as any;
|
||||
|
||||
vi.mock("../../hooks/models/useModels", () => ({
|
||||
useModelsInfo: () => mockUseModelsInfo(),
|
||||
useModelsInfo: (page?: number, size?: number) => mockUseModelsInfo(page, size),
|
||||
}));
|
||||
|
||||
// Mock the useModelCostMap hook
|
||||
@@ -51,6 +56,21 @@ const createModelCostMapMock = (data: Record<string, any>) => ({
|
||||
error: null,
|
||||
});
|
||||
|
||||
// Helper function to create paginated model data mock
|
||||
const createPaginatedModelData = (
|
||||
models: any[],
|
||||
totalCount: number = models.length,
|
||||
currentPage: number = 1,
|
||||
totalPages: number = 1,
|
||||
size: number = 50,
|
||||
) => ({
|
||||
data: models,
|
||||
total_count: totalCount,
|
||||
current_page: currentPage,
|
||||
total_pages: totalPages,
|
||||
size: size,
|
||||
});
|
||||
|
||||
describe("AllModelsTab", () => {
|
||||
const mockSetSelectedModelGroup = vi.fn();
|
||||
const mockSetSelectedModelId = vi.fn();
|
||||
@@ -84,7 +104,11 @@ describe("AllModelsTab", () => {
|
||||
});
|
||||
|
||||
it("should render with empty data", () => {
|
||||
mockUseModelsInfo.mockReturnValueOnce({ data: { data: [] } });
|
||||
mockUseModelsInfo.mockReturnValueOnce({
|
||||
data: createPaginatedModelData([], 0, 1, 1, 50),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
mockUseTeams.mockReturnValueOnce({
|
||||
data: [],
|
||||
@@ -130,28 +154,26 @@ describe("AllModelsTab", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4-accessible",
|
||||
model_info: {
|
||||
id: "model-1",
|
||||
access_via_team_ids: ["team-456"],
|
||||
access_groups: [],
|
||||
},
|
||||
const modelData = createPaginatedModelData([
|
||||
{
|
||||
model_name: "gpt-4-accessible",
|
||||
model_info: {
|
||||
id: "model-1",
|
||||
access_via_team_ids: ["team-456"],
|
||||
access_groups: [],
|
||||
},
|
||||
{
|
||||
model_name: "gpt-3.5-turbo-blocked",
|
||||
model_info: {
|
||||
id: "model-2",
|
||||
access_via_team_ids: ["team-789"],
|
||||
access_groups: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
model_name: "gpt-3.5-turbo-blocked",
|
||||
model_info: {
|
||||
id: "model-2",
|
||||
access_via_team_ids: ["team-789"],
|
||||
access_groups: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
], 2, 1, 1, 50);
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData });
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
@@ -191,28 +213,26 @@ describe("AllModelsTab", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4-sales",
|
||||
model_info: {
|
||||
id: "model-sales-1",
|
||||
access_via_team_ids: [],
|
||||
access_groups: ["sales-model-group"],
|
||||
},
|
||||
const modelData = createPaginatedModelData([
|
||||
{
|
||||
model_name: "gpt-4-sales",
|
||||
model_info: {
|
||||
id: "model-sales-1",
|
||||
access_via_team_ids: [],
|
||||
access_groups: ["sales-model-group"],
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4-engineering",
|
||||
model_info: {
|
||||
id: "model-eng-1",
|
||||
access_via_team_ids: [],
|
||||
access_groups: ["engineering-model-group"],
|
||||
},
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4-engineering",
|
||||
model_info: {
|
||||
id: "model-eng-1",
|
||||
access_via_team_ids: [],
|
||||
access_groups: ["engineering-model-group"],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
], 2, 1, 1, 50);
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData });
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
@@ -236,30 +256,28 @@ describe("AllModelsTab", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4-personal",
|
||||
model_info: {
|
||||
id: "model-personal-1",
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
},
|
||||
const modelData = createPaginatedModelData([
|
||||
{
|
||||
model_name: "gpt-4-personal",
|
||||
model_info: {
|
||||
id: "model-personal-1",
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4-team-only",
|
||||
model_info: {
|
||||
id: "model-team-1",
|
||||
direct_access: false,
|
||||
access_via_team_ids: ["team-123"],
|
||||
access_groups: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4-team-only",
|
||||
model_info: {
|
||||
id: "model-team-1",
|
||||
direct_access: false,
|
||||
access_via_team_ids: ["team-123"],
|
||||
access_groups: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
], 2, 1, 1, 50);
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData });
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
@@ -283,42 +301,40 @@ describe("AllModelsTab", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4-config",
|
||||
litellm_model_name: "gpt-4-config",
|
||||
provider: "openai",
|
||||
model_info: {
|
||||
id: "model-config-1",
|
||||
db_model: false,
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
created_by: "user-123",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
},
|
||||
const modelData = createPaginatedModelData([
|
||||
{
|
||||
model_name: "gpt-4-config",
|
||||
litellm_model_name: "gpt-4-config",
|
||||
provider: "openai",
|
||||
model_info: {
|
||||
id: "model-config-1",
|
||||
db_model: false,
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
created_by: "user-123",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4-db",
|
||||
litellm_model_name: "gpt-4-db",
|
||||
provider: "openai",
|
||||
model_info: {
|
||||
id: "model-db-1",
|
||||
db_model: true,
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
created_by: "user-123",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4-db",
|
||||
litellm_model_name: "gpt-4-db",
|
||||
provider: "openai",
|
||||
model_info: {
|
||||
id: "model-db-1",
|
||||
db_model: true,
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
created_by: "user-123",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
], 2, 1, 1, 50);
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData });
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
@@ -342,27 +358,25 @@ describe("AllModelsTab", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4-config",
|
||||
litellm_model_name: "gpt-4-config",
|
||||
provider: "openai",
|
||||
model_info: {
|
||||
id: "model-config-1",
|
||||
db_model: false,
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
created_by: "user-123",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
},
|
||||
const modelData = createPaginatedModelData([
|
||||
{
|
||||
model_name: "gpt-4-config",
|
||||
litellm_model_name: "gpt-4-config",
|
||||
provider: "openai",
|
||||
model_info: {
|
||||
id: "model-config-1",
|
||||
db_model: false,
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
created_by: "user-123",
|
||||
created_at: "2024-01-01",
|
||||
updated_at: "2024-01-01",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
], 1, 1, 1, 50);
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData });
|
||||
mockUseModelsInfo.mockReturnValue({ data: modelData, isLoading: false, error: null });
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
@@ -370,4 +384,110 @@ describe("AllModelsTab", () => {
|
||||
expect(screen.getByText("Defined in config")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle pagination: Previous button is disabled on first page and Next button works", async () => {
|
||||
mockUseTeams.mockReturnValue({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refetch: vi.fn(),
|
||||
});
|
||||
|
||||
mockUseModelCostMap.mockReturnValue(
|
||||
createModelCostMapMock({
|
||||
"gpt-4-page1": { litellm_provider: "openai" },
|
||||
"gpt-4-page2": { litellm_provider: "openai" },
|
||||
}),
|
||||
);
|
||||
|
||||
// Mock first page response (page 1 of 2)
|
||||
const page1Data = createPaginatedModelData(
|
||||
[
|
||||
{
|
||||
model_name: "gpt-4-page1",
|
||||
model_info: {
|
||||
id: "model-page1-1",
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
2, // total_count
|
||||
1, // current_page
|
||||
2, // total_pages
|
||||
50, // size
|
||||
);
|
||||
|
||||
// Set up mock to return page1Data for page 1
|
||||
mockUseModelsInfo.mockImplementation((page: number = 1) => {
|
||||
return { data: page1Data, isLoading: false, error: null };
|
||||
});
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Showing 1 - 1 of 1 results")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Check that Previous button is disabled on first page
|
||||
const previousButton = screen.getByRole("button", { name: /previous/i });
|
||||
expect(previousButton).toBeDisabled();
|
||||
|
||||
// Check that Next button is enabled (since we're on page 1 of 2)
|
||||
const nextButton = screen.getByRole("button", { name: /next/i });
|
||||
expect(nextButton).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it("should handle pagination: Next button is disabled on last page", async () => {
|
||||
mockUseTeams.mockReturnValue({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refetch: vi.fn(),
|
||||
});
|
||||
|
||||
mockUseModelCostMap.mockReturnValue(
|
||||
createModelCostMapMock({
|
||||
"gpt-4-page2": { litellm_provider: "openai" },
|
||||
}),
|
||||
);
|
||||
|
||||
// Mock single page response (page 1 of 1 - last page)
|
||||
const singlePageData = createPaginatedModelData(
|
||||
[
|
||||
{
|
||||
model_name: "gpt-4-page2",
|
||||
model_info: {
|
||||
id: "model-page2-1",
|
||||
direct_access: true,
|
||||
access_via_team_ids: [],
|
||||
access_groups: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
1, // total_count
|
||||
1, // current_page
|
||||
1, // total_pages (only 1 page, so this is the last page)
|
||||
50, // size
|
||||
);
|
||||
|
||||
mockUseModelsInfo.mockImplementation(() => {
|
||||
return { data: singlePageData, isLoading: false, error: null };
|
||||
});
|
||||
|
||||
render(<AllModelsTab {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Showing 1 - 1 of 1 results")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// When there's only 1 page (last page), Next should be disabled
|
||||
const nextButton = screen.getByRole("button", { name: /next/i });
|
||||
expect(nextButton).toBeDisabled();
|
||||
|
||||
// Previous should also be disabled on the first (and only) page
|
||||
const previousButton = screen.getByRole("button", { name: /previous/i });
|
||||
expect(previousButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
+57
-37
@@ -31,11 +31,26 @@ const AllModelsTab = ({
|
||||
setSelectedModelId,
|
||||
setSelectedTeamId,
|
||||
}: AllModelsTabProps) => {
|
||||
const { data: rawModelData, isLoading: isLoadingModelsInfo } = useModelsInfo();
|
||||
const { data: modelCostMapData, isLoading: isLoadingModelCostMap } = useModelCostMap();
|
||||
const { userId, userRole, premiumUser } = useAuthorized();
|
||||
const { data: teams } = useTeams();
|
||||
|
||||
const [modelNameSearch, setModelNameSearch] = useState<string>("");
|
||||
const [modelViewMode, setModelViewMode] = useState<ModelViewMode>("current_team");
|
||||
const [currentTeam, setCurrentTeam] = useState<Team | "personal">("personal");
|
||||
const [showFilters, setShowFilters] = useState<boolean>(false);
|
||||
const [selectedModelAccessGroupFilter, setSelectedModelAccessGroupFilter] = useState<string | null>(null);
|
||||
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
|
||||
const [currentPage, setCurrentPage] = useState<number>(1);
|
||||
const [pageSize] = useState<number>(50);
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 50,
|
||||
});
|
||||
|
||||
const { data: rawModelData, isLoading: isLoadingModelsInfo } = useModelsInfo(currentPage, pageSize);
|
||||
const isLoading = isLoadingModelsInfo || isLoadingModelCostMap;
|
||||
|
||||
const getProviderFromModel = (model: string) => {
|
||||
if (modelCostMapData !== null && modelCostMapData !== undefined) {
|
||||
if (typeof modelCostMapData == "object" && model in modelCostMapData) {
|
||||
@@ -50,18 +65,23 @@ const AllModelsTab = ({
|
||||
return transformModelData(rawModelData, getProviderFromModel);
|
||||
}, [rawModelData, modelCostMapData]);
|
||||
|
||||
const [modelNameSearch, setModelNameSearch] = useState<string>("");
|
||||
const [modelViewMode, setModelViewMode] = useState<ModelViewMode>("current_team");
|
||||
const [currentTeam, setCurrentTeam] = useState<Team | "personal">("personal");
|
||||
const [showFilters, setShowFilters] = useState<boolean>(false);
|
||||
const [selectedModelAccessGroupFilter, setSelectedModelAccessGroupFilter] = useState<string | null>(null);
|
||||
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 50,
|
||||
});
|
||||
|
||||
const isLoading = isLoadingModelsInfo || isLoadingModelCostMap;
|
||||
// Get pagination metadata from the response
|
||||
const paginationMeta = useMemo(() => {
|
||||
if (!rawModelData) {
|
||||
return {
|
||||
total_count: 0,
|
||||
current_page: 1,
|
||||
total_pages: 1,
|
||||
size: pageSize,
|
||||
};
|
||||
}
|
||||
return {
|
||||
total_count: rawModelData.total_count ?? 0,
|
||||
current_page: rawModelData.current_page ?? 1,
|
||||
total_pages: rawModelData.total_pages ?? 1,
|
||||
size: rawModelData.size ?? pageSize,
|
||||
};
|
||||
}, [rawModelData, pageSize]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (!modelData || !modelData.data || modelData.data.length === 0) {
|
||||
@@ -114,6 +134,7 @@ const AllModelsTab = ({
|
||||
setSelectedModelAccessGroupFilter(null);
|
||||
setCurrentTeam("personal");
|
||||
setModelViewMode("current_team");
|
||||
setCurrentPage(1);
|
||||
setPagination({ pageIndex: 0, pageSize: 50 });
|
||||
};
|
||||
|
||||
@@ -334,10 +355,7 @@ const AllModelsTab = ({
|
||||
) : (
|
||||
<span className="text-sm text-gray-700">
|
||||
{filteredData.length > 0
|
||||
? `Showing ${pagination.pageIndex * pagination.pageSize + 1} - ${Math.min(
|
||||
(pagination.pageIndex + 1) * pagination.pageSize,
|
||||
filteredData.length,
|
||||
)} of ${filteredData.length} results`
|
||||
? `Showing 1 - ${filteredData.length} of ${filteredData.length} results`
|
||||
: "Showing 0 results"}
|
||||
</span>
|
||||
)}
|
||||
@@ -347,15 +365,16 @@ const AllModelsTab = ({
|
||||
<Skeleton.Button active style={{ width: 84, height: 30 }} />
|
||||
) : (
|
||||
<button
|
||||
onClick={() =>
|
||||
setPagination((prev: PaginationState) => ({ ...prev, pageIndex: prev.pageIndex - 1 }))
|
||||
}
|
||||
disabled={pagination.pageIndex === 0}
|
||||
className={`px-3 py-1 text-sm border rounded-md ${
|
||||
pagination.pageIndex === 0
|
||||
? "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
: "hover:bg-gray-50"
|
||||
}`}
|
||||
onClick={() => {
|
||||
const newPage = currentPage - 1;
|
||||
setCurrentPage(newPage);
|
||||
setPagination((prev: PaginationState) => ({ ...prev, pageIndex: 0 }));
|
||||
}}
|
||||
disabled={currentPage === 1}
|
||||
className={`px-3 py-1 text-sm border rounded-md ${currentPage === 1
|
||||
? "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
: "hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
@@ -365,15 +384,16 @@ const AllModelsTab = ({
|
||||
<Skeleton.Button active style={{ width: 56, height: 30 }} />
|
||||
) : (
|
||||
<button
|
||||
onClick={() =>
|
||||
setPagination((prev: PaginationState) => ({ ...prev, pageIndex: prev.pageIndex + 1 }))
|
||||
}
|
||||
disabled={pagination.pageIndex >= Math.ceil(filteredData.length / pagination.pageSize) - 1}
|
||||
className={`px-3 py-1 text-sm border rounded-md ${
|
||||
pagination.pageIndex >= Math.ceil(filteredData.length / pagination.pageSize) - 1
|
||||
? "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
: "hover:bg-gray-50"
|
||||
}`}
|
||||
onClick={() => {
|
||||
const newPage = currentPage + 1;
|
||||
setCurrentPage(newPage);
|
||||
setPagination((prev: PaginationState) => ({ ...prev, pageIndex: 0 }));
|
||||
}}
|
||||
disabled={currentPage >= paginationMeta.total_pages}
|
||||
className={`px-3 py-1 text-sm border rounded-md ${currentPage >= paginationMeta.total_pages
|
||||
? "bg-gray-100 text-gray-400 cursor-not-allowed"
|
||||
: "hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
@@ -391,8 +411,8 @@ const AllModelsTab = ({
|
||||
setSelectedModelId,
|
||||
setSelectedTeamId,
|
||||
getDisplayModelName,
|
||||
() => {},
|
||||
() => {},
|
||||
() => { },
|
||||
() => { },
|
||||
expandedRows,
|
||||
setExpandedRows,
|
||||
)}
|
||||
|
||||
@@ -2007,15 +2007,17 @@ export const regenerateKeyCall = async (accessToken: string, keyToRegenerate: st
|
||||
let ModelListerrorShown = false;
|
||||
let errorTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
export const modelInfoCall = async (accessToken: string, userID: string, userRole: string) => {
|
||||
export const modelInfoCall = async (accessToken: string, userID: string, userRole: string, page: number = 1, size: number = 50) => {
|
||||
/**
|
||||
* Get all models on proxy
|
||||
*/
|
||||
try {
|
||||
console.log("modelInfoCall:", accessToken, userID, userRole);
|
||||
console.log("modelInfoCall:", accessToken, userID, userRole, page, size);
|
||||
let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/model/info` : `/v2/model/info`;
|
||||
const params = new URLSearchParams();
|
||||
params.append("include_team_models", "true");
|
||||
params.append("page", page.toString());
|
||||
params.append("size", size.toString());
|
||||
if (params.toString()) {
|
||||
url += `?${params.toString()}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user