Skip to content

Migrate Gemini to Interactions API and add interaction threading#90

Open
Benjamin-Sayaque wants to merge 3 commits into
mainfrom
codex/migrate-gemini-api-to-interactions-api
Open

Migrate Gemini to Interactions API and add interaction threading#90
Benjamin-Sayaque wants to merge 3 commits into
mainfrom
codex/migrate-gemini-api-to-interactions-api

Conversation

@Benjamin-Sayaque

Copy link
Copy Markdown
Contributor

Motivation

  • Replace legacy :generateContent usage with the newer Interactions API shape to support Gemini/Vertex multi-turn continuation and unified model specification in the request body.
  • Move conversation threading to interaction ID-based continuation instead of resending full contents[] history to reduce payload size and align with Interactions API semantics.
  • Update function-calling plumbing to detect and handle Interactions steps (model_output / function_call) and to batch function results for continuation.

Description

  • Switch endpoint construction to Interactions endpoints: public Generative Language API https://generativelanguage.googleapis.com/v1beta/interactions and Vertex AI global/regional https://aiplatform.googleapis.com/v1beta1/.../interactions paths, removing :generateContent model URLs. (src/code.gs)
  • Refactor this._buildGeminiPayload to emit an Interactions-style payload with top-level model, input (converted from legacy contents[] via _geminiContentsToInteractionInput), max_output_tokens, temperature, tools, and optional previous_interaction_id; remove legacy tool_config usage and map function call options to tool_choice and tool entries. (src/code.gs)
  • Add interaction state and public API: new instance variables previous_interaction_id, last_gemini_interaction_id, pending_gemini_input, and last_gemini_content_count, plus setPreviousInteractionId() and retrieveLastInteractionId() methods; run() stores returned interaction id into last_gemini_interaction_id and uses previous_interaction_id for continuation. (src/code.gs)
  • Replace Gemini response parsing to inspect Interactions steps: added helpers _extractGeminiResponseText, _extractGeminiFunctionCalls, and _geminiContentsToInteractionInput; update _callGenAIApi finish-reason mapping for Interactions responses. (src/code.gs)
  • Rework _handleGeminiToolCalls to detect function_call steps, execute functions with _callFunction, batch function_result entries and return {contents, input, endWithResult, onlyReturnArguments} so the caller can continue with previous_interaction_id rather than resending full history. (src/code.gs)
  • Simplify contents[] handling so it is used to build the initial input but not accumulated indefinitely when using interaction IDs; preserve backward-compatible fallbacks for legacy response shapes. (src/code.gs)
  • Add interaction-focused tests to src/testFunctions.gs: testGeminiInteractionThreading, testGeminiRetrieveLastInteractionId, testGeminiFunctionCallingInteractionContinuation, and testGeminiVertexInteractionThreading, and include them in testAll(). (src/testFunctions.gs)

Testing

  • Performed static syntax checks with node --check on copies of src/code.gs and src/testFunctions.gs, which succeeded.
  • Searched the codebase for deprecated patterns (:generateContent, models/${model}, generationConfig, tool_config, candidates) to confirm migration of those constructs, and validated no remaining :generateContent endpoints; the grep checks passed.
  • Ran repository sanity checks including git diff --check which reported no whitespace/merge issues.
  • Added automated unit-style tests in src/testFunctions.gs for interaction threading and interaction ID retrieval; these tests were added but not executed against live Gemini/Vertex APIs in this run (they require valid API credentials and network).

Codex Task

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0d9a0dd2-25cb-4d21-9cee-e7c42555ba66

📥 Commits

Reviewing files that changed from the base of the PR and between 41ad4d1 and 5366561.

📒 Files selected for processing (2)
  • src/code.gs
  • src/testFunctions.gs
📜 Recent review details
🔇 Additional comments (25)
src/testFunctions.gs (9)

47-51: testAll() still does not run the Vertex interaction threading test.

The Gemini block executes three Gemini tests but still omits testGeminiVertexInteractionThreading(), so full-suite Gemini coverage remains incomplete for Vertex threading.


230-243: testGeminiInteractionThreading() still lacks continuation assertions.

The test captures only the first interaction ID and logs the second response without asserting second-turn interaction progression or memory carry-over.


258-276: testGeminiFunctionCallingInteractionContinuation() is still single-turn.

The test validates only one run/ID and does not execute a continuation turn to verify interaction-state advancement after function calling.


278-289: testGeminiVertexInteractionThreading() still does not test threading behavior.

Despite the name, it performs one turn and one ID check only; it still misses second-turn continuation validation.


3-34: LGTM!


53-57: LGTM!


69-72: LGTM!


172-175: LGTM!


245-256: LGTM!

src/code.gs (16)

554-566: Vertex AI Interactions API endpoints are not yet available.

