diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index ec1e3ac05ba..a2aaf61263e 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -177,9 +177,6 @@ "src/app/(dashboard)/caching/_components/cache_health.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/caching/_components/cache_settings/CacheFormField.tsx": { @@ -187,11 +184,6 @@ "count": 1 } }, - "src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/caching/_components/cache_settings/cacheSettingsFields.ts": { "no-restricted-imports": { "count": 1 @@ -213,11 +205,6 @@ "count": 1 } }, - "src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/caching/_components/coordination_redis_settings/coordinationRedisFields.ts": { "no-restricted-imports": { "count": 1 @@ -1469,9 +1456,6 @@ "no-nested-ternary": { "count": 10 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/immutability": { "count": 1 } @@ -1483,9 +1467,6 @@ "no-nested-ternary": { "count": 1 }, - "no-restricted-imports": { - "count": 1 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1509,9 +1490,6 @@ "src/app/(dashboard)/policies/_components/impact_preview_alert.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/policies/_components/index.test.tsx": { @@ -1526,9 +1504,6 @@ "local/no-complex-jsx-arrow": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1543,9 +1518,6 @@ "no-nested-ternary": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/set-state-in-effect": { "count": 2 } @@ -1554,9 +1526,6 @@ "local/filename-pascal-case": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -1564,9 +1533,6 @@ "src/app/(dashboard)/policies/_components/policy_templates.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/policies/_components/policy_test_panel.tsx": { @@ -1584,9 +1550,6 @@ "local/filename-pascal-case": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/immutability": { "count": 1 }, @@ -2218,26 +2181,11 @@ "count": 1 } }, - "src/components/CloudZeroCostTracking/CloudZeroCostTracking.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/components/CloudZeroCostTracking/CloudZeroEmptyPlaceholder.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/CloudZeroCostTracking/CloudZeroIntegrationSettings.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx": { "no-restricted-imports": { "count": 1 @@ -3240,9 +3188,6 @@ "local/filename-pascal-case": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "react-hooks/immutability": { "count": 1 } @@ -3251,9 +3196,6 @@ "local/filename-pascal-case": { "count": 1 }, - "no-restricted-imports": { - "count": 2 - }, "prefer-const": { "count": 1 } @@ -4116,6 +4058,11 @@ "count": 1 } }, + "src/components/ui/radio-group.tsx": { + "local/filename-pascal-case": { + "count": 1 + } + }, "src/components/ui/scroll-area.tsx": { "local/filename-pascal-case": { "count": 1 diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx index 51c0b85cedb..95b73d1aacb 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_dashboard.tsx @@ -1,25 +1,24 @@ -import { - Card, - Col, - DateRangePickerValue, - Grid, - Icon, - MultiSelect, - MultiSelectItem, - Tab, - TabGroup, - TabList, - TabPanel, - TabPanels, - Text, -} from "@tremor/react"; +import { DateRangePickerValue } from "@tremor/react"; import React, { useEffect, useState } from "react"; import NotificationsManager from "@/components/molecules/notifications_manager"; import UsageDatePicker from "@/components/shared/usage_date_picker"; import { BarChart } from "@/components/shared/charts"; -import { Card as ChartCard, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Combobox, + ComboboxChip, + ComboboxChips, + ComboboxChipsInput, + ComboboxContent, + ComboboxEmpty, + ComboboxItem, + ComboboxList, + ComboboxValue, +} from "@/components/ui/combobox"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { RefreshIcon } from "@heroicons/react/outline"; +import { RefreshCw } from "lucide-react"; import { adminGlobalCacheActivity, cachingHealthCheckCall } from "@/components/networking"; // Import the new component @@ -258,31 +257,42 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole } }; + const statCards = [ + { label: "Cache Hit Ratio", value: `${cacheHitRatio}%` }, + { label: "Cache Hits", value: cachedResponses }, + { label: "Cached Completion Tokens", value: cachedTokens }, + ]; + return ( - - -
- Cache Analytics - Cache Health - Cache Settings - Coordination Redis -
+ +
+ + + Cache Analytics + + + Cache Health + + + Cache Settings + + + Coordination Redis + +
- {lastRefreshed && Last Refreshed: {lastRefreshed}} - + {lastRefreshed &&

Last Refreshed: {lastRefreshed}

} +
- - - - - +
+ + + + +

Analytics for LiteLLM's{" "} = ({ accessToken, token, userRole {" "} (cached input tokens from Anthropic, OpenAI, etc.) is not shown here; see "Prompt Caching Metrics" on the Usage page or individual requests in the Logs page. - - - - - {uniqueApiKeys.map((key) => ( - - {key} - - ))} - - - - - {uniqueModels.map((model) => ( - - {model} - - ))} - - - - { - setDateValue(value); - updateCachingData(value.from, value.to); - }} - /> - - +

-
- -

- Cache Hit Ratio -

-
-

- {cacheHitRatio}% -

-
-
- -

- Cache Hits -

-
-

- {cachedResponses} -

-
-
+
+ setSelectedApiKeys(keys)} + > + + + {(keys: string[]) => + keys.map((key) => ( + + {key} + + )) + } + + + + + No virtual keys found + + {(key: string) => ( + + {key} + + )} + + + - -

- Cached Completion Tokens -

-
-

- {cachedTokens} -

-
-
+ setSelectedModels(models)} + > + + + {(models: string[]) => + models.map((model) => ( + + {model} + + )) + } + + + + + No models found + + {(model: string) => ( + + {model} + + )} + + + + + { + setDateValue(value); + updateCachingData(value.from, value.to); + }} + />
- +
+ {statCards.map((stat) => ( + + +

{stat.label}

+
+

{stat.value}

+
+
+
+ ))} +
+ + Cache Hits vs API Requests @@ -387,9 +413,9 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole yAxisWidth={48} /> -
+ - + Cached Completion Tokens vs Generated Completion Tokens @@ -406,24 +432,27 @@ const CacheDashboard: React.FC = ({ accessToken, token, userRole yAxisWidth={48} /> - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + ); }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx index 887c12a4f3d..85649c1cf26 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_health.tsx @@ -1,6 +1,7 @@ import React from "react"; -import { Text, Button, TabGroup, TabList, Tab, TabPanel, TabPanels } from "@tremor/react"; -import { CheckCircleIcon, XCircleIcon, ClipboardCopyIcon } from "@heroicons/react/outline"; +import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { CheckCircle2, XCircle, ClipboardCopy } from "lucide-react"; import { ResponseTimeIndicator } from "./response_time_indicator"; // Helper function to deep-parse a JSON string if possible @@ -30,22 +31,26 @@ const TableClickableErrorField: React.FC<{ label: string; value: string | null | }; return ( - + -
-
-
-
{label}
-
-                {isExpanded ? safeValue : truncated}
-              
+
{label}
+
{isExpanded ? safeValue : truncated}
-
@@ -151,112 +156,114 @@ const HealthCheckDetails: React.FC<{ response: any }> = ({ response }) => { }; return ( -
- - - Summary - Raw Response - +
+ + + + Summary + + + Raw Response + + - - -
-
- {response?.status === "healthy" ? ( - - ) : ( - + +
+
+ {response?.status === "healthy" ? ( + + ) : ( + + )} +

+ Cache Status: {response?.status || "unhealthy"} +

+
+ + + + {/* Show error message if present */} + {errorDetails && ( + <> + + + + + + )} - - Cache Status: {response?.status || "unhealthy"} - - -
+ Error Details +
- - {/* Show error message if present */} - {errorDetails && ( - <> - - - - - - - )} + {/* Always show cache details, regardless of error state */} + + + + + + + - {/* Always show cache details, regardless of error state */} - - - - - - - + {/* Redis Details Section */} + {parsedLitellmParams?.type === "redis" && ( + <> + + + + + + + + + + )} + +
- Error Details -
+ Cache Details +
- Cache Details -
+ Redis Details +
+
+
- {/* Redis Details Section */} - {parsedLitellmParams?.type === "redis" && ( - <> - - - Redis Details - - - - - - - - - )} - - -
- - - -
-
-                {(() => {
-                  try {
-                    const data = {
-                      ...response,
-                      litellm_cache_params: parsedLitellmParams,
-                      health_check_cache_params: parsedRedisParams,
-                    };
-                    // First parse any string JSON values
-                    const prettyData = JSON.parse(
-                      JSON.stringify(data, (key, value) => {
-                        if (typeof value === "string") {
-                          try {
-                            return JSON.parse(value);
-                          } catch {
-                            return value;
-                          }
+        
+          
+
+              {(() => {
+                try {
+                  const data = {
+                    ...response,
+                    litellm_cache_params: parsedLitellmParams,
+                    health_check_cache_params: parsedRedisParams,
+                  };
+                  // First parse any string JSON values
+                  const prettyData = JSON.parse(
+                    JSON.stringify(data, (key, value) => {
+                      if (typeof value === "string") {
+                        try {
+                          return JSON.parse(value);
+                        } catch {
+                          return value;
                         }
-                        return value;
-                      }),
-                    );
-                    // Then stringify with proper formatting
-                    return JSON.stringify(prettyData, null, 2);
-                  } catch (e) {
-                    return "Error formatting JSON: " + (e as Error).message;
-                  }
-                })()}
-              
-
- - - + } + return value; + }), + ); + // Then stringify with proper formatting + return JSON.stringify(prettyData, null, 2); + } catch (e) { + return "Error formatting JSON: " + (e as Error).message; + } + })()} +
+
+ +
); }; @@ -282,11 +289,7 @@ export const CacheHealthTab: React.FC<{ return (
- diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx index fbca7ab5a97..40f553e6fde 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/cache_settings/RedisTypeSelector.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Select, SelectItem } from "@tremor/react"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; interface RedisTypeSelectorProps { redisType: string; @@ -7,17 +7,30 @@ interface RedisTypeSelectorProps { onTypeChange: (type: string) => void; } +const REDIS_TYPE_LABELS: Readonly> = { + node: "Node (Single Instance)", + cluster: "Cluster", + sentinel: "Sentinel", + semantic: "Semantic", +}; + const RedisTypeSelector: React.FC = ({ redisType, redisTypeDescriptions, onTypeChange }) => { return (
- - value !== null && onTypeChange(value)}> + + {REDIS_TYPE_LABELS[redisType] ?? redisType} + + + {Object.entries(REDIS_TYPE_LABELS).map(([value, label]) => ( + + {label} + + ))} + -

+

{redisTypeDescriptions[redisType] || "Select the type of Redis deployment you're using"}

diff --git a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx index daab8505890..e6385e83649 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/caching/_components/coordination_redis_settings/CoordinationRedisTypeSelector.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Select } from "antd"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { COORDINATION_REDIS_TYPES, COORDINATION_REDIS_TYPE_DESCRIPTIONS, @@ -12,21 +12,24 @@ interface CoordinationRedisTypeSelectorProps { onTypeChange: (type: CoordinationRedisType) => void; } -const OPTIONS = COORDINATION_REDIS_TYPES.map((type) => ({ value: type, label: COORDINATION_REDIS_TYPE_LABELS[type] })); - const CoordinationRedisTypeSelector: React.FC = ({ redisType, onTypeChange }) => (
-
); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx index 34204624702..cbf24b425f4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/ai_suggestion_modal.tsx @@ -1,13 +1,14 @@ import React, { useEffect, useMemo, useState } from "react"; -import { Modal, Spin, Checkbox, Select, Input, Typography, Tooltip } from "antd"; -import { Button, Card } from "@tremor/react"; -import { - CheckCircleOutlined, - CloseCircleOutlined, - InfoCircleOutlined, - DownOutlined, - RightOutlined, -} from "@ant-design/icons"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; +import { SearchSelect } from "@/components/shared/SearchSelect"; +import { CheckCircle2, ChevronDown, ChevronRight, Info, XCircle } from "lucide-react"; import { suggestPolicyTemplates, modelHubCall, @@ -15,9 +16,6 @@ import { enrichPolicyTemplateStream, } from "@/components/networking"; -const { TextArea } = Input; -const { Text } = Typography; - interface SuggestedTemplate { template_id: string; reason: string; @@ -427,7 +425,7 @@ const AiSuggestionModal: React.FC = ({
toggleTemplate(suggestion.template_id)} + onCheckedChange={() => toggleTemplate(suggestion.template_id)} className="mt-0.5" />
@@ -447,20 +445,25 @@ const AiSuggestionModal: React.FC = ({ )} {template.estimated_latency_ms != null && ( - - + + } > +{template.estimated_latency_ms <= 1 ? "<1" : template.estimated_latency_ms}ms latency - + + Estimated latency overhead added to each request )}
-

{template.description}

+

{template.description}

{template.guardrails && template.guardrails.slice(0, 4).map((g: string) => ( @@ -476,7 +479,7 @@ const AiSuggestionModal: React.FC = ({ )}
- +

{suggestion.reason}

@@ -490,7 +493,7 @@ const AiSuggestionModal: React.FC = ({ {explanation && (
- + Why these templates @@ -555,7 +558,7 @@ const AiSuggestionModal: React.FC = ({ >
{hasEnrichedGuardrails ? ( - + ) : ( = ({
setEnrichBrandName(e.target.value)} - onPressEnter={() => enrichBrandName.trim() && handleEnrichCompetitors()} + onKeyDown={(e) => { + if (e.key === "Enter" && enrichBrandName.trim() && !isEnriching) handleEnrichCompetitors(); + }} className="flex-1" /> -
{isEnriching && enrichStatusMessage && ( -
- +
+ {enrichStatusMessage}
)} {hasEnrichedGuardrails && (
- + Competitor names loaded for {enrichBrandName}
)} @@ -631,33 +630,29 @@ const AiSuggestionModal: React.FC = ({
- - + + } /> + Press Enter to submit. Use Shift+Enter for new line.
- Characters: {testInputText.length} + Characters: {testInputText.length}
-