Skip to content

Commit 862def1

Browse files
feat: add Sheets custom function with ADK AI agent (#565)
* feat: add Sheets custom function with ADK AI agent * add missing license
1 parent f41be36 commit 862def1

File tree

5 files changed

+197
-0
lines changed

5 files changed

+197
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
const LOCATION = PropertiesService.getScriptProperties().getProperty('LOCATION');
18+
const GEMINI_MODEL_ID = PropertiesService.getScriptProperties().getProperty('GEMINI_MODEL_ID');
19+
const REASONING_ENGINE_ID = PropertiesService.getScriptProperties().getProperty('REASONING_ENGINE_ID');
20+
const SERVICE_ACCOUNT_KEY = PropertiesService.getScriptProperties().getProperty('SERVICE_ACCOUNT_KEY');
21+
22+
const credentials = credentialsForVertexAI();
23+
24+
/**
25+
* @param {string} statement The statement to fact-check.
26+
*/
27+
function requestLlmAuditorAdkAiAgent(statement) {
28+
return UrlFetchApp.fetch(
29+
`https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${credentials.projectId}/locations/${LOCATION}/reasoningEngines/${REASONING_ENGINE_ID}:streamQuery?alt=sse`,
30+
{
31+
method: 'post',
32+
headers: { 'Authorization': `Bearer ${credentials.accessToken}` },
33+
contentType: 'application/json',
34+
muteHttpExceptions: true,
35+
payload: JSON.stringify({
36+
"class_method": "async_stream_query",
37+
"input": {
38+
"user_id": "google_sheets_custom_function_fact_check",
39+
"message": statement,
40+
}
41+
})
42+
}
43+
).getContentText();
44+
}
45+
46+
/**
47+
* @param {string} prompt The Gemini prompt to use.
48+
*/
49+
function requestOutputFormatting(prompt) {
50+
const response = UrlFetchApp.fetch(
51+
`https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${credentials.projectId}/locations/${LOCATION}/publishers/google/models/${GEMINI_MODEL_ID}:generateContent`,
52+
{
53+
method: 'post',
54+
headers: { 'Authorization': `Bearer ${credentials.accessToken}` },
55+
contentType: 'application/json',
56+
muteHttpExceptions: true,
57+
payload: JSON.stringify({
58+
"contents": [{
59+
"role": "user",
60+
"parts": [{ "text": prompt }]
61+
}],
62+
"generationConfig": { "temperature": 0.1, "maxOutputTokens": 2048 },
63+
"safetySettings": [
64+
{
65+
"category": "HARM_CATEGORY_HARASSMENT",
66+
"threshold": "BLOCK_NONE"
67+
},
68+
{
69+
"category": "HARM_CATEGORY_HATE_SPEECH",
70+
"threshold": "BLOCK_NONE"
71+
},
72+
{
73+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
74+
"threshold": "BLOCK_NONE"
75+
},
76+
{
77+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
78+
"threshold": "BLOCK_NONE"
79+
}
80+
]
81+
})
82+
}
83+
);
84+
return JSON.parse(response).candidates[0].content.parts[0].text
85+
}
86+
87+
/**
88+
* Gets credentials required to call Vertex API using a Service Account.
89+
* Requires use of Service Account Key stored with project.
90+
*
91+
* @return {!Object} Containing the Google Cloud project ID and the access token.
92+
*/
93+
function credentialsForVertexAI() {
94+
const credentials = SERVICE_ACCOUNT_KEY;
95+
if (!credentials) {
96+
throw new Error("service_account_key script property must be set.");
97+
}
98+
99+
const parsedCredentials = JSON.parse(credentials);
100+
101+
const service = OAuth2.createService("Vertex")
102+
.setTokenUrl('https://oauth2.googleapis.com/token')
103+
.setPrivateKey(parsedCredentials['private_key'])
104+
.setIssuer(parsedCredentials['client_email'])
105+
.setPropertyStore(PropertiesService.getScriptProperties())
106+
.setScope("https://www.googleapis.com/auth/cloud-platform");
107+
return {
108+
projectId: parsedCredentials['project_id'],
109+
accessToken: service.getAccessToken(),
110+
}
111+
}

ai/custom-func-ai-agent/Code.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
/**
18+
* Passes a statement to fact-check and, optionally, output formatting instructions.
19+
*
20+
* @param {string} statement The statement to fact-check as a string or single cell
21+
* reference (data ranges are not supported).
22+
* @param {string} outputFormat The instructions as a string or single cell reference
23+
* (data ranges are not supported).
24+
*
25+
* @return The generated and formatted verdict
26+
* @customfunction
27+
*/
28+
function FACT_CHECK(statement, outputFormat) {
29+
if (!outputFormat || outputFormat == "") {
30+
outputFormat = 'Summarize it. Only keep the verdict result and main arguments. '
31+
+ 'Do not reiterate the fact being checked. Remove all markdown. '
32+
+ 'State the verdit result in a first paragraph in a few words and the rest of the summary in a second paragraph.';
33+
}
34+
35+
return requestOutputFormatting(`Here is a fact checking result: ${requestLlmAuditorAdkAiAgent(statement)}.\n\n${outputFormat}`);
36+
}

ai/custom-func-ai-agent/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Google Sheets Custom Function relying on ADK AI Agent and Gemini model
2+
3+
A [Vertex AI](https://cloud.google.com/vertex-ai) agent-powered **fact checker** custom function for Google Sheets to be used as a bound Apps Script project.
4+
5+
![](./images/showcase.png)
6+
7+
## Overview
8+
9+
The **Google Sheets custom function** named `FACT_CHECK` integrates the sophisticated, multi-tool, multi-step reasoning capabilities of a **Vertex AI Agent Engine (ADK Agent)** directly into your Google Sheets spreadsheets.
10+
11+
It operates as an end-to-end solution. It analyzes a statement, grounds its response using the latest web information, and returns the result in the format you need:
12+
13+
* Usage: `=FACT_CHECK("Your statement here")` for a concise and summarized output. `=FACT_CHECK("Your statement here", "Your output formatting instructions here")` for a specific output format.
14+
* Reasoning: [**LLM Auditor ADK AI Agent (Python sample)**](https://github.com/google/adk-samples/tree/main/python/agents/llm-auditor).
15+
* Output formatting: [**Gemini model**](https://cloud.google.com/vertex-ai/generative-ai/docs/models).
16+
17+
## Prerequisites
18+
19+
* Google Cloud Project with billing enabled.
20+
21+
## Set up your environment
22+
23+
1. Configure the Google Cloud project
24+
1. Enable the Vertex AI API
25+
1. Create a Service Account and grant the role `Vertex AI User`
26+
1. Create a private key with type JSON. This will download the JSON file.
27+
1. Setup, install, and deploy the LLM Auditor ADK AI Agent sample
28+
1. Use Vertex AI
29+
1. Use the same Google Cloud project
30+
1. Use the location `us-central1`
31+
1. Use the Vertex AI Agent Engine
32+
1. Open an Apps Script project bound to a Google Sheets spreadsheet
33+
1. Add a Script Property. Enter `LOCATION` as the property name and `us-central1` as the value.
34+
1. Add a Script Property. Enter `GEMINI_MODEL_ID` as the property name and `gemini-2.5-flash-lite` as the value.
35+
1. Add a Script Property. Enter `REASONING_ENGINE_ID` as the property name and the ID of the deployed LLM Auditor ADK AI Agent as the value.
36+
1. Add a Script Property. Enter `SERVICE_ACCOUNT_KEY` as the property name and paste the JSON key from the service account as the value.
37+
1. Add OAuth2 v43 Apps Script Library using the ID `1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF`
38+
1. Set the script files `Code.gs` and `AiVertex.gs` in the Apps Script project with the JS file contents in this project
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"timeZone": "America/Los_Angeles",
3+
"dependencies": {
4+
"libraries": [{
5+
"userSymbol": "OAuth2",
6+
"libraryId": "1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF",
7+
"version": "43"
8+
}]
9+
},
10+
"exceptionLogging": "STACKDRIVER",
11+
"runtimeVersion": "V8"
12+
}
848 KB
Loading

0 commit comments

Comments
 (0)