"use client"; import React, { useEffect, useState } from "react"; import { keyDeleteCall, modelAvailableCall } from "./networking"; import { InformationCircleIcon, StatusOnlineIcon, TrashIcon, PencilAltIcon } from "@heroicons/react/outline"; import { keySpendLogsCall, PredictedSpendLogsCall, keyUpdateCall } from "./networking"; import { Badge, Card, Table, Button, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Dialog, DialogPanel, Text, Title, Subtitle, Icon, BarChart, } from "@tremor/react"; import { Select as Select3, SelectItem, MultiSelect, MultiSelectItem } from "@tremor/react"; import { Button as Button2, Modal, Form, Input, Select as Select2, InputNumber, message, Select, } from "antd"; const { Option } = Select; interface EditKeyModalProps { visible: boolean; onCancel: () => void; token: any; // Assuming TeamType is a type representing your team object onSubmit: (data: FormData) => void; // Assuming FormData is the type of data to be submitted } // Define the props type interface ViewKeyTableProps { userID: string; userRole: string | null; accessToken: string; selectedTeam: any | null; data: any[] | null; setData: React.Dispatch>; teams: any[] | null; } interface ItemData { key_alias: string | null; key_name: string; spend: string; max_budget: string | null; models: string[]; tpm_limit: string | null; rpm_limit: string | null; token: string; token_id: string | null; id: number; team_id: string; metadata: any; expires: any; // Add any other properties that exist in the item data } const ViewKeyTable: React.FC = ({ userID, userRole, accessToken, selectedTeam, data, setData, teams }) => { const [isButtonClicked, setIsButtonClicked] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [keyToDelete, setKeyToDelete] = useState(null); const [selectedItem, setSelectedItem] = useState(null); const [spendData, setSpendData] = useState<{ day: string; spend: number }[] | null>( null ); const [predictedSpendString, setPredictedSpendString] = useState(""); const [editModalVisible, setEditModalVisible] = useState(false); const [infoDialogVisible, setInfoDialogVisible] = useState(false); const [selectedToken, setSelectedToken] = useState(null); const [userModels, setUserModels] = useState([]); const initialKnownTeamIDs: Set = new Set(); const [knownTeamIDs, setKnownTeamIDs] = useState(initialKnownTeamIDs); useEffect(() => { const fetchUserModels = async () => { try { if (userID === null) { return; } if (accessToken !== null && userRole !== null) { const model_available = await modelAvailableCall(accessToken, userID, userRole); let available_model_names = model_available["data"].map( (element: { id: string }) => element.id ); console.log("available_model_names:", available_model_names); setUserModels(available_model_names); } } catch (error) { console.error("Error fetching user models:", error); } }; fetchUserModels(); }, [accessToken, userID, userRole]); useEffect(() => { if (teams) { const teamIDSet: Set = new Set(); teams.forEach((team: any, index: number) => { const team_obj: string = team.team_id teamIDSet.add(team_obj); }); setKnownTeamIDs(teamIDSet) } }, [teams]) const EditKeyModal: React.FC = ({ visible, onCancel, token, onSubmit }) => { const [form] = Form.useForm(); const [keyTeam, setKeyTeam] = useState(selectedTeam); const [errorModels, setErrorModels] = useState([]); const [errorBudget, setErrorBudget] = useState(false); const handleOk = () => { form .validateFields() .then((values) => { // const updatedValues = {...values, team_id: team.team_id}; // onSubmit(updatedValues); form.resetFields(); }) .catch((error) => { console.error("Validation failed:", error); }); }; return (
<> { const errorModels = value.filter((model: string) => ( !keyTeam.models.includes(model) && model !== "all-team-models" && model !== "all-proxy-models" && !keyTeam.models.includes("all-proxy-models") )); console.log(`errorModels: ${errorModels}`) if (errorModels.length > 0) { return Promise.reject(`Some models are not part of the new team\'s models - ${errorModels}Team models: ${keyTeam.models}`); } else { return Promise.resolve(); } } } ]}> { if (value && keyTeam && keyTeam.max_budget !== null && value > keyTeam.max_budget) { console.log(`keyTeam.max_budget: ${keyTeam.max_budget}`) throw new Error(`Budget cannot exceed team max budget: $${keyTeam.max_budget}`); } }, }, ]} > {teams?.map((team_obj, index) => ( setKeyTeam(team_obj)} > {team_obj.team_alias} ))}
Edit Key
); }; const handleEditClick = (token: any) => { console.log("handleEditClick:", token); // set token.token to token.token_id if token_id is not null if (token.token == null) { if (token.token_id !== null) { token.token = token.token_id; } } setSelectedToken(token); setEditModalVisible(true); }; const handleEditCancel = () => { setEditModalVisible(false); setSelectedToken(null); }; const handleEditSubmit = async (formValues: Record) => { /** * Call API to update team with teamId and values * * Client-side validation: For selected team, ensure models in team + max budget < team max budget */ if (accessToken == null) { return; } const currentKey = formValues.token; formValues.key = currentKey; console.log("handleEditSubmit:", formValues); let newKeyValues = await keyUpdateCall(accessToken, formValues); console.log("handleEditSubmit: newKeyValues", newKeyValues); // Update the keys with the update key if (data) { const updatedData = data.map((key) => key.token === currentKey ? newKeyValues : key ); setData(updatedData); } message.success("Key updated successfully"); setEditModalVisible(false); setSelectedToken(null); }; const handleDelete = async (token: any) => { console.log("handleDelete:", token); if (token.token == null) { if (token.token_id !== null) { token.token = token.token_id; } } if (data == null) { return; } // Set the key to delete and open the confirmation modal setKeyToDelete(token.token); localStorage.removeItem("userData" + userID); setIsDeleteModalOpen(true); }; const confirmDelete = async () => { if (keyToDelete == null || data == null) { return; } try { await keyDeleteCall(accessToken, keyToDelete); // Successfully completed the deletion. Update the state to trigger a rerender. const filteredData = data.filter((item) => item.token !== keyToDelete); setData(filteredData); } catch (error) { console.error("Error deleting the key:", error); // Handle any error situations, such as displaying an error message to the user. } // Close the confirmation modal and reset the keyToDelete setIsDeleteModalOpen(false); setKeyToDelete(null); }; const cancelDelete = () => { // Close the confirmation modal and reset the keyToDelete setIsDeleteModalOpen(false); setKeyToDelete(null); }; if (data == null) { return; } console.log("RERENDER TRIGGERED"); return (
Key Alias Secret Key Spend (USD) Budget (USD) Models TPM / RPM Limits {data.map((item) => { console.log(item); // skip item if item.team_id == "litellm-dashboard" if (item.team_id === "litellm-dashboard") { return null; } if (selectedTeam) { /** * if selected team id is null -> show the keys with no team id or team id's that don't exist in db */ console.log(`item team id: ${item.team_id}, knownTeamIDs.has(item.team_id): ${knownTeamIDs.has(item.team_id)}, selectedTeam id: ${selectedTeam.team_id}`) if (selectedTeam.team_id == null && item.team_id !== null && !knownTeamIDs.has(item.team_id)) { // do nothing -> returns a row with this key } else if (item.team_id != selectedTeam.team_id) { return null; } console.log(`item team id: ${item.team_id}, is returned`) } return ( {item.key_alias != null ? ( {item.key_alias} ) : ( Not Set )} {item.key_name} {(() => { try { return parseFloat(item.spend).toFixed(4); } catch (error) { return item.spend; } })()} {item.max_budget != null ? ( {item.max_budget} ) : ( Unlimited )} {/* */} {/* {item.team_alias && item.team_alias != "None" ? item.team_alias : item.team_id} */} {/* {JSON.stringify(item.metadata).slice(0, 400)} */} {Array.isArray(item.models) ? (
{item.models.length === 0 ? ( <> {selectedTeam && selectedTeam.models && selectedTeam.models.length > 0 ? ( selectedTeam.models.map((model: string, index: number) => ( model === "all-proxy-models" ? ( All Proxy Models ) : model === "all-team-models" ? ( All Team Models ) : ( {model.length > 30 ? `${model.slice(0, 30)}...` : model} ) )) ) : ( // If selected team is None or selected team's models are empty, show all models all-proxy-models )} ) : ( item.models.map((model: string, index: number) => ( model === "all-proxy-models" ? ( All Proxy Models ) : model === "all-team-models" ? ( All Team Models ) : ( {model.length > 30 ? `${model.slice(0, 30)}...` : model} ) )) )}
) : null}
TPM: {item.tpm_limit ? item.tpm_limit : "Unlimited"}{" "}

RPM:{" "} {item.rpm_limit ? item.rpm_limit : "Unlimited"}
{ setSelectedToken(item); setInfoDialogVisible(true); }} icon={InformationCircleIcon} size="sm" /> { setInfoDialogVisible(false); setSelectedToken(null); }} footer={null} width={800} > {selectedToken && ( <>

Spend

{(() => { try { return parseFloat(selectedToken.spend).toFixed(4); } catch (error) { return selectedToken.spend; } })()}

Budget

{selectedToken.max_budget != null ? ( <>{selectedToken.max_budget} ) : ( <>Unlimited )}

Expires

{selectedToken.expires != null ? ( <> {new Date(selectedToken.expires).toLocaleString(undefined, { day: 'numeric', month: 'long', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' })} ) : ( <>Never )}

Token Name {selectedToken.key_alias ? selectedToken.key_alias : selectedToken.key_name} Token ID {selectedToken.token} Metadata
{JSON.stringify(selectedToken.metadata)} 
)}
handleEditClick(item)} /> handleDelete(item)} icon={TrashIcon} size="sm" />
); })}
{isDeleteModalOpen && (
{/* Modal Panel */} {/* Confirmation Modal Content */}

Delete Key

Are you sure you want to delete this key ?

)}
{selectedToken && ( )}
); }; export default ViewKeyTable;