Skip to content

Commit 0141be1

Browse files
committed
feat: add configurable context window limit for Qwen CLI provider
- Add qwenCodeMaxContextWindow setting to provider-settings schema - Update QwenCodeHandler to apply context window limit when configured - Add comprehensive tests for the new functionality - Addresses issue #7598 where large context windows cause performance issues
1 parent 2e59347 commit 0141be1

File tree

3 files changed

+110
-1
lines changed

3 files changed

+110
-1
lines changed

packages/types/src/provider-settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ const ioIntelligenceSchema = apiModelIdProviderModelSchema.extend({
318318

319319
const qwenCodeSchema = apiModelIdProviderModelSchema.extend({
320320
qwenCodeOauthPath: z.string().optional(),
321+
qwenCodeMaxContextWindow: z.number().int().min(1000).max(1000000).optional(),
321322
})
322323

323324
const rooSchema = apiModelIdProviderModelSchema.extend({
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { describe, it, expect, vi, beforeEach } from "vitest"
2+
import { QwenCodeHandler } from "../qwen-code"
3+
import { qwenCodeDefaultModelId, qwenCodeModels } from "@roo-code/types"
4+
5+
// Mock fs module
6+
vi.mock("node:fs", () => ({
7+
promises: {
8+
readFile: vi.fn(),
9+
writeFile: vi.fn(),
10+
},
11+
}))
12+
13+
// Mock fetch
14+
global.fetch = vi.fn()
15+
16+
describe("QwenCodeHandler", () => {
17+
beforeEach(() => {
18+
vi.clearAllMocks()
19+
})
20+
21+
describe("getModel", () => {
22+
it("should return default model when no model is specified", () => {
23+
const handler = new QwenCodeHandler({})
24+
const { id, info } = handler.getModel()
25+
26+
expect(id).toBe(qwenCodeDefaultModelId)
27+
expect(info).toEqual(qwenCodeModels[qwenCodeDefaultModelId])
28+
})
29+
30+
it("should return specified model when valid model is provided", () => {
31+
const testModelId = "qwen3-coder-flash"
32+
const handler = new QwenCodeHandler({
33+
apiModelId: testModelId,
34+
})
35+
const { id, info } = handler.getModel()
36+
37+
expect(id).toBe(testModelId)
38+
expect(info).toEqual(qwenCodeModels[testModelId])
39+
})
40+
41+
it("should use default context window when qwenCodeMaxContextWindow is not set", () => {
42+
const handler = new QwenCodeHandler({})
43+
const { info } = handler.getModel()
44+
45+
expect(info.contextWindow).toBe(1_000_000) // Default context window
46+
})
47+
48+
it("should limit context window when qwenCodeMaxContextWindow is set", () => {
49+
const maxContextWindow = 256_000
50+
const handler = new QwenCodeHandler({
51+
qwenCodeMaxContextWindow: maxContextWindow,
52+
})
53+
const { info } = handler.getModel()
54+
55+
expect(info.contextWindow).toBe(maxContextWindow)
56+
})
57+
58+
it("should use original context window when qwenCodeMaxContextWindow is larger", () => {
59+
const handler = new QwenCodeHandler({
60+
qwenCodeMaxContextWindow: 2_000_000, // Larger than default
61+
})
62+
const { info } = handler.getModel()
63+
64+
expect(info.contextWindow).toBe(1_000_000) // Should not exceed original
65+
})
66+
67+
it("should ignore qwenCodeMaxContextWindow when it's 0 or negative", () => {
68+
const handler1 = new QwenCodeHandler({
69+
qwenCodeMaxContextWindow: 0,
70+
})
71+
const { info: info1 } = handler1.getModel()
72+
73+
expect(info1.contextWindow).toBe(1_000_000) // Should use default
74+
75+
const handler2 = new QwenCodeHandler({
76+
qwenCodeMaxContextWindow: -1,
77+
})
78+
const { info: info2 } = handler2.getModel()
79+
80+
expect(info2.contextWindow).toBe(1_000_000) // Should use default
81+
})
82+
83+
it("should apply context window limit to different models", () => {
84+
const maxContextWindow = 200_000
85+
const handler = new QwenCodeHandler({
86+
apiModelId: "qwen3-coder-flash",
87+
qwenCodeMaxContextWindow: maxContextWindow,
88+
})
89+
const { id, info } = handler.getModel()
90+
91+
expect(id).toBe("qwen3-coder-flash")
92+
expect(info.contextWindow).toBe(maxContextWindow)
93+
// Other properties should remain unchanged
94+
expect(info.maxTokens).toBe(qwenCodeModels["qwen3-coder-flash"].maxTokens)
95+
expect(info.description).toBe(qwenCodeModels["qwen3-coder-flash"].description)
96+
})
97+
})
98+
})

src/api/providers/qwen-code.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ interface QwenOAuthCredentials {
3030

3131
interface QwenCodeHandlerOptions extends ApiHandlerOptions {
3232
qwenCodeOauthPath?: string
33+
qwenCodeMaxContextWindow?: number
3334
}
3435

3536
function getQwenCachedCredentialPath(customPath?: string): string {
@@ -286,7 +287,16 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan
286287

287288
override getModel(): { id: string; info: ModelInfo } {
288289
const id = this.options.apiModelId ?? qwenCodeDefaultModelId
289-
const info = qwenCodeModels[id as keyof typeof qwenCodeModels] || qwenCodeModels[qwenCodeDefaultModelId]
290+
let info = qwenCodeModels[id as keyof typeof qwenCodeModels] || qwenCodeModels[qwenCodeDefaultModelId]
291+
292+
// Apply custom context window limit if configured
293+
if (this.options.qwenCodeMaxContextWindow && this.options.qwenCodeMaxContextWindow > 0) {
294+
info = {
295+
...info,
296+
contextWindow: Math.min(info.contextWindow, this.options.qwenCodeMaxContextWindow),
297+
}
298+
}
299+
290300
return { id, info }
291301
}
292302

0 commit comments

Comments
 (0)