diff --git a/.gitignore b/.gitignore index 011fa638a..d90c8e89c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ temp/ testfiles nodejs-vertexai/ samples/package-lock.json +.env diff --git a/package-lock.json b/package-lock.json index a11f272ff..f084e7dca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,17 @@ { "name": "@google/generative-ai", - "version": "0.21.0", + "version": "0.24.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@google/generative-ai", - "version": "0.21.0", + "version": "0.24.0", "license": "Apache-2.0", + "dependencies": { + "@google/generative-ai": "^0.24.0", + "dotenv": "^16.4.7" + }, "devDependencies": { "@changesets/cli": "^2.27.1", "@esm-bundle/chai": "^4.3.4-fix.0", @@ -116,10 +120,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dev": true, + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -494,6 +499,15 @@ "@types/chai": "^4.2.12" } }, + "node_modules/@google/generative-ai": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.0.tgz", + "integrity": "sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -3718,6 +3732,18 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -9561,9 +9587,9 @@ } }, "@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dev": true, "requires": { "regenerator-runtime": "^0.14.0" @@ -9888,6 +9914,11 @@ "@types/chai": "^4.2.12" } }, + "@google/generative-ai": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.0.tgz", + "integrity": "sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q==" + }, "@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -12351,6 +12382,11 @@ "esutils": "^2.0.2" } }, + "dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==" + }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", diff --git a/package.json b/package.json index 54266a7ca..16f8082b6 100644 --- a/package.json +++ b/package.json @@ -88,5 +88,9 @@ "bugs": { "url": "https://github.com/google/generative-ai-js/issues" }, - "homepage": "https://github.com/google/generative-ai-js#readme" + "homepage": "https://github.com/google/generative-ai-js#readme", + "dependencies": { + "@google/generative-ai": "^0.24.0", + "dotenv": "^16.4.7" + } } diff --git a/samples/Analyze_a_Video_Summarization.js b/samples/Analyze_a_Video_Summarization.js new file mode 100644 index 000000000..5bd9fa830 --- /dev/null +++ b/samples/Analyze_a_Video_Summarization.js @@ -0,0 +1,87 @@ +import "dotenv/config"; +import { GoogleGenerativeAI } from "@google/generative-ai"; +import fs from "fs"; +import https from "https"; +import ffmpeg from "fluent-ffmpeg"; + +const API_KEY = process.env.GOOGLE_API_KEY; +const genAI = new GoogleGenerativeAI(API_KEY); + +const videoPath = "wingit.webm"; +const framePath = "frame.jpg"; +const videoUrl = "https://upload.wikimedia.org/wikipedia/commons/3/38/WING_IT%21_-_Blender_Open_Movie-full_movie.webm"; + +async function downloadFile(url, dest) { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(dest); + https.get(url, (response) => { + response.pipe(file); + file.on("finish", () => { + file.close(); + console.log(`Downloaded ${dest}`); + resolve(); + }); + }).on("error", (err) => { + fs.unlink(dest, () => reject(err)); + }); + }); +} + +async function extractFrame(videoPath, outputPath) { + return new Promise((resolve, reject) => { + ffmpeg(videoPath) + .seekInput(10) // Extract a frame at 10 seconds + .frames(1) + .output(outputPath) + .on("end", () => { + console.log(`Frame extracted to ${outputPath}`); + resolve(outputPath); + }) + .on("error", (err) => { + reject(new Error(`FFmpeg error: ${err.message}`)); + }) + .run(); + }); +} + +async function summarizeFrame(imagePath) { + const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" }); + const imageData = fs.readFileSync(imagePath).toString("base64"); + + const response = await model.generateContent({ + contents: [ + { text: "Summarize this image from a video." }, + { inlineData: { mimeType: "image/jpeg", data: imageData } }, + ], + }); + + console.log("Summary:", response.text); +} + +async function main() { + let videoExists = false; + let frameExists = false; + + try { + await downloadFile(videoUrl, videoPath); + videoExists = true; + + await extractFrame(videoPath, framePath); + frameExists = true; + + await summarizeFrame(framePath); + } catch (error) { + console.error("Error:", error.message); + } finally { + if (videoExists && fs.existsSync(videoPath)) { + fs.unlinkSync(videoPath); + console.log(`Cleaned up ${videoPath}`); + } + if (frameExists && fs.existsSync(framePath)) { + fs.unlinkSync(framePath); + console.log(`Cleaned up ${framePath}`); + } + } +} + +main(); \ No newline at end of file diff --git a/samples/Barista_bot.js b/samples/Barista_bot.js new file mode 100644 index 000000000..6543f131c --- /dev/null +++ b/samples/Barista_bot.js @@ -0,0 +1,143 @@ +import "dotenv/config"; // Load .env variables +import readline from "readline"; // For terminal input +import { GoogleGenerativeAI } from "@google/generative-ai"; // Gemini SDK Setup + +// Initialize Gemini client with your API key +const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY); + +// Export the generative model (Gemini Pro) +const model = genAI.getGenerativeModel({ model: "gemini-pro" }); + + +// Store the current order +let order = []; + +/** + * Add a drink to the order with optional modifiers (like oat milk, caramel) + */ +function addToOrder(drink, modifiers = []) { + order.push({ drink, modifiers }); + return `โœ… Added ${drink}${modifiers.length ? " with " + modifiers.join(", ") : ""}`; +} + +/** + * Get the full text summary of the order + */ +function getOrderText() { + if (order.length === 0) return "๐Ÿ“ You haven't added anything yet."; + return order + .map(({ drink, modifiers }, i) => + `${i + 1}. ${drink}${modifiers.length ? " (" + modifiers.join(", ") + ")" : ""}` + ) + .join("\n"); +} + +/** + * Clear the current order + */ +function clearOrder() { + order = []; + return "๐Ÿงน Order cleared."; +} + +/** + * Place the order, get a random ETA, then clear it + */ +function placeOrder() { + const eta = Math.floor(Math.random() * 10) + 1; + const summary = getOrderText(); + clearOrder(); + return `โœ… Order placed!\n๐Ÿ“ Summary:\n${summary}\n๐Ÿšš ETA: ${eta} minutes.`; +} + + + +// Setup CLI input/output +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +// Main loop +async function main() { + console.log("โ˜• Welcome to Barista Bot (powered by Gemini)\nType 'exit' to quit.\n"); + + while (true) { + const input = await new Promise((res) => rl.question("> ", res)); + + if (input.toLowerCase() === "exit") break; + + try { + // Ask Gemini to interpret the user's command + const result = await model.generateContent(` + This is a barista bot. The user said: "${input}". + Respond in lowercase with a simple command: +- "add [drink] with [modifiers]" (e.g. add latte with oat milk) +- "get order" +- "clear" +- "place order" +Respond with only the command. +`); + + const aiResponse = result.response.text().toLowerCase().trim(); + console.log(`๐Ÿค– Gemini says: ${aiResponse}`); + + let output = "๐Ÿค” I didn't understand that."; + + // Parse Gemini's instruction + if (aiResponse.startsWith("add")) { + const match = aiResponse.match(/add\s(.+?)(?:\swith\s(.+))?$/); + if (match) { + const drink = match[1].trim(); + const mods = match[2]?.split(" and ").map((m) => m.trim()) ?? []; + output = addToOrder(drink, mods); + } + } else if (aiResponse.includes("get order")) { + output = getOrderText(); + } else if (aiResponse.includes("clear")) { + output = clearOrder(); + } else if (aiResponse.includes("place order")) { + output = placeOrder(); + } + + console.log(output + "\n"); + } catch (err) { + console.error("โŒ Error:", err.message); + } + } + + rl.close(); +} + +main(); +// ๐Ÿ“ฆ Install Dependencies + +// npm init -y +// npm install @google/generative-ai dotenv +// ๐Ÿงช Run the Bot + +// โ˜• Welcome to Barista Bot (powered by Gemini) +// Type 'exit' to quit. + +// > I'd like a cappuccino with oat milk +// ๐Ÿค– Gemini says: add cappuccino with oat milk +// โœ… Added cappuccino with oat milk + +// > What's my order? +// ๐Ÿค– Gemini says: get order +// 1. cappuccino (oat milk) + +// > clear it please +// ๐Ÿค– Gemini says: clear +// ๐Ÿงน Order cleared. + +// > give me a mocha with almond milk and caramel +// ๐Ÿค– Gemini says: add mocha with almond milk and caramel +// โœ… Added mocha with almond milk and caramel + +// > place the order +// ๐Ÿค– Gemini says: place order +// โœ… Order placed! +// ๐Ÿ“ Summary: +// 1. mocha (almond milk, caramel) +// ๐Ÿšš ETA: 6 minutes. \ No newline at end of file diff --git a/samples/package.json b/samples/package.json index 6bc712c2d..581ebc887 100644 --- a/samples/package.json +++ b/samples/package.json @@ -1,7 +1,8 @@ { "type": "module", "dependencies": { - "@google/generative-ai": "*" + "@google/generative-ai": "*", + "fluent-ffmpeg": "^2.1.3" }, "license": "Apache-2.0", "engines": {