Can anyone help me to use a custom function and the google search tool with gemini-2.0-flash-exp in node? I am always getting the error “Requst contains an invalid argument” Am using the npm package Using one of these tools by itself works fine. Here;s my code (edited)
class UnifiedGeminiAPI { constructor(options = {}) { const apiKey = process.env.GEMINI_API_KEY; if (!apiKey) { throw new Error("GEMINI_API_KEY not found in environment variables."); } this.genAI = new GoogleGenerativeAI(apiKey); this.model = this.genAI.getGenerativeModel({ model: "gemini-2.0-flash-exp" }); this.debug = options.debug || false; this.defaultSystemPrompt = options.systemPrompt || `You are Professor Regenbogen, an expert LGBTQ+ journalist and content writer for Pride.Direct. Your expertise is in creating engaging, well-researched articles about LGBTQ+ topics for a German-speaking audience.`; // Define function declarations for blog publishing this.functionDeclarations = [{ name: "publish_blog_post", description: "Publishes a blog post to Shopify. Only use when content is ready and user has confirmed publication.", parameters: { type: "object", properties: { title: { type: "string", description: "The title of the blog post" }, author: { type: "string", description: "The author of the blog post" }, content: { type: "string", description: "The HTML content of the blog post" }, tags: { type: "array", items: { type: "string" }, description: "Array of tags for the blog post" }, published: { type: "boolean", description: "Whether to publish immediately" }, userConfirmed: { type: "boolean", description: "Whether user has confirmed publication" } }, required: ["title", "author", "content", "userConfirmed"] } }]; } log(...args) { if (this.debug) { console.log('[DEBUG]', ...args); } } async ask(text, options = {}) { try { // Build the complete request payload const requestPayload = { contents: [{ role: 'user', parts: [{ text: `${options.systemPrompt || this.defaultSystemPrompt}\n\n${text}` }] }], tools: [ { google_search: {} }, // Search tool { function_declarations: this.functionDeclarations } // Reuse the class property ], generationConfig: { maxOutputTokens: 2048, }, safetySettings: [ { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" }, { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" }, { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" }, { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" } ] }; // Log the complete request payload this.log('Sending request to Gemini:'); this.log(JSON.stringify(requestPayload, null, 2)); const prompt = `${options.systemPrompt || this.defaultSystemPrompt}\n\n${text}`; const result = await this.model.generateContent(requestPayload); if (!result?.response?.candidates?.[0]?.content?.parts) { throw new Error('Invalid response format from Gemini'); } // Get the response content const responsePart = result.response.candidates[0].content.parts[0]; // Check if we have a function call if (responsePart.functionCall) { this.log('Function call detected:', responsePart.functionCall); const { name, args } = responsePart.functionCall; if (name === 'publish_blog_post') { if (!args.userConfirmed) { return responsePart.text + '\n\nWould you like to publish this article? Please confirm.'; } this.log('Publishing blog post...'); const result = await createBlogArticle(args); return responsePart.text + `\n\nBlog post published successfully with ID: ${result.id}`; } } // Get any search citations if available let response = responsePart.text || ''; if (result.response.candidates[0].citations) { response += '\n\nSources:\n'; result.response.candidates[0].citations.forEach((citation, index) => { response += `[${index + 1}] ${citation.url}\n`; }); } return response; } catch (error) { console.error('Error querying Gemini:', error); throw error; } } }