This guide covers the InteractionBuilder fluent API, including method naming conventions, interactions, and common patterns.
- Overview
- Method Naming Conventions
- Input Methods
- Method Interactions
- Validation Errors
- Storage Constraints
- Best Practices
- Retrieving Stored Interactions
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?;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() |
| 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.
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 singleAllowedToolsentry; use.with_allowed_tools_config(Vec<AllowedTools>)to supply pre-builtAllowedToolsobjects (e.g. with a mode viaAllowedTools::new(tools).with_mode(mode)).- Each
RetrievalConfig::with_*backend method also enables the correspondingRetrievalType, keepingretrieval_typesin 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.
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 | — |
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")
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?;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: second call wins
.with_text("first").with_text("second") // → "second"
// Accumulation for tools
.add_function(func1).add_function(func2) // → [func1, func2]The builder validates configuration at build() time and returns clear errors:
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?;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!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!Certain combinations of builder methods are invalid because they conflict with storage requirements. The builder validates these constraints at build() time:
| 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!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?;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()))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?;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?;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?;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 |
- Conversation Patterns - Multi-turn conversation strategies
- Multimodal - Working with images, audio, video
- Function Calling - Tool integration
- Multi-Turn Function Calling - Function calling in conversations