gemini_genai_rs/models/
mod.rs

1//! Models API — list and get model metadata.
2//!
3//! Feature-gated behind `models`.
4
5use serde::{Deserialize, Serialize};
6
7use crate::client::Client;
8use crate::client::http::HttpError;
9use crate::protocol::types::ModelId;
10use crate::transport::auth::ServiceEndpoint;
11
12/// Model metadata returned by the API.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct ModelInfo {
16    /// Full model resource name (e.g., "models/gemini-2.5-flash").
17    pub name: String,
18
19    /// Human-readable display name.
20    #[serde(default)]
21    pub display_name: Option<String>,
22
23    /// Model description.
24    #[serde(default)]
25    pub description: Option<String>,
26
27    /// Model version.
28    #[serde(default)]
29    pub version: Option<String>,
30
31    /// Supported generation methods.
32    #[serde(default)]
33    pub supported_generation_methods: Vec<String>,
34
35    /// Maximum input tokens.
36    #[serde(default)]
37    pub input_token_limit: Option<u32>,
38
39    /// Maximum output tokens.
40    #[serde(default)]
41    pub output_token_limit: Option<u32>,
42
43    /// Default temperature.
44    #[serde(default)]
45    pub temperature: Option<f32>,
46
47    /// Default top_p.
48    #[serde(default)]
49    pub top_p: Option<f32>,
50
51    /// Default top_k.
52    #[serde(default)]
53    pub top_k: Option<u32>,
54}
55
56/// Response from list models.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase")]
59pub struct ListModelsResponse {
60    /// Available models.
61    #[serde(default)]
62    pub models: Vec<ModelInfo>,
63
64    /// Pagination token for next page.
65    #[serde(default)]
66    pub next_page_token: Option<String>,
67}
68
69/// Errors from the Models API.
70#[derive(Debug, thiserror::Error)]
71pub enum ModelsError {
72    #[error(transparent)]
73    /// Transport-level HTTP failure.
74    Http(#[from] HttpError),
75    #[error("Failed to parse response: {0}")]
76    /// Response body failed to parse.
77    Parse(#[from] serde_json::Error),
78    #[error("Auth error: {0}")]
79    /// Authentication/authorization failure.
80    Auth(#[from] crate::session::AuthError),
81}
82
83impl Client {
84    /// List available models.
85    pub async fn list_models(&self) -> Result<ListModelsResponse, ModelsError> {
86        let url = self.rest_url_for(ServiceEndpoint::ListModels, self.default_model());
87        let headers = self.auth_headers().await?;
88
89        let json = self.http_client().get_json(&url, headers).await?;
90        Ok(serde_json::from_value(json)?)
91    }
92
93    /// Get metadata for a specific model.
94    pub async fn get_model(&self, model: &ModelId) -> Result<ModelInfo, ModelsError> {
95        let url = self.rest_url_for(ServiceEndpoint::GetModel, model);
96        let headers = self.auth_headers().await?;
97
98        let json = self.http_client().get_json(&url, headers).await?;
99        Ok(serde_json::from_value(json)?)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn parse_model_info() {
109        let json = serde_json::json!({
110            "name": "models/gemini-2.5-flash",
111            "displayName": "Gemini 2.5 Flash",
112            "description": "Fast model",
113            "supportedGenerationMethods": ["generateContent", "countTokens"],
114            "inputTokenLimit": 1048576,
115            "outputTokenLimit": 8192,
116            "temperature": 1.0,
117            "topP": 0.95,
118            "topK": 40
119        });
120        let model: ModelInfo = serde_json::from_value(json).unwrap();
121        assert_eq!(model.name, "models/gemini-2.5-flash");
122        assert_eq!(model.input_token_limit, Some(1048576));
123        assert_eq!(model.supported_generation_methods.len(), 2);
124    }
125
126    #[test]
127    fn parse_list_models_response() {
128        let json = serde_json::json!({
129            "models": [
130                {"name": "models/gemini-2.5-flash"},
131                {"name": "models/gemini-2.5-pro"}
132            ],
133            "nextPageToken": "abc123"
134        });
135        let resp: ListModelsResponse = serde_json::from_value(json).unwrap();
136        assert_eq!(resp.models.len(), 2);
137        assert_eq!(resp.next_page_token, Some("abc123".to_string()));
138    }
139}