Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions apps-script/generative-ai/cat-add-on/Code.gs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Copyright 2025 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
*
* http://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.
*/

// --- Add-on Entry Points ---

/**
* Returns the add-on's homepage card.
*
* @param {GoogleAppsScript.Addons.EventObject} e The event object.
* @returns {GoogleAppsScript.Card_Service.Card} The constructed Card object.
*/
function onHomepage(e) {
// Calls the card builder function defined in CardBuilder.js
return createCatCard();
}

/**
* Handles the button click action to refresh the card with a new cat image.
*
* @param {GoogleAppsScript.Addons.EventObject} e The event object.
* @returns {GoogleAppsScript.Card_Service.ActionResponse} The action response to update the card.
*/
function updateCatImage(e) {
// Re-run the function that generates the new image and builds the card.
const updatedCard = createCatCard();

// Create an ActionResponse to push the new card to the user's interface.
return CardService.newActionResponseBuilder()
.setNavigation(CardService.newNavigation().updateCard(updatedCard))
.build();
}

// NOTE: This function needs access to the global constants defined in Configuration.gs

/**
* Creates a card with an AI-generated cat image and a refresh button.
*
* @returns {GoogleAppsScript.Card_Service.Card} The card object.
*/
function createCatCard() {
// Calls the API function defined in VertexAI.js
const imageDataUri = generateImage(IMAGE_PROMPT);

// Define the action to be taken when the button is clicked
// Calls the function defined in Code.gs
const updateAction = CardService.newAction()
.setFunctionName('updateCatImage');

// Create the "Generate New Cat" button
const button = CardService.newTextButton()
.setText('Generate New Cat 🐈')
.setTextButtonStyle(CardService.TextButtonStyle.FILLED)
.setOnClickAction(updateAction);

// Create the image widget using the generated Data URI
const imageWidget = CardService.newImage()
.setImageUrl(imageDataUri)
.setAltText('An AI-generated cat');

// Build the card structure
const header = CardService.newCardHeader().setTitle('AI Generated Cat');
const section = CardService.newCardSection()
.addWidget(imageWidget)
.addWidget(button);

return CardService.newCardBuilder()
.setHeader(header)
.addSection(section)
.build();
}
26 changes: 26 additions & 0 deletions apps-script/generative-ai/cat-add-on/Configuration.gs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright 2025 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
*
* http://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.
*/

// --- Configuration & Constants ---

/**
* Your Google Cloud Project ID for Vertex AI access.
* ⚠️ WARNING: Update this value before running.
* NOTE: Ensure the Apps Script project has the 'https://www.googleapis.com/auth/cloud-platform' scope.
*/
const PROJECT_ID = 'your-project-id';
const MODEL_ID = 'gemini-2.5-flash-image';
const IMAGE_PROMPT = 'A high-quality, photorealistic image of a random cat.';
83 changes: 83 additions & 0 deletions apps-script/generative-ai/cat-add-on/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# AI Generated Cat - Google Workspace Add-on

This Google Workspace Add-on displays an AI-generated image of a cat in the Gmail side panel. Users can click a button to generate a new cat image.

The add-on leverages the Gemini 2.5 Flash image model via the Google Vertex AI API.

## Prerequisites

Before you begin, ensure you have the following:

1. **Google Cloud Project:** A Google Cloud project to host the add-on and enable the Vertex AI API.
2. **Google Cloud SDK:** The `gcloud` command-line tool installed and authenticated. You can find installation instructions [here](https://cloud.google.com/sdk/docs/install).
3. **clasp:** The command-line tool for Apps Script development. Install it using npm:
```bash
npm install -g @google/clasp
```

## Setup and Configuration

1. **Enable the Vertex AI API:**

Enable the Vertex AI API in your Google Cloud project:
```bash
gcloud services enable aiplatform.googleapis.com --project=YOUR_PROJECT_ID
```
Replace `YOUR_PROJECT_ID` with your actual Google Cloud project ID.

2. **Configure the Project ID:**

Open the `Configuration.js` file and update the `PROJECT_ID` constant with your Google Cloud project ID:
```javascript
const PROJECT_ID = 'YOUR_PROJECT_ID';
```

3. **Log in to clasp:**

Authenticate `clasp` with your Google account:
```bash
clasp login
```

## Deployment

1. **Push the Apps Script Project:**

Use `clasp` to push your local code to your Apps Script project:
```bash
clasp push
```

2. **Create a Google Workspace Add-on Deployment:**

Create a deployment for your add-on using the `gcloud` command:
```bash
gcloud workspace-add-ons deployments create my-deployment \
--project=YOUR_PROJECT_ID \
--add-on-type=GMAIL \
--name="AI Generated Cat" \
--description="An add-on that shows AI generated cats." \
--script-id=YOUR_SCRIPT_ID
```
- Replace `YOUR_PROJECT_ID` with your Google Cloud project ID.
- Replace `YOUR_SCRIPT_ID` with your Apps Script project's script ID. You can find this in the `.clasp.json` file or in the Apps Script editor URL.

## Installation

To install the add-on for your own account, use the following command:

```bash
gcloud workspace-add-ons deployments install my-deployment --project=YOUR_PROJECT_ID
```
- Replace `my-deployment` with the name of the deployment you created in the previous step.
- Replace `YOUR_PROJECT_ID` with your Google Cloud project ID.

After installation, you should see the "AI Generated Cat" add-on in your Gmail side panel.

## OAuth Scopes

This add-on requires the following OAuth scopes:

* `https://www.googleapis.com/auth/cloud-platform`: To access the Vertex AI API.
* `https://www.googleapis.com/auth/gmail.addons.execute`: To run as a Gmail add-on.
* `https://www.googleapis.com/auth/script.external_request`: To make external HTTP requests using `UrlFetchApp`.
82 changes: 82 additions & 0 deletions apps-script/generative-ai/cat-add-on/VertexAI.gs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright 2025 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
*
* http://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.
*/

/**
* Generates an image using the gemini-2.5-flash-image model on Vertex AI
* and returns it as a Data URI.
*
* @param {string} prompt The text prompt to generate the image from.
* @returns {string} A Data URI string (e.g., 'data:image/png;base64,...') or a fallback image URL on error.
*/
function generateImage(prompt) {
// Uses global constants from Configuration.gs
const ENDPOINT = `https://aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/publishers/google/models/${MODEL_ID}:generateContent`;

const payload = {
generationConfig: {
responseModalities: ['IMAGE', 'TEXT'],
imageConfig: {
aspectRatio: '1:1',
}
},
contents: [{
role: 'user',
parts: [{
text: prompt
}]
}]
};

const options = {
method: 'post',
contentType: 'application/json',
headers: {
Authorization: `Bearer ${ScriptApp.getOAuthToken()}`,
Accept: 'application/json'
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};

try {
const response = UrlFetchApp.fetch(ENDPOINT, options);
const responseBody = response.getContentText();
const responseData = JSON.parse(responseBody);

if (response.getResponseCode() !== 200) {
console.error(`Vertex AI API Error (${response.getResponseCode()}): ${responseBody}`);
const errorMessage = responseData?.error?.message || 'Unknown API Error';
throw new Error(`Vertex AI API call failed: ${errorMessage}`);
}

const imagePart = responseData.candidates?.[0]?.content?.parts?.find(
part => part.inlineData?.mimeType?.startsWith('image/')
);

if (!imagePart) {
console.error(`No image data found in response: ${responseBody}`);
throw new Error('Image generation failed or no image data was returned.');
}

const { data: base64Data, mimeType } = imagePart.inlineData;

return `data:${mimeType};base64,${base64Data}`;

} catch (e) {
console.error(`An error occurred during image generation: ${e.toString()}`);
return 'https://www.google.com/images/errors/robot.png';
}
}
21 changes: 21 additions & 0 deletions apps-script/generative-ai/cat-add-on/appsscript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"timeZone": "America/New_York",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/script.external_request"
],
"gmail": {
"name": "AI Generated Cat",
"logoUrl": "https://fonts.gstatic.com/s/i/googlematerialicons/pets/v12/gm_blue-48dp/1x/gm_pets_gm_blue_48dp.png",
"homepageTrigger": {
"runFunction": "onHomepage"
}
},
"urlFetchWhitelist": [
"https://*.googleapis.com/"
]
}
Loading