mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
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:
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -14,8 +14,7 @@
|
|||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"env": {
|
"env": {
|
||||||
"GEMINI_SANDBOX": "false",
|
"GEMINI_SANDBOX": "false"
|
||||||
"DEBUG": "1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,12 +119,14 @@ 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**
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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 = {
|
||||||
@@ -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,
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user