From 57430eb4fdbea03fc2ca643484bfa850d662eebe Mon Sep 17 00:00:00 2001 From: Steve Bazyl Date: Fri, 10 May 2024 12:30:30 -0600 Subject: [PATCH] Import custom func demo from Next 24 (#464) --- ai/custom_func_vertex/Code.js | 12 +++ ai/custom_func_vertex/README.md | 33 ++++++++ ai/custom_func_vertex/aiVertex.js | 108 ++++++++++++++++++++++++++ ai/custom_func_vertex/appsscript.json | 12 +++ 4 files changed, 165 insertions(+) create mode 100644 ai/custom_func_vertex/Code.js create mode 100644 ai/custom_func_vertex/README.md create mode 100644 ai/custom_func_vertex/aiVertex.js create mode 100644 ai/custom_func_vertex/appsscript.json diff --git a/ai/custom_func_vertex/Code.js b/ai/custom_func_vertex/Code.js new file mode 100644 index 000000000..e8fb881eb --- /dev/null +++ b/ai/custom_func_vertex/Code.js @@ -0,0 +1,12 @@ +/** + * Passes a prompt and a data range to Gemini AI. + * + * @param {range} range The range of cells. + * @param {string} prompt The text prompt as a string or single cell reference. + * @return The Gemini response. + * @customfunction + */ +function gemini(range,prompt) { + prompt = `For the table of data: ${range}, Answer the following: ${prompt}. Do not use formatting. Remove all markdown.` + return getAiSummary(prompt); +} \ No newline at end of file diff --git a/ai/custom_func_vertex/README.md b/ai/custom_func_vertex/README.md new file mode 100644 index 000000000..7960aebab --- /dev/null +++ b/ai/custom_func_vertex/README.md @@ -0,0 +1,33 @@ +# Google Sheets Custom Function with AI Studio + +## Project Description + +Google Sheets Custom Function to be used as a bound Apps Script project with a Google Sheets Spreadsheet. + +## Prerequisites + +* Google Cloud Project (aka Standard Cloud Project for Apps Script) with billing enabled + +## Set up your environment + +1. Create a Cloud Project + 1. Enable the Vertex AI API + 1. Create a Service Account and grant the role Service Account Token Creator Role + 1. Create a private key with type JSON. This will download the JSON file for use in the next section. +1. Open an Apps Script Project bound to a Google Sheets Spreadsheet + 1. From Project Settings, change project to GCP project number of Cloud Project from step 1 + 1. Add a Script Property. Enter `model_id` as the property name and `gemini-pro` as the value. + 1. Add a Script Property. Enter `project_location` as the property name and `us-central-1` as the value. + 1. Add a Script Property. Enter `service_account_key` as the property name and paste the JSON key from the service account as the value. +1. Add OAuth2 v43 Apps Script Library using the ID `1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF`. +1. Add the project code to Apps Script + +## Usage + +Insert a custom function in Google Sheets, passing a range and a prompt as parameters + +Example: + +``` +=gemini(A1:A10,"Extract colors from the product description") +``` \ No newline at end of file diff --git a/ai/custom_func_vertex/aiVertex.js b/ai/custom_func_vertex/aiVertex.js new file mode 100644 index 000000000..708582614 --- /dev/null +++ b/ai/custom_func_vertex/aiVertex.js @@ -0,0 +1,108 @@ +/* +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 + + https://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. +*/ + +const VERTEX_AI_LOCATION = PropertiesService.getScriptProperties().getProperty('project_location'); +const MODEL_ID = PropertiesService.getScriptProperties().getProperty('model_id'); +const SERVICE_ACCOUNT_KEY = PropertiesService.getScriptProperties().getProperty('service_account_key'); + +/** + * Packages prompt and necessary settings, then sends a request to + * Vertex API. Returns the response as an JSON object extracted from the + * Vertex API response object. + * + * @param prompt - String representing your prompt for Gemini AI. + */ +function getAiSummary(prompt) { + + const request = { + "contents": [{ + "role": "user", + "parts": [{ + "text": prompt + }] + }], + "generationConfig": { + "temperature": 0.1, + "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" + } + ] + }; + + const credentials = credentialsForVertexAI(); + + const fetchOptions = { + method: 'post', + headers: { + 'Authorization': `Bearer ${credentials.accessToken}` + }, + contentType: 'application/json', + muteHttpExceptions: true, + payload: JSON.stringify(request) + } + + const url = `https://${VERTEX_AI_LOCATION}-aiplatform.googleapis.com/v1/projects/${credentials.projectId}/` + + `locations/${VERTEX_AI_LOCATION}/publishers/google/models/${MODEL_ID}:generateContent` + + const response = UrlFetchApp.fetch(url, fetchOptions); + + const payload = JSON.parse(response); + const text = payload.candidates[0].content.parts[0].text + + return text +} + +/** + * Gets credentials required to call Vertex API using a Service Account. + * Requires use of Service Account Key stored with project + * + * @return {!Object} Containing the Cloud Project Id and the access token. + */ +function credentialsForVertexAI() { + const credentials = SERVICE_ACCOUNT_KEY; + if (!credentials) { + throw new Error("service_account_key script property must be set."); + } + + const parsedCredentials = JSON.parse(credentials); + + const service = OAuth2.createService("Vertex") + .setTokenUrl('https://oauth2.googleapis.com/token') + .setPrivateKey(parsedCredentials['private_key']) + .setIssuer(parsedCredentials['client_email']) + .setPropertyStore(PropertiesService.getScriptProperties()) + .setScope("https://www.googleapis.com/auth/cloud-platform"); + return { + projectId: parsedCredentials['project_id'], + accessToken: service.getAccessToken(), + } +} diff --git a/ai/custom_func_vertex/appsscript.json b/ai/custom_func_vertex/appsscript.json new file mode 100644 index 000000000..d1a41c7c1 --- /dev/null +++ b/ai/custom_func_vertex/appsscript.json @@ -0,0 +1,12 @@ +{ + "timeZone": "America/Los_Angeles", + "dependencies": { + "libraries": [{ + "userSymbol": "OAuth2", + "libraryId": "1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF", + "version": "43" + }] + }, + "exceptionLogging": "STACKDRIVER", + "runtimeVersion": "V8" +} \ No newline at end of file