Files
diegosouzapw--omniroute/tests/unit/compression/rtk-file-read-no-truncate-4559.test.ts
2026-07-13 13:39:12 +08:00

109 lines
4.5 KiB
TypeScript

/**
* TDD regression for #4559: RTK compression over-truncates tool-result content.
*
* A user reported that when a tool returns a file's contents (e.g. a ~147-line
* text/code file via a Read tool), RTK's default caps (maxLinesPerResult: 120,
* maxCharsPerResult: 12000) drop the middle and most lines are silently
* suppressed. Disabling compression OR the RTK "tool results" toggle fixes it;
* OpenRouter (no OmniRoute) is unaffected.
*
* Root cause: the final `smartTruncate` hard-cap in processRtkText fires for ANY
* tool result over 120 lines — including document/file reads — even though RTK's
* intent is repetitive *command output* (npm install logs, make, docker logs),
* NOT prose/code file reads.
*
* Fix: when the content is NOT a recognized repetitive command/log output
* (detection.type === "unknown" with no detected command — i.e. a document-style
* read), RTK must NOT apply the line/char truncation that drops the middle.
* Genuine repetitive logs (which detect as a known command type) are unaffected,
* preserving RTK's value there.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { processRtkText } from "../../../open-sse/services/compression/engines/rtk/index.ts";
// A ~147-line mixed prose/code file read — every line is distinct content, NOT
// repetitive log noise. No shell-command prefix, so RTK detects it as "unknown".
function buildFileRead(): string {
const lines: string[] = [];
lines.push("# Module: payment processor");
lines.push("");
lines.push("This module reconciles charges against the ledger.");
lines.push("");
lines.push("import { Decimal } from './decimal';");
lines.push("import { Ledger } from './ledger';");
lines.push("");
for (let i = 0; i < 70; i++) {
lines.push(`export function step${i}(input: Input${i}): Result${i} {`);
lines.push(` // reconcile bucket ${i} against the prior settlement window`);
lines.push(` const value = computeBucket${i}(input.amount, input.currency);`);
lines.push(` const settled = applyDiscount${i}(value, input.coupon);`);
lines.push(` return { id: ${i}, value: settled, settledAt: input.timestamp${i} };`);
lines.push("}");
lines.push("");
}
return lines.join("\n");
}
describe("RTK file-read tool result — #4559 no over-truncation", () => {
it("does not drop the middle of a 147-line document/file read", () => {
const fileRead = buildFileRead();
const totalLines = fileRead.split("\n").length;
assert.ok(totalLines > 120, `fixture should exceed default cap; got ${totalLines}`);
// Default RTK tool-result path (applyToToolResults defaults true; this is the
// text path the Anthropic/OpenAI tool_result blocks feed into).
const result = processRtkText(fileRead, {
config: { maxLinesPerResult: 120, maxCharsPerResult: 12000 },
});
// The hard-cap truncation must NOT have fired for a document/file read.
assert.ok(
!result.techniquesUsed.includes("rtk-truncate"),
`file-read content must not be hard-cap truncated; techniquesUsed: ${result.techniquesUsed.join(
", "
)}`
);
// The truncation marker must be absent and the middle content retained.
assert.ok(
!result.text.includes("[rtk:truncated"),
"no truncation marker should be injected into a file read"
);
// Sample lines from the head, middle, and tail must all survive.
assert.ok(result.text.includes("step0("), "head content survives");
assert.ok(result.text.includes("step35("), "middle content survives");
assert.ok(result.text.includes("step69("), "tail content survives");
// Output retains the vast majority of the lines (not dropped below a
// reasonable threshold).
const outLines = result.text.split("\n").length;
assert.ok(
outLines >= totalLines * 0.9,
`expected to retain >=90% of lines; kept ${outLines}/${totalLines}`
);
});
it("still truncates genuine repetitive command output (RTK value preserved)", () => {
// npm install-style repetitive log lines, with a detected command so RTK
// recognizes it as repetitive output worth truncating.
const noise = Array.from(
{ length: 300 },
(_, i) => `added package-${i}@1.0.0 to node_modules`
).join("\n");
const input = `$ npm install\n${noise}`;
const result = processRtkText(input, {
command: "npm install",
config: { maxLinesPerResult: 120, maxCharsPerResult: 12000 },
});
assert.ok(
result.compressed,
"repetitive npm install output should still be compressed"
);
});
});