
Generate API Key
- Open Google AI Studio : Go to AI Studio and sign in with your Google account.
- Click on Get API Key.
3. If you already have an API key created, click on it and copy it. If not, click on Create API Key.
4. You will be prompted to provide a name for the API key, followed by selecting a Google Cloud Project. To learn more about using Gemini API Keys click here.
5. Once done click Create Key and your Gemini API key is ready.
While you are in the Google Doc, let’s open up the Script Editor to write some Google Apps Script. To open the Script Editor, follow these steps:
- Click on Extensions and open the Script Editor.
2. This brings up the Script Editor as shown below.
We have reached the script editor, let’s code.
const GEMINI_API_KEY = "AIzaSyABOylpWPu3AvTcV0KTo8YsqkIgml6Bzgk";function onOpen()
We start off by declaring the API key we created from Google AI Studio. The next part of the code sets up the custom menu inside your Google Docs interface. This is handled by the onOpen()
function, which runs automatically whenever the document is opened.
Inside this function, we use DocumentApp.getUi()
to access the document’s user interface and create a new menu titled AI Comment Tools. A menu item called Summarize All Comments (Gemini) is then added, which links to the summarizeDocComments()
function.
This makes it easy for users to trigger the summarization process directly from the Google Docs menu bar without needing to open the script editor.
function listComments() {
const documentId = DocumentApp.getActiveDocument().getId();
const documentBodyText = DocumentApp.getActiveDocument().getBody().getText();
const options = '[No context found]';let allCommentsAndContext = '';
let commentCount = 0;
try {
const response = Drive.Comments.list(documentId, options);
const comments = response.comments;
if (!comments || comments.length === 0) '[No context found]'
commentCount = comments.length;
comments.forEach(comment => {
const author = comment.author ? comment.author.displayName : 'Unknown Author';
const commentContent = comment.content || '[No Content]';
let contextualText = '';
if (comment.anchor && comment.anchor.range) contextualText
allCommentsAndContext += `--- Context for Comment by $ ---\n`;
allCommentsAndContext += `${contextualText || '[No context found]'}\n`;
allCommentsAndContext += `--- Comment: ---\n${commentContent}\n`;
if (comment.replies && comment.replies.length > 0) {
comment.replies.forEach(reply => {
const replyAuthor = reply.author ? reply.author.displayName : 'Unknown Author';
const replyContent = reply.content || '[No Content]';
allCommentsAndContext += ` ↳ Reply from ${replyAuthor}: ${replyContent}\n`;
});
}
allCommentsAndContext += `------------------------\n\n`;
});
} catch (e) {
Logger.log(`Error fetching comments: ${e.message}`);
return { commentsString: '', count: 0 };
}
return { commentsString: allCommentsAndContext, count: commentCount };
}
The core of the solution lies in the listComments()
function. This function retrieves all comments, replies, and their associated contextual text from the active document.
It first gets the document’s ID using DocumentApp.getActiveDocument().getId()
and extracts the full document text. It then calls Drive.Comments.list()
from the Google Drive API to fetch all comments, along with their metadata such as author name, creation time, resolved status, and the content of replies.
For each comment, the function checks if an anchor range exists, which indicates where in the document the comment was made. It then extracts the specific portion of text from the document body that corresponds to that comment, ensuring that the summary later produced has clear context. The function also iterates through any replies to a comment, capturing their author and content.
Finally, it compiles all of this information into a single string with clear markers for context and comment content, which will later be fed to Gemini for summarization.
function generateSummary(commentsAndContext) {
const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_API_KEY}`;
const promptText = `Summarize the following Google Docs comments (with context). Identify key feedback, action items, and recurring themes. Format as clear bullet points. Be concise and omit quotation marks.\n\n${commentsAndContext}`;const headers = { "Content-Type": "application/json" };
const payload = JSON.stringify({
contents: [{ parts: [{ text: promptText }] }]
});
const response = UrlFetchApp.fetch(endpoint, {
method: "POST",
headers: headers,
payload: payload,
muteHttpExceptions: true
});
const json = JSON.parse(response.getContentText());
if (json.candidates && json.candidates.length > 0) {
return json.candidates[0].content.parts[0].text.trim();
} else if (json.error) {
throw new Error(json.error.message);
} else {
throw new Error("Unexpected API response format.");
}
}
The generateSummary()
function is where the magic happens. This function takes the compiled string of comments and context and sends it to the Gemini API for summarization.
It constructs a prompt instructing Gemini to summarize the comments, identify key feedback, extract action items, and highlight recurring themes. The prompt also specifies that the summary should be concise and formatted as bullet points without quotation marks.
The function then makes a POST request to the Gemini API endpoint using UrlFetchApp.fetch()
, passing the API key and the prepared prompt. Once Gemini returns a response, the function parses it and extracts the summary text. If there is any issue with the API call, such as an invalid key or unexpected response format, the function throws an error and stops execution.
function insertStyledSummary(summary) {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();// Insert header
const header = body.insertParagraph(0, "🧠 AI Comment Summary (Powered by Gemini AI)");
header.setHeading(DocumentApp.ParagraphHeading.HEADING2);
// Insert timestamp
const timestamp = new Date().toLocaleString();
const timestampPara = body.insertParagraph(1, `Generated on: ${timestamp}`);
timestampPara.setItalic(true);
// Insert summary content
const summaryPara = body.insertParagraph(2, summary);
summaryPara.setSpacingAfter(12);
}
Finally, the insertStyledSummary()
function takes the AI-generated summary and inserts it at the very top of the document. It first inserts a heading that clearly labels the section as an AI-generated summary powered by Gemini. It also adds a timestamp showing when the summary was generated, providing useful context for collaborators reviewing the document.
The actual summary text is inserted below the heading with extra spacing for readability. This ensures that the summary is prominent and easy to access, turning a document’s buried comments into a concise, actionable overview that accelerates collaboration.
function summarizeDocComments() {
const ui = DocumentApp.getUi();if (!GEMINI_API_KEY || GEMINI_API_KEY === "YOUR_GEMINI_API_KEY") {
ui.alert("API Key Missing", "Please add your Gemini API key at the top of the script.", ui.ButtonSet.OK);
return;
}
ui.alert("Fetching all comments from your document...");
const commentsData = listComments();
if (!commentsData.count) {
ui.alert("No Comments Found", "This document has no comments to summarize.", ui.ButtonSet.OK);
return;
}
const confirmMessage = `Found ${commentsData.count} comments. Do you want to summarize them using Gemini AI?`;
const userResponse = ui.alert("Confirm Summarization", confirmMessage, ui.ButtonSet.YES_NO);
if (userResponse !== ui.Button.YES) {
ui.alert("Action Cancelled", "Comment summarization was cancelled.", ui.ButtonSet.OK);
return;
}
ui.alert("Processing...", "Sending comments to Gemini AI for summarization.", ui.ButtonSet.OK);
let summary;
try {
summary = generateSummary(commentsData.commentsString);
} catch (e) {
ui.alert("Gemini API Error", `An error occurred: ${e.message}`, ui.ButtonSet.OK);
return;
}
insertStyledSummary(summary);
ui.alert("✅ Summary Inserted", "Your AI summary has been added to the top of the document.", ui.ButtonSet.OK);
}
The entire process is tied together by the summarizeDocComments()
function, which acts as the main controller. This function verifies that a valid Gemini API key is available, calls listComments()
to gather all comments, then sends them to Gemini using generateSummary()
, and finally inserts the result into the document using insertStyledSummary()
.
It also contains error handling to alert the user if there are no comments or if an issue occurs during API communication. Together, these functions create a smooth, automated workflow that transforms scattered comments into clear, actionable insights with a single click.
Our code is complete and good to go.
Check the Output
Its time to see if the code is able to retrieve the comments, generate a summary and insert it back into the
Source Credit: https://medium.com/google-cloud/turn-google-docs-comments-into-actionable-summaries-with-gemini-and-google-apps-script-599527d74ca9?source=rss—-e52cf94d98af—4