mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 08:47:44 +00:00
fix: enhance 429 error handling and fix failed cases
This commit is contained in:
@@ -213,7 +213,7 @@ describe('simple-mcp-server', () => {
|
|||||||
it('should add two numbers', async () => {
|
it('should add two numbers', async () => {
|
||||||
// Test directory is already set up in before hook
|
// Test directory is already set up in before hook
|
||||||
// Just run the command - MCP server config is in settings.json
|
// Just run the command - MCP server config is in settings.json
|
||||||
const output = await rig.run('add 5 and 10');
|
const output = await rig.run('add 5 and 10, use tool if you can.');
|
||||||
|
|
||||||
const foundToolCall = await rig.waitForToolCall('add');
|
const foundToolCall = await rig.waitForToolCall('add');
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import type {
|
|||||||
CLIControlInitializeRequest,
|
CLIControlInitializeRequest,
|
||||||
CLIControlSetModelRequest,
|
CLIControlSetModelRequest,
|
||||||
} from '../../types.js';
|
} from '../../types.js';
|
||||||
|
import { CommandService } from '../../../services/CommandService.js';
|
||||||
|
import { BuiltinCommandLoader } from '../../../services/BuiltinCommandLoader.js';
|
||||||
|
|
||||||
export class SystemController extends BaseController {
|
export class SystemController extends BaseController {
|
||||||
/**
|
/**
|
||||||
@@ -141,31 +143,10 @@ export class SystemController extends BaseController {
|
|||||||
can_set_permission_mode:
|
can_set_permission_mode:
|
||||||
typeof this.context.config.setApprovalMode === 'function',
|
typeof this.context.config.setApprovalMode === 'function',
|
||||||
can_set_model: typeof this.context.config.setModel === 'function',
|
can_set_model: typeof this.context.config.setModel === 'function',
|
||||||
|
/* TODO: sdkMcpServers support */
|
||||||
|
can_handle_mcp_message: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if MCP message handling is available
|
|
||||||
try {
|
|
||||||
const mcpProvider = this.context.config as unknown as {
|
|
||||||
getMcpServers?: () => Record<string, unknown> | undefined;
|
|
||||||
};
|
|
||||||
if (typeof mcpProvider.getMcpServers === 'function') {
|
|
||||||
const servers = mcpProvider.getMcpServers();
|
|
||||||
capabilities['can_handle_mcp_message'] = Boolean(
|
|
||||||
servers && Object.keys(servers).length > 0,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
capabilities['can_handle_mcp_message'] = false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (this.context.debugMode) {
|
|
||||||
console.error(
|
|
||||||
'[SystemController] Failed to determine MCP capability:',
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
capabilities['can_handle_mcp_message'] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,27 +221,45 @@ export class SystemController extends BaseController {
|
|||||||
/**
|
/**
|
||||||
* Handle supported_commands request
|
* Handle supported_commands request
|
||||||
*
|
*
|
||||||
* Returns list of supported control commands
|
* Returns list of supported slash commands loaded dynamically
|
||||||
*
|
|
||||||
* Note: This list should match the ControlRequestType enum in
|
|
||||||
* packages/sdk/typescript/src/types/controlRequests.ts
|
|
||||||
*/
|
*/
|
||||||
private async handleSupportedCommands(): Promise<Record<string, unknown>> {
|
private async handleSupportedCommands(): Promise<Record<string, unknown>> {
|
||||||
const commands = [
|
const slashCommands = await this.loadSlashCommandNames();
|
||||||
'initialize',
|
|
||||||
'interrupt',
|
|
||||||
'set_model',
|
|
||||||
'supported_commands',
|
|
||||||
'can_use_tool',
|
|
||||||
'set_permission_mode',
|
|
||||||
'mcp_message',
|
|
||||||
'mcp_server_status',
|
|
||||||
'hook_callback',
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subtype: 'supported_commands',
|
subtype: 'supported_commands',
|
||||||
commands,
|
commands: slashCommands,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load slash command names using CommandService
|
||||||
|
*
|
||||||
|
* @returns Promise resolving to array of slash command names
|
||||||
|
*/
|
||||||
|
private async loadSlashCommandNames(): Promise<string[]> {
|
||||||
|
const controller = new AbortController();
|
||||||
|
try {
|
||||||
|
const service = await CommandService.create(
|
||||||
|
[new BuiltinCommandLoader(this.context.config)],
|
||||||
|
controller.signal,
|
||||||
|
);
|
||||||
|
const names = new Set<string>();
|
||||||
|
const commands = service.getCommands();
|
||||||
|
for (const command of commands) {
|
||||||
|
names.add(command.name);
|
||||||
|
}
|
||||||
|
return Array.from(names).sort();
|
||||||
|
} catch (error) {
|
||||||
|
if (this.context.debugMode) {
|
||||||
|
console.error(
|
||||||
|
'[SystemController] Failed to load slash commands:',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} finally {
|
||||||
|
controller.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import type {
|
|||||||
ServerGeminiStreamEvent,
|
ServerGeminiStreamEvent,
|
||||||
TaskResultDisplay,
|
TaskResultDisplay,
|
||||||
} from '@qwen-code/qwen-code-core';
|
} from '@qwen-code/qwen-code-core';
|
||||||
import { GeminiEventType, ToolErrorType } from '@qwen-code/qwen-code-core';
|
import {
|
||||||
|
GeminiEventType,
|
||||||
|
ToolErrorType,
|
||||||
|
parseAndFormatApiError,
|
||||||
|
} from '@qwen-code/qwen-code-core';
|
||||||
import type { Part, GenerateContentResponseUsageMetadata } from '@google/genai';
|
import type { Part, GenerateContentResponseUsageMetadata } from '@google/genai';
|
||||||
import type {
|
import type {
|
||||||
CLIAssistantMessage,
|
CLIAssistantMessage,
|
||||||
@@ -600,6 +604,18 @@ export abstract class BaseJsonOutputAdapter {
|
|||||||
}
|
}
|
||||||
this.finalizePendingBlocks(state, null);
|
this.finalizePendingBlocks(state, null);
|
||||||
break;
|
break;
|
||||||
|
case GeminiEventType.Error: {
|
||||||
|
// Format the error message using parseAndFormatApiError for consistency
|
||||||
|
// with interactive mode error display
|
||||||
|
const errorText = parseAndFormatApiError(
|
||||||
|
event.value.error,
|
||||||
|
this.config.getContentGeneratorConfig()?.authType,
|
||||||
|
undefined,
|
||||||
|
this.config.getModel(),
|
||||||
|
);
|
||||||
|
this.appendText(state, errorText, null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ function createConfig(overrides: ConfigOverrides = {}): Config {
|
|||||||
getDebugMode: () => false,
|
getDebugMode: () => false,
|
||||||
getApprovalMode: () => 'auto',
|
getApprovalMode: () => 'auto',
|
||||||
getOutputFormat: () => 'stream-json',
|
getOutputFormat: () => 'stream-json',
|
||||||
|
initialize: vi.fn(),
|
||||||
};
|
};
|
||||||
return { ...base, ...overrides } as unknown as Config;
|
return { ...base, ...overrides } as unknown as Config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
OutputFormat,
|
OutputFormat,
|
||||||
InputFormat,
|
InputFormat,
|
||||||
uiTelemetryService,
|
uiTelemetryService,
|
||||||
|
parseAndFormatApiError,
|
||||||
} from '@qwen-code/qwen-code-core';
|
} from '@qwen-code/qwen-code-core';
|
||||||
import type { Content, Part, PartListUnion } from '@google/genai';
|
import type { Content, Part, PartListUnion } from '@google/genai';
|
||||||
import type { CLIUserMessage, PermissionMode } from './nonInteractive/types.js';
|
import type { CLIUserMessage, PermissionMode } from './nonInteractive/types.js';
|
||||||
@@ -210,6 +211,15 @@ export async function runNonInteractive(
|
|||||||
process.stdout.write(event.value);
|
process.stdout.write(event.value);
|
||||||
} else if (event.type === GeminiEventType.ToolCallRequest) {
|
} else if (event.type === GeminiEventType.ToolCallRequest) {
|
||||||
toolCallRequests.push(event.value);
|
toolCallRequests.push(event.value);
|
||||||
|
} else if (event.type === GeminiEventType.Error) {
|
||||||
|
// Format and output the error message for text mode
|
||||||
|
const errorText = parseAndFormatApiError(
|
||||||
|
event.value.error,
|
||||||
|
config.getContentGeneratorConfig()?.authType,
|
||||||
|
undefined,
|
||||||
|
config.getModel(),
|
||||||
|
);
|
||||||
|
process.stderr.write(`${errorText}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ execSync('tsc --project tsconfig.build.json', {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
execSync(
|
execSync(
|
||||||
'npx dts-bundle-generator --project tsconfig.build.json -o dist/index.d.ts src/index.ts --no-check',
|
'npx dts-bundle-generator --project tsconfig.build.json -o dist/index.d.ts src/index.ts',
|
||||||
{
|
{
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
cwd: rootDir,
|
cwd: rootDir,
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
testTimeout: testTimeoutMs,
|
testTimeout: testTimeoutMs,
|
||||||
hookTimeout: 10000,
|
hookTimeout: 10000,
|
||||||
globalSetup: './test/e2e/globalSetup.ts',
|
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
Reference in New Issue
Block a user