Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
40 lines
1.9 KiB
TypeScript
40 lines
1.9 KiB
TypeScript
import { compactToolHistory } from "./run-context-compaction";
|
|
import type { AgentTurnMessage, ToolDefinition } from "./provider-tool-turn";
|
|
|
|
/** Tokens kept free for the model's answer. */
|
|
export const OUTPUT_RESERVE_TOKENS = 16_000;
|
|
|
|
/** Share of the input budget the compiled skills may take. */
|
|
export const SKILL_BUDGET_SHARE = 0.4;
|
|
|
|
const CJK = /[\u3000-\u9fff\uac00-\ud7af\uf900-\ufaff\uff00-\uffef]/g;
|
|
|
|
/** Conservative: CJK counts one token per character, other text one per three. */
|
|
export function estimateTokens(text: string): number {
|
|
const cjk = text.match(CJK)?.length ?? 0;
|
|
return cjk + Math.ceil((text.length - cjk) / 3);
|
|
}
|
|
|
|
export function inputBudgetTokens(contextWindowTokens: number): number {
|
|
return contextWindowTokens - OUTPUT_RESERVE_TOKENS;
|
|
}
|
|
|
|
export function skillBudgetTokens(contextWindowTokens: number): number {
|
|
return Math.floor(inputBudgetTokens(contextWindowTokens) * SKILL_BUDGET_SHARE);
|
|
}
|
|
|
|
export /** Shrinks history to fit the window, or fails with the numbers. */
|
|
function fitContextBudget(instructions: string, messages: AgentTurnMessage[], tools: ToolDefinition[], budget: number, iteration: number): void {
|
|
const fixed = estimateTokens(instructions) + estimateTokens(JSON.stringify(tools));
|
|
let total = fixed + estimateTokens(JSON.stringify(messages));
|
|
if (total > budget) {
|
|
messages.splice(0, messages.length, ...compactToolHistory(messages, 1).messages);
|
|
const compacted = fixed + estimateTokens(JSON.stringify(messages));
|
|
console.warn(`[run-loop] iteration ${iteration}: ~${total} tokens over the ${budget} budget; aggressive compaction brought it to ~${compacted}`);
|
|
total = compacted;
|
|
}
|
|
if (total > budget) {
|
|
throw new Error(`This run needs ~${total} input tokens but the provider's context window leaves ${budget}. Raise the provider's context window in Settings > Advanced if the model supports more, or split the task.`);
|
|
}
|
|
}
|