- Deduplication: removes consecutive identical tool_call+tool_result pairs - Purge-errors: removes failed/empty tool results - Transform orchestrator runs strategies in sequence pre-payload - Wired into turn.ts before buildMessagesPayload - Clean-room reimplementation (AGPL reference: behavior only)
51 lines
1.5 KiB
TypeScript
51 lines
1.5 KiB
TypeScript
import type { DcpMessage } from '../messages.js';
|
|
|
|
export function deduplicate(messages: DcpMessage[]): { messages: DcpMessage[]; stats: { removedCount: number; freedTokens: number } } {
|
|
const result: DcpMessage[] = [];
|
|
let removedCount = 0;
|
|
let freedTokens = 0;
|
|
let i = 0;
|
|
|
|
while (i < messages.length) {
|
|
const current: DcpMessage = messages[i]!;
|
|
const next = messages[i + 1];
|
|
|
|
if (
|
|
current.role === 'assistant' &&
|
|
current.tool_calls &&
|
|
next &&
|
|
next.role === 'tool' &&
|
|
next.tool_call_id === current.tool_calls[0]?.id
|
|
) {
|
|
const nextNext = messages[i + 2];
|
|
const nextNextNext = messages[i + 3];
|
|
|
|
if (
|
|
nextNext &&
|
|
nextNext.role === 'assistant' &&
|
|
nextNext.tool_calls &&
|
|
nextNextNext &&
|
|
nextNextNext.role === 'tool' &&
|
|
nextNextNext.tool_call_id === nextNext.tool_calls[0]?.id &&
|
|
nextNext.tool_calls[0]?.name === current.tool_calls[0]?.name &&
|
|
nextNext.tool_calls[0]?.arguments === current.tool_calls[0]?.arguments &&
|
|
nextNextNext.content === next.content
|
|
) {
|
|
result.push(current, next);
|
|
i += 4;
|
|
removedCount += 2;
|
|
freedTokens += Math.ceil(nextNext.content.length / 4);
|
|
freedTokens += Math.ceil(current.content.length / 4);
|
|
} else {
|
|
result.push(current);
|
|
i++;
|
|
}
|
|
} else {
|
|
result.push(current);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return { messages: result, stats: { removedCount, freedTokens } };
|
|
}
|