// Adds a custom AI menu to the Google Docs interface function onOpen() { DocumentApp.getUi() .createMenu("AI Tools") .addItem("Format with GPT", "sendToGPT") .addItem("Format with Claude", "sendToClaude") .addToUi(); } // Retrieves the OpenAI API key from script properties function getApiKey() { const props = PropertiesService.getScriptProperties(); return props.getProperty("OPENAI_API_KEY"); } // Retrieves the Claude API key from script properties function getClaudeApiKey() { const props = PropertiesService.getScriptProperties(); return props.getProperty("CLAUDE_API_KEY"); } // Sends selected text to GPT and inserts the response after the selection function sendToGPT() { const doc = DocumentApp.getActiveDocument(); const selection = doc.getSelection(); if (!selection) { DocumentApp.getUi().alert("Please select some text in the document."); return; } let selectedText = ""; const elements = selection.getRangeElements(); for (const el of elements) { if (el.getElement().editAsText) { selectedText += el.getElement().asText().getText().substring( el.getStartOffset(), el.getEndOffsetInclusive() + 1 ); } } const instruction = DocumentApp.getUi().prompt("Enter your instruction for GPT (e.g. summarise, rewrite):").getResponseText(); if (!instruction) return; const prompt = `${instruction}:\n\n${selectedText}`; const response = callGPTAPI(prompt); if (response) { for (const el of elements.reverse()) { if (el.getElement().editAsText) { const text = el.getElement().asText(); const end = el.getEndOffsetInclusive(); text.insertText(end + 1, "\n---\nGPT response:\n" + response); break; } } } } // Sends selected text to Claude and inserts the response after the selection function sendToClaude() { const doc = DocumentApp.getActiveDocument(); const selection = doc.getSelection(); if (!selection) { DocumentApp.getUi().alert("Please select some text in the document."); return; } let selectedText = ""; const elements = selection.getRangeElements(); for (const el of elements) { if (el.getElement().editAsText) { selectedText += el.getElement().asText().getText().substring( el.getStartOffset(), el.getEndOffsetInclusive() + 1 ); } } const instruction = DocumentApp.getUi().prompt("Enter your instruction for Claude (e.g. simplify, edit, summarise):").getResponseText(); if (!instruction) return; const prompt = `${instruction}:\n\n${selectedText}`; const response = callClaudeAPI(prompt); if (response) { for (const el of elements.reverse()) { if (el.getElement().editAsText) { const text = el.getElement().asText(); const end = el.getEndOffsetInclusive(); text.insertText(end + 1, "\n---\nClaude response:\n" + response); break; } } } } // Calls the OpenAI GPT API and returns the response function callGPTAPI(prompt) { const url = "https://api.openai.com/v1/chat/completions"; const payload = { model: "gpt-4", messages: [ { role: "system", content: "You are a helpful scientific writing assistant." }, { role: "user", content: prompt } ], temperature: 0.7 }; const options = { method: "post", contentType: "application/json", headers: { Authorization: `Bearer ${getApiKey()}` }, payload: JSON.stringify(payload), muteHttpExceptions: true }; try { const response = UrlFetchApp.fetch(url, options); const json = JSON.parse(response.getContentText()); if (json.choices && json.choices.length > 0) { return json.choices[0].message.content.trim(); } else { Logger.log(json); DocumentApp.getUi().alert("No response received from GPT."); return null; } } catch (e) { Logger.log(e); DocumentApp.getUi().alert("GPT API error: " + e); return null; } } // Calls the Claude API and returns the response function callClaudeAPI(prompt) { const url = "https://api.anthropic.com/v1/messages"; const payload = { model: "claude-3-opus-20240229", max_tokens: 1024, temperature: 0.7, messages: [ { role: "user", content: prompt } ] }; const options = { method: "post", contentType: "application/json", headers: { "x-api-key": getClaudeApiKey(), "anthropic-version": "2023-06-01" }, payload: JSON.stringify(payload), muteHttpExceptions: true }; try { const response = UrlFetchApp.fetch(url, options); const json = JSON.parse(response.getContentText()); if (json.content && json.content.length > 0 && json.content[0].text) { return json.content[0].text.trim(); } else { Logger.log(json); DocumentApp.getUi().alert("No response received from Claude."); return null; } } catch (e) { Logger.log(e); DocumentApp.getUi().alert("Claude API error: " + e); return null; } }