pre-release commit

This commit is contained in:
koalazf.99
2025-07-22 19:59:07 +08:00
parent c5dee4bb17
commit a9d6965bef
485 changed files with 111444 additions and 2 deletions

View File

@@ -0,0 +1,275 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
Config,
ToolCallRequestInfo,
executeToolCall,
ToolRegistry,
shutdownTelemetry,
isTelemetrySdkInitialized,
ToolResultDisplay,
} from '@qwen/qwen-code-core';
import {
Content,
Part,
FunctionCall,
GenerateContentResponse,
} from '@google/genai';
import { parseAndFormatApiError } from './ui/utils/errorParsing.js';
function getResponseText(response: GenerateContentResponse): string | null {
if (response.candidates && response.candidates.length > 0) {
const candidate = response.candidates[0];
if (
candidate.content &&
candidate.content.parts &&
candidate.content.parts.length > 0
) {
// We are running in headless mode so we don't need to return thoughts to STDOUT.
const thoughtPart = candidate.content.parts[0];
if (thoughtPart?.thought) {
return null;
}
return candidate.content.parts
.filter((part) => part.text)
.map((part) => part.text)
.join('');
}
}
return null;
}
// Helper function to format tool call arguments for display
function formatToolArgs(args: Record<string, unknown>): string {
if (!args || Object.keys(args).length === 0) {
return '(no arguments)';
}
const formattedArgs = Object.entries(args)
.map(([key, value]) => {
if (typeof value === 'string') {
return `${key}: "${value}"`;
} else if (typeof value === 'object' && value !== null) {
return `${key}: ${JSON.stringify(value)}`;
} else {
return `${key}: ${value}`;
}
})
.join(', ');
return `(${formattedArgs})`;
}
// Helper function to display tool call information
function displayToolCallInfo(
toolName: string,
args: Record<string, unknown>,
status: 'start' | 'success' | 'error',
resultDisplay?: ToolResultDisplay,
errorMessage?: string,
): void {
const timestamp = new Date().toLocaleTimeString();
const argsStr = formatToolArgs(args);
switch (status) {
case 'start':
process.stdout.write(
`\n[${timestamp}] 🔧 Executing tool: ${toolName} ${argsStr}\n`,
);
break;
case 'success':
if (resultDisplay) {
if (typeof resultDisplay === 'string' && resultDisplay.trim()) {
process.stdout.write(
`[${timestamp}] ✅ Tool ${toolName} completed successfully\n`,
);
process.stdout.write(`📋 Result:\n${resultDisplay}\n`);
} else if (
typeof resultDisplay === 'object' &&
'fileDiff' in resultDisplay
) {
process.stdout.write(
`[${timestamp}] ✅ Tool ${toolName} completed successfully\n`,
);
process.stdout.write(`📋 File: ${resultDisplay.fileName}\n`);
process.stdout.write(`📋 Diff:\n${resultDisplay.fileDiff}\n`);
} else {
process.stdout.write(
`[${timestamp}] ✅ Tool ${toolName} completed successfully (no output)\n`,
);
}
} else {
process.stdout.write(
`[${timestamp}] ✅ Tool ${toolName} completed successfully (no output)\n`,
);
}
break;
case 'error':
process.stdout.write(
`[${timestamp}] ❌ Tool ${toolName} failed: ${errorMessage}\n`,
);
break;
default:
process.stdout.write(
`[${timestamp}] ⚠️ Tool ${toolName} reported unknown status: ${status}\n`,
);
break;
}
}
export async function runNonInteractive(
config: Config,
input: string,
prompt_id: string,
): Promise<void> {
await config.initialize();
// Handle EPIPE errors when the output is piped to a command that closes early.
process.stdout.on('error', (err: NodeJS.ErrnoException) => {
if (err.code === 'EPIPE') {
// Exit gracefully if the pipe is closed.
process.exit(0);
}
});
const geminiClient = config.getGeminiClient();
const toolRegistry: ToolRegistry = await config.getToolRegistry();
const chat = await geminiClient.getChat();
const abortController = new AbortController();
let currentMessages: Content[] = [{ role: 'user', parts: [{ text: input }] }];
let turnCount = 0;
try {
while (true) {
turnCount++;
if (
config.getMaxSessionTurns() > 0 &&
turnCount > config.getMaxSessionTurns()
) {
console.error(
'\n Reached max session turns for this session. Increase the number of turns by specifying maxSessionTurns in settings.json.',
);
return;
}
const functionCalls: FunctionCall[] = [];
const responseStream = await chat.sendMessageStream(
{
message: currentMessages[0]?.parts || [], // Ensure parts are always provided
config: {
abortSignal: abortController.signal,
tools: [
{ functionDeclarations: toolRegistry.getFunctionDeclarations() },
],
},
},
prompt_id,
);
for await (const resp of responseStream) {
if (abortController.signal.aborted) {
console.error('Operation cancelled.');
return;
}
const textPart = getResponseText(resp);
if (textPart) {
process.stdout.write(textPart);
}
if (resp.functionCalls) {
functionCalls.push(...resp.functionCalls);
}
}
if (functionCalls.length > 0) {
const toolResponseParts: Part[] = [];
for (const fc of functionCalls) {
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
const requestInfo: ToolCallRequestInfo = {
callId,
name: fc.name as string,
args: (fc.args ?? {}) as Record<string, unknown>,
isClientInitiated: false,
prompt_id,
};
//Display tool call start information
displayToolCallInfo(fc.name as string, fc.args ?? {}, 'start');
const toolResponse = await executeToolCall(
config,
requestInfo,
toolRegistry,
abortController.signal,
);
if (toolResponse.error) {
// Display tool call error information
const errorMessage =
typeof toolResponse.resultDisplay === 'string'
? toolResponse.resultDisplay
: toolResponse.error?.message;
displayToolCallInfo(
fc.name as string,
fc.args ?? {},
'error',
undefined,
errorMessage,
);
const isToolNotFound = toolResponse.error.message.includes(
'not found in registry',
);
console.error(
`Error executing tool ${fc.name}: ${toolResponse.resultDisplay || toolResponse.error.message}`,
);
if (!isToolNotFound) {
process.exit(1);
}
} else {
// Display tool call success information
displayToolCallInfo(
fc.name as string,
fc.args ?? {},
'success',
toolResponse.resultDisplay,
);
}
if (toolResponse.responseParts) {
const parts = Array.isArray(toolResponse.responseParts)
? toolResponse.responseParts
: [toolResponse.responseParts];
for (const part of parts) {
if (typeof part === 'string') {
toolResponseParts.push({ text: part });
} else if (part) {
toolResponseParts.push(part);
}
}
}
}
currentMessages = [{ role: 'user', parts: toolResponseParts }];
} else {
process.stdout.write('\n'); // Ensure a final newline
return;
}
}
} catch (error) {
console.error(
parseAndFormatApiError(
error,
config.getContentGeneratorConfig()?.authType,
),
);
process.exit(1);
} finally {
if (isTelemetrySdkInitialized()) {
await shutdownTelemetry();
}
}
}