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