diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.test.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.test.ts new file mode 100644 index 0000000000..325503408c --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.test.ts @@ -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 = { + "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"); + }); +}); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.ts new file mode 100644 index 0000000000..2d82eedf25 --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/models/useModelCostMap.ts @@ -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>({ + queryKey: modelCostMapKeys.list({}), + queryFn: async () => await modelCostMap(), + staleTime: 60 * 1000, // 1 minute + gcTime: 60 * 1000, // 1 minute + }); +}; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx index 8dc7d1ff3d..1e8eabaea2 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.test.tsx @@ -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( {}} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx index 4b62ce8cf8..e833686542 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView.tsx @@ -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 = ({ - 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(null); const [lastRefreshed, setLastRefreshed] = useState(""); - const [providerModels, setProviderModels] = useState>([]); // Explicitly typing providerModels as a string array - - const [providerSettings, setProviderSettings] = useState([]); const [selectedProvider, setSelectedProvider] = useState(Providers.Anthropic); - const [editModalVisible, setEditModalVisible] = useState(false); - - const [selectedModel, setSelectedModel] = useState(null); const [availableModelGroups, setAvailableModelGroups] = useState>([]); const [availableModelAccessGroups, setAvailableModelAccessGroups] = useState>([]); const [selectedModelGroup, setSelectedModelGroup] = useState(null); - const [modelMetrics, setModelMetrics] = useState([]); - const [modelMetricsCategories, setModelMetricsCategories] = useState([]); - const [streamingModelMetrics, setStreamingModelMetrics] = useState([]); - const [streamingModelMetricsCategories, setStreamingModelMetricsCategories] = useState([]); - const [modelExceptions, setModelExceptions] = useState([]); - const [allExceptions, setAllExceptions] = useState([]); - const [slowResponsesData, setSlowResponsesData] = useState([]); - const [dateValue, setDateValue] = useState({ - from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), - to: new Date(), - }); const [modelGroupRetryPolicy, setModelGroupRetryPolicy] = useState(null); const [globalRetryPolicy, setGlobalRetryPolicy] = useState(null); const [defaultRetry, setDefaultRetry] = useState(0); - - const [globalExceptionData, setGlobalExceptionData] = useState( - {} as GlobalExceptionActivityData, - ); - const [globalExceptionPerDeployment, setGlobalExceptionPerDeployment] = useState([]); - - const [showAdvancedFilters, setShowAdvancedFilters] = useState(false); - const [selectedAPIKey, setSelectedAPIKey] = useState(null); - const [selectedCustomer, setSelectedCustomer] = useState(null); - - const [allEndUsers, setAllEndUsers] = useState([]); - - // Model Group Alias state const [modelGroupAlias, setModelGroupAlias] = useState<{ [key: string]: string }>({}); - - // Add state for advanced settings visibility const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); - - // Add these state variables const [selectedModelId, setSelectedModelId] = useState(null); - const [editModel, setEditModel] = useState(false); - const [selectedTeamId, setSelectedTeamId] = useState(null); - const [selectedTeam, setSelectedTeam] = useState(null); - - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const dropdownRef = useRef(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 = ({ 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 = ({ }; reader.readAsText(file); } - // Prevent upload return false; }, onChange(info) { @@ -209,10 +135,8 @@ const ModelsAndEndpointsView: React.FC = ({ }; 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 = ({ }; if (selectedModelGroup === "global") { - // Only update global retry policy if (globalRetryPolicy) { payload.router_settings.retry_policy = globalRetryPolicy; } @@ -253,19 +176,12 @@ const ModelsAndEndpointsView: React.FC = ({ 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 = 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 = ({ 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 = ({ 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 = ({ 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
Loading...
; } - if (!accessToken || !token || !userRole || !userID) { - return
Loading...
; - } let all_models_on_proxy: any[] = []; let all_providers: string[] = []; @@ -409,7 +238,6 @@ const ModelsAndEndpointsView: React.FC = ({ 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 = ({ * - 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 = ({ ); } - 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 ( -
- {date &&

Date: {date}

} - {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 ( -
-
-
-

{category.dataKey}

-
-

{displayValue}

-
- ); - })} -
- ); - }; const handleOk = async () => { try { @@ -589,17 +377,13 @@ const ModelsAndEndpointsView: React.FC = ({ {selectedModelId ? ( { 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 = ({ {all_admin_roles.includes(userRole) && LLM Credentials} {all_admin_roles.includes(userRole) && Pass-Through Endpoints} {all_admin_roles.includes(userRole) && Health Status} - {all_admin_roles.includes(userRole) && Model Analytics} {all_admin_roles.includes(userRole) && Model Retry Settings} {all_admin_roles.includes(userRole) && Model Group Alias} {all_admin_roles.includes(userRole) && Price Data Reload} @@ -658,7 +441,6 @@ const ModelsAndEndpointsView: React.FC = ({ availableModelAccessGroups={availableModelAccessGroups} setSelectedModelId={setSelectedModelId} setSelectedTeamId={setSelectedTeamId} - setEditModel={setEditModel} /> {!shouldHideAddModelTab && ( @@ -701,41 +483,6 @@ const ModelsAndEndpointsView: React.FC = ({ setSelectedModelId={setSelectedModelId} /> - = ({ onAliasUpdate={setModelGroupAlias} /> - + )} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx index 04c05ede5c..8df181ecab 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/AllModelsTab.tsx @@ -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, )} diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/PriceDataManagementTab.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/PriceDataManagementTab.tsx index 4076c19c66..d44d19879d 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/PriceDataManagementTab.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/components/PriceDataManagementTab.tsx @@ -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 ( @@ -23,12 +20,7 @@ const PriceDataManagementTab = ({ setModelMap }: PriceDataManagementPanelProps) { - // Refresh the model map after successful reload - const fetchModelMap = async () => { - const data = await modelCostMap(); - setModelMap(data); - }; - fetchModelMap(); + refetchModelCostMap(); }} buttonText="Reload Price Data" size="middle" diff --git a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx index 01dd97505c..77496aef3e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/models-and-endpoints/page.tsx @@ -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([]); const { teams } = useTeams(); return ( {}} diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 6b94f514d9..c0d6e52ae1 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -323,11 +323,8 @@ export default function CreateKeyPage() { /> ) : page == "models" ? ( 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) { diff --git a/ui/litellm-dashboard/src/components/molecules/models/columns.tsx b/ui/litellm-dashboard/src/components/molecules/models/columns.tsx index c1a16e3936..43813852f8 100644 --- a/ui/litellm-dashboard/src/components/molecules/models/columns.tsx +++ b/ui/litellm-dashboard/src/components/molecules/models/columns.tsx @@ -14,7 +14,6 @@ export const columns = ( getDisplayModelName: (model: any) => string, handleEditClick: (model: any) => void, handleRefreshClick: () => void, - setEditModel: (edit: boolean) => void, expandedRows: Set, setExpandedRows: (expandedRows: Set) => void, ): ColumnDef[] => [ @@ -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"} diff --git a/ui/litellm-dashboard/src/components/templates/model_dashboard.tsx b/ui/litellm-dashboard/src/components/templates/model_dashboard.tsx index d0db420480..e3a27dc11f 100644 --- a/ui/litellm-dashboard/src/components/templates/model_dashboard.tsx +++ b/ui/litellm-dashboard/src/components/templates/model_dashboard.tsx @@ -1021,7 +1021,6 @@ const OldModelDashboard: React.FC = ({ {selectedModelId ? ( { setSelectedModelId(null); setEditModel(false); @@ -1030,8 +1029,6 @@ const OldModelDashboard: React.FC = ({ 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 = ({ getDisplayModelName, handleEditClick, handleRefreshClick, - setEditModel, expandedRows, setExpandedRows, )}