> ## 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 properties = new HashMap<>(); properties.put("recipe_name", Map.of("type", "string")); properties.put("ingredients", Map.of("type", "array", "items", Map.of("type", "string"))); Map 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 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 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 properties = new HashMap<>(); properties.put("location", Map.of("type", "string")); Map 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 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 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 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 properties = new HashMap<>(); properties.put("location", Map.of("type", "string")); Map 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 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 %}