From f5ac5dd37d1391bdddf00a1eace8bcf208ce0cc1 Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 20:55:37 -0700 Subject: [PATCH 01/12] [Condense] Skip condense and show error if the context grows --- src/core/condense/index.ts | 11 +++++++++-- src/core/sliding-window/index.ts | 3 ++- src/core/task/Task.ts | 21 +++++++++++++++++---- src/i18n/locales/en/common.json | 4 +++- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index 1a20184371..2a7d959f60 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -1,4 +1,5 @@ import Anthropic from "@anthropic-ai/sdk" +import { t } from "../../i18n" import { ApiHandler } from "../../api" import { ApiMessage } from "../task-persistence/apiMessages" import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning" @@ -51,6 +52,7 @@ export type SummarizeResponse = { summary: string // The summary text; empty string for no summary cost: number // The cost of the summarization operation newContextTokens?: number // The number of tokens in the context for the next API request + error?: string // Populated iff the operation fails: error message shown to the user on failure (see Task.ts) } /** @@ -80,6 +82,7 @@ export async function summarizeConversation( apiHandler: ApiHandler, systemPrompt: string, taskId: string, + prevContextTokens: number, isAutomaticTrigger?: boolean, customCondensingPrompt?: string, condensingApiHandler?: ApiHandler, @@ -151,8 +154,8 @@ export async function summarizeConversation( } summary = summary.trim() if (summary.length === 0) { - console.warn("Received empty summary from API") - return { ...response, cost } + const error = t("common:errors.condense_failed") + return { ...response, cost, error } } const summaryMessage: ApiMessage = { role: "assistant", @@ -172,6 +175,10 @@ export async function summarizeConversation( typeof message.content === "string" ? [{ text: message.content, type: "text" as const }] : message.content, ) const newContextTokens = outputTokens + (await apiHandler.countTokens(contextBlocks)) + if (newContextTokens >= prevContextTokens) { + const error = t("common:errors.condense_context_grew") + return { ...response, cost, error } + } return { messages: newMessages, summary, cost, newContextTokens } } diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index b883c97407..d80ebdd73e 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -122,11 +122,12 @@ export async function truncateConversationIfNeeded({ apiHandler, systemPrompt, taskId, + prevContextTokens, true, // automatic trigger customCondensingPrompt, condensingApiHandler, ) - if (result.summary) { + if (!result.error) { return { ...result, prevContextTokens } } } diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 6223f0fdfc..33579ef676 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -509,26 +509,37 @@ export class Task extends EventEmitter { } } + const { contextTokens: prevContextTokens } = this.getTokenUsage() const { messages, summary, cost, newContextTokens = 0, + error, } = await summarizeConversation( this.apiConversationHistory, this.api, // Main API handler (fallback) systemPrompt, // Default summarization prompt (fallback) this.taskId, + prevContextTokens, false, // manual trigger customCondensingPrompt, // User's custom prompt condensingApiHandler, // Specific handler for condensing ) - if (!summary) { + if (error) { + this.say( + "error", + error, + undefined /* images */, + false /* partial */, + undefined /* checkpoint */, + undefined /* progressStatus */, + { isNonInteractive: true } /* options */, + ) return } await this.overwriteApiConversationHistory(messages) - const { contextTokens } = this.getTokenUsage() - const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens: contextTokens } + const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens } await this.say( "condense_context", undefined /* text */, @@ -1598,7 +1609,9 @@ export class Task extends EventEmitter { if (truncateResult.messages !== this.apiConversationHistory) { await this.overwriteApiConversationHistory(truncateResult.messages) } - if (truncateResult.summary) { + if (truncateResult.error) { + await this.say("error", truncateResult.error) + } else if (truncateResult.summary) { const { summary, cost, prevContextTokens, newContextTokens = 0 } = truncateResult const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens } await this.say( diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index b4f69c23ab..208c7fc880 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -53,7 +53,9 @@ "cannot_access_path": "Cannot access path {{path}}: {{error}}", "failed_update_project_mcp": "Failed to update project MCP servers", "settings_import_failed": "Settings import failed: {{error}}.", - "mistake_limit_guidance": "This may indicate a failure in the model's thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. \"Try breaking down the task into smaller steps\")." + "mistake_limit_guidance": "This may indicate a failure in the model's thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. \"Try breaking down the task into smaller steps\").", + "condense_failed": "Failed to condense context", + "condense_context_grew": "Context size increased during condensing; skipping this attempt" }, "warnings": { "no_terminal_content": "No terminal content selected", From 90a0a10ff6e32887738f425f4ab9862c230ec58d Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:15:22 -0700 Subject: [PATCH 02/12] update tests --- src/core/condense/__tests__/index.test.ts | 151 ++++++++++++++++-- src/core/condense/index.ts | 1 + .../__tests__/sliding-window.test.ts | 7 +- 3 files changed, 141 insertions(+), 18 deletions(-) diff --git a/src/core/condense/__tests__/index.test.ts b/src/core/condense/__tests__/index.test.ts index e1003dcdaf..839594cd4d 100644 --- a/src/core/condense/__tests__/index.test.ts +++ b/src/core/condense/__tests__/index.test.ts @@ -17,6 +17,7 @@ jest.mock("../../../services/telemetry/TelemetryService", () => ({ })) const taskId = "test-task-id" +const DEFAULT_PREV_CONTEXT_TOKENS = 1000 describe("getMessagesSinceLastSummary", () => { it("should return all messages when there is no summary", () => { @@ -115,11 +116,18 @@ describe("summarizeConversation", () => { { role: "assistant", content: "Hi there", ts: 2 }, ] - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) expect(result.messages).toEqual(messages) expect(result.cost).toBe(0) expect(result.summary).toBe("") expect(result.newContextTokens).toBeUndefined() + expect(result.error).toBeUndefined() expect(mockApiHandler.createMessage).not.toHaveBeenCalled() }) @@ -134,11 +142,18 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) expect(result.messages).toEqual(messages) expect(result.cost).toBe(0) expect(result.summary).toBe("") expect(result.newContextTokens).toBeUndefined() + expect(result.error).toBeUndefined() expect(mockApiHandler.createMessage).not.toHaveBeenCalled() }) @@ -153,7 +168,13 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) // Check that the API was called correctly expect(mockApiHandler.createMessage).toHaveBeenCalled() @@ -177,9 +198,10 @@ describe("summarizeConversation", () => { expect(result.cost).toBe(0.05) expect(result.summary).toBe("This is a summary") expect(result.newContextTokens).toBe(250) // 150 output tokens + 100 from countTokens + expect(result.error).toBeUndefined() }) - it("should handle empty summary response", async () => { + it("should handle empty summary response and return error", async () => { // We need enough messages to trigger summarization const messages: ApiMessage[] = [ { role: "user", content: "Hello", ts: 1 }, @@ -191,11 +213,6 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - // Mock console.warn before we call the function - const originalWarn = console.warn - const mockWarn = jest.fn() - console.warn = mockWarn - // Setup empty summary response with usage information const emptyStream = (async function* () { yield { type: "text" as const, text: "" } @@ -211,16 +228,20 @@ describe("summarizeConversation", () => { return messages.map(({ role, content }: { role: string; content: any }) => ({ role, content })) }) - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) // Should return original messages when summary is empty expect(result.messages).toEqual(messages) expect(result.cost).toBe(0.02) expect(result.summary).toBe("") - expect(mockWarn).toHaveBeenCalledWith("Received empty summary from API") - - // Restore console.warn - console.warn = originalWarn + expect(result.error).toBeDefined() + expect(result.newContextTokens).toBeUndefined() }) it("should correctly format the request to the API", async () => { @@ -234,7 +255,7 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId, DEFAULT_PREV_CONTEXT_TOKENS) // Verify the final request message const expectedFinalMessage = { @@ -275,7 +296,13 @@ describe("summarizeConversation", () => { // Override the mock for this test mockApiHandler.createMessage = jest.fn().mockReturnValue(streamWithUsage) as any - const result = await summarizeConversation(messages, mockApiHandler, systemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + systemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) // Verify that countTokens was called with the correct messages including system prompt expect(mockApiHandler.countTokens).toHaveBeenCalled() @@ -284,6 +311,89 @@ describe("summarizeConversation", () => { expect(result.newContextTokens).toBe(300) // 200 output tokens + 100 from countTokens expect(result.cost).toBe(0.06) expect(result.summary).toBe("This is a summary with system prompt") + expect(result.error).toBeUndefined() + }) + + it("should return error when new context tokens >= previous context tokens", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + // Create a stream that produces a summary + const streamWithLargeTokens = (async function* () { + yield { type: "text" as const, text: "This is a very long summary that uses many tokens" } + yield { type: "usage" as const, totalCost: 0.08, outputTokens: 500 } + })() + + // Override the mock for this test + mockApiHandler.createMessage = jest.fn().mockReturnValue(streamWithLargeTokens) as any + + // Mock countTokens to return a high value that when added to outputTokens (500) + // will be >= prevContextTokens (600) + mockApiHandler.countTokens = jest.fn().mockImplementation(() => Promise.resolve(200)) as any + + const prevContextTokens = 600 + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + prevContextTokens, + ) + + // Should return original messages when context would grow + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0.08) + expect(result.summary).toBe("") + expect(result.error).toBeDefined() + expect(result.newContextTokens).toBeUndefined() + }) + + it("should successfully summarize when new context tokens < previous context tokens", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + // Create a stream that produces a summary with reasonable token count + const streamWithSmallTokens = (async function* () { + yield { type: "text" as const, text: "Concise summary" } + yield { type: "usage" as const, totalCost: 0.03, outputTokens: 50 } + })() + + // Override the mock for this test + mockApiHandler.createMessage = jest.fn().mockReturnValue(streamWithSmallTokens) as any + + // Mock countTokens to return a small value so total is < prevContextTokens + mockApiHandler.countTokens = jest.fn().mockImplementation(() => Promise.resolve(30)) as any + + const prevContextTokens = 200 + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + prevContextTokens, + ) + + // Should successfully summarize + expect(result.messages.length).toBe(messages.length + 1) // Original + summary + expect(result.cost).toBe(0.03) + expect(result.summary).toBe("Concise summary") + expect(result.error).toBeUndefined() + expect(result.newContextTokens).toBe(80) // 50 output tokens + 30 from countTokens + expect(result.newContextTokens).toBeLessThan(prevContextTokens) }) }) @@ -373,6 +483,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, customPrompt, ) @@ -393,6 +504,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, " ", // Empty custom prompt ) @@ -409,6 +521,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, // No custom prompt ) @@ -428,6 +541,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, mockCondensingApiHandler, @@ -447,6 +561,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, undefined, @@ -477,6 +592,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, invalidHandler, @@ -503,6 +619,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, "Custom prompt", ) @@ -525,6 +642,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, mockCondensingApiHandler, @@ -548,6 +666,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, true, // isAutomaticTrigger "Custom prompt", mockCondensingApiHandler, diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index 2a7d959f60..05ea896d5f 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -72,6 +72,7 @@ export type SummarizeResponse = { * @param {ApiHandler} apiHandler - The API handler to use for token counting (fallback if condensingApiHandler not provided) * @param {string} systemPrompt - The system prompt for API requests (fallback if customCondensingPrompt not provided) * @param {string} taskId - The task ID for the conversation, used for telemetry + * @param {number} prevContextTokens - The number of tokens currently in the context, used to ensure we don't grow the context * @param {boolean} isAutomaticTrigger - Whether the summarization is triggered automatically * @param {string} customCondensingPrompt - Optional custom prompt to use for condensing * @param {ApiHandler} condensingApiHandler - Optional specific API handler to use for condensing diff --git a/src/core/sliding-window/__tests__/sliding-window.test.ts b/src/core/sliding-window/__tests__/sliding-window.test.ts index 74bbdf0caa..e99e2ed61f 100644 --- a/src/core/sliding-window/__tests__/sliding-window.test.ts +++ b/src/core/sliding-window/__tests__/sliding-window.test.ts @@ -532,6 +532,7 @@ describe("truncateConversationIfNeeded", () => { mockApiHandler, "System prompt", taskId, + 70001, true, undefined, // customCondensingPrompt undefined, // condensingApiHandler @@ -551,11 +552,12 @@ describe("truncateConversationIfNeeded", () => { }) it("should fall back to truncateConversation when autoCondenseContext is true but summarization fails", async () => { - // Mock the summarizeConversation function to return empty summary + // Mock the summarizeConversation function to return an error const mockSummarizeResponse: condenseModule.SummarizeResponse = { messages: messages, // Original messages unchanged - summary: "", // Empty summary indicates failure + summary: "", // Empty summary cost: 0.01, + error: "Summarization failed", // Error indicates failure } const summarizeSpy = jest @@ -678,6 +680,7 @@ describe("truncateConversationIfNeeded", () => { mockApiHandler, "System prompt", taskId, + 60000, true, undefined, // customCondensingPrompt undefined, // condensingApiHandler From 15b964da7d7dda5df4aea8bc50c18289b86477e9 Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:16:46 -0700 Subject: [PATCH 03/12] changeset --- .changeset/spotty-steaks-brake.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-steaks-brake.md diff --git a/.changeset/spotty-steaks-brake.md b/.changeset/spotty-steaks-brake.md new file mode 100644 index 0000000000..47fa9afc09 --- /dev/null +++ b/.changeset/spotty-steaks-brake.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Skips condense operations if the context size grows & shows an error From 58ac0cb08f20a658dc167f92f9796d79eaf66d5a Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:24:06 -0700 Subject: [PATCH 04/12] nit: error should be nonempty --- src/core/condense/__tests__/index.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/condense/__tests__/index.test.ts b/src/core/condense/__tests__/index.test.ts index 839594cd4d..b9dd883ad9 100644 --- a/src/core/condense/__tests__/index.test.ts +++ b/src/core/condense/__tests__/index.test.ts @@ -240,7 +240,7 @@ describe("summarizeConversation", () => { expect(result.messages).toEqual(messages) expect(result.cost).toBe(0.02) expect(result.summary).toBe("") - expect(result.error).toBeDefined() + expect(result.error).toBeTruthy() // Error should be set expect(result.newContextTokens).toBeUndefined() }) @@ -351,7 +351,7 @@ describe("summarizeConversation", () => { expect(result.messages).toEqual(messages) expect(result.cost).toBe(0.08) expect(result.summary).toBe("") - expect(result.error).toBeDefined() + expect(result.error).toBeTruthy() // Error should be set expect(result.newContextTokens).toBeUndefined() }) From 32f6624aca249d2fa4bd8e922c498fab9f2edb27 Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:26:48 -0700 Subject: [PATCH 05/12] update translations --- src/i18n/locales/ca/common.json | 4 +++- src/i18n/locales/de/common.json | 4 +++- src/i18n/locales/es/common.json | 4 +++- src/i18n/locales/fr/common.json | 4 +++- src/i18n/locales/hi/common.json | 4 +++- src/i18n/locales/it/common.json | 4 +++- src/i18n/locales/ja/common.json | 4 +++- src/i18n/locales/ko/common.json | 4 +++- src/i18n/locales/nl/common.json | 4 +++- src/i18n/locales/pl/common.json | 4 +++- src/i18n/locales/pt-BR/common.json | 4 +++- src/i18n/locales/ru/common.json | 4 +++- src/i18n/locales/tr/common.json | 4 +++- src/i18n/locales/vi/common.json | 4 +++- src/i18n/locales/zh-CN/common.json | 4 +++- src/i18n/locales/zh-TW/common.json | 4 +++- 16 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 739972f996..d5777095b7 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -57,7 +57,9 @@ "custom_storage_path_unusable": "La ruta d'emmagatzematge personalitzada \"{{path}}\" no és utilitzable, s'utilitzarà la ruta predeterminada", "cannot_access_path": "No es pot accedir a la ruta {{path}}: {{error}}", "settings_import_failed": "Ha fallat la importació de la configuració: {{error}}.", - "mistake_limit_guidance": "Això pot indicar un error en el procés de pensament del model o la incapacitat d'utilitzar una eina correctament, que es pot mitigar amb orientació de l'usuari (p. ex. \"Prova de dividir la tasca en passos més petits\")." + "mistake_limit_guidance": "Això pot indicar un error en el procés de pensament del model o la incapacitat d'utilitzar una eina correctament, que es pot mitigar amb orientació de l'usuari (p. ex. \"Prova de dividir la tasca en passos més petits\").", + "condense_failed": "Ha fallat la condensació del context", + "condense_context_grew": "La mida del context ha augmentat durant la condensació; s'omet aquest intent" }, "warnings": { "no_terminal_content": "No s'ha seleccionat contingut de terminal", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index ec88125bdd..d09be1a5a3 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "Benutzerdefinierter Speicherpfad \"{{path}}\" ist nicht verwendbar, Standardpfad wird verwendet", "cannot_access_path": "Zugriff auf Pfad {{path}} nicht möglich: {{error}}", "settings_import_failed": "Fehler beim Importieren der Einstellungen: {{error}}.", - "mistake_limit_guidance": "Dies kann auf einen Fehler im Denkprozess des Modells oder die Unfähigkeit hinweisen, ein Tool richtig zu verwenden, was durch Benutzerführung behoben werden kann (z.B. \"Versuche, die Aufgabe in kleinere Schritte zu unterteilen\")." + "mistake_limit_guidance": "Dies kann auf einen Fehler im Denkprozess des Modells oder die Unfähigkeit hinweisen, ein Tool richtig zu verwenden, was durch Benutzerführung behoben werden kann (z.B. \"Versuche, die Aufgabe in kleinere Schritte zu unterteilen\").", + "condense_failed": "Fehler beim Verdichten des Kontexts", + "condense_context_grew": "Kontextgröße ist während der Verdichtung gewachsen; dieser Versuch wird übersprungen" }, "warnings": { "no_terminal_content": "Kein Terminal-Inhalt ausgewählt", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 47c97b717b..5df62bdb3d 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "La ruta de almacenamiento personalizada \"{{path}}\" no es utilizable, se usará la ruta predeterminada", "cannot_access_path": "No se puede acceder a la ruta {{path}}: {{error}}", "settings_import_failed": "Error al importar la configuración: {{error}}.", - "mistake_limit_guidance": "Esto puede indicar un fallo en el proceso de pensamiento del modelo o la incapacidad de usar una herramienta correctamente, lo cual puede mitigarse con orientación del usuario (ej. \"Intenta dividir la tarea en pasos más pequeños\")." + "mistake_limit_guidance": "Esto puede indicar un fallo en el proceso de pensamiento del modelo o la incapacidad de usar una herramienta correctamente, lo cual puede mitigarse con orientación del usuario (ej. \"Intenta dividir la tarea en pasos más pequeños\").", + "condense_failed": "Error al condensar el contexto", + "condense_context_grew": "El tamaño del contexto aumentó durante la condensación; se omite este intento" }, "warnings": { "no_terminal_content": "No hay contenido de terminal seleccionado", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index 23b3470a0c..473977a1e8 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "Le chemin de stockage personnalisé \"{{path}}\" est inutilisable, le chemin par défaut sera utilisé", "cannot_access_path": "Impossible d'accéder au chemin {{path}} : {{error}}", "settings_import_failed": "Échec de l'importation des paramètres : {{error}}", - "mistake_limit_guidance": "Cela peut indiquer un échec dans le processus de réflexion du modèle ou une incapacité à utiliser un outil correctement, ce qui peut être atténué avec des conseils de l'utilisateur (par ex. \"Essaie de diviser la tâche en étapes plus petites\")." + "mistake_limit_guidance": "Cela peut indiquer un échec dans le processus de réflexion du modèle ou une incapacité à utiliser un outil correctement, ce qui peut être atténué avec des conseils de l'utilisateur (par ex. \"Essaie de diviser la tâche en étapes plus petites\").", + "condense_failed": "Échec de la condensation du contexte", + "condense_context_grew": "La taille du contexte a augmenté pendant la condensation ; cette tentative est ignorée" }, "warnings": { "no_terminal_content": "Aucun contenu de terminal sélectionné", diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 9f52fa4714..817db05e93 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "कस्टम स्टोरेज पाथ \"{{path}}\" उपयोग योग्य नहीं है, डिफ़ॉल्ट पाथ का उपयोग किया जाएगा", "cannot_access_path": "पाथ {{path}} तक पहुंच नहीं पा रहे हैं: {{error}}", "settings_import_failed": "सेटिंग्स इम्पोर्ट करने में विफल: {{error}}।", - "mistake_limit_guidance": "यह मॉडल की सोच प्रक्रिया में विफलता या किसी टूल का सही उपयोग न कर पाने का संकेत हो सकता है, जिसे उपयोगकर्ता के मार्गदर्शन से ठीक किया जा सकता है (जैसे \"कार्य को छोटे चरणों में बांटने की कोशिश करें\")।" + "mistake_limit_guidance": "यह मॉडल की सोच प्रक्रिया में विफलता या किसी टूल का सही उपयोग न कर पाने का संकेत हो सकता है, जिसे उपयोगकर्ता के मार्गदर्शन से ठीक किया जा सकता है (जैसे \"कार्य को छोटे चरणों में बांटने की कोशिश करें\")।", + "condense_failed": "संदर्भ को संक्षिप्त करने में विफल", + "condense_context_grew": "संक्षिप्तीकरण के दौरान संदर्भ का आकार बढ़ गया; इस प्रयास को छोड़ा जा रहा है" }, "warnings": { "no_terminal_content": "कोई टर्मिनल सामग्री चयनित नहीं", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index c4da3cbfaa..ca80ab915c 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "Il percorso di archiviazione personalizzato \"{{path}}\" non è utilizzabile, verrà utilizzato il percorso predefinito", "cannot_access_path": "Impossibile accedere al percorso {{path}}: {{error}}", "settings_import_failed": "Importazione delle impostazioni fallita: {{error}}.", - "mistake_limit_guidance": "Questo può indicare un fallimento nel processo di pensiero del modello o l'incapacità di utilizzare correttamente uno strumento, che può essere mitigato con la guida dell'utente (ad es. \"Prova a suddividere l'attività in passaggi più piccoli\")." + "mistake_limit_guidance": "Questo può indicare un fallimento nel processo di pensiero del modello o l'incapacità di utilizzare correttamente uno strumento, che può essere mitigato con la guida dell'utente (ad es. \"Prova a suddividere l'attività in passaggi più piccoli\").", + "condense_failed": "Impossibile condensare il contesto", + "condense_context_grew": "La dimensione del contesto è aumentata durante la condensazione; questo tentativo viene saltato" }, "warnings": { "no_terminal_content": "Nessun contenuto del terminale selezionato", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 30c2b754f4..691f06c398 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "カスタムストレージパス \"{{path}}\" が使用できないため、デフォルトパスを使用します", "cannot_access_path": "パス {{path}} にアクセスできません:{{error}}", "settings_import_failed": "設定のインポートに失敗しました:{{error}}", - "mistake_limit_guidance": "これは、モデルの思考プロセスの失敗やツールを適切に使用できないことを示している可能性があり、ユーザーのガイダンスによって軽減できます(例:「タスクをより小さなステップに分割してみてください」)。" + "mistake_limit_guidance": "これは、モデルの思考プロセスの失敗やツールを適切に使用できないことを示している可能性があり、ユーザーのガイダンスによって軽減できます(例:「タスクをより小さなステップに分割してみてください」)。", + "condense_failed": "コンテキストの圧縮に失敗しました", + "condense_context_grew": "圧縮中にコンテキストサイズが増加しました;この試行をスキップします" }, "warnings": { "no_terminal_content": "選択されたターミナルコンテンツがありません", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index f90637a196..a416b5bc1d 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "사용자 지정 저장 경로 \"{{path}}\"를 사용할 수 없어 기본 경로를 사용합니다", "cannot_access_path": "경로 {{path}}에 접근할 수 없습니다: {{error}}", "settings_import_failed": "설정 가져오기 실패: {{error}}.", - "mistake_limit_guidance": "이는 모델의 사고 과정 실패나 도구를 제대로 사용하지 못하는 것을 나타낼 수 있으며, 사용자 가이드를 통해 완화할 수 있습니다 (예: \"작업을 더 작은 단계로 나누어 시도해보세요\")." + "mistake_limit_guidance": "이는 모델의 사고 과정 실패나 도구를 제대로 사용하지 못하는 것을 나타낼 수 있으며, 사용자 가이드를 통해 완화할 수 있습니다 (예: \"작업을 더 작은 단계로 나누어 시도해보세요\").", + "condense_failed": "컨텍스트 압축에 실패했습니다", + "condense_context_grew": "압축 중 컨텍스트 크기가 증가했습니다; 이 시도를 건너뜁니다" }, "warnings": { "no_terminal_content": "선택된 터미널 내용이 없습니다", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index 395e0bfe75..e7590a1c5e 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -53,7 +53,9 @@ "cannot_access_path": "Kan pad {{path}} niet openen: {{error}}", "failed_update_project_mcp": "Bijwerken van project MCP-servers mislukt", "settings_import_failed": "Importeren van instellingen mislukt: {{error}}.", - "mistake_limit_guidance": "Dit kan duiden op een fout in het denkproces van het model of het onvermogen om een tool correct te gebruiken, wat kan worden verminderd met gebruikersbegeleiding (bijv. \"Probeer de taak op te delen in kleinere stappen\")." + "mistake_limit_guidance": "Dit kan duiden op een fout in het denkproces van het model of het onvermogen om een tool correct te gebruiken, wat kan worden verminderd met gebruikersbegeleiding (bijv. \"Probeer de taak op te delen in kleinere stappen\").", + "condense_failed": "Comprimeren van context mislukt", + "condense_context_grew": "Contextgrootte nam toe tijdens comprimeren; deze poging wordt overgeslagen" }, "warnings": { "no_terminal_content": "Geen terminalinhoud geselecteerd", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index f17cf06894..0f0b83c71f 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "Niestandardowa ścieżka przechowywania \"{{path}}\" nie jest użyteczna, zostanie użyta domyślna ścieżka", "cannot_access_path": "Nie można uzyskać dostępu do ścieżki {{path}}: {{error}}", "settings_import_failed": "Nie udało się zaimportować ustawień: {{error}}.", - "mistake_limit_guidance": "To może wskazywać na błąd w procesie myślowym modelu lub niezdolność do prawidłowego użycia narzędzia, co można złagodzić poprzez wskazówki użytkownika (np. \"Spróbuj podzielić zadanie na mniejsze kroki\")." + "mistake_limit_guidance": "To może wskazywać na błąd w procesie myślowym modelu lub niezdolność do prawidłowego użycia narzędzia, co można złagodzić poprzez wskazówki użytkownika (np. \"Spróbuj podzielić zadanie na mniejsze kroki\").", + "condense_failed": "Nie udało się skondensować kontekstu", + "condense_context_grew": "Rozmiar kontekstu wzrósł podczas kondensacji; pomijanie tej próby" }, "warnings": { "no_terminal_content": "Nie wybrano zawartości terminala", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index e0b71c0b0e..cc82604f32 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -57,7 +57,9 @@ "custom_storage_path_unusable": "O caminho de armazenamento personalizado \"{{path}}\" não pode ser usado, será usado o caminho padrão", "cannot_access_path": "Não é possível acessar o caminho {{path}}: {{error}}", "settings_import_failed": "Falha ao importar configurações: {{error}}", - "mistake_limit_guidance": "Isso pode indicar uma falha no processo de pensamento do modelo ou incapacidade de usar uma ferramenta adequadamente, o que pode ser mitigado com orientação do usuário (ex. \"Tente dividir a tarefa em etapas menores\")." + "mistake_limit_guidance": "Isso pode indicar uma falha no processo de pensamento do modelo ou incapacidade de usar uma ferramenta adequadamente, o que pode ser mitigado com orientação do usuário (ex. \"Tente dividir a tarefa em etapas menores\").", + "condense_failed": "Falha ao condensar o contexto", + "condense_context_grew": "O tamanho do contexto aumentou durante a condensação; pulando esta tentativa" }, "warnings": { "no_terminal_content": "Nenhum conteúdo do terminal selecionado", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index ba557059a8..3683eaed71 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -53,7 +53,9 @@ "cannot_access_path": "Невозможно получить доступ к пути {{path}}: {{error}}", "failed_update_project_mcp": "Не удалось обновить серверы проекта MCP", "settings_import_failed": "Не удалось импортировать настройки: {{error}}.", - "mistake_limit_guidance": "Это может указывать на сбой в процессе мышления модели или неспособность правильно использовать инструмент, что можно смягчить с помощью руководства пользователя (например, \"Попробуйте разбить задачу на более мелкие шаги\")." + "mistake_limit_guidance": "Это может указывать на сбой в процессе мышления модели или неспособность правильно использовать инструмент, что можно смягчить с помощью руководства пользователя (например, \"Попробуйте разбить задачу на более мелкие шаги\").", + "condense_failed": "Не удалось сжать контекст", + "condense_context_grew": "Размер контекста увеличился во время сжатия; пропускаем эту попытку" }, "warnings": { "no_terminal_content": "Не выбрано содержимое терминала", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 77af2295f7..d4e5033c5e 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "Özel depolama yolu \"{{path}}\" kullanılamıyor, varsayılan yol kullanılacak", "cannot_access_path": "{{path}} yoluna erişilemiyor: {{error}}", "settings_import_failed": "Ayarlar içe aktarılamadı: {{error}}.", - "mistake_limit_guidance": "Bu, modelin düşünce sürecindeki bir başarısızlığı veya bir aracı düzgün kullanamama durumunu gösterebilir, bu da kullanıcı rehberliği ile hafifletilebilir (örn. \"Görevi daha küçük adımlara bölmeyi deneyin\")." + "mistake_limit_guidance": "Bu, modelin düşünce sürecindeki bir başarısızlığı veya bir aracı düzgün kullanamama durumunu gösterebilir, bu da kullanıcı rehberliği ile hafifletilebilir (örn. \"Görevi daha küçük adımlara bölmeyi deneyin\").", + "condense_failed": "Bağlam sıkıştırılamadı", + "condense_context_grew": "Sıkıştırma sırasında bağlam boyutu arttı; bu deneme atlanıyor" }, "warnings": { "no_terminal_content": "Seçili terminal içeriği yok", diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index 3919eb607e..4848daae47 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "Đường dẫn lưu trữ tùy chỉnh \"{{path}}\" không thể sử dụng được, sẽ sử dụng đường dẫn mặc định", "cannot_access_path": "Không thể truy cập đường dẫn {{path}}: {{error}}", "settings_import_failed": "Nhập cài đặt thất bại: {{error}}.", - "mistake_limit_guidance": "Điều này có thể cho thấy sự thất bại trong quá trình suy nghĩ của mô hình hoặc không thể sử dụng công cụ đúng cách, có thể được giảm thiểu bằng hướng dẫn của người dùng (ví dụ: \"Hãy thử chia nhỏ nhiệm vụ thành các bước nhỏ hơn\")." + "mistake_limit_guidance": "Điều này có thể cho thấy sự thất bại trong quá trình suy nghĩ của mô hình hoặc không thể sử dụng công cụ đúng cách, có thể được giảm thiểu bằng hướng dẫn của người dùng (ví dụ: \"Hãy thử chia nhỏ nhiệm vụ thành các bước nhỏ hơn\").", + "condense_failed": "Không thể nén ngữ cảnh", + "condense_context_grew": "Kích thước ngữ cảnh tăng lên trong quá trình nén; bỏ qua lần thử này" }, "warnings": { "no_terminal_content": "Không có nội dung terminal được chọn", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index e169355628..b93484cd85 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "自定义存储路径 \"{{path}}\" 不可用,将使用默认路径", "cannot_access_path": "无法访问路径 {{path}}:{{error}}", "settings_import_failed": "设置导入失败:{{error}}。", - "mistake_limit_guidance": "这可能表明模型思维过程失败或无法正确使用工具,可通过用户指导来缓解(例如\"尝试将任务分解为更小的步骤\")。" + "mistake_limit_guidance": "这可能表明模型思维过程失败或无法正确使用工具,可通过用户指导来缓解(例如\"尝试将任务分解为更小的步骤\")。", + "condense_failed": "压缩上下文失败", + "condense_context_grew": "压缩过程中上下文大小增加;跳过此次尝试" }, "warnings": { "no_terminal_content": "没有选择终端内容", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 2331f767d1..49b141b09a 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -53,7 +53,9 @@ "custom_storage_path_unusable": "自訂儲存路徑 \"{{path}}\" 無法使用,將使用預設路徑", "cannot_access_path": "無法存取路徑 {{path}}:{{error}}", "settings_import_failed": "設定匯入失敗:{{error}}。", - "mistake_limit_guidance": "這可能表明模型思維過程失敗或無法正確使用工具,可透過使用者指導來緩解(例如「嘗試將工作分解為更小的步驟」)。" + "mistake_limit_guidance": "這可能表明模型思維過程失敗或無法正確使用工具,可透過使用者指導來緩解(例如「嘗試將工作分解為更小的步驟」)。", + "condense_failed": "壓縮上下文失敗", + "condense_context_grew": "壓縮過程中上下文大小增加;跳過此次嘗試" }, "warnings": { "no_terminal_content": "沒有選擇終端機內容", From 28946f695c90acdebe410c12cff30230817fef2d Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:41:52 -0700 Subject: [PATCH 06/12] add more errors --- src/core/condense/index.ts | 14 ++++++-------- src/i18n/locales/en/common.json | 3 +++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index 05ea896d5f..ecd8f0ed39 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -97,13 +97,15 @@ export async function summarizeConversation( const response: SummarizeResponse = { messages, cost: 0, summary: "" } const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP)) if (messagesToSummarize.length <= 1) { - return response // Not enough messages to warrant a summary + const error = t("common:errors.condense_not_enough_messages") + return { ...response, error } } const keepMessages = messages.slice(-N_MESSAGES_TO_KEEP) // Check if there's a recent summary in the messages we're keeping const recentSummaryExists = keepMessages.some((message) => message.isSummary) if (recentSummaryExists) { - return response // We recently summarized these messages; it's too soon to summarize again. + const error = t("common:errors.condensed_recently") + return { ...response, error } } const finalRequestMessage: Anthropic.MessageParam = { role: "user", @@ -131,12 +133,8 @@ export async function summarizeConversation( // Consider throwing an error or returning a specific error response. console.error("Main API handler is also invalid for condensing. Cannot proceed.") // Return an appropriate error structure for SummarizeResponse - return { - messages, - summary: "", - cost: 0, - newContextTokens: 0, - } + const error = t("common:errors.condense_handler_invalid") + return { ...response, error } } } diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 208c7fc880..de8ea1e11c 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Settings import failed: {{error}}.", "mistake_limit_guidance": "This may indicate a failure in the model's thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. \"Try breaking down the task into smaller steps\").", "condense_failed": "Failed to condense context", + "condense_not_enough_messages": "Not enough messages to condense context", + "condensed_recently": "Context was condensed recently; skipping this attempt", + "condense_handler_invalid": "API handler for condensing context is invalid", "condense_context_grew": "Context size increased during condensing; skipping this attempt" }, "warnings": { From 4f0bc0fe52560884b23e8c7e7fd57b460d15368a Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:44:58 -0700 Subject: [PATCH 07/12] add more test cases --- src/core/condense/__tests__/index.test.ts | 108 +++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/src/core/condense/__tests__/index.test.ts b/src/core/condense/__tests__/index.test.ts index b9dd883ad9..e3b613f903 100644 --- a/src/core/condense/__tests__/index.test.ts +++ b/src/core/condense/__tests__/index.test.ts @@ -127,7 +127,7 @@ describe("summarizeConversation", () => { expect(result.cost).toBe(0) expect(result.summary).toBe("") expect(result.newContextTokens).toBeUndefined() - expect(result.error).toBeUndefined() + expect(result.error).toBeTruthy() // Error should be set for not enough messages expect(mockApiHandler.createMessage).not.toHaveBeenCalled() }) @@ -153,7 +153,7 @@ describe("summarizeConversation", () => { expect(result.cost).toBe(0) expect(result.summary).toBe("") expect(result.newContextTokens).toBeUndefined() - expect(result.error).toBeUndefined() + expect(result.error).toBeTruthy() // Error should be set for recent summary expect(mockApiHandler.createMessage).not.toHaveBeenCalled() }) @@ -395,6 +395,110 @@ describe("summarizeConversation", () => { expect(result.newContextTokens).toBe(80) // 50 output tokens + 30 from countTokens expect(result.newContextTokens).toBeLessThan(prevContextTokens) }) + + it("should return error when not enough messages to summarize", async () => { + const messages: ApiMessage[] = [{ role: "user", content: "Hello", ts: 1 }] + + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) + + // Should return original messages when not enough to summarize + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0) + expect(result.summary).toBe("") + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() + expect(mockApiHandler.createMessage).not.toHaveBeenCalled() + }) + + it("should return error when recent summary exists in kept messages", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Recent summary", ts: 6, isSummary: true }, // Summary in last 3 messages + { role: "user", content: "Tell me more", ts: 7 }, + ] + + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) + + // Should return original messages when recent summary exists + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0) + expect(result.summary).toBe("") + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() + expect(mockApiHandler.createMessage).not.toHaveBeenCalled() + }) + + it("should return error when both condensing and main API handlers are invalid", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + // Create invalid handlers (missing createMessage) + const invalidMainHandler = { + countTokens: jest.fn(), + getModel: jest.fn(), + // createMessage is missing + } as unknown as ApiHandler + + const invalidCondensingHandler = { + countTokens: jest.fn(), + getModel: jest.fn(), + // createMessage is missing + } as unknown as ApiHandler + + // Mock console.error to verify error message + const originalError = console.error + const mockError = jest.fn() + console.error = mockError + + const result = await summarizeConversation( + messages, + invalidMainHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + false, + undefined, + invalidCondensingHandler, + ) + + // Should return original messages when both handlers are invalid + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0) + expect(result.summary).toBe("") + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() + + // Verify error was logged + expect(mockError).toHaveBeenCalledWith( + expect.stringContaining("Main API handler is also invalid for condensing"), + ) + + // Restore console.error + console.error = originalError + }) }) describe("summarizeConversation with custom settings", () => { From 890f53ffc318afbefc955c16639f9f73229aaf16 Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:50:11 -0700 Subject: [PATCH 08/12] update translations --- src/i18n/locales/ca/common.json | 3 +++ src/i18n/locales/de/common.json | 3 +++ src/i18n/locales/es/common.json | 3 +++ src/i18n/locales/fr/common.json | 3 +++ src/i18n/locales/hi/common.json | 3 +++ src/i18n/locales/it/common.json | 3 +++ src/i18n/locales/ja/common.json | 3 +++ src/i18n/locales/ko/common.json | 3 +++ src/i18n/locales/nl/common.json | 3 +++ src/i18n/locales/pl/common.json | 3 +++ src/i18n/locales/pt-BR/common.json | 3 +++ src/i18n/locales/ru/common.json | 3 +++ src/i18n/locales/tr/common.json | 3 +++ src/i18n/locales/vi/common.json | 3 +++ src/i18n/locales/zh-CN/common.json | 3 +++ src/i18n/locales/zh-TW/common.json | 3 +++ 16 files changed, 48 insertions(+) diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index d5777095b7..195fe2dac8 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -59,6 +59,9 @@ "settings_import_failed": "Ha fallat la importació de la configuració: {{error}}.", "mistake_limit_guidance": "Això pot indicar un error en el procés de pensament del model o la incapacitat d'utilitzar una eina correctament, que es pot mitigar amb orientació de l'usuari (p. ex. \"Prova de dividir la tasca en passos més petits\").", "condense_failed": "Ha fallat la condensació del context", + "condense_not_enough_messages": "No hi ha prou missatges per condensar el context", + "condensed_recently": "El context s'ha condensat recentment; s'omet aquest intent", + "condense_handler_invalid": "El gestor de l'API per condensar el context no és vàlid", "condense_context_grew": "La mida del context ha augmentat durant la condensació; s'omet aquest intent" }, "warnings": { diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index d09be1a5a3..a9b39e3df1 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Fehler beim Importieren der Einstellungen: {{error}}.", "mistake_limit_guidance": "Dies kann auf einen Fehler im Denkprozess des Modells oder die Unfähigkeit hinweisen, ein Tool richtig zu verwenden, was durch Benutzerführung behoben werden kann (z.B. \"Versuche, die Aufgabe in kleinere Schritte zu unterteilen\").", "condense_failed": "Fehler beim Verdichten des Kontexts", + "condense_not_enough_messages": "Nicht genügend Nachrichten zum Verdichten des Kontexts", + "condensed_recently": "Kontext wurde kürzlich verdichtet; dieser Versuch wird übersprungen", + "condense_handler_invalid": "API-Handler zum Verdichten des Kontexts ist ungültig", "condense_context_grew": "Kontextgröße ist während der Verdichtung gewachsen; dieser Versuch wird übersprungen" }, "warnings": { diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 5df62bdb3d..95285e8628 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Error al importar la configuración: {{error}}.", "mistake_limit_guidance": "Esto puede indicar un fallo en el proceso de pensamiento del modelo o la incapacidad de usar una herramienta correctamente, lo cual puede mitigarse con orientación del usuario (ej. \"Intenta dividir la tarea en pasos más pequeños\").", "condense_failed": "Error al condensar el contexto", + "condense_not_enough_messages": "No hay suficientes mensajes para condensar el contexto", + "condensed_recently": "El contexto se condensó recientemente; se omite este intento", + "condense_handler_invalid": "El manejador de API para condensar el contexto no es válido", "condense_context_grew": "El tamaño del contexto aumentó durante la condensación; se omite este intento" }, "warnings": { diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index 473977a1e8..c0ef1a5816 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Échec de l'importation des paramètres : {{error}}", "mistake_limit_guidance": "Cela peut indiquer un échec dans le processus de réflexion du modèle ou une incapacité à utiliser un outil correctement, ce qui peut être atténué avec des conseils de l'utilisateur (par ex. \"Essaie de diviser la tâche en étapes plus petites\").", "condense_failed": "Échec de la condensation du contexte", + "condense_not_enough_messages": "Pas assez de messages pour condenser le contexte", + "condensed_recently": "Le contexte a été condensé récemment ; cette tentative est ignorée", + "condense_handler_invalid": "Le gestionnaire d'API pour condenser le contexte est invalide", "condense_context_grew": "La taille du contexte a augmenté pendant la condensation ; cette tentative est ignorée" }, "warnings": { diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 817db05e93..5718134f5b 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "सेटिंग्स इम्पोर्ट करने में विफल: {{error}}।", "mistake_limit_guidance": "यह मॉडल की सोच प्रक्रिया में विफलता या किसी टूल का सही उपयोग न कर पाने का संकेत हो सकता है, जिसे उपयोगकर्ता के मार्गदर्शन से ठीक किया जा सकता है (जैसे \"कार्य को छोटे चरणों में बांटने की कोशिश करें\")।", "condense_failed": "संदर्भ को संक्षिप्त करने में विफल", + "condense_not_enough_messages": "संदर्भ को संक्षिप्त करने के लिए पर्याप्त संदेश नहीं हैं", + "condensed_recently": "संदर्भ हाल ही में संक्षिप्त किया गया था; इस प्रयास को छोड़ा जा रहा है", + "condense_handler_invalid": "संदर्भ को संक्षिप्त करने के लिए API हैंडलर अमान्य है", "condense_context_grew": "संक्षिप्तीकरण के दौरान संदर्भ का आकार बढ़ गया; इस प्रयास को छोड़ा जा रहा है" }, "warnings": { diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index ca80ab915c..ec754bd0df 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Importazione delle impostazioni fallita: {{error}}.", "mistake_limit_guidance": "Questo può indicare un fallimento nel processo di pensiero del modello o l'incapacità di utilizzare correttamente uno strumento, che può essere mitigato con la guida dell'utente (ad es. \"Prova a suddividere l'attività in passaggi più piccoli\").", "condense_failed": "Impossibile condensare il contesto", + "condense_not_enough_messages": "Non ci sono abbastanza messaggi per condensare il contesto", + "condensed_recently": "Il contesto è stato condensato di recente; questo tentativo viene saltato", + "condense_handler_invalid": "Il gestore API per condensare il contesto non è valido", "condense_context_grew": "La dimensione del contesto è aumentata durante la condensazione; questo tentativo viene saltato" }, "warnings": { diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 691f06c398..b73d932e91 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "設定のインポートに失敗しました:{{error}}", "mistake_limit_guidance": "これは、モデルの思考プロセスの失敗やツールを適切に使用できないことを示している可能性があり、ユーザーのガイダンスによって軽減できます(例:「タスクをより小さなステップに分割してみてください」)。", "condense_failed": "コンテキストの圧縮に失敗しました", + "condense_not_enough_messages": "コンテキストを圧縮するのに十分なメッセージがありません", + "condensed_recently": "コンテキストは最近圧縮されました;この試行をスキップします", + "condense_handler_invalid": "コンテキストを圧縮するためのAPIハンドラーが無効です", "condense_context_grew": "圧縮中にコンテキストサイズが増加しました;この試行をスキップします" }, "warnings": { diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index a416b5bc1d..38c3c5a15c 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "설정 가져오기 실패: {{error}}.", "mistake_limit_guidance": "이는 모델의 사고 과정 실패나 도구를 제대로 사용하지 못하는 것을 나타낼 수 있으며, 사용자 가이드를 통해 완화할 수 있습니다 (예: \"작업을 더 작은 단계로 나누어 시도해보세요\").", "condense_failed": "컨텍스트 압축에 실패했습니다", + "condense_not_enough_messages": "컨텍스트를 압축할 메시지가 충분하지 않습니다", + "condensed_recently": "컨텍스트가 최근 압축되었습니다; 이 시도를 건너뜁니다", + "condense_handler_invalid": "컨텍스트 압축을 위한 API 핸들러가 유효하지 않습니다", "condense_context_grew": "압축 중 컨텍스트 크기가 증가했습니다; 이 시도를 건너뜁니다" }, "warnings": { diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index e7590a1c5e..285086575a 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Importeren van instellingen mislukt: {{error}}.", "mistake_limit_guidance": "Dit kan duiden op een fout in het denkproces van het model of het onvermogen om een tool correct te gebruiken, wat kan worden verminderd met gebruikersbegeleiding (bijv. \"Probeer de taak op te delen in kleinere stappen\").", "condense_failed": "Comprimeren van context mislukt", + "condense_not_enough_messages": "Niet genoeg berichten om context te comprimeren", + "condensed_recently": "Context is recent gecomprimeerd; deze poging wordt overgeslagen", + "condense_handler_invalid": "API-handler voor het comprimeren van context is ongeldig", "condense_context_grew": "Contextgrootte nam toe tijdens comprimeren; deze poging wordt overgeslagen" }, "warnings": { diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index 0f0b83c71f..0fad576518 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Nie udało się zaimportować ustawień: {{error}}.", "mistake_limit_guidance": "To może wskazywać na błąd w procesie myślowym modelu lub niezdolność do prawidłowego użycia narzędzia, co można złagodzić poprzez wskazówki użytkownika (np. \"Spróbuj podzielić zadanie na mniejsze kroki\").", "condense_failed": "Nie udało się skondensować kontekstu", + "condense_not_enough_messages": "Za mało wiadomości do skondensowania kontekstu", + "condensed_recently": "Kontekst został niedawno skondensowany; pomijanie tej próby", + "condense_handler_invalid": "Nieprawidłowy handler API do kondensowania kontekstu", "condense_context_grew": "Rozmiar kontekstu wzrósł podczas kondensacji; pomijanie tej próby" }, "warnings": { diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index cc82604f32..d97067daf6 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -59,6 +59,9 @@ "settings_import_failed": "Falha ao importar configurações: {{error}}", "mistake_limit_guidance": "Isso pode indicar uma falha no processo de pensamento do modelo ou incapacidade de usar uma ferramenta adequadamente, o que pode ser mitigado com orientação do usuário (ex. \"Tente dividir a tarefa em etapas menores\").", "condense_failed": "Falha ao condensar o contexto", + "condense_not_enough_messages": "Não há mensagens suficientes para condensar o contexto", + "condensed_recently": "O contexto foi condensado recentemente; pulando esta tentativa", + "condense_handler_invalid": "O manipulador de API para condensar o contexto é inválido", "condense_context_grew": "O tamanho do contexto aumentou durante a condensação; pulando esta tentativa" }, "warnings": { diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index 3683eaed71..f00422e26e 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Не удалось импортировать настройки: {{error}}.", "mistake_limit_guidance": "Это может указывать на сбой в процессе мышления модели или неспособность правильно использовать инструмент, что можно смягчить с помощью руководства пользователя (например, \"Попробуйте разбить задачу на более мелкие шаги\").", "condense_failed": "Не удалось сжать контекст", + "condense_not_enough_messages": "Недостаточно сообщений для сжатия контекста", + "condensed_recently": "Контекст был недавно сжат; пропускаем эту попытку", + "condense_handler_invalid": "Обработчик API для сжатия контекста недействителен", "condense_context_grew": "Размер контекста увеличился во время сжатия; пропускаем эту попытку" }, "warnings": { diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index d4e5033c5e..c55e4faa2f 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Ayarlar içe aktarılamadı: {{error}}.", "mistake_limit_guidance": "Bu, modelin düşünce sürecindeki bir başarısızlığı veya bir aracı düzgün kullanamama durumunu gösterebilir, bu da kullanıcı rehberliği ile hafifletilebilir (örn. \"Görevi daha küçük adımlara bölmeyi deneyin\").", "condense_failed": "Bağlam sıkıştırılamadı", + "condense_not_enough_messages": "Bağlamı sıkıştırmak için yeterli mesaj yok", + "condensed_recently": "Bağlam yakın zamanda sıkıştırıldı; bu deneme atlanıyor", + "condense_handler_invalid": "Bağlamı sıkıştırmak için API işleyicisi geçersiz", "condense_context_grew": "Sıkıştırma sırasında bağlam boyutu arttı; bu deneme atlanıyor" }, "warnings": { diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index 4848daae47..b7efe4be2f 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "Nhập cài đặt thất bại: {{error}}.", "mistake_limit_guidance": "Điều này có thể cho thấy sự thất bại trong quá trình suy nghĩ của mô hình hoặc không thể sử dụng công cụ đúng cách, có thể được giảm thiểu bằng hướng dẫn của người dùng (ví dụ: \"Hãy thử chia nhỏ nhiệm vụ thành các bước nhỏ hơn\").", "condense_failed": "Không thể nén ngữ cảnh", + "condense_not_enough_messages": "Không đủ tin nhắn để nén ngữ cảnh", + "condensed_recently": "Ngữ cảnh đã được nén gần đây; bỏ qua lần thử này", + "condense_handler_invalid": "Trình xử lý API để nén ngữ cảnh không hợp lệ", "condense_context_grew": "Kích thước ngữ cảnh tăng lên trong quá trình nén; bỏ qua lần thử này" }, "warnings": { diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index b93484cd85..a88c81305b 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "设置导入失败:{{error}}。", "mistake_limit_guidance": "这可能表明模型思维过程失败或无法正确使用工具,可通过用户指导来缓解(例如\"尝试将任务分解为更小的步骤\")。", "condense_failed": "压缩上下文失败", + "condense_not_enough_messages": "没有足够的对话来压缩上下文", + "condensed_recently": "上下文最近已压缩;跳过此次尝试", + "condense_handler_invalid": "压缩上下文的API处理程序无效", "condense_context_grew": "压缩过程中上下文大小增加;跳过此次尝试" }, "warnings": { diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 49b141b09a..e7cff14c91 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -55,6 +55,9 @@ "settings_import_failed": "設定匯入失敗:{{error}}。", "mistake_limit_guidance": "這可能表明模型思維過程失敗或無法正確使用工具,可透過使用者指導來緩解(例如「嘗試將工作分解為更小的步驟」)。", "condense_failed": "壓縮上下文失敗", + "condense_not_enough_messages": "沒有足夠的訊息來壓縮上下文", + "condensed_recently": "上下文最近已壓縮;跳過此次嘗試", + "condense_handler_invalid": "壓縮上下文的 API 處理程式無效", "condense_context_grew": "壓縮過程中上下文大小增加;跳過此次嘗試" }, "warnings": { From 381196e202671ccf5d18f4a39074a88b14c6391c Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Tue, 27 May 2025 21:54:40 -0700 Subject: [PATCH 09/12] pipe error back from truncate code --- src/core/sliding-window/index.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index d80ebdd73e..6a0b0f1b27 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -96,6 +96,8 @@ export async function truncateConversationIfNeeded({ customCondensingPrompt, condensingApiHandler, }: TruncateOptions): Promise { + let error: string | undefined + let cost = 0 // Calculate the maximum tokens reserved for response const reservedTokens = maxTokens || contextWindow * 0.2 @@ -127,7 +129,10 @@ export async function truncateConversationIfNeeded({ customCondensingPrompt, condensingApiHandler, ) - if (!result.error) { + if (result.error) { + error = result.error + cost = result.cost + } else { return { ...result, prevContextTokens } } } @@ -136,8 +141,8 @@ export async function truncateConversationIfNeeded({ // Fall back to sliding window truncation if needed if (prevContextTokens > allowedTokens) { const truncatedMessages = truncateConversation(messages, 0.5, taskId) - return { messages: truncatedMessages, prevContextTokens, summary: "", cost: 0 } + return { messages: truncatedMessages, prevContextTokens, summary: "", cost, error } } // No truncation or condensation needed - return { messages, summary: "", cost: 0, prevContextTokens } + return { messages, summary: "", cost, prevContextTokens, error } } From 9a40cdee071981c7349849537ae85a8f71ff45ed Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Wed, 28 May 2025 09:46:28 -0700 Subject: [PATCH 10/12] add condense_context_error ClineMessage --- packages/types/src/message.ts | 1 + src/core/task/Task.ts | 4 ++-- webview-ui/src/components/chat/ChatRow.tsx | 4 +++- webview-ui/src/components/chat/ContextCondenseRow.tsx | 11 +++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index e870e8d707..33c2b7a108 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -50,6 +50,7 @@ export const clineSays = [ "rooignore_error", "diff_error", "condense_context", + "condense_context_error", "codebase_search_result", ] as const diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 33579ef676..8165953e7d 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -528,7 +528,7 @@ export class Task extends EventEmitter { ) if (error) { this.say( - "error", + "condense_context_error", error, undefined /* images */, false /* partial */, @@ -1610,7 +1610,7 @@ export class Task extends EventEmitter { await this.overwriteApiConversationHistory(truncateResult.messages) } if (truncateResult.error) { - await this.say("error", truncateResult.error) + await this.say("condense_context_error", truncateResult.error) } else if (truncateResult.summary) { const { summary, cost, prevContextTokens, newContextTokens = 0 } = truncateResult const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens } diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index eaabb77e70..675eb2ba5e 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -35,7 +35,7 @@ import { Markdown } from "./Markdown" import { CommandExecution } from "./CommandExecution" import { CommandExecutionError } from "./CommandExecutionError" import { AutoApprovedRequestLimitWarning } from "./AutoApprovedRequestLimitWarning" -import { CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow" +import { CondenseContextErrorRow, CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow" import CodebaseSearchResultsDisplay from "./CodebaseSearchResultsDisplay" interface ChatRowProps { @@ -969,6 +969,8 @@ export const ChatRowContent = ({ return } return message.contextCondense ? : null + case "condense_context_error": + return case "codebase_search_result": let parsed: { content: { diff --git a/webview-ui/src/components/chat/ContextCondenseRow.tsx b/webview-ui/src/components/chat/ContextCondenseRow.tsx index 6a80208770..eadc46ad71 100644 --- a/webview-ui/src/components/chat/ContextCondenseRow.tsx +++ b/webview-ui/src/components/chat/ContextCondenseRow.tsx @@ -59,3 +59,14 @@ export const CondensingContextRow = () => { ) } + +export const CondenseContextErrorRow = ({ errorText }: { errorText?: string }) => { + const { t } = useTranslation() + return ( +
+ + {t("common:contextCondense.errorHeader")} + {errorText} +
+ ) +} From 1ec47c3d131133dc99aa7c86d2c8b8fad78326b0 Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Wed, 28 May 2025 12:28:35 -0700 Subject: [PATCH 11/12] fixes --- src/core/condense/index.ts | 5 ++++- webview-ui/src/components/chat/ContextCondenseRow.tsx | 8 +++++--- webview-ui/src/i18n/locales/en/chat.json | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index ecd8f0ed39..58a81f3d22 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -97,7 +97,10 @@ export async function summarizeConversation( const response: SummarizeResponse = { messages, cost: 0, summary: "" } const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP)) if (messagesToSummarize.length <= 1) { - const error = t("common:errors.condense_not_enough_messages") + const error = + messages.length <= N_MESSAGES_TO_KEEP + 1 + ? t("common:errors.condense_not_enough_messages") + : t("common:errors.condensed_recently") return { ...response, error } } const keepMessages = messages.slice(-N_MESSAGES_TO_KEEP) diff --git a/webview-ui/src/components/chat/ContextCondenseRow.tsx b/webview-ui/src/components/chat/ContextCondenseRow.tsx index eadc46ad71..9664b03e00 100644 --- a/webview-ui/src/components/chat/ContextCondenseRow.tsx +++ b/webview-ui/src/components/chat/ContextCondenseRow.tsx @@ -63,9 +63,11 @@ export const CondensingContextRow = () => { export const CondenseContextErrorRow = ({ errorText }: { errorText?: string }) => { const { t } = useTranslation() return ( -
- - {t("common:contextCondense.errorHeader")} +
+
+ + {t("chat:contextCondense.errorHeader")} +
{errorText}
) diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index adc47fc7d4..c96e18b6c6 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -133,6 +133,7 @@ "contextCondense": { "title": "Context Condensed", "condensing": "Condensing context...", + "errorHeader": "Failed to condense context", "tokens": "tokens" }, "instructions": { From 5e975aacdfe835b17174a697b8b9fd752c80431d Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Wed, 28 May 2025 12:41:17 -0700 Subject: [PATCH 12/12] translations --- webview-ui/src/i18n/locales/ca/chat.json | 1 + webview-ui/src/i18n/locales/de/chat.json | 1 + webview-ui/src/i18n/locales/es/chat.json | 1 + webview-ui/src/i18n/locales/fr/chat.json | 1 + webview-ui/src/i18n/locales/hi/chat.json | 1 + webview-ui/src/i18n/locales/it/chat.json | 1 + webview-ui/src/i18n/locales/ja/chat.json | 1 + webview-ui/src/i18n/locales/ko/chat.json | 1 + webview-ui/src/i18n/locales/nl/chat.json | 1 + webview-ui/src/i18n/locales/pl/chat.json | 1 + webview-ui/src/i18n/locales/pt-BR/chat.json | 1 + webview-ui/src/i18n/locales/ru/chat.json | 1 + webview-ui/src/i18n/locales/tr/chat.json | 1 + webview-ui/src/i18n/locales/vi/chat.json | 1 + webview-ui/src/i18n/locales/zh-CN/chat.json | 1 + webview-ui/src/i18n/locales/zh-TW/chat.json | 1 + 16 files changed, 16 insertions(+) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 3cb2874c61..4babea2510 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Context condensat", "condensing": "Condensant context...", + "errorHeader": "Error en condensar el context", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index ca2bb62b20..1a0aaa4cd7 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Kontext komprimiert", "condensing": "Kontext wird komprimiert...", + "errorHeader": "Kontext konnte nicht komprimiert werden", "tokens": "Tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index c05863f3dc..c8d5c4e25d 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contexto condensado", "condensing": "Condensando contexto...", + "errorHeader": "Error al condensar el contexto", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 073da26ee2..753a86f614 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contexte condensé", "condensing": "Condensation du contexte...", + "errorHeader": "Échec de la condensation du contexte", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 26c94b8574..beebba1157 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "संदर्भ संक्षिप्त किया गया", "condensing": "संदर्भ संघनित कर रहा है...", + "errorHeader": "संदर्भ संघनित करने में विफल", "tokens": "टोकन" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 4208040b59..5795ef9a7f 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contesto condensato", "condensing": "Condensazione del contesto...", + "errorHeader": "Impossibile condensare il contesto", "tokens": "token" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index c09ed2bc99..a04f04f952 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "コンテキスト要約", "condensing": "コンテキストを圧縮中...", + "errorHeader": "コンテキストの圧縮に失敗しました", "tokens": "トークン" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 6c2a6de5c7..4c0d7fc5b2 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "컨텍스트 요약됨", "condensing": "컨텍스트 압축 중...", + "errorHeader": "컨텍스트 압축 실패", "tokens": "토큰" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index b5217049aa..5d7eb16ba5 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -212,6 +212,7 @@ "contextCondense": { "title": "Context samengevat", "condensing": "Context aan het samenvatten...", + "errorHeader": "Context samenvatten mislukt", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index a33c3126d8..2f42b32974 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Kontekst skondensowany", "condensing": "Kondensowanie kontekstu...", + "errorHeader": "Nie udało się skondensować kontekstu", "tokens": "tokeny" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index f75634c45b..185b133ac2 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contexto condensado", "condensing": "Condensando contexto...", + "errorHeader": "Falha ao condensar contexto", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 4c7ef08021..218c389c96 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -212,6 +212,7 @@ "contextCondense": { "title": "Контекст сжат", "condensing": "Сжатие контекста...", + "errorHeader": "Не удалось сжать контекст", "tokens": "токены" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 98fde1efe7..94b0e86c7e 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Bağlam Özetlendi", "condensing": "Bağlam yoğunlaştırılıyor...", + "errorHeader": "Bağlam yoğunlaştırılamadı", "tokens": "token" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index a6b661a1ae..2832380c93 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Ngữ cảnh đã tóm tắt", "condensing": "Đang cô đọng ngữ cảnh...", + "errorHeader": "Không thể cô đọng ngữ cảnh", "tokens": "token" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 80cae1e519..59a4f7200b 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "上下文已压缩", "condensing": "正在压缩上下文...", + "errorHeader": "上下文压缩失败", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 75b42c44e2..683f10fdf5 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "上下文已壓縮", "condensing": "正在壓縮上下文...", + "errorHeader": "上下文壓縮失敗", "tokens": "tokens" }, "followUpSuggest": {