> ## AI Agent Context
> Get the complete documentation index at: https://aistudio.google.com/llms.txt
> Use this file to discover all available pages.
> For the full documentation in a single file, fetch: https://aistudio.google.com/llms-full.txt

# Migrating to the Interactions API

This guide helps you migrate from the `generateContent` API to the Interactions
API.

The Interactions API is the standard interface for building with Gemini. It is
optimized for agentic workflows, server-side state management, and complex
multi-modal, multi-turn conversations, while still fully supporting simple
stateless single-turn requests. While `generateContent` remains fully supported,
we recommend the Interactions API for all new development.

{% callout type="note" %}
  **Automate this migration with a coding agent.** If you use
  a coding agent that supports skills (like Gemini CLI or Jules), install the
  [Gemini API skill](/docs/coding-agents#gemini-api-dev) and run:

  `/gemini-api-dev migrate my app to the interactions api`

  The skill applies the API changes described in this guide.

{% /callout %}

### Why migrate?  {% id="why-migrate" %}

The Interactions API provides a more structured and powerful way to build with
Gemini:

* **Server-side history management**: Simplified multi-turn flows via
  `previous_interaction_id`. The server enables state by default (`store=true`),
  but you can opt into stateless behavior by setting `store=false`.
* **Observable execution steps**: Typed steps make it easy to debug complex
  flows and render UI for intermediate events (like thoughts or search widgets).
* **Built for agentic workflows**: Native support for multi-step tool use,
  orchestration, and complex reasoning flows through typed execution steps.
* **Long-running and background tasks**: Supports offloading time-intensive
  operations like Deep Think and Deep Research to background processes using
  `background=true`.

## Basic input/output  {% id="basic-input-output" %}

This section shows how to migrate a simple text generation request.

### Before (generateContent)  {% id="before-generatecontent" %}

The `generateContent` API is stateless and returns the response directly. The
response structure wraps the output in a list of `candidates`, each containing
`content` with a list of `parts` to parse.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  response = client.models.generate_content(
      model="gemini-3.8-flash", contents="Tell me a joke."
  )
  print(response.text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const ai = new GoogleGenAI({});
  
  const response = await ai.models.generateContent({
    model: "gemini-3.8-flash",
    contents: "Tell me a joke.",
  });
  console.log(response.text);
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.types.GenerateContentResponse;
  
  Client client = new Client();
  
  GenerateContentResponse response =
      client.models.generateContent("gemini-2.5-flash-lite", "Tell me a joke.", null);
  System.out.println(response.text());
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      response, err := client.Models.GenerateContent(
          ctx,
          "gemini-2.5-flash-lite",
          genai.Text("Tell me a joke."),
          nil,
      )
      if err != nil {
          log.Fatal(err)
      }
      fmt.Println(response.Text())
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [{
          "parts": [{
              "text": "Tell me a joke."
          }]
      }]{{ '**' }}
  }'
  
  # Response
  {
    {{ '**' }}"candidates": [
      {
        "content": {
          "parts": [
            {
              "text": "Why did the chicken cross the road? To get to the other side!"
            }
          ],
          "role": "model"
        },
        "finishReason": "STOP",
        "index": 0
      }
    ]{{ '**' }},
    "usageMetadata": {
      "promptTokenCount": 4,
      "candidatesTokenCount": 12,
      "totalTokenCount": 16
    }
  }
  ```

  {% /tab %}

{% /tabs %}

The Interactions API returns a stored interaction resource with a `steps`
timeline. While you can inspect the `steps` array manually to find intermediate
events, the Google GenAI SDKs provide convenience properties
directly on the returned `Interaction` object to access the final output.

The most common convenience property is **`.output_text`** (String), which
automatically extracts and joins consecutive `TextContent` blocks at the
end of the model's response. While this works perfectly for simple responses,
it does not include earlier text blocks separated by non-text content (such
as thoughts, images, audio, or tool calls). For complex or interleaved
multimodal responses, you must manually iterate over `steps` instead.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  interaction = client.interactions.create(
      model="gemini-3.8-flash", input="Tell me a joke."
  )
  
  print(interaction.output_text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  let interaction = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: 'Tell me a joke.'
  });
  
  console.log(interaction.output_text);
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.Interaction;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  
  Client client = new Client();
  
  CreateModelInteraction request =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(InteractionsInput.of("Tell me a joke."))
          .build();
  
  Interaction interaction =
      client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();
  
  System.out.println(interaction.outputText().orElse(""));
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model: interactions.Model("gemini-3.8-flash"),
              Input: interactions.NewInteractionsInput("Tell me a joke."),
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
      if res.Interaction.OutputText != nil {
          fmt.Println(*res.Interaction.OutputText)
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"input": "Tell me a joke."{{ '**' }}
  }'
  
  # Response
  {
    "id": "int_123",
    "status": "completed",
    {{ '**' }}"steps": [
      {
        "type": "user_input",
        "status": "done",
        "content": [
          {
            "type": "text",
            "text": "Tell me a joke."
          }
        ]
      },
      {
        "type": "model_output",
        "status": "done",
        "content": [
          {
            "type": "text",
            "text": "Why did the chicken cross the road?"
          }
        ]
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

## Multi-turn conversations  {% id="multi-turn-conversations" %}

The Interactions API stores interactions by default, enabling server-side state
management for multi-turn conversations.

### Before (generateContent)  {% id="before-generatecontent" %}

In `generateContent`, you must manually manage conversation history using the
`contents` array or a client-side chat helper.

{% tabs %}
  {% tab label="Python" %}
  **Using the chat helper (recommended)**

  ```python
  from google import genai
  
  client = genai.Client()
  
  chat = client.chats.create(model="gemini-3.8-flash")
  response1 = chat.send_message("Hi, my name is Phil.")
  print(response1.text)
  
  response2 = chat.send_message("What is my name?")
  print(response2.text)
  ```

  **Manually managing history**

  ```python
  from google import genai
  from google.genai import types
  
  client = genai.Client()
  
  response = client.models.generate_content(
      model="gemini-3.8-flash",
      contents=[
          types.Content(
              role="user", parts=[types.Part.from_text("Hi, my name is Phil.")]
          ),
          types.Content(
              role="model",
              parts=[types.Part.from_text("Hi Phil, how can I help you?")],
          ),
          types.Content(
              role="user", parts=[types.Part.from_text("What is my name?")]
          ),
      ],
  )
  print(response.text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  **Using the chat helper (recommended)**

  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const chat = client.chats.create({ model: 'gemini-3.8-flash' });
  let response = await chat.sendMessage({ message: 'Hi, my name is Phil.' });
  console.log(response.text);
  
  response = await chat.sendMessage({ message: 'What is my name?' });
  console.log(response.text);
  ```

  **Manually managing history**

  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const response = await client.models.generateContent({
      model: 'gemini-3.8-flash',
      contents: [
          { role: 'user', parts: [{ text: 'Hi, my name is Phil.' }] },
          { role: 'model', parts: [{ text: 'Hi Phil, how can I help you?' }] },
          { role: 'user', parts: [{ text: 'What is my name?' }] }
      ]
  });
  console.log(response.text);
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Chat;
  import com.google.genai.Client;
  import com.google.genai.types.Content;
  import com.google.genai.types.GenerateContentResponse;
  import com.google.genai.types.Part;
  import java.util.Arrays;
  
  Client client = new Client();
  
  // Using the chat helper (recommended)
  Chat chat = client.chats.create("gemini-2.5-flash-lite");
  GenerateContentResponse response1 = chat.sendMessage("Hi, my name is Phil.");
  System.out.println(response1.text());
  
  GenerateContentResponse response2 = chat.sendMessage("What is my name?");
  System.out.println(response2.text());
  
  // Manually managing history
  GenerateContentResponse manualResponse =
      client.models.generateContent(
          "gemini-2.5-flash-lite",
          Arrays.asList(
              Content.builder()
                  .role("user")
                  .parts(Arrays.asList(Part.fromText("Hi, my name is Phil.")))
                  .build(),
              Content.builder()
                  .role("model")
                  .parts(Arrays.asList(Part.fromText("Hi Phil, how can I help you?")))
                  .build(),
              Content.builder()
                  .role("user")
                  .parts(Arrays.asList(Part.fromText("What is my name?")))
                  .build()),
          null);
  System.out.println(manualResponse.text());
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      // Using the chat helper (recommended)
      chat, err := client.Chats.Create(ctx, "gemini-2.5-flash-lite", nil, nil)
      if err != nil {
          log.Fatal(err)
      }
      response1, err := chat.SendMessage(ctx, genai.Part{Text: "Hi, my name is Phil."})
      if err != nil {
          log.Fatal(err)
      }
      fmt.Println(response1.Text())
  
      response2, err := chat.SendMessage(ctx, genai.Part{Text: "What is my name?"})
      if err != nil {
          log.Fatal(err)
      }
      fmt.Println(response2.Text())
  
      // Manually managing history
      history := []*genai.Content{
          genai.NewContentFromText("Hi, my name is Phil.", genai.RoleUser),
          genai.NewContentFromText("Hi Phil, how can I help you?", genai.RoleModel),
          genai.NewContentFromText("What is my name?", genai.RoleUser),
      }
      manualResponse, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash-lite", history, nil)
      if err != nil {
          log.Fatal(err)
      }
      fmt.Println(manualResponse.Text())
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request (the second turn requires sending the entire history)
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [
          {"role": "user", "parts": [{"text": "Hi, my name is Phil."}]},
          {"role": "model", "parts": [{"text": "Hi Phil, how can I help you?"}]},
          {"role": "user", "parts": [{"text": "What is my name?"}]}
      ]{{ '**' }}
  }'
  
  # Response
  {
    {{ '**' }}"candidates": [
      {
        "content": {
          "parts": [
            {
              "text": "Your name is Phil."
            }
          ],
          "role": "model"
        },
        "finishReason": "STOP",
        "index": 0
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

### After (Interactions API)  {% id="after-interactions-api" %}

The Interactions API manages state on the server. You continue a conversation by
referencing the `previous_interaction_id`.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  interaction1 = client.interactions.create(
      model="gemini-3.8-flash", input="Hi, my name is Phil."
  )
  print("Response 1:", interaction1.output_text)
  
  interaction2 = client.interactions.create(
      model="gemini-3.8-flash",
      previous_interaction_id=interaction1.id,
      input="What is my name?",
  )
  print("Response 2:", interaction2.output_text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  let interaction = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: 'Hi, my name is Phil.'
  });
  console.log("Response 1:", interaction.output_text);
  
  interaction = await client.interactions.create({
      model: 'gemini-3.8-flash',
      previous_interaction_id: interaction.id,
      input: 'What is my name?'
  });
  console.log("Response 2:", interaction.output_text);
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.Interaction;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  
  Client client = new Client();
  
  CreateModelInteraction req1 =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(InteractionsInput.of("Hi, my name is Phil."))
          .build();
  
  Interaction interaction1 =
      client.interactions.create(CreateInteractionRequestBody.of(req1)).interaction().get();
  System.out.println("Response 1: " + interaction1.outputText().orElse(""));
  
  CreateModelInteraction req2 =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .previousInteractionId(interaction1.id().orElse(""))
          .input(InteractionsInput.of("What is my name?"))
          .build();
  
  Interaction interaction2 =
      client.interactions.create(CreateInteractionRequestBody.of(req2)).interaction().get();
  System.out.println("Response 2: " + interaction2.outputText().orElse(""));
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      res1, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model: interactions.Model("gemini-3.8-flash"),
              Input: interactions.NewInteractionsInput("Hi, my name is Phil."),
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
      if res1.Interaction.OutputText != nil {
          fmt.Println("Response 1:", *res1.Interaction.OutputText)
      }
  
      res2, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model:                 interactions.Model("gemini-3.8-flash"),
              PreviousInteractionID: res1.Interaction.ID,
              Input:                 interactions.NewInteractionsInput("What is my name?"),
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
      if res2.Interaction.OutputText != nil {
          fmt.Println("Response 2:", *res2.Interaction.OutputText)
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # First Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"input": "Hi, my name is Phil."{{ '**' }}
  }'
  
  # Second Request (using ID from first response)
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"previous_interaction_id": "int_123",
      "input": "What is my name?"{{ '**' }}
  }'
  
  # Response to Second Request
  {
    "id": "int_123",
    {{ '**' }}"steps": [
      {
        "type": "user_input",
        "status": "done",
        "content": [{ "type": "text", "text": "Hi, my name is Phil." }]
      },
      {
        "type": "model_output",
        "status": "done",
        "content": [{ "type": "text", "text": "Hello Phil! How can I help you today?" }]
      },
      {
        "type": "user_input",
        "status": "done",
        "content": [{ "type": "text", "text": "What is my name?" }]
      },
      {
        "type": "model_output",
        "status": "done",
        "content": [{ "type": "text", "text": "Your name is Phil." }]
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

## Multimodal inputs  {% id="multimodal-inputs" %}

Both APIs support multimodal inputs (text, images, video, etc.).

### Before (generateContent)  {% id="before-generatecontent" %}

In `generateContent`, you pass a list of `parts` within the `contents` array.
The response returns output in the `parts` of the first candidate.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  from google.genai import types
  
  client = genai.Client()
  
  with open("sample.jpg", "rb") as f:
      image_bytes = f.read()
  
  response = client.models.generate_content(
      model="gemini-3.8-flash",
      contents=[
          types.Part.from_bytes(data=image_bytes, mime_type="image/jpeg"),
          "Describe this image.",
      ],
  )
  print(response.text)
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.types.Content;
  import com.google.genai.types.GenerateContentResponse;
  import com.google.genai.types.Part;
  import java.nio.file.Files;
  import java.nio.file.Paths;
  
  Client client = new Client();
  
  byte[] imageBytes = Files.readAllBytes(Paths.get("sample.jpg"));
  
  GenerateContentResponse response =
      client.models.generateContent(
          "gemini-2.5-flash-lite",
          Content.fromParts(
              Part.fromBytes(imageBytes, "image/jpeg"), Part.fromText("Describe this image.")),
          null);
  System.out.println(response.text());
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
      "os"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      imageBytes, err := os.ReadFile("sample.jpg")
      if err != nil {
          log.Fatal(err)
      }
  
      contents := []*genai.Content{
          genai.NewContentFromParts([]*genai.Part{
              genai.NewPartFromBytes(imageBytes, "image/jpeg"),
              genai.NewPartFromText("Describe this image."),
          }, genai.RoleUser),
      }
  
      response, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash-lite", contents, nil)
      if err != nil {
          log.Fatal(err)
      }
      fmt.Println(response.Text())
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [{
          "parts": [
              {
                  "inlineData": {
                      "mimeType": "image/jpeg",
                      "data": "..."
                  }
              },
              {
                  "text": "Describe this image."
              }
          ]
      }]{{ '**' }}
  }'
  
  # Response
  {
    {{ '**' }}"candidates": [
      {
        "content": {
          "parts": [
            {
              "text": "This is a picture of a beautiful sunset."
            }
          ],
          "role": "model"
        }
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

### After (Interactions API)  {% id="after-interactions-api" %}

In the Interactions API, you pass an array to the `input` field. You retrieve
output content by finding the `model_output` step in the timeline.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  with open("sample.jpg", "rb") as f:
      image_bytes = f.read()
  
  interaction = client.interactions.create(
      model="gemini-3.8-flash",
      input=[
          {
              "type": "image",
              "mime_type": "image/jpeg",
              "data": image_bytes,
          },
          {"type": "text", "text": "Describe this image."},
      ],
  )
  print(interaction.output_text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  import * as fs from 'fs';
  
  const client = new GoogleGenAI({});
  
  const imageBytes = fs.readFileSync('sample.jpg').toString('base64');
  
  const interaction = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: [
          {
              type: 'image',
              mime_type: 'image/jpeg',
              data: imageBytes
          },
          {
              type: 'text',
              text: 'Describe this image.'
          }
      ]
  });
  console.log(interaction.output_text);
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.ImageContent;
  import com.google.genai.gaos.models.interactions.ImageContentMimeType;
  import com.google.genai.gaos.models.interactions.Interaction;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.interactions.TextContent;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  import java.nio.file.Files;
  import java.nio.file.Paths;
  import java.util.Arrays;
  import java.util.Base64;
  
  Client client = new Client();
  
  byte[] imageBytes = Files.readAllBytes(Paths.get("sample.jpg"));
  String base64ImageData = Base64.getEncoder().encodeToString(imageBytes);
  
  CreateModelInteraction request =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(
              InteractionsInput.ofContent(
                  Arrays.asList(
                      ImageContent.builder()
                          .mimeType(ImageContentMimeType.IMAGE_JPEG)
                          .data(base64ImageData)
                          .build(),
                      TextContent.builder().text("Describe this image.").build())))
          .build();
  
  Interaction interaction =
      client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();
  System.out.println(interaction.outputText().orElse(""));
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "encoding/base64"
      "fmt"
      "log"
      "os"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      imageBytes, err := os.ReadFile("sample.jpg")
      if err != nil {
          log.Fatal(err)
      }
      base64ImageData := base64.StdEncoding.EncodeToString(imageBytes)
  
      res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model: interactions.Model("gemini-3.8-flash"),
              Input: interactions.NewInteractionsInput([]interactions.Content{
                  interactions.NewContent(interactions.ImageContent{
                      MimeType: interactions.ImageContentMimeType("image/jpeg").ToPointer(),
                      Data:     genai.Ptr(base64ImageData),
                  }),
                  interactions.NewContent(interactions.TextContent{
                      Text: "Describe this image.",
                  }),
              }),
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
      if res.Interaction.OutputText != nil {
          fmt.Println(*res.Interaction.OutputText)
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"input": [
          {
              "type": "image",
              "mime_type": "image/jpeg",
              "data": "..."
          },
          {
              "type": "text",
              "text": "Describe this image."
          }
      ]{{ '**' }}
  }'
  
  # Response
  {
    "id": "int_multimodal",
    {{ '**' }}"steps": [
      {
        "type": "user_input",
        "status": "done",
        "content": [
          {
            "type": "image",
            "mime_type": "image/jpeg",
            "data": "..."
          },
          {
            "type": "text",
            "text": "Describe this image."
          }
        ]
      },
      {
        "type": "model_output",
        "status": "done",
        "content": [
          {
            "type": "text",
            "text": "This is a picture of a beautiful sunset over the mountains."
          }
        ]
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

## Structured output  {% id="structured-output" %}

To make the model return JSON matching a specific schema, configure the response
format.

### Before (generateContent)  {% id="before-generatecontent" %}

In `generateContent`, you configure output format using the `response_mime_type`
and `response_schema` fields nested inside the `config` (or `generationConfig`)
object.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  from google.genai import types
  from pydantic import BaseModel
  
  client = genai.Client()
  
  class Recipe(BaseModel):
      recipe_name: str
      ingredients: list[str]
  
  response = client.models.generate_content(
      model="gemini-2.5-flash-lite",
      contents="Give me a recipe for chocolate chip cookies.",
      config=types.GenerateContentConfig(
          response_mime_type="application/json",
          response_schema=Recipe,
      ),
  )
  print(response.text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI, Type } from '@google/genai';
  
  const ai = new GoogleGenAI({});
  
  const response = await ai.models.generateContent({
      model: 'gemini-2.5-flash-lite',
      contents: 'Give me a recipe for chocolate chip cookies.',
      config: {
          responseMimeType: 'application/json',
          responseSchema: {
              type: Type.OBJECT,
              properties: {
                  recipe_name: { type: Type.STRING },
                  ingredients: {
                      type: Type.ARRAY,
                      items: { type: Type.STRING },
                  },
              },
              required: ['recipe_name', 'ingredients'],
          },
      },
  });
  console.log(response.text);
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.types.GenerateContentConfig;
  import com.google.genai.types.GenerateContentResponse;
  import com.google.genai.types.Schema;
  import com.google.genai.types.Type;
  import java.util.Arrays;
  import java.util.Map;
  
  Client client = new Client();
  
  Schema recipeSchema =
      Schema.builder()
          .type(Type.Known.OBJECT)
          .properties(
              Map.of(
                  "recipe_name", Schema.builder().type(Type.Known.STRING).build(),
                  "ingredients",
                      Schema.builder()
                          .type(Type.Known.ARRAY)
                          .items(Schema.builder().type(Type.Known.STRING).build())
                          .build()))
          .required(Arrays.asList("recipe_name", "ingredients"))
          .build();
  
  GenerateContentResponse response =
      client.models.generateContent(
          "gemini-2.5-flash-lite",
          "Give me a recipe for chocolate chip cookies.",
          GenerateContentConfig.builder()
              .responseMimeType("application/json")
              .responseSchema(recipeSchema)
              .build());
  System.out.println(response.text());
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      recipeSchema := &genai.Schema{
          Type: genai.TypeObject,
          Properties: map[string]*genai.Schema{
              "recipe_name": {Type: genai.TypeString},
              "ingredients": {
                  Type:  genai.TypeArray,
                  Items: &genai.Schema{Type: genai.TypeString},
              },
          },
          Required: []string{"recipe_name", "ingredients"},
      }
  
      response, err := client.Models.GenerateContent(
          ctx,
          "gemini-2.5-flash-lite",
          genai.Text("Give me a recipe for chocolate chip cookies."),
          &genai.GenerateContentConfig{
              ResponseMIMEType: "application/json",
              ResponseSchema:   recipeSchema,
          },
      )
      if err != nil {
          log.Fatal(err)
      }
      fmt.Println(response.Text())
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:generateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [{
          "parts": [{
              "text": "Give me a recipe for chocolate chip cookies."
          }]
      }],
      "generationConfig": {
          "responseMimeType": "application/json",
          "responseSchema": {
              "type": "OBJECT",
              "properties": {
                  "recipe_name": { "type": "STRING" },
                  "ingredients": {
                      "type": "ARRAY",
                      "items": { "type": "STRING" }
                  }
              },
              "required": ["recipe_name", "ingredients"]
          }
      }{{ '**' }}
  }'
  
  # Response
  {
    {{ '**' }}"candidates": [
      {
        "content": {
          "parts": [
            {
              "text": "{\n \"recipe_name\": \"Chocolate Chip Cookies\",\n \"ingredients\": [\n \"1 cup butter\",\n \"1 cup sugar\",\n \"2 cups flour\",\n \"1 cup chocolate chips\"\n ]\n}"
            }
          ],
          "role": "model"
        }
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

### After (Interactions API)  {% id="after-interactions-api" %}

In the Interactions API, output format controls move to a
top-level `response_format` field. Pass a single format object, or an array of
format objects when you request more than one output type.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  from pydantic import BaseModel
  
  client = genai.Client()
  
  class Recipe(BaseModel):
      recipe_name: str
      ingredients: list[str]
  
  interaction = client.interactions.create(
      model="gemini-3.8-flash",
      input="Give me a recipe for chocolate chip cookies.",
      response_format={
          "type": "text",
          "mime_type": "application/json",
          "schema": Recipe.model_json_schema(),
      },
  )
  
  print(interaction.output_text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const interaction = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: 'Give me a recipe for chocolate chip cookies.',
      response_format: {
          type: 'text',
          mime_type: 'application/json',
          schema: {
              type: 'object',
              properties: {
                  recipe_name: { type: 'string' },
                  ingredients: {
                      type: 'array',
                      items: { type: 'string' }
                  }
              },
              required: ['recipe_name', 'ingredients']
          }
      }
  });
  console.log(interaction.output_text);
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.CreateModelInteractionResponseFormat;
  import com.google.genai.gaos.models.interactions.Interaction;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.interactions.ResponseFormat;
  import com.google.genai.gaos.models.interactions.TextResponseFormat;
  import com.google.genai.gaos.models.interactions.TextResponseFormatMimeType;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Map;
  
  Client client = new Client();
  
  Map<String, Object> properties = new HashMap<>();
  properties.put("recipe_name", Map.of("type", "string"));
  properties.put("ingredients", Map.of("type", "array", "items", Map.of("type", "string")));
  
  Map<String, Object> schema = new HashMap<>();
  schema.put("type", "object");
  schema.put("properties", properties);
  schema.put("required", Arrays.asList("recipe_name", "ingredients"));
  
  CreateModelInteraction request =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(InteractionsInput.of("Give me a recipe for chocolate chip cookies."))
          .responseFormat(
              CreateModelInteractionResponseFormat.of(
                  ResponseFormat.of(
                      TextResponseFormat.builder()
                          .mimeType(TextResponseFormatMimeType.APPLICATION_JSON)
                          .schema(schema)
                          .build())))
          .build();
  
  Interaction interaction =
      client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();
  System.out.println(interaction.outputText().orElse(""));
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      schema := map[string]any{
          "type": "object",
          "properties": map[string]any{
              "recipe_name": map[string]any{"type": "string"},
              "ingredients": map[string]any{
                  "type":  "array",
                  "items": map[string]any{"type": "string"},
              },
          },
          "required": []string{"recipe_name", "ingredients"},
      }
  
      res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model: interactions.Model("gemini-3.8-flash"),
              Input: interactions.NewInteractionsInput("Give me a recipe for chocolate chip cookies."),
              ResponseFormat: genai.Ptr(interactions.NewCreateModelInteractionResponseFormat(
                  interactions.NewResponseFormat(interactions.TextResponseFormat{
                      MimeType: interactions.TextResponseFormatMimeType("application/json").ToPointer(),
                      Schema:   schema,
                  }),
              )),
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
      if res.Interaction.OutputText != nil {
          fmt.Println(*res.Interaction.OutputText)
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"input": "Give me a recipe for chocolate chip cookies.",
      "response_format": {
          "type": "text",
          "mime_type": "application/json",
          "schema": {
              "type": "object",
              "properties": {
                  "recipe_name": { "type": "string" },
                  "ingredients": {
                      "type": "array",
                      "items": { "type": "string" }
                  }
              },
              "required": ["recipe_name", "ingredients"]
          }
      }{{ '**' }}
  }'
  
  # Response
  {
    "id": "int_structured",
    {{ '**' }}"steps": [
      {
        "type": "user_input",
        "status": "done",
        "content": [{ "type": "text", "text": "Give me a recipe for chocolate chip cookies." }]
      },
      {
        "type": "model_output",
        "status": "done",
        "content": [
          {
            "type": "text",
            "text": "{\n \"recipe_name\": \"Chocolate Chip Cookies\",\n \"ingredients\": [\n \"1 cup butter\",\n \"1 cup sugar\",\n \"2 cups flour\",\n \"1 cup chocolate chips\"\n ]\n}"
          }
        ]
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

## Multimodal generation  {% id="multimodal-generation" %}

When generating content in modalities beyond text (such as images or audio), the
primary difference is how the response structures the generated media.

### Before (generateContent)  {% id="before-generatecontent" %}

In `generateContent`, the response returns generated media directly in the
`parts` of the candidate, typically as base64 data in `inlineData`.

```json
# Response structure concept
{
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "Here is your generated image:"
          },
          {
            "inlineData": {
              "mimeType": "image/jpeg",
              "data": "...base64..."
            }
          }
        ]
      }
    }
  ]
}
```

### After (Interactions API)  {% id="after-interactions-api" %}

In the Interactions API, generated media appears as distinct items within the
`content` array of a `model_output` step in the timeline, maintaining the
chronological flow of the interaction.

```json
# Response structure concept
{
  "id": "int_123",
  "steps": [
    {
      "type": "model_output",
      "status": "done",
      "content": [
        {
          "type": "text",
          "text": "Here is your generated image:"
        },
        {
          "type": "image",
          "mime_type": "image/jpeg",
          "data": "...base64..." // Or a reference URL in future
        }
      ]
    }
  ]
}
```

This keeps the response parsing consistent with how inputs and text outputs are
handled-everything is a step in the timeline.

## Server-side tools  {% id="server-side-tools" %}

Gemini supports built-in server-side tools like Google Search grounding. The
primary difference is how the response represents tool execution.

### Before (generateContent)  {% id="before-generatecontent" %}

In `generateContent`, server-side tools are largely opaque. You enable the tool
and get a final answer with a separate `groundingMetadata` object. Crucially,
citations are not inline; `groundingSupports` use character indices to map text
segments back to web sources in `groundingChunks`.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  from google.genai import types
  
  client = genai.Client()
  
  response = client.models.generate_content(
      model="gemini-3.8-flash",
      contents="Who won Euro 2024?",
      config=types.GenerateContentConfig(
          tools=[{"google_search": {}}]
      ),
  )
  
  metadata = response.candidates[0].grounding_metadata
  if metadata.search_entry_point:
      print(f"Search Entry Point: {metadata.search_entry_point.rendered_content}")
  
  for support in metadata.grounding_supports:
      print(f"Citation: {support.segment.text}")
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const response = await client.models.generateContent({
      model: 'gemini-3.8-flash',
      contents: 'Who won Euro 2024?',
      config: {
          tools: [{ googleSearch: {} }]
      }
  });
  
  const metadata = response.candidates?.[0]?.groundingMetadata;
  if (metadata?.searchEntryPoint) {
      console.log(`Search Entry Point: ${metadata.searchEntryPoint.renderedContent}`);
  }
  if (metadata?.groundingSupports) {
      for (const support of metadata.groundingSupports) {
          if (support?.segment?.text) {
              console.log(`Citation: ${support.segment.text}`);
          }
      }
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.types.Candidate;
  import com.google.genai.types.GenerateContentConfig;
  import com.google.genai.types.GenerateContentResponse;
  import com.google.genai.types.GoogleSearch;
  import com.google.genai.types.GroundingMetadata;
  import com.google.genai.types.GroundingSupport;
  import com.google.genai.types.Tool;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.Optional;
  
  Client client = new Client();
  
  GenerateContentResponse response =
      client.models.generateContent(
          "gemini-2.5-flash-lite",
          "Who won Euro 2024?",
          GenerateContentConfig.builder()
              .tools(
                  Arrays.asList(
                      Tool.builder().googleSearch(GoogleSearch.builder().build()).build()))
              .build());
  
  List<Candidate> candidates = response.candidates().orElse(Collections.emptyList());
  if (!candidates.isEmpty() && candidates.get(0).groundingMetadata().isPresent()) {
    GroundingMetadata metadata = candidates.get(0).groundingMetadata().get();
    if (metadata.searchEntryPoint().isPresent()) {
      System.out.println(
          "Search Entry Point: " + metadata.searchEntryPoint().get().renderedContent().orElse(""));
    }
    for (GroundingSupport support : metadata.groundingSupports().orElse(Collections.emptyList())) {
      Optional<String> segmentText = support.segment().flatMap(s -> s.text());
      segmentText.ifPresent(text -> System.out.println("Citation: " + text));
    }
  }
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      response, err := client.Models.GenerateContent(
          ctx,
          "gemini-2.5-flash-lite",
          genai.Text("Who won Euro 2024?"),
          &genai.GenerateContentConfig{
              Tools: []*genai.Tool{
                  {GoogleSearch: &genai.GoogleSearch{}},
              },
          },
      )
      if err != nil {
          log.Fatal(err)
      }
  
      if len(response.Candidates) > 0 && response.Candidates[0].GroundingMetadata != nil {
          metadata := response.Candidates[0].GroundingMetadata
          if metadata.SearchEntryPoint != nil {
              fmt.Println("Search Entry Point:", metadata.SearchEntryPoint.RenderedContent)
          }
          for _, support := range metadata.GroundingSupports {
              if support.Segment != nil {
                  fmt.Println("Citation:", support.Segment.Text)
              }
          }
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [{
          "parts": [{
              "text": "Who won Euro 2024?"
          }]
      }],
      "tools": [{
          "googleSearchRetrieval": {}
      }]{{ '**' }}
  }'
  
  # Response
  {
    {{ '**' }}"candidates": [
      {
        "content": {
          "parts": [
            {
              "text": "Spain won Euro 2024, defeating England 2-1 in the final. This victory marks Spain's record fourth European Championship title."
            }
          ],
          "role": "model"
        },
        "groundingMetadata": {
          "webSearchQueries": [
            "UEFA Euro 2024 winner",
            "who won euro 2024"
          ],
          "searchEntryPoint": {
            "renderedContent": " HTML and CSS for the search widget "
          },
          "groundingChunks": [
            {"web": {"uri": "https://vertexaisearch.cloud.google.com.....", "title": "aljazeera.com"}},
            {"web": {"uri": "https://vertexaisearch.cloud.google.com.....", "title": "uefa.com"}}
          ],
          "groundingSupports": [
            {
              "segment": {"startIndex": 0, "endIndex": 85, "text": "Spain won Euro 2024, defeatin..."},
              "groundingChunkIndices": [0]
            },
            {
              "segment": {"startIndex": 86, "endIndex": 210, "text": "This victory marks Spain's..."},
              "groundingChunkIndices": [0, 1]
            }
          ]
        }
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

### After (Interactions API)  {% id="after-interactions-api" %}

In the Interactions API, server-side tools provide full timeline transparency.
The API records the call and result as distinct execution `steps`
(`google_search_call` and `google_search_result`), exposing exactly what data
the model retrieved.

Furthermore, the API returns citations **inline**. Instead of mapping indices
from a separate metadata object, the text item within the `model_output` step
contains its own `annotations` array linking directly to the source.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  interaction = client.interactions.create(
      model="gemini-3.8-flash",
      input="Who won Euro 2024?",
      tools=[{"type": "google_search"}],
  )
  
  for step in interaction.steps:
      if step.type == "google_search_result":
          print(f"Search Suggestions: {step.search_suggestions}")
      elif step.type == "model_output":
          print(f"Answer: {step.content[0].text}")
          if step.content[0].annotations:
              for anno in step.content[0].annotations:
                  print(f"Citation: {anno.title} ({anno.uri})")
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const interaction = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: 'Who won Euro 2024?',
      tools: [{ type: 'google_search' }]
  });
  
  for (const step of interaction.steps || []) {
      if (step.type === 'google_search_result') {
          if (step.result?.[0]?.search_suggestions) {
              console.log(`Search Suggestions: ${step.result[0].search_suggestions}`);
          }
      } else if (step.type === 'model_output') {
          for (const part of step.content || []) {
              if (part.type === 'text') {
                  console.log(`Answer: ${part.text}`);
                  if (part.annotations) {
                      for (const anno of part.annotations) {
                          if (anno.type === 'url_citation') {
                              console.log(`Citation: ${anno.title} (${anno.url})`);
                          }
                      }
                  }
              }
          }
      }
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.Annotation;
  import com.google.genai.gaos.models.interactions.Content;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.GoogleSearch;
  import com.google.genai.gaos.models.interactions.GoogleSearchResult;
  import com.google.genai.gaos.models.interactions.GoogleSearchResultStep;
  import com.google.genai.gaos.models.interactions.Interaction;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.interactions.ModelOutputStep;
  import com.google.genai.gaos.models.interactions.Step;
  import com.google.genai.gaos.models.interactions.TextContent;
  import com.google.genai.gaos.models.interactions.URLCitation;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  import java.util.Arrays;
  import java.util.Collections;
  
  Client client = new Client();
  
  CreateModelInteraction request =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(InteractionsInput.of("Who won Euro 2024?"))
          .tools(Arrays.asList(GoogleSearch.builder().build()))
          .build();
  
  Interaction interaction =
      client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();
  
  for (Step step : interaction.steps().orElse(Collections.emptyList())) {
    if (step instanceof GoogleSearchResultStep) {
      GoogleSearchResultStep searchStep = (GoogleSearchResultStep) step;
      for (GoogleSearchResult res : searchStep.result().orElse(Collections.emptyList())) {
        System.out.println("Search Suggestions: " + res.searchSuggestions().orElse(""));
      }
    } else if (step instanceof ModelOutputStep) {
      ModelOutputStep modelOutput = (ModelOutputStep) step;
      for (Content contentBlock : modelOutput.content().orElse(Collections.emptyList())) {
        if (contentBlock instanceof TextContent) {
          TextContent textContent = (TextContent) contentBlock;
          System.out.println("Answer: " + textContent.text().orElse(""));
          for (Annotation anno : textContent.annotations().orElse(Collections.emptyList())) {
            if (anno instanceof URLCitation) {
              URLCitation cit = (URLCitation) anno;
              System.out.println(
                  "Citation: " + cit.title().orElse("") + " (" + cit.url().orElse("") + ")");
            }
          }
        }
      }
    }
  }
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model: interactions.Model("gemini-3.8-flash"),
              Input: interactions.NewInteractionsInput("Who won Euro 2024?"),
              Tools: []interactions.Tool{
                  interactions.NewTool(interactions.GoogleSearch{}),
              },
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
  
      for _, step := range res.Interaction.Steps {
          if searchStep := step.GoogleSearchResultStep; searchStep != nil {
              for _, r := range searchStep.Result {
                  if r.SearchSuggestions != nil {
                      fmt.Println("Search Suggestions:", *r.SearchSuggestions)
                  }
              }
          } else if modelOutput := step.ModelOutputStep; modelOutput != nil {
              for _, contentBlock := range modelOutput.Content {
                  if textContent := contentBlock.TextContent; textContent != nil {
                      fmt.Println("Answer:", textContent.Text)
                      for _, anno := range textContent.Annotations {
                          if cit := anno.URLCitation; cit != nil {
                              fmt.Printf("Citation: %s (%s)\n", cit.GetTitle(), cit.GetURL())
                          }
                      }
                  }
              }
          }
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"input": "Who won Euro 2024?",
      "tools": [{{ '{' }}"type": "google_search"{{ '}' }}]{{ '**' }}
  }'
  
  # Response (showing grounding)
  {
    "id": "int_grounded",
    {{ '**' }}"steps": [
      {
        "type": "user_input",
        "status": "done",
        "content": [{ "type": "text", "text": "Who won Euro 2024?" }]
      },
      {
        "type": "google_search_call",
        "status": "done",
        "content": [{ "type": "text", "text": "UEFA Euro 2024 winner" }]
      },
      {
        "type": "google_search_result",
        "status": "done",
        "content": [
          {
            "type": "text",
            "text": "Spain won Euro 2024..."
          }
        ]
      },
      {
        "type": "model_output",
        "status": "done",
        "content": [
          {
            "type": "text",
            "text": "Spain won Euro 2024, defeating England 2-1.",
            "annotations": [
              {
                "start_index": 0,
                "end_index": 42,
                "uri": "https://vertexaisearch...",
                "title": "aljazeera.com"
              }
            ]
          }
        ]
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

## Function calling  {% id="function-calling" %}

The structure of function calls and results has also changed to fit the Steps
schema.

### Before (generateContent)  {% id="before-generatecontent" %}

In `generateContent`, the response returns function calls within the candidates.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  from google.genai import types
  
  client = genai.Client()
  
  response = client.models.generate_content(
      model="gemini-3.8-flash",
      contents="What's the weather in Boston?",
      config=types.GenerateContentConfig(tools=[weather_tool]),
  )
  
  function_call = response.candidates[0].content.parts[0].function_call
  print(f"Requested tool: {function_call.name}")
  
  result = "52°F and rain"
  
  response = client.models.generate_content(
      model="gemini-3.8-flash",
      contents=[
          types.Content(
              role="user",
              parts=[
                  types.Part.from_text(text="What's the weather in Boston?")
              ],
          ),
          response.candidates[0].content,
          types.Content(
              role="user",
              parts=[
                  types.Part.from_function_response(
                      name=function_call.name,
                      response={"result": result},
                  )
              ],
          ),
      ],
      config=types.GenerateContentConfig(tools=[weather_tool]),
  )
  print(response.text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  let response = await client.models.generateContent({
      model: 'gemini-3.8-flash',
      contents: "What's the weather in Boston?",
      config: { tools: [weatherTool] }
  });
  
  const candidateContent = response.candidates?.[0]?.content;
  const functionCall = candidateContent?.parts?.[0]?.functionCall;
  if (functionCall) {
      console.log(`Requested tool: ${functionCall.name}`);
  }
  
  const result = "52°F and rain";
  
  if (candidateContent && functionCall) {
      response = await client.models.generateContent({
          model: 'gemini-3.8-flash',
          contents: [
              { role: 'user', parts: [{ text: "What's the weather in Boston?" }] },
              candidateContent,
              {
                  role: 'user',
                  parts: [{
                      functionResponse: {
                          name: functionCall.name,
                          response: { result: result }
                      }
                  }]
              }
          ],
          config: { tools: [weatherTool] }
      });
      console.log(response.text);
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.types.Content;
  import com.google.genai.types.FunctionCall;
  import com.google.genai.types.FunctionDeclaration;
  import com.google.genai.types.GenerateContentConfig;
  import com.google.genai.types.GenerateContentResponse;
  import com.google.genai.types.Part;
  import com.google.genai.types.Schema;
  import com.google.genai.types.Tool;
  import com.google.genai.types.Type;
  import java.util.Arrays;
  import java.util.Map;
  
  Client client = new Client();
  
  FunctionDeclaration weatherFunc =
      FunctionDeclaration.builder()
          .name("get_weather")
          .description("Gets weather")
          .parameters(
              Schema.builder()
                  .type(Type.Known.OBJECT)
                  .properties(Map.of("location", Schema.builder().type(Type.Known.STRING).build()))
                  .build())
          .build();
  
  Tool weatherTool = Tool.builder().functionDeclarations(Arrays.asList(weatherFunc)).build();
  
  GenerateContentResponse response =
      client.models.generateContent(
          "gemini-2.5-flash-lite",
          "What's the weather in Boston?",
          GenerateContentConfig.builder().tools(Arrays.asList(weatherTool)).build());
  
  FunctionCall functionCall = response.functionCalls().get(0);
  System.out.println("Requested tool: " + functionCall.name().orElse(""));
  
  String result = "52°F and rain";
  
  GenerateContentResponse finalResponse =
      client.models.generateContent(
          "gemini-2.5-flash-lite",
          Arrays.asList(
              Content.builder()
                  .role("user")
                  .parts(Arrays.asList(Part.fromText("What's the weather in Boston?")))
                  .build(),
              response.candidates().get().get(0).content().get(),
              Content.builder()
                  .role("user")
                  .parts(
                      Arrays.asList(
                          Part.fromFunctionResponse(
                              functionCall.name().orElse(""), Map.of("result", result))))
                  .build()),
          GenerateContentConfig.builder().tools(Arrays.asList(weatherTool)).build());
  System.out.println(finalResponse.text());
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      weatherTool := &genai.Tool{
          FunctionDeclarations: []*genai.FunctionDeclaration{
              {
                  Name:        "get_weather",
                  Description: "Gets weather",
                  Parameters: &genai.Schema{
                      Type: genai.TypeObject,
                      Properties: map[string]*genai.Schema{
                          "location": {Type: genai.TypeString},
                      },
                  },
              },
          },
      }
  
      config := &genai.GenerateContentConfig{
          Tools: []*genai.Tool{weatherTool},
      }
  
      response, err := client.Models.GenerateContent(
          ctx,
          "gemini-2.5-flash-lite",
          genai.Text("What's the weather in Boston?"),
          config,
      )
      if err != nil {
          log.Fatal(err)
      }
  
      calls := response.FunctionCalls()
      if len(calls) > 0 {
          functionCall := calls[0]
          fmt.Println("Requested tool:", functionCall.Name)
  
          result := "52°F and rain"
          history := []*genai.Content{
              genai.NewContentFromText("What's the weather in Boston?", genai.RoleUser),
              response.Candidates[0].Content,
              genai.NewContentFromParts([]*genai.Part{
                  genai.NewPartFromFunctionResponse(functionCall.Name, map[string]any{"result": result}),
              }, genai.RoleUser),
          }
  
          finalResponse, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash-lite", history, config)
          if err != nil {
              log.Fatal(err)
          }
          fmt.Println(finalResponse.Text())
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [{
          "parts": [{
              "text": "What is the weather like in Boston, MA?"
          }]
      }],
      "tools": [{
          "functionDeclarations": [{
              "name": "get_weather",
              "description": "Get the current weather",
              "parameters": {
                  "type": "OBJECT",
                  "properties": {
                      "location": {"type": "STRING"}
                  },
                  "required": ["location"]
              }
          }]
      }]{{ '**' }}
  }'
  
  # Response
  {
    {{ '**' }}"candidates": [
      {
        "content": {
          "parts": [
            {
              "functionCall": {
                "name": "get_weather",
                "args": { "location": "Boston, MA" }
              }
            }
          ],
          "role": "model"
        },
        "finishReason": "STOP",
        "index": 0
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

### After (Interactions API)  {% id="after-interactions-api" %}

Tool calls and results are now distinct steps in the timeline.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  weather_tool = {
      "type": "function",
      "name": "get_weather",
      "description": "Gets weather",
      "parameters": {
          "type": "object",
          "properties": {
              "location": {"type": "string"}
          },
      },
  }
  
  interaction = client.interactions.create(
      model="gemini-3.8-flash",
      input="What's the weather in Boston?",
      tools=[weather_tool],
  )
  
  for step in interaction.steps:
      if step.type == "function_call":
          print(f"Executing {step.name} for {step.arguments}")
  
          result = "52°F and rain"
  
          interaction = client.interactions.create(
              model="gemini-3.8-flash",
              previous_interaction_id=interaction.id,
              input=[
                  {
                      "type": "function_result",
                      "call_id": step.id,
                      "name": step.name,
                      "result": [{"type": "text", "text": result}],
                  }
              ],
          )
          print(next_interaction.output_text)
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const interaction = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: "What's the weather in Boston?",
      tools: [
          {
              type: "function",
              name: "get_weather",
              description: "Get weather for a location",
              parameters: {
                  type: "object",
                  properties: {
                      location: { type: "string" }
                  },
                  required: ["location"]
              }
          }
      ]
  });
  
  for (const step of interaction.steps || []) {
      if (step.type === 'function_call') {
          console.log(`Executing ${step.name} for ${JSON.stringify(step.arguments)}`);
  
          const result = "52°F and rain";
  
          const nextInteraction = await client.interactions.create({
              model: 'gemini-3.8-flash',
              previous_interaction_id: interaction.id,
              input: [
                  {
                      type: 'function_result',
                      call_id: step.id,
                      name: step.name,
                      result: [{ type: 'text', text: result }]
                  }
              ]
          });
  
          console.log(nextInteraction.output_text);
      }
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.Function;
  import com.google.genai.gaos.models.interactions.FunctionCallStep;
  import com.google.genai.gaos.models.interactions.FunctionResultStep;
  import com.google.genai.gaos.models.interactions.FunctionResultStepResultUnion;
  import com.google.genai.gaos.models.interactions.Interaction;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.interactions.Step;
  import com.google.genai.gaos.models.interactions.TextContent;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  
  Client client = new Client();
  
  Map<String, Object> properties = new HashMap<>();
  properties.put("location", Map.of("type", "string"));
  
  Map<String, Object> parameters = new HashMap<>();
  parameters.put("type", "object");
  parameters.put("properties", properties);
  parameters.put("required", Arrays.asList("location"));
  
  Function weatherTool =
      Function.builder()
          .name("get_weather")
          .description("Gets weather")
          .parameters(parameters)
          .build();
  
  CreateModelInteraction request =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(InteractionsInput.of("What's the weather in Boston?"))
          .tools(Arrays.asList(weatherTool))
          .build();
  
  Interaction interaction =
      client.interactions.create(CreateInteractionRequestBody.of(request)).interaction().get();
  
  for (Step step : interaction.steps().orElse(Collections.emptyList())) {
    if (step instanceof FunctionCallStep) {
      FunctionCallStep fcStep = (FunctionCallStep) step;
      System.out.println(
          "Executing "
              + fcStep.name().orElse("")
              + " for "
              + fcStep.arguments().orElse(Collections.emptyMap()));
  
      String result = "52°F and rain";
  
      FunctionResultStep funcResult =
          FunctionResultStep.builder()
              .callId(fcStep.id().orElse(""))
              .name(fcStep.name().orElse(""))
              .result(
                  FunctionResultStepResultUnion.of(
                      Arrays.asList(TextContent.builder().text(result).build())))
              .build();
  
      CreateModelInteraction nextRequest =
          CreateModelInteraction.builder()
              .model(Model.of("gemini-3.8-flash"))
              .previousInteractionId(interaction.id().orElse(""))
              .input(InteractionsInput.ofStep(Arrays.asList(funcResult)))
              .build();
  
      Interaction nextInteraction =
          client.interactions.create(CreateInteractionRequestBody.of(nextRequest)).interaction().get();
      System.out.println(nextInteraction.outputText().orElse(""));
    }
  }
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      weatherTool := interactions.NewTool(interactions.Function{
          Name:        genai.Ptr("get_weather"),
          Description: genai.Ptr("Gets weather"),
          Parameters: map[string]any{
              "type": "object",
              "properties": map[string]any{
                  "location": map[string]any{"type": "string"},
              },
              "required": []string{"location"},
          },
      })
  
      res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model: interactions.Model("gemini-3.8-flash"),
              Input: interactions.NewInteractionsInput("What's the weather in Boston?"),
              Tools: []interactions.Tool{weatherTool},
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
  
      for _, step := range res.Interaction.Steps {
          if fcStep := step.FunctionCallStep; fcStep != nil {
              fmt.Printf("Executing %s for %v\n", fcStep.Name, fcStep.Arguments)
  
              result := "52°F and rain"
              funcResult := interactions.NewStep(interactions.FunctionResultStep{
                  CallID: fcStep.ID,
                  Name:   genai.Ptr(fcStep.Name),
                  Result: interactions.NewFunctionResultStepResultUnion([]interactions.FunctionResultSubcontent{
                      interactions.NewFunctionResultSubcontent(interactions.TextContent{Text: result}),
                  }),
              })
  
              nextRes, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
                  Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
                      Model:                 interactions.Model("gemini-3.8-flash"),
                      PreviousInteractionID: res.Interaction.ID,
                      Input:                 interactions.NewInteractionsInput([]interactions.Step{funcResult}),
                  }),
              })
              if err != nil {
                  log.Fatal(err)
              }
              if nextRes.Interaction.OutputText != nil {
                  fmt.Println(*nextRes.Interaction.OutputText)
              }
          }
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Initial Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"input": "What's the weather in Boston?",
      "tools": [{
          "type": "function",
          "name": "get_weather",
          "description": "Get weather for a location",
          "parameters": {
              "type": "object",
              "properties": {
                  "location": { "type": "string" }
              },
              "required": ["location"]
          }
      }]{{ '**' }}
  }'
  
  # Response (requires action)
  {
    "id": "int_001",
    "status": "requires_action",
    {{ '**' }}"steps": [
      {
        "type": "user_input",
        "status": "done",
        "content": [
          { "type": "text", "text": "What's the weather in Boston?" }
        ]
      },
      {
        "type": "function_call",
        "status": "waiting",
        "id": "fc_1",
        "name": "get_weather",
        "arguments": { "location": "Boston, MA" }
      }
    ]{{ '**' }}
  }
  
  # Submit Tool Result Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"previous_interaction_id": "int_001",
      "input": {
          "type": "function_result",
          "call_id": "fc_1",
          "name": "get_weather",
          "result": [
              { "type": "text", "text": "52°F with rain" }
          ]
      }{{ '**' }}
  }'
  
  # Final Response
  {
    "id": "int_002",
    "status": "completed",
    {{ '**' }}"steps": [
      {
        "type": "function_result",
        "call_id": "fc_1",
        "name": "get_weather",
        "result": [
          { "type": "text", "text": "52°F with rain" }
        ]
      },
      {
        "type": "model_output",
        "status": "done",
        "content": [
          { "type": "text", "text": "It's 52°F with rain in Boston." }
        ]
      }
    ]{{ '**' }}
  }
  ```

  {% /tab %}

