Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fixes/15037-lite-current-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **fix(compression):** Lite no longer truncates the results of the latest tool calls, so OpenAI-format agents (Cursor IDE, Kilo/OpenCode) stop re-reading a file whose content was cut to 2000 characters every time ([#15037](https://github.com/diegosouzapw/OmniRoute/pull/15037)).
8 changes: 7 additions & 1 deletion open-sse/services/compression/lite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,14 @@ export function compressToolResults(
} {
if (!body.messages) return { body, applied: false };
const MAX_TOOL_LENGTH = resolveLiteMaxToolLength(options.maxToolLength);
// Results of the latest tool calls (after the last assistant message) are
// what the model asked for this turn. Cutting them makes it re-read the same
// file forever, since every re-read is cut again.
const lastAssistant = body.messages.findLastIndex((msg) => msg.role === "assistant");
const currentTurnStart = lastAssistant === -1 ? body.messages.length : lastAssistant + 1;
let applied = false;
const messages = body.messages.map((msg) => {
const messages = body.messages.map((msg, index) => {
if (index >= currentTurnStart) return msg;
if (msg.role !== "tool" || typeof msg.content !== "string") return msg;
if (msg.content.length <= MAX_TOOL_LENGTH) return msg;
applied = true;
Expand Down
87 changes: 87 additions & 0 deletions tests/unit/compression/lite-current-turn.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Lite must not truncate the results of the latest tool calls (the tool
* messages after the last assistant message). The model asked for that content
* this turn; cutting it to 2000 chars left it re-reading the same file forever,
* because every re-read was cut (or deduped against the cut copy) again.
* Run: node --import tsx/esm --test tests/unit/compression/lite-current-turn.test.ts
*/

import { describe, it } from "node:test";
import assert from "node:assert/strict";

import { compressToolResults } from "../../../open-sse/services/compression/lite.ts";
import { applyCompression } from "../../../open-sse/services/compression/strategySelector.ts";

const FILE = Array.from({ length: 400 }, (_, i) => `${i + 1}: def metric_${i}(x): return x`).join(
"\n"
);

type Msg = { role: string; content: string | null; [key: string]: unknown };

function readCall(id: string): Msg {
return {
role: "assistant",
content: null,
tool_calls: [
{ id, type: "function", function: { name: "read", arguments: '{"filePath":"/s.py"}' } },
],
};
}

describe("lite tool-result truncation and the current turn", () => {
it("keeps the newest tool result whole", () => {
const { body } = compressToolResults({
messages: [
{ role: "user", content: "read it" },
readCall("c1"),
{ role: "tool", tool_call_id: "c1", content: FILE },
],
});
assert.equal(body.messages![2].content, FILE);
});

it("still truncates tool results from earlier turns", () => {
const { body, applied } = compressToolResults({
messages: [
{ role: "user", content: "read it" },
readCall("c1"),
{ role: "tool", tool_call_id: "c1", content: FILE },
readCall("c2"),
{ role: "tool", tool_call_id: "c2", content: FILE },
],
});
assert.equal(applied, true);
assert.match(String(body.messages![2].content), /\.\.\.\[truncated\]$/);
assert.equal(body.messages![4].content, FILE);
});

it("keeps a re-read whole through the default session-dedup + lite pipeline", () => {
const result = applyCompression(
{
messages: [
{ role: "user", content: "read it" },
readCall("c1"),
{ role: "tool", tool_call_id: "c1", content: FILE },
readCall("c2"),
{ role: "tool", tool_call_id: "c2", content: FILE },
],
},
"stacked",
{
config: {
enabled: true,
defaultMode: "off",
autoTriggerTokens: 0,
cacheMinutes: 5,
preserveSystemPrompt: true,
comboOverrides: {},
engines: {},
activeComboId: null,
stackedPipeline: [{ engine: "session-dedup" }, { engine: "lite" }],
},
}
);
const messages = result.body.messages as Msg[];
assert.equal(messages[4].content, FILE);
});
});
Loading