mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 05:45:04 +03:00
* fix(compression): stop RTK over-truncating file-read tool results (#4559) * chore(quality): trim #4559 comment to keep rtk/index.ts within size cap --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
This commit is contained in:
committed by
GitHub
parent
d38bf09551
commit
bf6c8ca3fd
@@ -13,7 +13,7 @@ _In development — bullets added per PR; finalized at release._
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
- **fix(dashboard):** show custom provider given-name instead of internal id across dashboard pages — cache, combo health, compression analytics, cost overview, health/autopilot, provider stats, route explainability, provider utilization, runtime. Adds shared `resolveProviderName` resolver and `useProviderNodeMap` hook. (#4603)
|
||||
- **fix(sse):** fail over on 400 responses carrying rate-limit text — providers like MiMoCode signal throttling with a non-standard 400 whose body reads `"Detected high-frequency non-compliant requests from you."`. These are now classified as fallback-worthy (`RATE_LIMIT_EXCEEDED`, connection-cooldown scope) so combo routing fails over to another free target instead of surfacing `[502]: fetch failed`. Malformed-400 detection still wins, preserving the #2101 infinite-loop guard. (#4976)
|
||||
- **fix(compression):** stop RTK over-truncating file-read tool results — a tool returning a file's contents (e.g. a ~147-line code/prose file via a Read tool) is no longer head/tail-truncated by the generic-output fallback filter or the line/char hard-cap, which were silently dropping the middle. RTK now treats content with no detected command, an `unknown` type, and no error markers as a document read and skips those truncation paths; genuine repetitive command output (npm install, make, docker logs) is unaffected. (#4559)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -331,8 +331,21 @@ export function processRtkText(
|
||||
let result = text;
|
||||
|
||||
const detection = detectCommandType(text, options.command);
|
||||
// #4559: A document/file read (e.g. a Read tool returning a ~147-line code/prose
|
||||
// file) is NOT repetitive command output, but the generic-output *fallback* filter
|
||||
// and the final line/char hard-cap (designed for npm/make/docker logs) silently drop
|
||||
// its middle. Treat content as a document read when RTK recognized no command,
|
||||
// classified it "unknown", and it carries none of the generic error markers the
|
||||
// generic-output filter keys on — then skip the truncating fallbacks. Genuine logs
|
||||
// detect as a known command type (or carry a command / error markers), so RTK's
|
||||
// value on those is preserved.
|
||||
const hasGenericErrorMarkers = /Error:|Exception:|Traceback \(most recent call last\):/.test(
|
||||
text
|
||||
);
|
||||
const isDocumentLikeRead =
|
||||
detection.type === "unknown" && !detection.command && !hasGenericErrorMarkers;
|
||||
let matchedFilterPatterns: string[] = [];
|
||||
if (!options.skipFilters) {
|
||||
if (!options.skipFilters && !isDocumentLikeRead) {
|
||||
const filter = matchRtkFilter(text, detection.command, {
|
||||
customFiltersEnabled: config.customFiltersEnabled,
|
||||
trustProjectFilters: config.trustProjectFilters,
|
||||
@@ -403,13 +416,17 @@ export function processRtkText(
|
||||
return [];
|
||||
}
|
||||
});
|
||||
const truncated = smartTruncate(result, {
|
||||
maxLines: effectiveMaxLines(config.maxLinesPerResult, config.intensity),
|
||||
maxChars: config.maxCharsPerResult,
|
||||
preserveHead: config.intensity === "aggressive" ? 16 : 24,
|
||||
preserveTail: config.intensity === "aggressive" ? 16 : 24,
|
||||
priorityPatterns: [...defaultPriorityPatterns, ...filterPriorityPatterns],
|
||||
});
|
||||
// #4559: skip the generic line/char hard-cap for document/file reads (see
|
||||
// isDocumentLikeRead above) so the middle of a code/prose read is not dropped.
|
||||
const truncated = isDocumentLikeRead
|
||||
? { text: result, truncated: false, droppedLines: 0 }
|
||||
: smartTruncate(result, {
|
||||
maxLines: effectiveMaxLines(config.maxLinesPerResult, config.intensity),
|
||||
maxChars: config.maxCharsPerResult,
|
||||
preserveHead: config.intensity === "aggressive" ? 16 : 24,
|
||||
preserveTail: config.intensity === "aggressive" ? 16 : 24,
|
||||
priorityPatterns: [...defaultPriorityPatterns, ...filterPriorityPatterns],
|
||||
});
|
||||
if (truncated.truncated) {
|
||||
result = truncated.text;
|
||||
techniquesUsed.push("rtk-truncate");
|
||||
|
||||
108
tests/unit/compression/rtk-file-read-no-truncate-4559.test.ts
Normal file
108
tests/unit/compression/rtk-file-read-no-truncate-4559.test.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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"
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user