Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/types/src/experiment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
* ExperimentId
*/

export const experimentIds = ["powerSteering", "concurrentFileReads"] as const
export const experimentIds = ["powerSteering", "concurrentFileReads", "disableSlidingWindow"] as const

export const experimentIdsSchema = z.enum(experimentIds)

Expand All @@ -19,6 +19,7 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
export const experimentsSchema = z.object({
powerSteering: z.boolean(),
concurrentFileReads: z.boolean(),
disableSlidingWindow: z.boolean(),
})

export type Experiments = z.infer<typeof experimentsSchema>
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const globalSettingsSchema = z.object({
allowedMaxRequests: z.number().nullish(),
autoCondenseContext: z.boolean().optional(),
autoCondenseContextPercent: z.number().optional(),
maxConcurrentFileReads: z.number().optional(),
maxConcurrentFileReads: z.number().optional(),

browserToolEnabled: z.boolean().optional(),
browserViewportSize: z.string().optional(),
Expand Down
95 changes: 95 additions & 0 deletions src/core/sliding-window/__tests__/sliding-window.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -302,6 +303,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -335,6 +337,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -347,6 +350,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -366,6 +370,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -378,6 +383,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -412,6 +418,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -445,6 +452,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -471,6 +479,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -507,6 +516,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -552,6 +562,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: true,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -617,6 +628,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: true,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -662,6 +674,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 50, // This shouldn't matter since autoCondenseContext is false
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -717,6 +730,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: true,
autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 60%
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -767,6 +781,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: true,
autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 40%
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -785,6 +800,78 @@ describe("Sliding Window", () => {
// Clean up
summarizeSpy.mockRestore()
})

it("should not truncate when disableSlidingWindow is true even if tokens exceed threshold", async () => {
const modelInfo = createModelInfo(100000, 30000)
const totalTokens = 80001 // Well above threshold

// Create messages with very small content in the last one to avoid token overflow
const messagesWithSmallContent = [
...messages.slice(0, -1),
{ ...messages[messages.length - 1], content: "" },
]

const result = await truncateConversationIfNeeded({
messages: messagesWithSmallContent,
totalTokens,
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: true, // This should prevent any truncation
systemPrompt: "System prompt",
taskId,
})

// Should return original messages without any truncation
expect(result).toEqual({
messages: messagesWithSmallContent,
summary: "",
cost: 0,
prevContextTokens: totalTokens,
})
})

it("should not use summarizeConversation when disableSlidingWindow is true even with autoCondenseContext enabled", async () => {
// Reset any previous mock calls
jest.clearAllMocks()
const summarizeSpy = jest.spyOn(condenseModule, "summarizeConversation")

const modelInfo = createModelInfo(100000, 30000)
const totalTokens = 80001 // Well above threshold
const messagesWithSmallContent = [
...messages.slice(0, -1),
{ ...messages[messages.length - 1], content: "" },
]

const result = await truncateConversationIfNeeded({
messages: messagesWithSmallContent,
totalTokens,
contextWindow: modelInfo.contextWindow,
maxTokens: modelInfo.maxTokens,
apiHandler: mockApiHandler,
autoCondenseContext: true, // This would normally trigger summarization
autoCondenseContextPercent: 50, // Well below our 80% usage
disableSlidingWindow: true, // This should prevent any processing
systemPrompt: "System prompt",
taskId,
})

// Verify summarizeConversation was not called
expect(summarizeSpy).not.toHaveBeenCalled()

// Should return original messages without any processing
expect(result).toEqual({
messages: messagesWithSmallContent,
summary: "",
cost: 0,
prevContextTokens: totalTokens,
})

// Clean up
summarizeSpy.mockRestore()
})
})

