Simplify GeminiClient (#101)

Doing some more clean-up:
* Remove confusing continue/break
* Handle empty result
* Rename the file just client.js
This commit is contained in:
Jaana Dogan 2025-04-21 17:15:20 -07:00 committed by GitHub
parent dd81be1b9b
commit cacf0cc0ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 12 additions and 23 deletions

View File

@ -21,7 +21,7 @@ import { getFolderStructure } from '../utils/getFolderStructure.js';
import { Turn, ServerTool, ServerGeminiStreamEvent } from './turn.js'; import { Turn, ServerTool, ServerGeminiStreamEvent } from './turn.js';
export class GeminiClient { export class GeminiClient {
private ai: GoogleGenAI; private client: GoogleGenAI;
private model: string; private model: string;
private generateContentConfig: GenerateContentConfig = { private generateContentConfig: GenerateContentConfig = {
temperature: 0, temperature: 0,
@ -30,7 +30,7 @@ export class GeminiClient {
private readonly MAX_TURNS = 100; private readonly MAX_TURNS = 100;
constructor(apiKey: string, model: string) { constructor(apiKey: string, model: string) {
this.ai = new GoogleGenAI({ apiKey: apiKey }); this.client = new GoogleGenAI({ apiKey: apiKey });
this.model = model; this.model = model;
} }
@ -56,14 +56,9 @@ export class GeminiClient {
async startChat(toolDeclarations: FunctionDeclaration[]): Promise<Chat> { async startChat(toolDeclarations: FunctionDeclaration[]): Promise<Chat> {
const envPart = await this.getEnvironment(); const envPart = await this.getEnvironment();
// const tools: Tool[] = toolDeclarations.map((declaration) => ({
// functionDeclarations: [declaration],
// }));
// merge all functions into a single tool, as seems to be required for gemini 2.5 series
// can test by asking "what tools do you have?", which lists single function unless merged
const tools: Tool[] = [{ functionDeclarations: toolDeclarations }]; const tools: Tool[] = [{ functionDeclarations: toolDeclarations }];
try { try {
const chat = this.ai.chats.create({ return this.client.chats.create({
model: this.model, model: this.model,
config: { config: {
systemInstruction: CoreSystemPrompt, systemInstruction: CoreSystemPrompt,
@ -81,7 +76,6 @@ export class GeminiClient {
}, },
], ],
}); });
return chat;
} catch (error) { } catch (error) {
console.error('Error initializing Gemini chat session:', error); console.error('Error initializing Gemini chat session:', error);
const message = error instanceof Error ? error.message : 'Unknown error.'; const message = error instanceof Error ? error.message : 'Unknown error.';
@ -111,14 +105,11 @@ export class GeminiClient {
} }
// What do we do when we have both function responses and confirmations? // What do we do when we have both function responses and confirmations?
const fnResponses = turn.getFunctionResponses(); const fnResponses = turn.getFunctionResponses();
if (fnResponses.length > 0) { if (fnResponses.length == 0) {
request = fnResponses; break; // user's turn to respond
continue;
} else {
break;
} }
request = fnResponses;
} }
if (turns >= this.MAX_TURNS) { if (turns >= this.MAX_TURNS) {
console.warn( console.warn(
@ -140,7 +131,7 @@ export class GeminiClient {
schema: SchemaUnion, schema: SchemaUnion,
): Promise<Record<string, unknown>> { ): Promise<Record<string, unknown>> {
try { try {
const result = await this.ai.models.generateContent({ const result = await this.client.models.generateContent({
model: this.model, model: this.model,
config: { config: {
...this.generateContentConfig, ...this.generateContentConfig,
@ -150,15 +141,13 @@ export class GeminiClient {
}, },
contents, contents,
}); });
const responseText = result.text; if (!result || !result.text) {
if (!responseText) {
throw new Error('API returned an empty response.'); throw new Error('API returned an empty response.');
} }
try { try {
const parsedJson = JSON.parse(responseText); return JSON.parse(result.text);
return parsedJson;
} catch (parseError) { } catch (parseError) {
console.error('Failed to parse JSON response:', responseText); console.error('Failed to parse JSON response:', result.text);
throw new Error( throw new Error(
`Failed to parse API response as JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`, `Failed to parse API response as JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
); );

View File

@ -8,7 +8,7 @@
export * from './config/config.js'; export * from './config/config.js';
// Export Core Logic // Export Core Logic
export * from './core/gemini-client.js'; export * from './core/client.js';
export * from './core/prompts.js'; export * from './core/prompts.js';
export * from './core/turn.js'; export * from './core/turn.js';
// Potentially export types from turn.ts if needed externally // Potentially export types from turn.ts if needed externally

View File

@ -6,7 +6,7 @@
import { Content, SchemaUnion, Type } from '@google/genai'; import { Content, SchemaUnion, Type } from '@google/genai';
import { getErrorMessage, isNodeError } from '../utils/errors.js'; import { getErrorMessage, isNodeError } from '../utils/errors.js';
import { GeminiClient } from '../core/gemini-client.js'; import { GeminiClient } from '../core/client.js';
import { Config } from '../config/config.js'; import { Config } from '../config/config.js';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { exec as _exec } from 'child_process'; import { exec as _exec } from 'child_process';