1use futures::TryStream;
2use std::sync::Arc;
3use tracing::instrument;
4
5use crate::{
6 cache::CachedContentHandle,
7 client::{Error as ClientError, GeminiClient},
8 generation::{GenerateContentRequest, SpeakerVoiceConfig, SpeechConfig, ThinkingConfig},
9 tools::{FunctionCallingConfig, ToolConfig},
10 Content, FunctionCallingMode, FunctionDeclaration, GenerationConfig, GenerationResponse,
11 Message, Role, Tool,
12};
13
14pub struct ContentBuilder {
16 client: Arc<GeminiClient>,
17 pub contents: Vec<Content>,
18 generation_config: Option<GenerationConfig>,
19 tools: Option<Vec<Tool>>,
20 tool_config: Option<ToolConfig>,
21 system_instruction: Option<Content>,
22 cached_content: Option<String>,
23}
24
25impl ContentBuilder {
26 pub(crate) fn new(client: Arc<GeminiClient>) -> Self {
28 Self {
29 client,
30 contents: Vec::new(),
31 generation_config: None,
32 tools: None,
33 tool_config: None,
34 system_instruction: None,
35 cached_content: None,
36 }
37 }
38
39 pub fn with_system_prompt(self, text: impl Into<String>) -> Self {
43 self.with_system_instruction(text)
44 }
45
46 pub fn with_system_instruction(mut self, text: impl Into<String>) -> Self {
51 let content = Content::text(text);
52 self.system_instruction = Some(content);
53 self
54 }
55
56 pub fn with_user_message(mut self, text: impl Into<String>) -> Self {
58 let message = Message::user(text);
59 self.contents.push(message.content);
60 self
61 }
62
63 pub fn with_model_message(mut self, text: impl Into<String>) -> Self {
65 let message = Message::model(text);
66 self.contents.push(message.content);
67 self
68 }
69
70 pub fn with_inline_data(
74 mut self,
75 data: impl Into<String>,
76 mime_type: impl Into<String>,
77 ) -> Self {
78 let content = Content::inline_data(mime_type, data).with_role(Role::User);
79 self.contents.push(content);
80 self
81 }
82
83 pub fn with_function_response<Response>(
87 mut self,
88 name: impl Into<String>,
89 response: Response,
90 ) -> std::result::Result<Self, serde_json::Error>
91 where
92 Response: serde::Serialize,
93 {
94 let content = Content::function_response_json(name, serde_json::to_value(response)?)
95 .with_role(Role::User);
96 self.contents.push(content);
97 Ok(self)
98 }
99
100 pub fn with_function_response_str(
104 mut self,
105 name: impl Into<String>,
106 response: impl Into<String>,
107 ) -> std::result::Result<Self, serde_json::Error> {
108 let response_str = response.into();
109 let json = serde_json::from_str(&response_str)?;
110 let content = Content::function_response_json(name, json).with_role(Role::User);
111 self.contents.push(content);
112 Ok(self)
113 }
114
115 pub fn with_message(mut self, message: Message) -> Self {
117 let content = message.content.clone();
118 let role = content.role.clone().unwrap_or(message.role);
119 self.contents.push(content.with_role(role));
120 self
121 }
122
123 pub fn with_cached_content(mut self, cached_content: &CachedContentHandle) -> Self {
128 self.cached_content = Some(cached_content.name().to_string());
129 self
130 }
131
132 pub fn with_messages(mut self, messages: impl IntoIterator<Item = Message>) -> Self {
134 for message in messages {
135 self = self.with_message(message);
136 }
137 self
138 }
139
140 pub fn with_generation_config(mut self, config: GenerationConfig) -> Self {
142 self.generation_config = Some(config);
143 self
144 }
145
146 pub fn with_temperature(mut self, temperature: f32) -> Self {
151 self.generation_config
152 .get_or_insert_with(Default::default)
153 .temperature = Some(temperature);
154 self
155 }
156
157 pub fn with_top_p(mut self, top_p: f32) -> Self {
162 self.generation_config
163 .get_or_insert_with(Default::default)
164 .top_p = Some(top_p);
165 self
166 }
167
168 pub fn with_top_k(mut self, top_k: i32) -> Self {
172 self.generation_config
173 .get_or_insert_with(Default::default)
174 .top_k = Some(top_k);
175 self
176 }
177
178 pub fn with_max_output_tokens(mut self, max_output_tokens: i32) -> Self {
180 self.generation_config
181 .get_or_insert_with(Default::default)
182 .max_output_tokens = Some(max_output_tokens);
183 self
184 }
185
186 pub fn with_candidate_count(mut self, candidate_count: i32) -> Self {
188 self.generation_config
189 .get_or_insert_with(Default::default)
190 .candidate_count = Some(candidate_count);
191 self
192 }
193
194 pub fn with_stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
198 self.generation_config
199 .get_or_insert_with(Default::default)
200 .stop_sequences = Some(stop_sequences);
201 self
202 }
203
204 pub fn with_response_mime_type(mut self, mime_type: impl Into<String>) -> Self {
208 self.generation_config
209 .get_or_insert_with(Default::default)
210 .response_mime_type = Some(mime_type.into());
211 self
212 }
213
214 pub fn with_response_schema(mut self, schema: serde_json::Value) -> Self {
219 self.generation_config
220 .get_or_insert_with(Default::default)
221 .response_schema = Some(schema);
222 self
223 }
224
225 pub fn with_tool(mut self, tool: Tool) -> Self {
229 self.tools.get_or_insert_with(Vec::new).push(tool);
230 self
231 }
232
233 pub fn with_function(mut self, function: FunctionDeclaration) -> Self {
237 let tool = Tool::new(function);
238 self = self.with_tool(tool);
239 self
240 }
241
242 pub fn with_function_calling_mode(mut self, mode: FunctionCallingMode) -> Self {
244 self.tool_config
245 .get_or_insert_with(Default::default)
246 .function_calling_config = Some(FunctionCallingConfig { mode });
247 self
248 }
249
250 pub fn with_thinking_config(mut self, thinking_config: ThinkingConfig) -> Self {
252 self.generation_config
253 .get_or_insert_with(Default::default)
254 .thinking_config = Some(thinking_config);
255 self
256 }
257
258 pub fn with_thinking_budget(mut self, budget: i32) -> Self {
262 self.generation_config
263 .get_or_insert_with(Default::default)
264 .thinking_config
265 .get_or_insert_with(Default::default)
266 .thinking_budget = Some(budget);
267 self
268 }
269
270 pub fn with_dynamic_thinking(self) -> Self {
276 self.with_thinking_budget(-1)
277 }
278
279 pub fn with_thoughts_included(mut self, include: bool) -> Self {
283 self.generation_config
284 .get_or_insert_with(Default::default)
285 .thinking_config
286 .get_or_insert_with(Default::default)
287 .include_thoughts = Some(include);
288 self
289 }
290
291 pub fn with_audio_output(mut self) -> Self {
293 self.generation_config
294 .get_or_insert_with(Default::default)
295 .response_modalities = Some(vec!["AUDIO".to_string()]);
296 self
297 }
298
299 pub fn with_speech_config(mut self, speech_config: SpeechConfig) -> Self {
301 self.generation_config
302 .get_or_insert_with(Default::default)
303 .speech_config = Some(speech_config);
304 self
305 }
306
307 pub fn with_voice(self, voice_name: impl Into<String>) -> Self {
309 let speech_config = SpeechConfig::single_voice(voice_name);
310 self.with_speech_config(speech_config).with_audio_output()
311 }
312
313 pub fn with_multi_speaker_config(self, speakers: Vec<SpeakerVoiceConfig>) -> Self {
315 let speech_config = SpeechConfig::multi_speaker(speakers);
316 self.with_speech_config(speech_config).with_audio_output()
317 }
318
319 pub fn build(self) -> GenerateContentRequest {
321 GenerateContentRequest {
322 contents: self.contents,
323 generation_config: self.generation_config,
324 safety_settings: None,
325 tools: self.tools,
326 tool_config: self.tool_config,
327 system_instruction: self.system_instruction,
328 cached_content: self.cached_content,
329 }
330 }
331
332 #[instrument(skip_all, fields(
334 messages.parts.count = self.contents.len(),
335 tools.present = self.tools.is_some(),
336 system.instruction.present = self.system_instruction.is_some(),
337 cached.content.present = self.cached_content.is_some(),
338 ))]
339 pub async fn execute(self) -> Result<GenerationResponse, ClientError> {
340 let client = self.client.clone();
341 let request = self.build();
342 client.generate_content_raw(request).await
343 }
344
345 #[instrument(skip_all, fields(
347 messages.parts.count = self.contents.len(),
348 tools.present = self.tools.is_some(),
349 system.instruction.present = self.system_instruction.is_some(),
350 cached.content.present = self.cached_content.is_some(),
351 ))]
352 pub async fn execute_stream(
353 self,
354 ) -> Result<impl TryStream<Ok = GenerationResponse, Error = ClientError> + Send, ClientError>
355 {
356 let client = self.client.clone();
357 let request = self.build();
358 client.generate_content_stream(request).await
359 }
360}