use std::sync::Arc; use anyhow::Result; use gpui::App; use language_model::LanguageModelCacheConfiguration; use project::Fs; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsSources, update_settings_file}; use crate::provider::{ self, anthropic::AnthropicSettings, bedrock::AmazonBedrockSettings, cloud::{self, ZedDotDevSettings}, copilot_chat::CopilotChatSettings, deepseek::DeepSeekSettings, google::GoogleSettings, lmstudio::LmStudioSettings, mistral::MistralSettings, ollama::OllamaSettings, open_ai::OpenAiSettings, }; /// Initializes the language model settings. pub fn init(fs: Arc, cx: &mut App) { AllLanguageModelSettings::register(cx); if AllLanguageModelSettings::get_global(cx) .openai .needs_setting_migration { update_settings_file::(fs.clone(), cx, move |setting, _| { if let Some(settings) = setting.openai.clone() { let (newest_version, _) = settings.upgrade(); setting.openai = Some(OpenAiSettingsContent::Versioned( VersionedOpenAiSettingsContent::V1(newest_version), )); } }); } if AllLanguageModelSettings::get_global(cx) .anthropic .needs_setting_migration { update_settings_file::(fs, cx, move |setting, _| { if let Some(settings) = setting.anthropic.clone() { let (newest_version, _) = settings.upgrade(); setting.anthropic = Some(AnthropicSettingsContent::Versioned( VersionedAnthropicSettingsContent::V1(newest_version), )); } }); } } #[derive(Default)] pub struct AllLanguageModelSettings { pub anthropic: AnthropicSettings, pub bedrock: AmazonBedrockSettings, pub ollama: OllamaSettings, pub openai: OpenAiSettings, pub zed_dot_dev: ZedDotDevSettings, pub google: GoogleSettings, pub copilot_chat: CopilotChatSettings, pub lmstudio: LmStudioSettings, pub deepseek: DeepSeekSettings, pub mistral: MistralSettings, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct AllLanguageModelSettingsContent { pub anthropic: Option, pub ollama: Option, pub lmstudio: Option, pub openai: Option, #[serde(rename = "zed.dev")] pub zed_dot_dev: Option, pub google: Option, pub deepseek: Option, pub copilot_chat: Option, pub mistral: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] #[serde(untagged)] pub enum AnthropicSettingsContent { Legacy(LegacyAnthropicSettingsContent), Versioned(VersionedAnthropicSettingsContent), } impl AnthropicSettingsContent { pub fn upgrade(self) -> (AnthropicSettingsContentV1, bool) { match self { AnthropicSettingsContent::Legacy(content) => ( AnthropicSettingsContentV1 { api_url: content.api_url, available_models: content.available_models.map(|models| { models .into_iter() .filter_map(|model| match model { anthropic::Model::Custom { name, display_name, max_tokens, tool_override, cache_configuration, max_output_tokens, default_temperature, extra_beta_headers, mode, } => Some(provider::anthropic::AvailableModel { name, display_name, max_tokens, tool_override, cache_configuration: cache_configuration.as_ref().map( |config| LanguageModelCacheConfiguration { max_cache_anchors: config.max_cache_anchors, should_speculate: config.should_speculate, min_total_token: config.min_total_token, }, ), max_output_tokens, default_temperature, extra_beta_headers, mode: Some(mode.into()), }), _ => None, }) .collect() }), }, true, ), AnthropicSettingsContent::Versioned(content) => match content { VersionedAnthropicSettingsContent::V1(content) => (content, false), }, } } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct LegacyAnthropicSettingsContent { pub api_url: Option, pub available_models: Option>, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] #[serde(tag = "version")] pub enum VersionedAnthropicSettingsContent { #[serde(rename = "1")] V1(AnthropicSettingsContentV1), } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct AnthropicSettingsContentV1 { pub api_url: Option, pub available_models: Option>, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct OllamaSettingsContent { pub api_url: Option, pub available_models: Option>, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct LmStudioSettingsContent { pub api_url: Option, pub available_models: Option>, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct DeepseekSettingsContent { pub api_url: Option, pub available_models: Option>, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct MistralSettingsContent { pub api_url: Option, pub available_models: Option>, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] #[serde(untagged)] pub enum OpenAiSettingsContent { Legacy(LegacyOpenAiSettingsContent), Versioned(VersionedOpenAiSettingsContent), } impl OpenAiSettingsContent { pub fn upgrade(self) -> (OpenAiSettingsContentV1, bool) { match self { OpenAiSettingsContent::Legacy(content) => ( OpenAiSettingsContentV1 { api_url: content.api_url, available_models: content.available_models.map(|models| { models .into_iter() .filter_map(|model| match model { open_ai::Model::Custom { name, display_name, max_tokens, max_output_tokens, max_completion_tokens, } => Some(provider::open_ai::AvailableModel { name, max_tokens, max_output_tokens, display_name, max_completion_tokens, }), _ => None, }) .collect() }), }, true, ), OpenAiSettingsContent::Versioned(content) => match content { VersionedOpenAiSettingsContent::V1(content) => (content, false), }, } } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct LegacyOpenAiSettingsContent { pub api_url: Option, pub available_models: Option>, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] #[serde(tag = "version")] pub enum VersionedOpenAiSettingsContent { #[serde(rename = "1")] V1(OpenAiSettingsContentV1), } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct OpenAiSettingsContentV1 { pub api_url: Option, pub available_models: Option>, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct GoogleSettingsContent { pub api_url: Option, pub available_models: Option>, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct ZedDotDevSettingsContent { available_models: Option>, } #[derive(Default, Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct CopilotChatSettingsContent {} impl settings::Settings for AllLanguageModelSettings { const KEY: Option<&'static str> = Some("language_models"); const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]); type FileContent = AllLanguageModelSettingsContent; fn load(sources: SettingsSources, _: &mut App) -> Result { fn merge(target: &mut T, value: Option) { if let Some(value) = value { *target = value; } } let mut settings = AllLanguageModelSettings::default(); for value in sources.defaults_and_customizations() { // Anthropic let (anthropic, upgraded) = match value.anthropic.clone().map(|s| s.upgrade()) { Some((content, upgraded)) => (Some(content), upgraded), None => (None, false), }; if upgraded { settings.anthropic.needs_setting_migration = true; } merge( &mut settings.anthropic.api_url, anthropic.as_ref().and_then(|s| s.api_url.clone()), ); merge( &mut settings.anthropic.available_models, anthropic.as_ref().and_then(|s| s.available_models.clone()), ); // Ollama let ollama = value.ollama.clone(); merge( &mut settings.ollama.api_url, value.ollama.as_ref().and_then(|s| s.api_url.clone()), ); merge( &mut settings.ollama.available_models, ollama.as_ref().and_then(|s| s.available_models.clone()), ); // LM Studio let lmstudio = value.lmstudio.clone(); merge( &mut settings.lmstudio.api_url, value.lmstudio.as_ref().and_then(|s| s.api_url.clone()), ); merge( &mut settings.lmstudio.available_models, lmstudio.as_ref().and_then(|s| s.available_models.clone()), ); // DeepSeek let deepseek = value.deepseek.clone(); merge( &mut settings.deepseek.api_url, value.deepseek.as_ref().and_then(|s| s.api_url.clone()), ); merge( &mut settings.deepseek.available_models, deepseek.as_ref().and_then(|s| s.available_models.clone()), ); // OpenAI let (openai, upgraded) = match value.openai.clone().map(|s| s.upgrade()) { Some((content, upgraded)) => (Some(content), upgraded), None => (None, false), }; if upgraded { settings.openai.needs_setting_migration = true; } merge( &mut settings.openai.api_url, openai.as_ref().and_then(|s| s.api_url.clone()), ); merge( &mut settings.openai.available_models, openai.as_ref().and_then(|s| s.available_models.clone()), ); merge( &mut settings.zed_dot_dev.available_models, value .zed_dot_dev .as_ref() .and_then(|s| s.available_models.clone()), ); merge( &mut settings.google.api_url, value.google.as_ref().and_then(|s| s.api_url.clone()), ); merge( &mut settings.google.available_models, value .google .as_ref() .and_then(|s| s.available_models.clone()), ); // Mistral let mistral = value.mistral.clone(); merge( &mut settings.mistral.api_url, mistral.as_ref().and_then(|s| s.api_url.clone()), ); merge( &mut settings.mistral.available_models, mistral.as_ref().and_then(|s| s.available_models.clone()), ); } Ok(settings) } }