mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-20 19:11:23 -07:00
This commit refactors the telemetry system to pass a object to various logging and metrics functions. This change centralizes configuration management within the telemetry system, making it more modular and easier to maintain. The constructor and various tool execution functions have been updated to accept the object, which is then passed down to the telemetry functions. This eliminates the need to pass individual configuration values, such as , through multiple layers of the application.
124 lines
3.4 KiB
TypeScript
124 lines
3.4 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import {
|
|
Config,
|
|
ToolCallRequestInfo,
|
|
executeToolCall,
|
|
ToolRegistry,
|
|
} from '@gemini-cli/core';
|
|
import {
|
|
Content,
|
|
Part,
|
|
FunctionCall,
|
|
GenerateContentResponse,
|
|
} from '@google/genai';
|
|
|
|
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
|
|
) {
|
|
return candidate.content.parts
|
|
.filter((part) => part.text)
|
|
.map((part) => part.text)
|
|
.join('');
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export async function runNonInteractive(
|
|
config: Config,
|
|
input: string,
|
|
): Promise<void> {
|
|
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 }] }];
|
|
|
|
try {
|
|
while (true) {
|
|
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() },
|
|
],
|
|
},
|
|
});
|
|
|
|
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>,
|
|
};
|
|
|
|
const toolResponse = await executeToolCall(
|
|
config,
|
|
requestInfo,
|
|
toolRegistry,
|
|
abortController.signal,
|
|
);
|
|
|
|
if (toolResponse.error) {
|
|
console.error(
|
|
`Error executing tool ${fc.name}: ${toolResponse.resultDisplay || toolResponse.error.message}`,
|
|
);
|
|
}
|
|
|
|
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('Error processing input:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|