├── README.md ├── PromptManager.html ├── Code.gs ├── ApiKeyManager.html └── Sidebar.html /README.md: -------------------------------------------------------------------------------- 1 | # ai-sheet-frontend 2 | 3 | ## Domain 4 | 5 | // Start of Selection 6 | Our application is accessible at [https://aisheeter.com](https://aisheeter.com) 7 | 8 | ## Legal Pages 9 | 10 | - Privacy Policy: [https://aisheeter.com/privacy](https://aisheeter.com/privacy) 11 | - Terms of Service: [https://aisheeter.com/terms](https://aisheeter.com/terms) 12 | 13 | Please ensure these pages are created and contain the appropriate legal information. 14 | -------------------------------------------------------------------------------- /PromptManager.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 25 | 26 | 27 | 36 | 37 | -------------------------------------------------------------------------------- /Code.gs: -------------------------------------------------------------------------------- 1 | var CryptoJS = cCryptoGS.CryptoJS; 2 | 3 | function onOpen() { 4 | var ui = SpreadsheetApp.getUi(); 5 | ui.createMenu('AISheeter - Any LLM in One Google Sheets™') 6 | .addItem('Show Sidebar', 'showSidebar') 7 | // Remove the line below if it exists 8 | // .addItem('Test API Connection', 'testAPIConnection') 9 | .addItem('Open Sidebar', 'openSidebar') 10 | .addItem('Manage Saved Prompts', 'openPromptManager') 11 | .addToUi(); 12 | Sheeter 13 | // Automatically show the sidebar when the spreadsheet is opened 14 | showSidebar(); 15 | } 16 | 17 | function showSidebar() { 18 | var html = HtmlService.createHtmlOutputFromFile('Sidebar') 19 | .setTitle('AI Sheet - Any LLM') 20 | .setWidth(300); 21 | SpreadsheetApp.getUi().showSidebar(html); 22 | } 23 | 24 | function getUserEmail() { 25 | var email = PropertiesService.getUserProperties().getProperty('userEmail'); 26 | if (!email) { 27 | email = Session.getActiveUser().getEmail(); 28 | setUserEmail(email); 29 | } 30 | return email; 31 | } 32 | 33 | function setUserEmail(email) { 34 | PropertiesService.getUserProperties().setProperty('userEmail', email); 35 | } 36 | 37 | function logCreditUsage(creditsUsed) { 38 | var userProperties = PropertiesService.getUserProperties(); 39 | var logs = userProperties.getProperty('creditUsageLogs') || ''; 40 | logs += `Credits used: ${creditsUsed.toFixed(4)}\n`; 41 | userProperties.setProperty('creditUsageLogs', logs); 42 | } 43 | 44 | function AIQuery(model, input, specificModel) { 45 | var userEmail = getUserEmail(); 46 | var userSettings = getUserSettings(); 47 | var encryptedApiKey = encryptApiKey(userSettings[model].apiKey); 48 | if (!specificModel) { 49 | switch(model) { 50 | case 'CHATGPT': 51 | specificModel = 'gpt-4o'; 52 | break; 53 | case 'CLAUDE': 54 | specificModel = 'claude-3-sonnet-20240229'; 55 | break; 56 | case 'GROQ': 57 | specificModel = 'llama-3.1-8b-instant'; 58 | break; 59 | case 'GEMINI': 60 | specificModel = 'gemini-1.5-flash'; 61 | break; 62 | default: 63 | // Optional: handle unknown model or leave specificModel as undefined 64 | break; 65 | } 66 | } 67 | var url = 'https://aisheet.vercel.app/api/query'; 68 | var payload = { 69 | model: model, 70 | input: input, 71 | userEmail: userEmail, 72 | specificModel: specificModel, 73 | encryptedApiKey: encryptedApiKey // This is already encrypted 74 | }; 75 | var options = { 76 | 'method': 'post', 77 | 'contentType': 'application/json', 78 | 'payload': JSON.stringify(payload), 79 | 'muteHttpExceptions': true 80 | }; 81 | 82 | try { 83 | Logger.log('Request URL: ' + url); 84 | Logger.log('Request Payload: ' + JSON.stringify(payload)); 85 | 86 | var response = UrlFetchApp.fetch(url, options); 87 | var responseText = response.getContentText(); 88 | Logger.log('Response: ' + responseText); 89 | 90 | var result = JSON.parse(responseText); 91 | if (result.error) { 92 | return "Error: " + result.error; 93 | } 94 | logCreditUsage(result.creditsUsed); 95 | return result.result; 96 | } catch (error) { 97 | Logger.log('Error: ' + error.toString()); 98 | return "Error: " + error.toString(); 99 | } 100 | } 101 | 102 | /** 103 | * Custom function to call ChatGPT model 104 | * @param {string} input - The input prompt for the model 105 | * @param {string} model - The specific model to use (optional) 106 | * @return {string} - The result from the AI model 107 | * @customfunction 108 | */ 109 | function ChatGPT(input, model) { 110 | return AIQuery('CHATGPT', input, model); 111 | } 112 | 113 | /** 114 | * Custom function to call Claude model 115 | * @param {string} input - The input prompt for the model 116 | * @param {string} model - The specific model to use (optional) 117 | * @return {string} - The result from the AI model 118 | * @customfunction 119 | */ 120 | function Claude(input, model) { 121 | return AIQuery('CLAUDE', input, model); 122 | } 123 | 124 | /** 125 | * Custom function to call Groq model 126 | * @param {string} input - The input prompt for the model 127 | * @param {string} model - The specific model to use (optional) 128 | * @return {string} - The result from the AI model 129 | * @customfunction 130 | */ 131 | function Groq(input, model) { 132 | return AIQuery('GROQ', input, model); 133 | } 134 | 135 | /** 136 | * Custom function to call Gemini model 137 | * @param {string} input - The input prompt for the model 138 | * @param {string} model - The specific model to use (optional) 139 | * @return {string} - The result from the AI model 140 | * @customfunction 141 | */ 142 | function Gemini(input, model) { 143 | return AIQuery('GEMINI', input, model); 144 | } 145 | 146 | // Update the saveAllSettings function 147 | function saveAllSettings(settings) { 148 | try { 149 | console.log('Received settings:', JSON.stringify(settings)); 150 | 151 | // Validate settings 152 | if (!settings || typeof settings !== 'object') { 153 | throw new Error('Invalid settings object'); 154 | } 155 | 156 | const models = ['CHATGPT', 'CLAUDE', 'GROQ', 'GEMINI']; 157 | models.forEach(model => { 158 | if (!settings[model] || typeof settings[model] !== 'object') { 159 | throw new Error(`Invalid settings for ${model}`); 160 | } 161 | if (typeof settings[model].apiKey !== 'string') { 162 | throw new Error(`Invalid API key for ${model}`); 163 | } 164 | if (typeof settings[model].defaultModel !== 'string') { 165 | throw new Error(`Invalid default model for ${model}`); 166 | } 167 | // Encrypt the API key 168 | settings[model].apiKey = encryptApiKey(settings[model].apiKey); 169 | }); 170 | 171 | // Get the user's email 172 | var userEmail = getUserEmail(); 173 | 174 | // Prepare the payload in the format the server expects 175 | var payload = { 176 | userEmail: userEmail, 177 | settings: settings 178 | }; 179 | 180 | // Save settings 181 | var url = 'https://aisheet.vercel.app/api/save-all-settings'; 182 | var options = { 183 | 'method': 'post', 184 | 'contentType': 'application/json', 185 | 'payload': JSON.stringify(payload), 186 | 'muteHttpExceptions': true 187 | }; 188 | 189 | var response = UrlFetchApp.fetch(url, options); 190 | var result = JSON.parse(response.getContentText()); 191 | 192 | if (result.error) { 193 | throw new Error(result.error); 194 | } 195 | 196 | console.log('Settings saved successfully'); 197 | return 'Settings saved successfully'; 198 | } catch (error) { 199 | console.error('Error in saveAllSettings:', error); 200 | throw new Error('Failed to save settings: ' + error.message); 201 | } 202 | } 203 | 204 | // Update the getUserSettings function 205 | function getUserSettings() { 206 | var userEmail = getUserEmail(); 207 | 208 | var url = 'https://aisheet.vercel.app/api/get-user-settings?userEmail=' + encodeURIComponent(userEmail); 209 | 210 | try { 211 | var response = UrlFetchApp.fetch(url); 212 | var result = JSON.parse(response.getContentText()); 213 | var settings = result.settings; 214 | 215 | // Decrypt the API keys to show to the user 216 | Object.keys(settings).forEach(model => { 217 | if (settings[model].apiKey) { 218 | settings[model].apiKey = decryptApiKey(settings[model].apiKey); 219 | } 220 | }); 221 | 222 | return settings; 223 | } catch (error) { 224 | throw new Error("Error fetching user settings: " + error.toString()); 225 | } 226 | } 227 | 228 | function getCreditUsageLogs() { 229 | return PropertiesService.getUserProperties().getProperty('creditUsageLogs') || ''; 230 | } 231 | 232 | function saveApiKey(apiKey) { 233 | // Logic to save the API key 234 | // This might involve saving to a user properties, or sending to a server 235 | PropertiesService.getUserProperties().setProperty('apiKey', apiKey); 236 | } 237 | 238 | function fetchModels() { 239 | var url = 'https://aisheet.vercel.app/api/models'; 240 | try { 241 | var response = UrlFetchApp.fetch(url); 242 | var models = JSON.parse(response.getContentText()); 243 | console.log('Fetched models:', models); // Add this line for debugging 244 | return models; 245 | } catch (error) { 246 | console.error('Error fetching models:', error); 247 | throw error; // Rethrow the error so it can be caught by the failure handler 248 | } 249 | } 250 | 251 | // Update the encryptApiKey function 252 | function encryptApiKey(apiKey) { 253 | var salt = PropertiesService.getScriptProperties().getProperty('ENCRYPTION_SALT'); 254 | if (!salt) { 255 | salt = Utilities.getUuid(); 256 | PropertiesService.getScriptProperties().setProperty('ENCRYPTION_SALT', salt); 257 | } 258 | return CryptoJS.AES.encrypt(apiKey, salt).toString(); 259 | } 260 | 261 | // Update the decryptApiKey function 262 | function decryptApiKey(encryptedApiKey) { 263 | var salt = PropertiesService.getScriptProperties().getProperty('ENCRYPTION_SALT'); 264 | return CryptoJS.AES.decrypt(encryptedApiKey, salt).toString(CryptoJS.enc.Utf8); 265 | } 266 | 267 | function setEncryptionSalt() { 268 | var salt = Utilities.getUuid(); 269 | PropertiesService.getScriptProperties().setProperty('ENCRYPTION_SALT', salt); 270 | Logger.log('Encryption salt set: ' + salt); 271 | } 272 | 273 | function generateAndSetEncryptionSalt() { 274 | var salt = Utilities.getUuid(); 275 | PropertiesService.getScriptProperties().setProperty('ENCRYPTION_SALT', salt); 276 | Logger.log('New encryption salt generated and set: ' + salt); 277 | return salt; 278 | } 279 | 280 | function getSavedPrompts() { 281 | // Call your backend API to fetch saved prompts 282 | // Return the prompts to the frontend 283 | } 284 | 285 | function savePrompt(name, prompt, variables) { 286 | // Call your backend API to save a new prompt 287 | } 288 | 289 | function updatePrompt(id, name, prompt, variables) { 290 | // Call your backend API to update an existing prompt 291 | } 292 | 293 | function deletePrompt(id) { 294 | // Call your backend API to delete a prompt 295 | } -------------------------------------------------------------------------------- /ApiKeyManager.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

AI Assistant

10 |
11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 | 184 | 185 | -------------------------------------------------------------------------------- /Sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 32 | 33 | 34 |
35 |
36 |

AISheeter - Any LLM in One Google Sheets™

37 | 42 |
43 |
44 | 57 |
58 | 59 |
60 |
61 | 62 | 354 | 355 | --------------------------------------------------------------------------------