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}