Skip to content

Commit 1950019

Browse files
committed
fix: prevent negative allowedTokens when maxTokens equals contextWindow
- Modified reservedTokens calculation to check if maxTokens equals contextWindow - When they are equal, fall back to 20% of context window instead of using full window - This prevents allowedTokens from becoming negative and triggering premature condensing - Added test case to verify the fix works correctly
1 parent 37300ef commit 1950019

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,5 +1244,51 @@ describe("Sliding Window", () => {
12441244
expect(result2).not.toEqual(messagesWithSmallContent)
12451245
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
12461246
})
1247+
1248+
it("should handle models where maxTokens equals contextWindow without negative allowedTokens", async () => {
1249+
const contextWindow = 100000
1250+
const modelInfo = createModelInfo(contextWindow, contextWindow) // maxTokens = contextWindow
1251+
1252+
// Create messages with very small content in the last one to avoid token overflow
1253+
const messagesWithSmallContent = [
1254+
...messages.slice(0, -1),
1255+
{ ...messages[messages.length - 1], content: "" },
1256+
]
1257+
1258+
// With the fix, reservedTokens should be 20% of context window (20000)
1259+
// allowedTokens = 100000 * 0.9 - 20000 = 70000
1260+
// So tokens below 70000 should not trigger truncation
1261+
const result1 = await truncateConversationIfNeeded({
1262+
messages: messagesWithSmallContent,
1263+
totalTokens: 69999, // Just below the fixed threshold
1264+
contextWindow: modelInfo.contextWindow,
1265+
maxTokens: modelInfo.maxTokens,
1266+
apiHandler: mockApiHandler,
1267+
autoCondenseContext: false,
1268+
autoCondenseContextPercent: 100,
1269+
systemPrompt: "System prompt",
1270+
taskId,
1271+
profileThresholds: {},
1272+
currentProfileId: "default",
1273+
})
1274+
expect(result1.messages).toEqual(messagesWithSmallContent) // No truncation
1275+
1276+
// Above the threshold should trigger truncation
1277+
const result2 = await truncateConversationIfNeeded({
1278+
messages: messagesWithSmallContent,
1279+
totalTokens: 70001, // Above the fixed threshold
1280+
contextWindow: modelInfo.contextWindow,
1281+
maxTokens: modelInfo.maxTokens,
1282+
apiHandler: mockApiHandler,
1283+
autoCondenseContext: false,
1284+
autoCondenseContextPercent: 100,
1285+
systemPrompt: "System prompt",
1286+
taskId,
1287+
profileThresholds: {},
1288+
currentProfileId: "default",
1289+
})
1290+
expect(result2.messages).not.toEqual(messagesWithSmallContent) // Should truncate
1291+
expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction
1292+
})
12471293
})
12481294
})

src/core/sliding-window/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ export async function truncateConversationIfNeeded({
105105
let error: string | undefined
106106
let cost = 0
107107
// Calculate the maximum tokens reserved for response
108-
const reservedTokens = maxTokens || contextWindow * 0.2
108+
// If maxTokens equals contextWindow, fall back to 20% to avoid negative allowedTokens
109+
const reservedTokens = maxTokens && maxTokens !== contextWindow ? maxTokens : contextWindow * 0.2
109110

110111
// Estimate tokens for the last message (which is always a user message)
111112
const lastMessage = messages[messages.length - 1]

0 commit comments

Comments
 (0)