gemini_client_api/gemini/embed.rs
1use super::ask::BASE_URL;
2use super::error::GeminiResponseError;
3use super::types::embedding::*;
4use super::types::request::Part;
5use reqwest::Client;
6
7/// Client for generating embeddings using Gemini embedding models.
8///
9/// # Example
10/// ```no_run
11/// use gemini_client_api::gemini::embed::GeminiEmbedding;
12/// use gemini_client_api::gemini::types::embedding::TaskType;
13///
14/// # async fn run() {
15/// let embedder = GeminiEmbedding::new("YOUR_API_KEY", "gemini-embedding-001")
16/// .set_task_type(TaskType::RetrievalDocument);
17///
18/// let response = embedder.embed_text("Hello, world!").await.unwrap();
19/// println!("Embedding dimension: {}", response.embedding().values().len());
20/// # }
21/// ```
22#[derive(Clone, Debug)]
23pub struct GeminiEmbedding {
24 client: Client,
25 api_key: String,
26 model: String,
27 config: Option<EmbedContentConfig>,
28}
29
30impl GeminiEmbedding {
31 /// Creates a new `GeminiEmbedding` client.
32 ///
33 /// # Arguments
34 /// * `api_key` - Your Gemini API key. Get one from [Google AI studio](https://aistudio.google.com/app/apikey).
35 /// * `model` - The embedding model to use (e.g., `"gemini-embedding-001"`).
36 /// See [embedding models](https://ai.google.dev/gemini-api/docs/models#gemini-embedding).
37 pub fn new(api_key: impl Into<String>, model: impl Into<String>) -> Self {
38 Self {
39 client: Client::default(),
40 api_key: api_key.into(),
41 model: model.into(),
42 config: None,
43 }
44 }
45 /// Creates a new `GeminiEmbedding` client with a custom `reqwest::Client`.
46 ///
47 /// # Arguments
48 /// * `api_key` - Your Gemini API key.
49 /// * `model` - The embedding model to use.
50 /// * `client` - A custom `reqwest::Client` for making requests.
51 pub fn new_with_client(
52 api_key: impl Into<String>,
53 model: impl Into<String>,
54 client: Client,
55 ) -> Self {
56 Self {
57 client,
58 api_key: api_key.into(),
59 model: model.into(),
60 config: None,
61 }
62 }
63 /// Sets the task type for the embedding.
64 ///
65 /// The task type helps the model produce better embeddings tailored for the specific use case.
66 pub fn set_task_type(mut self, task_type: TaskType) -> Self {
67 let output_dimensionality = self
68 .config
69 .as_ref()
70 .and_then(|c| c.output_dimensionality().clone());
71 self.config = Some(EmbedContentConfig::new(
72 Some(task_type),
73 output_dimensionality,
74 ));
75 self
76 }
77 /// Sets the output dimensionality for the embedding.
78 ///
79 /// Allows reducing the embedding dimension for storage/performance optimization
80 /// via [Matryoshka Representation Learning](https://ai.google.dev/gemini-api/docs/embeddings#matryoshka).
81 pub fn set_output_dimensionality(mut self, output_dimensionality: u32) -> Self {
82 let task_type = self.config.as_ref().and_then(|c| c.task_type().clone());
83 self.config = Some(EmbedContentConfig::new(
84 task_type,
85 Some(output_dimensionality),
86 ));
87 self
88 }
89 pub fn set_api_key(mut self, api_key: impl Into<String>) -> Self {
90 self.api_key = api_key.into();
91 self
92 }
93 pub fn set_model(mut self, model: impl Into<String>) -> Self {
94 self.model = model.into();
95 self
96 }
97 /// Sets the full embedding configuration, replacing any previously set config.
98 pub fn set_config(mut self, config: EmbedContentConfig) -> Self {
99 self.config = Some(config);
100 self
101 }
102
103 /// Generates an embedding for the given content parts.
104 ///
105 /// # Arguments
106 /// * `content` - The content parts to embed (e.g., text, inline data).
107 ///
108 /// # Errors
109 /// Returns `GeminiResponseError` on network failure or API error.
110 pub async fn embed(
111 &self,
112 content: Vec<Part>,
113 ) -> Result<EmbedContentResponse, GeminiResponseError> {
114 let req_url = format!(
115 "{BASE_URL}/{}:embedContent?key={}",
116 self.model, self.api_key
117 );
118
119 let request_body = EmbedContentRequest {
120 model: format!("models/{}", self.model),
121 content: Content::new(content),
122 task_type: self.config.as_ref().and_then(|c| c.task_type().clone()),
123 output_dimensionality: self
124 .config
125 .as_ref()
126 .and_then(|c| c.output_dimensionality().clone()),
127 };
128
129 let response = self
130 .client
131 .post(req_url)
132 .json(&request_body)
133 .send()
134 .await
135 .map_err(|e| GeminiResponseError::ReqwestError(e))?;
136
137 if !response.status().is_success() {
138 let error = response
139 .json()
140 .await
141 .map_err(|e| GeminiResponseError::ReqwestError(e))?;
142 return Err(GeminiResponseError::StatusNotOk(error));
143 }
144
145 let embed_response: EmbedContentResponse = response
146 .json()
147 .await
148 .map_err(|e| GeminiResponseError::ReqwestError(e))?;
149 Ok(embed_response)
150 }
151
152 /// Convenience method to generate an embedding for a single text string.
153 ///
154 /// # Arguments
155 /// * `text` - The text to embed.
156 ///
157 /// # Example
158 /// ```no_run
159 /// # use gemini_client_api::gemini::embed::GeminiEmbedding;
160 /// # async fn run() {
161 /// let embedder = GeminiEmbedding::new("YOUR_API_KEY", "gemini-embedding-001");
162 /// let response = embedder.embed_text("What is the meaning of life?").await.unwrap();
163 /// println!("Got {} dimensions", response.embedding().values().len());
164 /// # }
165 /// ```
166 pub async fn embed_text(
167 &self,
168 text: impl Into<String>,
169 ) -> Result<EmbedContentResponse, GeminiResponseError> {
170 let part: Part = text.into().into();
171 self.embed(vec![part]).await
172 }
173}