From c80b24af414d7ad129dd250ee258e31cc1cc4d9e Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 18 Jul 2025 05:36:49 +0000 Subject: [PATCH] feat: deduplicate read_file history to optimize context length Adds deduplicateReadFileHistory() method to Task class to retain only the latest read_file result per file, optimizing context length and token usage. - Introduces deduplicateReadFileHistory() in Task class to keep only the latest read_file result for each file in apiConversationHistory - Invoked in attemptApiRequest() to clean up history before making a new API request - Iterates over apiConversationHistory to find and remove older read_file entries for the same file - Ensures only the most recent read_file entry is retained, reducing context length and token usage Co-authored-by: axb --- src/core/task/Task.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 53b8ef5b87..37ea859cf2 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1670,6 +1670,8 @@ export class Task extends EventEmitter { profileThresholds = {}, } = state ?? {} + this.deduplicateReadFileHistory() + // Get condensing configuration for automatic triggers const customCondensingPrompt = state?.customCondensingPrompt const condensingApiConfigId = state?.condensingApiConfigId @@ -1893,6 +1895,41 @@ export class Task extends EventEmitter { yield* iterator } + deduplicateReadFileHistory() { + for (let i = this.apiConversationHistory.length - 1; i >= 0; i--) { + const conversation = this.apiConversationHistory[i] + + if (conversation.role !== "user") continue + + const content = conversation.content + if (typeof content === "string") continue + + const firstItem = content[0] + if (typeof firstItem === "string" || !("type" in firstItem) || firstItem.type !== "text") continue + + const toolUseText = firstItem.text + if (!toolUseText || !toolUseText.startsWith("[read_file for ")) continue + + for (let j = i - 1; j >= 0; j--) { + const prevConversation = this.apiConversationHistory[j] + + if (prevConversation.role === "assistant") continue + + const prevContent = prevConversation.content + if (typeof prevContent === "string") continue + + const prevFirstItem = prevContent[0] + if (typeof prevFirstItem === "string" || !("type" in prevFirstItem) || prevFirstItem.type !== "text") + continue + + if (prevFirstItem.text === toolUseText && prevContent.length === 3) { + prevContent.splice(1, 1) + break + } + } + } + } + // Checkpoints public async checkpointSave(force: boolean = false) {