{% /tabs %}

## Streaming  {% id="streaming" %}

A key difference in streaming is that the Interactions API uses the same
endpoint with `"stream": true` in the request body, whereas the
`generateContent` API required calling a dedicated endpoint
(`:streamGenerateContent`).

Additionally, streaming events now use specialized types to monitor the
interaction lifecycle and track execution steps along the timeline.

### Before (generateContentStream)  {% id="before-generatecontentstream" %}

With `generateContent`, you consume a stream of response chunks.

{% tabs %}
  {% tab label="Python" %}
  ```python
  response = client.models.generate_content_stream(
      model="gemini-3.8-flash", contents="Tell me a story"
  )
  for chunk in response:
      print(chunk.text, end="")
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  const responseStream = await client.models.generateContentStream({
      model: 'gemini-3.8-flash',
      contents: 'Tell me a story',
  });
  for await (const chunk of responseStream) {
      process.stdout.write(chunk.text);
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.ResponseStream;
  import com.google.genai.types.GenerateContentResponse;
  
  Client client = new Client();
  
  try (ResponseStream<GenerateContentResponse> responseStream =
      client.models.generateContentStream("gemini-2.5-flash-lite", "Tell me a story", null)) {
    for (GenerateContentResponse chunk : responseStream) {
      System.out.print(chunk.text());
    }
  }
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      for chunk, err := range client.Models.GenerateContentStream(
          ctx,
          "gemini-2.5-flash-lite",
          genai.Text("Tell me a story"),
          nil,
      ) {
          if err != nil {
              log.Fatal(err)
          }
          fmt.Print(chunk.Text())
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  # Request
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:streamGenerateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [{
          "parts": [{
              "text": "Tell me a story"
          }]
      }]{{ '**' }}
  }'
  
  # Response stream
  {{ '**' }}event: content.start
  data: {"event_type": "content.start", "index": 0, "content": {"type": "thought"}}
  event: content.delta
  data: {"event_type": "content.delta", "index": 0, "delta": {"type": "thought_summary", "text": "User wants an explanation."}}
  event: content.stop
  data: {"event_type": "content.stop", "index": 0}
  event: content.start
  data: {"event_type": "content.start", "index": 1, "content": {"type": "text"}}
  event: content.delta
  data: {"event_type": "content.delta", "index": 1, "delta": {"type": "text", "text": "Hello"}}
  event: content.stop
  data: {"event_type": "content.stop", "index": 1}{{ '**' }}
  ```

  {% /tab %}

{% /tabs %}

### After (Interactions API)  {% id="after-interactions-api" %}

In the Interactions API, streaming uses Server-Sent Events (SSE) and specialized
delta types to represent execution steps as they happen.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  stream = client.interactions.create(
      model="gemini-3.8-flash",
      input="Tell me a story",
      stream=True,
  )
  
  for event in stream:
      if event.event_type == "step.delta":
          if event.delta.type == "text":
              print(event.delta.text, end="", flush=True)
      elif event.event_type == "interaction.completed":
          print(f"\n\n--- Stream Finished ---")
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const stream = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: 'Tell me a story',
      stream: true,
  });
  
  for await (const event of stream) {
      if (event.event_type === 'step.delta') {
          if (event.delta.type === 'text' && 'text' in event.delta) {
              process.stdout.write(event.delta.text);
          }
      } else if (event.event_type === 'interaction.completed') {
          console.log('\n\n--- Stream Finished ---');
      }
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.InteractionCompletedEvent;
  import com.google.genai.gaos.models.interactions.InteractionSSEEvent;
  import com.google.genai.gaos.models.interactions.InteractionSSEStreamEvent;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.interactions.StepDelta;
  import com.google.genai.gaos.models.interactions.TextDelta;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  import com.google.genai.gaos.utils.EventStream;
  
  Client client = new Client();
  
  CreateModelInteraction request =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(InteractionsInput.of("Tell me a story"))
          .stream(true)
          .build();
  
  try (EventStream<InteractionSSEStreamEvent> stream =
      client.interactions.create(CreateInteractionRequestBody.of(request)).events()) {
    for (InteractionSSEStreamEvent streamEvent : stream) {
      if (streamEvent.data().isPresent()) {
        InteractionSSEEvent event = streamEvent.data().get();
        if (event instanceof StepDelta) {
          StepDelta stepDelta = (StepDelta) event;
          if (stepDelta.delta().isPresent() && stepDelta.delta().get() instanceof TextDelta) {
            TextDelta textDelta = (TextDelta) stepDelta.delta().get();
            System.out.print(textDelta.text().orElse(""));
          }
        } else if (event instanceof InteractionCompletedEvent) {
          System.out.println("\n\n--- Stream Finished ---");
        }
      }
    }
  }
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model:  interactions.Model("gemini-3.8-flash"),
              Input:  interactions.NewInteractionsInput("Tell me a story"),
              Stream: genai.Ptr(true),
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
      stream := res.InteractionSSEStreamEvent
      defer stream.Close()
  
      for stream.Next() {
          event := stream.Value()
          if stepDelta := event.GetDataStepDelta(); stepDelta != nil {
              if textDelta := stepDelta.GetDeltaText(); textDelta != nil {
                  fmt.Print(textDelta.GetText())
              }
          } else if event.GetDataInteractionCompleted() != nil {
              fmt.Println("\n\n--- Stream Finished ---")
          }
      }
      if err := stream.Err(); err != nil {
          log.Fatal(err)
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
    {{ '**' }}event: interaction.created
    data: {"interaction": {"id": "int_xyz", "status": "in_progress"}, "event_type": "interaction.created"}
    event: interaction.status_update
    data: {"interaction_id": "int_xyz", "status": "in_progress", "event_type": "interaction.status_update"}
    event: step.start
    data: {"index": 0, "step": {"type": "thought"}, "event_type": "step.start"}
    event: step.delta
    data: {"index": 0, "delta": {"type": "thought_summary", "content": {"type": "text", "text": "User wants an explanation."}}, "event_type": "step.delta"}
    event: step.stop
    data: {"index": 0, "event_type": "step.stop"}
    event: step.start
    data: {"index": 1, "step": {"type": "model_output"}, "event_type": "step.start"}
    event: step.delta
    data: {"index": 1, "delta": {"type": "text", "text": "Hello"}, "event_type": "step.delta"}
    event: step.stop
    data: {"index": 1, "event_type": "step.stop"}
    event: interaction.completed
    data: {"interaction": {"id": "int_xyz", "status": "completed", "usage": {"total_input_tokens": 10, "total_output_tokens": 5, "total_tokens": 15}}, "event_type": "interaction.completed"}{{ '**' }}
  ```

  {% /tab %}

{% /tabs %}

### Streaming tools and function calls  {% id="streaming-tools-and-function-calls" %}

The way tools behave in the stream has shifted significantly from
`generateContent` to provide more granular control and visibility.

#### Before (generateContent)  {% id="before-generatecontent" %}

With `generateContent`, streaming function calls arrived complete in a single
chunk. You could not see the arguments being generated in real-time, so the
handler simply checked for a complete `functionCall` object.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  from google.genai import types
  
  client = genai.Client()
  
  stream = client.models.generate_content_stream(
      model="gemini-3.8-flash",
      contents="What's the weather in Boston?",
      config=types.GenerateContentConfig(tools=[weather_tool]),
  )
  
  for chunk in stream:
      if chunk.candidates[0].content.parts[0].function_call:
          fc = chunk.candidates[0].content.parts[0].function_call
          print(f"Call: {fc.name}({fc.args})")
      elif chunk.text:
          print(chunk.text, end="")
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const stream = await client.models.generateContentStream({
      model: 'gemini-3.8-flash',
      contents: "What's the weather in Boston?",
      config: { tools: [weatherTool] }
  });
  
  for await (const chunk of stream) {
      const part = chunk.candidates?.[0]?.content?.parts?.[0];
      if (part?.functionCall) {
          console.log(`Call: ${part.functionCall.name}(${JSON.stringify(part.functionCall.args)})`);
      } else if (part?.text) {
          process.stdout.write(part.text);
      }
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.ResponseStream;
  import com.google.genai.types.FunctionCall;
  import com.google.genai.types.FunctionDeclaration;
  import com.google.genai.types.GenerateContentConfig;
  import com.google.genai.types.GenerateContentResponse;
  import com.google.genai.types.Schema;
  import com.google.genai.types.Tool;
  import com.google.genai.types.Type;
  import java.util.Arrays;
  import java.util.Map;
  
  Client client = new Client();
  
  FunctionDeclaration weatherFunc =
      FunctionDeclaration.builder()
          .name("get_weather")
          .description("Gets weather")
          .parameters(
              Schema.builder()
                  .type(Type.Known.OBJECT)
                  .properties(Map.of("location", Schema.builder().type(Type.Known.STRING).build()))
                  .build())
          .build();
  
  Tool weatherTool = Tool.builder().functionDeclarations(Arrays.asList(weatherFunc)).build();
  
  try (ResponseStream<GenerateContentResponse> stream =
      client.models.generateContentStream(
          "gemini-2.5-flash-lite",
          "What's the weather in Boston?",
          GenerateContentConfig.builder().tools(Arrays.asList(weatherTool)).build())) {
    for (GenerateContentResponse chunk : stream) {
      if (chunk.functionCalls() != null && !chunk.functionCalls().isEmpty()) {
        FunctionCall fc = chunk.functionCalls().get(0);
        System.out.println("Call: " + fc.name().orElse("") + "(" + fc.args().orElse(Map.of()) + ")");
      } else if (chunk.text() != null) {
        System.out.print(chunk.text());
      }
    }
  }
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      weatherTool := &genai.Tool{
          FunctionDeclarations: []*genai.FunctionDeclaration{
              {
                  Name:        "get_weather",
                  Description: "Gets weather",
                  Parameters: &genai.Schema{
                      Type: genai.TypeObject,
                      Properties: map[string]*genai.Schema{
                          "location": {Type: genai.TypeString},
                      },
                  },
              },
          },
      }
  
      for chunk, err := range client.Models.GenerateContentStream(
          ctx,
          "gemini-2.5-flash-lite",
          genai.Text("What's the weather in Boston?"),
          &genai.GenerateContentConfig{
              Tools: []*genai.Tool{weatherTool},
          },
      ) {
          if err != nil {
              log.Fatal(err)
          }
          if calls := chunk.FunctionCalls(); len(calls) > 0 {
              fc := calls[0]
              fmt.Printf("Call: %s(%v)\n", fc.Name, fc.Args)
          } else if text := chunk.Text(); text != "" {
              fmt.Print(text)
          }
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:streamGenerateContent" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      {{ '**' }}"contents": [{"parts": [{"text": "What'\''s the weather in Boston?"}]}],
      "tools": [{"functionDeclarations": [{"name": "get_weather", "parameters": {"type": "OBJECT", "properties": {"location": {"type": "STRING"}}}}]}]{{ '**' }}
  }'
  
  {{ '**' }}{"candidates": [{"content": {"parts": [{"functionCall": {"name": "get_weather", "args": {"location": "Boston, MA"}}}]}}]}{{ '**' }}
  ```

  {% /tab %}

{% /tabs %}

#### After (Interactions API)  {% id="after-interactions-api" %}

The Interactions API streams function call arguments character-by-character as
`arguments_delta` events. The entire tool lifecycle - thought, call, result, and
output - plays out as a series of distinct steps.

{% tabs %}
  {% tab label="Python" %}
  ```python
  from google import genai
  
  client = genai.Client()
  
  stream = client.interactions.create(
      model="gemini-3.8-flash",
      input="What's the weather in Boston?",
      tools=[get_weather_tool],
      stream=True,
  )
  
  for event in stream:
      if event.event_type == "step.start":
          if event.step.type == "function_call":
              print(f"Calling: {event.step.name}")
      elif event.event_type == "step.delta":
          if event.delta.type == "arguments_delta":
              print(f" args: {event.delta.arguments}")
          elif event.delta.type == "text":
              print(event.delta.text, end="")
      elif event.event_type == "interaction.completed":
          print("\n--- Done ---")
  ```

  {% /tab %}

  {% tab label="JavaScript" %}
  ```javascript
  import { GoogleGenAI } from '@google/genai';
  
  const client = new GoogleGenAI({});
  
  const stream = await client.interactions.create({
      model: 'gemini-3.8-flash',
      input: "What's the weather in Boston?",
      tools: [getWeatherTool],
      stream: true,
  });
  
  for await (const event of stream) {
      if (event.event_type === 'step.start') {
          if (event.step.type === 'function_call') {
              console.log(`Calling: ${event.step.name}`);
          }
      } else if (event.event_type === 'step.delta') {
          if (event.delta.type === 'arguments_delta') {
              console.log(` args: ${event.delta.arguments}`);
          } else if (event.delta.type === 'text') {
              process.stdout.write(event.delta.text);
          }
      } else if (event.event_type === 'interaction.completed') {
          console.log('\n--- Done ---');
      }
  }
  ```

  {% /tab %}

  {% tab label="Java" %}
  ```java
  import com.google.genai.Client;
  import com.google.genai.gaos.models.interactions.ArgumentsDelta;
  import com.google.genai.gaos.models.interactions.CreateModelInteraction;
  import com.google.genai.gaos.models.interactions.Function;
  import com.google.genai.gaos.models.interactions.FunctionCallStep;
  import com.google.genai.gaos.models.interactions.InteractionCompletedEvent;
  import com.google.genai.gaos.models.interactions.InteractionSSEEvent;
  import com.google.genai.gaos.models.interactions.InteractionSSEStreamEvent;
  import com.google.genai.gaos.models.interactions.InteractionsInput;
  import com.google.genai.gaos.models.interactions.Model;
  import com.google.genai.gaos.models.interactions.StepDelta;
  import com.google.genai.gaos.models.interactions.StepDeltaData;
  import com.google.genai.gaos.models.interactions.StepStart;
  import com.google.genai.gaos.models.interactions.TextDelta;
  import com.google.genai.gaos.models.operations.CreateInteractionRequestBody;
  import com.google.genai.gaos.utils.EventStream;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Map;
  
  Client client = new Client();
  
  Map<String, Object> properties = new HashMap<>();
  properties.put("location", Map.of("type", "string"));
  
  Map<String, Object> parameters = new HashMap<>();
  parameters.put("type", "object");
  parameters.put("properties", properties);
  parameters.put("required", Arrays.asList("location"));
  
  Function getWeatherTool =
      Function.builder()
          .name("get_weather")
          .description("Gets weather")
          .parameters(parameters)
          .build();
  
  CreateModelInteraction request =
      CreateModelInteraction.builder()
          .model(Model.of("gemini-3.8-flash"))
          .input(InteractionsInput.of("What's the weather in Boston?"))
          .tools(Arrays.asList(getWeatherTool))
          .stream(true)
          .build();
  
  try (EventStream<InteractionSSEStreamEvent> stream =
      client.interactions.create(CreateInteractionRequestBody.of(request)).events()) {
    for (InteractionSSEStreamEvent streamEvent : stream) {
      if (streamEvent.data().isPresent()) {
        InteractionSSEEvent event = streamEvent.data().get();
        if (event instanceof StepStart) {
          StepStart stepStart = (StepStart) event;
          if (stepStart.step().isPresent() && stepStart.step().get() instanceof FunctionCallStep) {
            FunctionCallStep fcStep = (FunctionCallStep) stepStart.step().get();
            System.out.println("Calling: " + fcStep.name().orElse(""));
          }
        } else if (event instanceof StepDelta) {
          StepDelta stepDelta = (StepDelta) event;
          if (stepDelta.delta().isPresent()) {
            StepDeltaData delta = stepDelta.delta().get();
            if (delta instanceof ArgumentsDelta) {
              System.out.println("  args: " + ((ArgumentsDelta) delta).arguments().orElse(""));
            } else if (delta instanceof TextDelta) {
              System.out.print(((TextDelta) delta).text().orElse(""));
            }
          }
        } else if (event instanceof InteractionCompletedEvent) {
          System.out.println("\n--- Done ---");
        }
      }
    }
  }
  ```

  {% /tab %}

  {% tab label="Go" %}
  ```go
  package main
  
  import (
      "context"
      "fmt"
      "log"
  
      "google.golang.org/genai"
      "google.golang.org/genai/interactions/models/interactions"
      "google.golang.org/genai/interactions/models/operations"
  )
  
  func main() {
      ctx := context.Background()
      client, err := genai.NewClient(ctx, nil)
      if err != nil {
          log.Fatal(err)
      }
  
      getWeatherTool := interactions.NewTool(interactions.Function{
          Name:        genai.Ptr("get_weather"),
          Description: genai.Ptr("Gets weather"),
          Parameters: map[string]any{
              "type": "object",
              "properties": map[string]any{
                  "location": map[string]any{"type": "string"},
              },
              "required": []string{"location"},
          },
      })
  
      res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{
          Body: operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
              Model:  interactions.Model("gemini-3.8-flash"),
              Input:  interactions.NewInteractionsInput("What's the weather in Boston?"),
              Tools:  []interactions.Tool{getWeatherTool},
              Stream: genai.Ptr(true),
          }),
      })
      if err != nil {
          log.Fatal(err)
      }
      stream := res.InteractionSSEStreamEvent
      defer stream.Close()
  
      for stream.Next() {
          event := stream.Value()
          if stepStart := event.GetDataStepStart(); stepStart != nil {
              if fcStep := stepStart.GetStepFunctionCall(); fcStep != nil {
                  fmt.Println("Calling:", fcStep.Name)
              }
          } else if stepDelta := event.GetDataStepDelta(); stepDelta != nil {
              if argsDelta := stepDelta.GetDeltaArgumentsDelta(); argsDelta != nil {
                  fmt.Println("  args:", argsDelta.GetArguments())
              } else if textDelta := stepDelta.GetDeltaText(); textDelta != nil {
                  fmt.Print(textDelta.GetText())
              }
          } else if event.GetDataInteractionCompleted() != nil {
              fmt.Println("\n--- Done ---")
          }
      }
      if err := stream.Err(); err != nil {
          log.Fatal(err)
      }
  }
  ```

  {% /tab %}

  {% tab label="REST" %}
  ```json
  curl -X POST "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "Content-Type: application/json" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -d '{
      "model": "gemini-3.8-flash",
      {{ '**' }}"input": "What'\''s the weather in Boston?",
      "tools": [{"type": "function", "name": "get_weather", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}}}],
      "stream": true{{ '**' }}
  }'
  
  // Stream 1: the model calls the function
  event: interaction.created
  data: {"interaction": {"id": "int_1", "status": "in_progress"}, "event_type": "interaction.created"}
  
  event: interaction.status_update
  data: {"interaction_id": "int_1", "status": "in_progress", "event_type": "interaction.status_update"}
  
  // ── Step 0: Thought ──────────────────────────────────
  event: step.start
  data: {"index": 0, "step": {"type": "thought"}, "event_type": "step.start"}
  
  event: step.delta
  data: {"index": 0, "delta": {"type": "thought_summary", "content": {"type": "text", "text": "The user wants weather data for Boston. I'll call the get_weather tool."}}, "event_type": "step.delta"}
  
  event: step.stop
  data: {"index": 0, "event_type": "step.stop"}
  
  // ── Step 1: Function Call (arguments streamed) ───────
  event: step.start
  data: {"index": 1, "step": {"type": "function_call", "id": "fc_1", "name": "get_weather", "arguments": {}}, "event_type": "step.start"}
  
  event: step.delta
  data: {"index": 1, "delta": {"type": "arguments_delta", "arguments": "{\"location\": \"Boston, MA\"}"}, "event_type": "step.delta"}
  
  event: step.stop
  data: {"index": 1, "event_type": "step.stop"}
  
  // The stream ends with status requires_action: the model needs the tool result.
  event: interaction.completed
  data: {"interaction": {"id": "int_1", "status": "requires_action", "usage": {"total_input_tokens": 34, "total_output_tokens": 15, "total_tokens": 49}}, "event_type": "interaction.completed"}
  
  event: done
  data: [DONE]
  
  // ── (Client submits the tool result) ──────────────────
  // The client calls interactions.create with the function_result as input
  // and previous_interaction_id set to "int_1". This opens a new stream.
  
  // Stream 2: the model uses the result
  event: interaction.created
  data: {"interaction": {"id": "int_2", "status": "in_progress"}, "event_type": "interaction.created"}
  
  event: interaction.status_update
  data: {"interaction_id": "int_2", "status": "in_progress", "event_type": "interaction.status_update"}
  
  // ── Step 0: Thought ──────────────────────────────────
  event: step.start
  data: {"index": 0, "step": {"type": "thought"}, "event_type": "step.start"}
  
  event: step.delta
  data: {"index": 0, "delta": {"type": "thought_summary", "content": {"type": "text", "text": "Got weather data. Composing the final response."}}, "event_type": "step.delta"}
  
  event: step.stop
  data: {"index": 0, "event_type": "step.stop"}
  
  // ── Step 1: Model Output (text streamed) ─────────────
  event: step.start
  data: {"index": 1, "step": {"type": "model_output"}, "event_type": "step.start"}
  
  event: step.delta
  data: {"index": 1, "delta": {"type": "text", "text": "It's currently 52°F and rainy in Boston."}, "event_type": "step.delta"}
  
  event: step.stop
  data: {"index": 1, "event_type": "step.stop"}
  
  // ── Interaction complete ─────────────────────────────
  event: interaction.completed
  data: {"interaction": {"id": "int_2", "status": "completed", "usage": {"total_input_tokens": 256, "total_output_tokens": 128, "total_tokens": 384}}, "event_type": "interaction.completed"}
  
  event: done
  data: [DONE]
  ```

  {% /tab %}

{% /tabs %}

