mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-02 14:28:59 +02:00
[eric] router: small tool results age past the newest 30, which was a third of what a 735-call chat still carried
This commit is contained in:
@@ -24,6 +24,13 @@
|
||||
|
||||
const KEEP_RECENT = 5;
|
||||
const MIN_STUB_BYTES = 2000;
|
||||
// Small results are sacred while they are RECENT ("nothing to commit" IS the answer), but a
|
||||
// 735-tool chat carries hundreds of them and they became the floor: measured on the real thrashing
|
||||
// chat, small results were 32.0% (91,407 tokens) of everything the pruner left, more than any other
|
||||
// bucket. A status line from four hundred turns ago is stale state, not an answer. Newest
|
||||
// SMALL_KEEP_RECENT stay untouched; older ones collapse to a one-line stub that keeps nothing to
|
||||
// re-run because the tool_use above them already carries the command.
|
||||
const SMALL_KEEP_RECENT = 30;
|
||||
const ENGAGE_BYTES = 300000;
|
||||
// The CEILING, which is the property hermes has and an age-based cut does not. Clearing only OLD
|
||||
// results is a discount: measured on five real sessions it took 1.6M tokens to 422K, but the
|
||||
@@ -92,17 +99,29 @@ function pruneBody(bodyStr) {
|
||||
try { data = JSON.parse(bodyStr); } catch (_) { return none; }
|
||||
if (!data || !Array.isArray(data.messages)) return none;
|
||||
|
||||
// Collect every prunable tool_result in transcript order.
|
||||
// Collect every prunable tool_result in transcript order; small ones age on their own track.
|
||||
const candidates = [];
|
||||
const smalls = [];
|
||||
for (const msg of data.messages) {
|
||||
if (!msg || msg.role !== 'user' || !Array.isArray(msg.content)) continue;
|
||||
for (const block of msg.content) {
|
||||
if (!block || block.type !== 'tool_result') continue;
|
||||
const size = blockText(block).length;
|
||||
if (size >= MIN_STUB_BYTES || blockHasImage(block)) candidates.push(block);
|
||||
else if (size > 80) smalls.push(block);
|
||||
}
|
||||
}
|
||||
if (candidates.length <= KEEP_RECENT) return none;
|
||||
const smallKeepFrom = Math.max(0, smalls.length - SMALL_KEEP_RECENT);
|
||||
for (let i = 0; i < smallKeepFrom; i++) {
|
||||
smalls[i].content = [{ type: 'text', text: '[Old result cleared by OpenSwarm: '
|
||||
+ blockText(smalls[i]).length + ' characters from an earlier step.]' }];
|
||||
}
|
||||
if (candidates.length <= KEEP_RECENT && smallKeepFrom === 0) return none;
|
||||
if (candidates.length <= KEEP_RECENT) {
|
||||
let outSmall;
|
||||
try { outSmall = JSON.stringify(data); } catch (_) { return none; }
|
||||
return { body: outSmall, stats: { stubbed: smallKeepFrom, kept: KEEP_RECENT, savedBytes: bodyStr.length - outSmall.length } };
|
||||
}
|
||||
|
||||
const keepFrom = candidates.length - KEEP_RECENT;
|
||||
const seenNewestFirst = new Set();
|
||||
|
||||
@@ -210,3 +210,18 @@ test('a small input is never touched, and neither is a command', () => {
|
||||
assert.match(m.content[0].input.command, /^grep -rn/, 'a command is identity, never bulk');
|
||||
}
|
||||
});
|
||||
|
||||
test('old SMALL results age too, newest stay sacred (the 32% floor from the real thrashing chat)', () => {
|
||||
const msgs = [{ role: 'user', content: 'p'.repeat(ENGAGE_BYTES) }];
|
||||
for (let i = 0; i < 45; i++) {
|
||||
msgs.push({ role: 'user', content: [toolResult('s' + i, `ok ${i}: nothing to commit, working tree clean padded ${'x'.repeat(120)}`)] });
|
||||
}
|
||||
const s = JSON.stringify({ model: 'claude-sonnet-4-6', messages: msgs });
|
||||
const d = JSON.parse(pruneBody(s).body);
|
||||
const res = d.messages.slice(1).map((m) => m.content[0]);
|
||||
const cleared = res.filter((b) => /Old result cleared/.test(b.content[0].text)).length;
|
||||
assert.strictEqual(cleared, 15, '45 smalls, newest 30 kept, oldest 15 aged');
|
||||
for (let i = 45 - 30; i < 45; i++) {
|
||||
assert.match(res[i].content[0].text, /nothing to commit/, 'a recent small IS the answer and must survive');
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user