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