diff --git a/app/src/lib/audit/runner.ts b/app/src/lib/audit/runner.ts index 9467d15..03bc77f 100644 --- a/app/src/lib/audit/runner.ts +++ b/app/src/lib/audit/runner.ts @@ -212,29 +212,50 @@ export async function runAudit( onProgress?: (progress: number) => void, ): Promise { onProgress?.(0); - const maxRetries = 2; - const chunkSize = Math.ceil(scenarios.length / 1); - - let lastError: Error | null = null; - for (let retry = 0; retry <= maxRetries; retry++) { - try { - const prompt = buildPrompt(scenarios); - const rawResponse = await runLLMAudit(config, prompt.system, prompt.user, () => { - onProgress?.(Math.round((retry * chunkSize) / maxRetries)); - }); - const llmResults = parseLLMResponse(rawResponse, scenarios.map(s => s.id)); - const scenarioMap = new Map(scenarios.map(s => [s.id, { module: s.module, moduleLabel: s.moduleLabel }])); - const report = buildAuditReport(llmResults, scenarioMap, config.provider, config.model, rawResponse); - onProgress?.(scenarios.length); - return report; - } catch (e) { - lastError = e instanceof Error ? e : new Error(String(e)); - if (retry < maxRetries) { - onProgress?.(0); + const maxRetriesPerChunk = 2; + const chunkSize = 10; // Enviar 10 cenários por vez é muito mais seguro + + const allLlmResults = []; + let completed = 0; + let rawResponseFull = ''; + + for (let i = 0; i < scenarios.length; i += chunkSize) { + const chunk = scenarios.slice(i, i + chunkSize); + let chunkSuccess = false; + let chunkError: Error | null = null; + + for (let retry = 0; retry <= maxRetriesPerChunk; retry++) { + try { + const prompt = buildPrompt(chunk); + // Avisar a UI do progresso absoluto + onProgress?.(completed); + + const rawResponse = await runLLMAudit(config, prompt.system, prompt.user, (msg) => { + // Chamamos o onProgress para disparar a verificação de abortar do AuditPanel + onProgress?.(completed); + }); + rawResponseFull += rawResponse + '\n\n'; + + const llmResults = parseLLMResponse(rawResponse, chunk.map(s => s.id)); + allLlmResults.push(...llmResults); + chunkSuccess = true; + break; // Sucesso, sai do retry loop + } catch (e) { + chunkError = e instanceof Error ? e : new Error(String(e)); + console.warn(`Erro no chunk ${i} (tentativa ${retry}):`, chunkError); } } + + if (!chunkSuccess) { + throw chunkError ?? new Error(`Falha ao auditar cenários no índice ${i} após retentativas.`); + } + completed += chunk.length; + onProgress?.(completed); } - throw lastError ?? new Error('Audit failed after retries'); + + const scenarioMap = new Map(scenarios.map(s => [s.id, { module: s.module, moduleLabel: s.moduleLabel }])); + const report = buildAuditReport(allLlmResults, scenarioMap, config.provider, config.model, rawResponseFull); + return report; } async function fetchWithTimeout(