diff --git a/samples/node/README.md b/samples/node/README.md index 4c41be4c..30d1e6e5 100644 --- a/samples/node/README.md +++ b/samples/node/README.md @@ -13,27 +13,6 @@ It’s strongly recommended that you do not check an API key into your version c This sample assumes that you're providing an `API_KEY` environment variable. -## Features - -### Simple examples - -- [`simple-text.js`](simple-text.js) - Text-only input -- [`simple-text-and-images.js`](simple-text-and-images.js) - Text-and-images input (multimodal) -- [`simple-chat.js`](simple-chat.js) - Dialog language tasks, using `ChatSession` class -- [`simple-config.js`](simple-config.js) - Configuring model parameters -- [`simple-embedding.js`](simple-embedding.js) - Embeddings, using the `embedding-001` model - -### More examples - -- [`advanced-text.js`](advanced-text.js) - Using `countTokens`, `safetySettings` and streaming with a text-only input -- [`advanced-text-and-images.js`](advanced-text-and-images.js) - Using `countTokens`, `generationConfig` and streaming with multimodal input -- [`advanced-chat.js`](advanced-chat.js) - Using `countTokens`, `generationConfig` and streaming with multi-turn conversations -- [`advanced-embeddings.js`](advanced-embeddings.js) - Using `batchEmbedContents` -- [`advanced-function-calling.js`](advanced-function-calling.js) - Using function calling -- [`advanced-code-execution.js`](advanced-code-execution.js) - Using the code execution feature -- [`content-caching.js`](content-caching.js) - Using `GoogleAICacheManager` -- [`file-upload.js`](file-upload.js) - Using `GoogleAIFileManager` - ## Documentation - [Quickstart: Get started with the Gemini API in Node.js applications](https://ai.google.dev/tutorials/node_quickstart) diff --git a/samples/node/advanced-text.js b/samples/node/advanced-text.js deleted file mode 100644 index e2577435..00000000 --- a/samples/node/advanced-text.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { HarmBlockThreshold, HarmCategory } from "@google/generative-ai"; -import { genAI, displayTokenCount, streamToStdout } from "./utils/common.js"; - -async function run() { - const model = genAI.getGenerativeModel({ - model: "gemini-1.5-flash-latest", - safetySettings: [ - { - category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, - threshold: HarmBlockThreshold.BLOCK_NONE, - }, - ], - }); - - const prompt = 'Please describe in detail the movie "Eyes wide shut"'; - - displayTokenCount(model, prompt); - - const result = await model.generateContentStream(prompt); - await streamToStdout(result.stream); - - // Display the aggregated response - const response = await result.response; - console.log(JSON.stringify(response, null, 2)); -} - -run(); diff --git a/samples/node/media/Big_Buck_Bunny.mp4 b/samples/node/media/Big_Buck_Bunny.mp4 new file mode 100644 index 00000000..07096db4 Binary files /dev/null and b/samples/node/media/Big_Buck_Bunny.mp4 differ diff --git a/samples/node/media/firefighter.jpg b/samples/node/media/firefighter.jpg new file mode 100644 index 00000000..2c674ad3 Binary files /dev/null and b/samples/node/media/firefighter.jpg differ diff --git a/samples/node/media/jetpack.jpg b/samples/node/media/jetpack.jpg new file mode 100644 index 00000000..7cd3c7f7 Binary files /dev/null and b/samples/node/media/jetpack.jpg differ diff --git a/samples/node/media/piranha.jpg b/samples/node/media/piranha.jpg new file mode 100644 index 00000000..75e54b80 Binary files /dev/null and b/samples/node/media/piranha.jpg differ diff --git a/samples/node/media/samplesmall.mp3 b/samples/node/media/samplesmall.mp3 new file mode 100644 index 00000000..32732d9a Binary files /dev/null and b/samples/node/media/samplesmall.mp3 differ diff --git a/samples/node/simple-text-and-images.js b/samples/node/simple-text-and-images.js deleted file mode 100644 index 8789b391..00000000 --- a/samples/node/simple-text-and-images.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { genAI, fileToGenerativePart, streamToStdout } from "./utils/common.js"; - -async function run() { - const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash-latest" }); - - const prompt = "What do you see?"; - - // Note: The only accepted mime types are some image types, image/*. - const imageParts = [ - fileToGenerativePart("./utils/cat.jpg", "image/jpeg"), - fileToGenerativePart("./utils/scones.jpg", "image/jpeg"), - ]; - - const result = await model.generateContent([prompt, ...imageParts]); - const response = result.response; - const text = response.text(); - console.log(text); -} - -run(); diff --git a/samples/node/simple-text.js b/samples/node/simple-text.js deleted file mode 100644 index 1028fe4e..00000000 --- a/samples/node/simple-text.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { genAI } from "./utils/common.js"; - -async function run() { - const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash-latest" }); - - const prompt = "Write a story about a magic backpack."; - - const { totalTokens } = await model.countTokens(prompt); - console.log("Tokens count:", totalTokens); - - const result = await model.generateContent(prompt); - const response = result.response; - const text = response.text(); - console.log(text); -} - -run(); diff --git a/samples/node/text_generation.js b/samples/node/text_generation.js new file mode 100644 index 00000000..d5a2e4e9 --- /dev/null +++ b/samples/node/text_generation.js @@ -0,0 +1,305 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GoogleGenerativeAI } from "@google/generative-ai"; +import { GoogleAIFileManager, FileState } from "@google/generative-ai/server"; +import fs from "fs"; +import { dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const mediaPath = __dirname + "/media"; + +async function textGenTextOnlyPrompt() { + // [START text_gen_text_only_prompt] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + const prompt = "Write a story about a magic backpack."; + + const result = await model.generateContent(prompt); + const response = result.response; + const text = response.text(); + console.log(text); + // [END text_gen_text_only_prompt] +} + +async function textGenTextOnlyPromptStreaming() { + // [START text_gen_text_only_prompt_streaming] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + const prompt = "Write a story about a magic backpack."; + + const result = await model.generateContentStream(prompt); + + // Print text as it comes in. + for await (const chunk of result.stream) { + const chunkText = chunk.text(); + console.log(chunkText); + } + // [END text_gen_text_only_prompt_streaming] +} + +async function textGenMultimodalOneImagePrompt() { + // [START text_gen_multimodal_one_image_prompt] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + function fileToGenerativePart(path, mimeType) { + return { + inlineData: { + data: Buffer.from(fs.readFileSync(path)).toString("base64"), + mimeType, + }, + }; + } + + const prompt = "Describe how this product might be manufactured."; + // Note: The only accepted mime types are some image types, image/*. + const imagePart = fileToGenerativePart( + `${mediaPath}/jetpack.jpg`, + "image/jpeg", + ); + + const result = await model.generateContent([prompt, imagePart]); + const response = result.response; + const text = response.text(); + console.log(text); + // [END text_gen_multimodal_one_image_prompt] +} + +async function textGenMultimodalOneImagePromptStreaming() { + // [START text_gen_multimodal_one_image_prompt_streaming] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + function fileToGenerativePart(path, mimeType) { + return { + inlineData: { + data: Buffer.from(fs.readFileSync(path)).toString("base64"), + mimeType, + }, + }; + } + + const prompt = "Describe how this product might be manufactured."; + // Note: The only accepted mime types are some image types, image/*. + const imagePart = fileToGenerativePart( + `${mediaPath}/jetpack.jpg`, + "image/jpeg", + ); + + const result = await model.generateContentStream([prompt, imagePart]); + + // Print text as it comes in. + for await (const chunk of result.stream) { + const chunkText = chunk.text(); + console.log(chunkText); + } + // [END text_gen_multimodal_one_image_prompt_streaming] +} + +async function textGenMultimodalMultiImagePrompt() { + // [START text_gen_multimodal_multi_image_prompt] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + function fileToGenerativePart(path, mimeType) { + return { + inlineData: { + data: Buffer.from(fs.readFileSync(path)).toString("base64"), + mimeType, + }, + }; + } + + const prompt = + "Write an advertising jingle showing how the product in the" + + " first image could solve the problems shown in the second two images."; + + // Note: The only accepted mime types are some image types, image/*. + const imageParts = [ + fileToGenerativePart(`${mediaPath}/jetpack.jpg`, "image/jpeg"), + fileToGenerativePart(`${mediaPath}/piranha.jpg`, "image/jpeg"), + fileToGenerativePart(`${mediaPath}/firefighter.jpg`, "image/jpeg"), + ]; + + const result = await model.generateContent([prompt, ...imageParts]); + const response = result.response; + const text = response.text(); + console.log(text); + // [END text_gen_multimodal_multi_image_prompt] +} + +async function textGenMultimodalMultiImagePromptStreaming() { + // [START text_gen_multimodal_multi_image_prompt_streaming] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + function fileToGenerativePart(path, mimeType) { + return { + inlineData: { + data: Buffer.from(fs.readFileSync(path)).toString("base64"), + mimeType, + }, + }; + } + + const prompt = + "Write an advertising jingle showing how the product in the" + + " first image could solve the problems shown in the second two images."; + + // Note: The only accepted mime types are some image types, image/*. + const imageParts = [ + fileToGenerativePart(`${mediaPath}/jetpack.jpg`, "image/jpeg"), + fileToGenerativePart(`${mediaPath}/piranha.jpg`, "image/jpeg"), + fileToGenerativePart(`${mediaPath}/firefighter.jpg`, "image/jpeg"), + ]; + + const result = await model.generateContentStream([prompt, ...imageParts]); + + // Print text as it comes in. + for await (const chunk of result.stream) { + const chunkText = chunk.text(); + console.log(chunkText); + } + // [END text_gen_multimodal_multi_image_prompt_streaming] +} + +async function textGenMultimodalAudio() { + // [START text_gen_multimodal_audio] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + function fileToGenerativePart(path, mimeType) { + return { + inlineData: { + data: Buffer.from(fs.readFileSync(path)).toString("base64"), + mimeType, + }, + }; + } + + const prompt = "Give me a summary of this audio file."; + // Note: The only accepted mime types are some image types, image/*. + const audioPart = fileToGenerativePart( + `${mediaPath}/samplesmall.mp3`, + "audio/mp3", + ); + + const result = await model.generateContent([prompt, audioPart]); + const response = result.response; + const text = response.text(); + console.log(text); + // [END text_gen_multimodal_audio] +} + +async function textGenMultimodalVideoPrompt() { + // [START text_gen_multimodal_video_prompt] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + const fileManager = new GoogleAIFileManager(process.env.API_KEY); + + const uploadResult = await fileManager.uploadFile( + `${mediaPath}/Big_Buck_Bunny.mp4`, + { mimeType: "video/mp4" }, + ); + + let file = await fileManager.getFile(uploadResult.file.name); + while (file.state === FileState.PROCESSING) { + process.stdout.write("."); + // Sleep for 10 seconds + await new Promise((resolve) => setTimeout(resolve, 10_000)); + // Fetch the file from the API again + file = await fileManager.getFile(uploadResult.file.name); + } + + if (file.state === FileState.FAILED) { + throw new Error("Video processing failed."); + } + + const prompt = "Describe this video clip"; + const videoPart = { + fileData: { + fileUri: uploadResult.file.uri, + mimeType: uploadResult.file.mimeType, + }, + }; + + const result = await model.generateContent([prompt, videoPart]); + const response = result.response; + const text = response.text(); + console.log(text); + // [END text_gen_multimodal_video_prompt] +} + +async function textGenMultimodalVideoPromptStreaming() { + // [START text_gen_multimodal_video_prompt_streaming] + const genAI = new GoogleGenerativeAI(process.env.API_KEY); + const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + + const fileManager = new GoogleAIFileManager(process.env.API_KEY); + + const uploadResult = await fileManager.uploadFile( + `${mediaPath}/Big_Buck_Bunny.mp4`, + { mimeType: "video/mp4" }, + ); + + let file = await fileManager.getFile(uploadResult.file.name); + while (file.state === FileState.PROCESSING) { + process.stdout.write("."); + // Sleep for 10 seconds + await new Promise((resolve) => setTimeout(resolve, 10_000)); + // Fetch the file from the API again + file = await fileManager.getFile(uploadResult.file.name); + } + + if (file.state === FileState.FAILED) { + throw new Error("Video processing failed."); + } + + const prompt = "Describe this video clip"; + const videoPart = { + fileData: { + fileUri: uploadResult.file.uri, + mimeType: uploadResult.file.mimeType, + }, + }; + + const result = await model.generateContent([prompt, videoPart]); + const response = result.response; + const text = response.text(); + console.log(text); + // [END text_gen_multimodal_video_prompt_streaming] +} + +async function runAll() { + // Comment out or delete any sample cases you don't want to run. + await textGenTextOnlyPrompt(); + await textGenTextOnlyPromptStreaming(); + await textGenMultimodalOneImagePrompt(); + await textGenMultimodalOneImagePromptStreaming(); + await textGenMultimodalMultiImagePrompt(); + await textGenMultimodalMultiImagePromptStreaming(); + await textGenMultimodalAudio(); + await textGenMultimodalVideoPrompt(); + await textGenMultimodalVideoPromptStreaming(); +} + +runAll(); diff --git a/samples/node/utils/cat.jpg b/samples/node/utils/cat.jpg deleted file mode 100644 index 8d2069e6..00000000 Binary files a/samples/node/utils/cat.jpg and /dev/null differ diff --git a/samples/node/utils/scones.jpg b/samples/node/utils/scones.jpg deleted file mode 100644 index ce689588..00000000 Binary files a/samples/node/utils/scones.jpg and /dev/null differ diff --git a/samples/web/README.md b/samples/web/README.md deleted file mode 100644 index 9149689a..00000000 --- a/samples/web/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Google Generative AI Sample for Web (Javascript) - -This sample app demonstrates how to use state-of-the-art -generative AI models (like Gemini) to build AI-powered features and applications. - -To try out this sample app, you'll need a modern web browser and a local http server. - -## Requirements - -Follow the instructions on Google AI Studio [setup page](https://makersuite.google.com/app/apikey) to obtain an API key. - -It’s strongly recommended that you do not check an API key into your version control system. Instead, you should use a secrets store for your API key. - -The Node.js http server provided alonside this app (`http-server.js`) assumes that you're providing an `API_KEY` environment variable. - -## Features - -This sample showcases the following API capablilites: - -- `index.html` - demonstrates the Text and MultiModal feature from the SDK -- `chat.html` - demonstrates the Multi-turn Conversations feature from the SDK - -## Documentation - -- [Quickstart: Get started with the Gemini API in web apps](https://ai.google.dev/tutorials/web_quickstart) diff --git a/samples/web/chat.html b/samples/web/chat.html deleted file mode 100644 index 7f6987c6..00000000 --- a/samples/web/chat.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - Generative AI - Chat - - - -
Generative AI - Chat
-
-
-
-
-
- - -
- -
- - - - diff --git a/samples/web/favicon.svg b/samples/web/favicon.svg deleted file mode 100644 index c2f0bd64..00000000 --- a/samples/web/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/samples/web/http-server.js b/samples/web/http-server.js deleted file mode 100644 index ae71d8ae..00000000 --- a/samples/web/http-server.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from "fs"; -import http from "http"; -import path from "path"; -import url from "url"; - -// Local port for http server to listen on -const PORT = 9000; - -// Get your API key from https://makersuite.google.com/app/apikey -// Access your API key as an environment variable -const API_KEY = process.env.API_KEY; - -if (!API_KEY) { - throw new Error("API_KEY environment variable not set"); -} - -// Maps file extention to MIME types -// Full list can be found here: https://www.freeformatter.com/mime-types-list.html -const mimeType = { - ".html": "text/html", - ".js": "text/javascript", - ".mjs": "text/javascript", - ".css": "text/css", -}; - -http - .createServer((req, res) => { - console.log(` ${req.method} ${req.url}`); - - // Parse URL - const parsedUrl = url.parse(req.url); - - // Extract URL path - // Avoid https://en.wikipedia.org/wiki/Directory_traversal_attack - let sanitizedPath = path - .normalize(parsedUrl.pathname) - .replace(/^(\.\.[\/\\])+/, "") - .substring(1); - - if (sanitizedPath === "API_KEY") { - res.end(API_KEY); - return; - } - - if (sanitizedPath === "") { - sanitizedPath = "index.html"; - } - - // based on the URL path, extract the file extention. e.g. .js, .doc, ... - const ext = path.parse(sanitizedPath).ext; - - try { - const data = fs.readFileSync(sanitizedPath); - - // If the file is found, set Content-Type and send data - if (mimeType[ext]) { - res.setHeader("Content-Type", mimeType[ext]); - } - res.end(data); - } catch (err) { - // If the file is not found, return 404 - res.statusCode = 404; - res.end(); - } - }) - .listen(parseInt(PORT)); - -console.log( - `Server listening. Pages:\n - http://localhost:${PORT}\n - http://localhost:${PORT}/chat.html`, -); diff --git a/samples/web/index.html b/samples/web/index.html deleted file mode 100644 index b87097a5..00000000 --- a/samples/web/index.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - Generative AI - Text and Image - - - -
Generative AI - Text and Image
-
-
- - - -
-
-
-
-
-
- - - - diff --git a/samples/web/package.json b/samples/web/package.json deleted file mode 100644 index a264955f..00000000 --- a/samples/web/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "type": "module", - "scripts": { - "start": "node http-server.js", - "http-server": "node http-server.js" - }, - "dependencies": { - "@google/generative-ai": "*" - } -} diff --git a/samples/web/utils/main.css b/samples/web/utils/main.css deleted file mode 100644 index 336d7e3e..00000000 --- a/samples/web/utils/main.css +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -* { - box-sizing: border-box; -} - -header { - border-bottom: 2px solid rgb(127, 127, 127, 0.5); - font-size: 24px; - padding: 16px; - text-align: center; -} - -body { - font-family: "Roboto", sans-serif; - margin: 0; -} - -.loading::after { - content: "Loading..."; - display: block; - font-size: 80%; - font-style: italic; - margin: 16px 0; -} - -.loading { - opacity: 0.5; -} - -.error { - color: red; -} - -.container, -header, -.form-container { - margin: 0 auto; - max-width: 700px; -} - -.form-container { - border-bottom: 2px solid rgb(127, 127, 127, 0.5); -} - -img.thumb { - border: 1px solid grey; - border-radius: 8px; - height: 100px; - margin: 0px 16px 16px 0; - padding: 2px; - width: 100px; -} - -#form, -.history-item { - align-items: center; - display: flex; - justify-content: center; - padding: 16px 0; -} - -#file { - flex-grow: 0; -} - -#prompt { - margin: 4px; - padding: 2px; - width: 100%; -} - -button { - padding: 2px 16px; -} - -.name { - flex-shrink: 0; - font-size: 80%; - margin: 16px 16px 16px 0; - opacity: 0.5; - text-align: right; - width: 50px; -} - -blockquote { - margin: 0; -} - -.history-item { - padding: 0 8px 0 0; -} - -.history-item.model-role { - background: rgba(127, 127, 127, 0.1); -} - -.history-item > blockquote { - flex-grow: 1; - margin: 0; -} diff --git a/samples/web/utils/shared.js b/samples/web/utils/shared.js deleted file mode 100644 index f57422b8..00000000 --- a/samples/web/utils/shared.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { GoogleGenerativeAI } from "https://esm.run/@google/generative-ai"; -import { marked } from "https://esm.run/marked"; - -/** - * Returns a model instance. - * - * @param {GoogleGenerativeAI.ModelParams} params - * @returns {GoogleGenerativeAI.GenerativeModel} - */ -export async function getGenerativeModel(params) { - // Fetch API key from server - // If you need a new API key, get it from https://makersuite.google.com/app/apikey - const API_KEY = await (await fetch("API_KEY")).text(); - - const genAI = new GoogleGenerativeAI(API_KEY); - - return genAI.getGenerativeModel(params); -} - -/** - * Converts a File object to a GoogleGenerativeAI.Part object. - * - * @param {Blob} file - * @returns {GoogleGenerativeAI.Part} - */ -export async function fileToGenerativePart(file) { - const base64EncodedDataPromise = new Promise((resolve) => { - const reader = new FileReader(); - reader.onloadend = () => resolve(reader.result.split(",")[1]); - reader.readAsDataURL(file); - }); - return { - inlineData: { data: await base64EncodedDataPromise, mimeType: file.type }, - }; -} - -/** - * Scrolls the document all the way to the bottom. - */ -export function scrollToDocumentBottom() { - const scrollingElement = document.scrollingElement || document.body; - scrollingElement.scrollTop = scrollingElement.scrollHeight; -} - -/** - * Updates the `resultEl` with parsed markdown text returned by a `getResult()` call. - * - * @param {HTMLElement}} resultEl - * @param {() => Promise} getResult - * @param {boolean} streaming - */ -export async function updateUI(resultEl, getResult, streaming) { - resultEl.className = "loading"; - let text = ""; - try { - const result = await getResult(); - - if (streaming) { - resultEl.innerText = ""; - for await (const chunk of result.stream) { - // Get first candidate's current text chunk - const chunkText = chunk.text(); - text += chunkText; - resultEl.innerHTML = marked.parse(text); - scrollToDocumentBottom(); - } - } else { - const response = await result.response; - text = response.text(); - } - - resultEl.className = ""; // Remove .loading class - } catch (err) { - text += "\n\n> " + err; - resultEl.className = "error"; - } - resultEl.innerHTML = marked.parse(text); - scrollToDocumentBottom(); -}