mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +00:00
Merge branch 'main' into feat/debug-logging-refactor
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
commit
135df54f27
378 changed files with 40051 additions and 6776 deletions
|
|
@ -4,11 +4,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type {
|
||||
Config,
|
||||
ToolCallRequestInfo,
|
||||
ToolResultDisplay,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
import type { Config, ToolCallRequestInfo } from '@qwen-code/qwen-code-core';
|
||||
import { isSlashCommand } from './ui/utils/commandUtils.js';
|
||||
import type { LoadedSettings } from './config/settings.js';
|
||||
import {
|
||||
|
|
@ -56,19 +52,12 @@ import {
|
|||
async function emitNonInteractiveFinalMessage(params: {
|
||||
message: string;
|
||||
isError: boolean;
|
||||
adapter?: JsonOutputAdapterInterface;
|
||||
adapter: JsonOutputAdapterInterface;
|
||||
config: Config;
|
||||
startTimeMs: number;
|
||||
}): Promise<void> {
|
||||
const { message, isError, adapter, config } = params;
|
||||
|
||||
if (!adapter) {
|
||||
// Text output mode: write directly to stdout/stderr
|
||||
const target = isError ? process.stderr : process.stdout;
|
||||
target.write(`${message}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
// JSON output mode: emit assistant message and result
|
||||
// (systemMessage should already be emitted by caller)
|
||||
adapter.startAssistantMessage();
|
||||
|
|
@ -125,18 +114,18 @@ export async function runNonInteractive(
|
|||
): Promise<void> {
|
||||
return promptIdContext.run(prompt_id, async () => {
|
||||
// Create output adapter based on format
|
||||
let adapter: JsonOutputAdapterInterface | undefined;
|
||||
let adapter: JsonOutputAdapterInterface;
|
||||
const outputFormat = config.getOutputFormat();
|
||||
|
||||
if (options.adapter) {
|
||||
adapter = options.adapter;
|
||||
} else if (outputFormat === OutputFormat.JSON) {
|
||||
adapter = new JsonOutputAdapter(config);
|
||||
} else if (outputFormat === OutputFormat.STREAM_JSON) {
|
||||
adapter = new StreamJsonOutputAdapter(
|
||||
config,
|
||||
config.getIncludePartialMessages(),
|
||||
);
|
||||
} else {
|
||||
adapter = new JsonOutputAdapter(config);
|
||||
}
|
||||
|
||||
// Get readonly values once at the start
|
||||
|
|
@ -170,14 +159,12 @@ export async function runNonInteractive(
|
|||
process.on('SIGTERM', shutdownHandler);
|
||||
|
||||
// Emit systemMessage first (always the first message in JSON mode)
|
||||
if (adapter) {
|
||||
const systemMessage = await buildSystemMessage(
|
||||
config,
|
||||
sessionId,
|
||||
permissionMode,
|
||||
);
|
||||
adapter.emitMessage(systemMessage);
|
||||
}
|
||||
const systemMessage = await buildSystemMessage(
|
||||
config,
|
||||
sessionId,
|
||||
permissionMode,
|
||||
);
|
||||
adapter.emitMessage(systemMessage);
|
||||
|
||||
let initialPartList: PartListUnion | null = extractPartsFromUserMessage(
|
||||
options.userMessage,
|
||||
|
|
@ -283,46 +270,33 @@ export async function runNonInteractive(
|
|||
isFirstTurn = false;
|
||||
|
||||
// Start assistant message for this turn
|
||||
if (adapter) {
|
||||
adapter.startAssistantMessage();
|
||||
}
|
||||
adapter.startAssistantMessage();
|
||||
|
||||
for await (const event of responseStream) {
|
||||
if (abortController.signal.aborted) {
|
||||
handleCancellationError(config);
|
||||
}
|
||||
|
||||
if (adapter) {
|
||||
// Use adapter for all event processing
|
||||
adapter.processEvent(event);
|
||||
if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
toolCallRequests.push(event.value);
|
||||
}
|
||||
} else {
|
||||
// Text output mode - direct stdout
|
||||
if (event.type === GeminiEventType.Thought) {
|
||||
process.stdout.write(event.value.description);
|
||||
} else if (event.type === GeminiEventType.Content) {
|
||||
process.stdout.write(event.value);
|
||||
} else if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
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,
|
||||
);
|
||||
process.stderr.write(`${errorText}\n`);
|
||||
// Throw error to exit with non-zero code
|
||||
throw new Error(errorText);
|
||||
}
|
||||
// Use adapter for all event processing
|
||||
adapter.processEvent(event);
|
||||
if (event.type === GeminiEventType.ToolCallRequest) {
|
||||
toolCallRequests.push(event.value);
|
||||
}
|
||||
if (
|
||||
outputFormat === OutputFormat.TEXT &&
|
||||
event.type === GeminiEventType.Error
|
||||
) {
|
||||
const errorText = parseAndFormatApiError(
|
||||
event.value.error,
|
||||
config.getContentGeneratorConfig()?.authType,
|
||||
);
|
||||
process.stderr.write(`${errorText}\n`);
|
||||
// Throw error to exit with non-zero code
|
||||
throw new Error(errorText);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize assistant message
|
||||
if (adapter) {
|
||||
adapter.finalizeAssistantMessage();
|
||||
}
|
||||
adapter.finalizeAssistantMessage();
|
||||
totalApiDurationMs += Date.now() - apiStartTime;
|
||||
|
||||
if (toolCallRequests.length > 0) {
|
||||
|
|
@ -351,35 +325,13 @@ export async function runNonInteractive(
|
|||
: undefined;
|
||||
const taskToolProgressHandler = taskToolProgress?.handler;
|
||||
|
||||
// Create output handler for non-Task tools in text mode (for console output)
|
||||
const nonTaskOutputHandler =
|
||||
!isTaskTool && !adapter
|
||||
? (callId: string, outputChunk: ToolResultDisplay) => {
|
||||
// Print tool output to console in text mode
|
||||
if (typeof outputChunk === 'string') {
|
||||
process.stdout.write(outputChunk);
|
||||
} else if (
|
||||
outputChunk &&
|
||||
typeof outputChunk === 'object' &&
|
||||
'ansiOutput' in outputChunk
|
||||
) {
|
||||
// Handle ANSI output - just print as string for now
|
||||
process.stdout.write(String(outputChunk.ansiOutput));
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
// Combine output handlers
|
||||
const outputUpdateHandler =
|
||||
taskToolProgressHandler || nonTaskOutputHandler;
|
||||
|
||||
const toolResponse = await executeToolCall(
|
||||
config,
|
||||
finalRequestInfo,
|
||||
abortController.signal,
|
||||
outputUpdateHandler || toolCallUpdateCallback
|
||||
taskToolProgressHandler || toolCallUpdateCallback
|
||||
? {
|
||||
...(outputUpdateHandler && { outputUpdateHandler }),
|
||||
...(taskToolProgressHandler && { taskToolProgressHandler }),
|
||||
...(toolCallUpdateCallback && {
|
||||
onToolCallsUpdate: toolCallUpdateCallback,
|
||||
}),
|
||||
|
|
@ -406,9 +358,7 @@ export async function runNonInteractive(
|
|||
);
|
||||
}
|
||||
|
||||
if (adapter) {
|
||||
adapter.emitToolResult(finalRequestInfo, toolResponse);
|
||||
}
|
||||
adapter.emitToolResult(finalRequestInfo, toolResponse);
|
||||
|
||||
if (toolResponse.responseParts) {
|
||||
toolResponseParts.push(...toolResponse.responseParts);
|
||||
|
|
@ -416,51 +366,43 @@ export async function runNonInteractive(
|
|||
}
|
||||
currentMessages = [{ role: 'user', parts: toolResponseParts }];
|
||||
} else {
|
||||
// For JSON and STREAM_JSON modes, compute usage from metrics
|
||||
if (adapter) {
|
||||
const metrics = uiTelemetryService.getMetrics();
|
||||
const usage = computeUsageFromMetrics(metrics);
|
||||
// Get stats for JSON format output
|
||||
const stats =
|
||||
outputFormat === OutputFormat.JSON
|
||||
? uiTelemetryService.getMetrics()
|
||||
: undefined;
|
||||
adapter.emitResult({
|
||||
isError: false,
|
||||
durationMs: Date.now() - startTime,
|
||||
apiDurationMs: totalApiDurationMs,
|
||||
numTurns: turnCount,
|
||||
usage,
|
||||
stats,
|
||||
});
|
||||
} else {
|
||||
// Text output mode - no usage needed
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
const metrics = uiTelemetryService.getMetrics();
|
||||
const usage = computeUsageFromMetrics(metrics);
|
||||
// Get stats for JSON format output
|
||||
const stats =
|
||||
outputFormat === OutputFormat.JSON
|
||||
? uiTelemetryService.getMetrics()
|
||||
: undefined;
|
||||
adapter.emitResult({
|
||||
isError: false,
|
||||
durationMs: Date.now() - startTime,
|
||||
apiDurationMs: totalApiDurationMs,
|
||||
numTurns: turnCount,
|
||||
usage,
|
||||
stats,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// For JSON and STREAM_JSON modes, compute usage from metrics
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (adapter) {
|
||||
const metrics = uiTelemetryService.getMetrics();
|
||||
const usage = computeUsageFromMetrics(metrics);
|
||||
// Get stats for JSON format output
|
||||
const stats =
|
||||
outputFormat === OutputFormat.JSON
|
||||
? uiTelemetryService.getMetrics()
|
||||
: undefined;
|
||||
adapter.emitResult({
|
||||
isError: true,
|
||||
durationMs: Date.now() - startTime,
|
||||
apiDurationMs: totalApiDurationMs,
|
||||
numTurns: turnCount,
|
||||
errorMessage: message,
|
||||
usage,
|
||||
stats,
|
||||
});
|
||||
}
|
||||
const metrics = uiTelemetryService.getMetrics();
|
||||
const usage = computeUsageFromMetrics(metrics);
|
||||
// Get stats for JSON format output
|
||||
const stats =
|
||||
outputFormat === OutputFormat.JSON
|
||||
? uiTelemetryService.getMetrics()
|
||||
: undefined;
|
||||
adapter.emitResult({
|
||||
isError: true,
|
||||
durationMs: Date.now() - startTime,
|
||||
apiDurationMs: totalApiDurationMs,
|
||||
numTurns: turnCount,
|
||||
errorMessage: message,
|
||||
usage,
|
||||
stats,
|
||||
});
|
||||
handleError(error, config);
|
||||
} finally {
|
||||
process.stdout.removeListener('error', stdoutErrorHandler);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue