import { useState, useCallback, useRef } from 'react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Play, FileDown, Loader2, AlertCircle } from 'lucide-react'; import type { AuditConfig, AuditScenario, ScenarioResult, AuditReport } from '@/lib/audit/types'; import { generateAllScenarios } from '@/lib/audit/scenarios'; import AuditDetailPopup from './AuditDetailPopup'; const PROVIDERS: { value: string; label: string }[] = [ { value: 'openai', label: 'OpenAI' }, { value: 'anthropic', label: 'Anthropic' }, { value: 'minimax', label: 'MiniMax' }, { value: 'openrouter', label: 'OpenRouter' }, { value: 'ollama', label: 'Ollama' }, ]; const DEFAULT_MODELS: Record = { openai: 'gpt-4o', anthropic: 'claude-3-5-sonnet-latest', minimax: 'minimax-m3.0', openrouter: 'openai/gpt-4o', ollama: 'deepseek-r1', }; export default function AuditPanel() { const [config, setConfig] = useState({ provider: 'openai', model: 'gpt-4o', apiKey: '', baseUrl: '', }); const [status, setStatus] = useState<'idle' | 'generating' | 'running' | 'done' | 'error'>('idle'); const [progress, setProgress] = useState(0); const [total, setTotal] = useState(0); const [report, setReport] = useState(null); const [error, setError] = useState(null); const abortRef = useRef(false); const [popupOpen, setPopupOpen] = useState(false); const [selectedScenario, setSelectedScenario] = useState(null); const [selectedResult, setSelectedResult] = useState(null); const handleProviderChange = (val: string) => { setConfig((c) => ({ ...c, provider: val as AuditConfig['provider'], model: DEFAULT_MODELS[val] ?? c.model, })); }; const handleRun = useCallback(async () => { if (!config.apiKey && config.provider !== 'ollama') { setError('Informe uma API key para continuar.'); setStatus('error'); return; } const controller = new AbortController(); abortRef.current = controller as any; // Usando any pois não precisamos refatorar todos os tipos agora, apenas para passar o signal setError(null); setReport(null); setProgress(0); setStatus('generating'); const scenarios = generateAllScenarios(); setTotal(scenarios.length); setStatus('running'); try { const { runAudit } = await import('@/lib/audit/runner'); const reportResult = await runAudit( config, scenarios, (p) => { if (typeof p === 'number') setProgress(p); }, controller.signal ); setReport(reportResult); setStatus('done'); // Auto-download MD import('@/lib/audit/export-audit-md').then(m => m.downloadAuditMarkdown(reportResult)); } catch (e) { if (e instanceof Error && e.message.includes('Abortado')) { setStatus('idle'); return; } setError(e instanceof Error ? e.message : 'Erro desconhecido'); setStatus('error'); } }, [config]); const handleExportPDF = () => { if (!report) return; import('@/lib/audit/export-audit-pdf').then((mod) => mod.exportAuditReportToPDF(report)); }; const grouped = report && report.modules ? report.modules.map((m: { module: string; moduleLabel: string; totalScenarios: number; passed: number; failed: number; warnings: number }) => ({ module: m.module, label: m.moduleLabel, total: m.totalScenarios, passed: m.passed, failed: m.failed, warnings: m.warnings, })) : []; return (
Auditoria LLM Validação dos cálculos de vento usando LLMs sequenciais. 100+ cenários cobrindo todos os módulos.
setConfig((c) => ({ ...c, model: e.target.value }))} placeholder="gpt-4o, claude-3-5-sonnet-..." />
setConfig((c) => ({ ...c, apiKey: e.target.value }))} placeholder="sk-..." />
setConfig((c) => ({ ...c, baseUrl: e.target.value || undefined }))} placeholder={config.provider === 'openrouter' ? 'https://openrouter.ai/api/v1 (padrão)' : config.provider === 'minimax' ? 'https://api.minimax.chat/v1 (padrão)' : config.provider === 'ollama' ? 'http://localhost:11434 (padrão)' : 'https://api.openai.com/v1'} />
{(status === 'running' || status === 'generating') && ( )} {status === 'done' && report && ( <> )}
{(status === 'running' || status === 'generating') && total > 0 && (
{status === 'generating' ? 'Gerando cenários...' : `Processando cenários...`} {progress} / {total}
Aguardando resposta do modelo para o lote atual... (pode demorar alguns segundos)
)} {status === 'error' && error && (
{error}
)} {status === 'done' && report && ( Resultados {report.totalPassed}/{report.totalScenarios} PASS — W:{report.totalWarnings} F:{report.totalFailed} Provedor: {report.provider} ({report.model}) — {new Date(report.timestamp).toLocaleString('pt-BR')}
{grouped.map((g: { module: string; label: string; total: number; passed: number; failed: number; warnings: number }) => ( { const mod = report.modules.find((m) => m.module === g.module); if (!mod) return; const modScenarios = generateAllScenarios().filter((s) => s.module === g.module); const dialog = mod.scenarios[0]; if (modScenarios.length > 0 && dialog) { setSelectedScenario(modScenarios[0]); setSelectedResult(dialog); setPopupOpen(true); } }}> ))}
Módulo Total PASS WARN FAIL
{g.label} {g.total} {g.passed} {g.warnings} {g.failed}

Clique em um módulo para ver detalhes dos cenários e verificações.

)}
); }