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 FileData {
84 #[serde(rename = "fileData")]
85 file_data: FileData,
86 },
87 ExecutableCode {
89 #[serde(rename = "executableCode")]
91 executable_code: super::tools::ExecutableCode,
92 },
93 CodeExecutionResult {
95 #[serde(rename = "codeExecutionResult")]
97 code_execution_result: super::tools::CodeExecutionResult,
98 },
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
111#[serde(rename_all = "camelCase")]
112pub struct FileData {
113 pub mime_type: String,
115 pub file_uri: String,
117}
118
119impl TryFrom<&FileHandle> for FileData {
120 type Error = FilesError;
121
122 fn try_from(file_handle: &FileHandle) -> Result<Self, Self::Error> {
123 let File { mime_type, uri, .. } = file_handle.get_file_meta();
124
125 let none_fields: Vec<_> = [
126 mime_type.is_none().then_some("mime_type"),
127 uri.is_none().then_some("uri"),
128 ]
129 .into_iter()
130 .flatten()
131 .map(String::from)
132 .collect();
133
134 if !none_fields.is_empty() {
135 return Err(FilesError::Incomplete {
136 fields: none_fields,
137 });
138 }
139
140 Ok(Self {
141 mime_type: mime_type.as_ref().expect("Some-ness checked above").clone(),
142 file_uri: uri.as_ref().expect("Some-ness checked above").to_string(),
143 })
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
149#[serde(rename_all = "camelCase")]
150pub struct Blob {
151 pub mime_type: String,
153 pub data: String,
155}
156
157impl Blob {
158 pub fn new(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
160 Self {
161 mime_type: mime_type.into(),
162 data: data.into(),
163 }
164 }
165}
166
167#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
169#[serde(rename_all = "camelCase")]
170pub struct Content {
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub parts: Option<Vec<Part>>,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub role: Option<Role>,
177}
178
179impl Content {
180 pub fn text(text: impl Into<String>) -> Self {
182 Self {
183 parts: Some(vec![Part::Text {
184 text: text.into(),
185 thought: None,
186 thought_signature: None,
187 }]),
188 role: None,
189 }
190 }
191
192 pub fn function_call(function_call: super::tools::FunctionCall) -> Self {
194 Self {
195 parts: Some(vec![Part::FunctionCall {
196 function_call,
197 thought_signature: None,
198 }]),
199 role: None,
200 }
201 }
202
203 pub fn function_call_with_thought(
205 function_call: super::tools::FunctionCall,
206 thought_signature: impl Into<String>,
207 ) -> Self {
208 Self {
209 parts: Some(vec![Part::FunctionCall {
210 function_call,
211 thought_signature: Some(thought_signature.into()),
212 }]),
213 role: None,
214 }
215 }
216
217 pub fn text_with_thought_signature(
219 text: impl Into<String>,
220 thought_signature: impl Into<String>,
221 ) -> Self {
222 Self {
223 parts: Some(vec![Part::Text {
224 text: text.into(),
225 thought: None,
226 thought_signature: Some(thought_signature.into()),
227 }]),
228 role: None,
229 }
230 }
231
232 pub fn thought_with_signature(
234 text: impl Into<String>,
235 thought_signature: impl Into<String>,
236 ) -> Self {
237 Self {
238 parts: Some(vec![Part::Text {
239 text: text.into(),
240 thought: Some(true),
241 thought_signature: Some(thought_signature.into()),
242 }]),
243 role: None,
244 }
245 }
246
247 pub fn function_response(function_response: super::tools::FunctionResponse) -> Self {
249 Self {
250 parts: Some(vec![Part::FunctionResponse { function_response }]),
251 role: None,
252 }
253 }
254
255 pub fn function_response_json(name: impl Into<String>, response: serde_json::Value) -> Self {
257 Self {
258 parts: Some(vec![Part::FunctionResponse {
259 function_response: super::tools::FunctionResponse::new(name, response),
260 }]),
261 role: None,
262 }
263 }
264
265 pub fn inline_data(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
267 Self {
268 parts: Some(vec![Part::InlineData {
269 inline_data: Blob::new(mime_type, data),
270 media_resolution: None,
271 }]),
272 role: None,
273 }
274 }
275
276 pub fn inline_data_with_resolution(
278 mime_type: impl Into<String>,
279 data: impl Into<String>,
280 resolution: super::generation::model::MediaResolutionLevel,
281 ) -> Self {
282 Self {
283 parts: Some(vec![Part::InlineData {
284 inline_data: Blob::new(mime_type, data),
285 media_resolution: Some(super::generation::model::MediaResolution {
286 level: resolution,
287 }),
288 }]),
289 role: None,
290 }
291 }
292
293 pub fn text_with_file(
295 text: impl Into<String>,
296 file_handle: &FileHandle,
297 ) -> Result<Self, FilesError> {
298 Ok(Self {
299 parts: Some(vec![
300 Part::Text {
301 text: text.into(),
302 thought: None,
303 thought_signature: None,
304 },
305 Part::FileData {
306 file_data: FileData::try_from(file_handle)?,
307 },
308 ]),
309 role: None,
310 })
311 }
312
313 pub fn with_role(mut self, role: Role) -> Self {
315 self.role = Some(role);
316 self
317 }
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct Message {
323 pub content: Content,
325 pub role: Role,
327}
328
329impl Message {
330 pub fn user(text: impl Into<String>) -> Self {
332 Self {
333 content: Content::text(text).with_role(Role::User),
334 role: Role::User,
335 }
336 }
337
338 pub fn model(text: impl Into<String>) -> Self {
340 Self {
341 content: Content::text(text).with_role(Role::Model),
342 role: Role::Model,
343 }
344 }
345
346 pub fn embed(text: impl Into<String>) -> Self {
348 Self {
349 content: Content::text(text),
350 role: Role::Model,
351 }
352 }
353
354 pub fn function(name: impl Into<String>, response: serde_json::Value) -> Self {
356 Self {
357 content: Content::function_response_json(name, response).with_role(Role::Model),
358 role: Role::Model,
359 }
360 }
361
362 pub fn function_str(
364 name: impl Into<String>,
365 response: impl Into<String>,
366 ) -> Result<Self, serde_json::Error> {
367 let response_str = response.into();
368 let json = serde_json::from_str(&response_str)?;
369 Ok(Self {
370 content: Content::function_response_json(name, json).with_role(Role::Model),
371 role: Role::Model,
372 })
373 }
374}
375
376#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
378#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
379pub enum Modality {
380 ModalityUnspecified,
382 Document,
384 Text,
386 Image,
388 Audio,
390 Video,
392 #[serde(untagged)]
393 Other(String),
394}