settings.rs

  1use std::sync::Arc;
  2
  3use anyhow::Result;
  4use gpui::App;
  5use language_model::LanguageModelCacheConfiguration;
  6use project::Fs;
  7use schemars::JsonSchema;
  8use serde::{Deserialize, Serialize};
  9use settings::{Settings, SettingsSources, update_settings_file};
 10
 11use crate::provider::{
 12    self,
 13    anthropic::AnthropicSettings,
 14    bedrock::AmazonBedrockSettings,
 15    cloud::{self, ZedDotDevSettings},
 16    deepseek::DeepSeekSettings,
 17    google::GoogleSettings,
 18    lmstudio::LmStudioSettings,
 19    mistral::MistralSettings,
 20    ollama::OllamaSettings,
 21    open_ai::OpenAiSettings,
 22    open_router::OpenRouterSettings,
 23    vercel::VercelSettings,
 24};
 25
 26/// Initializes the language model settings.
 27pub fn init(fs: Arc<dyn Fs>, cx: &mut App) {
 28    AllLanguageModelSettings::register(cx);
 29
 30    if AllLanguageModelSettings::get_global(cx)
 31        .openai
 32        .needs_setting_migration
 33    {
 34        update_settings_file::<AllLanguageModelSettings>(fs.clone(), cx, move |setting, _| {
 35            if let Some(settings) = setting.openai.clone() {
 36                let (newest_version, _) = settings.upgrade();
 37                setting.openai = Some(OpenAiSettingsContent::Versioned(
 38                    VersionedOpenAiSettingsContent::V1(newest_version),
 39                ));
 40            }
 41        });
 42    }
 43
 44    if AllLanguageModelSettings::get_global(cx)
 45        .anthropic
 46        .needs_setting_migration
 47    {
 48        update_settings_file::<AllLanguageModelSettings>(fs, cx, move |setting, _| {
 49            if let Some(settings) = setting.anthropic.clone() {
 50                let (newest_version, _) = settings.upgrade();
 51                setting.anthropic = Some(AnthropicSettingsContent::Versioned(
 52                    VersionedAnthropicSettingsContent::V1(newest_version),
 53                ));
 54            }
 55        });
 56    }
 57}
 58
 59#[derive(Default)]
 60pub struct AllLanguageModelSettings {
 61    pub anthropic: AnthropicSettings,
 62    pub bedrock: AmazonBedrockSettings,
 63    pub ollama: OllamaSettings,
 64    pub openai: OpenAiSettings,
 65    pub open_router: OpenRouterSettings,
 66    pub zed_dot_dev: ZedDotDevSettings,
 67    pub google: GoogleSettings,
 68    pub vercel: VercelSettings,
 69
 70    pub lmstudio: LmStudioSettings,
 71    pub deepseek: DeepSeekSettings,
 72    pub mistral: MistralSettings,
 73}
 74
 75#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
 76pub struct AllLanguageModelSettingsContent {
 77    pub anthropic: Option<AnthropicSettingsContent>,
 78    pub bedrock: Option<AmazonBedrockSettingsContent>,
 79    pub ollama: Option<OllamaSettingsContent>,
 80    pub lmstudio: Option<LmStudioSettingsContent>,
 81    pub openai: Option<OpenAiSettingsContent>,
 82    pub open_router: Option<OpenRouterSettingsContent>,
 83    #[serde(rename = "zed.dev")]
 84    pub zed_dot_dev: Option<ZedDotDevSettingsContent>,
 85    pub google: Option<GoogleSettingsContent>,
 86    pub deepseek: Option<DeepseekSettingsContent>,
 87    pub vercel: Option<VercelSettingsContent>,
 88
 89    pub mistral: Option<MistralSettingsContent>,
 90}
 91
 92#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
 93#[serde(untagged)]
 94pub enum AnthropicSettingsContent {
 95    Versioned(VersionedAnthropicSettingsContent),
 96    Legacy(LegacyAnthropicSettingsContent),
 97}
 98
 99impl AnthropicSettingsContent {
100    pub fn upgrade(self) -> (AnthropicSettingsContentV1, bool) {
101        match self {
102            AnthropicSettingsContent::Legacy(content) => (
103                AnthropicSettingsContentV1 {
104                    api_url: content.api_url,
105                    available_models: content.available_models.map(|models| {
106                        models
107                            .into_iter()
108                            .filter_map(|model| match model {
109                                anthropic::Model::Custom {
110                                    name,
111                                    display_name,
112                                    max_tokens,
113                                    tool_override,
114                                    cache_configuration,
115                                    max_output_tokens,
116                                    default_temperature,
117                                    extra_beta_headers,
118                                    mode,
119                                } => Some(provider::anthropic::AvailableModel {
120                                    name,
121                                    display_name,
122                                    max_tokens,
123                                    tool_override,
124                                    cache_configuration: cache_configuration.as_ref().map(
125                                        |config| LanguageModelCacheConfiguration {
126                                            max_cache_anchors: config.max_cache_anchors,
127                                            should_speculate: config.should_speculate,
128                                            min_total_token: config.min_total_token,
129                                        },
130                                    ),
131                                    max_output_tokens,
132                                    default_temperature,
133                                    extra_beta_headers,
134                                    mode: Some(mode.into()),
135                                }),
136                                _ => None,
137                            })
138                            .collect()
139                    }),
140                },
141                true,
142            ),
143            AnthropicSettingsContent::Versioned(content) => match content {
144                VersionedAnthropicSettingsContent::V1(content) => (content, false),
145            },
146        }
147    }
148}
149
150#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
151pub struct LegacyAnthropicSettingsContent {
152    pub api_url: Option<String>,
153    pub available_models: Option<Vec<anthropic::Model>>,
154}
155
156#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
157#[serde(tag = "version")]
158pub enum VersionedAnthropicSettingsContent {
159    #[serde(rename = "1")]
160    V1(AnthropicSettingsContentV1),
161}
162
163#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
164pub struct AnthropicSettingsContentV1 {
165    pub api_url: Option<String>,
166    pub available_models: Option<Vec<provider::anthropic::AvailableModel>>,
167}
168
169#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
170pub struct AmazonBedrockSettingsContent {
171    available_models: Option<Vec<provider::bedrock::AvailableModel>>,
172    endpoint_url: Option<String>,
173    region: Option<String>,
174    profile: Option<String>,
175    authentication_method: Option<provider::bedrock::BedrockAuthMethod>,
176}
177
178#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
179pub struct OllamaSettingsContent {
180    pub api_url: Option<String>,
181    pub available_models: Option<Vec<provider::ollama::AvailableModel>>,
182}
183
184#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
185pub struct LmStudioSettingsContent {
186    pub api_url: Option<String>,
187    pub available_models: Option<Vec<provider::lmstudio::AvailableModel>>,
188}
189
190#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
191pub struct DeepseekSettingsContent {
192    pub api_url: Option<String>,
193    pub available_models: Option<Vec<provider::deepseek::AvailableModel>>,
194}
195
196#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
197pub struct MistralSettingsContent {
198    pub api_url: Option<String>,
199    pub available_models: Option<Vec<provider::mistral::AvailableModel>>,
200}
201
202#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
203#[serde(untagged)]
204pub enum OpenAiSettingsContent {
205    Versioned(VersionedOpenAiSettingsContent),
206    Legacy(LegacyOpenAiSettingsContent),
207}
208
209impl OpenAiSettingsContent {
210    pub fn upgrade(self) -> (OpenAiSettingsContentV1, bool) {
211        match self {
212            OpenAiSettingsContent::Legacy(content) => (
213                OpenAiSettingsContentV1 {
214                    api_url: content.api_url,
215                    available_models: content.available_models.map(|models| {
216                        models
217                            .into_iter()
218                            .filter_map(|model| match model {
219                                open_ai::Model::Custom {
220                                    name,
221                                    display_name,
222                                    max_tokens,
223                                    max_output_tokens,
224                                    max_completion_tokens,
225                                } => Some(provider::open_ai::AvailableModel {
226                                    name,
227                                    max_tokens,
228                                    max_output_tokens,
229                                    display_name,
230                                    max_completion_tokens,
231                                }),
232                                _ => None,
233                            })
234                            .collect()
235                    }),
236                },
237                true,
238            ),
239            OpenAiSettingsContent::Versioned(content) => match content {
240                VersionedOpenAiSettingsContent::V1(content) => (content, false),
241            },
242        }
243    }
244}
245
246#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
247pub struct LegacyOpenAiSettingsContent {
248    pub api_url: Option<String>,
249    pub available_models: Option<Vec<open_ai::Model>>,
250}
251
252#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
253#[serde(tag = "version")]
254pub enum VersionedOpenAiSettingsContent {
255    #[serde(rename = "1")]
256    V1(OpenAiSettingsContentV1),
257}
258
259#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
260pub struct OpenAiSettingsContentV1 {
261    pub api_url: Option<String>,
262    pub available_models: Option<Vec<provider::open_ai::AvailableModel>>,
263}
264
265#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
266pub struct VercelSettingsContent {
267    pub api_url: Option<String>,
268    pub available_models: Option<Vec<provider::vercel::AvailableModel>>,
269}
270
271#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
272pub struct GoogleSettingsContent {
273    pub api_url: Option<String>,
274    pub available_models: Option<Vec<provider::google::AvailableModel>>,
275}
276
277#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
278pub struct ZedDotDevSettingsContent {
279    available_models: Option<Vec<cloud::AvailableModel>>,
280}
281
282#[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
283pub struct OpenRouterSettingsContent {
284    pub api_url: Option<String>,
285    pub available_models: Option<Vec<provider::open_router::AvailableModel>>,
286}
287
288impl settings::Settings for AllLanguageModelSettings {
289    const KEY: Option<&'static str> = Some("language_models");
290
291    const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
292
293    type FileContent = AllLanguageModelSettingsContent;
294
295    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
296        fn merge<T>(target: &mut T, value: Option<T>) {
297            if let Some(value) = value {
298                *target = value;
299            }
300        }
301
302        let mut settings = AllLanguageModelSettings::default();
303
304        for value in sources.defaults_and_customizations() {
305            // Anthropic
306            let (anthropic, upgraded) = match value.anthropic.clone().map(|s| s.upgrade()) {
307                Some((content, upgraded)) => (Some(content), upgraded),
308                None => (None, false),
309            };
310
311            if upgraded {
312                settings.anthropic.needs_setting_migration = true;
313            }
314
315            merge(
316                &mut settings.anthropic.api_url,
317                anthropic.as_ref().and_then(|s| s.api_url.clone()),
318            );
319            merge(
320                &mut settings.anthropic.available_models,
321                anthropic.as_ref().and_then(|s| s.available_models.clone()),
322            );
323
324            // Bedrock
325            let bedrock = value.bedrock.clone();
326            merge(
327                &mut settings.bedrock.profile_name,
328                bedrock.as_ref().map(|s| s.profile.clone()),
329            );
330            merge(
331                &mut settings.bedrock.authentication_method,
332                bedrock.as_ref().map(|s| s.authentication_method.clone()),
333            );
334            merge(
335                &mut settings.bedrock.region,
336                bedrock.as_ref().map(|s| s.region.clone()),
337            );
338            merge(
339                &mut settings.bedrock.endpoint,
340                bedrock.as_ref().map(|s| s.endpoint_url.clone()),
341            );
342
343            // Ollama
344            let ollama = value.ollama.clone();
345
346            merge(
347                &mut settings.ollama.api_url,
348                value.ollama.as_ref().and_then(|s| s.api_url.clone()),
349            );
350            merge(
351                &mut settings.ollama.available_models,
352                ollama.as_ref().and_then(|s| s.available_models.clone()),
353            );
354
355            // LM Studio
356            let lmstudio = value.lmstudio.clone();
357
358            merge(
359                &mut settings.lmstudio.api_url,
360                value.lmstudio.as_ref().and_then(|s| s.api_url.clone()),
361            );
362            merge(
363                &mut settings.lmstudio.available_models,
364                lmstudio.as_ref().and_then(|s| s.available_models.clone()),
365            );
366
367            // DeepSeek
368            let deepseek = value.deepseek.clone();
369
370            merge(
371                &mut settings.deepseek.api_url,
372                value.deepseek.as_ref().and_then(|s| s.api_url.clone()),
373            );
374            merge(
375                &mut settings.deepseek.available_models,
376                deepseek.as_ref().and_then(|s| s.available_models.clone()),
377            );
378
379            // OpenAI
380            let (openai, upgraded) = match value.openai.clone().map(|s| s.upgrade()) {
381                Some((content, upgraded)) => (Some(content), upgraded),
382                None => (None, false),
383            };
384
385            if upgraded {
386                settings.openai.needs_setting_migration = true;
387            }
388
389            merge(
390                &mut settings.openai.api_url,
391                openai.as_ref().and_then(|s| s.api_url.clone()),
392            );
393            merge(
394                &mut settings.openai.available_models,
395                openai.as_ref().and_then(|s| s.available_models.clone()),
396            );
397
398            // Vercel
399            let vercel = value.vercel.clone();
400            merge(
401                &mut settings.vercel.api_url,
402                vercel.as_ref().and_then(|s| s.api_url.clone()),
403            );
404            merge(
405                &mut settings.vercel.available_models,
406                vercel.as_ref().and_then(|s| s.available_models.clone()),
407            );
408
409            merge(
410                &mut settings.zed_dot_dev.available_models,
411                value
412                    .zed_dot_dev
413                    .as_ref()
414                    .and_then(|s| s.available_models.clone()),
415            );
416            merge(
417                &mut settings.google.api_url,
418                value.google.as_ref().and_then(|s| s.api_url.clone()),
419            );
420            merge(
421                &mut settings.google.available_models,
422                value
423                    .google
424                    .as_ref()
425                    .and_then(|s| s.available_models.clone()),
426            );
427
428            // Mistral
429            let mistral = value.mistral.clone();
430            merge(
431                &mut settings.mistral.api_url,
432                mistral.as_ref().and_then(|s| s.api_url.clone()),
433            );
434            merge(
435                &mut settings.mistral.available_models,
436                mistral.as_ref().and_then(|s| s.available_models.clone()),
437            );
438
439            // OpenRouter
440            let open_router = value.open_router.clone();
441            merge(
442                &mut settings.open_router.api_url,
443                open_router.as_ref().and_then(|s| s.api_url.clone()),
444            );
445            merge(
446                &mut settings.open_router.available_models,
447                open_router
448                    .as_ref()
449                    .and_then(|s| s.available_models.clone()),
450            );
451        }
452
453        Ok(settings)
454    }
455
456    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
457}