mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-29 02:21:04 +00:00
Merge pull request #18552 from BerriAI/litellm_ui_model_loading
[Refactor] UI - Remove Model Analytics From Model Page
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import React, { ReactNode } from "react";
|
||||
import { useModelCostMap } from "./useModelCostMap";
|
||||
import { modelCostMap } from "@/components/networking";
|
||||
|
||||
// Mock the networking function
|
||||
vi.mock("@/components/networking", () => ({
|
||||
modelCostMap: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock the queryKeysFactory - we'll mock the specific return value
|
||||
vi.mock("../common/queryKeysFactory", () => ({
|
||||
createQueryKeys: vi.fn((resource: string) => ({
|
||||
all: [resource],
|
||||
lists: () => [resource, "list"],
|
||||
list: (params?: any) => [resource, "list", { params }],
|
||||
details: () => [resource, "detail"],
|
||||
detail: (uid: string) => [resource, "detail", uid],
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock data
|
||||
const mockModelCostData: Record<string, any> = {
|
||||
"gpt-3.5-turbo": {
|
||||
litellm_provider: "openai",
|
||||
input_cost_per_token: 0.0015,
|
||||
output_cost_per_token: 0.002,
|
||||
},
|
||||
"claude-3-sonnet-20240229": {
|
||||
litellm_provider: "anthropic",
|
||||
input_cost_per_token: 0.003,
|
||||
output_cost_per_token: 0.015,
|
||||
},
|
||||
};
|
||||
|
||||
describe("useModelCostMap", () => {
|
||||
let queryClient: QueryClient;
|
||||
|
||||
beforeEach(() => {
|
||||
queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Reset all mocks
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) =>
|
||||
React.createElement(QueryClientProvider, { client: queryClient }, children);
|
||||
|
||||
it("should return model cost map data when query is successful", async () => {
|
||||
// Mock successful API call
|
||||
(modelCostMap as any).mockResolvedValue(mockModelCostData);
|
||||
|
||||
const { result } = renderHook(() => useModelCostMap(), { wrapper });
|
||||
|
||||
// Initially loading
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
|
||||
// Wait for success
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual(mockModelCostData);
|
||||
expect(result.current.error).toBeNull();
|
||||
expect(modelCostMap).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should handle error when modelCostMap fails", async () => {
|
||||
const errorMessage = "Failed to fetch model cost map";
|
||||
const testError = new Error(errorMessage);
|
||||
|
||||
// Mock failed API call
|
||||
(modelCostMap as any).mockRejectedValue(testError);
|
||||
|
||||
const { result } = renderHook(() => useModelCostMap(), { wrapper });
|
||||
|
||||
// Initially loading
|
||||
expect(result.current.isLoading).toBe(true);
|
||||
|
||||
// Wait for error
|
||||
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(modelCostMap).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should return empty object when API returns empty data", async () => {
|
||||
// Mock API returning empty object
|
||||
(modelCostMap as any).mockResolvedValue({});
|
||||
|
||||
const { result } = renderHook(() => useModelCostMap(), { wrapper });
|
||||
|
||||
// Wait for success
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.data).toEqual({});
|
||||
expect(modelCostMap).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should handle network timeout error", async () => {
|
||||
const timeoutError = new Error("Network timeout");
|
||||
|
||||
// Mock network timeout
|
||||
(modelCostMap as any).mockRejectedValue(timeoutError);
|
||||
|
||||
const { result } = renderHook(() => useModelCostMap(), { wrapper });
|
||||
|
||||
// Wait for error
|
||||
await waitFor(() => {
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
expect(result.current.error).toEqual(timeoutError);
|
||||
expect(result.current.data).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should have correct query configuration", async () => {
|
||||
// Mock successful API call
|
||||
(modelCostMap as any).mockResolvedValue(mockModelCostData);
|
||||
|
||||
const { result } = renderHook(() => useModelCostMap(), { wrapper });
|
||||
|
||||
// Wait for query to complete
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBe(true);
|
||||
});
|
||||
|
||||
// Verify the query was called
|
||||
expect(modelCostMap).toHaveBeenCalledTimes(1);
|
||||
|
||||
// The hook should have the expected properties from useQuery
|
||||
expect(result.current).toHaveProperty("data");
|
||||
expect(result.current).toHaveProperty("isLoading");
|
||||
expect(result.current).toHaveProperty("isError");
|
||||
expect(result.current).toHaveProperty("isSuccess");
|
||||
expect(result.current).toHaveProperty("error");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { modelCostMap } from "@/components/networking";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { createQueryKeys } from "../common/queryKeysFactory";
|
||||
|
||||
const modelCostMapKeys = createQueryKeys("modelCostMap");
|
||||
|
||||
export const useModelCostMap = () => {
|
||||
return useQuery<Record<string, any>>({
|
||||
queryKey: modelCostMapKeys.list({}),
|
||||
queryFn: async () => await modelCostMap(),
|
||||
staleTime: 60 * 1000, // 1 minute
|
||||
gcTime: 60 * 1000, // 1 minute
|
||||
});
|
||||
};
|
||||
+31
-23
@@ -9,34 +9,24 @@ vi.mock("@/components/networking", () => ({
|
||||
credentialListCall: vi.fn().mockResolvedValue({ credentials: [] }),
|
||||
modelInfoCall: vi.fn().mockResolvedValue({ data: [] }),
|
||||
modelCostMap: vi.fn().mockResolvedValue({}),
|
||||
modelMetricsCall: vi.fn().mockResolvedValue({ data: [], all_api_bases: [] }),
|
||||
streamingModelMetricsCall: vi.fn().mockResolvedValue({ data: [], all_api_bases: [] }),
|
||||
modelExceptionsCall: vi.fn().mockResolvedValue({ data: [], exception_types: [] }),
|
||||
modelMetricsSlowResponsesCall: vi.fn().mockResolvedValue([]),
|
||||
getPassThroughEndpointsCall: vi.fn().mockResolvedValue({ endpoints: {} }),
|
||||
getCallbacksCall: vi.fn().mockResolvedValue({ router_settings: {} }),
|
||||
setCallbacksCall: vi.fn().mockResolvedValue(undefined),
|
||||
modelSettingsCall: vi.fn().mockResolvedValue([]),
|
||||
adminGlobalActivityExceptions: vi.fn().mockResolvedValue({ sum_num_rate_limit_exceptions: 0, daily_data: [] }),
|
||||
adminGlobalActivityExceptionsPerDeployment: vi.fn().mockResolvedValue([]),
|
||||
allEndUsersCall: vi.fn().mockResolvedValue([]),
|
||||
latestHealthChecksCall: vi.fn().mockResolvedValue({ latest_health_checks: {} }),
|
||||
getPassThroughEndpointsCall: vi.fn().mockResolvedValue({ endpoints: {} }),
|
||||
getGuardrailsList: vi.fn().mockResolvedValue([]),
|
||||
tagListCall: vi.fn().mockResolvedValue([]),
|
||||
modelAvailableCall: vi.fn().mockResolvedValue({ data: [] }),
|
||||
modelHubCall: vi.fn().mockResolvedValue({ data: [] }),
|
||||
getModelCostMapReloadStatus: vi.fn().mockResolvedValue({
|
||||
scheduled: false,
|
||||
interval_hours: null,
|
||||
last_run: null,
|
||||
next_run: null,
|
||||
}),
|
||||
getUiSettings: vi.fn().mockResolvedValue({ values: {} }),
|
||||
}));
|
||||
|
||||
vi.mock("@/app/(dashboard)/models-and-endpoints/components/ModelAnalyticsTab/ModelAnalyticsTab", () => ({
|
||||
default: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/add_model/add_auto_router_tab", () => ({
|
||||
default: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/add_model/AddModelForm", () => ({
|
||||
default: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("@/app/(dashboard)/hooks/useTeams", () => ({
|
||||
default: () => ({
|
||||
teams: [],
|
||||
@@ -54,6 +44,16 @@ vi.mock("@/app/(dashboard)/hooks/uiSettings/useUISettings", () => ({
|
||||
useUISettings: () => mockUseUISettings(),
|
||||
}));
|
||||
|
||||
const mockUseModelCostMap = vi.fn();
|
||||
vi.mock("@/app/(dashboard)/hooks/models/useModelCostMap", () => ({
|
||||
useModelCostMap: () => mockUseModelCostMap(),
|
||||
}));
|
||||
|
||||
const mockUseAuthorized = vi.fn();
|
||||
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
|
||||
default: () => mockUseAuthorized(),
|
||||
}));
|
||||
|
||||
const createQueryClient = () =>
|
||||
new QueryClient({
|
||||
defaultOptions: { queries: { retry: false, gcTime: 0 } },
|
||||
@@ -69,6 +69,17 @@ describe("ModelsAndEndpointsView", () => {
|
||||
mockUseUISettings.mockReturnValue({
|
||||
data: { values: {} },
|
||||
});
|
||||
mockUseModelCostMap.mockReturnValue({
|
||||
data: {},
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
mockUseAuthorized.mockReturnValue({
|
||||
accessToken: "123",
|
||||
token: "123",
|
||||
userRole: "Admin",
|
||||
userId: "123",
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(global as any).ResizeObserver = class {
|
||||
observe() {}
|
||||
@@ -82,10 +93,7 @@ describe("ModelsAndEndpointsView", () => {
|
||||
const { findByText } = render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ModelsAndEndpointsView
|
||||
accessToken="123"
|
||||
token="123"
|
||||
userRole="123"
|
||||
userID="123"
|
||||
modelData={{ data: [] }}
|
||||
keys={[]}
|
||||
setModelData={() => {}}
|
||||
|
||||
+25
-278
@@ -1,52 +1,34 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Col, Grid, Text } from "@tremor/react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { handleAddModelSubmit } from "@/components/add_model/handle_add_model_submit";
|
||||
|
||||
import { useCredentials } from "@/app/(dashboard)/hooks/credentials/useCredentials";
|
||||
import { useModelCostMap } from "@/app/(dashboard)/hooks/models/useModelCostMap";
|
||||
import { useModelsInfo } from "@/app/(dashboard)/hooks/models/useModels";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
import CredentialsPanel from "@/components/model_add/credentials";
|
||||
import {
|
||||
adminGlobalActivityExceptions,
|
||||
adminGlobalActivityExceptionsPerDeployment,
|
||||
allEndUsersCall,
|
||||
getCallbacksCall,
|
||||
modelCostMap,
|
||||
modelExceptionsCall,
|
||||
modelMetricsCall,
|
||||
modelMetricsSlowResponsesCall,
|
||||
modelSettingsCall,
|
||||
setCallbacksCall,
|
||||
streamingModelMetricsCall,
|
||||
} from "@/components/networking";
|
||||
import { Providers, getPlaceholder, getProviderModels } from "@/components/provider_info_helpers";
|
||||
import { getDisplayModelName } from "@/components/view_model/model_name_display";
|
||||
import { RefreshIcon } from "@heroicons/react/outline";
|
||||
import { DateRangePickerValue, Icon, Tab, TabGroup, TabList, TabPanel, TabPanels } from "@tremor/react";
|
||||
import type { UploadProps } from "antd";
|
||||
import { Form, Typography } from "antd";
|
||||
import AddModelTab from "../../../components/add_model/add_model_tab";
|
||||
import ModelInfoView from "../../../components/model_info_view";
|
||||
import TeamInfoView from "../../../components/team/team_info";
|
||||
|
||||
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
|
||||
import AllModelsTab from "@/app/(dashboard)/models-and-endpoints/components/AllModelsTab";
|
||||
import ModelAnalyticsTab from "@/app/(dashboard)/models-and-endpoints/components/ModelAnalyticsTab/ModelAnalyticsTab";
|
||||
import ModelRetrySettingsTab from "@/app/(dashboard)/models-and-endpoints/components/ModelRetrySettingsTab";
|
||||
import PriceDataManagementTab from "@/app/(dashboard)/models-and-endpoints/components/PriceDataManagementTab";
|
||||
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
|
||||
import { handleAddModelSubmit } from "@/components/add_model/handle_add_model_submit";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
import CredentialsPanel from "@/components/model_add/credentials";
|
||||
import { getCallbacksCall, setCallbacksCall } from "@/components/networking";
|
||||
import { Providers, getPlaceholder, getProviderModels } from "@/components/provider_info_helpers";
|
||||
import { getDisplayModelName } from "@/components/view_model/model_name_display";
|
||||
import { all_admin_roles, internalUserRoles, isProxyAdminRole, isUserTeamAdminForAnyTeam } from "@/utils/roles";
|
||||
import { RefreshIcon } from "@heroicons/react/outline";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Col, Grid, Icon, Tab, TabGroup, TabList, TabPanel, TabPanels, Text } from "@tremor/react";
|
||||
import type { UploadProps } from "antd";
|
||||
import { Form, Typography } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import AddModelTab from "../../../components/add_model/add_model_tab";
|
||||
import HealthCheckComponent from "../../../components/model_dashboard/HealthCheckComponent";
|
||||
import ModelGroupAliasSettings from "../../../components/model_group_alias_settings";
|
||||
import ModelInfoView from "../../../components/model_info_view";
|
||||
import NotificationsManager from "../../../components/molecules/notifications_manager";
|
||||
import PassThroughSettings from "../../../components/pass_through_settings";
|
||||
import TeamInfoView from "../../../components/team/team_info";
|
||||
import useAuthorized from "../hooks/useAuthorized";
|
||||
|
||||
interface ModelDashboardProps {
|
||||
accessToken: string | null;
|
||||
token: string | null;
|
||||
userRole: string | null;
|
||||
userID: string | null;
|
||||
modelData: any;
|
||||
keys: any[] | null;
|
||||
setModelData: any;
|
||||
@@ -82,77 +64,33 @@ interface ProviderSettings {
|
||||
}
|
||||
|
||||
const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
accessToken,
|
||||
token,
|
||||
userRole,
|
||||
userID,
|
||||
modelData = { data: [] },
|
||||
keys,
|
||||
setModelData,
|
||||
premiumUser,
|
||||
teams,
|
||||
}) => {
|
||||
const { accessToken, token, userRole, userId: userID } = useAuthorized();
|
||||
const [addModelForm] = Form.useForm();
|
||||
const [modelMap, setModelMap] = useState<any>(null);
|
||||
const [lastRefreshed, setLastRefreshed] = useState("");
|
||||
|
||||
const [providerModels, setProviderModels] = useState<Array<string>>([]); // Explicitly typing providerModels as a string array
|
||||
|
||||
const [providerSettings, setProviderSettings] = useState<ProviderSettings[]>([]);
|
||||
const [selectedProvider, setSelectedProvider] = useState<Providers>(Providers.Anthropic);
|
||||
const [editModalVisible, setEditModalVisible] = useState<boolean>(false);
|
||||
|
||||
const [selectedModel, setSelectedModel] = useState<any>(null);
|
||||
const [availableModelGroups, setAvailableModelGroups] = useState<Array<string>>([]);
|
||||
const [availableModelAccessGroups, setAvailableModelAccessGroups] = useState<Array<string>>([]);
|
||||
const [selectedModelGroup, setSelectedModelGroup] = useState<string | null>(null);
|
||||
const [modelMetrics, setModelMetrics] = useState<any[]>([]);
|
||||
const [modelMetricsCategories, setModelMetricsCategories] = useState<any[]>([]);
|
||||
const [streamingModelMetrics, setStreamingModelMetrics] = useState<any[]>([]);
|
||||
const [streamingModelMetricsCategories, setStreamingModelMetricsCategories] = useState<any[]>([]);
|
||||
const [modelExceptions, setModelExceptions] = useState<any[]>([]);
|
||||
const [allExceptions, setAllExceptions] = useState<any[]>([]);
|
||||
const [slowResponsesData, setSlowResponsesData] = useState<any[]>([]);
|
||||
const [dateValue, setDateValue] = useState<DateRangePickerValue>({
|
||||
from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
|
||||
to: new Date(),
|
||||
});
|
||||
|
||||
const [modelGroupRetryPolicy, setModelGroupRetryPolicy] = useState<RetryPolicyObject | null>(null);
|
||||
const [globalRetryPolicy, setGlobalRetryPolicy] = useState<GlobalRetryPolicyObject | null>(null);
|
||||
const [defaultRetry, setDefaultRetry] = useState<number>(0);
|
||||
|
||||
const [globalExceptionData, setGlobalExceptionData] = useState<GlobalExceptionActivityData>(
|
||||
{} as GlobalExceptionActivityData,
|
||||
);
|
||||
const [globalExceptionPerDeployment, setGlobalExceptionPerDeployment] = useState<any[]>([]);
|
||||
|
||||
const [showAdvancedFilters, setShowAdvancedFilters] = useState<boolean>(false);
|
||||
const [selectedAPIKey, setSelectedAPIKey] = useState<any | null>(null);
|
||||
const [selectedCustomer, setSelectedCustomer] = useState<any | null>(null);
|
||||
|
||||
const [allEndUsers, setAllEndUsers] = useState<any[]>([]);
|
||||
|
||||
// Model Group Alias state
|
||||
const [modelGroupAlias, setModelGroupAlias] = useState<{ [key: string]: string }>({});
|
||||
|
||||
// Add state for advanced settings visibility
|
||||
const [showAdvancedSettings, setShowAdvancedSettings] = useState<boolean>(false);
|
||||
|
||||
// Add these state variables
|
||||
const [selectedModelId, setSelectedModelId] = useState<string | null>(null);
|
||||
const [editModel, setEditModel] = useState<boolean>(false);
|
||||
|
||||
const [selectedTeamId, setSelectedTeamId] = useState<string | null>(null);
|
||||
const [selectedTeam, setSelectedTeam] = useState<string | null>(null);
|
||||
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const { data: modelDataResponse, isLoading: isLoadingModels, refetch: refetchModels } = useModelsInfo();
|
||||
const { data: modelCostMapData } = useModelCostMap();
|
||||
const { data: credentialsResponse } = useCredentials();
|
||||
const credentialsList = credentialsResponse?.credentials || [];
|
||||
const { data: uiSettings } = useUISettings(accessToken || "");
|
||||
@@ -166,21 +104,10 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
const shouldHideAddModelTab = !isProxyAdmin && (addModelDisabledForInternalUsers || !isUserTeamAdmin);
|
||||
|
||||
const setProviderModelsFn = (provider: Providers) => {
|
||||
const _providerModels = getProviderModels(provider, modelMap);
|
||||
const _providerModels = getProviderModels(provider, modelCostMapData);
|
||||
setProviderModels(_providerModels);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||
setIsDropdownOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const uploadProps: UploadProps = {
|
||||
name: "file",
|
||||
accept: ".json",
|
||||
@@ -196,7 +123,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
// Prevent upload
|
||||
return false;
|
||||
},
|
||||
onChange(info) {
|
||||
@@ -209,10 +135,8 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
};
|
||||
|
||||
const handleRefreshClick = () => {
|
||||
// Update the 'lastRefreshed' state to the current date and time
|
||||
const currentDate = new Date();
|
||||
setLastRefreshed(currentDate.toLocaleString());
|
||||
// Invalidate and refetch models data using React Query
|
||||
queryClient.invalidateQueries({ queryKey: ["models", "list"] });
|
||||
refetchModels();
|
||||
};
|
||||
@@ -228,7 +152,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
};
|
||||
|
||||
if (selectedModelGroup === "global") {
|
||||
// Only update global retry policy
|
||||
if (globalRetryPolicy) {
|
||||
payload.router_settings.retry_policy = globalRetryPolicy;
|
||||
}
|
||||
@@ -253,19 +176,12 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setModelData(modelDataResponse);
|
||||
const _providerSettings = await modelSettingsCall(accessToken);
|
||||
if (_providerSettings) {
|
||||
setProviderSettings(_providerSettings);
|
||||
}
|
||||
|
||||
// loop through modelDataResponse and get all`model_name` values
|
||||
let all_model_groups: Set<string> = new Set();
|
||||
for (let i = 0; i < modelDataResponse.data.length; i++) {
|
||||
const model = modelDataResponse.data[i];
|
||||
all_model_groups.add(model.model_name);
|
||||
}
|
||||
let _array_model_groups = Array.from(all_model_groups);
|
||||
// sort _array_model_groups alphabetically
|
||||
_array_model_groups = _array_model_groups.sort();
|
||||
|
||||
setAvailableModelGroups(_array_model_groups);
|
||||
@@ -286,80 +202,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
|
||||
setAvailableModelAccessGroups(Array.from(all_model_access_groups));
|
||||
|
||||
let _initial_model_group = "all";
|
||||
if (_array_model_groups.length > 0) {
|
||||
_initial_model_group = _array_model_groups[_array_model_groups.length - 1];
|
||||
}
|
||||
|
||||
const modelMetricsResponse = await modelMetricsCall(
|
||||
accessToken,
|
||||
userID,
|
||||
userRole,
|
||||
_initial_model_group,
|
||||
dateValue.from?.toISOString(),
|
||||
dateValue.to?.toISOString(),
|
||||
selectedAPIKey?.token,
|
||||
selectedCustomer,
|
||||
);
|
||||
|
||||
setModelMetrics(modelMetricsResponse.data);
|
||||
setModelMetricsCategories(modelMetricsResponse.all_api_bases);
|
||||
|
||||
const streamingModelMetricsResponse = await streamingModelMetricsCall(
|
||||
accessToken,
|
||||
_initial_model_group,
|
||||
dateValue.from?.toISOString(),
|
||||
dateValue.to?.toISOString(),
|
||||
);
|
||||
|
||||
// Assuming modelMetricsResponse now contains the metric data for the specified model group
|
||||
setStreamingModelMetrics(streamingModelMetricsResponse.data);
|
||||
setStreamingModelMetricsCategories(streamingModelMetricsResponse.all_api_bases);
|
||||
|
||||
const modelExceptionsResponse = await modelExceptionsCall(
|
||||
accessToken,
|
||||
userID,
|
||||
userRole,
|
||||
_initial_model_group,
|
||||
dateValue.from?.toISOString(),
|
||||
dateValue.to?.toISOString(),
|
||||
selectedAPIKey?.token,
|
||||
selectedCustomer,
|
||||
);
|
||||
setModelExceptions(modelExceptionsResponse.data);
|
||||
setAllExceptions(modelExceptionsResponse.exception_types);
|
||||
|
||||
const slowResponses = await modelMetricsSlowResponsesCall(
|
||||
accessToken,
|
||||
userID,
|
||||
userRole,
|
||||
_initial_model_group,
|
||||
dateValue.from?.toISOString(),
|
||||
dateValue.to?.toISOString(),
|
||||
selectedAPIKey?.token,
|
||||
selectedCustomer,
|
||||
);
|
||||
|
||||
const dailyExceptions = await adminGlobalActivityExceptions(
|
||||
accessToken,
|
||||
dateValue.from?.toISOString().split("T")[0],
|
||||
dateValue.to?.toISOString().split("T")[0],
|
||||
_initial_model_group,
|
||||
);
|
||||
|
||||
setGlobalExceptionData(dailyExceptions);
|
||||
|
||||
const dailyExceptionsPerDeplyment = await adminGlobalActivityExceptionsPerDeployment(
|
||||
accessToken,
|
||||
dateValue.from?.toISOString().split("T")[0],
|
||||
dateValue.to?.toISOString().split("T")[0],
|
||||
_initial_model_group,
|
||||
);
|
||||
|
||||
setGlobalExceptionPerDeployment(dailyExceptionsPerDeplyment);
|
||||
setSlowResponsesData(slowResponses);
|
||||
let all_end_users_data = await allEndUsersCall(accessToken);
|
||||
setAllEndUsers(all_end_users_data?.map((u: any) => u.user_id));
|
||||
const routerSettingsInfo = await getCallbacksCall(accessToken, userID, userRole);
|
||||
let router_settings = routerSettingsInfo.router_settings;
|
||||
|
||||
@@ -370,7 +212,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
setGlobalRetryPolicy(router_settings.retry_policy);
|
||||
setDefaultRetry(default_retries);
|
||||
|
||||
// Set model group alias
|
||||
const model_group_alias = router_settings.model_group_alias || {};
|
||||
setModelGroupAlias(model_group_alias);
|
||||
} catch (error) {
|
||||
@@ -381,24 +222,12 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
if (accessToken && token && userRole && userID && modelDataResponse) {
|
||||
fetchData();
|
||||
}
|
||||
|
||||
const fetchModelMap = async () => {
|
||||
const data = await modelCostMap();
|
||||
console.log(`received model cost map data: ${Object.keys(data)}`);
|
||||
setModelMap(data);
|
||||
};
|
||||
if (modelMap == null) {
|
||||
fetchModelMap();
|
||||
}
|
||||
}, [accessToken, token, userRole, userID, modelDataResponse]);
|
||||
|
||||
if (!modelData || isLoadingModels) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (!accessToken || !token || !userRole || !userID) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
let all_models_on_proxy: any[] = [];
|
||||
let all_providers: string[] = [];
|
||||
|
||||
@@ -409,7 +238,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
let custom_llm_provider = curr_model?.litellm_params?.custom_llm_provider;
|
||||
let model_info = curr_model?.model_info;
|
||||
|
||||
let defaultProvider = "openai";
|
||||
let provider = "";
|
||||
let input_cost = "Undefined";
|
||||
let output_cost = "Undefined";
|
||||
@@ -423,9 +251,9 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
* - check if model in model map
|
||||
* - return it's litellm_provider, if so
|
||||
*/
|
||||
if (modelMap !== null && modelMap !== undefined) {
|
||||
if (typeof modelMap == "object" && model in modelMap) {
|
||||
return modelMap[model]["litellm_provider"];
|
||||
if (modelCostMapData !== null && modelCostMapData !== undefined) {
|
||||
if (typeof modelCostMapData == "object" && model in modelCostMapData) {
|
||||
return modelCostMapData[model]["litellm_provider"];
|
||||
}
|
||||
}
|
||||
return "openai";
|
||||
@@ -495,46 +323,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const customTooltip = (props: any) => {
|
||||
const { payload, active } = props;
|
||||
if (!active || !payload) return null;
|
||||
|
||||
// Extract the date from the first item in the payload array
|
||||
const date = payload[0]?.payload?.date;
|
||||
|
||||
// Sort the payload array by category.value in descending order
|
||||
let sortedPayload = payload.sort((a: any, b: any) => b.value - a.value);
|
||||
|
||||
// Only show the top 5, the 6th one should be called "X other categories" depending on how many categories were not shown
|
||||
if (sortedPayload.length > 5) {
|
||||
let remainingItems = sortedPayload.length - 5;
|
||||
sortedPayload = sortedPayload.slice(0, 5);
|
||||
sortedPayload.push({
|
||||
dataKey: `${remainingItems} other deployments`,
|
||||
value: payload.slice(5).reduce((acc: number, curr: any) => acc + curr.value, 0),
|
||||
color: "gray",
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-150 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown">
|
||||
{date && <p className="text-tremor-content-emphasis mb-2">Date: {date}</p>}
|
||||
{sortedPayload.map((category: any, idx: number) => {
|
||||
const roundedValue = parseFloat(category.value.toFixed(5));
|
||||
const displayValue = roundedValue === 0 && category.value > 0 ? "<0.00001" : roundedValue.toFixed(5);
|
||||
return (
|
||||
<div key={idx} className="flex justify-between">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className={`w-2 h-2 mt-1 rounded-full bg-${category.color}-500`} />
|
||||
<p className="text-tremor-content">{category.dataKey}</p>
|
||||
</div>
|
||||
<p className="font-medium text-tremor-content-emphasis text-righ ml-2">{displayValue}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
@@ -589,17 +377,13 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
{selectedModelId ? (
|
||||
<ModelInfoView
|
||||
modelId={selectedModelId}
|
||||
editModel={true}
|
||||
onClose={() => {
|
||||
setSelectedModelId(null);
|
||||
setEditModel(false);
|
||||
}}
|
||||
modelData={modelData.data.find((model: any) => model.model_info.id === selectedModelId)}
|
||||
accessToken={accessToken}
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
setEditModalVisible={setEditModalVisible}
|
||||
setSelectedModel={setSelectedModel}
|
||||
onModelUpdate={(updatedModel) => {
|
||||
// Handle model deletion
|
||||
if (updatedModel.deleted) {
|
||||
@@ -633,7 +417,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
{all_admin_roles.includes(userRole) && <Tab>LLM Credentials</Tab>}
|
||||
{all_admin_roles.includes(userRole) && <Tab>Pass-Through Endpoints</Tab>}
|
||||
{all_admin_roles.includes(userRole) && <Tab>Health Status</Tab>}
|
||||
{all_admin_roles.includes(userRole) && <Tab>Model Analytics</Tab>}
|
||||
{all_admin_roles.includes(userRole) && <Tab>Model Retry Settings</Tab>}
|
||||
{all_admin_roles.includes(userRole) && <Tab>Model Group Alias</Tab>}
|
||||
{all_admin_roles.includes(userRole) && <Tab>Price Data Reload</Tab>}
|
||||
@@ -658,7 +441,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
availableModelAccessGroups={availableModelAccessGroups}
|
||||
setSelectedModelId={setSelectedModelId}
|
||||
setSelectedTeamId={setSelectedTeamId}
|
||||
setEditModel={setEditModel}
|
||||
/>
|
||||
{!shouldHideAddModelTab && (
|
||||
<TabPanel className="h-full">
|
||||
@@ -701,41 +483,6 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
setSelectedModelId={setSelectedModelId}
|
||||
/>
|
||||
</TabPanel>
|
||||
<ModelAnalyticsTab
|
||||
dateValue={dateValue}
|
||||
setDateValue={setDateValue}
|
||||
selectedModelGroup={selectedModelGroup}
|
||||
availableModelGroups={availableModelGroups}
|
||||
setShowAdvancedFilters={setShowAdvancedFilters}
|
||||
modelMetrics={modelMetrics}
|
||||
modelMetricsCategories={modelMetricsCategories}
|
||||
streamingModelMetrics={streamingModelMetrics}
|
||||
streamingModelMetricsCategories={streamingModelMetricsCategories}
|
||||
customTooltip={customTooltip}
|
||||
slowResponsesData={slowResponsesData}
|
||||
modelExceptions={modelExceptions}
|
||||
globalExceptionData={globalExceptionData}
|
||||
allExceptions={allExceptions}
|
||||
globalExceptionPerDeployment={globalExceptionPerDeployment}
|
||||
allEndUsers={allEndUsers}
|
||||
keys={keys}
|
||||
setSelectedAPIKey={setSelectedAPIKey}
|
||||
setSelectedCustomer={setSelectedCustomer}
|
||||
teams={teams}
|
||||
selectedAPIKey={selectedAPIKey}
|
||||
selectedCustomer={selectedCustomer}
|
||||
selectedTeam={selectedTeam}
|
||||
setAllExceptions={setAllExceptions}
|
||||
setGlobalExceptionData={setGlobalExceptionData}
|
||||
setGlobalExceptionPerDeployment={setGlobalExceptionPerDeployment}
|
||||
setModelExceptions={setModelExceptions}
|
||||
setModelMetrics={setModelMetrics}
|
||||
setModelMetricsCategories={setModelMetricsCategories}
|
||||
setSelectedModelGroup={setSelectedModelGroup}
|
||||
setSlowResponsesData={setSlowResponsesData}
|
||||
setStreamingModelMetrics={setStreamingModelMetrics}
|
||||
setStreamingModelMetricsCategories={setStreamingModelMetricsCategories}
|
||||
/>
|
||||
<ModelRetrySettingsTab
|
||||
selectedModelGroup={selectedModelGroup}
|
||||
setSelectedModelGroup={setSelectedModelGroup}
|
||||
@@ -754,7 +501,7 @@ const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
|
||||
onAliasUpdate={setModelGroupAlias}
|
||||
/>
|
||||
</TabPanel>
|
||||
<PriceDataManagementTab setModelMap={setModelMap} />
|
||||
<PriceDataManagementTab />
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
)}
|
||||
|
||||
-3
@@ -19,7 +19,6 @@ interface AllModelsTabProps {
|
||||
availableModelAccessGroups: string[];
|
||||
setSelectedModelId: (id: string) => void;
|
||||
setSelectedTeamId: (id: string) => void;
|
||||
setEditModel: (edit: boolean) => void;
|
||||
}
|
||||
|
||||
const AllModelsTab = ({
|
||||
@@ -29,7 +28,6 @@ const AllModelsTab = ({
|
||||
availableModelAccessGroups,
|
||||
setSelectedModelId,
|
||||
setSelectedTeamId,
|
||||
setEditModel,
|
||||
}: AllModelsTabProps) => {
|
||||
const { data: modelData } = useModelsInfo();
|
||||
const { userId, userRole, premiumUser } = useAuthorized();
|
||||
@@ -359,7 +357,6 @@ const AllModelsTab = ({
|
||||
getDisplayModelName,
|
||||
() => {},
|
||||
() => {},
|
||||
setEditModel,
|
||||
expandedRows,
|
||||
setExpandedRows,
|
||||
)}
|
||||
|
||||
+4
-12
@@ -1,15 +1,12 @@
|
||||
import { TabPanel, Text, Title } from "@tremor/react";
|
||||
import PriceDataReload from "@/components/price_data_reload";
|
||||
import { modelCostMap } from "@/components/networking";
|
||||
import React from "react";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { useModelCostMap } from "../../hooks/models/useModelCostMap";
|
||||
|
||||
interface PriceDataManagementPanelProps {
|
||||
setModelMap: (data: any) => void;
|
||||
}
|
||||
|
||||
const PriceDataManagementTab = ({ setModelMap }: PriceDataManagementPanelProps) => {
|
||||
const PriceDataManagementTab = () => {
|
||||
const { accessToken } = useAuthorized();
|
||||
const { refetch: refetchModelCostMap } = useModelCostMap();
|
||||
|
||||
return (
|
||||
<TabPanel>
|
||||
@@ -23,12 +20,7 @@ const PriceDataManagementTab = ({ setModelMap }: PriceDataManagementPanelProps)
|
||||
<PriceDataReload
|
||||
accessToken={accessToken}
|
||||
onReloadSuccess={() => {
|
||||
// Refresh the model map after successful reload
|
||||
const fetchModelMap = async () => {
|
||||
const data = await modelCostMap();
|
||||
setModelMap(data);
|
||||
};
|
||||
fetchModelMap();
|
||||
refetchModelCostMap();
|
||||
}}
|
||||
buttonText="Reload Price Data"
|
||||
size="middle"
|
||||
|
||||
@@ -6,17 +6,14 @@ import { useState } from "react";
|
||||
import ModelsAndEndpointsView from "@/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView";
|
||||
|
||||
const ModelsAndEndpointsPage = () => {
|
||||
const { token, accessToken, userRole, userId, premiumUser } = useAuthorized();
|
||||
const { token, premiumUser } = useAuthorized();
|
||||
const [keys, setKeys] = useState<null | any[]>([]);
|
||||
|
||||
const { teams } = useTeams();
|
||||
|
||||
return (
|
||||
<ModelsAndEndpointsView
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
userRole={userRole}
|
||||
userID={userId}
|
||||
modelData={{ data: [] }}
|
||||
keys={keys}
|
||||
setModelData={() => {}}
|
||||
|
||||
@@ -323,11 +323,8 @@ export default function CreateKeyPage() {
|
||||
/>
|
||||
) : page == "models" ? (
|
||||
<OldModelDashboard
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
token={token}
|
||||
keys={keys}
|
||||
accessToken={accessToken}
|
||||
modelData={modelData}
|
||||
setModelData={setModelData}
|
||||
premiumUser={premiumUser}
|
||||
|
||||
@@ -47,9 +47,6 @@ interface ModelInfoViewProps {
|
||||
accessToken: string | null;
|
||||
userID: string | null;
|
||||
userRole: string | null;
|
||||
editModel: boolean;
|
||||
setEditModalVisible: (visible: boolean) => void;
|
||||
setSelectedModel: (model: any) => void;
|
||||
onModelUpdate?: (updatedModel: any) => void;
|
||||
modelAccessGroups: string[] | null;
|
||||
}
|
||||
@@ -61,9 +58,6 @@ export default function ModelInfoView({
|
||||
accessToken,
|
||||
userID,
|
||||
userRole,
|
||||
editModel,
|
||||
setEditModalVisible,
|
||||
setSelectedModel,
|
||||
onModelUpdate,
|
||||
modelAccessGroups,
|
||||
}: ModelInfoViewProps) {
|
||||
|
||||
@@ -14,7 +14,6 @@ export const columns = (
|
||||
getDisplayModelName: (model: any) => string,
|
||||
handleEditClick: (model: any) => void,
|
||||
handleRefreshClick: () => void,
|
||||
setEditModel: (edit: boolean) => void,
|
||||
expandedRows: Set<string>,
|
||||
setExpandedRows: (expandedRows: Set<string>) => void,
|
||||
): ColumnDef<ModelData>[] => [
|
||||
@@ -301,7 +300,6 @@ export const columns = (
|
||||
onClick={() => {
|
||||
if (canEditModel) {
|
||||
setSelectedModelId(model.model_info.id);
|
||||
setEditModel(false);
|
||||
}
|
||||
}}
|
||||
className={!canEditModel ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:text-red-600"}
|
||||
|
||||
@@ -1021,7 +1021,6 @@ const OldModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
{selectedModelId ? (
|
||||
<ModelInfoView
|
||||
modelId={selectedModelId}
|
||||
editModel={true}
|
||||
onClose={() => {
|
||||
setSelectedModelId(null);
|
||||
setEditModel(false);
|
||||
@@ -1030,8 +1029,6 @@ const OldModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
accessToken={accessToken}
|
||||
userID={userID}
|
||||
userRole={userRole}
|
||||
setEditModalVisible={setEditModalVisible}
|
||||
setSelectedModel={setSelectedModel}
|
||||
onModelUpdate={(updatedModel) => {
|
||||
// Update the model in the modelData.data array
|
||||
const updatedModelData = {
|
||||
@@ -1327,7 +1324,6 @@ const OldModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
getDisplayModelName,
|
||||
handleEditClick,
|
||||
handleRefreshClick,
|
||||
setEditModel,
|
||||
expandedRows,
|
||||
setExpandedRows,
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user