Files
openmaic/OpenMAIC/lib/orchestration/summarizers/code-line-budget.ts
2026-08-16 14:58:47 +08:00

84 lines
3.0 KiB
TypeScript

// Shared, bounded rendering for whiteboard code-block lines.
//
// Both the request-start state summary (state-context.ts) and the per-round
// virtual whiteboard context (whiteboard-ledger.ts) can carry pre-existing code
// blocks whose line count and id length have no schema cap. Rendering every
// line (even ids-only) would let a code-heavy board grow the child prompt
// without bound. This module is the single source of truth for that cap so the
// two summarizers cannot drift into two different truncation rules.
//
// The budget has two CHARACTER tiers, shared across all code blocks handed the
// same budget object:
// - content: lines shown with (truncated) content, fully editable.
// - idList: remaining lines listed as bare ids (cheaper, still editable).
// Past both tiers, the tail is reported as an omitted count only — bounded and
// no longer individually addressable. Normal code (a few dozen short lines)
// fits entirely and stays fully editable.
export const MAX_LINE_CONTENT_CHARS = 80;
export const MAX_CODE_CONTENT_CHARS = 1200;
export const MAX_CODE_IDLIST_CHARS = 400;
export interface CodeRenderBudget {
content: number;
idList: number;
}
export interface CodeLine {
id: string;
content: string;
}
export function createCodeRenderBudget(): CodeRenderBudget {
return { content: MAX_CODE_CONTENT_CHARS, idList: MAX_CODE_IDLIST_CHARS };
}
export function truncateLineContent(content: string): string {
return content.length > MAX_LINE_CONTENT_CHARS
? `${content.slice(0, MAX_LINE_CONTENT_CHARS)}…`
: content;
}
/**
* Render code lines against a shared budget, returning the text and the
* remaining budget so the caller can thread it across multiple code blocks.
*/
export function renderCodeLines(
codeLines: CodeLine[],
budget: CodeRenderBudget,
indent = ' ',
): { text: string; budget: CodeRenderBudget } {
const out: string[] = [];
let { content, idList } = budget;
let i = 0;
// Tier 1: lines shown with (truncated) content, until the content char budget
// can no longer fit the next line.
for (; i < codeLines.length; i += 1) {
const rendered = `${indent}${codeLines[i].id}: ${truncateLineContent(codeLines[i].content)}`;
if (rendered.length > content) break;
out.push(rendered);
content -= rendered.length;
}
// Tier 2: remaining lines listed as bare ids (cheaper, still editable), until
// the id-list char budget can no longer fit the next id.
const idOnly: string[] = [];
for (; i < codeLines.length; i += 1) {
const piece = idOnly.length === 0 ? codeLines[i].id : `, ${codeLines[i].id}`;
if (piece.length > idList) break;
idOnly.push(codeLines[i].id);
idList -= piece.length;
}
if (idOnly.length > 0) {
out.push(`${indent}(ids only: ${idOnly.join(', ')})`);
}
// Anything still unrendered is reported as a count — bounded, not editable.
const omitted = codeLines.length - i;
if (omitted > 0) {
out.push(`${indent}(… ${omitted} more line(s) omitted)`);
}
return { text: out.join('\n'), budget: { content, idList } };
}