Skip to content

Latest commit

 

History

History
408 lines (321 loc) · 16.4 KB

File metadata and controls

408 lines (321 loc) · 16.4 KB

InteractionBuilder API Guide

This guide covers the InteractionBuilder fluent API, including method naming conventions, interactions, and common patterns.

Table of Contents

Overview

The InteractionBuilder provides a fluent interface for constructing requests to the Gemini API. Methods can be chained in any order, and the request is validated and built when you call create(), create_stream(), or build().

let response = client
    .interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_system_instruction("You are helpful")
    .with_text("Hello!")
    .create()
    .await?;

Method Naming Conventions

Methods follow a consistent naming pattern based on their behavior:

Prefix Behavior Example
with_* Configures a setting (replaces if called twice) with_model(), with_text(), with_content()
add_* Accumulates items to a collection add_function(), add_tool()

Complete Method Reference

Method Prefix Behavior Notes
Configuration
with_model() with replaces Mutually exclusive with with_agent()
with_agent() with replaces Mutually exclusive with with_model()
with_agent_config() with replaces Requires with_agent()
with_system_instruction() with replaces Plain string; not inherited across turns
with_timeout() with replaces
with_service_tier(ServiceTier) with replaces Flex/Standard/Priority (wire: lowercase "flex"/"standard"/"priority")
with_cached_content() Removed — the API rejects the field (400 Unknown parameter 'cached_content'); no Interactions request can reference a cache
with_webhook_config(WebhookConfig) with replaces Per-request webhook routing (uris + user_metadata)
with_environment(impl Into<EnvironmentSpec>) with replaces Environment ID string or typed RemoteEnvironment
with_safety_settings(Vec<SafetySetting>) with replaces Vertex-only (Gemini API rejects with 400)
add_safety_setting(SafetySetting) add accumulates Appends one safety setting; Vertex-only
with_labels(impl IntoIterator<Item = (K, V)>) with replaces User metadata labels; Vertex-only
add_label(key, value) add merges Inserts one label (same key overwrites — map-backed, unlike add_safety_setting's list); Vertex-only
Input
with_text() with replaces Composes with with_history()
with_history(Vec<Step>) with replaces Composes with with_text()
with_content() with replaces For multimodal; incompatible with history
Tools
add_function() add accumulates Single function declaration
add_functions() add accumulates Multiple function declarations
with_tool_service() with replaces Dependency-injected tools
Server-Side Tools (enable capabilities)
add_tool(impl Into<Tool>) add accumulates Unified tool entry point (see Tool Configuration Structs)
with_google_search() with accumulates Enables Google Search
with_google_maps() with accumulates Enables Google Maps
with_code_execution() with accumulates Enables code execution
with_url_context() with accumulates Enables URL fetching
with_computer_use() Removed — use add_tool(ComputerUseConfig::new())
with_computer_use_excluding() Removed — use add_tool(ComputerUseConfig::new().excluding(...))
add_mcp_server() Removed — use add_tool(McpServerConfig::new(name, url))
with_file_search() Removed — use add_tool(FileSearchConfig::new(stores))
with_file_search_config() Removed — use add_tool(FileSearchConfig::new(stores))
Generation Config
with_function_calling_mode() with replaces Auto/Any/None/Validated (wire: lowercase "auto"/"any"/"none"/"validated"); sets tool_choice to the plain-mode form
with_tool_choice(ToolChoice) with replaces Sets the full tool_choice union directly; escape hatch for custom shapes
with_allowed_tools(Vec<String>) with replaces Restricts model to named tools; sets tool_choice to the AllowedTools restriction object (preserves a previously set mode)
with_image_config(ImageConfig) with replaces Image generation aspect ratio and size
with_video_config(VideoConfig) with replaces Video generation task mode (generation_config.video_config)
with_speech_config(SpeechConfig) with replaces Single-voice TTS (sends a single-entry speech_config list)
with_speech_configs(Vec<SpeechConfig>) with replaces Full multi-speaker speech_config list
add_speech_config(SpeechConfig) add accumulates Appends one speaker config
with_thinking_level() with replaces Chain-of-thought reasoning level
with_seed() with replaces Deterministic output
with_stop_sequences() with replaces Halt generation on sequences
with_presence_penalty(f32) with replaces Penalizes tokens already present; range [-2.0, 2.0]
with_frequency_penalty(f32) with replaces Penalizes tokens by frequency; range [-2.0, 2.0]
with_transcription_config(TranscriptionConfig) with replaces Audio transcription tuning (generation_config.transcription_config)
Response Format & Modalities
with_response_format(impl Into<ResponseFormat>) with replaces Typed format (text/audio/image/video) or a raw JSON schema serde_json::Value (maps to the text/application/json form)
with_response_formats(Vec<ResponseFormat>) with replaces List form — one format per output modality
with_response_modalities(Vec<String>) with replaces Deprecated in the API docs — prefer typed with_response_format(s) (modalities are inferred)
with_image_output() / with_audio_output() / with_video_output() with replaces Modality shortcuts

Note: GenerationConfig no longer has a top_k field (removed in API revision 2026-05-20), so there is no with_top_k() builder method. FileSearchConfig::with_top_k() (a file-search retrieval setting) is unrelated and still exists.

Tool Configuration Structs

For tools with optional configuration, use a config struct with add_tool():

Config Struct Required Fields Optional Methods
GoogleSearchConfig (none) .with_search_types(Vec<SearchType>)
GoogleMapsConfig (none) .with_widget(), .with_location(latitude, longitude)
McpServerConfig name, url .with_allowed_tools(Vec<String>), .with_allowed_tools_config(Vec<AllowedTools>), .with_headers(...)
ComputerUseConfig (none) .with_environment(env), .excluding(Vec<String>), .with_prompt_injection_detection(bool), .disabling_safety_policies(Vec<String>)
FileSearchConfig store_names .with_top_k(i32), .with_metadata_filter(String)
RetrievalConfig (none) .with_vertex_ai_search(...), .with_rag_store(...), .with_exa_ai_search(...), .with_parallel_ai_search(...), .with_retrieval_types(Vec<RetrievalType>)

Notes:

  • ComputerUseConfig::with_environment() accepts "browser" (default), "mobile", or "desktop".
  • McpServerConfig::with_allowed_tools(Vec<String>) wraps the names into a single AllowedTools entry; use .with_allowed_tools_config(Vec<AllowedTools>) to supply pre-built AllowedTools objects (e.g. with a mode via AllowedTools::new(tools).with_mode(mode)).
  • Each RetrievalConfig::with_* backend method also enables the corresponding RetrievalType, keeping retrieval_types in sync; .with_retrieval_types() is the explicit escape hatch.

Convention: Prefer struct variants with optional fields over unit variants when the API has configuration options. This avoids breaking changes when adding fields later.

Input Methods

The builder has three ways to set the input content:

Method Purpose Composes With
with_text(str) Simple text message with_history(), with_content()
with_history(Vec<Step>) Conversation history with_text()
with_content(Vec<Content>) Multimodal content with_text()
conversation()...done() Fluent conversation builder

How Inputs Compose at Build Time

content_input set?
├── Yes
│   └── history set?
│       ├── Yes → ERROR (incompatible)
│       └── No
│           └── current_message set?
│               ├── Yes → Content([Content::text(message), ...content_items])
│               └── No  → Content([...content_items])
└── No
    └── history set?
        ├── Yes
        │   └── current_message set?
        │       ├── Yes → Steps([...history, Step::user_text(message)])
        │       └── No  → Steps([...history])
        └── No
            └── current_message set?
                ├── Yes → Text(message)
                └── No  → ERROR ("Input is required")

Multimodal Input with Content

For multimodal requests, use with_content() with Content constructors:

use genai_rs::{Client, Content};

let response = client
    .interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_content(vec![
        Content::text("Describe this image"),
        Content::image_data(base64_data, "image/png"),
    ])
    .create()
    .await?;

For file-based content, use helper functions:

use genai_rs::{Client, Content, image_from_file};

let image_content = image_from_file("photo.jpg").await?;
let response = client
    .interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_content(vec![
        Content::text("What's in this image?"),
        image_content,
    ])
    .create()
    .await?;

Method Interactions

Order Independence

Most input methods are order-independent - calling them in different orders produces the same result:

// These are equivalent:
.with_history(h).with_text("question")
.with_text("question").with_history(h)

Replacement vs Accumulation

// Replacement: second call wins
.with_text("first").with_text("second")  // → "second"

// Accumulation for tools
.add_function(func1).add_function(func2)  // → [func1, func2]

Validation Errors

The builder validates configuration at build() time and returns clear errors:

1. Content Cannot Combine with History

Content input (via with_content()) is for single-turn multimodal messages. It cannot be combined with multi-turn history.

// ERROR: Cannot combine content with history
client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_history(conversation_history)
    .with_content(vec![Content::image_data(base64, "image/png")])
    .build()  // Returns Err!

Workaround: For multimodal multi-turn, build Step objects with content arrays:

use genai_rs::{Step, Content};

let multimodal_step = Step::user_input(vec![
    Content::text("What's in this image?"),
    Content::image_data(base64_data, "image/png"),
]);

let mut history = existing_history;
history.push(multimodal_step);

client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_history(history)
    .create()
    .await?;

2. Model vs Agent is Mutually Exclusive

You must specify exactly one of with_model() or with_agent():

// ERROR: Both specified
client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_agent("deep-research-pro-preview-12-2025")
    .build()  // Returns Err!

// ERROR: Neither specified
client.interaction()
    .with_text("Hello")
    .build()  // Returns Err!

3. Agent Config Requires Agent

with_agent_config() is only valid when using with_agent():

// ERROR: agent_config without agent
client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_agent_config(DeepResearchConfig::new())
    .with_text("Research AI trends")
    .build()  // Returns Err!

Storage Constraints

Certain combinations of builder methods are invalid because they conflict with storage requirements. The builder validates these constraints at build() time:

Invalid Combinations

If You Call Cannot Also Call Reason
with_store_disabled() with_previous_interaction() Chaining requires storage
with_store_disabled() with_background(true) Background requires storage
with_store_disabled() create_with_auto_functions() Auto-functions require storage
// Runtime error from build(): "Chained interactions require storage..."
client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_text("Hello")
    .with_previous_interaction("id-123")
    .with_store_disabled()
    .build()  // Returns Err!

Conditional Chaining

Since validation happens at runtime, you can conditionally chain methods:

// Your application tracks conversation state
let previous_interaction_id: Option<String> = session.last_interaction_id();
let should_disable_storage: bool = config.privacy_mode;

let mut builder = client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_text("Hello");

// Conditionally add previous interaction
if let Some(prev_id) = previous_interaction_id {
    builder = builder.with_previous_interaction(prev_id);
}

// Conditionally disable storage (only valid if not chaining)
if previous_interaction_id.is_none() && should_disable_storage {
    builder = builder.with_store_disabled();
}

let response = builder.create().await?;

Best Practices

1. Use Specific Input Methods

Prefer the specific method for your use case:

// Good: Clear intent
.with_text("Hello")  // Simple text
.with_history(steps)  // Multi-turn (Vec<Step>)
.with_content(vec![Content::text("Question"), Content::image_data(...)]) // Multimodal

// Avoid: Generic method is less clear
.with_input(InteractionInput::Text("Hello".to_string()))

2. Chain Related Configuration

Group related builder calls together:

use genai_rs::CallableFunction;

client.interaction()
    // Target
    .with_model(genai_rs::DEFAULT_MODEL)
    // Context
    .with_system_instruction("You are helpful")
    .with_history(history)
    // Input
    .with_text("Current question")
    // Tools
    .add_function(GetWeatherCallable.declaration())
    // Execute
    .create()
    .await?;

3. Handle Errors at Build Time

Call build() explicitly when you want to validate without executing:

let request = client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .with_text("Hello")
    .build()?;  // Validate configuration

// Later: execute
let response = client.execute(request).await?;

4. Use ConversationBuilder for Inline Conversations

For test fixtures or inline conversation construction. .user() and .model() produce user_input/model_output Steps under the hood:

let response = client.interaction()
    .with_model(genai_rs::DEFAULT_MODEL)
    .conversation()
        .user("What is 2+2?")
        .model("4")
        .user("Times 3?")
        .done()
    .create()
    .await?;

Retrieving Stored Interactions

When storage is enabled, interactions can be fetched back by ID via the Client:

Method Behavior
client.get_interaction(id) Fetches the interaction; response input field is None
client.get_interaction_with_input(id) Fetches with include_input=true so the response's input: Option<InteractionInput> is populated

Related Documentation