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}