2026-07-17 22:10:34 +02:00

76 lines
2.2 KiB
TypeScript

export interface CompressionResult {
originalInput: string;
input: string;
applied: boolean;
method: string;
tokensBefore: number;
tokensAfter: number;
tokensSaved: number;
ratio: number;
strategy: string;
tags: string[];
notes: string[];
}
interface CompressionOptions {
enabled?: boolean;
mode?: string;
targetTokens?: number;
}
function estimateTokens(text: string): number {
return Math.max(1, Math.ceil(text.length / 4));
}
function noCompression(input: string, method = 'none:none'): CompressionResult {
const tokens = estimateTokens(input);
return {
originalInput: input,
input,
applied: false,
method,
tokensBefore: tokens,
tokensAfter: tokens,
tokensSaved: 0,
ratio: 0,
strategy: 'none',
tags: [],
notes: ['compression not applied'],
};
}
export function compressContext(input: string, options: CompressionOptions = {}): CompressionResult {
if (options.enabled === false || input.trim().length === 0) return noCompression(input);
const tokensBefore = estimateTokens(input);
const targetTokens = options.targetTokens ?? (tokensBefore > 12_000 ? 6_000 : 0);
if (!targetTokens || tokensBefore <= targetTokens) return noCompression(input);
const targetChars = Math.max(2_000, targetTokens * 4);
const headChars = Math.floor(targetChars * 0.62);
const tailChars = Math.max(800, targetChars - headChars);
const omittedChars = Math.max(0, input.length - headChars - tailChars);
const compacted = [
input.slice(0, headChars).trimEnd(),
'',
`[ctxlean omitted ${omittedChars} chars from the middle; preserve exact visible context]`,
'',
input.slice(-tailChars).trimStart(),
].join('\n');
const tokensAfter = estimateTokens(compacted);
return {
originalInput: input,
input: compacted,
applied: true,
method: `ctxlean:${options.mode ?? 'auto'}`,
tokensBefore,
tokensAfter,
tokensSaved: Math.max(0, tokensBefore - tokensAfter),
ratio: tokensBefore > 0 ? Math.max(0, 1 - tokensAfter / tokensBefore) : 0,
strategy: 'head-tail-excerpt',
tags: ['subscription-bridge-safe', 'lossy-excerpt'],
notes: ['long context compacted before model routing'],
};
}