Skip to content

Commit c3716f1

Browse files
authored
[Condense] Add a button to condense the task context (#3623)
* [Condense] Add a button to condense the task context * wip * wip * wip * bring back delete size * account for the system prompt in the context * update tests to use systemPrompt * add type * translations * nit * update tests * filter to the current task * nit * refactor * nit * non interactive option * simplify chat summary UI * changeset * nit * fix check-types * throw
1 parent 737bad6 commit c3716f1

File tree

33 files changed

+242
-105
lines changed

33 files changed

+242
-105
lines changed

.changeset/tired-dogs-worry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
Adds a button to intelligently condense the context window

src/core/condense/__tests__/index.test.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,16 @@ describe("summarizeConversation", () => {
9797
} as unknown as ApiHandler
9898
})
9999

100+
// Default system prompt for tests
101+
const defaultSystemPrompt = "You are a helpful assistant."
102+
100103
it("should not summarize when there are not enough messages", async () => {
101104
const messages: ApiMessage[] = [
102105
{ role: "user", content: "Hello", ts: 1 },
103106
{ role: "assistant", content: "Hi there", ts: 2 },
104107
]
105108

106-
const result = await summarizeConversation(messages, mockApiHandler)
109+
const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
107110
expect(result.messages).toEqual(messages)
108111
expect(result.cost).toBe(0)
109112
expect(result.summary).toBe("")
@@ -122,7 +125,7 @@ describe("summarizeConversation", () => {
122125
{ role: "user", content: "Tell me more", ts: 7 },
123126
]
124127

125-
const result = await summarizeConversation(messages, mockApiHandler)
128+
const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
126129
expect(result.messages).toEqual(messages)
127130
expect(result.cost).toBe(0)
128131
expect(result.summary).toBe("")
@@ -141,7 +144,7 @@ describe("summarizeConversation", () => {
141144
{ role: "user", content: "Tell me more", ts: 7 },
142145
]
143146

144-
const result = await summarizeConversation(messages, mockApiHandler)
147+
const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
145148

146149
// Check that the API was called correctly
147150
expect(mockApiHandler.createMessage).toHaveBeenCalled()
@@ -199,7 +202,7 @@ describe("summarizeConversation", () => {
199202
return messages.map(({ role, content }: { role: string; content: any }) => ({ role, content }))
200203
})
201204

202-
const result = await summarizeConversation(messages, mockApiHandler)
205+
const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
203206

204207
// Should return original messages when summary is empty
205208
expect(result.messages).toEqual(messages)
@@ -222,7 +225,7 @@ describe("summarizeConversation", () => {
222225
{ role: "user", content: "Tell me more", ts: 7 },
223226
]
224227

225-
await summarizeConversation(messages, mockApiHandler)
228+
await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt)
226229

227230
// Verify the final request message
228231
const expectedFinalMessage = {

src/core/condense/index.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@ export type SummarizeResponse = {
5757
*
5858
* @param {ApiMessage[]} messages - The conversation messages
5959
* @param {ApiHandler} apiHandler - The API handler to use for token counting.
60+
* @param {string} systemPrompt - The system prompt for API requests, which should be considered in the context token count
6061
* @returns {SummarizeResponse} - The result of the summarization operation (see above)
6162
*/
6263
export async function summarizeConversation(
6364
messages: ApiMessage[],
6465
apiHandler: ApiHandler,
65-
systemPrompt?: string,
66+
systemPrompt: string,
6667
): Promise<SummarizeResponse> {
6768
const response: SummarizeResponse = { messages, cost: 0, summary: "" }
6869
const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP))
@@ -111,10 +112,10 @@ export async function summarizeConversation(
111112

112113
// Count the tokens in the context for the next API request
113114
// We only estimate the tokens in summaryMesage if outputTokens is 0, otherwise we use outputTokens
114-
const contextMessages = outputTokens ? [...keepMessages] : [summaryMessage, ...keepMessages]
115-
if (systemPrompt) {
116-
contextMessages.unshift({ role: "user", content: systemPrompt })
117-
}
115+
const systemPromptMessage: ApiMessage = { role: "user", content: systemPrompt }
116+
const contextMessages = outputTokens
117+
? [systemPromptMessage, ...keepMessages]
118+
: [systemPromptMessage, summaryMessage, ...keepMessages]
118119
const contextBlocks = contextMessages.flatMap((message) =>
119120
typeof message.content === "string" ? [{ text: message.content, type: "text" as const }] : message.content,
120121
)

src/core/sliding-window/__tests__/sliding-window.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ describe("truncateConversationIfNeeded", () => {
248248
contextWindow: modelInfo.contextWindow,
249249
maxTokens: modelInfo.maxTokens,
250250
apiHandler: mockApiHandler,
251+
systemPrompt: "System prompt",
251252
})
252253

253254
// Check the new return type
@@ -276,6 +277,7 @@ describe("truncateConversationIfNeeded", () => {
276277
contextWindow: modelInfo.contextWindow,
277278
maxTokens: modelInfo.maxTokens,
278279
apiHandler: mockApiHandler,
280+
systemPrompt: "System prompt",
279281
})
280282

281283
expect(result).toEqual({
@@ -302,6 +304,7 @@ describe("truncateConversationIfNeeded", () => {
302304
contextWindow: modelInfo1.contextWindow,
303305
maxTokens: modelInfo1.maxTokens,
304306
apiHandler: mockApiHandler,
307+
systemPrompt: "System prompt",
305308
})
306309

307310
const result2 = await truncateConversationIfNeeded({
@@ -310,6 +313,7 @@ describe("truncateConversationIfNeeded", () => {
310313
contextWindow: modelInfo2.contextWindow,
311314
maxTokens: modelInfo2.maxTokens,
312315
apiHandler: mockApiHandler,
316+
systemPrompt: "System prompt",
313317
})
314318

315319
expect(result1.messages).toEqual(result2.messages)
@@ -325,6 +329,7 @@ describe("truncateConversationIfNeeded", () => {
325329
contextWindow: modelInfo1.contextWindow,
326330
maxTokens: modelInfo1.maxTokens,
327331
apiHandler: mockApiHandler,
332+
systemPrompt: "System prompt",
328333
})
329334

330335
const result4 = await truncateConversationIfNeeded({
@@ -333,6 +338,7 @@ describe("truncateConversationIfNeeded", () => {
333338
contextWindow: modelInfo2.contextWindow,
334339
maxTokens: modelInfo2.maxTokens,
335340
apiHandler: mockApiHandler,
341+
systemPrompt: "System prompt",
336342
})
337343

338344
expect(result3.messages).toEqual(result4.messages)
@@ -363,6 +369,7 @@ describe("truncateConversationIfNeeded", () => {
363369
contextWindow: modelInfo.contextWindow,
364370
maxTokens,
365371
apiHandler: mockApiHandler,
372+
systemPrompt: "System prompt",
366373
})
367374
expect(resultWithSmall).toEqual({
368375
messages: messagesWithSmallContent,
@@ -392,6 +399,7 @@ describe("truncateConversationIfNeeded", () => {
392399
contextWindow: modelInfo.contextWindow,
393400
maxTokens,
394401
apiHandler: mockApiHandler,
402+
systemPrompt: "System prompt",
395403
})
396404
expect(resultWithLarge.messages).not.toEqual(messagesWithLargeContent) // Should truncate
397405
expect(resultWithLarge.summary).toBe("")
@@ -414,6 +422,7 @@ describe("truncateConversationIfNeeded", () => {
414422
contextWindow: modelInfo.contextWindow,
415423
maxTokens,
416424
apiHandler: mockApiHandler,
425+
systemPrompt: "System prompt",
417426
})
418427
expect(resultWithVeryLarge.messages).not.toEqual(messagesWithVeryLargeContent) // Should truncate
419428
expect(resultWithVeryLarge.summary).toBe("")
@@ -439,6 +448,7 @@ describe("truncateConversationIfNeeded", () => {
439448
contextWindow: modelInfo.contextWindow,
440449
maxTokens: modelInfo.maxTokens,
441450
apiHandler: mockApiHandler,
451+
systemPrompt: "System prompt",
442452
})
443453
expect(result).toEqual({
444454
messages: expectedResult,
@@ -524,6 +534,7 @@ describe("truncateConversationIfNeeded", () => {
524534
maxTokens: modelInfo.maxTokens,
525535
apiHandler: mockApiHandler,
526536
autoCondenseContext: true,
537+
systemPrompt: "System prompt",
527538
})
528539

529540
// Verify summarizeConversation was called
@@ -559,6 +570,7 @@ describe("truncateConversationIfNeeded", () => {
559570
maxTokens: modelInfo.maxTokens,
560571
apiHandler: mockApiHandler,
561572
autoCondenseContext: false,
573+
systemPrompt: "System prompt",
562574
})
563575

564576
// Verify summarizeConversation was not called
@@ -612,6 +624,7 @@ describe("getMaxTokens", () => {
612624
contextWindow: modelInfo.contextWindow,
613625
maxTokens: modelInfo.maxTokens,
614626
apiHandler: mockApiHandler,
627+
systemPrompt: "System prompt",
615628
})
616629
expect(result1).toEqual({
617630
messages: messagesWithSmallContent,
@@ -627,6 +640,7 @@ describe("getMaxTokens", () => {
627640
contextWindow: modelInfo.contextWindow,
628641
maxTokens: modelInfo.maxTokens,
629642
apiHandler: mockApiHandler,
643+
systemPrompt: "System prompt",
630644
})
631645
expect(result2.messages).not.toEqual(messagesWithSmallContent)
632646
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
@@ -650,6 +664,7 @@ describe("getMaxTokens", () => {
650664
contextWindow: modelInfo.contextWindow,
651665
maxTokens: modelInfo.maxTokens,
652666
apiHandler: mockApiHandler,
667+
systemPrompt: "System prompt",
653668
})
654669
expect(result1).toEqual({
655670
messages: messagesWithSmallContent,
@@ -665,6 +680,7 @@ describe("getMaxTokens", () => {
665680
contextWindow: modelInfo.contextWindow,
666681
maxTokens: modelInfo.maxTokens,
667682
apiHandler: mockApiHandler,
683+
systemPrompt: "System prompt",
668684
})
669685
expect(result2.messages).not.toEqual(messagesWithSmallContent)
670686
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
@@ -687,6 +703,7 @@ describe("getMaxTokens", () => {
687703
contextWindow: modelInfo.contextWindow,
688704
maxTokens: modelInfo.maxTokens,
689705
apiHandler: mockApiHandler,
706+
systemPrompt: "System prompt",
690707
})
691708
expect(result1.messages).toEqual(messagesWithSmallContent)
692709

@@ -697,6 +714,7 @@ describe("getMaxTokens", () => {
697714
contextWindow: modelInfo.contextWindow,
698715
maxTokens: modelInfo.maxTokens,
699716
apiHandler: mockApiHandler,
717+
systemPrompt: "System prompt",
700718
})
701719
expect(result2).not.toEqual(messagesWithSmallContent)
702720
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
@@ -717,6 +735,7 @@ describe("getMaxTokens", () => {
717735
contextWindow: modelInfo.contextWindow,
718736
maxTokens: modelInfo.maxTokens,
719737
apiHandler: mockApiHandler,
738+
systemPrompt: "System prompt",
720739
})
721740
expect(result1.messages).toEqual(messagesWithSmallContent)
722741

@@ -727,6 +746,7 @@ describe("getMaxTokens", () => {
727746
contextWindow: modelInfo.contextWindow,
728747
maxTokens: modelInfo.maxTokens,
729748
apiHandler: mockApiHandler,
749+
systemPrompt: "System prompt",
730750
})
731751
expect(result2).not.toEqual(messagesWithSmallContent)
732752
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction

src/core/sliding-window/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ type TruncateOptions = {
6464
maxTokens?: number | null
6565
apiHandler: ApiHandler
6666
autoCondenseContext?: boolean
67-
systemPrompt?: string
67+
systemPrompt: string
6868
}
6969

7070
type TruncateResponse = SummarizeResponse & { prevContextTokens: number }

0 commit comments

Comments
 (0)