mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(guardrails/usage_endpoints.py): fix usage dashboard
This commit is contained in:
parent
886f1a3472
commit
9828b9929f
39 changed files with 118 additions and 198 deletions
|
|
@ -106,9 +106,7 @@ def _aggregate_daily_metrics(metrics: Any, id_attr: str) -> Dict[str, Dict[str,
|
|||
return agg
|
||||
|
||||
|
||||
def _prev_fail_rates(
|
||||
metrics_prev: Any, id_attr: str
|
||||
) -> Dict[str, float]:
|
||||
def _prev_fail_rates(metrics_prev: Any, id_attr: str) -> Dict[str, float]:
|
||||
prev_agg_raw: Dict[str, Dict[str, int]] = {}
|
||||
for m in metrics_prev:
|
||||
gid = getattr(m, id_attr)
|
||||
|
|
@ -137,15 +135,34 @@ def _chart_from_metrics(metrics: Any) -> List[Dict[str, Any]]:
|
|||
]
|
||||
|
||||
|
||||
def _get_guardrail_attrs(g: Any) -> tuple[Any, str]:
|
||||
"""Get (guardrail_id, display_name) from guardrail - handles Prisma model or dict."""
|
||||
gid = getattr(g, "guardrail_id", None) or (
|
||||
g.get("guardrail_id") if isinstance(g, dict) else None
|
||||
)
|
||||
name = getattr(g, "guardrail_name", None) or (
|
||||
g.get("guardrail_name") if isinstance(g, dict) else None
|
||||
)
|
||||
return gid, (name or gid or "")
|
||||
|
||||
|
||||
def _guardrail_overview_rows(
|
||||
guardrails: Any,
|
||||
agg: Dict[str, Dict[str, Any]],
|
||||
prev_agg: Dict[str, float],
|
||||
) -> List[UsageOverviewRow]:
|
||||
rows: List[UsageOverviewRow] = []
|
||||
covered_keys: set = set()
|
||||
for g in guardrails:
|
||||
gid = g.guardrail_id
|
||||
a = agg.get(gid, {"requests": 0, "passed": 0, "blocked": 0, "flagged": 0})
|
||||
gid, display_name = _get_guardrail_attrs(g)
|
||||
# Metrics are keyed by logical name from spend log metadata; guardrails table uses UUID
|
||||
lookup_keys = [k for k in (display_name, gid) if k]
|
||||
covered_keys.update(lookup_keys)
|
||||
a = {"requests": 0, "passed": 0, "blocked": 0, "flagged": 0}
|
||||
for k in lookup_keys:
|
||||
if k in agg:
|
||||
a = agg[k]
|
||||
break
|
||||
req, blocked = a["requests"], a["blocked"]
|
||||
fail_rate = (100.0 * blocked / req) if req else 0.0
|
||||
litellm_params = (
|
||||
|
|
@ -156,16 +173,16 @@ def _guardrail_overview_rows(
|
|||
(g.guardrail_info or {}) if isinstance(g.guardrail_info, dict) else {}
|
||||
)
|
||||
gtype = str(guardrail_info.get("type", "Guardrail"))
|
||||
prev_fail = (
|
||||
prev_agg.get(gid, 0.0)
|
||||
if isinstance(prev_agg.get(gid), (int, float))
|
||||
else 0.0
|
||||
)
|
||||
prev_fail = 0.0
|
||||
for k in lookup_keys:
|
||||
if k in prev_agg:
|
||||
prev_fail = float(prev_agg.get(k, 0.0) or 0.0)
|
||||
break
|
||||
trend = _trend_from_comparison(fail_rate, prev_fail)
|
||||
rows.append(
|
||||
UsageOverviewRow(
|
||||
id=gid,
|
||||
name=g.guardrail_name or gid,
|
||||
name=display_name or str(gid),
|
||||
type=gtype,
|
||||
provider=provider,
|
||||
requestsEvaluated=req,
|
||||
|
|
@ -176,6 +193,28 @@ def _guardrail_overview_rows(
|
|||
trend=trend,
|
||||
)
|
||||
)
|
||||
# Add rows for guardrails with metrics but not in guardrails table (e.g. MCP, config)
|
||||
for agg_key, a in agg.items():
|
||||
if agg_key in covered_keys or a["requests"] == 0:
|
||||
continue
|
||||
req, blocked = a["requests"], a["blocked"]
|
||||
fail_rate = (100.0 * blocked / req) if req else 0.0
|
||||
prev_fail = float(prev_agg.get(agg_key, 0.0) or 0.0)
|
||||
trend = _trend_from_comparison(fail_rate, prev_fail)
|
||||
rows.append(
|
||||
UsageOverviewRow(
|
||||
id=agg_key,
|
||||
name=agg_key,
|
||||
type="Guardrail",
|
||||
provider="Custom",
|
||||
requestsEvaluated=req,
|
||||
failRate=round(fail_rate, 1),
|
||||
avgScore=None,
|
||||
avgLatency=None,
|
||||
status=_status_from_fail_rate(fail_rate),
|
||||
trend=trend,
|
||||
)
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
|
|
@ -411,9 +450,9 @@ def _usage_log_entry_from_row(
|
|||
duration = entry_for_guardrail.get("duration")
|
||||
if duration is not None:
|
||||
latency_val = round(float(duration) * 1000, 0)
|
||||
score_val = entry_for_guardrail.get("confidence_score") or entry_for_guardrail.get(
|
||||
"risk_score"
|
||||
)
|
||||
score_val = entry_for_guardrail.get(
|
||||
"confidence_score"
|
||||
) or entry_for_guardrail.get("risk_score")
|
||||
if score_val is not None:
|
||||
score_val = round(float(score_val), 2)
|
||||
resp = entry_for_guardrail.get("guardrail_response")
|
||||
|
|
@ -486,9 +525,7 @@ async def guardrails_usage_logs(
|
|||
return UsageLogsResponse(logs=[], total=0, page=page, page_size=page_size)
|
||||
|
||||
try:
|
||||
where = _build_usage_logs_where(
|
||||
guardrail_id, policy_id, start_date, end_date
|
||||
)
|
||||
where = _build_usage_logs_where(guardrail_id, policy_id, start_date, end_date)
|
||||
index_rows = await prisma_client.db.litellm_spendlogguardrailindex.find_many(
|
||||
where=where,
|
||||
order={"start_time": "desc"},
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
import {
|
||||
ArrowLeftOutlined,
|
||||
BellOutlined,
|
||||
PlayCircleOutlined,
|
||||
SafetyOutlined,
|
||||
SettingOutlined,
|
||||
WarningOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Card, Col, Grid, Title } from "@tremor/react";
|
||||
import { Col, Grid } from "@tremor/react";
|
||||
import { Button, Spin, Tabs } from "antd";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import {
|
||||
formatDate,
|
||||
getGuardrailsUsageDetail,
|
||||
getGuardrailsUsageLogs,
|
||||
} from "@/components/networking";
|
||||
|
|
@ -24,6 +21,8 @@ interface GuardrailDetailProps {
|
|||
guardrailId: string;
|
||||
onBack: () => void;
|
||||
accessToken?: string | null;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
}
|
||||
|
||||
const statusColors: Record<
|
||||
|
|
@ -35,19 +34,15 @@ const statusColors: Record<
|
|||
critical: { bg: "bg-red-50", text: "text-red-700", dot: "bg-red-500" },
|
||||
};
|
||||
|
||||
const defaultEnd = new Date();
|
||||
const defaultStart = new Date();
|
||||
defaultStart.setDate(defaultStart.getDate() - 7);
|
||||
|
||||
export function GuardrailDetail({
|
||||
guardrailId,
|
||||
onBack,
|
||||
accessToken = null,
|
||||
startDate,
|
||||
endDate,
|
||||
}: GuardrailDetailProps) {
|
||||
const [activeTab, setActiveTab] = useState("overview");
|
||||
const [evaluationModalOpen, setEvaluationModalOpen] = useState(false);
|
||||
const [startDate] = useState(() => formatDate(defaultStart));
|
||||
const [endDate] = useState(() => formatDate(defaultEnd));
|
||||
const [logsPage, setLogsPage] = useState(1);
|
||||
const logsPageSize = 50;
|
||||
|
||||
|
|
@ -156,23 +151,12 @@ export function GuardrailDetail({
|
|||
<span className="inline-flex items-center px-2.5 py-1 text-xs font-medium rounded-md bg-indigo-50 text-indigo-700 border border-indigo-200">
|
||||
{data.provider}
|
||||
</span>
|
||||
<Button type="default" icon={<PlayCircleOutlined />} title="Coming soon">
|
||||
Re-run AI
|
||||
</Button>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<SettingOutlined />}
|
||||
onClick={() => setEvaluationModalOpen(true)}
|
||||
title="Evaluation settings"
|
||||
/>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<BellOutlined />}
|
||||
title="Coming soon"
|
||||
className="opacity-75"
|
||||
>
|
||||
Notify
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -223,52 +207,6 @@ export function GuardrailDetail({
|
|||
</Col>
|
||||
</Grid>
|
||||
|
||||
<Card className="bg-white border border-gray-200 rounded-lg p-6">
|
||||
<Title className="text-base font-semibold text-gray-900 mb-1">
|
||||
Root Cause Analysis
|
||||
</Title>
|
||||
<p className="text-xs text-gray-500 mb-4">Common patterns in failing requests</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3 p-3 bg-red-50 rounded-lg border border-red-100">
|
||||
<WarningOutlined className="text-red-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-red-800">
|
||||
High sensitivity to medical terminology
|
||||
</p>
|
||||
<p className="text-xs text-red-600 mt-0.5">
|
||||
34% of blocked requests contain common medical terms (e.g., "symptoms",
|
||||
"treatment", "medication") that are benign in context.
|
||||
Consider adding an allowlist or relaxing sensitivity for these categories.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3 p-3 bg-amber-50 rounded-lg border border-amber-100">
|
||||
<WarningOutlined className="text-amber-500 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-amber-800">
|
||||
False positives on educational content
|
||||
</p>
|
||||
<p className="text-xs text-amber-600 mt-0.5">
|
||||
22% of blocked requests are educational queries about safety topics. The guardrail
|
||||
is flagging the topic itself rather than harmful intent.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3 p-3 bg-gray-50 rounded-lg border border-gray-200">
|
||||
<WarningOutlined className="text-gray-400 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-800">
|
||||
Sensitivity may be too aggressive
|
||||
</p>
|
||||
<p className="text-xs text-gray-600 mt-0.5">
|
||||
Many blocked requests may be false positives. Consider relaxing sensitivity or
|
||||
adding allowlisted patterns to reduce blocks by ~40% while maintaining safety.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<LogViewer
|
||||
guardrailName={data.name}
|
||||
filterAction="blocked"
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@ import * as networking from "@/components/networking";
|
|||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getGuardrailsUsageOverview: vi.fn(),
|
||||
getPoliciesUsageOverview: vi.fn(),
|
||||
formatDate: vi.fn((d: Date) => d.toISOString().slice(0, 10)),
|
||||
}));
|
||||
|
||||
const mockGetGuardrailsUsageOverview = vi.mocked(networking.getGuardrailsUsageOverview);
|
||||
const mockGetPoliciesUsageOverview = vi.mocked(networking.getPoliciesUsageOverview);
|
||||
|
||||
function wrapper({ children }: { children: React.ReactNode }) {
|
||||
const queryClient = new QueryClient({
|
||||
|
|
@ -27,7 +25,7 @@ function wrapper({ children }: { children: React.ReactNode }) {
|
|||
}
|
||||
|
||||
describe("GuardrailsMonitorView", () => {
|
||||
it("should render overview and fetch guardrails and policies usage when accessToken is provided", async () => {
|
||||
it("should render overview and fetch guardrails usage when accessToken is provided", async () => {
|
||||
mockGetGuardrailsUsageOverview.mockResolvedValue({
|
||||
rows: [],
|
||||
chart: [],
|
||||
|
|
@ -35,13 +33,6 @@ describe("GuardrailsMonitorView", () => {
|
|||
totalBlocked: 0,
|
||||
passRate: 100,
|
||||
});
|
||||
mockGetPoliciesUsageOverview.mockResolvedValue({
|
||||
rows: [],
|
||||
chart: [],
|
||||
totalRequests: 0,
|
||||
totalBlocked: 0,
|
||||
passRate: 100,
|
||||
});
|
||||
|
||||
render(
|
||||
<GuardrailsMonitorView accessToken="test-token" />,
|
||||
|
|
@ -51,7 +42,6 @@ describe("GuardrailsMonitorView", () => {
|
|||
expect(await screen.findByRole("heading", { name: /Guardrails Monitor/i })).toBeDefined();
|
||||
await waitFor(() => {
|
||||
expect(mockGetGuardrailsUsageOverview).toHaveBeenCalled();
|
||||
expect(mockGetPoliciesUsageOverview).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React, { useState } from "react";
|
||||
import { GuardrailsOverview } from "./GuardrailsOverview";
|
||||
import type { DateRangePickerValue } from "@tremor/react";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { formatDate } from "@/components/networking";
|
||||
import AdvancedDatePicker from "@/components/shared/advanced_date_picker";
|
||||
import { GuardrailDetail } from "./GuardrailDetail";
|
||||
import { GuardrailsOverview } from "./GuardrailsOverview";
|
||||
|
||||
type View =
|
||||
| { type: "overview" }
|
||||
|
|
@ -10,9 +13,28 @@ interface GuardrailsMonitorViewProps {
|
|||
accessToken?: string | null;
|
||||
}
|
||||
|
||||
const defaultEnd = new Date();
|
||||
const defaultStart = new Date();
|
||||
defaultStart.setDate(defaultStart.getDate() - 7);
|
||||
|
||||
export default function GuardrailsMonitorView({ accessToken = null }: GuardrailsMonitorViewProps) {
|
||||
const [view, setView] = useState<View>({ type: "overview" });
|
||||
|
||||
const initialFrom = useMemo(() => new Date(defaultStart), []);
|
||||
const initialTo = useMemo(() => new Date(defaultEnd), []);
|
||||
|
||||
const [dateValue, setDateValue] = useState<DateRangePickerValue>({
|
||||
from: initialFrom,
|
||||
to: initialTo,
|
||||
});
|
||||
|
||||
const startDate = dateValue.from ? formatDate(dateValue.from) : "";
|
||||
const endDate = dateValue.to ? formatDate(dateValue.to) : "";
|
||||
|
||||
const handleDateChange = useCallback((newValue: DateRangePickerValue) => {
|
||||
setDateValue(newValue);
|
||||
}, []);
|
||||
|
||||
const handleSelectGuardrail = (id: string) => {
|
||||
setView({ type: "detail", guardrailId: id });
|
||||
};
|
||||
|
|
@ -23,13 +45,28 @@ export default function GuardrailsMonitorView({ accessToken = null }: Guardrails
|
|||
|
||||
return (
|
||||
<div className="p-6 w-full min-w-0 flex-1">
|
||||
<div className="flex items-center justify-end mb-4">
|
||||
<AdvancedDatePicker
|
||||
value={dateValue}
|
||||
onValueChange={handleDateChange}
|
||||
label=""
|
||||
showTimeRange={false}
|
||||
/>
|
||||
</div>
|
||||
{view.type === "overview" ? (
|
||||
<GuardrailsOverview accessToken={accessToken} onSelectGuardrail={handleSelectGuardrail} />
|
||||
<GuardrailsOverview
|
||||
accessToken={accessToken}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onSelectGuardrail={handleSelectGuardrail}
|
||||
/>
|
||||
) : (
|
||||
<GuardrailDetail
|
||||
guardrailId={view.guardrailId}
|
||||
onBack={handleBack}
|
||||
accessToken={accessToken}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import {
|
||||
DownloadOutlined,
|
||||
FileTextOutlined,
|
||||
PlayCircleOutlined,
|
||||
RiseOutlined,
|
||||
SafetyOutlined,
|
||||
SettingOutlined,
|
||||
|
|
@ -11,12 +9,8 @@ import { useQuery } from "@tanstack/react-query";
|
|||
import { Card, Col, Grid, Title } from "@tremor/react";
|
||||
import { Button, Spin, Table } from "antd";
|
||||
import type { ColumnsType } from "antd/es/table";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
getGuardrailsUsageOverview,
|
||||
getPoliciesUsageOverview,
|
||||
} from "@/components/networking";
|
||||
import { formatDate } from "@/components/networking";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { getGuardrailsUsageOverview } from "@/components/networking";
|
||||
import { type PerformanceRow } from "./mockData";
|
||||
import { EvaluationSettingsModal } from "./EvaluationSettingsModal";
|
||||
import { MetricCard } from "./MetricCard";
|
||||
|
|
@ -24,10 +18,11 @@ import { ScoreChart } from "./ScoreChart";
|
|||
|
||||
interface GuardrailsOverviewProps {
|
||||
accessToken?: string | null;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
onSelectGuardrail: (id: string) => void;
|
||||
}
|
||||
|
||||
type ViewMode = "guardrails" | "policies";
|
||||
type SortKey =
|
||||
| "failRate"
|
||||
| "requestsEvaluated"
|
||||
|
|
@ -58,46 +53,25 @@ function computeMetricsFromRows(data: PerformanceRow[]) {
|
|||
return { totalRequests, totalBlocked, passRate, avgLatency, count: data.length };
|
||||
}
|
||||
|
||||
type RerunState = "idle" | "running" | "done";
|
||||
|
||||
const defaultEnd = new Date();
|
||||
const defaultStart = new Date();
|
||||
defaultStart.setDate(defaultStart.getDate() - 7);
|
||||
|
||||
export function GuardrailsOverview({
|
||||
accessToken = null,
|
||||
startDate,
|
||||
endDate,
|
||||
onSelectGuardrail,
|
||||
}: GuardrailsOverviewProps) {
|
||||
const [viewMode, setViewMode] = useState<ViewMode>("guardrails");
|
||||
const [sortBy, setSortBy] = useState<SortKey>("failRate");
|
||||
const [sortDir, setSortDir] = useState<"asc" | "desc">("desc");
|
||||
const [rerunState, setRerunState] = useState<RerunState>("idle");
|
||||
const [evaluationModalOpen, setEvaluationModalOpen] = useState(false);
|
||||
const [startDate, setStartDate] = useState<string>(() => formatDate(defaultStart));
|
||||
const [endDate, setEndDate] = useState<string>(() => formatDate(defaultEnd));
|
||||
|
||||
const { data: guardrailsData, isLoading: guardrailsLoading, error: guardrailsError } = useQuery({
|
||||
queryKey: ["guardrails-usage-overview", startDate, endDate],
|
||||
queryFn: () => getGuardrailsUsageOverview(accessToken!, startDate, endDate),
|
||||
enabled: !!accessToken,
|
||||
});
|
||||
const { data: policiesData, isLoading: policiesLoading, error: policiesError } = useQuery({
|
||||
queryKey: ["policies-usage-overview", startDate, endDate],
|
||||
queryFn: () => getPoliciesUsageOverview(accessToken!, startDate, endDate),
|
||||
enabled: !!accessToken,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (rerunState !== "done") return;
|
||||
const t = setTimeout(() => setRerunState("idle"), 4000);
|
||||
return () => clearTimeout(t);
|
||||
}, [rerunState]);
|
||||
|
||||
const activeData: PerformanceRow[] = viewMode === "guardrails"
|
||||
? (guardrailsData?.rows ?? [])
|
||||
: (policiesData?.rows ?? []);
|
||||
const activeData: PerformanceRow[] = guardrailsData?.rows ?? [];
|
||||
const metrics = useMemo(() => {
|
||||
if (viewMode === "guardrails" && guardrailsData) {
|
||||
if (guardrailsData) {
|
||||
return {
|
||||
totalRequests: guardrailsData.totalRequests ?? 0,
|
||||
totalBlocked: guardrailsData.totalBlocked ?? 0,
|
||||
|
|
@ -106,18 +80,9 @@ export function GuardrailsOverview({
|
|||
count: activeData.length,
|
||||
};
|
||||
}
|
||||
if (viewMode === "policies" && policiesData) {
|
||||
return {
|
||||
totalRequests: policiesData.totalRequests ?? 0,
|
||||
totalBlocked: policiesData.totalBlocked ?? 0,
|
||||
passRate: String(policiesData.passRate ?? 0),
|
||||
avgLatency: activeData.length ? Math.round(activeData.reduce((s, r) => s + (r.avgLatency ?? 0), 0) / activeData.length) : 0,
|
||||
count: activeData.length,
|
||||
};
|
||||
}
|
||||
return computeMetricsFromRows(activeData);
|
||||
}, [viewMode, guardrailsData, policiesData, activeData]);
|
||||
const chartData = viewMode === "guardrails" ? guardrailsData?.chart : policiesData?.chart;
|
||||
}, [guardrailsData, activeData]);
|
||||
const chartData = guardrailsData?.chart;
|
||||
const sorted = useMemo(() => {
|
||||
return [...activeData].sort((a, b) => {
|
||||
const mult = sortDir === "desc" ? -1 : 1;
|
||||
|
|
@ -126,14 +91,12 @@ export function GuardrailsOverview({
|
|||
return (Number(aVal) - Number(bVal)) * mult;
|
||||
});
|
||||
}, [activeData, sortBy, sortDir]);
|
||||
const isLoading = viewMode === "guardrails" ? guardrailsLoading : policiesLoading;
|
||||
const error = viewMode === "guardrails" ? guardrailsError : policiesError;
|
||||
|
||||
const isGuardrails = viewMode === "guardrails";
|
||||
const isLoading = guardrailsLoading;
|
||||
const error = guardrailsError;
|
||||
|
||||
const columns: ColumnsType<PerformanceRow> = [
|
||||
{
|
||||
title: isGuardrails ? "Guardrail" : "Policy",
|
||||
title: "Guardrail",
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
render: (name: string, row) => (
|
||||
|
|
@ -236,12 +199,6 @@ export function GuardrailsOverview({
|
|||
}
|
||||
};
|
||||
|
||||
const handleRerun = () => {
|
||||
if (rerunState !== "idle") return;
|
||||
setRerunState("running");
|
||||
setTimeout(() => setRerunState("done"), 2500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-start justify-between mb-5">
|
||||
|
|
@ -251,50 +208,16 @@ export function GuardrailsOverview({
|
|||
<h1 className="text-xl font-semibold text-gray-900">Guardrails Monitor</h1>
|
||||
</div>
|
||||
<p className="text-sm text-gray-500">
|
||||
{isGuardrails
|
||||
? "Monitor guardrail performance across all requests"
|
||||
: "Monitor policy enforcement across all requests"}
|
||||
Monitor guardrail performance across all requests
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-sm text-gray-600 bg-white border border-gray-200 rounded-md px-3 py-2">
|
||||
{startDate} – {endDate}
|
||||
</span>
|
||||
<Button type="default" icon={<DownloadOutlined />} title="Coming soon">
|
||||
Export Data
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 p-1 bg-gray-100 rounded-lg w-fit mb-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setViewMode("guardrails");
|
||||
setSortBy("failRate");
|
||||
setSortDir("desc");
|
||||
}}
|
||||
className={`inline-flex items-center gap-1.5 px-3.5 py-1.5 text-sm font-medium rounded-md transition-colors ${
|
||||
isGuardrails ? "bg-white text-gray-900 shadow-sm" : "text-gray-500 hover:text-gray-700"
|
||||
}`}
|
||||
>
|
||||
<SafetyOutlined /> Guardrail Performance
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setViewMode("policies");
|
||||
setSortBy("failRate");
|
||||
setSortDir("desc");
|
||||
}}
|
||||
className={`inline-flex items-center gap-1.5 px-3.5 py-1.5 text-sm font-medium rounded-md transition-colors ${
|
||||
!isGuardrails ? "bg-white text-gray-900 shadow-sm" : "text-gray-500 hover:text-gray-700"
|
||||
}`}
|
||||
>
|
||||
<FileTextOutlined /> Policy Performance
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Grid numItems={2} numItemsLg={5} className="gap-4 mb-6 items-stretch">
|
||||
<Col className="flex flex-col">
|
||||
<MetricCard label="Total Requests Evaluated" value={metrics.totalRequests.toLocaleString()} />
|
||||
|
|
@ -330,7 +253,7 @@ export function GuardrailsOverview({
|
|||
</Col>
|
||||
<Col className="flex flex-col">
|
||||
<MetricCard
|
||||
label={isGuardrails ? "Active Guardrails" : "Active Policies"}
|
||||
label="Active Guardrails"
|
||||
value={metrics.count}
|
||||
/>
|
||||
</Col>
|
||||
|
|
@ -350,12 +273,10 @@ export function GuardrailsOverview({
|
|||
<div className="px-6 py-4 border-b border-gray-200 flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<Title className="text-base font-semibold text-gray-900">
|
||||
{isGuardrails ? "Guardrail Performance" : "Policy Performance"}
|
||||
Guardrail Performance
|
||||
</Title>
|
||||
<p className="text-xs text-gray-500 mt-0.5">
|
||||
{isGuardrails
|
||||
? "Click a guardrail to view details, logs, and configuration"
|
||||
: "Click a policy to view details, logs, and configuration"}
|
||||
Click a guardrail to view details, logs, and configuration
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -365,9 +286,6 @@ export function GuardrailsOverview({
|
|||
onClick={() => setEvaluationModalOpen(true)}
|
||||
title="Evaluation settings"
|
||||
/>
|
||||
<Button type="default" icon={<PlayCircleOutlined />} title="Coming soon">
|
||||
Re-run AI on last 100 logs
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Table
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export const pageDescriptions: Record<string, string> = {
|
|||
"vector-stores": "Manage vector databases for embeddings",
|
||||
new_usage: "View usage analytics and metrics",
|
||||
logs: "Access request and response logs",
|
||||
"guardrails-monitor": "Monitor guardrail and policy performance and view logs",
|
||||
"guardrails-monitor": "Monitor guardrail performance and view logs",
|
||||
users: "Manage internal user accounts and permissions",
|
||||
teams: "Create and manage teams for access control",
|
||||
organizations: "Manage organizations and their members",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue