mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-12 21:02:17 +02:00
feat(core): keep a head preview in elided tool results
A bare placeholder tells the model only the tool name and size; the first 400 characters tell it what the output actually was (a skill guide reads very differently from a fetched page), which is what it needs to judge whether re-running the tool is worth it. The preview is capped at the threshold so small thresholds still shrink content, and the transform stays a pure per-message function (byte-stable prefixes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4fbca2dbad
commit
04348dcc41
2 changed files with 23 additions and 1 deletions
|
|
@ -179,6 +179,18 @@ describe("elideHistoricToolResults", () => {
|
|||
expect(elided[0].content).toContain("101");
|
||||
});
|
||||
|
||||
it("keeps a head preview ahead of the placeholder", () => {
|
||||
const content = `SKILL GUIDE${"y".repeat(5000)}`;
|
||||
const [elided] = elideHistoricToolResults([toolMsg(content)], 2500);
|
||||
// Starts with the first 400 chars verbatim, then the marker.
|
||||
expect(elided.content.startsWith(content.slice(0, 400))).toBe(true);
|
||||
expect(elided.content).toContain("Rest of tool result elided");
|
||||
expect(elided.content.length).toBeLessThan(700);
|
||||
// Preview never exceeds a tiny threshold.
|
||||
const [tiny] = elideHistoricToolResults([toolMsg(content)], 50);
|
||||
expect(tiny.content.startsWith(`${content.slice(0, 50)}\n[Rest`)).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps tool results at or below the threshold verbatim", () => {
|
||||
const exact = toolMsg("x".repeat(100));
|
||||
expect(elideHistoricToolResults([exact], 100)).toEqual([exact]);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ export const DEFAULT_ELISION_POLICY: ElisionPolicy = {
|
|||
// placeholder saves nothing and a short note may carry useful context.
|
||||
const MIDDLE_PANE_CONTENT_FLOOR_CHARS = 500;
|
||||
|
||||
// Head of an elided tool result kept verbatim ahead of the placeholder.
|
||||
const TOOL_RESULT_PREVIEW_CHARS = 400;
|
||||
|
||||
const ContextConfig = z.object({
|
||||
elideHistoricToolResults: z.boolean().optional(),
|
||||
elideHistoricToolResultsThresholdChars: z.number().int().min(0).optional(),
|
||||
|
|
@ -102,9 +105,16 @@ export function elideHistoricToolResults(
|
|||
) {
|
||||
return message;
|
||||
}
|
||||
// Keep a head preview so the model knows what it is declining to
|
||||
// re-fetch (a skill body reads very differently from a web page).
|
||||
// Capped at the threshold so tiny thresholds still shrink content.
|
||||
const preview = message.content.slice(
|
||||
0,
|
||||
Math.min(TOOL_RESULT_PREVIEW_CHARS, thresholdChars),
|
||||
);
|
||||
return {
|
||||
...message,
|
||||
content: `[Tool result elided to save context: "${message.toolName}" returned ${message.content.length} characters in an earlier turn. Call the tool again if you need this output now.]`,
|
||||
content: `${preview}\n[Rest of tool result elided to save context: "${message.toolName}" returned ${message.content.length} characters in an earlier turn. Call the tool again if you need the full output now.]`,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue