Google Search Tool and Custom Function Declaration

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;
        }
    }
}

Hi @Philomath, Welcome to the forum.

Currently, multi-tool use is supported only through the Multimodal Live API.

We have a notebook that explains how to use tools with the Multimodal Live API. Please take a look.

Thanks!