Spaces:
Sleeping
Sleeping
| import { getGroqTokens } from "@/lib/apiKeys"; // GAP-6: key rotation | |
| import { acquireGroqHigh } from "@/lib/groqSemaphore"; // ORCH-5: priority lane per percorso critico | |
| /** | |
| * debateBlock.ts — S495: extracted from agentLoop.ts | |
| * | |
| * Wrapper per il Dual-Agent Debate (S224). | |
| * Trigger: reasoning_depth="high" + task NON di codice. | |
| * Output: stringa consenso da iniettare come contesto pre-loop, o null. | |
| * | |
| * Separa la logica di orchestrazione del debate dalle variabili di closure | |
| * di agentLoop.ts, rendendo il debate block testabile in isolamento. | |
| * | |
| * @module debateBlock | |
| */ | |
| /** | |
| * iOS-safe AbortSignal con timeout. | |
| * AbortSignal.timeout() richiede iOS Safari >= 16.4 — non disponibile su iOS 15. | |
| * Fallback: AbortController + setTimeout. | |
| */ | |
| function _makeTimedSignal(ms: number): AbortSignal { | |
| if (typeof AbortSignal.timeout === "function") { | |
| try { return AbortSignal.timeout(ms); } catch { /* iOS 15 safeguard */ } | |
| } | |
| const c = new AbortController(); | |
| setTimeout(() => c.abort(), ms); | |
| return c.signal; | |
| } | |
| /** | |
| * Esegue il Dual-Agent Debate se le condizioni sono soddisfatte. | |
| * Restituisce il testo del consenso o null (mai lancia — tutto wrapped in try/catch). | |
| * | |
| * @param task — testo del task/messaggio utente | |
| * @param groqToken — Bearer token Groq (null → skip debate) | |
| * @param opts — opzioni: signal, onStatus callback | |
| * @returns — testo consenso o null | |
| */ | |
| export async function runDebateIfNeeded( | |
| task: string, | |
| groqToken: string | null | undefined, | |
| opts?: { | |
| signal?: AbortSignal; | |
| onStatus?: (msg: string) => void; | |
| }, | |
| ): Promise<string | null> { | |
| if (!groqToken || opts?.signal?.aborted) return null; | |
| try { | |
| opts?.onStatus?.("Valuto da più angolazioni…"); | |
| const { debateToText } = await import("../agent/debateRunner"); | |
| // Groq 70B per il debate (ragionamento architetturale, non streaming) | |
| // GAP-6: key rotation — tenta tutti gli slot Groq disponibili se 429 | |
| const _debateLLM = async ( | |
| msgs: Array<{ role: string; content: string }>, | |
| llmOpts?: { signal?: AbortSignal }, | |
| ): Promise<string> => { | |
| // ORCH-5: acquireGroqHigh — debate è percorso critico, usa priority lane. | |
| // GAP-3: coordina con il semaforo globale (MAX_CONCURRENT_GROQ=3). | |
| // Debate fa 2 chiamate LLM sequenziali (BUILDER + CRITIC) senza passare per providerChain. | |
| const _release = await acquireGroqHigh(); | |
| try { | |
| const _pool = [...new Set([...getGroqTokens(), ...(groqToken ? [groqToken] : [])])]; | |
| if (_pool.length === 0) return ""; | |
| for (let _i = 0; _i < _pool.length; _i++) { | |
| const _tok = _pool[_i]; | |
| const r = await fetch("https://api.groq.com/openai/v1/chat/completions", { | |
| method: "POST", | |
| headers: { Authorization: `Bearer ${_tok}`, "Content-Type": "application/json" }, | |
| body: JSON.stringify({ | |
| model: "llama-3.3-70b-versatile", | |
| messages: msgs, | |
| max_tokens: 1024, | |
| temperature: 0.4, | |
| }), | |
| // iOS-safe: AbortSignal.timeout non disponibile su iOS Safari < 16.4 | |
| signal: llmOpts?.signal ?? _makeTimedSignal(22_000), | |
| }); | |
| // 429 rate-limit: prova slot successivo se disponibile | |
| if (r.status === 429 && _i < _pool.length - 1) continue; | |
| const d = await r.json() as { choices?: Array<{ message?: { content?: string } }> }; | |
| return d.choices?.[0]?.message?.content ?? ""; | |
| } | |
| return ""; | |
| } finally { | |
| _release(); | |
| } | |
| }; | |
| const _ct = await debateToText(task, _debateLLM, { | |
| maxRounds: 2, | |
| roundTimeoutMs: 20_000, | |
| totalTimeoutMs: 40_000, | |
| signal: opts?.signal, | |
| }); | |
| if (_ct && !_ct.startsWith("❌")) { | |
| opts?.onStatus?.("Analisi completata ✓"); | |
| return _ct; | |
| } | |
| return null; | |
| } catch { | |
| // Non-blocking — debate fallisce silenziosamente, loop prosegue invariato | |
| return null; | |
| } | |
| } | |