refactor: re-organize Qwen related code files.

Co-authored-by: tanzhenxin <tanzhenxing1987@gmail.com>
Co-authored-by: pomelo-nwu <czynwu@outlook.com>
This commit is contained in:
mingholy.lmh
2025-08-08 11:44:38 +08:00
parent ea7dcf8347
commit ce632725b0
9 changed files with 39 additions and 34 deletions

3
.vscode/launch.json vendored
View File

@@ -14,8 +14,7 @@
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"console": "integratedTerminal", "console": "integratedTerminal",
"env": { "env": {
"GEMINI_SANDBOX": "false", "GEMINI_SANDBOX": "false"
"DEBUG": "1"
} }
}, },
{ {

View File

@@ -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: Get started with Qwen Code at no cost using any of these free options:
### 🔥 Qwen OAuth (Recommended) ### 🔥 Qwen OAuth (Recommended)
- **2,000 requests per day** with no token limits - **2,000 requests per day** with no token limits
- **60 requests per minute** rate limit - **60 requests per minute** rate limit
- Simply run `qwen` and authenticate with your qwen.ai account - 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 - No API key setup required
### 🌏 Regional Free Tiers ### 🌏 Regional Free Tiers
- **Mainland China**: ModelScope offers **2,000 free API calls per day** - **Mainland China**: ModelScope offers **2,000 free API calls per day**
- **International**: OpenRouter provides **up to 1,000 free API calls per day** worldwide - **International**: OpenRouter provides **up to 1,000 free API calls per day** worldwide
@@ -117,15 +119,17 @@ qwen
``` ```
**What happens:** **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 2. **One-Click Login**: Authenticate with your qwen.ai account
3. **Automatic Management**: Credentials cached locally for future use 3. **Automatic Management**: Credentials cached locally for future use
4. **No Configuration**: Zero setup required - just start coding! 4. **No Configuration**: Zero setup required - just start coding!
**Free Tier Benefits:** **Free Tier Benefits:**
-**2,000 requests/day** (no token counting needed) -**2,000 requests/day** (no token counting needed)
-**60 requests/minute** rate limit -**60 requests/minute** rate limit
-**Automatic credential refresh** -**Automatic credential refresh**
-**Zero cost** for individual users -**Zero cost** for individual users
- **Note**: Model fallback may occur to maintain service quality - **Note**: Model fallback may occur to maintain service quality

View File

@@ -65,12 +65,7 @@ export const useQwenAuth = (
})); }));
// Set up event listeners // Set up event listeners
const handleDeviceAuth = (deviceAuth: { const handleDeviceAuth = (deviceAuth: DeviceAuthorizationInfo) => {
verification_uri: string;
verification_uri_complete: string;
user_code: string;
expires_in: number;
}) => {
setQwenAuthState((prev) => ({ setQwenAuthState((prev) => ({
...prev, ...prev,
deviceAuth: { deviceAuth: {

View File

@@ -201,9 +201,11 @@ export async function createContentGenerator(
// Import required classes dynamically // Import required classes dynamically
const { getQwenOAuthClient: getQwenOauthClient } = await import( 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 { try {
// Get the Qwen OAuth client (now includes integrated token management) // Get the Qwen OAuth client (now includes integrated token management)

View File

@@ -21,7 +21,7 @@ export * from './core/nonInteractiveToolExecutor.js';
export * from './code_assist/codeAssist.js'; export * from './code_assist/codeAssist.js';
export * from './code_assist/oauth2.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/server.js';
export * from './code_assist/types.js'; export * from './code_assist/types.js';

View File

@@ -7,9 +7,9 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { import {
IQwenOAuth2Client, IQwenOAuth2Client,
QwenCredentials, type QwenCredentials,
ErrorData, type ErrorData,
} from '../code_assist/qwenOAuth2.js'; } from './qwenOAuth2.js';
import { import {
GenerateContentParameters, GenerateContentParameters,
GenerateContentResponse, GenerateContentResponse,
@@ -23,7 +23,7 @@ import { QwenContentGenerator } from './qwenContentGenerator.js';
import { Config } from '../config/config.js'; import { Config } from '../config/config.js';
// Mock the OpenAIContentGenerator parent class // Mock the OpenAIContentGenerator parent class
vi.mock('./openaiContentGenerator.js', () => ({ vi.mock('../core/openaiContentGenerator.js', () => ({
OpenAIContentGenerator: class { OpenAIContentGenerator: class {
client: { client: {
apiKey: string; apiKey: string;
@@ -106,7 +106,19 @@ describe('QwenContentGenerator', () => {
vi.clearAllMocks(); vi.clearAllMocks();
// Mock Config // 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 // Mock QwenOAuth2Client
mockQwenClient = { mockQwenClient = {

View File

@@ -4,13 +4,13 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import { OpenAIContentGenerator } from './openaiContentGenerator.js'; import { OpenAIContentGenerator } from '../core/openaiContentGenerator.js';
import { import {
IQwenOAuth2Client, IQwenOAuth2Client,
type TokenRefreshData, type TokenRefreshData,
type ErrorData, type ErrorData,
isErrorResponse, isErrorResponse,
} from '../code_assist/qwenOAuth2.js'; } from './qwenOAuth2.js';
import { Config } from '../config/config.js'; import { Config } from '../config/config.js';
import { import {
GenerateContentParameters, GenerateContentParameters,

View File

@@ -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`; const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`;
// OAuth Client Configuration // OAuth Client Configuration
// const QWEN_OAUTH_CLIENT_ID = '93a239d6ed36412c8c442e91b60fa305';
const QWEN_OAUTH_CLIENT_ID = 'f0304373b74a44d2b584a3fb70ca9e56'; const QWEN_OAUTH_CLIENT_ID = 'f0304373b74a44d2b584a3fb70ca9e56';
const QWEN_OAUTH_SCOPE = 'openid profile email model.completion'; const QWEN_OAUTH_SCOPE = 'openid profile email model.completion';
@@ -332,8 +331,6 @@ export class QwenOAuth2Client implements IQwenOAuth2Client {
try { try {
const errorData = (await response.json()) as ErrorData; const errorData = (await response.json()) as ErrorData;
console.log(errorData.error);
// According to OAuth RFC 8628, handle standard polling responses // According to OAuth RFC 8628, handle standard polling responses
if ( if (
response.status === 400 && response.status === 400 &&
@@ -567,14 +564,13 @@ async function authWithQwenDeviceFlow(
// Emit device authorization event for UI integration immediately // Emit device authorization event for UI integration immediately
qwenOAuth2Events.emit(QwenOAuth2Event.AuthUri, deviceAuth); 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 = () => { 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 // If browser launch is not suppressed, try to open the URL
@@ -645,8 +641,7 @@ async function authWithQwenDeviceFlow(
token_type: tokenData.token_type, token_type: tokenData.token_type,
resource_url: tokenData.resource_url, resource_url: tokenData.resource_url,
expiry_date: tokenData.expires_in expiry_date: tokenData.expires_in
? /* ts-ignore */ ? Date.now() + tokenData.expires_in * 1000
Date.now() + (tokenData.expires_in ?? 1) * 1000
: undefined, : undefined,
}; };
@@ -670,8 +665,6 @@ async function authWithQwenDeviceFlow(
if (isDeviceTokenPending(tokenResponse)) { if (isDeviceTokenPending(tokenResponse)) {
const pendingData = tokenResponse as DeviceTokenPendingData; const pendingData = tokenResponse as DeviceTokenPendingData;
console.log(pendingData);
// Handle slow_down error by increasing poll interval // Handle slow_down error by increasing poll interval
if (pendingData.slowDown) { if (pendingData.slowDown) {
pollInterval = Math.min(pollInterval * 1.5, 10000); // Increase by 50%, max 10 seconds pollInterval = Math.min(pollInterval * 1.5, 10000); // Increase by 50%, max 10 seconds