/**
Expand Down Expand Up @@ -827,6 +914,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -846,6 +934,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -876,6 +965,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -895,6 +985,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -924,6 +1015,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -938,6 +1030,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down Expand Up @@ -965,6 +1058,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand All @@ -979,6 +1073,7 @@ describe("Sliding Window", () => {
apiHandler: mockApiHandler,
autoCondenseContext: false,
autoCondenseContextPercent: 100,
disableSlidingWindow: false,
systemPrompt: "System prompt",
taskId,
})
Expand Down
7 changes: 7 additions & 0 deletions src/core/sliding-window/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type TruncateOptions = {
apiHandler: ApiHandler
autoCondenseContext: boolean
autoCondenseContextPercent: number
disableSlidingWindow: boolean
systemPrompt: string
taskId: string
customCondensingPrompt?: string
Expand All @@ -93,6 +94,7 @@ export async function truncateConversationIfNeeded({
apiHandler,
autoCondenseContext,
autoCondenseContextPercent,
disableSlidingWindow,
systemPrompt,
taskId,
customCondensingPrompt,
Expand All @@ -113,6 +115,11 @@ export async function truncateConversationIfNeeded({
// Calculate total effective tokens (totalTokens never includes the last message)
const prevContextTokens = totalTokens + lastMessageTokens

// If sliding window is disabled, return original messages without any truncation
if (disableSlidingWindow) {
return { messages, summary: "", cost, prevContextTokens, error }
}

// Calculate available tokens for conversation history
// Truncate if we're within TOKEN_BUFFER_PERCENTAGE of the context window
const allowedTokens = contextWindow * (1 - TOKEN_BUFFER_PERCENTAGE) - reservedTokens
Expand Down
4 changes: 4 additions & 0 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import delay from "delay"
import pWaitFor from "p-wait-for"
import { serializeError } from "serialize-error"

import { experiments, EXPERIMENT_IDS, experimentDefault } from "../../shared/experiments"

import {
type ProviderSettings,
type TokenUsage,
Expand Down Expand Up @@ -1607,6 +1609,7 @@ export class Task extends EventEmitter<ClineEvents> {
mode,
autoCondenseContext = true,
autoCondenseContextPercent = 100,
experiments: experimentsConfig = {},
} = state ?? {}

// Get condensing configuration for automatic triggers
Expand Down Expand Up @@ -1677,6 +1680,7 @@ export class Task extends EventEmitter<ClineEvents> {
apiHandler: this.api,
autoCondenseContext,
autoCondenseContextPercent,
disableSlidingWindow: experiments.isEnabled({ ...experimentDefault, ...experimentsConfig }, EXPERIMENT_IDS.DISABLE_SLIDING_WINDOW),
systemPrompt,
taskId: this.taskId,
customCondensingPrompt,
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export interface WebviewMessage {
| "alwaysAllowSubtasks"
| "autoCondenseContext"
| "autoCondenseContextPercent"
| "disableSlidingWindow"
| "condensingApiConfigId"
| "updateCondensingPrompt"
| "playSound"
Expand Down
3 changes: 3 additions & 0 deletions src/shared/__tests__/experiments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe("experiments", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
concurrentFileReads: false,
disableSlidingWindow: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
Expand All @@ -27,6 +28,7 @@ describe("experiments", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: true,
concurrentFileReads: false,
disableSlidingWindow: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
Expand All @@ -35,6 +37,7 @@ describe("experiments", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
concurrentFileReads: false,
disableSlidingWindow: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
Expand Down
2 changes: 2 additions & 0 deletions src/shared/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AssertEqual, Equals, Keys, Values, ExperimentId } from "@roo-code/
export const EXPERIMENT_IDS = {
POWER_STEERING: "powerSteering",
CONCURRENT_FILE_READS: "concurrentFileReads",
DISABLE_SLIDING_WINDOW: "disableSlidingWindow",
} as const satisfies Record<string, ExperimentId>

type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
Expand All @@ -16,6 +17,7 @@ interface ExperimentConfig {
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
POWER_STEERING: { enabled: false },
CONCURRENT_FILE_READS: { enabled: false },
DISABLE_SLIDING_WINDOW: { enabled: false },
}

export const experimentDefault = Object.fromEntries(
Expand Down
Loading
Loading