1use std::sync::Arc;
2use tracing::instrument;
3
4use crate::{
5 cache::CachedContentHandle,
6 client::{Error as ClientError, GeminiClient, GenerationStream},
7 files::Error as FilesError,
8 generation::{
9 GenerateContentRequest, ImageConfig, MediaResolutionLevel, SpeakerVoiceConfig,
10 SpeechConfig, ThinkingConfig, ThinkingLevel,
11 },
12 tools::{FunctionCallingConfig, ToolConfig},
13 Content, FileHandle, FunctionCallingMode, FunctionDeclaration, GenerationConfig,
14 GenerationResponse, Message, Role, SafetySetting, Tool,
15};
16
17#[deprecated(
19 since = "1.8.0",
20 note = "Use crate::interactions::InteractionBuilder instead. See migration guide: interactions-api/migration-plan.md"
21)]
22#[derive(Clone)]
23pub struct ContentBuilder {
24 client: Arc<GeminiClient>,
25 pub contents: Vec<Content>,
26 generation_config: Option<GenerationConfig>,
27 safety_settings: Option<Vec<SafetySetting>>,
28 tools: Option<Vec<Tool>>,
29 tool_config: Option<ToolConfig>,
30 system_instruction: Option<Content>,
31 cached_content: Option<String>,
32}
33
34impl ContentBuilder {
35 pub(crate) fn new(client: Arc<GeminiClient>) -> Self {
37 Self {
38 client,
39 contents: Vec::new(),
40 generation_config: None,
41 safety_settings: None,
42 tools: None,
43 tool_config: None,
44 system_instruction: None,
45 cached_content: None,
46 }
47 }
48
49 pub fn with_safety_settings(mut self, safety_settings: Vec<SafetySetting>) -> Self {
51 self.safety_settings = Some(safety_settings);
52 self
53 }
54
55 pub fn with_system_prompt(self, text: impl Into<String>) -> Self {
59 self.with_system_instruction(text)
60 }
61
62 pub fn with_system_instruction(mut self, text: impl Into<String>) -> Self {
67 let content = Content::text(text);
68 self.system_instruction = Some(content);
69 self
70 }
71
72 pub fn with_user_message(mut self, text: impl Into<String>) -> Self {
74 let message = Message::user(text);
75 self.contents.push(message.content);
76 self
77 }
78
79 pub fn with_user_message_and_file(
88 mut self,
89 text: impl Into<String>,
90 file_handle: &FileHandle,
91 ) -> Result<Self, FilesError> {
92 let content = Content::text_with_file(text, file_handle)?.with_role(Role::User);
93 self.contents.push(content);
94 Ok(self)
95 }
96
97 pub fn with_model_message(mut self, text: impl Into<String>) -> Self {
99 let message = Message::model(text);
100 self.contents.push(message.content);
101 self
102 }
103
104 pub fn with_inline_data(
108 mut self,
109 data: impl Into<String>,
110 mime_type: impl Into<String>,
111 ) -> Self {
112 let content = Content::inline_data(mime_type, data).with_role(Role::User);
113 self.contents.push(content);
114 self
115 }
116
117 pub fn with_inline_data_and_resolution(
124 mut self,
125 data: impl Into<String>,
126 mime_type: impl Into<String>,
127 resolution: MediaResolutionLevel,
128 ) -> Self {
129 let content =
130 Content::inline_data_with_resolution(mime_type, data, resolution).with_role(Role::User);
131 self.contents.push(content);
132 self
133 }
134
135 pub fn with_function_response<Response>(
139 mut self,
140 name: impl Into<String>,
141 response: Response,
142 ) -> std::result::Result<Self, serde_json::Error>
143 where
144 Response: serde::Serialize,
145 {
146 let content = Content::function_response_json(name, serde_json::to_value(response)?)
147 .with_role(Role::User);
148 self.contents.push(content);
149 Ok(self)
150 }
151
152 pub fn with_function_response_str(
156 mut self,
157 name: impl Into<String>,
158 response: impl Into<String>,
159 ) -> std::result::Result<Self, serde_json::Error> {
160 let response_str = response.into();
161 let json = serde_json::from_str(&response_str)?;
162 let content = Content::function_response_json(name, json).with_role(Role::User);
163 self.contents.push(content);
164 Ok(self)
165 }
166
167 pub fn with_message(mut self, message: Message) -> Self {
169 let content = message.content.clone();
170 let role = content.role.clone().unwrap_or(message.role);
171 self.contents.push(content.with_role(role));
172 self
173 }
174
175 pub fn with_cached_content(mut self, cached_content: &CachedContentHandle) -> Self {
180 self.cached_content = Some(cached_content.name().to_string());
181 self
182 }
183
184 pub fn with_messages(mut self, messages: impl IntoIterator<Item = Message>) -> Self {
186 for message in messages {
187 self = self.with_message(message);
188 }
189 self
190 }
191
192 pub fn with_generation_config(mut self, config: GenerationConfig) -> Self {
194 self.generation_config = Some(config);
195 self
196 }
197
198 pub fn with_temperature(mut self, temperature: f32) -> Self {
203 self.generation_config
204 .get_or_insert_with(Default::default)
205 .temperature = Some(temperature);
206 self
207 }
208
209 pub fn with_top_p(mut self, top_p: f32) -> Self {
214 self.generation_config
215 .get_or_insert_with(Default::default)
216 .top_p = Some(top_p);
217 self
218 }
219
220 pub fn with_top_k(mut self, top_k: i32) -> Self {
224 self.generation_config
225 .get_or_insert_with(Default::default)
226 .top_k = Some(top_k);
227 self
228 }
229
230 pub fn with_seed(mut self, seed: i32) -> Self {
232 self.generation_config
233 .get_or_insert_with(Default::default)
234 .seed = Some(seed);
235 self
236 }
237
238 pub fn with_max_output_tokens(mut self, max_output_tokens: i32) -> Self {
240 self.generation_config
241 .get_or_insert_with(Default::default)
242 .max_output_tokens = Some(max_output_tokens);
243 self
244 }
245
246 pub fn with_candidate_count(mut self, candidate_count: i32) -> Self {
248 self.generation_config
249 .get_or_insert_with(Default::default)
250 .candidate_count = Some(candidate_count);
251 self
252 }
253
254 pub fn with_stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
258 self.generation_config
259 .get_or_insert_with(Default::default)
260 .stop_sequences = Some(stop_sequences);
261 self
262 }
263
264 pub fn with_response_mime_type(mut self, mime_type: impl Into<String>) -> Self {
268 self.generation_config
269 .get_or_insert_with(Default::default)
270 .response_mime_type = Some(mime_type.into());
271 self
272 }
273
274 pub fn with_response_schema(mut self, schema: serde_json::Value) -> Self {
279 let config = self.generation_config.get_or_insert_with(Default::default);
280 config.response_schema = Some(schema);
281 config.response_json_schema = None;
282 self
283 }
284
285 pub fn with_response_json_schema(mut self, schema: serde_json::Value) -> Self {
289 let config = self.generation_config.get_or_insert_with(Default::default);
290 config.response_json_schema = Some(schema);
291 config.response_schema = None;
292 self
293 }
294
295 pub fn with_tool(mut self, tool: Tool) -> Self {
299 self.tools.get_or_insert_with(Vec::new).push(tool);
300 self
301 }
302
303 pub fn with_function(mut self, function: FunctionDeclaration) -> Self {
307 let tool = Tool::new(function);
308 self = self.with_tool(tool);
309 self
310 }
311
312 pub fn with_function_calling_mode(mut self, mode: FunctionCallingMode) -> Self {
314 self.tool_config
315 .get_or_insert_with(Default::default)
316 .function_calling_config = Some(FunctionCallingConfig { mode });
317 self
318 }
319
320 pub fn with_tool_config(mut self, tool_config: ToolConfig) -> Self {
322 self.tool_config = Some(tool_config);
323 self
324 }
325
326 pub fn with_thinking_config(mut self, thinking_config: ThinkingConfig) -> Self {
328 self.generation_config
329 .get_or_insert_with(Default::default)
330 .thinking_config = Some(thinking_config);
331 self
332 }
333
334 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
339 let config = self
340 .generation_config
341 .get_or_insert_with(Default::default)
342 .thinking_config
343 .get_or_insert_with(Default::default);
344 config.thinking_budget = Some(budget);
345 config.thinking_level = None;
346 self
347 }
348
349 pub fn with_dynamic_thinking(self) -> Self {
355 self.with_thinking_budget(-1)
356 }
357
358 pub fn with_thoughts_included(mut self, include: bool) -> Self {
362 self.generation_config
363 .get_or_insert_with(Default::default)
364 .thinking_config
365 .get_or_insert_with(Default::default)
366 .include_thoughts = Some(include);
367 self
368 }
369
370 pub fn with_thinking_level(mut self, level: ThinkingLevel) -> Self {
379 let config = self
380 .generation_config
381 .get_or_insert_with(Default::default)
382 .thinking_config
383 .get_or_insert_with(Default::default);
384 config.thinking_level = Some(level);
385 config.thinking_budget = None;
386 self
387 }
388
389 pub fn with_media_resolution(mut self, level: MediaResolutionLevel) -> Self {
395 self.generation_config
396 .get_or_insert_with(Default::default)
397 .media_resolution = Some(level);
398 self
399 }
400
401 pub fn with_code_execution(self) -> Self {
407 self.with_tool(Tool::code_execution())
408 }
409
410 pub fn with_audio_output(mut self) -> Self {
412 self.generation_config
413 .get_or_insert_with(Default::default)
414 .response_modalities = Some(vec!["AUDIO".to_string()]);
415 self
416 }
417
418 pub fn with_image_config(mut self, image_config: ImageConfig) -> Self {
420 self.generation_config
421 .get_or_insert_with(Default::default)
422 .image_config = Some(image_config);
423 self
424 }
425
426 pub fn with_speech_config(mut self, speech_config: SpeechConfig) -> Self {
428 self.generation_config
429 .get_or_insert_with(Default::default)
430 .speech_config = Some(speech_config);
431 self
432 }
433
434 pub fn with_voice(self, voice_name: impl Into<String>) -> Self {
436 let speech_config = SpeechConfig::single_voice(voice_name);
437 self.with_speech_config(speech_config).with_audio_output()
438 }
439
440 pub fn with_multi_speaker_config(self, speakers: Vec<SpeakerVoiceConfig>) -> Self {
442 let speech_config = SpeechConfig::multi_speaker(speakers);
443 self.with_speech_config(speech_config).with_audio_output()
444 }
445
446 pub fn build(self) -> GenerateContentRequest {
448 GenerateContentRequest {
449 contents: self.contents,
450 generation_config: self.generation_config,
451 safety_settings: self.safety_settings,
452 tools: self.tools,
453 tool_config: self.tool_config,
454 system_instruction: self.system_instruction,
455 cached_content: self.cached_content,
456 }
457 }
458
459 #[instrument(skip_all, fields(
461 messages.parts.count = self.contents.len(),
462 tools.present = self.tools.is_some(),
463 system.instruction.present = self.system_instruction.is_some(),
464 cached.content.present = self.cached_content.is_some(),
465 ))]
466 pub async fn execute(self) -> Result<GenerationResponse, ClientError> {
467 let client = self.client.clone();
468 let request = self.build();
469 client.generate_content_raw(request).await
470 }
471
472 #[instrument(skip_all, fields(
474 messages.parts.count = self.contents.len(),
475 tools.present = self.tools.is_some(),
476 system.instruction.present = self.system_instruction.is_some(),
477 cached.content.present = self.cached_content.is_some(),
478 ))]
479 pub async fn execute_stream(self) -> Result<GenerationStream, ClientError> {
480 let client = self.client.clone();
481 let request = self.build();
482 client.generate_content_stream(request).await
483 }
484
485 #[instrument(skip_all, fields(
487 messages.parts.count = self.contents.len(),
488 ))]
489 pub async fn count_tokens(self) -> Result<super::model::CountTokensResponse, ClientError> {
490 let client = self.client.clone();
491 let request = self.build();
492 client.count_tokens(request).await
493 }
494}