use std::sync::Arc;
use crate::backend::BackendStream;
use crate::{
ThinkingLevel,
client::{Error, GeminiClient, Model},
interactions::agent_config::AgentConfig,
interactions::environment::Environment,
interactions::model::{
Content, CreateInteractionRequest, GenerationConfig, Input, ResponseFormat,
ResponseModality, ServiceTier, Step, ThinkingSummaries, Tool, ToolChoice,
},
interactions::sse::InteractionSseEvent,
interactions::validate::validate_interaction_request,
};
pub struct InteractionBuilder {
client: Arc<GeminiClient>,
request: CreateInteractionRequest,
content_parts: Vec<Content>,
steps: Vec<Step>,
text_input: Option<String>,
}
impl InteractionBuilder {
pub(crate) fn new(client: Arc<GeminiClient>) -> Self {
Self {
client,
request: CreateInteractionRequest::default(),
content_parts: Vec::new(),
steps: Vec::new(),
text_input: None,
}
}
pub fn model(mut self, model: impl Into<Model>) -> Self {
let model = model.into();
let id = model.as_str().strip_prefix("models/").unwrap_or(model.as_str()).to_string();
self.request.model = Some(id);
self
}
pub fn agent(mut self, agent: impl Into<String>) -> Self {
self.request.agent = Some(agent.into());
self
}
pub fn input_text(mut self, text: impl Into<String>) -> Self {
self.text_input = Some(text.into());
self
}
pub fn content(mut self, content: Content) -> Self {
self.content_parts.push(content);
self
}
pub fn image(mut self, data: impl Into<String>, mime_type: impl Into<String>) -> Self {
self.content_parts.push(Content::image(data, mime_type));
self
}
pub fn steps(mut self, steps: Vec<Step>) -> Self {
self.steps = steps;
self
}
pub fn step(mut self, step: Step) -> Self {
self.steps.push(step);
self
}
pub fn function_result(
mut self,
call_id: impl Into<String>,
name: impl Into<String>,
result: serde_json::Value,
) -> Self {
self.steps.push(Step::FunctionResult {
call_id: call_id.into(),
name: Some(name.into()),
result,
is_error: None,
signature: None,
});
self
}
pub fn system_instruction(mut self, text: impl Into<String>) -> Self {
self.request.system_instruction = Some(text.into());
self
}
pub fn tool(mut self, tool: Tool) -> Self {
self.request.tools.push(tool);
self
}
pub fn function(
self,
name: impl Into<String>,
description: impl Into<String>,
parameters: serde_json::Value,
) -> Self {
self.tool(Tool::function(name, description, parameters))
}
pub fn previous_interaction_id(mut self, id: impl Into<String>) -> Self {
self.request.previous_interaction_id = Some(id.into());
self
}
pub fn background(mut self, background: bool) -> Self {
self.request.background = Some(background);
self
}
pub fn store(mut self, store: bool) -> Self {
self.request.store = Some(store);
self
}
pub fn thinking_level(mut self, level: ThinkingLevel) -> Self {
self.generation_config_mut().thinking_level = Some(level);
self
}
pub fn thinking_summaries(mut self, summaries: ThinkingSummaries) -> Self {
self.generation_config_mut().thinking_summaries = Some(summaries);
self
}
pub fn max_output_tokens(mut self, max: i32) -> Self {
self.generation_config_mut().max_output_tokens = Some(max);
self
}
pub fn temperature(mut self, temperature: f32) -> Self {
self.generation_config_mut().temperature = Some(temperature);
self
}
pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
self.generation_config_mut().tool_choice = Some(choice);
self
}
pub fn generation_config(mut self, config: GenerationConfig) -> Self {
self.request.generation_config = Some(config);
self
}
pub fn response_format(mut self, format: ResponseFormat) -> Self {
self.request.response_format = Some(format);
self
}
pub fn json_schema(mut self, schema: serde_json::Value) -> Self {
self.request.response_format = Some(ResponseFormat::json_schema(schema));
self
}
pub fn response_modalities(mut self, modalities: Vec<ResponseModality>) -> Self {
self.request.response_modalities = modalities;
self
}
pub fn service_tier(mut self, tier: ServiceTier) -> Self {
self.request.service_tier = Some(tier);
self
}
pub fn environment(mut self, env: Environment) -> Self {
self.request.environment = Some(env);
self
}
pub fn agent_config(mut self, config: AgentConfig) -> Self {
self.request.agent_config = Some(config);
self
}
pub fn antigravity(mut self) -> Self {
self.request.agent = Some("antigravity-preview-05-2026".to_string());
self.request.model = None;
self.request.store = Some(true);
self
}
pub fn deep_research(mut self, agent_id: impl Into<String>) -> Self {
self.request.agent = Some(agent_id.into());
self.request.model = None;
self.request.background = Some(true);
self.request.store = Some(true);
self
}
fn generation_config_mut(&mut self) -> &mut GenerationConfig {
self.request.generation_config.get_or_insert_with(GenerationConfig::default)
}
fn finalize_input(&mut self) {
if let Some(text) = self.text_input.take() {
self.request.input = Input::Text(text);
} else if !self.steps.is_empty() {
self.request.input = Input::Steps(std::mem::take(&mut self.steps));
} else {
self.request.input = Input::Content(std::mem::take(&mut self.content_parts));
}
}
pub async fn send(mut self) -> Result<crate::interactions::Interaction, Error> {
self.finalize_input();
validate_interaction_request(&self.request)?;
self.client.create_interaction(self.request).await
}
pub async fn stream(mut self) -> Result<BackendStream<InteractionSseEvent>, Error> {
self.finalize_input();
validate_interaction_request(&self.request)?;
self.request.stream = Some(true);
self.client.create_interaction_stream(self.request).await
}
}