diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index e0cdaf9d388..5c8b2d3fd97 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -2749,11 +2749,6 @@ "count": 1 } }, - "src/components/permissions/AgentPermissions.tsx": { - "no-restricted-imports": { - "count": 2 - } - }, "src/components/permissions/MCPServerPermissions.tsx": { "no-nested-ternary": { "count": 3 @@ -2762,11 +2757,6 @@ "count": 2 } }, - "src/components/permissions/VectorStorePermissions.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/policies/PolicySelector.tsx": { "no-nested-ternary": { "count": 1 @@ -3045,9 +3035,6 @@ "local/filename-pascal-case": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -3062,11 +3049,6 @@ "count": 1 } }, - "src/components/templates/KeyInfoHeader.tsx": { - "no-restricted-imports": { - "count": 2 - } - }, "src/components/templates/key_edit_view.tsx": { "local/filename-pascal-case": { "count": 1 @@ -3096,9 +3078,6 @@ "no-nested-ternary": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/set-state-in-effect": { "count": 1 } diff --git a/ui/litellm-dashboard/src/components/permissions/AgentPermissions.tsx b/ui/litellm-dashboard/src/components/permissions/AgentPermissions.tsx index 11951b2decb..ee6fbbd89f5 100644 --- a/ui/litellm-dashboard/src/components/permissions/AgentPermissions.tsx +++ b/ui/litellm-dashboard/src/components/permissions/AgentPermissions.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; -import { Text, Badge } from "@tremor/react"; import { UserGroupIcon } from "@heroicons/react/outline"; -import { Tooltip } from "antd"; +import { Badge } from "@/components/ui/badge"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { getAgentsList } from "../networking"; interface Agent { @@ -58,10 +58,8 @@ export function AgentPermissions({ agents, agentAccessGroups = [], accessToken }
- Agents - - {totalCount} - +

Agents

+ {totalCount}
{totalCount > 0 ? ( @@ -71,14 +69,17 @@ export function AgentPermissions({ agents, agentAccessGroups = [], accessToken }
{item.type === "agent" ? ( - -
- - - {getAgentDisplayName(item.value)} - -
-
+ + + }> + + + {getAgentDisplayName(item.value)} + + + {`Full ID: ${item.value}`} + + ) : (
@@ -96,7 +97,7 @@ export function AgentPermissions({ agents, agentAccessGroups = [], accessToken } ) : (
- No agents or access groups configured +

No agents or access groups configured

)}
diff --git a/ui/litellm-dashboard/src/components/permissions/VectorStorePermissions.tsx b/ui/litellm-dashboard/src/components/permissions/VectorStorePermissions.tsx index 8541d65e11f..6bf79d8a632 100644 --- a/ui/litellm-dashboard/src/components/permissions/VectorStorePermissions.tsx +++ b/ui/litellm-dashboard/src/components/permissions/VectorStorePermissions.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; -import { Text, Badge } from "@tremor/react"; import { DatabaseIcon } from "@heroicons/react/outline"; +import { Badge } from "@/components/ui/badge"; import { vectorStoreListCall } from "../networking"; interface VectorStoreDetails { @@ -52,10 +52,8 @@ export function VectorStorePermissions({ vectorStores, accessToken }: VectorStor
- Vector Stores - - {vectorStores.length} - +

Vector Stores

+ {vectorStores.length}
{vectorStores.length > 0 ? ( @@ -63,7 +61,7 @@ export function VectorStorePermissions({ vectorStores, accessToken }: VectorStor {vectorStores.map((store, index) => (
{getVectorStoreDisplayName(store)}
@@ -72,7 +70,7 @@ export function VectorStorePermissions({ vectorStores, accessToken }: VectorStor ) : (
- No vector stores configured +

No vector stores configured

)}
diff --git a/ui/litellm-dashboard/src/components/team/member_permissions.test.tsx b/ui/litellm-dashboard/src/components/team/member_permissions.test.tsx index 10c78331c68..652d4f8e685 100644 --- a/ui/litellm-dashboard/src/components/team/member_permissions.test.tsx +++ b/ui/litellm-dashboard/src/components/team/member_permissions.test.tsx @@ -1,5 +1,5 @@ import * as networking from "@/components/networking"; -import { act, fireEvent, screen, waitFor } from "@testing-library/react"; +import { act, fireEvent, screen, waitFor, within } from "@testing-library/react"; import { renderWithProviders } from "../../../tests/test-utils"; import { afterEach, describe, expect, it, vi } from "vitest"; import MemberPermissions from "./member_permissions"; @@ -9,6 +9,9 @@ vi.mock("@/components/networking", () => ({ teamPermissionsUpdateCall: vi.fn(), })); +const checkboxFor = (endpoint: string) => + within(screen.getByText(endpoint).closest("tr") as HTMLElement).getByRole("checkbox"); + describe("MemberPermissions", () => { afterEach(() => { vi.clearAllMocks(); @@ -69,32 +72,27 @@ describe("MemberPermissions", () => { expect(screen.getByText("Member Permissions")).toBeInTheDocument(); }); - const checkboxes = screen.getAllByRole("checkbox"); - const unselectedCheckbox = checkboxes.find((cb) => !(cb as HTMLInputElement).checked); + expect(checkboxFor("/key/generate")).toBeChecked(); + expect(checkboxFor("/key/list")).not.toBeChecked(); - if (unselectedCheckbox) { - await act(async () => { - fireEvent.click(unselectedCheckbox); - }); + await act(async () => { + fireEvent.click(checkboxFor("/key/list")); + }); - await waitFor(() => { - const saveButton = screen.getByRole("button", { name: /save changes/i }); - expect(saveButton).toBeInTheDocument(); - }); + expect(checkboxFor("/key/list")).toBeChecked(); - const saveButton = screen.getByRole("button", { name: /save changes/i }); - await act(async () => { - fireEvent.click(saveButton); - }); + const saveButton = await screen.findByRole("button", { name: /save changes/i }); + await act(async () => { + fireEvent.click(saveButton); + }); - await waitFor(() => { - expect(networking.teamPermissionsUpdateCall).toHaveBeenCalledWith( - "token-123", - "team-123", - expect.arrayContaining(["/key/generate", "/key/list"]), - ); - }); - } + await waitFor(() => { + expect(networking.teamPermissionsUpdateCall).toHaveBeenCalledWith( + "token-123", + "team-123", + expect.arrayContaining(["/key/generate", "/key/list"]), + ); + }); }); it("should render team daily activity permission with correct method and description", async () => { @@ -123,11 +121,13 @@ describe("MemberPermissions", () => { expect(screen.getByText("Member Permissions")).toBeInTheDocument(); }); - const checkboxes = screen.getAllByRole("checkbox"); - checkboxes.forEach((checkbox) => { - expect(checkbox).toBeDisabled(); + expect(checkboxFor("/key/list")).not.toBeChecked(); + + await act(async () => { + fireEvent.click(checkboxFor("/key/list")); }); + expect(checkboxFor("/key/list")).not.toBeChecked(); expect(screen.queryByRole("button", { name: /save changes/i })).not.toBeInTheDocument(); }); @@ -143,32 +143,27 @@ describe("MemberPermissions", () => { expect(screen.getByText("Member Permissions")).toBeInTheDocument(); }); - const checkboxes = screen.getAllByRole("checkbox"); - const unselectedCheckbox = checkboxes.find((cb) => !(cb as HTMLInputElement).checked); + await act(async () => { + fireEvent.click(checkboxFor("/key/list")); + }); - if (unselectedCheckbox) { - await act(async () => { - fireEvent.click(unselectedCheckbox); - }); + expect(checkboxFor("/key/list")).toBeChecked(); - await waitFor(() => { - const resetButton = screen.getByRole("button", { name: /reset/i }); - expect(resetButton).toBeInTheDocument(); - }); + vi.mocked(networking.getTeamPermissionsCall).mockResolvedValueOnce({ + all_available_permissions: ["/key/generate", "/key/list"], + team_member_permissions: ["/key/generate"], + }); - vi.mocked(networking.getTeamPermissionsCall).mockResolvedValueOnce({ - all_available_permissions: ["/key/generate", "/key/list"], - team_member_permissions: ["/key/generate"], - }); + const resetButton = await screen.findByRole("button", { name: /reset/i }); + await act(async () => { + fireEvent.click(resetButton); + }); - const resetButton = screen.getByRole("button", { name: /reset/i }); - await act(async () => { - fireEvent.click(resetButton); - }); + await waitFor(() => { + expect(networking.getTeamPermissionsCall).toHaveBeenCalledTimes(2); + }); - await waitFor(() => { - expect(networking.getTeamPermissionsCall).toHaveBeenCalledTimes(2); - }); - } + expect(checkboxFor("/key/list")).not.toBeChecked(); + expect(screen.queryByRole("button", { name: /save changes/i })).not.toBeInTheDocument(); }); }); diff --git a/ui/litellm-dashboard/src/components/team/member_permissions.tsx b/ui/litellm-dashboard/src/components/team/member_permissions.tsx index 5bbd82f4a5d..62c7d1f96da 100644 --- a/ui/litellm-dashboard/src/components/team/member_permissions.tsx +++ b/ui/litellm-dashboard/src/components/team/member_permissions.tsx @@ -1,7 +1,9 @@ import { getTeamPermissionsCall, teamPermissionsUpdateCall } from "@/components/networking"; -import { ReloadOutlined, SaveOutlined } from "@ant-design/icons"; -import { Card, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text, Title } from "@tremor/react"; -import { Button, Checkbox, Empty } from "antd"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { RotateCw, Save } from "lucide-react"; import React, { useEffect, useState } from "react"; import NotificationsManager from "../molecules/notifications_manager"; import { getPermissionInfo } from "./permission_definitions"; @@ -75,36 +77,38 @@ const MemberPermissions: React.FC = ({ teamId, accessTok const hasPermissions = permissions.length > 0; return ( - +
- Member Permissions +

Member Permissions

{canEditTeam && hasChanges && (
- -
)}
- Control what team members can do when they are not team admins. +

Control what team members can do when they are not team admins.

{hasPermissions ? (
- - +
+ - Method - Endpoint - Description - + Method + Endpoint + Description + Allow Access - + - + {permissions.map((permission) => { const permInfo = getPermissionInfo(permission); @@ -125,8 +129,9 @@ const MemberPermissions: React.FC = ({ teamId, accessTok {permInfo.description} handlePermissionChange(permission, e.target.checked)} + onCheckedChange={(checked) => handlePermissionChange(permission, checked)} disabled={!canEditTeam} /> @@ -138,7 +143,7 @@ const MemberPermissions: React.FC = ({ teamId, accessTok ) : (
- +

No permissions available

)} diff --git a/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx b/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx index d0dd782a697..f31a265da87 100644 --- a/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx +++ b/ui/litellm-dashboard/src/components/templates/KeyInfoHeader.tsx @@ -1,27 +1,35 @@ import React from "react"; -import { Button, Typography, Tooltip, Space, Divider, Flex, Popover, Dropdown, Tag } from "antd"; -import type { MenuProps } from "antd"; import { - ArrowLeftOutlined, - SyncOutlined, - DeleteOutlined, - PlusOutlined, - UserOutlined, - CalendarOutlined, - ClockCircleOutlined, - ThunderboltOutlined, - SafetyCertificateOutlined, - TransactionOutlined, - FieldTimeOutlined, - MoreOutlined, - StopOutlined, - CheckCircleOutlined, -} from "@ant-design/icons"; + ArrowLeft, + ArrowLeftRight, + Ban, + Calendar, + CircleCheck, + Clock, + MoreVertical, + Plus, + RefreshCw, + ShieldCheck, + Timer, + Trash2, + User, + Zap, +} from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"; +import { Separator } from "@/components/ui/separator"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +import CopyButton from "@/components/shared/CopyButton"; import LabeledField from "../common_components/LabeledField"; import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag"; -const { Title, Text } = Typography; - export interface KeyInfoData { keyName: string; keyId: string; @@ -52,14 +60,12 @@ interface KeyInfoHeaderProps { function UserField({ userAlias, userEmail, userId }: { userAlias?: string | null; userEmail: string; userId: string }) { const labelEl = ( - - - - - - User - - +
+ + + + User +
); const isEmpty = !userAlias && !userEmail && !userId; @@ -68,7 +74,7 @@ function UserField({ userAlias, userEmail, userId }: { userAlias?: string | null
{labelEl}
- - + -
); @@ -87,14 +93,12 @@ function UserField({ userAlias, userEmail, userId }: { userAlias?: string | null
{label} {value ? ( - - {value} - +
+ + {value} + + +
) : ( - )} @@ -108,11 +112,18 @@ function UserField({ userAlias, userEmail, userId }: { userAlias?: string | null
{labelEl}
- - - - - + + + + + } + /> + + {popoverContent} + +
); @@ -122,11 +133,14 @@ function UserField({ userAlias, userEmail, userId }: { userAlias?: string | null
{labelEl}
- - - {displayValue} - - + + {displayValue}} + /> + + {popoverContent} + +
); @@ -146,104 +160,124 @@ export function KeyInfoHeader({ regenerateDisabled = false, regenerateTooltip, }: KeyInfoHeaderProps) { - const destructiveActionItems: MenuProps["items"] = [ - ...(onToggleBlocked - ? [ - isBlocked - ? { key: "unblock", label: "Unblock Key", icon: } - : { key: "block", label: "Block Key", icon: , danger: true }, - ] - : []), - ...(onResetSpend - ? [{ key: "reset-spend", label: "Reset Spend", icon: , danger: true }] - : []), - { key: "delete", label: "Delete Key", icon: , danger: true }, - ]; - - const handleDestructiveActionClick: MenuProps["onClick"] = ({ key }) => { - if (key === "block" || key === "unblock") onToggleBlocked?.(); - if (key === "reset-spend") onResetSpend?.(); - if (key === "delete") onDelete?.(); - }; + const regenerateButton = ( + + + + ); return (
{onCreateNew && (
-
)}
-
- -
- - + <div className="flex items-start justify-between" style={{ marginBottom: 20 }}> + <div className="min-w-0"> + <div className="flex items-center gap-2"> + <h3 className="m-0 flex items-center gap-1 text-2xl font-semibold"> {data.keyName} - + + {isBlocked && ( - }> + + Blocked - + )} - - - Key ID: {data.keyId} - +
+
+ Key ID: {data.keyId} + +
{canModifyKey && ( - - - - - - - -
- - +
+
- } /> - + } /> +
- + - - } /> +
+ } /> } + icon={} truncate copyable defaultUserIdCheck /> - +
- + - - } /> - } /> - - +
+ } /> + } /> +
+
); } diff --git a/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx b/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx index b87beed048a..42d1884e563 100644 --- a/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx +++ b/ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx @@ -182,20 +182,6 @@ vi.mock("@heroicons/react/outline", async () => { return { ArrowLeftIcon, TrashIcon, RefreshIcon }; }); -vi.mock("lucide-react", async (importOriginal) => { - const actual = await importOriginal(); - const React = await import("react"); - function CopyIcon() { - return React.createElement("span"); - } - (CopyIcon as any).displayName = "CopyIcon"; - function CheckIcon() { - return React.createElement("span"); - } - (CheckIcon as any).displayName = "CheckIcon"; - return { ...actual, CopyIcon, CheckIcon }; -}); - // Heavy children -> async factories & local React vi.mock("../organisms/RegenerateKeyModal", () => { function RegenerateKeyModal() { diff --git a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx index 15d14d5abf5..a2d926dff8a 100644 --- a/ui/litellm-dashboard/src/components/templates/key_info_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_info_view.tsx @@ -4,9 +4,12 @@ import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings" import useTeams from "@/app/(dashboard)/hooks/useTeams"; import { formatNumberWithCommas } from "@/utils/dataUtils"; import { mapEmptyStringToNull } from "@/utils/keyUpdateUtils"; -import { ArrowLeftIcon } from "@heroicons/react/outline"; -import { Badge, Button, Card, Grid, Tab, TabGroup, TabList, TabPanel, TabPanels, Text, Title } from "@tremor/react"; -import { Modal, Tag } from "antd"; +import { ArrowLeft } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { KeyInfoHeader } from "./KeyInfoHeader"; import { useEffect, useState } from "react"; import { isProxyAdminRole, isUserTeamAdminForSingleTeam, rolesWithWriteAccess } from "../../utils/roles"; @@ -150,10 +153,11 @@ export default function KeyInfoView({ if (!currentKeyData) { return (
- - Key not found +

Key not found

); } @@ -534,93 +538,111 @@ export default function KeyInfoView({ /> {/* Reset Spend Confirmation Modal */} - setIsResetSpendModalOpen(false)} - okText="Reset" - okButtonProps={{ danger: true }} - confirmLoading={resetSpendLoading} - > -

- Reset spend for {currentKeyData?.key_alias || currentKeyData?.token_id || "this key"} to{" "} - $0? -

-

- Current spend: ${formatNumberWithCommas(currentKeyData.spend, 4)}. Spend history is preserved - in logs. This resets the current period spend counter, the same as an automatic budget reset. -

-
+ setIsResetSpendModalOpen(open)}> + + + Reset Key Spend + +

+ Reset spend for {currentKeyData?.key_alias || currentKeyData?.token_id || "this key"} to{" "} + $0? +

+

+ Current spend: ${formatNumberWithCommas(currentKeyData.spend, 4)}. Spend history is + preserved in logs. This resets the current period spend counter, the same as an automatic budget reset. +

+ + + + +
+
- setIsBlockModalOpen(false)} - okText={isBlocked ? "Unblock" : "Block"} - okButtonProps={isBlocked ? undefined : { danger: true }} - confirmLoading={blockLoading} - > -

- {isBlocked ? "Unblock" : "Block"}{" "} - {currentKeyData?.key_alias || currentKeyData?.token_id || "this key"}? -

-

- {isBlocked - ? "Requests using this key will be accepted again." - : "Requests using this key will be rejected with a 401 error until it is unblocked. The key is not deleted and can be unblocked at any time."} -

-
+ setIsBlockModalOpen(open)}> + + + {isBlocked ? "Unblock Key" : "Block Key"} + +

+ {isBlocked ? "Unblock" : "Block"}{" "} + {currentKeyData?.key_alias || currentKeyData?.token_id || "this key"}? +

+

+ {isBlocked + ? "Requests using this key will be accepted again." + : "Requests using this key will be rejected with a 401 error until it is unblocked. The key is not deleted and can be unblocked at any time."} +

+ + + + +
+
- - - Overview - Settings - + + + Overview + Settings + - +
{/* Overview Panel */} - - - - Spend + +
+ +

Spend

- ${formatNumberWithCommas(currentKeyData.spend, 4)} - of {budgetDisplay} +

${formatNumberWithCommas(currentKeyData.spend, 4)}

+

of {budgetDisplay}

{currentKeyData.budget_reset_at && ( - Resets {formatTimestamp(currentKeyData.budget_reset_at)} +

Resets {formatTimestamp(currentKeyData.budget_reset_at)}

)}
- - Rate Limits + +

Rate Limits

- TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"} - RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"} +

+ TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"} +

+

+ RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"} +

{Boolean(currentKeyData.metadata?.throttle_on_budget_exceeded) && ( - Throttle on budget exceeded: Yes +

Throttle on budget exceeded: Yes

)}
- - Models + +

Models

{currentKeyData.models && currentKeyData.models.length > 0 ? ( currentKeyData.models.map((model, index) => ( - + {model} )) ) : ( - No models specified +

No models specified

)}
- + - - Guardrails + +

Guardrails

{Array.isArray(currentKeyData.metadata?.guardrails) && currentKeyData.metadata.guardrails.length > 0 ? (
{currentKeyData.metadata.guardrails.map((guardrail: string, index: number) => ( - + {guardrail} ))}
) : ( - No guardrails configured +

No guardrails configured

)} {typeof currentKeyData.metadata?.disable_global_guardrails === "boolean" && currentKeyData.metadata.disable_global_guardrails === true && (
- Global Guardrails Disabled + Global Guardrails Disabled
)}
- - Policies + +

Policies

{Array.isArray(currentKeyData.metadata?.policies) && currentKeyData.metadata.policies.length > 0 ? (
{currentKeyData.metadata.policies.map((policy: string, index: number) => (
- {policy} - {loadingPolicies && Loading guardrails...} + + {policy} + + {loadingPolicies &&

Loading guardrails...

}
{!loadingPolicies && policyGuardrails[policy] && policyGuardrails[policy].length > 0 && (
- Resolved Guardrails: +

Resolved Guardrails:

{policyGuardrails[policy].map((guardrail: string, gIndex: number) => ( - + {guardrail} ))} @@ -675,7 +699,7 @@ export default function KeyInfoView({ ))}
) : ( - No policies configured +

No policies configured

)} @@ -697,15 +721,19 @@ export default function KeyInfoView({ nextRotationAt={currentKeyData.next_rotation_at} variant="card" /> - - +
+ {/* Settings Panel */} - - + +
- Key Settings - {!isEditing && canModifyKey && } +

Key Settings

+ {!isEditing && canModifyKey && ( + + )}
{isEditing ? ( @@ -722,29 +750,29 @@ export default function KeyInfoView({ ) : (
- Key ID - {currentKeyData.token_id || currentKeyData.token} +

Key ID

+

{currentKeyData.token_id || currentKeyData.token}

- Key Alias - {currentKeyData.key_alias || "Not Set"} +

Key Alias

+

{currentKeyData.key_alias || "Not Set"}

- Secret Key - {currentKeyData.key_name} +

Secret Key

+

{currentKeyData.key_name}

- Team ID - {currentKeyData.team_id || "Not Set"} +

Team ID

+

{currentKeyData.team_id || "Not Set"}

{enableProjectsUI && (
- Project - +

Project

+

{currentKeyData.project_id ? (() => { const project = projects?.find((p) => p.project_id === currentKeyData.project_id); @@ -753,41 +781,43 @@ export default function KeyInfoView({ : currentKeyData.project_id; })() : "Not Set"} - +

)}
- Organization - {(currentKeyData.organization_id ?? currentKeyData.org_id) || "Not Set"} +

Organization

+

{(currentKeyData.organization_id ?? currentKeyData.org_id) || "Not Set"}

- Created - {formatTimestamp(currentKeyData.created_at)} +

Created

+

{formatTimestamp(currentKeyData.created_at)}

{lastRegeneratedAt && (
- Last Regenerated +

Last Regenerated

- {formatTimestamp(lastRegeneratedAt)} - - Recent - +

{formatTimestamp(lastRegeneratedAt)}

+ Recent
)}
- Expires - {currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : "Never"} +

Expires

+

+ {currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : "Never"} +

{Boolean(currentKeyData.metadata?.enable_prompt_caching) && (
- Prompt Caching - Enabled (auto-injects cache_control markers on Anthropic and Bedrock Claude requests) +

Prompt Caching

+

+ Enabled (auto-injects cache_control markers on Anthropic and Bedrock Claude requests) +

)} @@ -802,31 +832,31 @@ export default function KeyInfoView({ />
- Spend - ${formatNumberWithCommas(currentKeyData.spend, 4)} USD +

Spend

+

${formatNumberWithCommas(currentKeyData.spend, 4)} USD

- Budget - +

Budget

+

{currentKeyData.max_budget !== null ? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}` : "Unlimited"} - +

- Budget Reset - +

Budget Reset

+

{currentKeyData.budget_reset_at ? `${currentKeyData.budget_duration ? `Every ${currentKeyData.budget_duration}, next ` : ""}${formatTimestamp(currentKeyData.budget_reset_at)}` : "Never"} - +

{currentKeyData.budget_fallbacks && Object.keys(currentKeyData.budget_fallbacks).length > 0 && (
- Budget Fallbacks +

Budget Fallbacks

{Object.entries(currentKeyData.budget_fallbacks).map(([model, fallbacks]) => (
@@ -841,7 +871,7 @@ export default function KeyInfoView({ {hasRouterSettings(currentKeyData.router_settings) && (
- Router Settings +

Router Settings

@@ -849,7 +879,7 @@ export default function KeyInfoView({ )}
- Tags +

Tags

{Array.isArray(currentKeyData.metadata?.tags) && currentKeyData.metadata.tags.length > 0 ? currentKeyData.metadata.tags.map((tag, index) => ( @@ -862,8 +892,8 @@ export default function KeyInfoView({
- Prompts - +

Prompts

+

{Array.isArray(currentKeyData.metadata?.prompts) && currentKeyData.metadata.prompts.length > 0 ? currentKeyData.metadata.prompts.map((prompt, index) => ( @@ -871,11 +901,11 @@ export default function KeyInfoView({ )) : "No prompts specified"} - +

- Allowed Routes +

Allowed Routes

{Array.isArray(currentKeyData.allowed_routes) && currentKeyData.allowed_routes.length > 0 ? ( currentKeyData.allowed_routes.map((route, index) => ( @@ -884,14 +914,14 @@ export default function KeyInfoView({ )) ) : ( - All routes allowed + All routes allowed )}
- Allowed Pass Through Routes - +

Allowed Pass Through Routes

+

{Array.isArray(currentKeyData.metadata?.allowed_passthrough_routes) && currentKeyData.metadata.allowed_passthrough_routes.length > 0 ? currentKeyData.metadata.allowed_passthrough_routes.map((route, index) => ( @@ -900,22 +930,22 @@ export default function KeyInfoView({ )) : "No pass through routes specified"} - +

- Disable Global Guardrails - +

Disable Global Guardrails

+

{currentKeyData.metadata?.disable_global_guardrails === true ? ( - Enabled - Global guardrails bypassed + Enabled - Global guardrails bypassed ) : ( - Disabled - Global guardrails active + Disabled - Global guardrails active )} - +

- Models +

Models

{currentKeyData.models && currentKeyData.models.length > 0 ? ( currentKeyData.models.map((model, index) => ( @@ -924,56 +954,60 @@ export default function KeyInfoView({ )) ) : ( - No models specified +

No models specified

)}
- Rate Limits - TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"} - RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"} - +

Rate Limits

+

+ TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"} +

+

+ RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"} +

+

Max Parallel Requests:{" "} {currentKeyData.max_parallel_requests !== null ? currentKeyData.max_parallel_requests : "Unlimited"} - - +

+

Model TPM Limits:{" "} {currentKeyData.metadata?.model_tpm_limit ? JSON.stringify(currentKeyData.metadata.model_tpm_limit) : "Unlimited"} - - +

+

Model RPM Limits:{" "} {currentKeyData.metadata?.model_rpm_limit ? JSON.stringify(currentKeyData.metadata.model_rpm_limit) : "Unlimited"} - - +

+

Tag RPM Limits:{" "} {currentKeyData.metadata?.tag_rpm_limit && Object.keys(currentKeyData.metadata.tag_rpm_limit).length > 0 ? JSON.stringify(currentKeyData.metadata.tag_rpm_limit) : "Unlimited"} - - +

+

Estimated Output Tokens:{" "} {currentKeyData.metadata?.default_estimated_output_tokens != null ? String(currentKeyData.metadata.default_estimated_output_tokens) : "Default"} - - +

+

Estimated Output Tokens Per Model:{" "} {currentKeyData.metadata?.default_estimated_output_tokens_per_model ? JSON.stringify(currentKeyData.metadata.default_estimated_output_tokens_per_model) : "Default"} - +

- Metadata +

Metadata

                       {formatMetadataForDisplay(stripTagsFromMetadata(currentKeyData.metadata))}
                     
@@ -999,9 +1033,9 @@ export default function KeyInfoView({
)} - - - + +
+
); }