Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
522 changes: 200 additions & 322 deletions README.md

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions samples/configuration-options.gs
Original file line number Diff line number Diff line change
@@ -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);
}
23 changes: 23 additions & 0 deletions samples/conversation-continuation.gs
Original file line number Diff line number Diff line change
@@ -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?');
Comment thread
aubrypaul marked this conversation as resolved.

const response = secondChat.run({ model: 'gpt-5.4' });
Logger.log(response);
}
31 changes: 31 additions & 0 deletions samples/document-analysis.gs
Original file line number Diff line number Diff line change
@@ -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);
Comment thread
aubrypaul marked this conversation as resolved.

const response = chat.run({ model: 'gpt-5.4' });
Logger.log(response);
}
39 changes: 39 additions & 0 deletions samples/function-calling-advanced.gs
Original file line number Diff line number Diff line change
@@ -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' };
}
31 changes: 31 additions & 0 deletions samples/function-calling-basics.gs
Original file line number Diff line number Diff line change
@@ -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' });
Comment thread
aubrypaul marked this conversation as resolved.
Logger.log(response);
}

function sampleGetWeather(city, unit) {
return {
city: city,
unit: unit || 'celsius',
condition: 'sunny',
temperature: 22
};
}
24 changes: 24 additions & 0 deletions samples/google-mcp-connector.gs
Original file line number Diff line number Diff line change
@@ -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);
}
20 changes: 20 additions & 0 deletions samples/image-analysis.gs
Original file line number Diff line number Diff line change
@@ -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');
Comment thread
aubrypaul marked this conversation as resolved.

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);
}
16 changes: 16 additions & 0 deletions samples/knowledge-links.gs
Original file line number Diff line number Diff line change
@@ -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);
}
22 changes: 22 additions & 0 deletions samples/mcp-connectors.gs
Original file line number Diff line number Diff line change
@@ -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);
}
20 changes: 20 additions & 0 deletions samples/multi-model-usage.gs
Original file line number Diff line number Diff line change
@@ -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'));
Comment thread
aubrypaul marked this conversation as resolved.

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);
});
}
26 changes: 26 additions & 0 deletions samples/sheets-ai-assistant.gs
Original file line number Diff line number Diff line change
@@ -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();
Comment thread
aubrypaul marked this conversation as resolved.
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);
Comment thread
aubrypaul marked this conversation as resolved.
}
15 changes: 15 additions & 0 deletions samples/simple-chat.gs
Original file line number Diff line number Diff line change
@@ -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'));
Comment thread
aubrypaul marked this conversation as resolved.

const chat = GenAIApp.newChat();
chat.addMessage('Say hello in one friendly sentence.');

const response = chat.run({ model: 'gpt-5.4' });
Logger.log(response);
}
16 changes: 16 additions & 0 deletions samples/system-prompts.gs
Original file line number Diff line number Diff line change
@@ -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);
}
37 changes: 37 additions & 0 deletions samples/vector-store-rag.gs
Original file line number Diff line number Diff line change
@@ -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' });
Comment thread
aubrypaul marked this conversation as resolved.
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);
}
19 changes: 19 additions & 0 deletions samples/vertex-ai-setup.gs
Original file line number Diff line number Diff line change
@@ -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' });
Comment thread
aubrypaul marked this conversation as resolved.
Logger.log(response);
}
16 changes: 16 additions & 0 deletions samples/web-browsing.gs
Original file line number Diff line number Diff line change
@@ -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);
}