Spaces:
Paused
Paused
| import React, { useState, useEffect, useRef } from "react"; | |
| import { | |
| Card, | |
| Title, | |
| Subtitle, | |
| Table, | |
| TableHead, | |
| TableRow, | |
| TableHeaderCell, | |
| TableCell, | |
| TableBody, | |
| Metric, | |
| Text, | |
| Grid, | |
| Accordion, | |
| AccordionHeader, | |
| AccordionBody, | |
| } from "@tremor/react"; | |
| import { CredentialItem, credentialListCall, CredentialsResponse } from "./networking"; | |
| import ConditionalPublicModelName from "./add_model/conditional_public_model_name"; | |
| import LiteLLMModelNameField from "./add_model/litellm_model_name"; | |
| import AdvancedSettings from "./add_model/advanced_settings"; | |
| import ProviderSpecificFields from "./add_model/provider_specific_fields"; | |
| import { handleAddModelSubmit } from "./add_model/handle_add_model_submit"; | |
| import CredentialsPanel from "@/components/model_add/credentials"; | |
| import { getDisplayModelName } from "./view_model/model_name_display"; | |
| import EditModelModal, { handleEditModelSubmit } from "./edit_model/edit_model_modal"; | |
| import { | |
| TabPanel, | |
| TabPanels, | |
| TabGroup, | |
| TabList, | |
| Tab, | |
| TextInput, | |
| Icon, | |
| DateRangePicker, | |
| } from "@tremor/react"; | |
| import { | |
| Select, | |
| SelectItem, | |
| MultiSelect, | |
| MultiSelectItem, | |
| DateRangePickerValue, | |
| } from "@tremor/react"; | |
| import { | |
| modelInfoCall, | |
| userGetRequesedtModelsCall, | |
| modelCreateCall, | |
| Model, | |
| modelCostMap, | |
| modelDeleteCall, | |
| healthCheckCall, | |
| modelUpdateCall, | |
| modelMetricsCall, | |
| streamingModelMetricsCall, | |
| modelExceptionsCall, | |
| modelMetricsSlowResponsesCall, | |
| getCallbacksCall, | |
| setCallbacksCall, | |
| modelSettingsCall, | |
| adminGlobalActivityExceptions, | |
| adminGlobalActivityExceptionsPerDeployment, | |
| allEndUsersCall, | |
| } from "./networking"; | |
| import { BarChart, AreaChart } from "@tremor/react"; | |
| import { | |
| Button as Button2, | |
| Modal, | |
| Popover, | |
| Form, | |
| Input, | |
| Select as AntdSelect, | |
| InputNumber, | |
| message, | |
| Descriptions, | |
| Tooltip, | |
| Space, | |
| Row, | |
| Col, | |
| } from "antd"; | |
| import { Badge, BadgeDelta, Button } from "@tremor/react"; | |
| import RequestAccess from "./request_model_access"; | |
| import { Typography } from "antd"; | |
| import TextArea from "antd/es/input/TextArea"; | |
| import { | |
| InformationCircleIcon, | |
| PencilAltIcon, | |
| PencilIcon, | |
| StatusOnlineIcon, | |
| TrashIcon, | |
| RefreshIcon, | |
| CheckCircleIcon, | |
| XCircleIcon, | |
| FilterIcon, | |
| ChevronUpIcon, | |
| ChevronDownIcon, | |
| TableIcon, | |
| } from "@heroicons/react/outline"; | |
| import DeleteModelButton from "./delete_model_button"; | |
| const { Title: Title2, Link } = Typography; | |
| import { UploadOutlined } from "@ant-design/icons"; | |
| import type { UploadProps } from "antd"; | |
| import { Upload } from "antd"; | |
| import TimeToFirstToken from "./model_metrics/time_to_first_token"; | |
| import DynamicFields from "./model_add/dynamic_form"; | |
| import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; | |
| import { Team } from "./key_team_helpers/key_list"; | |
| import TeamInfoView from "./team/team_info"; | |
| import { Providers, provider_map, providerLogoMap, getProviderLogoAndName, getPlaceholder, getProviderModels } from "./provider_info_helpers"; | |
| import ModelInfoView from "./model_info_view"; | |
| import AddModelTab from "./add_model/add_model_tab"; | |
| import { ModelDataTable } from "./model_dashboard/table"; | |
| import { columns } from "./model_dashboard/columns"; | |
| import { all_admin_roles } from "@/utils/roles"; | |
| import { Table as TableInstance } from '@tanstack/react-table'; | |
| interface ModelDashboardProps { | |
| accessToken: string | null; | |
| token: string | null; | |
| userRole: string | null; | |
| userID: string | null; | |
| modelData: any; | |
| keys: any[] | null; | |
| setModelData: any; | |
| premiumUser: boolean; | |
| teams: Team[] | null; | |
| } | |
| interface EditModelModalProps { | |
| visible: boolean; | |
| onCancel: () => void; | |
| model: any; // Assuming TeamType is a type representing your team object | |
| onSubmit: (data: FormData) => void; // Assuming FormData is the type of data to be submitted | |
| } | |
| interface RetryPolicyObject { | |
| [key: string]: { [retryPolicyKey: string]: number } | undefined; | |
| } | |
| interface GlobalExceptionActivityData { | |
| sum_num_rate_limit_exceptions: number; | |
| daily_data: { date: string; num_rate_limit_exceptions: number; }[]; | |
| } | |
| //["OpenAI", "Azure OpenAI", "Anthropic", "Gemini (Google AI Studio)", "Amazon Bedrock", "OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)"] | |
| interface ProviderFields { | |
| field_name: string; | |
| field_type: string; | |
| field_description: string; | |
| field_value: string; | |
| } | |
| interface ProviderSettings { | |
| name: string; | |
| fields: ProviderFields[]; | |
| } | |
| const retry_policy_map: Record<string, string> = { | |
| "BadRequestError (400)": "BadRequestErrorRetries", | |
| "AuthenticationError (401)": "AuthenticationErrorRetries", | |
| "TimeoutError (408)": "TimeoutErrorRetries", | |
| "RateLimitError (429)": "RateLimitErrorRetries", | |
| "ContentPolicyViolationError (400)": "ContentPolicyViolationErrorRetries", | |
| "InternalServerError (500)": "InternalServerErrorRetries", | |
| }; | |
| const ModelDashboard: React.FC<ModelDashboardProps> = ({ | |
| accessToken, | |
| token, | |
| userRole, | |
| userID, | |
| modelData = { data: [] }, | |
| keys, | |
| setModelData, | |
| premiumUser, | |
| teams, | |
| }) => { | |
| const [pendingRequests, setPendingRequests] = useState<any[]>([]); | |
| const [form] = 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 providers = Object.values(Providers).filter((key) => | |
| isNaN(Number(key)) | |
| ); | |
| const [providerSettings, setProviderSettings] = useState<ProviderSettings[]>( | |
| [] | |
| ); | |
| const [selectedProvider, setSelectedProvider] = useState<Providers>(Providers.OpenAI); | |
| const [healthCheckResponse, setHealthCheckResponse] = useState<string>(""); | |
| const [editModalVisible, setEditModalVisible] = useState<boolean>(false); | |
| const [selectedModel, setSelectedModel] = useState<any>(null); | |
| const [availableModelGroups, setAvailableModelGroups] = useState< | |
| Array<string> | |
| >([]); | |
| const [availableProviders, setavailableProviders] = useState< | |
| Array<string> | |
| >([]); | |
| const [selectedModelGroup, setSelectedModelGroup] = useState<string | null>( | |
| null | |
| ); | |
| const [modelLatencyMetrics, setModelLatencyMetrics] = useState<any[]>([]); | |
| 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 [failureTableData, setFailureTableData] = 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 [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[]>([]); | |
| const [credentialsList, setCredentialsList] = useState<CredentialItem[]>([]); | |
| // 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 [selectedTeamFilter, setSelectedTeamFilter] = useState<string | null>(null); | |
| const [showColumnDropdown, setShowColumnDropdown] = useState(false); | |
| const [isDropdownOpen, setIsDropdownOpen] = useState(false); | |
| const dropdownRef = useRef<HTMLDivElement>(null); | |
| const tableRef = useRef<TableInstance<any>>(null); | |
| const setProviderModelsFn = (provider: Providers) => { | |
| const _providerModels = getProviderModels(provider, modelMap); | |
| setProviderModels(_providerModels); | |
| console.log(`providerModels: ${_providerModels}`); | |
| }; | |
| const updateModelMetrics = async ( | |
| modelGroup: string | null, | |
| startTime: Date | undefined, | |
| endTime: Date | undefined, | |
| ) => { | |
| console.log("Updating model metrics for group:", modelGroup); | |
| if (!accessToken || !userID || !userRole || !startTime || !endTime) { | |
| return; | |
| } | |
| console.log( | |
| "inside updateModelMetrics - startTime:", | |
| startTime, | |
| "endTime:", | |
| endTime | |
| ); | |
| setSelectedModelGroup(modelGroup); | |
| let selected_token = selectedAPIKey?.token; | |
| if (selected_token === undefined) { | |
| selected_token = null; | |
| } | |
| let selected_customer = selectedCustomer; | |
| if (selected_customer === undefined) { | |
| selected_customer = null; | |
| } | |
| // make startTime and endTime to last hour of the day | |
| startTime.setHours(0); | |
| startTime.setMinutes(0); | |
| startTime.setSeconds(0); | |
| endTime.setHours(23); | |
| endTime.setMinutes(59); | |
| endTime.setSeconds(59); | |
| try { | |
| const modelMetricsResponse = await modelMetricsCall( | |
| accessToken, | |
| userID, | |
| userRole, | |
| modelGroup, | |
| startTime.toISOString(), | |
| endTime.toISOString(), | |
| selected_token, | |
| selected_customer | |
| ); | |
| console.log("Model metrics response:", modelMetricsResponse); | |
| // Assuming modelMetricsResponse now contains the metric data for the specified model group | |
| setModelMetrics(modelMetricsResponse.data); | |
| setModelMetricsCategories(modelMetricsResponse.all_api_bases); | |
| const streamingModelMetricsResponse = await streamingModelMetricsCall( | |
| accessToken, | |
| modelGroup, | |
| startTime.toISOString(), | |
| endTime.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, | |
| modelGroup, | |
| startTime.toISOString(), | |
| endTime.toISOString(), | |
| selected_token, | |
| selected_customer | |
| ); | |
| console.log("Model exceptions response:", modelExceptionsResponse); | |
| setModelExceptions(modelExceptionsResponse.data); | |
| setAllExceptions(modelExceptionsResponse.exception_types); | |
| const slowResponses = await modelMetricsSlowResponsesCall( | |
| accessToken, | |
| userID, | |
| userRole, | |
| modelGroup, | |
| startTime.toISOString(), | |
| endTime.toISOString(), | |
| selected_token, | |
| selected_customer | |
| ); | |
| console.log("slowResponses:", slowResponses); | |
| setSlowResponsesData(slowResponses); | |
| if (modelGroup) { | |
| const dailyExceptions = await adminGlobalActivityExceptions( | |
| accessToken, | |
| startTime?.toISOString().split('T')[0], | |
| endTime?.toISOString().split('T')[0], | |
| modelGroup, | |
| ); | |
| setGlobalExceptionData(dailyExceptions); | |
| const dailyExceptionsPerDeplyment = await adminGlobalActivityExceptionsPerDeployment( | |
| accessToken, | |
| startTime?.toISOString().split('T')[0], | |
| endTime?.toISOString().split('T')[0], | |
| modelGroup, | |
| ) | |
| setGlobalExceptionPerDeployment(dailyExceptionsPerDeplyment); | |
| } | |
| } catch (error) { | |
| console.error("Failed to fetch model metrics", error); | |
| } | |
| }; | |
| const fetchCredentials = async (accessToken: string) => { | |
| try { | |
| const response: CredentialsResponse = await credentialListCall(accessToken); | |
| console.log(`credentials: ${JSON.stringify(response)}`); | |
| setCredentialsList(response.credentials); | |
| } catch (error) { | |
| console.error('Error fetching credentials:', error); | |
| } | |
| }; | |
| useEffect(() => { | |
| updateModelMetrics( | |
| selectedModelGroup, | |
| dateValue.from, | |
| dateValue.to | |
| ); | |
| }, [selectedAPIKey, selectedCustomer, selectedTeam]); | |
| 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); | |
| }, []); | |
| function formatCreatedAt(createdAt: string | null) { | |
| if (createdAt) { | |
| const date = new Date(createdAt); | |
| const options = { month: "long", day: "numeric", year: "numeric" }; | |
| return date.toLocaleDateString("en-US"); | |
| } | |
| return null; | |
| } | |
| const handleEditClick = (model: any) => { | |
| setSelectedModel(model); | |
| setEditModalVisible(true); | |
| }; | |
| const handleEditCancel = () => { | |
| setEditModalVisible(false); | |
| setSelectedModel(null); | |
| }; | |
| const uploadProps: UploadProps = { | |
| name: "file", | |
| accept: ".json", | |
| beforeUpload: (file) => { | |
| if (file.type === "application/json") { | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| if (e.target) { | |
| const jsonStr = e.target.result as string; | |
| form.setFieldsValue({ vertex_credentials: jsonStr }); | |
| } | |
| }; | |
| reader.readAsText(file); | |
| } | |
| // Prevent upload | |
| return false; | |
| }, | |
| onChange(info) { | |
| if (info.file.status !== "uploading") { | |
| console.log(info.file, info.fileList); | |
| } | |
| if (info.file.status === "done") { | |
| message.success(`${info.file.name} file uploaded successfully`); | |
| } else if (info.file.status === "error") { | |
| message.error(`${info.file.name} file upload failed.`); | |
| } | |
| }, | |
| }; | |
| const handleRefreshClick = () => { | |
| // Update the 'lastRefreshed' state to the current date and time | |
| const currentDate = new Date(); | |
| setLastRefreshed(currentDate.toLocaleString()); | |
| }; | |
| const handleSaveRetrySettings = async () => { | |
| if (!accessToken) { | |
| console.error("Access token is missing"); | |
| return; | |
| } | |
| console.log("new modelGroupRetryPolicy:", modelGroupRetryPolicy); | |
| try { | |
| const payload = { | |
| router_settings: { | |
| model_group_retry_policy: modelGroupRetryPolicy, | |
| }, | |
| }; | |
| await setCallbacksCall(accessToken, payload); | |
| message.success("Retry settings saved successfully"); | |
| } catch (error) { | |
| console.error("Failed to save retry settings:", error); | |
| message.error("Failed to save retry settings"); | |
| } | |
| }; | |
| useEffect(() => { | |
| if (!accessToken || !token || !userRole || !userID) { | |
| return; | |
| } | |
| const fetchData = async () => { | |
| try { | |
| // Replace with your actual API call for model data | |
| const modelDataResponse = await modelInfoCall( | |
| accessToken, | |
| userID, | |
| userRole | |
| ); | |
| console.log("Model data response:", modelDataResponse.data); | |
| 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); | |
| } | |
| console.log("all_model_groups:", all_model_groups); | |
| 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); | |
| console.log("array_model_groups:", _array_model_groups); | |
| let _initial_model_group = "all"; | |
| if (_array_model_groups.length > 0) { | |
| // set selectedModelGroup to the last model group | |
| _initial_model_group = | |
| _array_model_groups[_array_model_groups.length - 1]; | |
| console.log("_initial_model_group:", _initial_model_group); | |
| //setSelectedModelGroup(_initial_model_group); | |
| } | |
| console.log("selectedModelGroup:", selectedModelGroup); | |
| const modelMetricsResponse = await modelMetricsCall( | |
| accessToken, | |
| userID, | |
| userRole, | |
| _initial_model_group, | |
| dateValue.from?.toISOString(), | |
| dateValue.to?.toISOString(), | |
| selectedAPIKey?.token, | |
| selectedCustomer | |
| ); | |
| console.log("Model metrics response:", modelMetricsResponse); | |
| // Sort by latency (avg_latency_per_token) | |
| 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 | |
| ); | |
| console.log("Model exceptions response:", modelExceptionsResponse); | |
| 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); | |
| console.log("dailyExceptions:", dailyExceptions); | |
| console.log("dailyExceptionsPerDeplyment:", dailyExceptionsPerDeplyment); | |
| console.log("slowResponses:", slowResponses); | |
| setSlowResponsesData(slowResponses); | |
| let all_end_users_data = await allEndUsersCall(accessToken); | |
| setAllEndUsers(all_end_users_data?.end_users); | |
| const routerSettingsInfo = await getCallbacksCall( | |
| accessToken, | |
| userID, | |
| userRole | |
| ); | |
| let router_settings = routerSettingsInfo.router_settings; | |
| console.log("routerSettingsInfo:", router_settings); | |
| let model_group_retry_policy = router_settings.model_group_retry_policy; | |
| let default_retries = router_settings.num_retries; | |
| console.log("model_group_retry_policy:", model_group_retry_policy); | |
| console.log("default_retries:", default_retries); | |
| setModelGroupRetryPolicy(model_group_retry_policy); | |
| setDefaultRetry(default_retries); | |
| } catch (error) { | |
| console.error("There was an error fetching the model data", error); | |
| } | |
| }; | |
| if (accessToken && token && userRole && userID) { | |
| fetchData(); | |
| } | |
| const fetchModelMap = async () => { | |
| const data = await modelCostMap(accessToken); | |
| console.log(`received model cost map data: ${Object.keys(data)}`); | |
| setModelMap(data); | |
| }; | |
| if (modelMap == null) { | |
| fetchModelMap(); | |
| } | |
| handleRefreshClick(); | |
| }, [accessToken, token, userRole, userID, modelMap, lastRefreshed, selectedTeam]); | |
| if (!modelData) { | |
| return <div>Loading...</div>; | |
| } | |
| if (!accessToken || !token || !userRole || !userID) { | |
| return <div>Loading...</div>; | |
| } | |
| let all_models_on_proxy: any[] = []; | |
| let all_providers: string[] = []; | |
| // loop through model data and edit each row | |
| for (let i = 0; i < modelData.data.length; i++) { | |
| let curr_model = modelData.data[i]; | |
| let litellm_model_name = curr_model?.litellm_params?.model; | |
| 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"; | |
| let max_tokens = "Undefined"; | |
| let max_input_tokens = "Undefined"; | |
| let cleanedLitellmParams = {}; | |
| const getProviderFromModel = (model: string) => { | |
| /** | |
| * Use model map | |
| * - check if model in model map | |
| * - return it's litellm_provider, if so | |
| */ | |
| console.log(`GET PROVIDER CALLED! - ${modelMap}`); | |
| if (modelMap !== null && modelMap !== undefined) { | |
| if (typeof modelMap == "object" && model in modelMap) { | |
| return modelMap[model]["litellm_provider"]; | |
| } | |
| } | |
| return "openai"; | |
| }; | |
| // Check if litellm_model_name is null or undefined | |
| if (litellm_model_name) { | |
| // Split litellm_model_name based on "/" | |
| let splitModel = litellm_model_name.split("/"); | |
| // Get the first element in the split | |
| let firstElement = splitModel[0]; | |
| // If there is only one element, default provider to openai | |
| provider = custom_llm_provider; | |
| if (!provider) { | |
| provider = | |
| splitModel.length === 1 | |
| ? getProviderFromModel(litellm_model_name) | |
| : firstElement; | |
| } | |
| } else { | |
| // litellm_model_name is null or undefined, default provider to openai | |
| provider = "-"; | |
| } | |
| if (model_info) { | |
| input_cost = model_info?.input_cost_per_token; | |
| output_cost = model_info?.output_cost_per_token; | |
| max_tokens = model_info?.max_tokens; | |
| max_input_tokens = model_info?.max_input_tokens; | |
| } | |
| if (curr_model?.litellm_params) { | |
| cleanedLitellmParams = Object.fromEntries( | |
| Object.entries(curr_model?.litellm_params).filter( | |
| ([key]) => key !== "model" && key !== "api_base" | |
| ) | |
| ); | |
| } | |
| modelData.data[i].provider = provider; | |
| modelData.data[i].input_cost = input_cost; | |
| modelData.data[i].output_cost = output_cost; | |
| modelData.data[i].litellm_model_name = litellm_model_name; | |
| all_providers.push(provider); | |
| // Convert Cost in terms of Cost per 1M tokens | |
| if (modelData.data[i].input_cost) { | |
| modelData.data[i].input_cost = ( | |
| Number(modelData.data[i].input_cost) * 1000000 | |
| ).toFixed(2); | |
| } | |
| if (modelData.data[i].output_cost) { | |
| modelData.data[i].output_cost = ( | |
| Number(modelData.data[i].output_cost) * 1000000 | |
| ).toFixed(2); | |
| } | |
| modelData.data[i].max_tokens = max_tokens; | |
| modelData.data[i].max_input_tokens = max_input_tokens; | |
| modelData.data[i].api_base = curr_model?.litellm_params?.api_base; | |
| modelData.data[i].cleanedLitellmParams = cleanedLitellmParams; | |
| all_models_on_proxy.push(curr_model.model_name); | |
| console.log(modelData.data[i]); | |
| } | |
| // when users click request access show pop up to allow them to request access | |
| if (userRole && userRole == "Admin Viewer") { | |
| const { Title, Paragraph } = Typography; | |
| return ( | |
| <div> | |
| <Title level={1}>Access Denied</Title> | |
| <Paragraph> | |
| Ask your proxy admin for access to view all models | |
| </Paragraph> | |
| </div> | |
| ); | |
| } | |
| const runHealthCheck = async () => { | |
| try { | |
| message.info("Running health check..."); | |
| setHealthCheckResponse(""); | |
| const response = await healthCheckCall(accessToken); | |
| setHealthCheckResponse(response); | |
| } catch (error) { | |
| console.error("Error running health check:", error); | |
| setHealthCheckResponse("Error running health check"); | |
| } | |
| }; | |
| const FilterByContent = ( | |
| <div > | |
| <Text className="mb-1">Select API Key Name</Text> | |
| { | |
| premiumUser ? ( | |
| <div> | |
| <Select defaultValue="all-keys"> | |
| <SelectItem | |
| key="all-keys" | |
| value="all-keys" | |
| onClick={() => { | |
| setSelectedAPIKey(null); | |
| }} | |
| > | |
| All Keys | |
| </SelectItem> | |
| {keys?.map((key: any, index: number) => { | |
| if ( | |
| key && | |
| key["key_alias"] !== null && | |
| key["key_alias"].length > 0 | |
| ) { | |
| return ( | |
| <SelectItem | |
| key={index} | |
| value={String(index)} | |
| onClick={() => { | |
| setSelectedAPIKey(key); | |
| }} | |
| > | |
| {key["key_alias"]} | |
| </SelectItem> | |
| ); | |
| } | |
| return null; | |
| })} | |
| </Select> | |
| <Text className="mt-1"> | |
| Select Customer Name | |
| </Text> | |
| <Select defaultValue="all-customers"> | |
| <SelectItem | |
| key="all-customers" | |
| value="all-customers" | |
| onClick={() => { | |
| setSelectedCustomer(null); | |
| }} | |
| > | |
| All Customers | |
| </SelectItem> | |
| { | |
| allEndUsers?.map((user: any, index: number) => { | |
| return ( | |
| <SelectItem | |
| key={index} | |
| value={user} | |
| onClick={() => { | |
| setSelectedCustomer(user); | |
| }} | |
| > | |
| {user} | |
| </SelectItem> | |
| ); | |
| }) | |
| } | |
| </Select> | |
| <Text className="mt-1"> | |
| Select Team | |
| </Text> | |
| <Select | |
| className="w-64 relative z-50" | |
| defaultValue="all" | |
| value={selectedTeamFilter ?? "all"} | |
| onValueChange={(value) => setSelectedTeamFilter(value === "all" ? null : value)} | |
| > | |
| <SelectItem value="all">All Teams</SelectItem> | |
| {teams?.filter(team => team.team_id).map((team) => ( | |
| <SelectItem | |
| key={team.team_id} | |
| value={team.team_id} | |
| > | |
| {team.team_alias | |
| ? `${team.team_alias} (${team.team_id.slice(0, 8)}...)` | |
| : `Team ${team.team_id.slice(0, 8)}...` | |
| } | |
| </SelectItem> | |
| ))} | |
| </Select> | |
| </div> | |
| ): ( | |
| <div> | |
| {/* ... existing non-premium user content ... */} | |
| <Text className="mt-1"> | |
| Select Team | |
| </Text> | |
| <Select | |
| className="w-64 relative z-50" | |
| defaultValue="all" | |
| value={selectedTeamFilter ?? "all"} | |
| onValueChange={(value) => setSelectedTeamFilter(value === "all" ? null : value)} | |
| > | |
| <SelectItem value="all">All Teams</SelectItem> | |
| {teams?.filter(team => team.team_id).map((team) => ( | |
| <SelectItem | |
| key={team.team_id} | |
| value={team.team_id} | |
| > | |
| {team.team_alias | |
| ? `${team.team_alias} (${team.team_id.slice(0, 8)}...)` | |
| : `Team ${team.team_id.slice(0, 8)}...` | |
| } | |
| </SelectItem> | |
| ))} | |
| </Select> | |
| </div> | |
| ) | |
| } | |
| </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 = () => { | |
| form | |
| .validateFields() | |
| .then((values) => { | |
| handleAddModelSubmit(values, accessToken, form, handleRefreshClick); | |
| }) | |
| .catch((error) => { | |
| console.error("Validation failed:", error); | |
| }); | |
| }; | |
| console.log(`selectedProvider: ${selectedProvider}`); | |
| console.log(`providerModels.length: ${providerModels.length}`); | |
| const providerKey = Object.keys(Providers).find( | |
| (key) => (Providers as { [index: string]: any })[key] === selectedProvider | |
| ); | |
| let dynamicProviderForm: ProviderSettings | undefined = undefined; | |
| if (providerKey && providerSettings) { | |
| dynamicProviderForm = providerSettings.find( | |
| (provider) => provider.name === provider_map[providerKey] | |
| ); | |
| } | |
| // If a team is selected, render TeamInfoView in full page layout | |
| if (selectedTeamId) { | |
| return ( | |
| <div className="w-full h-full"> | |
| <TeamInfoView | |
| teamId={selectedTeamId} | |
| onClose={() => setSelectedTeamId(null)} | |
| accessToken={accessToken} | |
| is_team_admin={userRole === "Admin"} | |
| is_proxy_admin={userRole === "Proxy Admin"} | |
| userModels={all_models_on_proxy} | |
| editTeam={false} | |
| onUpdate={handleRefreshClick} | |
| /> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div style={{ width: "100%", height: "100%" }}> | |
| {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) => { | |
| // Update the model in the modelData.data array | |
| const updatedModelData = { | |
| ...modelData, | |
| data: modelData.data.map((model: any) => | |
| model.model_info.id === updatedModel.model_info.id ? updatedModel : model | |
| ) | |
| }; | |
| setModelData(updatedModelData); | |
| // Trigger a refresh to update UI | |
| handleRefreshClick(); | |
| }} | |
| /> | |
| ) : ( | |
| <TabGroup className="gap-2 p-8 h-[75vh] w-full mt-2"> | |
| <TabList className="flex justify-between mt-2 w-full items-center"> | |
| <div className="flex"> | |
| {all_admin_roles.includes(userRole) ? <Tab>All Models</Tab> : <Tab>Your Models</Tab>} | |
| <Tab>Add Model</Tab> | |
| {all_admin_roles.includes(userRole) && <Tab>LLM Credentials</Tab>} | |
| {all_admin_roles.includes(userRole) && <Tab> | |
| <pre>/health Models</pre> | |
| </Tab>} | |
| {all_admin_roles.includes(userRole) && <Tab>Model Analytics</Tab>} | |
| {all_admin_roles.includes(userRole) && <Tab>Model Retry Settings</Tab>} | |
| </div> | |
| <div className="flex items-center space-x-2"> | |
| {lastRefreshed && <Text>Last Refreshed: {lastRefreshed}</Text>} | |
| <Icon | |
| icon={RefreshIcon} // Modify as necessary for correct icon name | |
| variant="shadow" | |
| size="xs" | |
| className="self-center" | |
| onClick={handleRefreshClick} | |
| /> | |
| </div> | |
| </TabList> | |
| <TabPanels> | |
| <TabPanel> | |
| <Grid> | |
| <div className="flex flex-col space-y-4"> | |
| <div className="flex justify-between items-center mb-4"> | |
| <div> | |
| <Title>Model Management</Title> | |
| {!all_admin_roles.includes(userRole) ? ( | |
| <Text className="text-tremor-content"> | |
| Add models for teams you are an admin for. | |
| </Text> | |
| ) : ( | |
| <Text className="text-tremor-content"> | |
| Add and manage models for the proxy | |
| </Text> | |
| )} | |
| </div> | |
| </div> | |
| <div className="bg-white rounded-lg shadow"> | |
| <div className="border-b px-6 py-4"> | |
| <div className="flex flex-col space-y-4"> | |
| {/* Search and Filter Controls */} | |
| <div className="flex items-center justify-between"> | |
| <div className="flex items-center gap-3"> | |
| {/* Model Name Filter */} | |
| <div className="flex items-center gap-2"> | |
| <Text>Filter by Public Model Name:</Text> | |
| <Select | |
| className="w-64" | |
| defaultValue={selectedModelGroup ?? "all"} | |
| onValueChange={(value) => setSelectedModelGroup(value === "all" ? "all" : value)} | |
| value={selectedModelGroup ?? "all"} | |
| > | |
| <SelectItem value="all">All Models</SelectItem> | |
| {availableModelGroups.map((group, idx) => ( | |
| <SelectItem | |
| key={idx} | |
| value={group} | |
| > | |
| {group} | |
| </SelectItem> | |
| ))} | |
| </Select> | |
| </div> | |
| {/* Team Filter */} | |
| <div className="flex items-center gap-2"> | |
| <Text>Filter by Team:</Text> | |
| <Select | |
| className="w-64" | |
| defaultValue="all" | |
| value={selectedTeamFilter ?? "all"} | |
| onValueChange={(value) => setSelectedTeamFilter(value === "all" ? null : value)} | |
| > | |
| <SelectItem value="all">All Teams</SelectItem> | |
| {teams?.filter(team => team.team_id).map((team) => ( | |
| <SelectItem | |
| key={team.team_id} | |
| value={team.team_id} | |
| > | |
| {team.team_alias | |
| ? `${team.team_alias} (${team.team_id.slice(0, 8)}...)` | |
| : `Team ${team.team_id.slice(0, 8)}...` | |
| } | |
| </SelectItem> | |
| ))} | |
| </Select> | |
| </div> | |
| </div> | |
| {/* Column Selector will be rendered here */} | |
| {/* <div className="relative" ref={dropdownRef}> | |
| <button | |
| onClick={() => setIsDropdownOpen(!isDropdownOpen)} | |
| className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" | |
| > | |
| <TableIcon className="h-4 w-4" /> | |
| Columns | |
| </button> | |
| {isDropdownOpen && tableRef.current && ( | |
| <div className="absolute right-0 mt-2 w-56 bg-white rounded-md shadow-lg ring-1 ring-black ring-opacity-5 z-50"> | |
| <div className="py-1"> | |
| {tableRef.current.getAllLeafColumns().map((column: any) => { | |
| if (column.id === 'actions') return null; | |
| return ( | |
| <div | |
| key={column.id} | |
| className="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" | |
| onClick={() => column.toggleVisibility()} | |
| > | |
| <input | |
| type="checkbox" | |
| checked={column.getIsVisible()} | |
| onChange={() => column.toggleVisibility()} | |
| className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" | |
| /> | |
| <span className="ml-2"> | |
| {typeof column.columnDef.header === 'string' | |
| ? column.columnDef.header | |
| : column.columnDef.header?.props?.children || column.id} | |
| </span> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| )} | |
| </div> */} | |
| </div> | |
| {/* Results Count */} | |
| <div className="flex justify-between items-center"> | |
| <Text className="text-sm text-gray-700"> | |
| Showing {modelData && modelData.data.length > 0 ? modelData.data.length : 0} results | |
| </Text> | |
| </div> | |
| </div> | |
| </div> | |
| <ModelDataTable | |
| columns={columns( | |
| userRole, | |
| userID, | |
| premiumUser, | |
| setSelectedModelId, | |
| setSelectedTeamId, | |
| getDisplayModelName, | |
| handleEditClick, | |
| handleRefreshClick, | |
| setEditModel, | |
| )} | |
| data={modelData.data.filter( | |
| (model: any) => ( | |
| (selectedModelGroup === "all" || model.model_name === selectedModelGroup || !selectedModelGroup) && | |
| (selectedTeamFilter === "all" || model.model_info["team_id"] === selectedTeamFilter || !selectedTeamFilter) | |
| ) | |
| )} | |
| isLoading={false} | |
| table={tableRef} | |
| /> | |
| </div> | |
| </div> | |
| </Grid> | |
| </TabPanel> | |
| <TabPanel className="h-full"> | |
| <AddModelTab | |
| form={form} | |
| handleOk={handleOk} | |
| selectedProvider={selectedProvider} | |
| setSelectedProvider={setSelectedProvider} | |
| providerModels={providerModels} | |
| setProviderModelsFn={setProviderModelsFn} | |
| getPlaceholder={getPlaceholder} | |
| uploadProps={uploadProps} | |
| showAdvancedSettings={showAdvancedSettings} | |
| setShowAdvancedSettings={setShowAdvancedSettings} | |
| teams={teams} | |
| credentials={credentialsList} | |
| accessToken={accessToken} | |
| userRole={userRole} | |
| /> | |
| </TabPanel> | |
| <TabPanel> | |
| <CredentialsPanel accessToken={accessToken} uploadProps={uploadProps} credentialList={credentialsList} fetchCredentials={fetchCredentials} /> | |
| </TabPanel> | |
| <TabPanel> | |
| <Card> | |
| <Text> | |
| `/health` will run a very small request through your models | |
| configured on litellm | |
| </Text> | |
| <Button onClick={runHealthCheck}>Run `/health`</Button> | |
| {healthCheckResponse && ( | |
| <pre>{JSON.stringify(healthCheckResponse, null, 2)}</pre> | |
| )} | |
| </Card> | |
| </TabPanel> | |
| <TabPanel> | |
| <Grid numItems={4} className="mt-2 mb-2"> | |
| <Col> | |
| <Text>Select Time Range</Text> | |
| <DateRangePicker | |
| enableSelect={true} | |
| value={dateValue} | |
| className="mr-2" | |
| onValueChange={(value) => { | |
| setDateValue(value); | |
| updateModelMetrics( | |
| selectedModelGroup, | |
| value.from, | |
| value.to | |
| ); // Call updateModelMetrics with the new date range | |
| }} | |
| /> | |
| </Col> | |
| <Col className="ml-2"> | |
| <Text>Select Model Group</Text> | |
| <Select | |
| defaultValue={ | |
| selectedModelGroup | |
| ? selectedModelGroup | |
| : availableModelGroups[0] | |
| } | |
| value={ | |
| selectedModelGroup | |
| ? selectedModelGroup | |
| : availableModelGroups[0] | |
| } | |
| > | |
| {availableModelGroups.map((group, idx) => ( | |
| <SelectItem | |
| key={idx} | |
| value={group} | |
| onClick={() => | |
| updateModelMetrics(group, dateValue.from, dateValue.to) | |
| } | |
| > | |
| {group} | |
| </SelectItem> | |
| ))} | |
| </Select> | |
| </Col> | |
| <Col> | |
| <Popover | |
| trigger="click" content={FilterByContent} | |
| overlayStyle={{ | |
| width: "20vw" | |
| }} | |
| > | |
| <Button | |
| icon={FilterIcon} | |
| size="md" | |
| variant="secondary" | |
| className="mt-4 ml-2" | |
| style={{ | |
| border: "none", | |
| }} | |
| onClick={() => setShowAdvancedFilters(true)} | |
| > | |
| </Button> | |
| </Popover> | |
| </Col> | |
| </Grid> | |
| <Grid numItems={2}> | |
| <Col> | |
| <Card className="mr-2 max-h-[400px] min-h-[400px]"> | |
| <TabGroup> | |
| <TabList variant="line" defaultValue="1"> | |
| <Tab value="1">Avg. Latency per Token</Tab> | |
| <Tab value="2">Time to first token</Tab> | |
| </TabList> | |
| <TabPanels> | |
| <TabPanel> | |
| <p className="text-gray-500 italic"> (seconds/token)</p> | |
| <Text className="text-gray-500 italic mt-1 mb-1"> | |
| average Latency for successfull requests divided by | |
| the total tokens | |
| </Text> | |
| {modelMetrics && modelMetricsCategories && ( | |
| <AreaChart | |
| title="Model Latency" | |
| className="h-72" | |
| data={modelMetrics} | |
| showLegend={false} | |
| index="date" | |
| categories={modelMetricsCategories} | |
| connectNulls={true} | |
| customTooltip={customTooltip} | |
| /> | |
| )} | |
| </TabPanel> | |
| <TabPanel> | |
| <TimeToFirstToken | |
| modelMetrics={streamingModelMetrics} | |
| modelMetricsCategories={ | |
| streamingModelMetricsCategories | |
| } | |
| customTooltip={customTooltip} | |
| premiumUser={premiumUser} | |
| /> | |
| </TabPanel> | |
| </TabPanels> | |
| </TabGroup> | |
| </Card> | |
| </Col> | |
| <Col> | |
| <Card className="ml-2 max-h-[400px] min-h-[400px] overflow-y-auto"> | |
| <Table> | |
| <TableHead> | |
| <TableRow> | |
| <TableHeaderCell>Deployment</TableHeaderCell> | |
| <TableHeaderCell>Success Responses</TableHeaderCell> | |
| <TableHeaderCell> | |
| Slow Responses <p>Success Responses taking 600+s</p> | |
| </TableHeaderCell> | |
| </TableRow> | |
| </TableHead> | |
| <TableBody> | |
| {slowResponsesData.map((metric, idx) => ( | |
| <TableRow key={idx}> | |
| <TableCell>{metric.api_base}</TableCell> | |
| <TableCell>{metric.total_count}</TableCell> | |
| <TableCell>{metric.slow_count}</TableCell> | |
| </TableRow> | |
| ))} | |
| </TableBody> | |
| </Table> | |
| </Card> | |
| </Col> | |
| </Grid> | |
| <Grid numItems={1} className="gap-2 w-full mt-2"> | |
| <Card> | |
| <Title>All Exceptions for {selectedModelGroup}</Title> | |
| <BarChart | |
| className="h-60" | |
| data={modelExceptions} | |
| index="model" | |
| categories={allExceptions} | |
| stack={true} | |
| yAxisWidth={30} | |
| /> | |
| </Card> | |
| </Grid> | |
| <Grid numItems={1} className="gap-2 w-full mt-2"> | |
| <Card> | |
| <Title>All Up Rate Limit Errors (429) for {selectedModelGroup}</Title> | |
| <Grid numItems={1}> | |
| <Col> | |
| <Subtitle style={{ fontSize: "15px", fontWeight: "normal", color: "#535452"}}>Num Rate Limit Errors { (globalExceptionData.sum_num_rate_limit_exceptions)}</Subtitle> | |
| <BarChart | |
| className="h-40" | |
| data={globalExceptionData.daily_data} | |
| index="date" | |
| colors={['rose']} | |
| categories={['num_rate_limit_exceptions']} | |
| onValueChange={(v) => console.log(v)} | |
| /> | |
| </Col> | |
| <Col> | |
| </Col> | |
| </Grid> | |
| </Card> | |
| { | |
| premiumUser ? ( | |
| <> | |
| {globalExceptionPerDeployment.map((globalActivity, index) => ( | |
| <Card key={index}> | |
| <Title>{globalActivity.api_base ? globalActivity.api_base : "Unknown API Base"}</Title> | |
| <Grid numItems={1}> | |
| <Col> | |
| <Subtitle style={{ fontSize: "15px", fontWeight: "normal", color: "#535452"}}>Num Rate Limit Errors (429) {(globalActivity.sum_num_rate_limit_exceptions)}</Subtitle> | |
| <BarChart | |
| className="h-40" | |
| data={globalActivity.daily_data} | |
| index="date" | |
| colors={['rose']} | |
| categories={['num_rate_limit_exceptions']} | |
| onValueChange={(v) => console.log(v)} | |
| /> | |
| </Col> | |
| </Grid> | |
| </Card> | |
| ))} | |
| </> | |
| ) : | |
| <> | |
| {globalExceptionPerDeployment && globalExceptionPerDeployment.length > 0 && | |
| globalExceptionPerDeployment.slice(0, 1).map((globalActivity, index) => ( | |
| <Card key={index}> | |
| <Title>✨ Rate Limit Errors by Deployment</Title> | |
| <p className="mb-2 text-gray-500 italic text-[12px]">Upgrade to see exceptions for all deployments</p> | |
| <Button variant="primary" className="mb-2"> | |
| <a href="https://forms.gle/W3U4PZpJGFHWtHyA9" target="_blank"> | |
| Get Free Trial | |
| </a> | |
| </Button> | |
| <Card> | |
| <Title>{globalActivity.api_base}</Title> | |
| <Grid numItems={1}> | |
| <Col> | |
| <Subtitle | |
| style={{ | |
| fontSize: "15px", | |
| fontWeight: "normal", | |
| color: "#535452", | |
| }} | |
| > | |
| Num Rate Limit Errors {(globalActivity.sum_num_rate_limit_exceptions)} | |
| </Subtitle> | |
| <BarChart | |
| className="h-40" | |
| data={globalActivity.daily_data} | |
| index="date" | |
| colors={['rose']} | |
| categories={['num_rate_limit_exceptions']} | |
| onValueChange={(v) => console.log(v)} | |
| /> | |
| </Col> | |
| </Grid> | |
| </Card> | |
| </Card> | |
| ))} | |
| </> | |
| } | |
| </Grid> | |
| </TabPanel> | |
| <TabPanel> | |
| <div className="flex items-center"> | |
| <Text>Filter by Public Model Name</Text> | |
| <Select | |
| className="mb-4 mt-2 ml-2 w-50" | |
| defaultValue={ | |
| selectedModelGroup | |
| ? selectedModelGroup | |
| : availableModelGroups[0] | |
| } | |
| value={ | |
| selectedModelGroup | |
| ? selectedModelGroup | |
| : availableModelGroups[0] | |
| } | |
| onValueChange={(value) => setSelectedModelGroup(value)} | |
| > | |
| {availableModelGroups.map((group, idx) => ( | |
| <SelectItem | |
| key={idx} | |
| value={group} | |
| onClick={() => setSelectedModelGroup(group)} | |
| > | |
| {group} | |
| </SelectItem> | |
| ))} | |
| </Select> | |
| </div> | |
| <Title>Retry Policy for {selectedModelGroup}</Title> | |
| <Text className="mb-6"> | |
| How many retries should be attempted based on the Exception | |
| </Text> | |
| {retry_policy_map && ( | |
| <table> | |
| <tbody> | |
| {Object.entries(retry_policy_map).map( | |
| ([exceptionType, retryPolicyKey], idx) => { | |
| let retryCount = | |
| modelGroupRetryPolicy?.[selectedModelGroup!]?.[ | |
| retryPolicyKey | |
| ]; | |
| if (retryCount == null) { | |
| retryCount = defaultRetry; | |
| } | |
| return ( | |
| <tr | |
| key={idx} | |
| className="flex justify-between items-center mt-2" | |
| > | |
| <td> | |
| <Text>{exceptionType}</Text> | |
| </td> | |
| <td> | |
| <InputNumber | |
| className="ml-5" | |
| value={retryCount} | |
| min={0} | |
| step={1} | |
| onChange={(value) => { | |
| setModelGroupRetryPolicy( | |
| (prevModelGroupRetryPolicy) => { | |
| const prevRetryPolicy = | |
| prevModelGroupRetryPolicy?.[ | |
| selectedModelGroup! | |
| ] ?? {}; | |
| return { | |
| ...(prevModelGroupRetryPolicy ?? {}), | |
| [selectedModelGroup!]: { | |
| ...prevRetryPolicy, | |
| [retryPolicyKey!]: value, | |
| }, | |
| } as RetryPolicyObject; | |
| } | |
| ); | |
| }} | |
| /> | |
| </td> | |
| </tr> | |
| ); | |
| } | |
| )} | |
| </tbody> | |
| </table> | |
| )} | |
| <Button className="mt-6 mr-8" onClick={handleSaveRetrySettings}> | |
| Save | |
| </Button> | |
| </TabPanel> | |
| </TabPanels> | |
| </TabGroup> | |
| )} | |
| </div> | |
| ); | |
| }; | |
| export default ModelDashboard; | |