diff --git a/README.md b/README.md index 20cab1a..7a67c66 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,139 @@ # GenAIApp -The **GenAIApp** library is a Google Apps Script library designed for creating, managing, and interacting with LLMs using Gemini and OpenAI's API. The library provides features like text-based conversation, browsing the web, image analysis, and more, allowing you to build versatile AI chat applications that can integrate with various functionalities and external data sources. +The **GenAIApp** library is a Google Apps Script library designed for creating, managing, and interacting with LLMs using Gemini and OpenAI APIs. It supports text conversations, web browsing, image and document analysis, function calling, vector-store retrieval, MCP connectors, and Google Workspace integrations. ## Table of Contents - [Features](#features) +- [Samples](#samples) + - [Quick Start](#quick-start) + - [Getting Started](#getting-started) + - [Content Analysis](#content-analysis) + - [Function Calling](#function-calling) + - [Advanced Features](#advanced-features) + - [Integration](#integration) - [Prerequisites](#prerequisites) - [Installation](#installation) -- [Usage](#usage) +- [API Guide](#api-guide) - [Setting API Keys](#setting-api-keys) - [Creating a New Chat](#creating-a-new-chat) - [Adding Messages](#adding-messages) - [Adding Callable Functions to the Chat](#adding-callable-functions-to-the-chat) - - [Enable Web Browsing (Optional)](#enable-web-browsing-optional) - - [Enable OpenAI server-side compaction (Optional)](#enable-openai-server-side-compaction-optional) - - [Give a Web Page as a Knowledge Base (Optional)](#give-a-web-page-as-a-knowledge-base-optional) - - [Add Image (Optional)](#add-image-optional) - - [Add File to Chat (optional)](#add-file-to-chat-optional) - - [Add MCP Connector (optional)](#add-mcp-connector-optional) + - [Enable Web Browsing Optional](#enable-web-browsing-optional) + - [Enable OpenAI Server-Side Compaction Optional](#enable-openai-server-side-compaction-optional) + - [Give a Web Page as a Knowledge Base Optional](#give-a-web-page-as-a-knowledge-base-optional) + - [Add Image Optional](#add-image-optional) + - [Add File to Chat Optional](#add-file-to-chat-optional) + - [Add an MCP Connector Optional](#add-an-mcp-connector-optional) - [Running the Chat](#running-the-chat) - [FunctionObject Class](#functionobject-class) - - [Creating a Function](#creating-a-function) - - [Configuring Parameters](#configuring-parameters) - [VectorStoreObject Class](#vectorstoreobject-class) - - [Retrieving Knowledge from an OpenAI Vector Store](#retrieving-knowledge-from-an-openai-vector-store) -- [Examples](#examples) - - [Example 1: Send a Prompt and Get Completion](#example-1--send-a-prompt-and-get-completion) - - [Example 2: Ask Open AI to Create a Draft Reply for the Last Email in Gmail Inbox](#example-2--ask-open-ai-to-create-a-draft-reply-for-the-last-email-in-gmail-inbox) - - [Example 3: Retrieve Structured Data Instead of Raw Text with onlyReturnArguments](#example-3--retrieve-structured-data-instead-of-raw-text-with-onlyreturnargument) - - [Example 4: Use Web Browsing](#example-4--use-web-browsing) - - [Example 5: Describe an Image](#example-5--describe-an-image) - - [Example 6: Extend a Chat with an MCP Connector](#example-6--extend-a-chat-with-an-mcp-connector) - - [Example 7: Connect to a Custom MCP Server with setServerUrl()](#example-7--connect-to-a-custom-mcp-server-with-setserverurl) - - [Example 8: Continue a Conversation with previous_response_id](#example-8--continue-a-conversation-with-previous_response_id) -- [Contributing](#contributing) -- [License](#license) - [Reference](#reference) - - [GenAIApp](#genaiapp) + - [GenAIApp Factory](#genaiapp-factory) - [Chat](#chat) - [Function Object](#function-object) - [Vector Store Object](#vector-store-object) - [Connector Object](#connector-object) - +- [Contributing](#contributing) +- [License](#license) ## Features -- **Chat Creation:** Create interactive chats that can send and receive messages using Gemini or OpenAI's API. +- **Chat Creation:** Create interactive chats that can send and receive messages using Gemini or OpenAI APIs. - **Web Search Integration:** Perform web searches to enhance chatbot responses. -- **Image Analysis:** Retrieve image descriptions using Gemini and OpenAI's vision models. -- **Function Calling:** Enable the chat to call predefined functions and utilize their results in conversations. +- **Image Analysis:** Retrieve image descriptions using Gemini and OpenAI vision models. +- **Function Calling:** Enable the chat to call predefined functions and use their results in conversations. - **Vector Store Search:** Retrieve knowledge from OpenAI vector stores for a better contextual response. -- **Document Analysis:** Analyze documents from Google Drive with support for various formats. -- **MCP Connectors:** Attach Google Workspace or custom Model Context Protocol connectors to securely retrieve additional context - during a conversation. +- **Document Analysis:** Analyze documents from Google Drive with support for multiple formats. +- **MCP Connectors:** Attach Google Workspace or custom Model Context Protocol connectors to securely retrieve additional context during a conversation. + +## Samples + +> **Start here:** The `samples/` directory contains copyable Google Apps Script examples organized by use case. Use this section as the fastest way to find a working pattern before reading the detailed API guide. + +### Quick Start + +Try [`samples/simple-chat.gs`](samples/simple-chat.gs) first. It is the recommended hello-world smoke test: set an OpenAI API key in Script Properties as `OPENAI_API_KEY`, paste or import the sample into Apps Script, and run `simpleChatSample()` to log a short model response. + +### Getting Started + +| Sample | Description | Demonstrates | +| --- | --- | --- | +| [`simple-chat.gs`](samples/simple-chat.gs) | Smallest possible GenAIApp chat request for a hello-world smoke test. | `setOpenAIAPIKey()`, `newChat()`, `addMessage()`, `run()` | +| [`system-prompts.gs`](samples/system-prompts.gs) | Sets assistant role, tone, and response format with a system message. | System messages with `addMessage(message, true)`, prompt shaping | +| [`configuration-options.gs`](samples/configuration-options.gs) | Shows common configuration and guardrail settings for production scripts. | `disableLogs()`, `warnIfResponseTokenUsageAbove()`, `enableCompaction()`, `setMaximumAPICalls()` | +| [`multi-model-usage.gs`](samples/multi-model-usage.gs) | Reuses the same prompt across OpenAI and Gemini models for comparison. | `setOpenAIAPIKey()`, `setGeminiAPIKey()`, model selection in `run()` | +| [`vertex-ai-setup.gs`](samples/vertex-ai-setup.gs) | Authenticates Gemini through a linked Google Cloud project instead of an API key. | `setGeminiAuth()`, Vertex AI configuration, Gemini model execution | + +### Content Analysis + +| Sample | Description | Demonstrates | +| --- | --- | --- | +| [`image-analysis.gs`](samples/image-analysis.gs) | Sends both a public image URL and an Apps Script Blob to a vision-capable model. | `addImage()`, multimodal prompts, Blob inputs | +| [`document-analysis.gs`](samples/document-analysis.gs) | Summarizes PDFs or exported Google Workspace files from Drive and Blob inputs. | `addFile()`, Drive file IDs, Blob file analysis | +| [`knowledge-links.gs`](samples/knowledge-links.gs) | Injects a known web page as direct context without broad web search. | `addKnowledgeLink()`, page-grounded answers | +| [`web-browsing.gs`](samples/web-browsing.gs) | Allows real-time browsing with an optional trusted-domain restriction. | `enableBrowsing(true, url)`, current-information prompts | +| [`vector-store-rag.gs`](samples/vector-store-rag.gs) | Creates a vector store, uploads source content, queries it, and returns chunks. | `newVectorStore()`, `uploadAndAttachFile()`, `addVectorStores()`, `onlyReturnChunks()` | + +### Function Calling + +| Sample | Description | Demonstrates | +| --- | --- | --- | +| [`function-calling-basics.gs`](samples/function-calling-basics.gs) | Registers a single callable tool and lets the model use Apps Script code. | `newFunction()`, `setName()`, `setDescription()`, `addParameter()`, `addFunction()` | +| [`function-calling-advanced.gs`](samples/function-calling-advanced.gs) | Extracts structured arguments or ends early after a tool result. | `onlyReturnArguments(true)`, `endWithResult(true)`, tool-routing patterns | + +### Advanced Features + +| Sample | Description | Demonstrates | +| --- | --- | --- | +| [`conversation-continuation.gs`](samples/conversation-continuation.gs) | Continues an OpenAI Responses API conversation without resending the full transcript. | `retrieveLastResponseId()`, `setPreviousResponseId()` | +| [`configuration-options.gs`](samples/configuration-options.gs) | Configures operational controls for long-running or budget-sensitive automations. | Token warnings, API call limits, compaction thresholds, logging controls | +| [`multi-model-usage.gs`](samples/multi-model-usage.gs) | Compares outputs from GPT, Gemini, and reasoning models with one prompt. | Multiple model IDs, `reasoning_effort`, provider switching | +| [`vector-store-rag.gs`](samples/vector-store-rag.gs) | Builds retrieval-augmented generation on OpenAI vector stores. | Vector-store lifecycle, chunking, attributes, retrieval responses | + +### Integration + +| Sample | Description | Demonstrates | +| --- | --- | --- | +| [`sheets-ai-assistant.gs`](samples/sheets-ai-assistant.gs) | Reads active Google Sheets data, asks AI for observations, and writes results back. | `SpreadsheetApp`, sheet-bound workflows, chat summarization | +| [`mcp-connectors.gs`](samples/mcp-connectors.gs) | Configures Gmail, Calendar, and Drive MCP connectors for Workspace-aware responses. | `newConnector()`, `setConnectorId()`, `setAuthorization()`, `addMCP()` | +| [`google-mcp-connector.gs`](samples/google-mcp-connector.gs) | Connects directly to Google's Native Gmail MCP endpoint. | `setServerUrl()`, native Google MCP endpoint setup, OAuth token authorization | ## Prerequisites -The setup for **GenAIApp** varies depending on which models you plan to use: -1. If you want to use **OpenAI models**: You'll need an **OpenAI API key** -2. If you want to use **Google Gemini models**: you’ll need a **Google Cloud Platform (GCP) project** with **Vertex AI** enabled for access to Gemini models. -Ensure to link your Google Apps Script project to a GCP project with Vertex AI enabled, and to include the following scopes in your manifest file: +Choose the credentials that match the models you plan to use: + +1. **OpenAI models:** Store an OpenAI API key for `GenAIApp.setOpenAIAPIKey()`. +2. **Gemini with an API key:** Store a Gemini API key for `GenAIApp.setGeminiAPIKey()`. +3. **Gemini through Vertex AI:** Link your Apps Script project to a Google Cloud project with Vertex AI enabled, then use `GenAIApp.setGeminiAuth(projectId, region)`. + +For Vertex AI or Google Workspace MCP connectors, include the required OAuth scopes in your Apps Script manifest. Start with: + ```js "oauthScopes": [ - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/script.external_request" - ] + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/script.external_request" +] ``` -1. An **OpenAI API key** for accessing OpenAI models. -2. A **Gemini API key** OR a **Google Cloud Platform (GCP) project** for using Gemini models. +> **Note:** The `oauthScopes` array shown above is a starting point. Google Workspace MCP connectors (Gmail, Calendar, Drive) require additional service-specific scopes. For example, Gmail requires `https://www.googleapis.com/auth/gmail.readonly`, Calendar requires `https://www.googleapis.com/auth/calendar`, and Drive requires `https://www.googleapis.com/auth/drive.readonly`. The exact scopes depend on which connectors and operations you use, so you must add the corresponding scopes to the `oauthScopes` manifest entry. ## Installation -To start using the library, include the **GenAIApp** code in your Google Apps Script project environment. +Setup is intentionally lightweight: drag and drop the **GenAIApp** library files into your Google Apps Script project, add the needed credentials in Script Properties, and start with [`samples/simple-chat.gs`](samples/simple-chat.gs). -## Usage +## API Guide ### Setting API Keys -You need to set your API keys before starting any chat: +You need to set your API keys before starting any chat. See [`samples/simple-chat.gs`](samples/simple-chat.gs), [`samples/multi-model-usage.gs`](samples/multi-model-usage.gs), and [`samples/vertex-ai-setup.gs`](samples/vertex-ai-setup.gs) for complete setup examples. ```js // Set Gemini API Key GenAIApp.setGeminiAPIKey('your-gemini-api-key'); // Set Gemini Auth if using Google Cloud -GenAIApp.setGeminiAuth('your-gcp-project-id','your-region'); +GenAIApp.setGeminiAuth('your-gcp-project-id', 'your-region'); // Set OpenAI API Key if using OpenAI GenAIApp.setOpenAIAPIKey('your-openai-api-key'); @@ -99,61 +147,61 @@ GenAIApp.setPrivateInstanceBaseUrl('https://your-endpoint.example.com'); ### Creating a New Chat -To start a new chat, call the `newChat()` method. This creates a new Chat instance. +To start a new chat, call the `newChat()` method. This creates a new `Chat` instance. For the smallest runnable version, use [`samples/simple-chat.gs`](samples/simple-chat.gs). ```js -let chat = GenAIApp.newChat(); +const chat = GenAIApp.newChat(); ``` ### Adding Messages -You can add messages to your chat using the `addMessage()` method. Messages can be from the user or the system. +You can add messages to your chat using the `addMessage()` method. Messages can be from the user or from the system. See [`samples/system-prompts.gs`](samples/system-prompts.gs) for a focused system-prompt example. ```js // Add a user message -chat.addMessage("Hello, how are you?"); +chat.addMessage('Hello, how are you?'); // Add a system message (optional) -chat.addMessage("Answer to the user in a professional way.", true); +chat.addMessage('Answer to the user in a professional way.', true); ``` -### Adding callable Functions to the Chat +### Adding Callable Functions to the Chat -You can create and add functions to the chat that the AI can call during the conversation: -The `newFunction()` method allows you to create a new Function instance. You can then add this function to your chat using the `addFunction()` method. +You can create functions that the AI can call during the conversation. The `newFunction()` method creates a `FunctionObject`, and `addFunction()` attaches it to your chat. See [`samples/function-calling-basics.gs`](samples/function-calling-basics.gs) and [`samples/function-calling-advanced.gs`](samples/function-calling-advanced.gs) for runnable patterns. ```js -// Create a new function -let myFunction = GenAIApp.newFunction() - .setName("getWeather") - .setDescription("Retrieve the current weather for a given city.") - .addParameter("city", "string", "The name of the city."); +const myFunction = GenAIApp.newFunction() + .setName('getWeather') + .setDescription('Retrieve the current weather for a given city.') + .addParameter('city', 'string', 'The name of the city.'); -// Add the function to the chat chat.addFunction(myFunction); ``` -From the moment that you add a function to chat, we will use function calling features. -For more information : -- [https://ai.google.dev/gemini-api/docs/function-calling](https://ai.google.dev/gemini-api/docs/function-calling) -- [https://platform.openai.com/docs/guides/gpt/function-calling](https://platform.openai.com/docs/guides/gpt/function-calling) +From the moment you add a function to a chat, GenAIApp uses function-calling features. + +For more information: -### Enable web browsing (optional) +- [Gemini function calling](https://ai.google.dev/gemini-api/docs/function-calling) +- [OpenAI function calling](https://platform.openai.com/docs/guides/gpt/function-calling) -If you want to allow the chat to perform web searches and fetch web pages, enable browsing on your chat instance: +### Enable Web Browsing (Optional) -```javascript +If you want to allow the chat to perform web searches and fetch web pages, enable browsing on your chat instance. See [`samples/web-browsing.gs`](samples/web-browsing.gs). + +```js chat.enableBrowsing(true); ``` -If want to restrict your browsing to a specific web page, you can add as a second argument the url of this web page as bellow. -```javascript - chat.enableBrowsing(true, "https://support.google.com"); +To restrict browsing to a specific web page or domain, add the URL as the second argument. + +```js +chat.enableBrowsing(true, 'https://support.google.com'); ``` -### Enable OpenAI server-side compaction (optional) +### Enable OpenAI Server-Side Compaction (Optional) -Use Responses API native compaction to let OpenAI compact long conversations automatically. +Use Responses API native compaction to let OpenAI compact long conversations automatically. See [`samples/configuration-options.gs`](samples/configuration-options.gs) for this alongside other operational settings. ```js const chat = GenAIApp.newChat() @@ -161,49 +209,47 @@ const chat = GenAIApp.newChat() .setCompactionThreshold(120000); // minimum: 1000 ``` -If you only need default behavior, enabling compaction is enough (default threshold is `10000`): +If you only need default behavior, enabling compaction is enough. The default threshold is `10000`. ```js const chat = GenAIApp.newChat().enableCompaction(true); ``` -### Give a web page as a knowledge base (optional) +### Give a Web Page as a Knowledge Base (Optional) -If you don't need the perform a web search and want to directly give a link for a web page you want the chat to read before performing any action, you can use the addKnowledgeLink(url) function. +If you do not need broad web search and want to give the model a specific page to read before answering, use `addKnowledgeLink(url)`. See [`samples/knowledge-links.gs`](samples/knowledge-links.gs). -```javascript - chat.addKnowledgeLink("https://developers.google.com/apps-script/guides/libraries"); +```js +chat.addKnowledgeLink('https://developers.google.com/apps-script/guides/libraries'); ``` -### Add Image (optional) +### Add Image (Optional) -To include an image in the conversation, use the `addImage()` method with a URL or a Blob. +To include an image in the conversation, use the `addImage()` method with a URL or a Blob. See [`samples/image-analysis.gs`](samples/image-analysis.gs). -```javascript +```js chat.addImage('https://example.com/image.jpg'); ``` -### Add File to Chat (optional) +### Add File to Chat (Optional) -You can include the contents of a Google Drive file or a Blob in your conversation using the `addFile()` method. This works with both Gemini and OpenAI multimodal models. +You can include the contents of a Google Drive file or a Blob in your conversation using the `addFile()` method. This works with both Gemini and OpenAI multimodal models. See [`samples/document-analysis.gs`](samples/document-analysis.gs). -```javascript +```js // Add a Google Drive file to the chat context using its Drive file ID chat.addFile('your-google-drive-file-id'); ``` ### Add an MCP Connector (Optional) -Use Model Context Protocol (MCP) connectors to let OpenAI Responses API models reach structured data sources such as native Google Workspace endpoints or your own custom MCP servers. +Use Model Context Protocol (MCP) connectors to let OpenAI Responses API models reach structured data sources such as native Google Workspace endpoints or your own custom MCP servers. See [`samples/mcp-connectors.gs`](samples/mcp-connectors.gs) and [`samples/google-mcp-connector.gs`](samples/google-mcp-connector.gs). > ⚠️ **Google Workspace Native MCP Requirements:** -> To connect to Google's official MCP endpoints (e.g., `https://drivemcp.googleapis.com/mcp/v1` or `https://calendarmcp.googleapis.com/mcp/v1`), your Google Apps Script must be linked to a **Standard Google Cloud Project** (the default Apps Script project will return a *403 Forbidden* error). -> In your GCP console, you must enable both the standard API (e.g., `drive.googleapis.com` for Drive or `calendar.googleapis.com` for Calendar) **AND** the specific MCP API (e.g., `drivemcp.googleapis.com` or `calendarmcp.googleapis.com`). +> To connect to Google's official MCP endpoints, such as `https://drivemcp.googleapis.com/mcp/v1` or `https://calendarmcp.googleapis.com/mcp/v1`, your Google Apps Script must be linked to a **Standard Google Cloud Project**. In your GCP console, enable both the standard API, such as `drive.googleapis.com` for Drive or `calendar.googleapis.com` for Calendar, and the specific MCP API, such as `drivemcp.googleapis.com` or `calendarmcp.googleapis.com`. -```javascript +```js const chat = GenAIApp.newChat(); -// Google Native Workspace Connector (e.g., Google Drive) const nativeDriveConnector = GenAIApp.newConnector() .setLabel('Google_Native_Drive') .setDescription('Official Google Workspace MCP server for Google Drive') @@ -213,7 +259,6 @@ const nativeDriveConnector = GenAIApp.newConnector() chat.addMCP(nativeDriveConnector); -// Custom Internal MCP Server const customConnector = GenAIApp.newConnector() .setLabel('Salesforce CRM') .setDescription('Query opportunity data from Salesforce via MCP proxy') @@ -224,291 +269,116 @@ const customConnector = GenAIApp.newConnector() chat.addMCP(customConnector); ``` -> **Note on Authorization Format:** -> Google native connectors using `ScriptApp.getOAuthToken()` do NOT require the "Bearer " prefix — the token is passed directly to `.setAuthorization()`. Custom MCP servers (like the Salesforce example using `'Bearer ' + SALESFORCE_MCP_TOKEN`) typically expect the full "Bearer " format. Always check your custom server's authentication requirements. +> **Note on Authorization Format:** Google native connectors using `ScriptApp.getOAuthToken()` do not require the `Bearer` prefix; the token is passed directly to `.setAuthorization()`. Custom MCP servers typically expect the full bearer-token format with a space following `Bearer`. Always check your custom server's authentication requirements. #### Connector Configuration -* **Google Native endpoints:** Configure a connector using `.setServerUrl()` pointing to the desired service (e.g., `"https://drivemcp.googleapis.com/mcp/v1"` or `"https://calendarmcp.googleapis.com/mcp/v1"`) and pass the script's OAuth token via `.setAuthorization(ScriptApp.getOAuthToken())`. -* **Custom MCP servers:** Configure a connector with `.setLabel()`, `.setDescription()`, and `.setServerUrl("https://...")`, and optionally `.setAuthorization()` if the server expects a bearer token. -* **Approval workflows:** `.setRequireApproval('never' | 'domain' | 'always')` lets you enforce end-user approval before the model calls the connector. +- **Google Native endpoints:** Configure a connector using `.setServerUrl()` pointing to the desired service and pass the script's OAuth token via `.setAuthorization(ScriptApp.getOAuthToken())`. +- **Custom MCP servers:** Configure a connector with `.setLabel()`, `.setDescription()`, `.setServerUrl('https://...')`, and optionally `.setAuthorization()` if the server expects a bearer token. +- **Approval workflows:** `.setRequireApproval('never' | 'domain' | 'always')` lets you enforce end-user approval before the model calls the connector. -> ⚠️ **Model Availability:** MCP connectors are currently available only when you run the chat with OpenAI Responses API models (for example, `o4-mini`, `o3`, or `gpt-5.4`). +> ⚠️ **Model Availability:** MCP connectors are currently available only when you run the chat with OpenAI Responses API models, for example `o4-mini`, `o3`, or `gpt-5.4`. ### Running the Chat -Once you've set up the chat and added the necessary components, you can start the conversation by calling the `run()` method. +Once you have set up the chat and added the necessary components, start the conversation by calling `run()`. See [`samples/simple-chat.gs`](samples/simple-chat.gs) for a minimal run and [`samples/multi-model-usage.gs`](samples/multi-model-usage.gs) for model switching. ```js -let response = chat.run({ - model: "gemini-2.5-flash", // Optional: set the model to use +const response = chat.run({ + model: 'gemini-2.5-flash', // Optional: set the model to use temperature: 0.5, // Optional: set response creativity - function_call: "getWeather" // Optional: force the first API response to call a function + function_call: 'getWeather' // Optional: force the first API response to call a function }); console.log(response); ``` -The library supports the following models: -1. Gemini: "gemini-2.5-pro" | "gemini-2.5-flash" -2. OpenAI: "gpt-5.4" | "o4-mini" | "o3" | "gpt-5" -⚠️ **Warning:** the "function_call" advanced parameter is supported by: - - OpenAI models (including GPT-5) - - Gemini 2.5 variants (gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-2.5-flash-native-audio) +The library supports the following model families: + +1. Gemini: `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3.1-pro-preview`, `gemini-3.1-flash-lite`, `gemini-3-flash-preview`, and `gemini-3.5-flash`. +2. OpenAI: `gpt-5.4`, `gpt-5.5`, `o4-mini`, and `o3`. - The "reasoning_effort" parameter is supported only by reasoning-capable OpenAI models and ignored by all others. -⚠️ **Warning:** The "reasoning_effort" parameter is supported only by reasoning-capable OpenAI models and ignored by all others. +⚠️ **Warning:** The `reasoning_effort` parameter is supported only by reasoning-capable OpenAI models and ignored by all others. -## FunctionObject Class +### FunctionObject Class -### Creating a Function +#### Creating a Function -The **FunctionObject** class represents a callable function by the chatbot. It is highly customizable with various options: +The **FunctionObject** class represents a callable function for the chatbot. It is customizable with names, descriptions, parameters, and execution behavior. See [`samples/function-calling-basics.gs`](samples/function-calling-basics.gs). ```js -let functionObject = GenAIApp.newFunction() - .setName("searchMovies") - .setDescription("Search for movies based on a genre.") - .addParameter("genre", "string", "The genre of movies to search for."); +const functionObject = GenAIApp.newFunction() + .setName('searchMovies') + .setDescription('Search for movies based on a genre.') + .addParameter('genre', 'string', 'The genre of movies to search for.'); ``` -### Configuring Parameters +#### Configuring Parameters -The function parameters can be configured to be required or optional: +Function parameters can be configured as required or optional. See [`samples/function-calling-advanced.gs`](samples/function-calling-advanced.gs) for structured extraction patterns. ```js // Adding required parameter -functionObject.addParameter("year", "number", "The year of the movie release."); +functionObject.addParameter('year', 'number', 'The year of the movie release.'); // Adding optional parameter -functionObject.addParameter("rating", "number", "The minimum rating of movies to return.", true); +functionObject.addParameter('rating', 'number', 'The minimum rating of movies to return.', true); ``` -## VectorStoreObject Class +### VectorStoreObject Class -### Retrieving Knowledge from an OpenAI Vector Store +#### Retrieving Knowledge from an OpenAI Vector Store -Retrieve contextual information from a specific OpenAI vector search : +Use a vector store when you want model answers grounded in uploaded source files. See [`samples/vector-store-rag.gs`](samples/vector-store-rag.gs) for a full create-upload-query workflow. ```js const vectorStoreObject = GenAIApp.newVectorStore() - .initializeFromId("your-vector-store-id"); -chat.addVectorStore(vectorStoreObject); -``` -To find out more : [https://platform.openai.com/docs/api-reference/vector_stores/search](https://platform.openai.com/docs/api-reference/vector_stores/search) + .initializeFromId('your-vector-store-id'); -## Examples - -### Example 1 : Send a prompt and get completion - -```javascript - GenAIApp.setOpenAIAPIKey(OPEN_AI_API_KEY); - - const chat = GenAIApp.newChat(); - chat.addMessage("What are the steps to add an external library to my Google Apps Script project?"); - - const chatAnswer = chat.run(); - Logger.log(chatAnswer); +chat.addVectorStores(vectorStoreObject.getId()); ``` -### Example 2 : Ask Open AI to create a draft reply for the last email in Gmail inbox - -```javascript - GenAIApp.setOpenAIAPIKey(OPEN_AI_API_KEY); - const chat = GenAIApp.newChat(); - - var getLatestThreadFunction = GenAIApp.newFunction() - .setName("getLatestThread") - .setDescription("Retrieve information from the last message received."); - - var createDraftResponseFunction = GenAIApp.newFunction() - .setName("createDraftResponse") - .setDescription("Create a draft response.") - .addParameter("threadId", "string", "the ID of the thread to retrieve") - .addParameter("body", "string", "the body of the email in plain text"); - - var resp = GenAIApp.newChat() - .addMessage("You are an assistant managing my Gmail inbox.", true) - .addMessage("Retrieve the latest message I received and draft a response.") - .addFunction(getLatestThreadFunction) - .addFunction(createDraftResponseFunction) - .run(); - - console.log(resp); -``` - -### Example 3 : Retrieve structured data instead of raw text with onlyReturnArgument() - -```javascript -const ticket = "Hello, could you check the status of my subscription under customer@example.com"; - - chat.addMessage("You just received this ticket : " + ticket); - chat.addMessage("What's the customer email address ? You will give it to me using the function getEmailAddress."); - - const myFunction = GenAIApp.newFunction() // in this example, getEmailAddress is not actually a real function in your script - .setName("getEmailAddress") - .setDescription("To give the user an email address") - .addParameter("emailAddress", "string", "the email address") - .onlyReturnArguments(true) // you will get your parameters in a json object - - chat.addFunction(myFunction); - - const chatAnswer = chat.run(); - Logger.log(chatAnswer["emailAddress"]); // the name of the parameter of your "fake" function - - // output : "customer@example.com" -``` - -### Example 4 : Use web browsing - -```javascript - const message = "You're a google support agent, a customer is asking you how to install a library he found on github in a google appscript project." - - const chat = GenAIApp.newChat(); - chat.addMessage(message); - chat.addMessage("Browse this website to answer : https://developers.google.com/apps-script", true) - chat.enableBrowsing(true); - - const chatAnswer = chat.run(); - Logger.log(chatAnswer); -``` - -### Example 5 : Describe an Image - -To have the chat model describe an image: - -```javascript -const chat = GenAIApp.newChat(); -chat.addMessage("Describe the following image."); -chat.addImage("https://example.com/image.jpg"); -const response = chat.run(); -Logger.log(response); -``` -This will use the selected model to provide a description of the image at the specified URL. - -### Example 6 : Extend a Chat with an MCP Connector - -```javascript -GenAIApp.setOpenAIAPIKey(OPEN_AI_API_KEY); - -const chat = GenAIApp.newChat(); -chat.addMessage('Search my latest unread Gmail message and summarize it.'); - -// Use Google's Native MCP Endpoint -const gmailConnector = GenAIApp.newConnector() - .setServerUrl('https://gmailmcp.googleapis.com/mcp/v1') - .setLabel('Google_Native_Gmail') - .setDescription('Official Google Workspace MCP server for Gmail') - .setAuthorization(ScriptApp.getOAuthToken()) - .setRequireApproval('never'); - -chat.addMCP(gmailConnector); - -const summary = chat.run({ model: 'gpt-5.4', max_tokens: 10000 }); -Logger.log(summary); -``` - -In this example, the connector points directly to Google's Native MCP infrastructure. It requires a linked GCP project with both **Gmail** API and **Gmail MCP API** enabled. - -### Example 7 : Connect to a Custom MCP Server with setServerUrl() - -```javascript -GenAIApp.setOpenAIAPIKey(OPEN_AI_API_KEY); - -const chat = GenAIApp.newChat(); -chat.addMessage('Check the latest closed-won opportunities and report total revenue.'); - -const salesforceConnector = GenAIApp.newConnector() - .setLabel('Salesforce CRM') - .setDescription('Internal MCP service that proxies Salesforce data') - .setServerUrl('https://mcp.example.com/salesforce') - .setAuthorization('Bearer ' + SALESFORCE_MCP_TOKEN) - .setRequireApproval('always'); - -chat.addMCP(salesforceConnector); - -const report = chat.run({ model: 'gpt-5.4' }); -Logger.log(report); -``` - -The `setServerUrl()` method points the connector to your MCP gateway, while `setAuthorization()` injects a bearer token or API -key that the proxy expects. Combine these settings with `.setRequireApproval('always')` if you want end users to explicitly -authorize every connector invocation. - -### Example 8 : Continue a Conversation with previous_response_id - -```javascript -GenAIApp.setOpenAIAPIKey(OPEN_AI_API_KEY); - -// First request -const firstChat = GenAIApp.newChat(); -firstChat.addMessage("Explain what Google Apps Script libraries are in 3 short bullet points."); - -const firstAnswer = firstChat.run({ model: "gpt-5.4" }); -Logger.log(firstAnswer); - -// Save the response id returned by the OpenAI Responses API -const previousResponseId = firstChat.retrieveLastResponseId(); -Logger.log(`Previous response id: ${previousResponseId}`); - -// Follow-up request using previous_response_id -const secondChat = GenAIApp.newChat(); -secondChat - .setPreviousResponseId(previousResponseId) - .addMessage("Now rewrite your previous answer for a beginner in one short paragraph."); - -const secondAnswer = secondChat.run({ model: "gpt-5.4" }); -Logger.log(secondAnswer); -``` - - -## Contributing - -Contributions are welcome! If you find any bugs, have feature requests, or want to contribute code, please submit an issue or pull request on this [GitHub repository](https://github.com/scriptit-fr/GenAIApp). - -## License - -The **GenAIApp** library is licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License. For more details, please see the [LICENSE](http://www.apache.org/licenses/LICENSE-2.0). +To find out more, see the [OpenAI vector stores search API reference](https://platform.openai.com/docs/api-reference/vector_stores/search). ## Reference -### GenAIApp +### GenAIApp Factory -- `newChat()`: Create a new `Chat` instance. -- `newFunction()`: Create a new `FunctionObject`. -- `newConnector()`: Create a new `ConnectorObject` for MCP integrations. -- `newVectorStore()`: Create a new `VectorStoreObject`. +- `newChat()`: Create a new `Chat` instance. Start with [`samples/simple-chat.gs`](samples/simple-chat.gs). +- `newFunction()`: Create a new `FunctionObject`. See [`samples/function-calling-basics.gs`](samples/function-calling-basics.gs). +- `newConnector()`: Create a new `ConnectorObject` for MCP integrations. See [`samples/mcp-connectors.gs`](samples/mcp-connectors.gs). +- `newVectorStore()`: Create a new `VectorStoreObject`. See [`samples/vector-store-rag.gs`](samples/vector-store-rag.gs). - `setOpenAIAPIKey(apiKey)`: Set the OpenAI API key. - `setGeminiAPIKey(apiKey)`: Set the Gemini API key. -- `setGeminiAuth(projectId, region)`: Use Vertex AI authentication. +- `setGeminiAuth(projectId, region)`: Use Vertex AI authentication. See [`samples/vertex-ai-setup.gs`](samples/vertex-ai-setup.gs). - `setGlobalMetadata(key, value)`: Attach a key/value pair to every request. -- `setPrivateInstanceBaseUrl(baseUrl)`: Use a custom OpenAI‑compatible endpoint. +- `setPrivateInstanceBaseUrl(baseUrl)`: Use a custom OpenAI-compatible endpoint. ### Chat A `Chat` represents a conversation with the model. -- `addMessage(messageContent, [system])`: Add a user or system message. -- `addFunction(functionObject)`: Attach a `FunctionObject` for function calling. -- `addImage(imageInput)`: Include an image URL or Blob in the conversation. -- `addFile(fileInput)`: Include the content of a Google Drive file or Blob. +- `addMessage(messageContent, [system])`: Add a user or system message. See [`samples/system-prompts.gs`](samples/system-prompts.gs). +- `addFunction(functionObject)`: Attach a `FunctionObject` for function calling. See [`samples/function-calling-basics.gs`](samples/function-calling-basics.gs). +- `addImage(imageInput)`: Include an image URL or Blob in the conversation. See [`samples/image-analysis.gs`](samples/image-analysis.gs). +- `addFile(fileInput)`: Include the content of a Google Drive file or Blob. See [`samples/document-analysis.gs`](samples/document-analysis.gs). - `addMetadata(key, value)`: Add metadata sent with the next request. - `getAttributes()`: Retrieve attributes from vector store search results. -- `onlyReturnChunks(bool)`: Return raw chunks from vector store searches. +- `onlyReturnChunks(bool)`: Return raw chunks from vector store searches. See [`samples/vector-store-rag.gs`](samples/vector-store-rag.gs). - `setMaxChunks(maxChunks)`: Limit the number of chunks returned by vector stores. - `getMessages()`: Get the messages as a JSON string. - `getFunctions()`: Get the functions as a JSON string. -- `disableLogs(bool)`: Disable library logs. -- `enableBrowsing(bool, [url])`: Allow the model to browse the web, optionally restricted to a URL. -- `enableCompaction(enabled)`: Enable/disable OpenAI Responses API server-side compaction (`false` by default). -- `setCompactionThreshold(threshold)`: Set the compaction threshold (`10000` by default, minimum `1000`; finite numbers only). -- `addKnowledgeLink(url)`: Inject the content of a web page into the conversation. -- `addMCP(connectorObject)`: Attach one or more MCP connectors to the chat request. -- `setMaximumAPICalls(maxAPICalls)`: Limit the number of API calls in a run. -- `retrieveLastResponseId()`: Get the last OpenAI response ID returned by `run()`. -- `setPreviousResponseId(id)`: Reuse a previous OpenAI response ID to continue a conversation. -- `warnIfResponseTokenUsageAbove(input_token_threshold)`: Logs a warning if the input tokens are greater than the threshold. Is not on by default. -- `addVectorStores(vectorStoreIds)`: Attach vector store IDs for retrieval. +- `disableLogs(bool)`: Disable library logs. See [`samples/configuration-options.gs`](samples/configuration-options.gs). +- `enableBrowsing(bool, [url])`: Allow the model to browse the web, optionally restricted to a URL. See [`samples/web-browsing.gs`](samples/web-browsing.gs). +- `enableCompaction(enabled)`: Enable or disable OpenAI Responses API server-side compaction. See [`samples/configuration-options.gs`](samples/configuration-options.gs). +- `setCompactionThreshold(threshold)`: Set the compaction threshold. The default is `10000`, the minimum is `1000`, and values must be finite numbers. +- `addKnowledgeLink(url)`: Inject the content of a web page into the conversation. See [`samples/knowledge-links.gs`](samples/knowledge-links.gs). +- `addMCP(connectorObject)`: Attach one or more MCP connectors to the chat request. See [`samples/mcp-connectors.gs`](samples/mcp-connectors.gs). +- `setMaximumAPICalls(maxAPICalls)`: Limit the number of API calls in a run. See [`samples/configuration-options.gs`](samples/configuration-options.gs). +- `retrieveLastResponseId()`: Get the last OpenAI response ID returned by `run()`. See [`samples/conversation-continuation.gs`](samples/conversation-continuation.gs). +- `setPreviousResponseId(id)`: Reuse a previous OpenAI response ID to continue a conversation. See [`samples/conversation-continuation.gs`](samples/conversation-continuation.gs). +- `warnIfResponseTokenUsageAbove(input_token_threshold)`: Log a warning if input tokens exceed the threshold. It is off by default. +- `addVectorStores(vectorStoreIds)`: Attach vector store IDs for retrieval. See [`samples/vector-store-rag.gs`](samples/vector-store-rag.gs). - `run([advancedParametersObject])`: Execute the chat and return the response. Supports `model`, `temperature`, `reasoning_effort`, `max_tokens`, and `function_call` parameters. ### Function Object @@ -518,8 +388,8 @@ A `FunctionObject` represents a function that can be called by the chat. - `setName(name)`: Set the function name. - `setDescription(description)`: Set the function description. - `addParameter(name, type, description, [isOptional])`: Add a parameter to the function. Parameters are required by default; set `isOptional` to `true` to make a parameter optional. -- `endWithResult(bool)`: End the conversation after the function is executed. -- `onlyReturnArguments(bool)`: End the conversation and return only the arguments. +- `endWithResult(bool)`: End the conversation after the function is executed. See [`samples/function-calling-advanced.gs`](samples/function-calling-advanced.gs). +- `onlyReturnArguments(bool)`: End the conversation and return only the arguments. See [`samples/function-calling-advanced.gs`](samples/function-calling-advanced.gs). ### Vector Store Object @@ -540,13 +410,21 @@ A `VectorStoreObject` represents an OpenAI vector store. A `ConnectorObject` represents a Google Workspace or custom MCP connector that can be attached to an OpenAI chat request. -- `setLabel(label)`: Set the identifier used in the chat payload (required for custom servers). +- `setLabel(label)`: Set the identifier used in the chat payload. This is required for custom servers. - `setDescription(description)`: Provide an optional description visible to the model. - `setServerUrl(url)`: Use a custom MCP server hosted at the provided HTTPS URL. -- `setConnectorId('gmail'|'calendar'|'drive')`: Reference a Google Workspace MCP connector by its predefined ID. -- `setAuthorization(token)`: Override the default OAuth token (for example, supply `Bearer ...`). -- `setRequireApproval('never'|'domain'|'always')`: Control whether the connector requires user approval before execution. +- `setConnectorId('gmail' | 'calendar' | 'drive')`: Reference a Google Workspace MCP connector by its predefined ID. +- `setAuthorization(token)`: Override the default OAuth token, for example supply `Bearer ...`. +- `setRequireApproval('never' | 'domain' | 'always')`: Control whether the connector requires user approval before execution. + +## Contributing + +Contributions are welcome! If you find bugs, have feature requests, or want to contribute code, please submit an issue or pull request on this [GitHub repository](https://github.com/scriptit-fr/GenAIApp). + +## License + +The **GenAIApp** library is licensed under the Apache License, Version 2.0. You may not use this file except in compliance with the License. For details, see the [LICENSE](http://www.apache.org/licenses/LICENSE-2.0). --- -Happy coding and enjoy building with the **GenAIApp** library! \ No newline at end of file +Happy coding and enjoy building with the **GenAIApp** library! diff --git a/samples/configuration-options.gs b/samples/configuration-options.gs new file mode 100644 index 0000000..5ea176c --- /dev/null +++ b/samples/configuration-options.gs @@ -0,0 +1,20 @@ +/* + * Purpose: Demonstrates common GenAIApp configuration and guardrail options. + * Use case: Control budget, monitor token use, reduce logs, and enable long-context compaction. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a compact project-summary response while enforcing the configured limits. + */ +function configurationOptionsSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const chat = GenAIApp.newChat() + .setMaximumAPICalls(3) + .warnIfResponseTokenUsageAbove(500) + .disableLogs(true) + .enableCompaction(true) + .setCompactionThreshold(10000) + .addMessage('Summarize three practical ways to keep AI usage predictable in Apps Script.'); + + const response = chat.run({ model: 'gpt-5.4', max_tokens: 800 }); + Logger.log(response); +} diff --git a/samples/conversation-continuation.gs b/samples/conversation-continuation.gs new file mode 100644 index 0000000..50b464c --- /dev/null +++ b/samples/conversation-continuation.gs @@ -0,0 +1,23 @@ +/* + * Purpose: Demonstrates multi-turn OpenAI conversations with previous response IDs. + * Use case: Continue a Responses API conversation without resending the whole transcript. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs the first response ID and a second answer that remembers the chosen color. + */ +function conversationContinuationSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const firstChat = GenAIApp.newChat() + .addMessage('Remember this preference: my dashboard accent color is teal.'); + Logger.log(firstChat.run({ model: 'gpt-5.4' })); + + const previousResponseId = firstChat.retrieveLastResponseId(); + Logger.log('Previous response ID: ' + previousResponseId); + + const secondChat = GenAIApp.newChat() + .setPreviousResponseId(previousResponseId) + .addMessage('What accent color did I choose?'); + + const response = secondChat.run({ model: 'gpt-5.4' }); + Logger.log(response); +} diff --git a/samples/document-analysis.gs b/samples/document-analysis.gs new file mode 100644 index 0000000..bc005dc --- /dev/null +++ b/samples/document-analysis.gs @@ -0,0 +1,31 @@ +/* + * Purpose: Demonstrates document analysis with addFile() using Drive IDs and Blobs. + * Use case: Summarize PDFs or exported Google Workspace files from Apps Script. + * Required config: Store OPENAI_API_KEY and SAMPLE_PDF_FILE_ID in Script Properties. + * Expected output: Logs three concise bullets summarizing the supplied documents. + */ +function documentAnalysisSample() { + const scriptProperties = PropertiesService.getScriptProperties(); + GenAIApp.setOpenAIAPIKey(scriptProperties.getProperty('OPENAI_API_KEY')); + + const textBlob = Utilities.newBlob(`Ah no! young blade! That was a trifle short! + You might have said at least a hundred things + By varying the tone. . .like this, suppose,. . . + Aggressive: 'Sir, if I had such a nose I'd amputate it!' + Friendly: 'When you sup It must annoy you, dipping in your cup; + You need a drinking-bowl of special shape!' + Descriptive: ''Tis a rock!. . .a peak!. . .a cape! -- + A cape, forsooth! 'Tis a peninsular!' + Curious: 'How serves that oblong capsular? + For scissor-sheath? Or pot to hold your ink?' + Gracious: 'You love the little birds, I think? + I see you've managed with a fond research + To find their tiny claws a roomy perch!' `, 'text/plain', 'goals.txt'); + + const chat = GenAIApp.newChat() + .addMessage('Summarize the attached file in three bullets.') + .addFile(textBlob); + + const response = chat.run({ model: 'gpt-5.4' }); + Logger.log(response); +} diff --git a/samples/function-calling-advanced.gs b/samples/function-calling-advanced.gs new file mode 100644 index 0000000..85b6d56 --- /dev/null +++ b/samples/function-calling-advanced.gs @@ -0,0 +1,39 @@ +/* + * Purpose: Shows advanced function-calling controls in one extraction/routing flow. + * Use case: Extract arguments without execution, or terminate early after a tool result. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a JSON object containing ticket fields because onlyReturnArguments(true) ends before execution. + */ +function functionCallingAdvancedSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const extractTicket = GenAIApp.newFunction() + .setName('extractSupportTicket') + .setDescription('Extracts support-ticket fields from a user message.') + .addParameter('email', 'string', 'Customer email address') + .addParameter('category', 'string', 'Short issue category') + .addParameter('priority', 'string', 'low, normal, or urgent') + .onlyReturnArguments(true); + + const lookupCustomer = GenAIApp.newFunction() + .setName('lookupCustomerPlan') + .setDescription('Looks up a customer plan by email.') + .addParameter('email', 'string', 'Customer email address') + .endWithResult(true); + + const chat = GenAIApp.newChat() + .addMessage('Extract this ticket: urgent billing problem for ana@example.com.') + .addFunction(extractTicket) + .addFunction(lookupCustomer); + + const response = chat.run({ model: 'gpt-5.4', function_call: 'extractSupportTicket' }); + Logger.log(response); +} + +function extractSupportTicket(email, category, priority) { + return { email: email, category: category, priority: priority }; +} + +function lookupCustomerPlan(email) { + return { email: email, plan: 'Business', status: 'active' }; +} diff --git a/samples/function-calling-basics.gs b/samples/function-calling-basics.gs new file mode 100644 index 0000000..9bb8507 --- /dev/null +++ b/samples/function-calling-basics.gs @@ -0,0 +1,31 @@ +/* + * Purpose: Demonstrates a single function-calling tool registered on a chat. + * Use case: Let the model call Apps Script code to retrieve structured app data. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a weather-style answer based on the sampleGetWeather stub result. + */ +function functionCallingBasicsSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const weatherFunction = GenAIApp.newFunction() + .setName('sampleGetWeather') + .setDescription('Gets the current weather for a city.') + .addParameter('city', 'string', 'City name, for example Paris') + .addParameter('unit', 'string', 'Temperature unit: celsius or fahrenheit', true); + + const chat = GenAIApp.newChat() + .addMessage('What is the weather in Paris? Use the weather function.') + .addFunction(weatherFunction); + + const response = chat.run({ model: 'gpt-5.4', function_call: 'sampleGetWeather' }); + Logger.log(response); +} + +function sampleGetWeather(city, unit) { + return { + city: city, + unit: unit || 'celsius', + condition: 'sunny', + temperature: 22 + }; +} diff --git a/samples/google-mcp-connector.gs b/samples/google-mcp-connector.gs new file mode 100644 index 0000000..2b575d9 --- /dev/null +++ b/samples/google-mcp-connector.gs @@ -0,0 +1,24 @@ +/* + * Purpose: Demonstrates direct Google Native Gmail MCP connector setup. + * Use case: Let an OpenAI Responses API model summarize Gmail data through Google's Native MCP infrastructure. + * Required config: Store OPENAI_API_KEY in Script Properties; link Apps Script to a standard GCP project with both Gmail API and Gmail MCP API enabled. + * Expected output: Logs a concise summary after the model uses the authorized Gmail MCP connector. + */ +function mcpConnectorsSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const chat = GenAIApp.newChat() + .addMessage('Summarize my latest unread Gmail message in three bullets.'); + + const gmailConnector = GenAIApp.newConnector() + .setServerUrl('https://gmailmcp.googleapis.com/mcp/v1') + .setLabel('Google_Native_Gmail') + .setDescription('Official Google Workspace MCP server for Gmail') + .setAuthorization(ScriptApp.getOAuthToken()) + .setRequireApproval('never'); + + chat.addMCP(gmailConnector); + + const summary = chat.run({ model: 'gpt-5.4', max_tokens: 10000 }); + Logger.log(summary); +} diff --git a/samples/image-analysis.gs b/samples/image-analysis.gs new file mode 100644 index 0000000..531eb4e --- /dev/null +++ b/samples/image-analysis.gs @@ -0,0 +1,20 @@ +/* + * Purpose: Demonstrates image analysis from both a public URL and an Apps Script Blob. + * Use case: Send screenshots, Drive images, or public images to a vision-capable model. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a short comparison of the URL image and generated Blob image. + */ +function imageAnalysisSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const imageUrl = 'https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png'; + const imageBlob = UrlFetchApp.fetch(imageUrl).getBlob().setName('apps-script-logo.png'); + + const chat = GenAIApp.newChat() + .addMessage('Describe these images and mention whether they appear related.') + .addImage(imageUrl) + .addImage(imageBlob); + + const response = chat.run({ model: 'gpt-5.4' }); + Logger.log(response); +} diff --git a/samples/knowledge-links.gs b/samples/knowledge-links.gs new file mode 100644 index 0000000..aee57ac --- /dev/null +++ b/samples/knowledge-links.gs @@ -0,0 +1,16 @@ +/* + * Purpose: Demonstrates injecting a web page as direct context with addKnowledgeLink(). + * Use case: Answer from a known page without allowing broad web search. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a concise answer based on the Apps Script libraries guide. + */ +function knowledgeLinksSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const chat = GenAIApp.newChat() + .addKnowledgeLink('https://developers.google.com/apps-script/guides/libraries') + .addMessage('Based only on the provided knowledge link, what is one reason to use an Apps Script library?'); + + const response = chat.run({ model: 'gpt-5.4' }); + Logger.log(response); +} diff --git a/samples/mcp-connectors.gs b/samples/mcp-connectors.gs new file mode 100644 index 0000000..8552e6b --- /dev/null +++ b/samples/mcp-connectors.gs @@ -0,0 +1,22 @@ +/* + * Purpose: Demonstrates Google Workspace MCP connector setup for Gmail, Calendar, and Drive. + * Use case: Let an OpenAI Responses API model inspect Workspace data through authorized connectors. + * Required config: Store OPENAI_API_KEY in Script Properties; link Apps Script to a standard GCP project with MCP APIs enabled. + * Expected output: Logs a concise Workspace summary after the model uses approved connectors. + */ +function mcpConnectorsSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const gmail = GenAIApp.newConnector().setConnectorId('gmail').setAuthorization(ScriptApp.getOAuthToken()).setRequireApproval('never'); + const calendar = GenAIApp.newConnector().setConnectorId('calendar').setAuthorization(ScriptApp.getOAuthToken()).setRequireApproval('never'); + const drive = GenAIApp.newConnector().setConnectorId('drive').setAuthorization(ScriptApp.getOAuthToken()).setRequireApproval('never'); + + const chat = GenAIApp.newChat() + .addMessage('Summarize my latest unread Gmail message, next calendar event, and one recently modified Drive file.') + .addMCP(gmail) + .addMCP(calendar) + .addMCP(drive); + + const response = chat.run({ model: 'gpt-5.4', max_tokens: 20000 }); + Logger.log(response); +} diff --git a/samples/multi-model-usage.gs b/samples/multi-model-usage.gs new file mode 100644 index 0000000..6670257 --- /dev/null +++ b/samples/multi-model-usage.gs @@ -0,0 +1,20 @@ +/* + * Purpose: Demonstrates reusing the same chat setup with different models. + * Use case: Compare GPT, Gemini, and reasoning-model responses without changing prompts. + * Required config: Store OPENAI_API_KEY and GEMINI_API_KEY in Script Properties. + * Expected output: Logs one response per model for the same haiku-generation prompt. + */ +function multiModelUsageSample() { + const scriptProperties = PropertiesService.getScriptProperties(); + GenAIApp.setOpenAIAPIKey(scriptProperties.getProperty('OPENAI_API_KEY')); + GenAIApp.setGeminiAPIKey(scriptProperties.getProperty('GEMINI_API_KEY')); + + const models = ['gpt-5.4', 'gemini-3.5-flash', 'o4-mini']; + models.forEach(function (model) { + const chat = GenAIApp.newChat() + .addMessage('Write a haiku about Apps Script automation.'); + + const response = chat.run({ model: model, reasoning_effort: 'low' }); + Logger.log(model + ': ' + response); + }); +} diff --git a/samples/sheets-ai-assistant.gs b/samples/sheets-ai-assistant.gs new file mode 100644 index 0000000..1e5469e --- /dev/null +++ b/samples/sheets-ai-assistant.gs @@ -0,0 +1,26 @@ +/* + * Purpose: Demonstrates a complete Google Sheets AI-assistant integration. + * Use case: Read active-sheet data, ask AI for a summary, and write the result back to the sheet. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY; run from a bound Google Sheet. + * Expected output: Writes an AI summary into cell A1 of a new sheet named AI Summary. + */ +function sheetsAiAssistantSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); + const sourceSheet = spreadsheet.getActiveSheet(); + const data = sourceSheet.getDataRange().getDisplayValues(); + const previewRows = data.slice(0, 20).map(function (row) { + return row.join(' | '); + }).join('\n'); + + const prompt = 'Analyze this sheet data and return three bullets with key observations:\n' + previewRows; + const response = GenAIApp.newChat() + .addMessage(prompt) + .run({ model: 'gpt-5.4', max_tokens: 800 }); + + const outputSheet = spreadsheet.getSheetByName('AI Summary') || spreadsheet.insertSheet('AI Summary'); + outputSheet.clear(); + outputSheet.getRange('A1').setValue(response); + outputSheet.getRange('A1').setWrap(true); +} diff --git a/samples/simple-chat.gs b/samples/simple-chat.gs new file mode 100644 index 0000000..3d697cb --- /dev/null +++ b/samples/simple-chat.gs @@ -0,0 +1,15 @@ +/* + * Purpose: Demonstrates the smallest GenAIApp chat request. + * Use case: Use this as a hello-world smoke test after installing the library. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a short greeting or one-sentence introduction from the model. + */ +function simpleChatSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const chat = GenAIApp.newChat(); + chat.addMessage('Say hello in one friendly sentence.'); + + const response = chat.run({ model: 'gpt-5.4' }); + Logger.log(response); +} diff --git a/samples/system-prompts.gs b/samples/system-prompts.gs new file mode 100644 index 0000000..30706c4 --- /dev/null +++ b/samples/system-prompts.gs @@ -0,0 +1,16 @@ +/* + * Purpose: Shows how to set assistant personality and context with a system message. + * Use case: Keep responses in a specific tone, role, or format for a user-facing app. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a concise response written in the configured librarian style. + */ +function systemPromptsSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const chat = GenAIApp.newChat() + .addMessage('You are a patient librarian. Answer in two calm bullet points.', true) + .addMessage('How should I choose my next book?'); + + const response = chat.run({ model: 'gpt-5.4' }); + Logger.log(response); +} diff --git a/samples/vector-store-rag.gs b/samples/vector-store-rag.gs new file mode 100644 index 0000000..7becd1f --- /dev/null +++ b/samples/vector-store-rag.gs @@ -0,0 +1,37 @@ +/* + * Purpose: Demonstrates a full OpenAI vector-store RAG workflow. + * Use case: Create a store, upload source files with attributes, attach it to a chat, and query it. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs the answer from file search, then logs raw chunks from onlyReturnChunks(true). + */ +function vectorStoreRagSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const policyBlob = Utilities.newBlob( + 'Refund policy: refunds are available within 30 days with a receipt.', + 'text/plain', + 'refund-policy.txt' + ); + + const vectorStore = GenAIApp.newVectorStore() + .setName('Sample Support Knowledge') + .setDescription('Tiny sample knowledge base for support answers') + .setChunkingStrategy(800, 200) + .createVectorStore(); + + vectorStore.uploadAndAttachFile(policyBlob, { topic: 'refunds', source: 'sample' }); + + const answer = GenAIApp.newChat() + .addVectorStores(vectorStore.getId()) + .addMessage('What is the refund window?') + .run({ model: 'gpt-5.4' }); + Logger.log(answer); + + const chunks = GenAIApp.newChat() + .addVectorStores(vectorStore.getId()) + .onlyReturnChunks(true) + .setMaxChunks(3) + .addMessage('refund window') + .run({ model: 'gpt-5.4' }); + Logger.log(chunks); +} diff --git a/samples/vertex-ai-setup.gs b/samples/vertex-ai-setup.gs new file mode 100644 index 0000000..b0b1c6b --- /dev/null +++ b/samples/vertex-ai-setup.gs @@ -0,0 +1,19 @@ +/* + * Purpose: Demonstrates Vertex AI authentication for Gemini without an API key. + * Use case: Run Gemini from a Google Cloud project linked to Apps Script. + * Required config: Store GCP_PROJECT_ID and GCP_REGION in Script Properties; enable Vertex AI and cloud-platform scopes. + * Expected output: Logs a short Gemini response generated through Vertex AI authentication. + */ +function vertexAiSetupSample() { + const scriptProperties = PropertiesService.getScriptProperties(); + GenAIApp.setGeminiAuth( + scriptProperties.getProperty('GCP_PROJECT_ID'), + scriptProperties.getProperty('GCP_REGION') || 'us-central1' + ); + + const chat = GenAIApp.newChat() + .addMessage('Explain Vertex AI authentication for Apps Script in one sentence.'); + + const response = chat.run({ model: 'gemini-2.5-flash' }); + Logger.log(response); +} diff --git a/samples/web-browsing.gs b/samples/web-browsing.gs new file mode 100644 index 0000000..0dc2992 --- /dev/null +++ b/samples/web-browsing.gs @@ -0,0 +1,16 @@ +/* + * Purpose: Demonstrates real-time web browsing with an optional domain restriction. + * Use case: Ask for current information while limiting browsing to a trusted site. + * Required config: Store an OpenAI API key in Script Properties as OPENAI_API_KEY. + * Expected output: Logs a brief answer grounded in content found under developers.google.com. + */ +function webBrowsingSample() { + GenAIApp.setOpenAIAPIKey(PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY')); + + const chat = GenAIApp.newChat() + .enableBrowsing(true, 'https://developers.google.com') + .addMessage('Find one current Apps Script documentation page about triggers and summarize it in two sentences.'); + + const response = chat.run({ model: 'gpt-5.4', max_tokens: 20000 }); + Logger.log(response); +}