Gemini Interactions API

The Gemini Interactions API is Google's stateful conversation endpoint. Unlike the standard generateContent/streamGenerateContent endpoints, Interactions uses previous_interaction_id for server-side conversation state, flat outputs[] instead of nested candidates[].content.parts[], and typed SSE events with an event_type field inside each JSON payload.

Endpoint

Method Path Format
POST /v1beta/interactions SSE (typed JSON events)

Quick Start

Set up a minimal mock that responds to a Gemini Interactions request:

quick-start.ts ts
import { LLMock } from "@copilotkit/aimock";

const mock = new LLMock();
mock.on({ userMessage: "hello" }, { content: "Hi there!" });
await mock.listen(4010);

// Client sends:
// POST /v1beta/interactions
// { model: "gemini-2.5-flash", input: "hello", stream: true }

Request Format

The Interactions API accepts several input shapes. aimock normalizes all of them into the unified fixture-matching format.

String Input (Simple Prompt)

string-input.json json
{
  "model": "gemini-2.5-flash",
  "input": "What is the capital of France?",
  "stream": true
}

Turn[] Input (Multi-Turn)

multi-turn-input.json json
{
  "model": "gemini-2.5-flash",
  "input": [
    { "role": "user", "parts": [{ "type": "text", "text": "Hello" }] },
    { "role": "model", "parts": [{ "type": "text", "text": "Hi there!" }] },
    { "role": "user", "parts": [{ "type": "text", "text": "Tell me a joke" }] }
  ],
  "stream": true
}

Content[] with Function Result (Tool Response)

tool-response-input.json json
{
  "model": "gemini-2.5-flash",
  "input": [
    {
      "role": "user",
      "parts": [
        { "type": "function_result", "name": "get_weather", "call_id": "call_abc123", "result": "72F sunny" }
      ]
    }
  ],
  "previous_interaction_id": "interaction_abc123",
  "stream": true
}

Stateful Chaining

The previous_interaction_id field links turns together on the server side. Each response includes an interaction_id that the client passes as previous_interaction_id in the next request, eliminating the need to resend full conversation history.

Fixture Matching

Fixtures use the same match object as all other providers. The most common matchers for Interactions are userMessage and sequenceIndex.

userMessage Matching

user-message-match.ts ts
const fixture = {
  match: { userMessage: "hello" },
  response: { content: "Hi from Gemini Interactions!" },
};

sequenceIndex for Multi-Turn Chains

Since the Interactions API is stateful, multi-turn conversations are the primary use case. Use sequenceIndex to match by turn position:

sequence-match.ts ts
const fixtures = [
  {
    match: { sequenceIndex: 0 },
    response: {
      toolCalls: [{ name: "get_weather", arguments: { city: "NYC" } }]
    },
  },
  {
    match: { sequenceIndex: 1 },
    response: { content: "The weather in NYC is 72F and sunny." },
  },
];

const instance = await createServer(fixtures);

// Turn 1: model calls get_weather tool
// Turn 2: after tool result, model produces final text

SSE Event Format

Unlike standard Gemini SSE (bare data: chunks), the Interactions API uses typed events. Each SSE line is a data: JSON object containing an event_type field.

Event Types

Event Type Description
interaction.start Opening event with interaction_id and model metadata
content.start Marks beginning of an output part
content.delta Incremental text or function_call chunk
content.stop Marks completion of an output part
interaction.complete Final event with usage metadata and finish reason

Text Streaming Example

text-sse-events.txt text
data: {"event_type":"interaction.start","interaction":{"id":"int_abc123","status":"in_progress"},"event_id":"evt_1"}

data: {"event_type":"content.start","index":0,"content":{"type":"text"},"event_id":"evt_2"}

data: {"event_type":"content.delta","index":0,"delta":{"type":"text","text":"Hi "},"event_id":"evt_3"}

data: {"event_type":"content.delta","index":0,"delta":{"type":"text","text":"there!"},"event_id":"evt_4"}

data: {"event_type":"content.stop","index":0,"event_id":"evt_5"}

data: {"event_type":"interaction.complete","interaction":{"id":"int_abc123","status":"completed","usage":{"total_input_tokens":5,"total_output_tokens":3,"total_tokens":8}},"event_id":"evt_6"}

Tool Call Streaming Example

tool-sse-events.txt text
data: {"event_type":"interaction.start","interaction":{"id":"int_def456","status":"in_progress"},"event_id":"evt_1"}

data: {"event_type":"content.start","index":0,"content":{"type":"function_call"},"event_id":"evt_2"}

data: {"event_type":"content.delta","index":0,"delta":{"type":"function_call","id":"tc_abc123","name":"get_weather","arguments":{"city":"NYC"}},"event_id":"evt_3"}

data: {"event_type":"content.stop","index":0,"event_id":"evt_4"}

data: {"event_type":"interaction.complete","interaction":{"id":"int_def456","status":"requires_action","usage":{"total_input_tokens":8,"total_output_tokens":12,"total_tokens":20}},"event_id":"evt_5"}

Recording

To record real Gemini Interactions API traffic, use the gemini provider settings (same base URL, same API key). The Interactions endpoint shares the Gemini infrastructure:

record-config.ts ts
import { LLMock } from "@copilotkit/aimock";

const mock = new LLMock({
  record: {
    providers: {
      gemini: "https://generativelanguage.googleapis.com",
    },
  },
});
await mock.listen(4010);

Unmatched requests to /v1beta/interactions are proxied to the real API, and the response is recorded as a new fixture.

Integration with TanStack AI

TanStack AI provides a geminiTextInteractions() adapter for the Interactions API. When your client uses this adapter, point it at your aimock instance and the same fixtures will serve the responses. See the TanStack AI docs for adapter configuration.

Known Limitations

The Gemini Interactions API is currently in beta. Both the real API and aimock's support for it are subject to change as the API stabilizes.