gemini_rust/interactions/
builder.rs1use std::sync::Arc;
2use tracing::{instrument, Span};
3
4use crate::client::{Error as ClientError, GeminiClient};
5use crate::interactions::model::*;
6use crate::interactions::stream::InteractionStream;
7
8#[derive(Clone)]
10pub struct InteractionBuilder {
11 client: Arc<GeminiClient>,
12 model: Option<String>,
13 agent: Option<String>,
14 input: Option<InteractionInput>,
15 system_instruction: Option<String>,
16 tools: Vec<InteractionTool>,
17 response_format: Option<ResponseFormat>,
18 store: Option<bool>,
19 background: bool,
20 generation_config: Option<InteractionGenerationConfig>,
21 agent_config: Option<AgentConfig>,
22 previous_interaction_id: Option<String>,
23 environment: Option<EnvironmentConfigOrString>,
24 cached_content: Option<String>,
25 response_modalities: Vec<ResponseModality>,
26 service_tier: Option<ServiceTier>,
27 webhook_config: Option<WebhookConfig>,
28}
29
30impl InteractionBuilder {
31 pub(crate) fn new(client: Arc<GeminiClient>) -> Self {
32 Self {
33 client,
34 model: None,
35 agent: None,
36 input: None,
37 system_instruction: None,
38 tools: Vec::new(),
39 response_format: None,
40 store: None,
41 background: false,
42 generation_config: None,
43 agent_config: None,
44 previous_interaction_id: None,
45 environment: None,
46 cached_content: None,
47 response_modalities: Vec::new(),
48 service_tier: None,
49 webhook_config: None,
50 }
51 }
52
53 pub fn with_model(mut self, model: impl Into<String>) -> Self {
57 self.model = Some(model.into());
58 self.agent = None;
59 self
60 }
61
62 pub fn with_agent(mut self, agent: impl Into<String>) -> Self {
64 self.agent = Some(agent.into());
65 self.model = None;
66 self
67 }
68
69 pub fn with_input(mut self, input: impl Into<InteractionInput>) -> Self {
73 self.input = Some(input.into());
74 self
75 }
76
77 pub fn with_text(mut self, text: impl Into<String>) -> Self {
79 self.input = Some(InteractionInput::Text(text.into()));
80 self
81 }
82
83 pub fn with_content_input(mut self, content: Vec<InteractionContent>) -> Self {
85 self.input = Some(InteractionInput::ContentArray(content));
86 self
87 }
88
89 pub fn with_step_input(mut self, steps: Vec<Step>) -> Self {
91 self.input = Some(InteractionInput::StepArray(steps));
92 self
93 }
94
95 pub fn with_image(mut self, data: impl Into<String>, mime_type: ImageMimeType) -> Self {
97 let content = InteractionContent::image(data, mime_type);
98 self.input = match self.input {
99 Some(InteractionInput::ContentArray(mut arr)) => {
100 arr.push(content);
101 Some(InteractionInput::ContentArray(arr))
102 }
103 _ => Some(InteractionInput::ContentArray(vec![content])),
104 };
105 self
106 }
107
108 pub fn with_image_uri(mut self, uri: impl Into<String>, mime_type: ImageMimeType) -> Self {
110 let content = InteractionContent::image_uri(uri, mime_type);
111 self.input = match self.input {
112 Some(InteractionInput::ContentArray(mut arr)) => {
113 arr.push(content);
114 Some(InteractionInput::ContentArray(arr))
115 }
116 _ => Some(InteractionInput::ContentArray(vec![content])),
117 };
118 self
119 }
120
121 pub fn with_audio(mut self, data: impl Into<String>, mime_type: AudioMimeType) -> Self {
123 let content = InteractionContent::audio(data, mime_type);
124 self.input = match self.input {
125 Some(InteractionInput::ContentArray(mut arr)) => {
126 arr.push(content);
127 Some(InteractionInput::ContentArray(arr))
128 }
129 _ => Some(InteractionInput::ContentArray(vec![content])),
130 };
131 self
132 }
133
134 pub fn with_video(mut self, uri: impl Into<String>) -> Self {
136 let content = InteractionContent::video_uri(uri);
137 self.input = match self.input {
138 Some(InteractionInput::ContentArray(mut arr)) => {
139 arr.push(content);
140 Some(InteractionInput::ContentArray(arr))
141 }
142 _ => Some(InteractionInput::ContentArray(vec![content])),
143 };
144 self
145 }
146
147 pub fn with_document(mut self, data: impl Into<String>, mime_type: DocumentMimeType) -> Self {
149 let content = InteractionContent::document(data, mime_type);
150 self.input = match self.input {
151 Some(InteractionInput::ContentArray(mut arr)) => {
152 arr.push(content);
153 Some(InteractionInput::ContentArray(arr))
154 }
155 _ => Some(InteractionInput::ContentArray(vec![content])),
156 };
157 self
158 }
159
160 pub fn with_system_instruction(mut self, instruction: impl Into<String>) -> Self {
164 self.system_instruction = Some(instruction.into());
165 self
166 }
167
168 pub fn with_tool(mut self, tool: InteractionTool) -> Self {
172 self.tools.push(tool);
173 self
174 }
175
176 pub fn with_tools(mut self, tools: Vec<InteractionTool>) -> Self {
178 self.tools.extend(tools);
179 self
180 }
181
182 pub fn with_function(
184 mut self,
185 name: impl Into<String>,
186 description: impl Into<String>,
187 parameters: serde_json::Value,
188 ) -> Self {
189 self.tools
190 .push(InteractionTool::function(name, description, parameters));
191 self
192 }
193
194 pub fn with_google_search(mut self) -> Self {
196 self.tools.push(InteractionTool::google_search());
197 self
198 }
199
200 pub fn with_code_execution(mut self) -> Self {
202 self.tools.push(InteractionTool::code_execution());
203 self
204 }
205
206 pub fn with_url_context(mut self) -> Self {
208 self.tools.push(InteractionTool::url_context());
209 self
210 }
211
212 pub fn with_google_maps(mut self) -> Self {
214 self.tools.push(InteractionTool::google_maps());
215 self
216 }
217
218 pub fn with_file_search(mut self, store_names: Vec<String>) -> Self {
220 self.tools.push(InteractionTool::file_search(store_names));
221 self
222 }
223
224 pub fn with_mcp_server(mut self, name: impl Into<String>, url: impl Into<String>) -> Self {
226 self.tools.push(InteractionTool::mcp_server(name, url));
227 self
228 }
229
230 pub fn with_computer_use(mut self, environment: ComputerUseEnvironment) -> Self {
232 self.tools.push(InteractionTool::computer_use(environment));
233 self
234 }
235
236 pub fn with_retrieval(mut self, retrieval_types: Vec<RetrievalType>) -> Self {
238 self.tools.push(InteractionTool::retrieval(retrieval_types));
239 self
240 }
241
242 pub fn with_response_format(mut self, format: ResponseFormat) -> Self {
246 self.response_format = Some(format);
247 self
248 }
249
250 pub fn with_json_schema(mut self, schema: serde_json::Value) -> Self {
252 self.response_format = Some(ResponseFormat::json_schema(schema));
253 self
254 }
255
256 pub fn with_temperature(mut self, temperature: f64) -> Self {
259 self.generation_config
260 .get_or_insert_with(Default::default)
261 .temperature = Some(temperature);
262 self
263 }
264
265 pub fn with_top_p(mut self, top_p: f64) -> Self {
266 self.generation_config
267 .get_or_insert_with(Default::default)
268 .top_p = Some(top_p);
269 self
270 }
271
272 pub fn with_seed(mut self, seed: i64) -> Self {
273 self.generation_config
274 .get_or_insert_with(Default::default)
275 .seed = Some(seed);
276 self
277 }
278
279 pub fn with_stop_sequences(mut self, sequences: Vec<String>) -> Self {
280 self.generation_config
281 .get_or_insert_with(Default::default)
282 .stop_sequences = sequences;
283 self
284 }
285
286 pub fn with_max_output_tokens(mut self, tokens: i64) -> Self {
287 self.generation_config
288 .get_or_insert_with(Default::default)
289 .max_output_tokens = Some(tokens);
290 self
291 }
292
293 pub fn with_thinking_level(mut self, level: InteractionThinkingLevel) -> Self {
294 self.generation_config
295 .get_or_insert_with(Default::default)
296 .thinking_level = Some(level);
297 self
298 }
299
300 pub fn with_thinking_summaries(mut self, summaries: ThinkingSummaries) -> Self {
301 self.generation_config
302 .get_or_insert_with(Default::default)
303 .thinking_summaries = Some(summaries);
304 self
305 }
306
307 pub fn with_presence_penalty(mut self, penalty: f64) -> Self {
308 self.generation_config
309 .get_or_insert_with(Default::default)
310 .presence_penalty = Some(penalty);
311 self
312 }
313
314 pub fn with_frequency_penalty(mut self, penalty: f64) -> Self {
315 self.generation_config
316 .get_or_insert_with(Default::default)
317 .frequency_penalty = Some(penalty);
318 self
319 }
320
321 pub fn with_tool_choice(mut self, choice: ToolChoiceConfig) -> Self {
322 self.generation_config
323 .get_or_insert_with(Default::default)
324 .tool_choice = Some(choice);
325 self
326 }
327
328 pub fn with_speech_config(mut self, config: InteractionSpeechConfig) -> Self {
329 self.generation_config
330 .get_or_insert_with(Default::default)
331 .speech_config
332 .push(config);
333 self
334 }
335
336 pub fn with_video_config(mut self, config: VideoConfig) -> Self {
337 self.generation_config
338 .get_or_insert_with(Default::default)
339 .video_config = Some(config);
340 self
341 }
342
343 pub fn with_generation_config(mut self, config: InteractionGenerationConfig) -> Self {
345 self.generation_config = Some(config);
346 self
347 }
348
349 pub fn with_agent_config(mut self, config: AgentConfig) -> Self {
353 self.agent_config = Some(config);
354 self
355 }
356
357 pub fn with_previous_interaction(mut self, id: impl Into<String>) -> Self {
361 self.previous_interaction_id = Some(id.into());
362 self
363 }
364
365 pub fn with_store(mut self, store: bool) -> Self {
367 self.store = Some(store);
368 self
369 }
370
371 pub fn with_background(mut self) -> Self {
373 self.background = true;
374 self
375 }
376
377 pub fn with_environment(mut self, env: EnvironmentConfig) -> Self {
379 self.environment = Some(EnvironmentConfigOrString::Config(env));
380 self
381 }
382
383 pub fn with_environment_id(mut self, env_id: impl Into<String>) -> Self {
385 self.environment = Some(EnvironmentConfigOrString::Id(env_id.into()));
386 self
387 }
388
389 pub fn with_cached_content(mut self, cached_content: impl Into<String>) -> Self {
391 self.cached_content = Some(cached_content.into());
392 self
393 }
394
395 pub fn with_response_modalities(mut self, modalities: Vec<ResponseModality>) -> Self {
397 self.response_modalities = modalities;
398 self
399 }
400
401 pub fn with_service_tier(mut self, tier: ServiceTier) -> Self {
403 self.service_tier = Some(tier);
404 self
405 }
406
407 pub fn with_webhook_config(mut self, config: WebhookConfig) -> Self {
409 self.webhook_config = Some(config);
410 self
411 }
412
413 pub fn build(self) -> Result<CreateInteractionRequest, ClientError> {
417 let input = self.input.ok_or_else(|| ClientError::InvalidResourceName {
418 name: "input is required for interaction".to_string(),
419 })?;
420
421 let model = if self.model.is_none() && self.agent.is_none() {
422 Some(
423 self.client
424 .model
425 .as_str()
426 .trim_start_matches("models/")
427 .to_string(),
428 )
429 } else {
430 self.model
431 };
432
433 Ok(CreateInteractionRequest {
434 model,
435 agent: self.agent,
436 input,
437 system_instruction: self.system_instruction,
438 tools: self.tools,
439 response_format: self.response_format,
440 stream: None,
441 store: self.store,
442 background: if self.background { Some(true) } else { None },
443 generation_config: self.generation_config,
444 agent_config: self.agent_config,
445 previous_interaction_id: self.previous_interaction_id,
446 environment: self.environment,
447 cached_content: self.cached_content,
448 response_modalities: self.response_modalities,
449 service_tier: self.service_tier,
450 webhook_config: self.webhook_config,
451 })
452 }
453
454 #[instrument(skip_all, fields(
456 model = self.model.as_deref().unwrap_or(""),
457 agent = self.agent.as_deref().unwrap_or(""),
458 tools.count = self.tools.len(),
459 system.instruction.present = self.system_instruction.is_some(),
460 background = self.background,
461 previous.interaction.present = self.previous_interaction_id.is_some(),
462 status.code,
463 usage.total_tokens,
464 ))]
465 pub async fn execute(self) -> Result<Interaction, ClientError> {
466 let client = self.client.clone();
467 let request = self.build()?;
468 let response = client.create_interaction(request).await?;
469
470 Span::current().record("status.code", response.status.as_ref());
471
472 if let Some(usage) = &response.usage {
473 Span::current().record("usage.total_tokens", usage.total_tokens);
474 }
475
476 Ok(response)
477 }
478
479 #[instrument(skip_all, fields(
481 model = self.model.as_deref().unwrap_or(""),
482 agent = self.agent.as_deref().unwrap_or(""),
483 tools.count = self.tools.len(),
484 ))]
485 pub async fn execute_stream(self) -> Result<InteractionStream, ClientError> {
486 let client = self.client.clone();
487 let request = self.build()?;
488 client.create_interaction_stream(request).await
489 }
490}