This was flagged in a previous review. The Interactions API is currently only available through the Gemini API (generativelanguage.googleapis.com), not on Vertex AI. The else branch (lines 557-565) implementing Vertex AI endpoints should be removed or should throw an error indicating the feature is not yet supported.


988-990: Remove toUpperCase() call—Gemini API expects lowercase schema types.

This was flagged in a previous review. The Gemini API documentation uses and expects lowercase types (e.g., "object", "string", "array"), but this code uppercases them, which may cause schema validation failures.


1852-1856: onlyArgs early exit doesn't terminate processing of remaining function calls.

This was flagged in a previous review. The return inside forEach at line 1855 only skips to the next iteration—it doesn't exit the function. If multiple function calls are present and one has onlyArgs=true, subsequent functions will still be executed before the final return. Replace forEach with a for...of loop to enable true early return.


66-69: LGTM!


373-379: LGTM!


402-411: LGTM!


457-471: LGTM!


589-597: LGTM!


629-650: LGTM!


694-694: LGTM!


1664-1668: LGTM!


1743-1765: LGTM!


1767-1789: LGTM!


1791-1816: LGTM!


1867-1898: LGTM!


949-957: The code structure is correct for the Interactions API.

The payload nesting with max_output_tokens and temperature inside generation_config matches the Interactions API specification. The Interactions API (v1beta) requires these parameters in the generation_config object using snake_case field names, which is exactly how the code structures them. No changes needed.

			> Likely an incorrect or invalid review comment.

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added Gemini interaction continuity support to resume conversations across runs by retrieving and setting the last interaction identifier.
    • Upgraded Gemini tool/function calling and response handling to improve reliability when continuing multi-step exchanges.
  • Bug Fixes
    • Improved Gemini payload construction and response parsing to better support interaction-thread resumption and finish-reason detection.
  • Tests
    • Added model-family-gated test runners and new Gemini interaction tests (threading, last-interaction-id, function-calling continuation, and Vertex-based coverage).
    • Updated Gemini test model to gemini-3.5-flash.

Walkthrough

The PR migrates Gemini integration from the legacy parts/functionCall format to the Interactions API. It adds interaction-ID tracking fields and two public methods on Chat, rewrites endpoint selection, payload construction, tool-call handling, and response parsing to target Interactions API shapes, adds three new helper functions, updates test infrastructure to gate execution by model family, and introduces tests for interaction threading and function-calling continuation.

Changes

Gemini Interactions API migration

