Skip to content

Commit 640d849

Browse files
committed
Adding a Disable automatic context clearing (Sliding Window) to Experimental settings
More complex use of Roo might not tolerate any kind of cleaning. With recent model development, they can work autonomously for longer time which needs some kind of guranarty of context in some situations.
1 parent 3f3825a commit 640d849

File tree

10 files changed

+122
-4
lines changed

10 files changed

+122
-4
lines changed

packages/types/src/experiment.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
66
* ExperimentId
77
*/
88

9-
export const experimentIds = ["powerSteering", "concurrentFileReads"] as const
9+
export const experimentIds = ["powerSteering", "concurrentFileReads", "disableSlidingWindow"] as const
1010

1111
export const experimentIdsSchema = z.enum(experimentIds)
1212

@@ -19,6 +19,7 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
1919
export const experimentsSchema = z.object({
2020
powerSteering: z.boolean(),
2121
concurrentFileReads: z.boolean(),
22+
disableSlidingWindow: z.boolean(),
2223
})
2324

2425
export type Experiments = z.infer<typeof experimentsSchema>

packages/types/src/global-settings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const globalSettingsSchema = z.object({
4848
allowedMaxRequests: z.number().nullish(),
4949
autoCondenseContext: z.boolean().optional(),
5050
autoCondenseContextPercent: z.number().optional(),
51-
maxConcurrentFileReads: z.number().optional(),
51+
maxConcurrentFileReads: z.number().optional(),
5252

5353
browserToolEnabled: z.boolean().optional(),
5454
browserViewportSize: z.string().optional(),

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ describe("Sliding Window", () => {
263263
apiHandler: mockApiHandler,
264264
autoCondenseContext: false,
265265
autoCondenseContextPercent: 100,
266+
disableSlidingWindow: false,
266267
systemPrompt: "System prompt",
267268
taskId,
268269
})
@@ -302,6 +303,7 @@ describe("Sliding Window", () => {
302303
apiHandler: mockApiHandler,
303304
autoCondenseContext: false,
304305
autoCondenseContextPercent: 100,
306+
disableSlidingWindow: false,
305307
systemPrompt: "System prompt",
306308
taskId,
307309
})
@@ -335,6 +337,7 @@ describe("Sliding Window", () => {
335337
apiHandler: mockApiHandler,
336338
autoCondenseContext: false,
337339
autoCondenseContextPercent: 100,
340+
disableSlidingWindow: false,
338341
systemPrompt: "System prompt",
339342
taskId,
340343
})
@@ -347,6 +350,7 @@ describe("Sliding Window", () => {
347350
apiHandler: mockApiHandler,
348351
autoCondenseContext: false,
349352
autoCondenseContextPercent: 100,
353+
disableSlidingWindow: false,
350354
systemPrompt: "System prompt",
351355
taskId,
352356
})
@@ -366,6 +370,7 @@ describe("Sliding Window", () => {
366370
apiHandler: mockApiHandler,
367371
autoCondenseContext: false,
368372
autoCondenseContextPercent: 100,
373+
disableSlidingWindow: false,
369374
systemPrompt: "System prompt",
370375
taskId,
371376
})
@@ -378,6 +383,7 @@ describe("Sliding Window", () => {
378383
apiHandler: mockApiHandler,
379384
autoCondenseContext: false,
380385
autoCondenseContextPercent: 100,
386+
disableSlidingWindow: false,
381387
systemPrompt: "System prompt",
382388
taskId,
383389
})
@@ -412,6 +418,7 @@ describe("Sliding Window", () => {
412418
apiHandler: mockApiHandler,
413419
autoCondenseContext: false,
414420
autoCondenseContextPercent: 100,
421+
disableSlidingWindow: false,
415422
systemPrompt: "System prompt",
416423
taskId,
417424
})
@@ -445,6 +452,7 @@ describe("Sliding Window", () => {
445452
apiHandler: mockApiHandler,
446453
autoCondenseContext: false,
447454
autoCondenseContextPercent: 100,
455+
disableSlidingWindow: false,
448456
systemPrompt: "System prompt",
449457
taskId,
450458
})
@@ -471,6 +479,7 @@ describe("Sliding Window", () => {
471479
apiHandler: mockApiHandler,
472480
autoCondenseContext: false,
473481
autoCondenseContextPercent: 100,
482+
disableSlidingWindow: false,
474483
systemPrompt: "System prompt",
475484
taskId,
476485
})
@@ -507,6 +516,7 @@ describe("Sliding Window", () => {
507516
apiHandler: mockApiHandler,
508517
autoCondenseContext: false,
509518
autoCondenseContextPercent: 100,
519+
disableSlidingWindow: false,
510520
systemPrompt: "System prompt",
511521
taskId,
512522
})
@@ -552,6 +562,7 @@ describe("Sliding Window", () => {
552562
apiHandler: mockApiHandler,
553563
autoCondenseContext: true,
554564
autoCondenseContextPercent: 100,
565+
disableSlidingWindow: false,
555566
systemPrompt: "System prompt",
556567
taskId,
557568
})
@@ -617,6 +628,7 @@ describe("Sliding Window", () => {
617628
apiHandler: mockApiHandler,
618629
autoCondenseContext: true,
619630
autoCondenseContextPercent: 100,
631+
disableSlidingWindow: false,
620632
systemPrompt: "System prompt",
621633
taskId,
622634
})
@@ -662,6 +674,7 @@ describe("Sliding Window", () => {
662674
apiHandler: mockApiHandler,
663675
autoCondenseContext: false,
664676
autoCondenseContextPercent: 50, // This shouldn't matter since autoCondenseContext is false
677+
disableSlidingWindow: false,
665678
systemPrompt: "System prompt",
666679
taskId,
667680
})
@@ -717,6 +730,7 @@ describe("Sliding Window", () => {
717730
apiHandler: mockApiHandler,
718731
autoCondenseContext: true,
719732
autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 60%
733+
disableSlidingWindow: false,
720734
systemPrompt: "System prompt",
721735
taskId,
722736
})
@@ -767,6 +781,7 @@ describe("Sliding Window", () => {
767781
apiHandler: mockApiHandler,
768782
autoCondenseContext: true,
769783
autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 40%
784+
disableSlidingWindow: false,
770785
systemPrompt: "System prompt",
771786
taskId,
772787
})
@@ -785,6 +800,78 @@ describe("Sliding Window", () => {
785800
// Clean up
786801
summarizeSpy.mockRestore()
787802
})
803+
804+
it("should not truncate when disableSlidingWindow is true even if tokens exceed threshold", async () => {
805+
const modelInfo = createModelInfo(100000, 30000)
806+
const totalTokens = 80001 // Well above threshold
807+
808+
// Create messages with very small content in the last one to avoid token overflow
809+
const messagesWithSmallContent = [
810+
...messages.slice(0, -1),
811+
{ ...messages[messages.length - 1], content: "" },
812+
]
813+
814+
const result = await truncateConversationIfNeeded({
815+
messages: messagesWithSmallContent,
816+
totalTokens,
817+
contextWindow: modelInfo.contextWindow,
818+
maxTokens: modelInfo.maxTokens,
819+
apiHandler: mockApiHandler,
820+
autoCondenseContext: false,
821+
autoCondenseContextPercent: 100,
822+
disableSlidingWindow: true, // This should prevent any truncation
823+
systemPrompt: "System prompt",
824+
taskId,
825+
})
826+
827+
// Should return original messages without any truncation
828+
expect(result).toEqual({
829+
messages: messagesWithSmallContent,
830+
summary: "",
831+
cost: 0,
832+
prevContextTokens: totalTokens,
833+
})
834+
})
835+
836+
it("should not use summarizeConversation when disableSlidingWindow is true even with autoCondenseContext enabled", async () => {
837+
// Reset any previous mock calls
838+
jest.clearAllMocks()
839+
const summarizeSpy = jest.spyOn(condenseModule, "summarizeConversation")
840+
841+
const modelInfo = createModelInfo(100000, 30000)
842+
const totalTokens = 80001 // Well above threshold
843+
const messagesWithSmallContent = [
844+
...messages.slice(0, -1),
845+
{ ...messages[messages.length - 1], content: "" },
846+
]
847+
848+
const result = await truncateConversationIfNeeded({
849+
messages: messagesWithSmallContent,
850+
totalTokens,
851+
contextWindow: modelInfo.contextWindow,
852+
maxTokens: modelInfo.maxTokens,
853+
apiHandler: mockApiHandler,
854+
autoCondenseContext: true, // This would normally trigger summarization
855+
autoCondenseContextPercent: 50, // Well below our 80% usage
856+
disableSlidingWindow: true, // This should prevent any processing
857+
systemPrompt: "System prompt",
858+
taskId,
859+
})
860+
861+
// Verify summarizeConversation was not called
862+
expect(summarizeSpy).not.toHaveBeenCalled()
863+
864+
// Should return original messages without any processing
865+
expect(result).toEqual({
866+
messages: messagesWithSmallContent,
867+
summary: "",
868+
cost: 0,
869+
prevContextTokens: totalTokens,
870+
})
871+
872+
// Clean up
873+
summarizeSpy.mockRestore()
874+
})
788875
})
789876

