From ce632725b07f6fb0f289dd7dace9f321047a5fcc Mon Sep 17 00:00:00 2001 From: "mingholy.lmh" Date: Fri, 8 Aug 2025 11:44:38 +0800 Subject: [PATCH] refactor: re-organize Qwen related code files. Co-authored-by: tanzhenxin Co-authored-by: pomelo-nwu --- .vscode/launch.json | 3 +-- README.md | 8 +++++-- packages/cli/src/ui/hooks/useQwenAuth.ts | 7 +----- packages/core/src/core/contentGenerator.ts | 6 +++-- packages/core/src/index.ts | 2 +- .../qwenContentGenerator.test.ts | 22 ++++++++++++++----- .../{core => qwen}/qwenContentGenerator.ts | 4 ++-- .../{code_assist => qwen}/qwenOAuth2.test.ts | 0 .../src/{code_assist => qwen}/qwenOAuth2.ts | 21 ++++++------------ 9 files changed, 39 insertions(+), 34 deletions(-) rename packages/core/src/{core => qwen}/qwenContentGenerator.test.ts (98%) rename packages/core/src/{core => qwen}/qwenContentGenerator.ts (98%) rename packages/core/src/{code_assist => qwen}/qwenOAuth2.test.ts (100%) rename packages/core/src/{code_assist => qwen}/qwenOAuth2.ts (98%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 1068682a..496e7233 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,8 +14,7 @@ "cwd": "${workspaceFolder}", "console": "integratedTerminal", "env": { - "GEMINI_SANDBOX": "false", - "DEBUG": "1" + "GEMINI_SANDBOX": "false" } }, { diff --git a/README.md b/README.md index 924f04ff..6590d61b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Qwen Code is a powerful command-line AI workflow tool adapted from [**Gemini CLI Get started with Qwen Code at no cost using any of these free options: ### đŸ”Ĩ Qwen OAuth (Recommended) + - **2,000 requests per day** with no token limits - **60 requests per minute** rate limit - Simply run `qwen` and authenticate with your qwen.ai account @@ -29,6 +30,7 @@ Get started with Qwen Code at no cost using any of these free options: - No API key setup required ### 🌏 Regional Free Tiers + - **Mainland China**: ModelScope offers **2,000 free API calls per day** - **International**: OpenRouter provides **up to 1,000 free API calls per day** worldwide @@ -117,15 +119,17 @@ qwen ``` **What happens:** -1. **Instant Setup**: CLI opens your browser automatically + +1. **Instant Setup**: CLI opens your browser automatically 2. **One-Click Login**: Authenticate with your qwen.ai account 3. **Automatic Management**: Credentials cached locally for future use 4. **No Configuration**: Zero setup required - just start coding! **Free Tier Benefits:** + - ✅ **2,000 requests/day** (no token counting needed) - ✅ **60 requests/minute** rate limit -- ✅ **Automatic credential refresh** +- ✅ **Automatic credential refresh** - ✅ **Zero cost** for individual users - â„šī¸ **Note**: Model fallback may occur to maintain service quality diff --git a/packages/cli/src/ui/hooks/useQwenAuth.ts b/packages/cli/src/ui/hooks/useQwenAuth.ts index c7caca30..fca21cbd 100644 --- a/packages/cli/src/ui/hooks/useQwenAuth.ts +++ b/packages/cli/src/ui/hooks/useQwenAuth.ts @@ -65,12 +65,7 @@ export const useQwenAuth = ( })); // Set up event listeners - const handleDeviceAuth = (deviceAuth: { - verification_uri: string; - verification_uri_complete: string; - user_code: string; - expires_in: number; - }) => { + const handleDeviceAuth = (deviceAuth: DeviceAuthorizationInfo) => { setQwenAuthState((prev) => ({ ...prev, deviceAuth: { diff --git a/packages/core/src/core/contentGenerator.ts b/packages/core/src/core/contentGenerator.ts index 3836b29a..c4a934e7 100644 --- a/packages/core/src/core/contentGenerator.ts +++ b/packages/core/src/core/contentGenerator.ts @@ -201,9 +201,11 @@ export async function createContentGenerator( // Import required classes dynamically const { getQwenOAuthClient: getQwenOauthClient } = await import( - '../code_assist/qwenOAuth2.js' + '../qwen/qwenOAuth2.js' + ); + const { QwenContentGenerator } = await import( + '../qwen/qwenContentGenerator.js' ); - const { QwenContentGenerator } = await import('./qwenContentGenerator.js'); try { // Get the Qwen OAuth client (now includes integrated token management) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 233a0ee4..57ed8672 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -21,7 +21,7 @@ export * from './core/nonInteractiveToolExecutor.js'; export * from './code_assist/codeAssist.js'; export * from './code_assist/oauth2.js'; -export * from './code_assist/qwenOAuth2.js'; +export * from './qwen/qwenOAuth2.js'; export * from './code_assist/server.js'; export * from './code_assist/types.js'; diff --git a/packages/core/src/core/qwenContentGenerator.test.ts b/packages/core/src/qwen/qwenContentGenerator.test.ts similarity index 98% rename from packages/core/src/core/qwenContentGenerator.test.ts rename to packages/core/src/qwen/qwenContentGenerator.test.ts index 131f1769..a54a4606 100644 --- a/packages/core/src/core/qwenContentGenerator.test.ts +++ b/packages/core/src/qwen/qwenContentGenerator.test.ts @@ -7,9 +7,9 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { IQwenOAuth2Client, - QwenCredentials, - ErrorData, -} from '../code_assist/qwenOAuth2.js'; + type QwenCredentials, + type ErrorData, +} from './qwenOAuth2.js'; import { GenerateContentParameters, GenerateContentResponse, @@ -23,7 +23,7 @@ import { QwenContentGenerator } from './qwenContentGenerator.js'; import { Config } from '../config/config.js'; // Mock the OpenAIContentGenerator parent class -vi.mock('./openaiContentGenerator.js', () => ({ +vi.mock('../core/openaiContentGenerator.js', () => ({ OpenAIContentGenerator: class { client: { apiKey: string; @@ -106,7 +106,19 @@ describe('QwenContentGenerator', () => { vi.clearAllMocks(); // Mock Config - mockConfig = {} as Config; + mockConfig = { + getContentGeneratorConfig: vi.fn().mockReturnValue({ + authType: 'qwen', + enableOpenAILogging: false, + timeout: 120000, + maxRetries: 3, + samplingParams: { + temperature: 0.7, + max_tokens: 1000, + top_p: 0.9, + }, + }), + } as unknown as Config; // Mock QwenOAuth2Client mockQwenClient = { diff --git a/packages/core/src/core/qwenContentGenerator.ts b/packages/core/src/qwen/qwenContentGenerator.ts similarity index 98% rename from packages/core/src/core/qwenContentGenerator.ts rename to packages/core/src/qwen/qwenContentGenerator.ts index 14fd174b..770b8328 100644 --- a/packages/core/src/core/qwenContentGenerator.ts +++ b/packages/core/src/qwen/qwenContentGenerator.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { OpenAIContentGenerator } from './openaiContentGenerator.js'; +import { OpenAIContentGenerator } from '../core/openaiContentGenerator.js'; import { IQwenOAuth2Client, type TokenRefreshData, type ErrorData, isErrorResponse, -} from '../code_assist/qwenOAuth2.js'; +} from './qwenOAuth2.js'; import { Config } from '../config/config.js'; import { GenerateContentParameters, diff --git a/packages/core/src/code_assist/qwenOAuth2.test.ts b/packages/core/src/qwen/qwenOAuth2.test.ts similarity index 100% rename from packages/core/src/code_assist/qwenOAuth2.test.ts rename to packages/core/src/qwen/qwenOAuth2.test.ts diff --git a/packages/core/src/code_assist/qwenOAuth2.ts b/packages/core/src/qwen/qwenOAuth2.ts similarity index 98% rename from packages/core/src/code_assist/qwenOAuth2.ts rename to packages/core/src/qwen/qwenOAuth2.ts index 332aacd8..45f91125 100644 --- a/packages/core/src/code_assist/qwenOAuth2.ts +++ b/packages/core/src/qwen/qwenOAuth2.ts @@ -22,7 +22,6 @@ const QWEN_OAUTH_DEVICE_CODE_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/de const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`; // OAuth Client Configuration -// const QWEN_OAUTH_CLIENT_ID = '93a239d6ed36412c8c442e91b60fa305'; const QWEN_OAUTH_CLIENT_ID = 'f0304373b74a44d2b584a3fb70ca9e56'; const QWEN_OAUTH_SCOPE = 'openid profile email model.completion'; @@ -332,8 +331,6 @@ export class QwenOAuth2Client implements IQwenOAuth2Client { try { const errorData = (await response.json()) as ErrorData; - console.log(errorData.error); - // According to OAuth RFC 8628, handle standard polling responses if ( response.status === 400 && @@ -567,14 +564,13 @@ async function authWithQwenDeviceFlow( // Emit device authorization event for UI integration immediately qwenOAuth2Events.emit(QwenOAuth2Event.AuthUri, deviceAuth); - console.log('\n=== Qwen OAuth Device Authorization ==='); - console.log( - `Please visit the following URL on your phone or browser for authorization:`, - ); - console.log(`\n${deviceAuth.verification_uri_complete}\n`); - const showFallbackMessage = () => { - // Fallback message for console output + console.log('\n=== Qwen OAuth Device Authorization ==='); + console.log( + 'Please visit the following URL in your browser to authorize:', + ); + console.log(`\n${deviceAuth.verification_uri_complete}\n`); + console.log('Waiting for authorization to complete...\n'); }; // If browser launch is not suppressed, try to open the URL @@ -645,8 +641,7 @@ async function authWithQwenDeviceFlow( token_type: tokenData.token_type, resource_url: tokenData.resource_url, expiry_date: tokenData.expires_in - ? /* ts-ignore */ - Date.now() + (tokenData.expires_in ?? 1) * 1000 + ? Date.now() + tokenData.expires_in * 1000 : undefined, }; @@ -670,8 +665,6 @@ async function authWithQwenDeviceFlow( if (isDeviceTokenPending(tokenResponse)) { const pendingData = tokenResponse as DeviceTokenPendingData; - console.log(pendingData); - // Handle slow_down error by increasing poll interval if (pendingData.slowDown) { pollInterval = Math.min(pollInterval * 1.5, 10000); // Increase by 50%, max 10 seconds