-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgoogle-sheets-gemini-function.js
More file actions
132 lines (111 loc) · 4.83 KB
/
google-sheets-gemini-function.js
File metadata and controls
132 lines (111 loc) · 4.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
As compelling at Google's Gemini features are, and the fact they are sprinkled across the Google Product suite
(including Sheets) it seems like a massive oversight to not include a direct function call from a cell to Gemini.
This script allows you to correct that with a hook into your own API instance.
By doing this you can leverage Gemini AI insights in a spreadsheet, with all the benefits of auto-fill and
updates as data changes.
Background:
To publish as function https://sheetbest.com/blog/deploying-google-sheets-custom-functions/
Url-fetch https://developers.google.com/apps-script/reference/url-fetch
*/
// Setup an API key: https://aistudio.google.com/app/apikey
const apiKey = "???"
// https://discuss.ai.google.dev/t/gemini-pro-open-source-model-suddenly-its-throwing-on-404-error/69200
// const url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent"
const url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-002:generateContent"
const cHITS = 'HITS'
const cMISSES = 'MISSES'
function onInstall(e) {
console.log('onInstall')
onOpen(e)
}
// add menu to google sheets when started
function onOpen() {
console.log('onOpen')
let menu = SpreadsheetApp.getUi().createMenu("Prompt Gemini AI")
menu.addItem("Enable and info", "turnOn")
menu.addItem("Cache Stats", "showCacheInfo")
menu.addToUi()
}
function turnOn() {
SpreadsheetApp.getUi().alert('You can access the Gemini AI prompt using the function PROMPT_GEMINI(prompt, data)')
}
// for the curious
function showCacheInfo() {
let cache = CacheService.getScriptCache()
let cacheHits = cache.get(cHITS)
let cacheMisses = cache.get(cMISSES)
SpreadsheetApp.getUi().alert('Cache Hits ' + (cacheHits ? cacheHits : 0) + ' // Misses (calls to API) ' + (cacheMisses ? cacheMisses : 0) )
}
function PROMPT_GEMINI(objective, dataRange) {
let cache = CacheService.getScriptCache()
let cacheKey = JSON.stringify([objective, dataRange])
console.log("cacheKey", cacheKey)
let cachedResult = cache.get(cacheKey)
let cacheHits = cache.get(cHITS)
let cacheMisses = cache.get(cMISSES)
if (cachedResult) {
let res = JSON.parse(cachedResult)
cacheHits++
cache.put(cHITS, cacheHits)
console.log("cache hit", cacheHits, cachedResult, res)
return res
} else {
cacheMisses++
cache.put(cMISSES, cacheMisses)
console.log("cache miss", cacheMisses)
}
if (!objective || !dataRange) {
console.log("Invalid input: Objective or dataRange is missing.")
return [["Invalid input. Please provide both an objective and a data range."]]
}
if (!Array.isArray(dataRange)) {
dataRange = [[dataRange]] // Converting single cell input to a 2D array
}
let results = []
console.log("Starting to process the data range.")
if (dataRange[0].length === 1) { // Process each cell in a single-column range individually
for (let i = 0; i < dataRange.length; i++) {
let cellData = dataRange[i][0].toString()
let cellPrompt = createPrompt(objective, cellData)
let apiResult = fetchAPIResponse(cellPrompt, url, apiKey)
results.push([apiResult]) // Store each response in its own row
}
} else {
let combinedData = combineData(dataRange)
let combinedPrompt = createPrompt(objective, combinedData)
let apiResult = fetchAPIResponse(combinedPrompt, url, apiKey)
results.push([apiResult])
}
let textToBeCached = JSON.stringify(results)
console.log("Finished", textToBeCached)
cache.put(cacheKey, JSON.stringify(textToBeCached))
return results
}
// Combine all data into a single string or a structured format that the API can understand
function combineData(dataRange) {
return dataRange.map(row => row.join(", ")).join("; ")
}
function createPrompt(objective, rowData) {
return `Objective: ${objective}\nData: ${rowData}\n`.trim()
}
function fetchAPIResponse(prompt, url, apiKey) {
let fullUrl = `${url}?key=${apiKey}`
console.log(`Requesting URL: ${fullUrl}`)
let requestBody = { contents: [{ parts: [{ text: prompt }] }] }
let options = { method: 'post', contentType: 'application/json', payload: JSON.stringify(requestBody), muteHttpExceptions: true }
let response = UrlFetchApp.fetch(fullUrl, options)
let responseCode = response.getResponseCode()
let responseContent = response.getContentText()
console.log(`Response Text: ${responseContent}`)
console.log(`Response Code: ${responseCode}`)
if (responseCode === 200) {
let jsonResponse = JSON.parse(responseContent)
return jsonResponse && jsonResponse.candidates && jsonResponse.candidates.length > 0 ?
jsonResponse.candidates[0].content.parts.map(part => part.text).join(' ') :
"No valid content generated"
} else {
console.error(`API call failed with status code: ${responseCode}`)
return "API call failed"
}
}