1#![allow(clippy::enum_variant_names)]
28
29use serde::{Deserialize, Serialize};
30
31use crate::{File, FileHandle, FilesError};
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
35#[serde(rename_all = "lowercase")]
36pub enum Role {
37 User,
39 Model,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
45#[serde(untagged)]
46pub enum Part {
47 Text {
49 text: String,
51 #[serde(skip_serializing_if = "Option::is_none")]
53 thought: Option<bool>,
54 #[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
56 thought_signature: Option<String>,
57 },
58 InlineData {
59 #[serde(rename = "inlineData")]
61 inline_data: Blob,
62 #[serde(skip_serializing_if = "Option::is_none")]
65 media_resolution: Option<super::generation::model::MediaResolution>,
66 },
67 FunctionCall {
69 #[serde(rename = "functionCall")]
71 function_call: super::tools::FunctionCall,
72 #[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
74 thought_signature: Option<String>,
75 },
76 FunctionResponse {
78 #[serde(rename = "functionResponse")]
80 function_response: super::tools::FunctionResponse,
81 },
82 ToolCall {
84 #[serde(rename = "toolCall")]
85 tool_call: serde_json::Value,
86 #[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
87 thought_signature: Option<String>,
88 },
89 ToolResponse {
91 #[serde(rename = "toolResponse")]
92 tool_response: serde_json::Value,
93 #[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
94 thought_signature: Option<String>,
95 },
96 FileData {
98 #[serde(rename = "fileData")]
99 file_data: FileData,
100 },
101 ExecutableCode {
103 #[serde(rename = "executableCode")]
105 executable_code: super::tools::ExecutableCode,
106 },
107 CodeExecutionResult {
109 #[serde(rename = "codeExecutionResult")]
111 code_execution_result: super::tools::CodeExecutionResult,
112 },
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
125#[serde(rename_all = "camelCase")]
126pub struct FileData {
127 pub mime_type: String,
129 pub file_uri: String,
131}
132
133impl TryFrom<&FileHandle> for FileData {
134 type Error = FilesError;
135
136 fn try_from(file_handle: &FileHandle) -> Result<Self, Self::Error> {
137 let File { mime_type, uri, .. } = file_handle.get_file_meta();
138
139 let none_fields: Vec<_> = [
140 mime_type.is_none().then_some("mime_type"),
141 uri.is_none().then_some("uri"),
142 ]
143 .into_iter()
144 .flatten()
145 .map(String::from)
146 .collect();
147
148 if !none_fields.is_empty() {
149 return Err(FilesError::Incomplete {
150 fields: none_fields,
151 });
152 }
153
154 Ok(Self {
155 mime_type: mime_type
156 .as_ref()
157 .expect("Some-ness checked above")
158 .to_string(),
159 file_uri: uri.as_ref().expect("Some-ness checked above").to_string(),
160 })
161 }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
166#[serde(rename_all = "camelCase")]
167pub struct Blob {
168 pub mime_type: String,
170 pub data: String,
172}
173
174impl Blob {
175 pub fn new(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
177 Self {
178 mime_type: mime_type.into(),
179 data: data.into(),
180 }
181 }
182}
183
184#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
186#[serde(rename_all = "camelCase")]
187pub struct Content {
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub parts: Option<Vec<Part>>,
191 #[serde(skip_serializing_if = "Option::is_none")]
193 pub role: Option<Role>,
194}
195
196impl Content {
197 pub fn text(text: impl Into<String>) -> Self {
199 Self {
200 parts: Some(vec![Part::Text {
201 text: text.into(),
202 thought: None,
203 thought_signature: None,
204 }]),
205 role: None,
206 }
207 }
208
209 pub fn function_call(mut function_call: super::tools::FunctionCall) -> Self {
211 let thought_signature = function_call.thought_signature.take();
212 Self {
213 parts: Some(vec![Part::FunctionCall {
214 function_call,
215 thought_signature,
216 }]),
217 role: None,
218 }
219 }
220
221 pub fn function_call_with_thought(
223 mut function_call: super::tools::FunctionCall,
224 thought_signature: impl Into<String>,
225 ) -> Self {
226 function_call.thought_signature = None;
227 Self {
228 parts: Some(vec![Part::FunctionCall {
229 function_call,
230 thought_signature: Some(thought_signature.into()),
231 }]),
232 role: None,
233 }
234 }
235
236 pub fn text_with_thought_signature(
238 text: impl Into<String>,
239 thought_signature: impl Into<String>,
240 ) -> Self {
241 Self {
242 parts: Some(vec![Part::Text {
243 text: text.into(),
244 thought: None,
245 thought_signature: Some(thought_signature.into()),
246 }]),
247 role: None,
248 }
249 }
250
251 pub fn thought_with_signature(
253 text: impl Into<String>,
254 thought_signature: impl Into<String>,
255 ) -> Self {
256 Self {
257 parts: Some(vec![Part::Text {
258 text: text.into(),
259 thought: Some(true),
260 thought_signature: Some(thought_signature.into()),
261 }]),
262 role: None,
263 }
264 }
265
266 pub fn function_response(function_response: super::tools::FunctionResponse) -> Self {
268 Self {
269 parts: Some(vec![Part::FunctionResponse { function_response }]),
270 role: None,
271 }
272 }
273
274 pub fn function_response_json(name: impl Into<String>, response: serde_json::Value) -> Self {
276 Self {
277 parts: Some(vec![Part::FunctionResponse {
278 function_response: super::tools::FunctionResponse::new(name, response),
279 }]),
280 role: None,
281 }
282 }
283
284 pub fn inline_data(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
286 Self {
287 parts: Some(vec![Part::InlineData {
288 inline_data: Blob::new(mime_type, data),
289 media_resolution: None,
290 }]),
291 role: None,
292 }
293 }
294
295 pub fn inline_data_with_resolution(
297 mime_type: impl Into<String>,
298 data: impl Into<String>,
299 resolution: super::generation::model::MediaResolutionLevel,
300 ) -> Self {
301 Self {
302 parts: Some(vec![Part::InlineData {
303 inline_data: Blob::new(mime_type, data),
304 media_resolution: Some(super::generation::model::MediaResolution {
305 level: resolution,
306 }),
307 }]),
308 role: None,
309 }
310 }
311
312 pub fn text_with_file(
314 text: impl Into<String>,
315 file_handle: &FileHandle,
316 ) -> Result<Self, FilesError> {
317 Ok(Self {
318 parts: Some(vec![
319 Part::Text {
320 text: text.into(),
321 thought: None,
322 thought_signature: None,
323 },
324 Part::FileData {
325 file_data: FileData::try_from(file_handle)?,
326 },
327 ]),
328 role: None,
329 })
330 }
331
332 pub fn with_role(mut self, role: Role) -> Self {
334 self.role = Some(role);
335 self
336 }
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct Message {
342 pub content: Content,
344 pub role: Role,
346}
347
348impl Message {
349 pub fn user(text: impl Into<String>) -> Self {
351 Self {
352 content: Content::text(text).with_role(Role::User),
353 role: Role::User,
354 }
355 }
356
357 pub fn model(text: impl Into<String>) -> Self {
359 Self {
360 content: Content::text(text).with_role(Role::Model),
361 role: Role::Model,
362 }
363 }
364
365 pub fn embed(text: impl Into<String>) -> Self {
367 Self {
368 content: Content::text(text),
369 role: Role::Model,
370 }
371 }
372
373 pub fn function(name: impl Into<String>, response: serde_json::Value) -> Self {
375 Self {
376 content: Content::function_response_json(name, response).with_role(Role::Model),
377 role: Role::Model,
378 }
379 }
380
381 pub fn function_str(
383 name: impl Into<String>,
384 response: impl Into<String>,
385 ) -> Result<Self, serde_json::Error> {
386 let response_str = response.into();
387 let json = serde_json::from_str(&response_str)?;
388 Ok(Self {
389 content: Content::function_response_json(name, json).with_role(Role::Model),
390 role: Role::Model,
391 })
392 }
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
397#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
398pub enum Modality {
399 ModalityUnspecified,
401 Document,
403 Text,
405 Image,
407 Audio,
409 Video,
411 #[serde(untagged)]
412 Other(String),
413}