From 0141be1a68e52dc3d418bca9463b8fe24bfb4317 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 2 Sep 2025 19:54:57 +0000 Subject: [PATCH 1/2] 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 --- packages/types/src/provider-settings.ts | 1 + src/api/providers/__tests__/qwen-code.spec.ts | 98 +++++++++++++++++++ src/api/providers/qwen-code.ts | 12 ++- 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/api/providers/__tests__/qwen-code.spec.ts diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 090dfe6693..fa5ef8bcb8 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -318,6 +318,7 @@ const ioIntelligenceSchema = apiModelIdProviderModelSchema.extend({ const qwenCodeSchema = apiModelIdProviderModelSchema.extend({ qwenCodeOauthPath: z.string().optional(), + qwenCodeMaxContextWindow: z.number().int().min(1000).max(1000000).optional(), }) const rooSchema = apiModelIdProviderModelSchema.extend({ diff --git a/src/api/providers/__tests__/qwen-code.spec.ts b/src/api/providers/__tests__/qwen-code.spec.ts new file mode 100644 index 0000000000..78fe4b7b93 --- /dev/null +++ b/src/api/providers/__tests__/qwen-code.spec.ts @@ -0,0 +1,98 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" +import { QwenCodeHandler } from "../qwen-code" +import { qwenCodeDefaultModelId, qwenCodeModels } from "@roo-code/types" + +// Mock fs module +vi.mock("node:fs", () => ({ + promises: { + readFile: vi.fn(), + writeFile: vi.fn(), + }, +})) + +// Mock fetch +global.fetch = vi.fn() + +describe("QwenCodeHandler", () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe("getModel", () => { + it("should return default model when no model is specified", () => { + const handler = new QwenCodeHandler({}) + const { id, info } = handler.getModel() + + expect(id).toBe(qwenCodeDefaultModelId) + expect(info).toEqual(qwenCodeModels[qwenCodeDefaultModelId]) + }) + + it("should return specified model when valid model is provided", () => { + const testModelId = "qwen3-coder-flash" + const handler = new QwenCodeHandler({ + apiModelId: testModelId, + }) + const { id, info } = handler.getModel() + + expect(id).toBe(testModelId) + expect(info).toEqual(qwenCodeModels[testModelId]) + }) + + it("should use default context window when qwenCodeMaxContextWindow is not set", () => { + const handler = new QwenCodeHandler({}) + const { info } = handler.getModel() + + expect(info.contextWindow).toBe(1_000_000) // Default context window + }) + + it("should limit context window when qwenCodeMaxContextWindow is set", () => { + const maxContextWindow = 256_000 + const handler = new QwenCodeHandler({ + qwenCodeMaxContextWindow: maxContextWindow, + }) + const { info } = handler.getModel() + + expect(info.contextWindow).toBe(maxContextWindow) + }) + + it("should use original context window when qwenCodeMaxContextWindow is larger", () => { + const handler = new QwenCodeHandler({ + qwenCodeMaxContextWindow: 2_000_000, // Larger than default + }) + const { info } = handler.getModel() + + expect(info.contextWindow).toBe(1_000_000) // Should not exceed original + }) + + it("should ignore qwenCodeMaxContextWindow when it's 0 or negative", () => { + const handler1 = new QwenCodeHandler({ + qwenCodeMaxContextWindow: 0, + }) + const { info: info1 } = handler1.getModel() + + expect(info1.contextWindow).toBe(1_000_000) // Should use default + + const handler2 = new QwenCodeHandler({ + qwenCodeMaxContextWindow: -1, + }) + const { info: info2 } = handler2.getModel() + + expect(info2.contextWindow).toBe(1_000_000) // Should use default + }) + + it("should apply context window limit to different models", () => { + const maxContextWindow = 200_000 + const handler = new QwenCodeHandler({ + apiModelId: "qwen3-coder-flash", + qwenCodeMaxContextWindow: maxContextWindow, + }) + const { id, info } = handler.getModel() + + expect(id).toBe("qwen3-coder-flash") + expect(info.contextWindow).toBe(maxContextWindow) + // Other properties should remain unchanged + expect(info.maxTokens).toBe(qwenCodeModels["qwen3-coder-flash"].maxTokens) + expect(info.description).toBe(qwenCodeModels["qwen3-coder-flash"].description) + }) + }) +}) diff --git a/src/api/providers/qwen-code.ts b/src/api/providers/qwen-code.ts index d930d9dfc7..5f1d53cd39 100644 --- a/src/api/providers/qwen-code.ts +++ b/src/api/providers/qwen-code.ts @@ -30,6 +30,7 @@ interface QwenOAuthCredentials { interface QwenCodeHandlerOptions extends ApiHandlerOptions { qwenCodeOauthPath?: string + qwenCodeMaxContextWindow?: number } function getQwenCachedCredentialPath(customPath?: string): string { @@ -286,7 +287,16 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan override getModel(): { id: string; info: ModelInfo } { const id = this.options.apiModelId ?? qwenCodeDefaultModelId - const info = qwenCodeModels[id as keyof typeof qwenCodeModels] || qwenCodeModels[qwenCodeDefaultModelId] + let info = qwenCodeModels[id as keyof typeof qwenCodeModels] || qwenCodeModels[qwenCodeDefaultModelId] + + // Apply custom context window limit if configured + if (this.options.qwenCodeMaxContextWindow && this.options.qwenCodeMaxContextWindow > 0) { + info = { + ...info, + contextWindow: Math.min(info.contextWindow, this.options.qwenCodeMaxContextWindow), + } + } + return { id, info } } From 22148e4bc90b593c0d6806240df4a2733780d25f Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 2 Sep 2025 19:57:08 +0000 Subject: [PATCH 2/2] fix: add explicit ModelInfo type to resolve TypeScript error --- src/api/providers/qwen-code.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/providers/qwen-code.ts b/src/api/providers/qwen-code.ts index 5f1d53cd39..203b6b5c0a 100644 --- a/src/api/providers/qwen-code.ts +++ b/src/api/providers/qwen-code.ts @@ -287,7 +287,8 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan override getModel(): { id: string; info: ModelInfo } { const id = this.options.apiModelId ?? qwenCodeDefaultModelId - let info = qwenCodeModels[id as keyof typeof qwenCodeModels] || qwenCodeModels[qwenCodeDefaultModelId] + let info: ModelInfo = + qwenCodeModels[id as keyof typeof qwenCodeModels] || qwenCodeModels[qwenCodeDefaultModelId] // Apply custom context window limit if configured if (this.options.qwenCodeMaxContextWindow && this.options.qwenCodeMaxContextWindow > 0) {