Skip to main content

vtcode_core/llm/providers/gemini/
provider.rs

1use super::*;
2use crate::config::core::ModelConfig;
3
4pub struct GeminiProvider {
5    pub(super) api_key: Arc<str>,
6    pub(super) http_client: HttpClient,
7    pub(super) base_url: Arc<str>,
8    pub(super) model: Arc<str>,
9    pub(super) prompt_cache_enabled: bool,
10    pub(super) prompt_cache_settings: GeminiPromptCacheSettings,
11    pub(super) timeouts: TimeoutsConfig,
12    pub(super) model_behavior: Option<ModelConfig>,
13}
14
15impl GeminiProvider {
16    pub fn new(api_key: String) -> Self {
17        Self::with_model_internal(
18            api_key,
19            models::google::GEMINI_3_FLASH_PREVIEW.to_string(),
20            None,
21            None,
22            TimeoutsConfig::default(),
23            None,
24        )
25    }
26
27    pub fn with_model(api_key: String, model: String) -> Self {
28        Self::with_model_internal(api_key, model, None, None, TimeoutsConfig::default(), None)
29    }
30
31    pub fn new_with_client(
32        api_key: String,
33        model: String,
34        http_client: reqwest::Client,
35        base_url: String,
36        timeouts: TimeoutsConfig,
37        prompt_cache_enabled: bool,
38        prompt_cache_settings: GeminiPromptCacheSettings,
39    ) -> Self {
40        Self {
41            api_key: Arc::from(api_key.as_str()),
42            http_client,
43            base_url: Arc::from(base_url.as_str()),
44            model: Arc::from(model.as_str()),
45            prompt_cache_enabled,
46            prompt_cache_settings,
47            timeouts,
48            model_behavior: None,
49        }
50    }
51
52    pub fn from_config(
53        api_key: Option<String>,
54        model: Option<String>,
55        base_url: Option<String>,
56        prompt_cache: Option<PromptCachingConfig>,
57        timeouts: Option<TimeoutsConfig>,
58        _anthropic: Option<AnthropicConfig>,
59        model_behavior: Option<ModelConfig>,
60    ) -> Self {
61        let api_key_value = api_key.unwrap_or_default();
62        let model_value = resolve_model(model, models::google::GEMINI_3_FLASH_PREVIEW);
63
64        Self::with_model_internal(
65            api_key_value,
66            model_value,
67            prompt_cache,
68            base_url,
69            timeouts.unwrap_or_default(),
70            model_behavior,
71        )
72    }
73
74    fn with_model_internal(
75        api_key: String,
76        model: String,
77        prompt_cache: Option<PromptCachingConfig>,
78        base_url: Option<String>,
79        timeouts: TimeoutsConfig,
80        model_behavior: Option<ModelConfig>,
81    ) -> Self {
82        use crate::llm::http_client::HttpClientFactory;
83
84        let (prompt_cache_enabled, prompt_cache_settings) = extract_prompt_cache_settings(
85            prompt_cache,
86            |providers| &providers.gemini,
87            |cfg, provider_settings| {
88                cfg.enabled
89                    && provider_settings.enabled
90                    && provider_settings.mode != GeminiPromptCacheMode::Off
91            },
92        );
93
94        Self {
95            api_key: Arc::from(api_key.as_str()),
96            http_client: HttpClientFactory::for_llm(&timeouts),
97            base_url: Arc::from(
98                override_base_url(
99                    urls::GEMINI_API_BASE,
100                    base_url,
101                    Some(env_vars::GEMINI_BASE_URL),
102                )
103                .as_str(),
104            ),
105            model: Arc::from(model.as_str()),
106            prompt_cache_enabled,
107            prompt_cache_settings,
108            timeouts,
109            model_behavior,
110        }
111    }
112
113    /// Handle HTTP response errors and convert to appropriate LLMError.
114    /// Uses shared rate limit detection from error_handling module.
115    #[inline]
116    pub(super) fn handle_http_error(status: reqwest::StatusCode, error_text: &str) -> LLMError {
117        let status_code = status.as_u16();
118
119        // Handle authentication errors
120        if status_code == 401 || status_code == 403 {
121            let formatted_error = error_display::format_llm_error(
122                "Gemini",
123                &format!(
124                    "Authentication failed: {}. Check your GOOGLE_API_KEY or GEMINI_API_KEY environment variable.",
125                    error_text
126                ),
127            );
128            return LLMError::Authentication {
129                message: formatted_error,
130                metadata: None,
131            };
132        }
133
134        // Handle rate limit and quota errors using shared detection
135        if is_rate_limit_error(status_code, error_text) {
136            return LLMError::RateLimit { metadata: None };
137        }
138
139        // Handle invalid request errors
140        if status_code == 400 {
141            let formatted_error = error_display::format_llm_error(
142                "Gemini",
143                &format!("Invalid request: {}", error_text),
144            );
145            return LLMError::InvalidRequest {
146                message: formatted_error,
147                metadata: None,
148            };
149        }
150
151        // Generic error for other cases
152        let formatted_error =
153            error_display::format_llm_error("Gemini", &format!("HTTP {}: {}", status, error_text));
154        LLMError::Provider {
155            message: formatted_error,
156            metadata: None,
157        }
158    }
159}