fix: update timeout settings and default logging level in SDK

This commit is contained in:
mingholy.lmh
2025-12-06 12:27:16 +08:00
parent ab228c682f
commit bf6abf7752
4 changed files with 32 additions and 21 deletions

View File

@@ -59,7 +59,7 @@ Creates a new query session with the Qwen Code.
| `model` | `string` | - | The AI model to use (e.g., `'qwen-max'`, `'qwen-plus'`, `'qwen-turbo'`). Takes precedence over `OPENAI_MODEL` and `QWEN_MODEL` environment variables. | | `model` | `string` | - | The AI model to use (e.g., `'qwen-max'`, `'qwen-plus'`, `'qwen-turbo'`). Takes precedence over `OPENAI_MODEL` and `QWEN_MODEL` environment variables. |
| `pathToQwenExecutable` | `string` | Auto-detected | Path to the Qwen Code executable. Supports multiple formats: `'qwen'` (native binary from PATH), `'/path/to/qwen'` (explicit path), `'/path/to/cli.js'` (Node.js bundle), `'node:/path/to/cli.js'` (force Node.js runtime), `'bun:/path/to/cli.js'` (force Bun runtime). If not provided, auto-detects from: `QWEN_CODE_CLI_PATH` env var, `~/.volta/bin/qwen`, `~/.npm-global/bin/qwen`, `/usr/local/bin/qwen`, `~/.local/bin/qwen`, `~/node_modules/.bin/qwen`, `~/.yarn/bin/qwen`. | | `pathToQwenExecutable` | `string` | Auto-detected | Path to the Qwen Code executable. Supports multiple formats: `'qwen'` (native binary from PATH), `'/path/to/qwen'` (explicit path), `'/path/to/cli.js'` (Node.js bundle), `'node:/path/to/cli.js'` (force Node.js runtime), `'bun:/path/to/cli.js'` (force Bun runtime). If not provided, auto-detects from: `QWEN_CODE_CLI_PATH` env var, `~/.volta/bin/qwen`, `~/.npm-global/bin/qwen`, `/usr/local/bin/qwen`, `~/.local/bin/qwen`, `~/node_modules/.bin/qwen`, `~/.yarn/bin/qwen`. |
| `permissionMode` | `'default' \| 'plan' \| 'auto-edit' \| 'yolo'` | `'default'` | Permission mode controlling tool execution approval. See [Permission Modes](#permission-modes) for details. | | `permissionMode` | `'default' \| 'plan' \| 'auto-edit' \| 'yolo'` | `'default'` | Permission mode controlling tool execution approval. See [Permission Modes](#permission-modes) for details. |
| `canUseTool` | `CanUseTool` | - | Custom permission handler for tool execution approval. Invoked when a tool requires confirmation. Must respond within 30 seconds or the request will be auto-denied. See [Custom Permission Handler](#custom-permission-handler). | | `canUseTool` | `CanUseTool` | - | Custom permission handler for tool execution approval. Invoked when a tool requires confirmation. Must respond within 60 seconds or the request will be auto-denied. See [Custom Permission Handler](#custom-permission-handler). |
| `env` | `Record<string, string>` | - | Environment variables to pass to the Qwen Code process. Merged with the current process environment. | | `env` | `Record<string, string>` | - | Environment variables to pass to the Qwen Code process. Merged with the current process environment. |
| `mcpServers` | `Record<string, McpServerConfig>` | - | MCP (Model Context Protocol) servers to connect. Supports external servers (stdio/SSE/HTTP) and SDK-embedded servers. External servers are configured with transport options like `command`, `args`, `url`, `httpUrl`, etc. SDK servers use `{ type: 'sdk', name: string, instance: Server }`. | | `mcpServers` | `Record<string, McpServerConfig>` | - | MCP (Model Context Protocol) servers to connect. Supports external servers (stdio/SSE/HTTP) and SDK-embedded servers. External servers are configured with transport options like `command`, `args`, `url`, `httpUrl`, etc. SDK servers use `{ type: 'sdk', name: string, instance: Server }`. |
| `abortController` | `AbortController` | - | Controller to cancel the query session. Call `abortController.abort()` to terminate the session and cleanup resources. | | `abortController` | `AbortController` | - | Controller to cancel the query session. Call `abortController.abort()` to terminate the session and cleanup resources. |
@@ -76,12 +76,12 @@ Creates a new query session with the Qwen Code.
The SDK enforces the following default timeouts: The SDK enforces the following default timeouts:
| Timeout | Default | Description | | Timeout | Default | Description |
| ---------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------- | | ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `canUseTool` | 30 seconds | Maximum time for `canUseTool` callback to respond. If exceeded, the tool request is auto-denied. | | `canUseTool` | 1 minute | Maximum time for `canUseTool` callback to respond. If exceeded, the tool request is auto-denied. |
| `mcpRequest` | 1 minute | Maximum time for SDK MCP tool calls to complete. | | `mcpRequest` | 1 minute | Maximum time for SDK MCP tool calls to complete. |
| `controlRequest` | 30 seconds | Maximum time for control operations like `initialize()`, `setModel()`, `setPermissionMode()`, and `interrupt()` to complete. | | `controlRequest` | 1 minute | Maximum time for control operations like `initialize()`, `setModel()`, `setPermissionMode()`, and `interrupt()` to complete. |
| `streamClose` | 1 minute | Maximum time to wait for initialization to complete before closing CLI stdin in multi-turn mode with SDK MCP servers. | | `streamClose` | 1 minute | Maximum time to wait for initialization to complete before closing CLI stdin in multi-turn mode with SDK MCP servers. |
You can customize these timeouts via the `timeout` option: You can customize these timeouts via the `timeout` option:

View File

@@ -5,9 +5,9 @@
* Implements AsyncIterator protocol for message consumption. * Implements AsyncIterator protocol for message consumption.
*/ */
const DEFAULT_CAN_USE_TOOL_TIMEOUT = 30_000; const DEFAULT_CAN_USE_TOOL_TIMEOUT = 60_000;
const DEFAULT_MCP_REQUEST_TIMEOUT = 60_000; const DEFAULT_MCP_REQUEST_TIMEOUT = 60_000;
const DEFAULT_CONTROL_REQUEST_TIMEOUT = 30_000; const DEFAULT_CONTROL_REQUEST_TIMEOUT = 60_000;
const DEFAULT_STREAM_CLOSE_TIMEOUT = 60_000; const DEFAULT_STREAM_CLOSE_TIMEOUT = 60_000;
import { randomUUID } from 'node:crypto'; import { randomUUID } from 'node:crypto';
@@ -434,8 +434,9 @@ export class Query implements AsyncIterable<SDKMessage> {
try { try {
const canUseToolTimeout = const canUseToolTimeout =
this.options.timeout?.canUseTool ?? DEFAULT_CAN_USE_TOOL_TIMEOUT; this.options.timeout?.canUseTool ?? DEFAULT_CAN_USE_TOOL_TIMEOUT;
let timeoutId: NodeJS.Timeout | undefined;
const timeoutPromise = new Promise<never>((_, reject) => { const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout( timeoutId = setTimeout(
() => reject(new Error('Permission callback timeout')), () => reject(new Error('Permission callback timeout')),
canUseToolTimeout, canUseToolTimeout,
); );
@@ -451,6 +452,10 @@ export class Query implements AsyncIterable<SDKMessage> {
timeoutPromise, timeoutPromise,
]); ]);
if (timeoutId) {
clearTimeout(timeoutId);
}
if (result.behavior === 'allow') { if (result.behavior === 'allow') {
return { return {
behavior: 'allow', behavior: 'allow',
@@ -789,14 +794,20 @@ export class Query implements AsyncIterable<SDKMessage> {
) { ) {
const streamCloseTimeout = const streamCloseTimeout =
this.options.timeout?.streamClose ?? DEFAULT_STREAM_CLOSE_TIMEOUT; this.options.timeout?.streamClose ?? DEFAULT_STREAM_CLOSE_TIMEOUT;
await Promise.race([ let timeoutId: NodeJS.Timeout | undefined;
this.firstResultReceivedPromise,
new Promise<void>((resolve) => { const timeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => { timeoutId = setTimeout(() => {
resolve(); logger.info('streamCloseTimeout resolved');
}, streamCloseTimeout); resolve();
}), }, streamCloseTimeout);
]); });
await Promise.race([this.firstResultReceivedPromise, timeoutPromise]);
if (timeoutId) {
clearTimeout(timeoutId);
}
} }
this.endInput(); this.endInput();

View File

@@ -316,7 +316,7 @@ export interface QueryOptions {
/** /**
* Logging level for the SDK. * Logging level for the SDK.
* Controls the verbosity of log messages output by the SDK. * Controls the verbosity of log messages output by the SDK.
* @default 'info' * @default 'error'
*/ */
logLevel?: 'debug' | 'info' | 'warn' | 'error'; logLevel?: 'debug' | 'info' | 'warn' | 'error';

View File

@@ -22,7 +22,7 @@ const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
export class SdkLogger { export class SdkLogger {
private static config: LoggerConfig = {}; private static config: LoggerConfig = {};
private static effectiveLevel: LogLevel = 'info'; private static effectiveLevel: LogLevel = 'error';
static configure(config: LoggerConfig): void { static configure(config: LoggerConfig): void {
this.config = config; this.config = config;
@@ -47,7 +47,7 @@ export class SdkLogger {
return 'debug'; return 'debug';
} }
return 'info'; return 'error';
} }
private static isValidLogLevel(level: string): boolean { private static isValidLogLevel(level: string): boolean {