gemini_rust/
models.rs

1//! # Core Gemini API Primitives
2//!
3//! This module contains the fundamental building blocks used across the Gemini API.
4//! These core data structures are shared by multiple modules and form the foundation
5//! for constructing requests and parsing responses.
6//!
7//! ## Core Types
8//!
9//! - [`Role`] - Represents the speaker in a conversation (User or Model)
10//! - [`Part`] - Content fragments that make up messages (text, images, function calls)
11//! - [`Blob`] - Binary data with MIME type for inline content
12//! - [`Content`] - Container for parts with optional role assignment
13//! - [`Message`] - Complete message with content and explicit role
14//! - [`Modality`] - Output format types (text, image, audio)
15//!
16//! ## Usage
17//!
18//! These types are typically used in combination with the domain-specific modules:
19//! - `generation` - For content generation requests and responses
20//! - `embedding` - For text embedding operations
21//! - `safety` - For content moderation settings
22//! - `tools` - For function calling capabilities
23//! - `batch` - For batch processing operations
24//! - `cache` - For content caching
25//! - `files` - For file management
26
27#![allow(clippy::enum_variant_names)]
28
29use serde::{Deserialize, Serialize};
30
31/// Role of a message in a conversation
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
33#[serde(rename_all = "lowercase")]
34pub enum Role {
35    /// Message from the user
36    User,
37    /// Message from the model
38    Model,
39}
40
41/// Content part that can be included in a message
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
43#[serde(untagged)]
44pub enum Part {
45    /// Text content
46    Text {
47        /// The text content
48        text: String,
49        /// Whether this is a thought summary (Gemini 2.5 series only)
50        #[serde(skip_serializing_if = "Option::is_none")]
51        thought: Option<bool>,
52        /// The thought signature for the text (Gemini 2.5 series only)
53        #[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
54        thought_signature: Option<String>,
55    },
56    InlineData {
57        /// The blob data
58        #[serde(rename = "inlineData")]
59        inline_data: Blob,
60    },
61    /// Function call from the model
62    FunctionCall {
63        /// The function call details
64        #[serde(rename = "functionCall")]
65        function_call: super::tools::FunctionCall,
66        /// The thought signature for the function call (Gemini 2.5 series only)
67        #[serde(rename = "thoughtSignature", skip_serializing_if = "Option::is_none")]
68        thought_signature: Option<String>,
69    },
70    /// Function response (results from executing a function call)
71    FunctionResponse {
72        /// The function response details
73        #[serde(rename = "functionResponse")]
74        function_response: super::tools::FunctionResponse,
75    },
76}
77
78/// Blob for a message part
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
80#[serde(rename_all = "camelCase")]
81pub struct Blob {
82    /// The MIME type of the data
83    pub mime_type: String,
84    /// Base64 encoded data
85    pub data: String,
86}
87
88impl Blob {
89    /// Create a new blob with mime type and data
90    pub fn new(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
91        Self {
92            mime_type: mime_type.into(),
93            data: data.into(),
94        }
95    }
96}
97
98/// Content of a message
99#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
100#[serde(rename_all = "camelCase")]
101pub struct Content {
102    /// Parts of the content
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub parts: Option<Vec<Part>>,
105    /// Role of the content
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub role: Option<Role>,
108}
109
110impl Content {
111    /// Create a new text content
112    pub fn text(text: impl Into<String>) -> Self {
113        Self {
114            parts: Some(vec![Part::Text {
115                text: text.into(),
116                thought: None,
117                thought_signature: None,
118            }]),
119            role: None,
120        }
121    }
122
123    /// Create a new content with a function call
124    pub fn function_call(function_call: super::tools::FunctionCall) -> Self {
125        Self {
126            parts: Some(vec![Part::FunctionCall {
127                function_call,
128                thought_signature: None,
129            }]),
130            role: None,
131        }
132    }
133
134    /// Create a new content with a function call and thought signature
135    pub fn function_call_with_thought(
136        function_call: super::tools::FunctionCall,
137        thought_signature: impl Into<String>,
138    ) -> Self {
139        Self {
140            parts: Some(vec![Part::FunctionCall {
141                function_call,
142                thought_signature: Some(thought_signature.into()),
143            }]),
144            role: None,
145        }
146    }
147
148    /// Create a new text content with thought signature
149    pub fn text_with_thought_signature(
150        text: impl Into<String>,
151        thought_signature: impl Into<String>,
152    ) -> Self {
153        Self {
154            parts: Some(vec![Part::Text {
155                text: text.into(),
156                thought: None,
157                thought_signature: Some(thought_signature.into()),
158            }]),
159            role: None,
160        }
161    }
162
163    /// Create a new thought content with thought signature
164    pub fn thought_with_signature(
165        text: impl Into<String>,
166        thought_signature: impl Into<String>,
167    ) -> Self {
168        Self {
169            parts: Some(vec![Part::Text {
170                text: text.into(),
171                thought: Some(true),
172                thought_signature: Some(thought_signature.into()),
173            }]),
174            role: None,
175        }
176    }
177
178    /// Create a new content with a function response
179    pub fn function_response(function_response: super::tools::FunctionResponse) -> Self {
180        Self {
181            parts: Some(vec![Part::FunctionResponse { function_response }]),
182            role: None,
183        }
184    }
185
186    /// Create a new content with a function response from name and JSON value
187    pub fn function_response_json(name: impl Into<String>, response: serde_json::Value) -> Self {
188        Self {
189            parts: Some(vec![Part::FunctionResponse {
190                function_response: super::tools::FunctionResponse::new(name, response),
191            }]),
192            role: None,
193        }
194    }
195
196    /// Create a new content with inline data (blob data)
197    pub fn inline_data(mime_type: impl Into<String>, data: impl Into<String>) -> Self {
198        Self {
199            parts: Some(vec![Part::InlineData {
200                inline_data: Blob::new(mime_type, data),
201            }]),
202            role: None,
203        }
204    }
205
206    /// Add a role to this content
207    pub fn with_role(mut self, role: Role) -> Self {
208        self.role = Some(role);
209        self
210    }
211}
212
213/// Message in a conversation
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct Message {
216    /// Content of the message
217    pub content: Content,
218    /// Role of the message
219    pub role: Role,
220}
221
222impl Message {
223    /// Create a new user message with text content
224    pub fn user(text: impl Into<String>) -> Self {
225        Self {
226            content: Content::text(text).with_role(Role::User),
227            role: Role::User,
228        }
229    }
230
231    /// Create a new model message with text content
232    pub fn model(text: impl Into<String>) -> Self {
233        Self {
234            content: Content::text(text).with_role(Role::Model),
235            role: Role::Model,
236        }
237    }
238
239    /// Create a new embedding message with text content
240    pub fn embed(text: impl Into<String>) -> Self {
241        Self {
242            content: Content::text(text),
243            role: Role::Model,
244        }
245    }
246
247    /// Create a new function message with function response content from JSON
248    pub fn function(name: impl Into<String>, response: serde_json::Value) -> Self {
249        Self {
250            content: Content::function_response_json(name, response).with_role(Role::Model),
251            role: Role::Model,
252        }
253    }
254
255    /// Create a new function message with function response from a JSON string
256    pub fn function_str(
257        name: impl Into<String>,
258        response: impl Into<String>,
259    ) -> Result<Self, serde_json::Error> {
260        let response_str = response.into();
261        let json = serde_json::from_str(&response_str)?;
262        Ok(Self {
263            content: Content::function_response_json(name, json).with_role(Role::Model),
264            role: Role::Model,
265        })
266    }
267}
268
269/// Content modality type - specifies the format of model output
270#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
271#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
272pub enum Modality {
273    /// Default value.
274    ModalityUnspecified,
275    /// Indicates the model should return text.
276    Text,
277    /// Indicates the model should return images.
278    Image,
279    /// Indicates the model should return audio.
280    Audio,
281    /// Indicates the model should return video.
282    Video,
283}