Chapter 25

Google: The Gemini Interactions API and function_result

Since June 2026 Google's Interactions API has GA status and is recommended for new Gemini projects. Model steps contain a function_call, and the application continues the conversation via previous_interaction_id and function_result. This is closer to Responses than to stateless Messages, but with its own fields.

Minimum SDK version. The Interactions API is available in the JavaScript package @google/genai starting from version 2.3.0. Install the current version and pin a genuinely tested build in the lockfile.

The adapter starts with the client and the tool-definition translation.

TypeScript
import { GoogleGenAI } from "@google/genai";

const client = new GoogleGenAI({});
const model = process.env.GOOGLE_MODEL ?? "gemini-3.6-flash";

type GoogleState = {
  previousInteractionId: string;
  instructions: string;
  tools: ToolDefinition[];
};

function toGoogleTool(tool: ToolDefinition) {
  return {
    type: "function" as const,
    name: tool.name,
    description: tool.description,
    parameters: tool.parameters
  };
}

The first interaction sends the message and tools, and the state is the interaction.id.

TypeScript
async function start(input: AgentInput): Promise<ProviderTurn> {
  const interaction = await client.interactions.create({
    model,
    input: input.message,
    system_instruction: input.instructions,
    tools: input.tools.map(toGoogleTool),
    store: true
  });

  return fromGoogleInteraction(interaction, {
    previousInteractionId: interaction.id,
    instructions: input.instructions,
    tools: input.tools
  });
}

The continuation returns the results as function_result keyed by previous_interaction_id.

TypeScript
async function resume(rawState: unknown, results: ToolResult[]) {
  const state = googleStateSchema.parse(rawState);
  const interaction = await client.interactions.create({
    model,
    previous_interaction_id: state.previousInteractionId,
    system_instruction: state.instructions,
    tools: state.tools.map(toGoogleTool),
    input: results.map(function toFunctionResult(result) {
      return {
        type: "function_result" as const,
        name: result.name,
        call_id: result.callId,
        result: [{
          type: "text" as const,
          text: JSON.stringify({ ok: !result.isError, value: result.output })
        }]
      };
    })
  });

  return fromGoogleInteraction(interaction, {
    ...state,
    previousInteractionId: interaction.id
  });
}

Normalizing the steps reduces them to the shared ProviderTurn.

TypeScript
function fromGoogleInteraction(interaction: any, state: GoogleState): ProviderTurn {
  const calls = interaction.steps
    .filter(function isCall(step: any) { return step.type === "function_call"; })
    .map(function normalize(step: any): ToolCall {
      return { id: step.id, name: step.name, arguments: step.arguments };
    });

  if (calls.length > 0) return { kind: "tool_calls", calls, state };
  return { kind: "text", text: interaction.output_text ?? "", state };
}

Two caveats finish the adapter. The first is about what previous_interaction_id does NOT carry over.

Repeat the interaction-scoped fields. previous_interaction_id preserves history but doesn't carry over tools, system_instruction, and generation_config. The adapter must pass them again on every turn.

The second is about when you still need the old generateContent.

When you need legacy generateContent. Google keeps supporting it. In the current documentation explicit caching, Batch, and some special capabilities are still a reason to use generateContent. For a new ordinary agent loop, start with Interactions.

Google, like OpenAI, also has a full framework - the ADK. Let's look at when it's justified.

Links