'use client' import React, { useState, useEffect, useCallback } from 'react' import { useSDK } from '@/lib/sdk' // ============================================================================= // TYPES // ============================================================================= interface LLMLogEntry { id: string user_id: string namespace: string model: string provider: string prompt_tokens: number completion_tokens: number total_tokens: number pii_detected: boolean pii_categories: string[] redacted: boolean duration_ms: number status: string created_at: string } interface UsageStats { total_requests: number total_tokens: number total_prompt_tokens: number total_completion_tokens: number models_used: Record providers_used: Record avg_duration_ms: number pii_detection_rate: number period_start: string period_end: string } interface ComplianceReport { total_requests: number pii_incidents: number pii_rate: number redaction_rate: number policy_violations: number top_pii_categories: Record namespace_breakdown: Record user_breakdown: Record period_start: string period_end: string } type TabId = 'llm-log' | 'usage' | 'compliance' // ============================================================================= // HELPERS // ============================================================================= const API_BASE = '/api/sdk/v1/audit-llm' function formatDate(iso: string): string { return new Date(iso).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }) } function formatNumber(n: number): string { return n.toLocaleString('de-DE') } function formatDuration(ms: number): string { if (ms < 1000) return `${ms}ms` return `${(ms / 1000).toFixed(1)}s` } function getDateRange(period: string): { from: string; to: string } { const now = new Date() const to = now.toISOString().slice(0, 10) const from = new Date(now) switch (period) { case '7d': from.setDate(from.getDate() - 7); break case '30d': from.setDate(from.getDate() - 30); break case '90d': from.setDate(from.getDate() - 90); break default: from.setDate(from.getDate() - 7) } return { from: from.toISOString().slice(0, 10), to } } // ============================================================================= // MAIN PAGE // ============================================================================= export default function AuditLLMPage() { const { state } = useSDK() const [activeTab, setActiveTab] = useState('llm-log') const [period, setPeriod] = useState('7d') const [loading, setLoading] = useState(false) const [error, setError] = useState(null) // LLM Log state const [logEntries, setLogEntries] = useState([]) const [logFilter, setLogFilter] = useState({ model: '', pii: '' }) // Usage state const [usageStats, setUsageStats] = useState(null) // Compliance state const [complianceReport, setComplianceReport] = useState(null) // ─── Load Data ─────────────────────────────────────────────────────── const loadLLMLog = useCallback(async () => { setLoading(true) setError(null) try { const { from, to } = getDateRange(period) const params = new URLSearchParams({ from, to, limit: '100' }) if (logFilter.model) params.set('model', logFilter.model) if (logFilter.pii === 'true') params.set('pii_detected', 'true') if (logFilter.pii === 'false') params.set('pii_detected', 'false') const res = await fetch(`${API_BASE}/llm?${params}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) const data = await res.json() setLogEntries(Array.isArray(data) ? data : data.entries || data.logs || []) } catch (e) { setError(e instanceof Error ? e.message : 'Fehler beim Laden') } finally { setLoading(false) } }, [period, logFilter]) const loadUsage = useCallback(async () => { setLoading(true) setError(null) try { const { from, to } = getDateRange(period) const res = await fetch(`${API_BASE}/usage?from=${from}&to=${to}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) const data = await res.json() setUsageStats(data) } catch (e) { setError(e instanceof Error ? e.message : 'Fehler beim Laden') } finally { setLoading(false) } }, [period]) const loadCompliance = useCallback(async () => { setLoading(true) setError(null) try { const { from, to } = getDateRange(period) const res = await fetch(`${API_BASE}/compliance-report?from=${from}&to=${to}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) const data = await res.json() setComplianceReport(data) } catch (e) { setError(e instanceof Error ? e.message : 'Fehler beim Laden') } finally { setLoading(false) } }, [period]) useEffect(() => { if (activeTab === 'llm-log') loadLLMLog() else if (activeTab === 'usage') loadUsage() else if (activeTab === 'compliance') loadCompliance() }, [activeTab, loadLLMLog, loadUsage, loadCompliance]) // ─── Export ────────────────────────────────────────────────────────── const handleExport = async (type: 'llm' | 'general' | 'compliance', format: 'json' | 'csv') => { try { const { from, to } = getDateRange(period) const res = await fetch(`${API_BASE}/export/${type}?from=${from}&to=${to}&format=${format}`) if (!res.ok) throw new Error(`Export fehlgeschlagen: ${res.status}`) const blob = await res.blob() const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `audit-${type}-${from}-${to}.${format}` a.click() URL.revokeObjectURL(url) } catch (e) { setError(e instanceof Error ? e.message : 'Export fehlgeschlagen') } } // ─── Tabs ──────────────────────────────────────────────────────────── const tabs: { id: TabId; label: string }[] = [ { id: 'llm-log', label: 'LLM-Log' }, { id: 'usage', label: 'Nutzung' }, { id: 'compliance', label: 'Compliance' }, ] return (
{/* Header */}

LLM Audit Dashboard

Monitoring und Compliance-Analyse der LLM-Operationen

{/* Period + Tabs */}
{tabs.map(tab => ( ))}
{error && (
{error}
)} {loading && (
)} {/* ── LLM-Log Tab ── */} {!loading && activeTab === 'llm-log' && (
{/* Filters */}
setLogFilter(f => ({ ...f, model: e.target.value }))} className="border border-gray-300 rounded-lg px-3 py-2 text-sm w-48" />
{/* Table */}
{logEntries.length === 0 ? ( ) : logEntries.map(entry => ( ))}
Zeitpunkt User Model Tokens PII Dauer Status
Keine Log-Eintraege im gewaehlten Zeitraum
{formatDate(entry.created_at)} {entry.user_id?.slice(0, 8)}... {entry.model} {formatNumber(entry.total_tokens)} {entry.pii_detected ? ( {entry.redacted ? 'Redacted' : 'Erkannt'} ) : ( - )} {formatDuration(entry.duration_ms)} {entry.status}
{logEntries.length} Eintraege
)} {/* ── Nutzung Tab ── */} {!loading && activeTab === 'usage' && usageStats && (
{/* Stats Cards */}
0.1} />
{/* Token Breakdown */}

Model-Nutzung

{Object.entries(usageStats.models_used || {}).length === 0 ? (

Keine Daten

) : (
{Object.entries(usageStats.models_used).sort((a, b) => b[1] - a[1]).map(([model, count]) => (
{model}
{formatNumber(count)}
))}
)}

Provider-Verteilung

{Object.entries(usageStats.providers_used || {}).length === 0 ? (

Keine Daten

) : (
{Object.entries(usageStats.providers_used).sort((a, b) => b[1] - a[1]).map(([provider, count]) => (
{provider} {formatNumber(count)}
))}
)}
{/* Token Details */}

Token-Aufschluesselung

{formatNumber(usageStats.total_prompt_tokens)}
Prompt Tokens
{formatNumber(usageStats.total_completion_tokens)}
Completion Tokens
{formatNumber(usageStats.total_tokens)}
Gesamt
)} {/* ── Compliance Tab ── */} {!loading && activeTab === 'compliance' && complianceReport && (
{/* Summary Cards */}
0} /> 0.05} />
{complianceReport.policy_violations > 0 && (
{complianceReport.policy_violations} Policy-Verletzungen im Zeitraum
)}
{/* PII Categories */}

PII-Kategorien

{Object.entries(complianceReport.top_pii_categories || {}).length === 0 ? (

Keine PII erkannt

) : (
{Object.entries(complianceReport.top_pii_categories).sort((a, b) => b[1] - a[1]).map(([cat, count]) => (
{cat} {count}
))}
)}
{/* Namespace Breakdown */}

Namespace-Analyse

{Object.entries(complianceReport.namespace_breakdown || {}).length === 0 ? (

Keine Namespace-Daten

) : (
{Object.entries(complianceReport.namespace_breakdown).map(([ns, data]) => ( ))}
Namespace Requests PII
{ns} {formatNumber(data.requests)} {data.pii_incidents > 0 ? ( {data.pii_incidents} ) : ( 0 )}
)}
{/* User Breakdown */} {Object.entries(complianceReport.user_breakdown || {}).length > 0 && (

Top-Nutzer

{Object.entries(complianceReport.user_breakdown) .sort((a, b) => b[1].requests - a[1].requests) .slice(0, 10) .map(([userId, data]) => ( ))}
User-ID Requests PII-Vorfaelle
{userId} {formatNumber(data.requests)} {data.pii_incidents > 0 ? ( {data.pii_incidents} ) : ( 0 )}
)}
)} {/* Empty state for usage/compliance when no data */} {!loading && activeTab === 'usage' && !usageStats && !error && (
Keine Nutzungsdaten verfuegbar
)} {!loading && activeTab === 'compliance' && !complianceReport && !error && (
Kein Compliance-Report verfuegbar
)}
) } // ============================================================================= // STAT CARD // ============================================================================= function StatCard({ label, value, highlight }: { label: string; value: string; highlight?: boolean }) { return (
{label}
{value}
) }