AUDIT / src /lib /agentLoop /contextSizeGuard.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 3)
95eb75a verified
Raw
History Blame Contribute Delete
4.2 kB
/**
* contextSizeGuard.ts — S496: extracted from agentLoop.ts
*
* Guard per la dimensione del contesto API: se il contesto supera 60KB
* (≈15k token), prima taglia le sezioni opzionali del system prompt
* (project_map, skill_hint), poi scarda i messaggi meno preziosi.
*
* Priorità messaggi: REAL_DATA/OBSERVATION/EPISTEMIC > recenti > vecchi.
*
* GAP-4: soglia abbassata da 90KB a 60KB — margine proattivo più sicuro
* per evitare timeout e OOM su modelli con finestra effettiva < 128k token.
*
* @module contextSizeGuard
*/
import type { ApiMsg } from "./networkTools";
const CTX_MAX_BYTES = 60_000; // GAP-4: era 90_000 — abbassato per budget context proattivo
/**
* Riduce il contesto se supera la soglia, preservando i messaggi più preziosi.
* Restituisce il nuovo array di messaggi (potenzialmente ridotto).
*/
export function applyContextSizeGuard(
loopMessages: ApiMsg[],
onStatus?: (msg: string) => void,
): ApiMsg[] {
const _ctxSize = loopMessages.reduce((acc, m) => acc + String(m.content ?? "").length, 0);
if (_ctxSize <= CTX_MAX_BYTES) return loopMessages;
const _origCount = loopMessages.filter(m => m.role !== "system").length;
onStatus?.(`🗜️ Contesto troppo lungo (${Math.round(_ctxSize / 1024)}KB) — comprimo…`);
// Step 1 (RT-11): taglia sezioni opzionali del system prompt — preserva history
let msgs = loopMessages;
const _sysMsgObj = msgs.find(m => m.role === "system");
if (_sysMsgObj) {
const _sysRaw = String(_sysMsgObj.content ?? "");
const _sysTrimmed = _sysRaw
.replace(/\n?## project_map\b[\s\S]*?(?=\n## |\n---|\s*$)/, "")
.replace(/\n?## skill_hint\b[\s\S]*?(?=\n## |\n---|\s*$)/, "");
if (_sysTrimmed.length < _sysRaw.length) {
msgs = msgs.map(m => m.role === "system" ? { ...m, content: _sysTrimmed } : m);
}
}
// Step 1.5: trim optional bracket sections (ordered by expendability) before message-level trim
// U5: DOCS/PAGINE prime (rigenerabili), SAGGEZZA ACQUISITA per ultima (lezioni uniche)
// Ordine: DOCS INDICIZZATI → PAGINE GIÀ LETTE → MEMORIA RILEVANTE → SAGGEZZA ACQUISITA
const _OPTIONAL_CTX_SECTIONS: RegExp[] = [
/\n?\[DOCS INDICIZZATI\][\s\S]*?(?=\n\[|$)/,
/\n?\[PAGINE G\u00c0 LETTE\][\s\S]*?(?=\n\[|$)/,
/\n?\[MEMORIA RILEVANTE\][\s\S]*?(?=\n\[|$)/,
/\n?\[SAGGEZZA ACQUISITA[^\]]*\][\s\S]*?(?=\n\[|$)/,
];
for (const _re of _OPTIONAL_CTX_SECTIONS) {
const _curSize = msgs.reduce((acc, m) => acc + String(m.content ?? "").length, 0);
if (_curSize <= CTX_MAX_BYTES) break; // già sotto soglia
msgs = msgs.map(m =>
m.role === "system" ? { ...m, content: String(m.content ?? "").replace(_re, "") } : m,
);
}
// Step 2: se ancora sopra soglia, scarta i messaggi più vecchi (non-system)
const _ctxAfterTrim = msgs.reduce((acc, m) => acc + String(m.content ?? "").length, 0);
if (_ctxAfterTrim > CTX_MAX_BYTES) {
const _sysMsg = msgs.filter(m => m.role === "system");
let _rest = msgs.filter(m => m.role !== "system");
// Fix D (S380): taglia prima i messaggi non-preziosi — preserva REAL_DATA/OBSERVATION/EPISTEMIC
// N4: più segnali di contenuto "prezioso" da preservare nel trim
const _isValuable = (m: { content?: unknown }) =>
/REAL_DATA|OBSERVATION|RISULTATO|\[EPISTEMIC:|PROOF|CONFIRMED|SCRAPED|\[TOOL_RESULT|\[SEARCH_RESULT|SUCCESSO_TOOL/.test(String(m.content ?? ""));
const _keepCount = Math.ceil(_rest.length * 0.65);
const _valuable = _rest.filter(_isValuable);
const _nonVal = _rest.filter(m => !_isValuable(m));
const _nonValKeep = Math.max(0, _keepCount - _valuable.length);
const _keptSet = new Set<(typeof _rest)[number]>([..._nonVal.slice(_nonVal.length - _nonValKeep), ..._valuable]);
_rest = _rest.filter(m => _keptSet.has(m));
msgs = [..._sysMsg, ..._rest];
}
// GAP-R1: narrazione post-trim — informa su quanti messaggi sono stati eliminati
const _keptCount = msgs.filter(m => m.role !== "system").length;
if (_keptCount < _origCount) {
onStatus?.(`✓ Contesto compresso: ${_keptCount}/${_origCount} messaggi mantenuti (priorità: osservazioni + recenti)`);
}
return msgs;
}