790877
/**
@@ -827,6 +914,7 @@ describe("Sliding Window", () => {
827914
apiHandler: mockApiHandler,
828915
autoCondenseContext: false,
829916
autoCondenseContextPercent: 100,
917+
disableSlidingWindow: false,
830918
systemPrompt: "System prompt",
831919
taskId,
832920
})
@@ -846,6 +934,7 @@ describe("Sliding Window", () => {
846934
apiHandler: mockApiHandler,
847935
autoCondenseContext: false,
848936
autoCondenseContextPercent: 100,
937+
disableSlidingWindow: false,
849938
systemPrompt: "System prompt",
850939
taskId,
851940
})
@@ -876,6 +965,7 @@ describe("Sliding Window", () => {
876965
apiHandler: mockApiHandler,
877966
autoCondenseContext: false,
878967
autoCondenseContextPercent: 100,
968+
disableSlidingWindow: false,
879969
systemPrompt: "System prompt",
880970
taskId,
881971
})
@@ -895,6 +985,7 @@ describe("Sliding Window", () => {
895985
apiHandler: mockApiHandler,
896986
autoCondenseContext: false,
897987
autoCondenseContextPercent: 100,
988+
disableSlidingWindow: false,
898989
systemPrompt: "System prompt",
899990
taskId,
900991
})
@@ -924,6 +1015,7 @@ describe("Sliding Window", () => {
9241015
apiHandler: mockApiHandler,
9251016
autoCondenseContext: false,
9261017
autoCondenseContextPercent: 100,
1018+
disableSlidingWindow: false,
9271019
systemPrompt: "System prompt",
9281020
taskId,
9291021
})
@@ -938,6 +1030,7 @@ describe("Sliding Window", () => {
9381030
apiHandler: mockApiHandler,
9391031
autoCondenseContext: false,
9401032
autoCondenseContextPercent: 100,
1033+
disableSlidingWindow: false,
9411034
systemPrompt: "System prompt",
9421035
taskId,
9431036
})
@@ -965,6 +1058,7 @@ describe("Sliding Window", () => {
9651058
apiHandler: mockApiHandler,
9661059
autoCondenseContext: false,
9671060
autoCondenseContextPercent: 100,
1061+
disableSlidingWindow: false,
9681062
systemPrompt: "System prompt",
9691063
taskId,
9701064
})
@@ -979,6 +1073,7 @@ describe("Sliding Window", () => {
9791073
apiHandler: mockApiHandler,
9801074
autoCondenseContext: false,
9811075
autoCondenseContextPercent: 100,
1076+
disableSlidingWindow: false,
9821077
systemPrompt: "System prompt",
9831078
taskId,
9841079
})

src/core/sliding-window/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ type TruncateOptions = {
7070
apiHandler: ApiHandler
7171
autoCondenseContext: boolean
7272
autoCondenseContextPercent: number
73+
disableSlidingWindow: boolean
7374
systemPrompt: string
7475
taskId: string
7576
customCondensingPrompt?: string
@@ -93,6 +94,7 @@ export async function truncateConversationIfNeeded({
9394
apiHandler,
9495
autoCondenseContext,
9596
autoCondenseContextPercent,
97+
disableSlidingWindow,
9698
systemPrompt,
9799
taskId,
98100
customCondensingPrompt,
@@ -113,6 +115,11 @@ export async function truncateConversationIfNeeded({
113115
// Calculate total effective tokens (totalTokens never includes the last message)
114116
const prevContextTokens = totalTokens + lastMessageTokens
115117

118+
// If sliding window is disabled, return original messages without any truncation
119+
if (disableSlidingWindow) {
120+
return { messages, summary: "", cost, prevContextTokens, error }
121+
}
122+
116123
// Calculate available tokens for conversation history
117124
// Truncate if we're within TOKEN_BUFFER_PERCENTAGE of the context window
118125
const allowedTokens = contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens

src/core/task/Task.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import delay from "delay"
88
import pWaitFor from "p-wait-for"
99
import { serializeError } from "serialize-error"
1010

11+
import { experiments, EXPERIMENT_IDS, experimentDefault } from "../../shared/experiments"
12+
1113
import {
1214
type ProviderSettings,
1315
type TokenUsage,
@@ -1607,6 +1609,7 @@ export class Task extends EventEmitter<ClineEvents> {
16071609
mode,
16081610
autoCondenseContext = true,
16091611
autoCondenseContextPercent = 100,
1612+
experiments: experimentsConfig = {},
16101613
} = state ?? {}
16111614

16121615
// Get condensing configuration for automatic triggers
@@ -1677,6 +1680,7 @@ export class Task extends EventEmitter<ClineEvents> {
16771680
apiHandler: this.api,
16781681
autoCondenseContext,
16791682
autoCondenseContextPercent,
1683+
disableSlidingWindow: experiments.isEnabled({ ...experimentDefault, ...experimentsConfig }, EXPERIMENT_IDS.DISABLE_SLIDING_WINDOW),
16801684
systemPrompt,
16811685
taskId: this.taskId,
16821686
customCondensingPrompt,

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export interface WebviewMessage {
6262
| "alwaysAllowSubtasks"
6363
| "autoCondenseContext"
6464
| "autoCondenseContextPercent"
65+
| "disableSlidingWindow"
6566
| "condensingApiConfigId"
6667
| "updateCondensingPrompt"
6768
| "playSound"

src/shared/__tests__/experiments.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe("experiments", () => {
1919
const experiments: Record<ExperimentId, boolean> = {
2020
powerSteering: false,
2121
concurrentFileReads: false,
22+
disableSlidingWindow: false,
2223
}
2324
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
2425
})
@@ -27,6 +28,7 @@ describe("experiments", () => {
2728
const experiments: Record<ExperimentId, boolean> = {
2829
powerSteering: true,
2930
concurrentFileReads: false,
31+
disableSlidingWindow: false,
3032
}
3133
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
3234
})
@@ -35,6 +37,7 @@ describe("experiments", () => {
3537
const experiments: Record<ExperimentId, boolean> = {
3638
powerSteering: false,
3739
concurrentFileReads: false,
40+
disableSlidingWindow: false,
3841
}
3942
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
4043
})

src/shared/experiments.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { AssertEqual, Equals, Keys, Values, ExperimentId } from "@roo-code/
33
export const EXPERIMENT_IDS = {
44
POWER_STEERING: "powerSteering",
55
CONCURRENT_FILE_READS: "concurrentFileReads",
6+
DISABLE_SLIDING_WINDOW: "disableSlidingWindow",
67
} as const satisfies Record<string, ExperimentId>
78

89
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@@ -16,6 +17,7 @@ interface ExperimentConfig {
1617
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
1718
POWER_STEERING: { enabled: false },
1819
CONCURRENT_FILE_READS: { enabled: false },
20+
DISABLE_SLIDING_WINDOW: { enabled: false },
1921
}
2022

2123
export const experimentDefault = Object.fromEntries(

0 commit comments

Comments
 (0)