diff --git a/ui/litellm-dashboard/src/components/GuardrailsMonitor/EvaluationSettingsModal.tsx b/ui/litellm-dashboard/src/components/GuardrailsMonitor/EvaluationSettingsModal.tsx index 90a2c0d597d..4d4b6c5e092 100644 --- a/ui/litellm-dashboard/src/components/GuardrailsMonitor/EvaluationSettingsModal.tsx +++ b/ui/litellm-dashboard/src/components/GuardrailsMonitor/EvaluationSettingsModal.tsx @@ -1,6 +1,7 @@ import { CloseOutlined, PlayCircleOutlined } from "@ant-design/icons"; import { Button, Modal, Select, Input } from "antd"; import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { fetchAvailableModels, type ModelGroup } from "@/components/playground/llm_calls/fetch_models"; const DEFAULT_PROMPT = `Evaluate whether this guardrail's decision was correct. @@ -37,6 +38,7 @@ export function EvaluationSettingsModal({ accessToken, onRunEvaluation, }: EvaluationSettingsModalProps) { + const { t } = useTranslation(); const [prompt, setPrompt] = useState(DEFAULT_PROMPT); const [schema, setSchema] = useState(DEFAULT_SCHEMA); const [model, setModel] = useState(null); @@ -80,7 +82,7 @@ export function EvaluationSettingsModal({ return (

{guardrailName - ? `Configure AI evaluation for ${guardrailName}` - : "Configure AI evaluation for re-running on logs"} + ? t("guardrailsMonitor.evaluationSettingsModal.describeForGuardrail", { name: guardrailName }) + : t("guardrailsMonitor.evaluationSettingsModal.describeForLogs")}

- +
-

- System prompt sent to the evaluation model. Output is structured via response_format. -

+

{t("guardrailsMonitor.evaluationSettingsModal.promptHint")}

- +

response_format: json_schema

- +
- +
@@ -97,62 +138,45 @@ export function GuardrailConfig({ guardrailName, guardrailType, provider }: Guar {/* Parameters */}
-

Parameters

-

Configure {guardrailName} behavior

+

+ {t("guardrailsMonitor.guardrailConfig.parametersTitle")} +

+

+ {t("guardrailsMonitor.guardrailConfig.parametersDesc", { name: guardrailName })} +

- -
- -
- -
- +
- Guardrail enabled in production + {t("guardrailsMonitor.guardrailConfig.enabledInProduction")}
@@ -163,9 +187,9 @@ export function GuardrailConfig({ guardrailName, guardrailType, provider }: Guar

- Custom Code Override + {t("guardrailsMonitor.guardrailConfig.customCodeTitle")}

-

Replace the built-in guardrail with custom evaluation code

+

{t("guardrailsMonitor.guardrailConfig.customCodeDesc")}

@@ -188,10 +212,10 @@ export function GuardrailConfig({ guardrailName, guardrailType, provider }: Guar {/* Re-run on Failing Logs */}
-

Test Configuration

-

- Re-run this guardrail on recent failing logs to validate your changes -

+

+ {t("guardrailsMonitor.guardrailConfig.testConfigTitle")} +

+

{t("guardrailsMonitor.guardrailConfig.testConfigDesc")}

{rerunStatus === "success" && ( - 7/10 would now pass with new config + {t("guardrailsMonitor.guardrailConfig.rerunSuccess")} )} - {rerunStatus === "error" && Error running tests} + {rerunStatus === "error" && ( + {t("guardrailsMonitor.guardrailConfig.rerunError")} + )}
diff --git a/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailDetail.tsx b/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailDetail.tsx index 2d0d5710f8d..a360d0b8151 100644 --- a/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailDetail.tsx +++ b/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailDetail.tsx @@ -2,6 +2,7 @@ import { ArrowLeftOutlined, SafetyOutlined, SettingOutlined, WarningOutlined } f import { useQuery } from "@tanstack/react-query"; import { Button, Col, Row, Spin, Tabs } from "antd"; import React, { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { getGuardrailsUsageDetail, getGuardrailsUsageLogs } from "@/components/networking"; import { EvaluationSettingsModal } from "./EvaluationSettingsModal"; import { LogViewer } from "./LogViewer"; @@ -23,6 +24,7 @@ const statusColors: Record = }; export function GuardrailDetail({ guardrailId, onBack, accessToken = null, startDate, endDate }: GuardrailDetailProps) { + const { t } = useTranslation(); const [activeTab, setActiveTab] = useState("overview"); const [evaluationModalOpen, setEvaluationModalOpen] = useState(false); const [logsPage, setLogsPage] = useState(1); @@ -100,9 +102,9 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start return (
-

Failed to load guardrail details.

+

{t("guardrailsMonitor.guardrailDetail.loadError")}

); } @@ -111,7 +113,7 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start
@@ -136,7 +138,7 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start type="default" icon={} onClick={() => setEvaluationModalOpen(true)} - title="Evaluation settings" + title={t("guardrailsMonitor.guardrailDetail.evaluationSettings")} />
@@ -146,8 +148,8 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start activeKey={activeTab} onChange={setActiveTab} items={[ - { key: "overview", label: "Overview" }, - { key: "logs", label: "Logs" }, + { key: "overview", label: t("guardrailsMonitor.guardrailDetail.tabOverview") }, + { key: "logs", label: t("guardrailsMonitor.guardrailDetail.tabLogs") }, ]} /> @@ -155,22 +157,27 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start
- + 15 ? "text-red-600" : data.failRate > 5 ? "text-amber-600" : "text-green-600" } - subtitle={`${Math.round((data.requestsEvaluated * data.failRate) / 100).toLocaleString()} blocked`} + subtitle={t("guardrailsMonitor.guardrailDetail.blockedCount", { + count: Math.round((data.requestsEvaluated * data.failRate) / 100).toLocaleString(), + })} icon={data.failRate > 15 ? : undefined} /> diff --git a/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailsOverview.tsx b/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailsOverview.tsx index 076ab13496f..e7dcf76b994 100644 --- a/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailsOverview.tsx +++ b/ui/litellm-dashboard/src/components/GuardrailsMonitor/GuardrailsOverview.tsx @@ -3,6 +3,7 @@ import { useQuery } from "@tanstack/react-query"; import { Button, Card, Col, Row, Spin, Table, Typography } from "antd"; import type { ColumnsType } from "antd/es/table"; import React, { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { getGuardrailsUsageOverview } from "@/components/networking"; import { type PerformanceRow } from "./mockData"; import { EvaluationSettingsModal } from "./EvaluationSettingsModal"; @@ -41,6 +42,7 @@ export function GuardrailsOverview({ endDate, onSelectGuardrail, }: GuardrailsOverviewProps) { + const { t } = useTranslation(); const [sortBy, setSortBy] = useState("failRate"); const [sortDir, setSortDir] = useState<"asc" | "desc">("desc"); const [evaluationModalOpen, setEvaluationModalOpen] = useState(false); @@ -82,92 +84,95 @@ export function GuardrailsOverview({ const isLoading = guardrailsLoading; const error = guardrailsError; - const columns: ColumnsType = [ - { - title: "Guardrail", - dataIndex: "name", - key: "name", - render: (name: string, row) => ( - - ), - }, - { - title: "Provider", - dataIndex: "provider", - key: "provider", - render: (provider: string) => ( - - {provider} - - ), - }, - { - title: "Requests", - dataIndex: "requestsEvaluated", - key: "requestsEvaluated", - align: "right", - sorter: true, - sortOrder: sortBy === "requestsEvaluated" ? (sortDir === "desc" ? "descend" : "ascend") : null, - render: (v: number) => v.toLocaleString(), - }, - { - title: "Fail Rate", - dataIndex: "failRate", - key: "failRate", - align: "right", - sorter: true, - sortOrder: sortBy === "failRate" ? (sortDir === "desc" ? "descend" : "ascend") : null, - render: (v: number, row) => ( - 15 ? "text-red-600" : v > 5 ? "text-amber-600" : "text-green-600"}> - {v}%{row.trend === "up" && ↑} - {row.trend === "down" && ↓} - - ), - }, - { - title: "Avg. latency added", - dataIndex: "avgLatency", - key: "avgLatency", - align: "right", - sorter: true, - sortOrder: sortBy === "avgLatency" ? (sortDir === "desc" ? "descend" : "ascend") : null, - render: (v?: number) => ( - 150 ? "text-red-600" : v > 50 ? "text-amber-600" : "text-green-600" - } - > - {v != null ? `${v}ms` : "—"} - - ), - }, - { - title: "Status", - dataIndex: "status", - key: "status", - align: "center", - render: (status: string) => ( - + const columns: ColumnsType = useMemo( + () => [ + { + title: t("guardrailsMonitor.guardrailsOverview.colGuardrail"), + dataIndex: "name", + key: "name", + render: (name: string, row) => ( + + ), + }, + { + title: t("guardrailsMonitor.guardrailsOverview.colProvider"), + dataIndex: "provider", + key: "provider", + render: (provider: string) => ( - {status} - - ), - }, - ]; + > + {provider} + + ), + }, + { + title: t("guardrailsMonitor.guardrailsOverview.colRequests"), + dataIndex: "requestsEvaluated", + key: "requestsEvaluated", + align: "right", + sorter: true, + sortOrder: sortBy === "requestsEvaluated" ? (sortDir === "desc" ? "descend" : "ascend") : null, + render: (v: number) => v.toLocaleString(), + }, + { + title: t("guardrailsMonitor.guardrailsOverview.colFailRate"), + dataIndex: "failRate", + key: "failRate", + align: "right", + sorter: true, + sortOrder: sortBy === "failRate" ? (sortDir === "desc" ? "descend" : "ascend") : null, + render: (v: number, row) => ( + 15 ? "text-red-600" : v > 5 ? "text-amber-600" : "text-green-600"}> + {v}%{row.trend === "up" && ↑} + {row.trend === "down" && ↓} + + ), + }, + { + title: t("guardrailsMonitor.guardrailsOverview.colAvgLatency"), + dataIndex: "avgLatency", + key: "avgLatency", + align: "right", + sorter: true, + sortOrder: sortBy === "avgLatency" ? (sortDir === "desc" ? "descend" : "ascend") : null, + render: (v?: number) => ( + 150 ? "text-red-600" : v > 50 ? "text-amber-600" : "text-green-600" + } + > + {v != null ? `${v}ms` : "—"} + + ), + }, + { + title: t("guardrailsMonitor.guardrailsOverview.colStatus"), + dataIndex: "status", + key: "status", + align: "center", + render: (status: string) => ( + + + {status} + + ), + }, + ], + [t, sortBy, sortDir, onSelectGuardrail], + ); const sortableKeys: SortKey[] = ["failRate", "requestsEvaluated", "avgLatency"]; const handleTableChange = (_pagination: unknown, _filters: unknown, sorter: unknown) => { @@ -184,24 +189,27 @@ export function GuardrailsOverview({
-

Guardrails Monitor

+

{t("guardrailsMonitor.guardrailsOverview.title")}

-

Monitor guardrail performance across all requests

+

{t("guardrailsMonitor.guardrailsOverview.subtitle")}

-
- + } @@ -209,7 +217,7 @@ export function GuardrailsOverview({ } @@ -217,7 +225,7 @@ export function GuardrailsOverview({ 150 ? "text-red-600" : metrics.avgLatency > 50 ? "text-amber-600" : "text-green-600" @@ -225,7 +233,7 @@ export function GuardrailsOverview({ /> - + @@ -237,22 +245,26 @@ export function GuardrailsOverview({ {(isLoading || error) && (
{isLoading && } - {error && Failed to load data. Try again.} + {error && ( + {t("guardrailsMonitor.guardrailsOverview.loadError")} + )}
)}
- Guardrail Performance + {t("guardrailsMonitor.guardrailsOverview.performanceTitle")} -

Click a guardrail to view details, logs, and configuration

+

+ {t("guardrailsMonitor.guardrailsOverview.performanceSubtitle")} +

@@ -263,7 +275,11 @@ export function GuardrailsOverview({ pagination={false} loading={isLoading} onChange={handleTableChange} - locale={activeData.length === 0 && !isLoading ? { emptyText: "No data for this period" } : undefined} + locale={ + activeData.length === 0 && !isLoading + ? { emptyText: t("guardrailsMonitor.guardrailsOverview.noDataForPeriod") } + : undefined + } onRow={(row) => ({ onClick: () => onSelectGuardrail(row.id), style: { cursor: "pointer" }, diff --git a/ui/litellm-dashboard/src/components/GuardrailsMonitor/LogViewer.tsx b/ui/litellm-dashboard/src/components/GuardrailsMonitor/LogViewer.tsx index 623a8252067..ddc91668006 100644 --- a/ui/litellm-dashboard/src/components/GuardrailsMonitor/LogViewer.tsx +++ b/ui/litellm-dashboard/src/components/GuardrailsMonitor/LogViewer.tsx @@ -2,36 +2,34 @@ import { CheckCircleOutlined, CloseOutlined, DownOutlined, WarningOutlined } fro import { useQuery } from "@tanstack/react-query"; import moment from "moment"; import { Button, Spin } from "antd"; -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { uiSpendLogsCall } from "@/components/networking"; import { LogDetailsDrawer } from "@/components/view_logs/LogDetailsDrawer"; import type { LogEntry as ViewLogsLogEntry } from "@/components/view_logs/columns"; import type { LogEntry } from "./mockData"; -const actionConfig: Record< +const actionStyles: Record< "blocked" | "passed" | "flagged", - { icon: React.ElementType; color: string; bg: string; border: string; label: string } + { icon: React.ElementType; color: string; bg: string; border: string } > = { blocked: { icon: CloseOutlined, color: "text-red-600", bg: "bg-red-50", border: "border-red-200", - label: "Blocked", }, passed: { icon: CheckCircleOutlined, color: "text-green-600", bg: "bg-green-50", border: "border-green-200", - label: "Passed", }, flagged: { icon: WarningOutlined, color: "text-amber-600", bg: "bg-amber-50", border: "border-amber-200", - label: "Flagged", }, }; @@ -56,11 +54,21 @@ export function LogViewer({ startDate = "", endDate = "", }: LogViewerProps) { + const { t } = useTranslation(); const [sampleSize, setSampleSize] = useState(10); const [activeFilter, setActiveFilter] = useState(filterAction); const [selectedRequestId, setSelectedRequestId] = useState(null); const [drawerOpen, setDrawerOpen] = useState(false); + const actionConfig = useMemo( + () => ({ + blocked: { ...actionStyles.blocked, label: t("guardrailsMonitor.logViewer.actionBlocked") }, + passed: { ...actionStyles.passed, label: t("guardrailsMonitor.logViewer.actionPassed") }, + flagged: { ...actionStyles.flagged, label: t("guardrailsMonitor.logViewer.actionFlagged") }, + }), + [t], + ); + const filteredLogs = logs.filter((log) => activeFilter === "all" || log.action === activeFilter); const displayLogs = filteredLogs.slice(0, sampleSize); const total = totalLogs ?? logs.length; @@ -109,14 +117,19 @@ export function LogViewer({

- {guardrailName ? `Logs — ${guardrailName}` : "Request Logs"} + {guardrailName + ? t("guardrailsMonitor.logViewer.titleWithName", { name: guardrailName }) + : t("guardrailsMonitor.logViewer.titleDefault")}

{logsLoading - ? "Loading…" + ? t("common.loading") : logs.length > 0 - ? `Showing ${displayLogs.length} of ${total} entries` - : "No logs for this period. Select a guardrail and date range."} + ? t("guardrailsMonitor.logViewer.showingEntries", { + shown: displayLogs.length, + total, + }) + : t("guardrailsMonitor.logViewer.noLogsForPeriod")}

{logs.length > 0 && ( @@ -135,7 +148,7 @@ export function LogViewer({
- Sample: + {t("guardrailsMonitor.logViewer.sampleLabel")} {sampleSizes.map((size) => (