Files
2026-07-13 13:39:12 +08:00

73 lines
2.3 KiB
TypeScript

/**
* encoderComparison.ts — pure A/B aggregator for the studio.
* Scans the same arrays SmartCrusher would compact and reports JSON vs GCF vs TOON
* sizes (bytes + tokens). countTokens is injected by the caller (open-sse must not
* import the app-side tiktoken counter). Fail-open: if TOON is unavailable on any
* array, toonAvailable=false and TOON is not eligible as winner.
*/
import { encodeTabularBlock, wrapTabular } from "./tabular.ts";
import { encodeToonBlock, wrapToon } from "./toon.ts";
import { collectCompactableArrays } from "./smartcrusher.ts";
export interface EncoderSize {
bytes: number;
tokens: number;
}
export interface EncoderComparison {
arraysCompared: number;
json: EncoderSize;
gcf: EncoderSize;
toon: EncoderSize;
toonAvailable: boolean;
winner: "gcf" | "toon" | "json";
}
type MessageLike = { role?: string; content?: unknown };
const ZERO: EncoderSize = { bytes: 0, tokens: 0 };
function add(a: EncoderSize, text: string, countTokens: (t: string) => number): EncoderSize {
return { bytes: a.bytes + Buffer.byteLength(text, "utf8"), tokens: a.tokens + countTokens(text) };
}
function pickWinner(
json: EncoderSize,
gcf: EncoderSize,
toon: EncoderSize,
toonAvailable: boolean
): "gcf" | "toon" | "json" {
const candidates: Array<["gcf" | "toon" | "json", EncoderSize]> = [
["gcf", gcf],
["json", json],
];
if (toonAvailable) candidates.push(["toon", toon]);
candidates.sort((a, b) => a[1].tokens - b[1].tokens || a[1].bytes - b[1].bytes);
return candidates[0][0];
}
export function summarizeEncoderCandidates(
messages: MessageLike[],
minRows: number,
countTokens: (text: string) => number
): EncoderComparison {
const arrays = collectCompactableArrays(messages as never, minRows);
let json = { ...ZERO },
gcf = { ...ZERO },
toon = { ...ZERO };
let toonAvailable = arrays.length > 0;
for (const arr of arrays) {
json = add(json, JSON.stringify(arr), countTokens);
gcf = add(gcf, wrapTabular(encodeTabularBlock(arr)), countTokens);
const toonInner = encodeToonBlock(arr);
if (toonInner === null) toonAvailable = false;
else toon = add(toon, wrapToon(toonInner), countTokens);
}
return {
arraysCompared: arrays.length,
json,
gcf,
toon: toonAvailable ? toon : { ...ZERO },
toonAvailable,
winner: pickWinner(json, gcf, toon, toonAvailable),
};
}