Layer / File(s) Summary
Chat interaction state and public API
src/code.gs
Adds four new Gemini state fields (previous_interaction_id, last_gemini_interaction_id, pending_gemini_input, last_gemini_content_count) to Chat, adds retrieveLastInteractionId() and setPreviousInteractionId() public methods, and updates _toJson to include Gemini contents and last interaction ID.
Run loop: endpoint selection and post-response state
src/code.gs
Switches run() endpoint selection to Interactions API URLs (public Generative Language when using geminiKey, otherwise Vertex AI Interactions with region handling and global for Gemini 3), and updates post-response handling to store and compute last_gemini_interaction_id, previous_interaction_id, and last_gemini_content_count.
Run loop: tool-calling control flow and response extraction
src/code.gs
Refactors the Gemini tool-calling branch in run() to use step-based function-call extraction via _extractGeminiFunctionCalls, delegates execution to _handleGeminiToolCalls, supports endWithResult/onlyReturnArguments early exits, and updates the no-tools path to call _extractGeminiResponseText.
Payload construction and response parsing
src/code.gs
Rewrites _buildGeminiPayload to emit Interaction API input (from pending_gemini_input or transformed contents), include previous_interaction_id for resumption, and rename browsing tools to url_context/google_search; adjusts _callGenAIApi to derive finish_reason from Gemini status/finishReason fields and function-call step presence.
Gemini helpers and rewritten _handleGeminiToolCalls
src/code.gs
Adds _geminiContentsToInteractionInput, _extractGeminiResponseText (with legacy fallback), and _extractGeminiFunctionCalls (with legacy fallback); rewrites _handleGeminiToolCalls to accumulate function results into Interaction-compatible contents turns and return structured state (contents, input, endWithResult, onlyReturnArguments).
Test model filtering and Gemini interaction tests
src/testFunctions.gs
Adds TEST_MODEL_TARGETS global and testAllGpt()/testAllThinking()/testAllGemini() wrappers to gate tests by model family; updates testAll() to conditionally run Gemini tests and gate GPT-only tests; modifies runTestAcrossModels() to filter by model family; gates testInputTokenWarning() on GPT selection; adds testGeminiInteractionThreading, testGeminiRetrieveLastInteractionId, testGeminiFunctionCallingInteractionContinuation, and testGeminiVertexInteractionThreading to validate interaction ID retrieval and continuation across turns.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: migrating Gemini to the Interactions API and adding interaction threading support.
Description check ✅ Passed The description provides comprehensive details about the motivation, implementation approach, and testing for the Interactions API migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch codex/migrate-gemini-api-to-interactions-api
  • 🛠️ JSDoc Checks

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8c0eaf17c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/code.gs Outdated
parts.forEach(part => {
if (!part) return;
if (part.text) {
item.content.push({ type: "input_text", text: part.text });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Send Gemini prompts as valid interaction content

When any Gemini chat contains a message added through addMessage, this helper sends OpenAI-shaped input items such as { role: "user", content: [{ type: "input_text", ... }] }. The Gemini Interactions API expects a string, Gemini content blocks such as type: "text", or step objects such as type: "user_input"; it has no input_text content type, so ordinary Gemini text turns are rejected before the model sees the prompt.

Useful? React with 👍 / 👎.

Comment thread src/code.gs Outdated
Comment on lines +951 to +952
max_output_tokens: max_tokens,
temperature: temperature,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Put Gemini generation settings under generation_config

Every Gemini Interactions request now puts max_output_tokens and temperature at the request root, but the Interactions API defines these only inside generation_config. With Gemini REST validation this makes chat.run({ model: "gemini...", max_tokens: ... }) send an invalid request rather than applying the caller's token and temperature settings.

Useful? React with 👍 / 👎.

Comment thread src/code.gs
}
else {
endpointUrl = `https://${region}-aiplatform.googleapis.com/v1/projects/${gcpProjectId}/locations/${region}/publishers/google/models/${model}:generateContent`;
endpointUrl = `https://${region}-aiplatform.googleapis.com/v1beta1/projects/${gcpProjectId}/locations/${region}/interactions`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep Vertex interaction paths on the global location

When callers use setGeminiAuth(project, "us-central1") with a non-Gemini-3 model, this branch posts to /locations/us-central1/interactions. The Vertex/Cloud Interactions create method is exposed as /projects/{project}/locations/global/interactions, so users with the normal regional Vertex configuration will get 404s unless they omit the region or happen to use a Gemini 3 model.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/code.gs`:
- Around line 983-985: The toolFunction.parameters.type assignment is converting
the type value to uppercase using .toUpperCase(), but the Gemini API expects
lowercase schema types like "object", "string", and "array". Remove the
.toUpperCase() method call from the line where toolFunction.parameters.type is
assigned so that the type value remains in its original lowercase format,
ensuring it matches the API's expected schema format.
- Around line 1848-1851: The `return` statement inside the `forEach` loop at the
`onlyArgs` check only skips the current iteration instead of exiting the entire
function. Replace the `forEach` loop with a traditional `for` loop so that when
`onlyArgs` is true and `onlyReturnArguments` is assigned, the function can use
`return` to exit immediately and stop processing remaining function calls,
similar to how the OpenAI handler at lines 1927-1933 handles early termination
with `return messages`.
- Around line 554-564: The Interactions API is only available through the Gemini
API and not yet supported on Vertex AI, so the Vertex AI endpoint branches
should be removed. Locate the else block that contains the comment "Enterprise
endpoint / Vertex AI API" and the conditions checking for region and gemini-3
model, then either remove this entire else block entirely, or replace it with an
error that explicitly states that Vertex AI is not supported for the
Interactions API. This ensures the code only uses the
generativelanguage.googleapis.com endpoint which is the only valid endpoint for
this API.

In `@src/testFunctions.gs`:
- Around line 195-208: The testGeminiInteractionThreading function verifies the
first interaction ID but fails to validate that threading actually succeeded on
the second turn. After calling chat.run() for the secondResponse, retrieve the
second interaction ID using chat.retrieveLastInteractionId() and assert that it
exists, then add an assertion to verify that the secondResponse actually
contains evidence of memory from the first turn (such as checking that the
response mentions the keyword "papaya" that was provided in the initial
message). This ensures the test will fail if continuation or threading is broken
rather than passing with incomplete validation.
- Around line 243-254: The testGeminiVertexInteractionThreading function is
named to indicate multi-turn conversation testing but only executes a single
message-response cycle. To properly test threading, add a second message to the
chat object using addMessage, execute another run call to get a follow-up
response, retrieve the new interaction ID using retrieveLastInteractionId, and
validate that the interaction progression is working correctly (such as
verifying the new interaction ID is different from or properly related to the
first one). This will ensure the function actually tests the multi-turn nature
of the chat threading.
- Around line 223-241: The testGeminiFunctionCallingInteractionContinuation
function only performs an initial chat run but never executes a continuation
turn to test the full function-calling workflow. After calling chat.run() and
retrieving the interaction ID, the test needs to simulate the function execution
by calling a continuation method on the chat object (likely
chat.continueWithFunctionResult or similar) with a mock weather result, then run
the chat again to verify the continuation returns a proper response. This
ensures the interaction ID is properly maintained across the function-calling
round-trip and the chat correctly handles the continuation contract.
- Around line 18-20: The testAll() function is missing an invocation of the
Vertex interaction threading test. Add a call to
testGeminiVertexInteractionThreading() in the testAll() function alongside the
other Gemini test invocations (testGeminiInteractionThreading,
testGeminiRetrieveLastInteractionId, and
testGeminiFunctionCallingInteractionContinuation) to ensure the Vertex
interaction threading path is tested during full-suite runs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 94b7f5b1-9960-4dc1-90a0-9493409db5d6

📥 Commits

Reviewing files that changed from the base of the PR and between c413ee2 and 8c0eaf1.

📒 Files selected for processing (2)
  • src/code.gs
  • src/testFunctions.gs
📜 Review details
🔇 Additional comments (9)
src/code.gs (9)

66-69: LGTM!


373-379: LGTM!


402-411: LGTM!


457-470: LGTM!


588-596: LGTM!


628-649: LGTM!


693-693: LGTM!


1659-1663: LGTM!


1738-1811: LGTM!

Comment thread src/code.gs
Comment on lines +554 to +564
endpointUrl = `https://generativelanguage.googleapis.com/v1beta/interactions`;
}
else {
// Enterprise endpoint / Vertex AI API
// https://console.cloud.google.com/apis/api/aiplatform.googleapis.com
// requires scope "https://www.googleapis.com/auth/cloud-platform.read-only" in access token
if (!region || model.includes("gemini-3")) { // Gemini 3 requires global endpoint when using Vertex AI API
endpointUrl = `https://aiplatform.googleapis.com/v1/projects/${gcpProjectId}/locations/global/publishers/google/models/${model}:generateContent`;
endpointUrl = `https://aiplatform.googleapis.com/v1beta1/projects/${gcpProjectId}/locations/global/interactions`;
}
else {
endpointUrl = `https://${region}-aiplatform.googleapis.com/v1/projects/${gcpProjectId}/locations/${region}/publishers/google/models/${model}:generateContent`;
endpointUrl = `https://${region}-aiplatform.googleapis.com/v1beta1/projects/${gcpProjectId}/locations/${region}/interactions`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Gemini Interactions API endpoint URL v1beta 2026

💡 Result:

As of June 2026, the Gemini Interactions API utilizes the v1beta version of the Generative Language API [1]. The base service endpoint for these requests is: https://generativelanguage.googleapis.com/v1beta/interactions [1] Key operations available at this endpoint include: * Creating an interaction: POST https://generativelanguage.googleapis.com/v1beta/interactions [1] * Retrieving an interaction: GET https://generativelanguage.googleapis.com/v1beta/interactions/{id} [1] * Canceling an interaction: POST https://generativelanguage.googleapis.com/v1beta/interactions/{id}/cancel [1] * Deleting an interaction: DELETE https://generativelanguage.googleapis.com/v1beta/interactions/{id} [1] The Interactions API is currently in a Beta/preview stage [2][3]. It is designed to support server-side state management, agentic workflows, and complex multi-turn conversations [2]. Because it is in an early development phase, it is subject to breaking changes, and the standard generateContent API is recommended for stable production workloads [2].

Citations:


🌐 Web query:

Vertex AI Interactions API endpoint URL aiplatform.googleapis.com v1beta1 2026

💡 Result:

As of June 22, 2026, the Interactions API is available within the Gemini Enterprise Agent Platform (often integrated under or alongside Vertex AI services) [1]. While earlier reports indicated it was limited to the Gemini API endpoint [2], documentation now explicitly lists the endpoint for the Interactions API under the aiplatform.googleapis.com service [1]. The base URL for the Interactions API is: https://aiplatform.googleapis.com/v1beta1/projects/{project}/locations/global/interactions [1] Key details include: - Scope: It is currently part of the Gemini Enterprise Agent Platform, which is accessed via the aiplatform.googleapis.com service [3][1]. - Version: The v1beta1 version is used for these interactions [1]. - Status: The API is in public beta and is subject to potential breaking changes [4][5]. For stable production workloads, the standard generateContent API remains the recommended service [4][5]. Developers should use the aiplatform.googleapis.com endpoint when configuring clients for this service, ensuring they specify the correct project and location pathing as required by the API structure [1].

Citations:


🌐 Web query:

Vertex AI Interactions API regional endpoints locations gemini-3 models 2026

💡 Result:

As of June 22, 2026, the status of the Interactions API and Gemini 3 models on Vertex AI is as follows: Interactions API: This API is currently only available through the Gemini API (Google AI for Developers) at the endpoint https://generativelanguage.googleapis.com/v1beta/interactions [1][2][3]. It is not yet available as a native feature within Vertex AI, though it is on the product roadmap [3]. Gemini 3 Models on Vertex AI: Gemini 3 models (including variants like gemini-3-pro-preview and gemini-3.5-flash) are available on Vertex AI primarily through a global endpoint [4][5]. While Vertex AI supports various regional endpoints for other services, users have reported that many regional endpoints do not currently support Gemini 3 models, often returning errors such as 404 NOT_FOUND or 400 FAILED_PRECONDITION when attempted [4][5][6]. Developers requiring data residency or specific latency optimization are often restricted to the global endpoint or specific, limited regional access, which may require allowlisting for production projects [4][5]. For the most up-to-date information on supported regional endpoints for specific models, you should consult the official Google Cloud model endpoint locations table in the Gemini Enterprise Agent Platform documentation, which distinguishes between global and regional availability [4][7].

Citations:


Remove Vertex AI endpoints for Interactions API—currently unavailable on Vertex AI.

The Interactions API is only available through the Gemini API (generativelanguage.googleapis.com) and is not yet available as a native feature on Vertex AI, despite being on the product roadmap. The code should not implement Vertex AI endpoints (lines 561, 564) for this API. Remove the entire Vertex AI branch or raise an error if Vertex AI is selected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/code.gs` around lines 554 - 564, The Interactions API is only available
through the Gemini API and not yet supported on Vertex AI, so the Vertex AI
endpoint branches should be removed. Locate the else block that contains the
comment "Enterprise endpoint / Vertex AI API" and the conditions checking for
region and gemini-3 model, then either remove this entire else block entirely,
or replace it with an error that explicitly states that Vertex AI is not
supported for the Interactions API. This ensures the code only uses the
generativelanguage.googleapis.com endpoint which is the only valid endpoint for
this API.

Comment thread src/code.gs
Comment on lines 983 to 985
if (parameters?.type) {
toolFunction.parameters.type = parameters.type.toUpperCase();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Gemini Interactions API function parameters schema type case

💡 Result:

In the Gemini API, function parameter schema definitions follow a subset of the OpenAPI 3.0 specification [1][2][3]. Regarding the casing of types and parameter names, the following rules apply: Parameter Names: Parameter names are case-sensitive [4][5][6]. It is recommended to use snake_case or camelCase consistently throughout your schema to ensure the model correctly maps arguments [1][2]. Type Values: While JSON Schema technically supports lowercase types (e.g., "string", "integer", "object", "array") [1][2][7], certain Google Cloud and Vertex AI documentation references indicate that uppercase types (e.g., "STRING", "INTEGER", "OBJECT", "ARRAY") are also accepted or sometimes utilized in their internal representations [3][8][9]. However, official developer documentation for the Gemini API (ai.google.dev) predominantly uses lowercase types [1][2][7]. When defining your parameters object, you must specify the type of the overall container (typically "object") and then define the properties within it, where each property also requires a type definition [1][2][4]. Example structure: { "type": "object", "properties": { "parameter_name": { "type": "string", "description": "Description of the parameter" } }, "required": ["parameter_name"] } Best Practice: Stick to lowercase types (e.g., "string", "number", "integer", "boolean", "array", "object") as shown in the primary ai.google.dev documentation [1][2], and ensure your parameter names are consistent, descriptive, and case-sensitive [4][5].

Citations:


Remove toUpperCase() call—Gemini API expects lowercase schema types.

Line 984 uppercases the type field (parameters.type.toUpperCase()), but the official Gemini API documentation uses and expects lowercase types (e.g., "object", "string", "array"). This conversion may cause schema validation failures. Remove the toUpperCase() call to ensure types match the API's expected format.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/code.gs` around lines 983 - 985, The toolFunction.parameters.type
assignment is converting the type value to uppercase using .toUpperCase(), but
the Gemini API expects lowercase schema types like "object", "string", and
"array". Remove the .toUpperCase() method call from the line where
toolFunction.parameters.type is assigned so that the type value remains in its
original lowercase format, ensuring it matches the API's expected schema format.

Comment thread src/code.gs
Comment on lines +1848 to 1851
if (onlyArgs) {
onlyReturnArguments = functionArgs;
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

onlyArgs early exit doesn't terminate processing of other function calls.

Using return inside forEach only skips to the next iteration—it doesn't exit the function. If multiple function calls are returned by Gemini and one has onlyArgs=true, the loop continues processing (and executing) the remaining functions before returning.

Compare with the OpenAI handler (lines 1927-1933) which uses return messages to exit immediately.

🐛 Proposed fix using a for-loop with early return
   function _handleGeminiToolCalls(responseMessage, tools, contents) {
     const functionCalls = _extractGeminiFunctionCalls(responseMessage);
     const functionResults = [];
     let shouldEndWithResult = false;
     let onlyReturnArguments = null;
 
-    functionCalls.forEach(functionCall => {
+    for (const functionCall of functionCalls) {
       const functionName = functionCall.name;
       const functionArgs = functionCall.args || {};
-      if (!functionName) return;
+      if (!functionName) continue;
 
       let argsOrder = [];
       let endWithResult = false;
       let onlyArgs = false;
 
       for (const t in tools) {
         const currentFunction = tools[t].function._toJson();
         if (currentFunction.name == functionName) {
           argsOrder = currentFunction.argumentsInRightOrder;
           endWithResult = currentFunction.endingFunction;
           onlyArgs = currentFunction.onlyArgs;
           break;
         }
       }
 
       // No actual call to the function
       if (onlyArgs) {
-        onlyReturnArguments = functionArgs;
-        return;
+        return {
+          contents: contents,
+          input: [],
+          endWithResult: false,
+          onlyReturnArguments: functionArgs
+        };
       }
 
       if (endWithResult) {
         shouldEndWithResult = true;
       }
 
       let functionResponse = _callFunction(functionName, functionArgs, argsOrder);
       // ... rest of loop body
-    });
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/code.gs` around lines 1848 - 1851, The `return` statement inside the
`forEach` loop at the `onlyArgs` check only skips the current iteration instead
of exiting the entire function. Replace the `forEach` loop with a traditional
`for` loop so that when `onlyArgs` is true and `onlyReturnArguments` is
assigned, the function can use `return` to exit immediately and stop processing
remaining function calls, similar to how the OpenAI handler at lines 1927-1933
handles early termination with `return messages`.

Comment thread src/testFunctions.gs Outdated
Comment on lines +18 to +20
testGeminiInteractionThreading();
testGeminiRetrieveLastInteractionId();
testGeminiFunctionCallingInteractionContinuation();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

testAll() skips the Vertex threading test.

Line 18–20 adds new Gemini tests, but testGeminiVertexInteractionThreading() is never invoked, so the Vertex interaction path is not exercised in full-suite runs.

Suggested patch
 function testAll() {
@@
   testGeminiInteractionThreading();
   testGeminiRetrieveLastInteractionId();
   testGeminiFunctionCallingInteractionContinuation();
+  if (typeof GCP_PROJECT_ID === "string" && GCP_PROJECT_ID &&
+      typeof REGION === "string" && REGION) {
+    testGeminiVertexInteractionThreading();
+  }
   // OpenAI-only tests - require valid Drive file IDs.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
testGeminiInteractionThreading();
testGeminiRetrieveLastInteractionId();
testGeminiFunctionCallingInteractionContinuation();
testGeminiInteractionThreading();
testGeminiRetrieveLastInteractionId();
testGeminiFunctionCallingInteractionContinuation();
if (typeof GCP_PROJECT_ID === "string" && GCP_PROJECT_ID &&
typeof REGION === "string" && REGION) {
testGeminiVertexInteractionThreading();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/testFunctions.gs` around lines 18 - 20, The testAll() function is missing
an invocation of the Vertex interaction threading test. Add a call to
testGeminiVertexInteractionThreading() in the testAll() function alongside the
other Gemini test invocations (testGeminiInteractionThreading,
testGeminiRetrieveLastInteractionId, and
testGeminiFunctionCallingInteractionContinuation) to ensure the Vertex
interaction threading path is tested during full-suite runs.

Comment thread src/testFunctions.gs
Comment on lines +195 to +208
function testGeminiInteractionThreading() {
GenAIApp.setGeminiAPIKey(GEMINI_API_KEY);
const chat = GenAIApp.newChat();
chat.addMessage("Remember this keyword for the next turn: papaya.");
const firstResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
const interactionId = chat.retrieveLastInteractionId();
if (!interactionId) {
throw new Error("Gemini interaction ID was not captured after the first response.");
}
console.log(`Gemini first interaction id: ${interactionId}`);
chat.addMessage("What keyword did I ask you to remember?");
const secondResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
console.log(`Gemini threaded response:\n${secondResponse}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

testGeminiInteractionThreading does not verify continuation success.

Line 205–207 runs a second turn but never validates that threading actually worked (no second interaction-ID check, no memory assertion), so this can pass even when continuation is broken.

Suggested patch
 function testGeminiInteractionThreading() {
@@
-  chat.addMessage("What keyword did I ask you to remember?");
+  chat.addMessage("What keyword did I ask you to remember? Reply with only that keyword.");
   const secondResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
+  const secondInteractionId = chat.retrieveLastInteractionId();
+  if (!secondInteractionId || secondInteractionId === interactionId) {
+    throw new Error("Gemini continuation did not produce a valid next interaction ID.");
+  }
+  if (!/papaya/i.test(secondResponse)) {
+    throw new Error("Gemini threaded response did not preserve prior-turn context.");
+  }
   console.log(`Gemini threaded response:\n${secondResponse}`);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testGeminiInteractionThreading() {
GenAIApp.setGeminiAPIKey(GEMINI_API_KEY);
const chat = GenAIApp.newChat();
chat.addMessage("Remember this keyword for the next turn: papaya.");
const firstResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
const interactionId = chat.retrieveLastInteractionId();
if (!interactionId) {
throw new Error("Gemini interaction ID was not captured after the first response.");
}
console.log(`Gemini first interaction id: ${interactionId}`);
chat.addMessage("What keyword did I ask you to remember?");
const secondResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
console.log(`Gemini threaded response:\n${secondResponse}`);
}
function testGeminiInteractionThreading() {
GenAIApp.setGeminiAPIKey(GEMINI_API_KEY);
const chat = GenAIApp.newChat();
chat.addMessage("Remember this keyword for the next turn: papaya.");
const firstResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
const interactionId = chat.retrieveLastInteractionId();
if (!interactionId) {
throw new Error("Gemini interaction ID was not captured after the first response.");
}
console.log(`Gemini first interaction id: ${interactionId}`);
chat.addMessage("What keyword did I ask you to remember? Reply with only that keyword.");
const secondResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
const secondInteractionId = chat.retrieveLastInteractionId();
if (!secondInteractionId || secondInteractionId === interactionId) {
throw new Error("Gemini continuation did not produce a valid next interaction ID.");
}
if (!/papaya/i.test(secondResponse)) {
throw new Error("Gemini threaded response did not preserve prior-turn context.");
}
console.log(`Gemini threaded response:\n${secondResponse}`);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/testFunctions.gs` around lines 195 - 208, The
testGeminiInteractionThreading function verifies the first interaction ID but
fails to validate that threading actually succeeded on the second turn. After
calling chat.run() for the secondResponse, retrieve the second interaction ID
using chat.retrieveLastInteractionId() and assert that it exists, then add an
assertion to verify that the secondResponse actually contains evidence of memory
from the first turn (such as checking that the response mentions the keyword
"papaya" that was provided in the initial message). This ensures the test will
fail if continuation or threading is broken rather than passing with incomplete
validation.

Comment thread src/testFunctions.gs
Comment on lines +223 to +241
function testGeminiFunctionCallingInteractionContinuation() {
GenAIApp.setGeminiAPIKey(GEMINI_API_KEY);
const weatherFunction = GenAIApp.newFunction()
.setName("getWeather")
.setDescription("To retrieve the weather in a city in °C")
.addParameter("cityName", "string", "The name of the city.");

const chat = GenAIApp.newChat();
chat
.addMessage("What's the weather in Paris? Use the available function, then answer normally.")
.addFunction(weatherFunction);
const response = chat.run({ model: GEMINI_MODEL, max_tokens: 1000 });
const interactionId = chat.retrieveLastInteractionId();
if (!interactionId) {
throw new Error("Gemini interaction ID was not captured after function-call continuation.");
}
console.log(`Gemini function continuation response:\n${response}`);
console.log(`Gemini function continuation interaction id: ${interactionId}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

testGeminiFunctionCallingInteractionContinuation never performs a continuation turn.

Lines 234–240 only validate the first run’s ID. This does not test the continuation contract after function-calling.

Suggested patch
 function testGeminiFunctionCallingInteractionContinuation() {
@@
-  const response = chat.run({ model: GEMINI_MODEL, max_tokens: 1000 });
-  const interactionId = chat.retrieveLastInteractionId();
-  if (!interactionId) {
+  const firstResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 1000 });
+  const firstInteractionId = chat.retrieveLastInteractionId();
+  if (!firstInteractionId) {
     throw new Error("Gemini interaction ID was not captured after function-call continuation.");
   }
-  console.log(`Gemini function continuation response:\n${response}`);
-  console.log(`Gemini function continuation interaction id: ${interactionId}`);
+  chat.addMessage("Using one word only, what city did you check?");
+  const secondResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
+  const secondInteractionId = chat.retrieveLastInteractionId();
+  if (!secondInteractionId || secondInteractionId === firstInteractionId) {
+    throw new Error("Gemini function-call continuation did not advance interaction state.");
+  }
+  if (!/paris/i.test(secondResponse)) {
+    throw new Error("Gemini function-call continuation did not preserve context.");
+  }
+  console.log(`Gemini function continuation first response:\n${firstResponse}`);
+  console.log(`Gemini function continuation second response:\n${secondResponse}`);
+  console.log(`Gemini function continuation interaction ids: ${firstInteractionId} -> ${secondInteractionId}`);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/testFunctions.gs` around lines 223 - 241, The
testGeminiFunctionCallingInteractionContinuation function only performs an
initial chat run but never executes a continuation turn to test the full
function-calling workflow. After calling chat.run() and retrieving the
interaction ID, the test needs to simulate the function execution by calling a
continuation method on the chat object (likely chat.continueWithFunctionResult
or similar) with a mock weather result, then run the chat again to verify the
continuation returns a proper response. This ensures the interaction ID is
properly maintained across the function-calling round-trip and the chat
correctly handles the continuation contract.

Comment thread src/testFunctions.gs
Comment on lines +243 to +254
function testGeminiVertexInteractionThreading() {
GenAIApp.setGeminiAuth(GCP_PROJECT_ID, REGION);
const chat = GenAIApp.newChat();
chat.addMessage("Reply with the word vertex and a one-sentence explanation of interactions.");
const response = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
const interactionId = chat.retrieveLastInteractionId();
if (!interactionId) {
throw new Error("Vertex Gemini interaction ID was not captured.");
}
console.log(`Vertex Gemini interaction response:\n${response}`);
console.log(`Vertex Gemini interaction id: ${interactionId}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

testGeminiVertexInteractionThreading is single-turn despite threading intent.

Line 243 names this as threading, but it only executes one call and checks one ID. It should perform a follow-up turn and validate interaction progression.

Suggested patch
 function testGeminiVertexInteractionThreading() {
   GenAIApp.setGeminiAuth(GCP_PROJECT_ID, REGION);
   const chat = GenAIApp.newChat();
-  chat.addMessage("Reply with the word vertex and a one-sentence explanation of interactions.");
-  const response = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
-  const interactionId = chat.retrieveLastInteractionId();
-  if (!interactionId) {
+  chat.addMessage("Remember this token for the next turn: vertex-token.");
+  const firstResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
+  const firstInteractionId = chat.retrieveLastInteractionId();
+  if (!firstInteractionId) {
     throw new Error("Vertex Gemini interaction ID was not captured.");
   }
-  console.log(`Vertex Gemini interaction response:\n${response}`);
-  console.log(`Vertex Gemini interaction id: ${interactionId}`);
+  chat.addMessage("Reply with only the remembered token.");
+  const secondResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 300 });
+  const secondInteractionId = chat.retrieveLastInteractionId();
+  if (!secondInteractionId || secondInteractionId === firstInteractionId) {
+    throw new Error("Vertex Gemini continuation did not produce a valid next interaction ID.");
+  }
+  if (!/vertex-token/i.test(secondResponse)) {
+    throw new Error("Vertex Gemini threaded response did not preserve prior-turn context.");
+  }
+  console.log(`Vertex Gemini first response:\n${firstResponse}`);
+  console.log(`Vertex Gemini second response:\n${secondResponse}`);
+  console.log(`Vertex Gemini interaction ids: ${firstInteractionId} -> ${secondInteractionId}`);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testGeminiVertexInteractionThreading() {
GenAIApp.setGeminiAuth(GCP_PROJECT_ID, REGION);
const chat = GenAIApp.newChat();
chat.addMessage("Reply with the word vertex and a one-sentence explanation of interactions.");
const response = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
const interactionId = chat.retrieveLastInteractionId();
if (!interactionId) {
throw new Error("Vertex Gemini interaction ID was not captured.");
}
console.log(`Vertex Gemini interaction response:\n${response}`);
console.log(`Vertex Gemini interaction id: ${interactionId}`);
}
function testGeminiVertexInteractionThreading() {
GenAIApp.setGeminiAuth(GCP_PROJECT_ID, REGION);
const chat = GenAIApp.newChat();
chat.addMessage("Remember this token for the next turn: vertex-token.");
const firstResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 500 });
const firstInteractionId = chat.retrieveLastInteractionId();
if (!firstInteractionId) {
throw new Error("Vertex Gemini interaction ID was not captured.");
}
chat.addMessage("Reply with only the remembered token.");
const secondResponse = chat.run({ model: GEMINI_MODEL, max_tokens: 300 });
const secondInteractionId = chat.retrieveLastInteractionId();
if (!secondInteractionId || secondInteractionId === firstInteractionId) {
throw new Error("Vertex Gemini continuation did not produce a valid next interaction ID.");
}
if (!/vertex-token/i.test(secondResponse)) {
throw new Error("Vertex Gemini threaded response did not preserve prior-turn context.");
}
console.log(`Vertex Gemini first response:\n${firstResponse}`);
console.log(`Vertex Gemini second response:\n${secondResponse}`);
console.log(`Vertex Gemini interaction ids: ${firstInteractionId} -> ${secondInteractionId}`);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/testFunctions.gs` around lines 243 - 254, The
testGeminiVertexInteractionThreading function is named to indicate multi-turn
conversation testing but only executes a single message-response cycle. To
properly test threading, add a second message to the chat object using
addMessage, execute another run call to get a follow-up response, retrieve the
new interaction ID using retrieveLastInteractionId, and validate that the
interaction progression is working correctly (such as verifying the new
interaction ID is different from or properly related to the first one). This
will ensure the function actually tests the multi-turn nature of the chat
threading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant