1use ai::providers::open_ai::{
2 AzureOpenAiApiVersion, OpenAiCompletionProviderKind, OPEN_AI_API_URL,
3};
4use anyhow::anyhow;
5use gpui::Pixels;
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8use settings::Settings;
9
10#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
11#[serde(rename_all = "snake_case")]
12pub enum OpenAiModel {
13 #[serde(rename = "gpt-3.5-turbo-0613")]
14 ThreePointFiveTurbo,
15 #[serde(rename = "gpt-4-0613")]
16 Four,
17 #[serde(rename = "gpt-4-1106-preview")]
18 FourTurbo,
19}
20
21impl OpenAiModel {
22 pub fn full_name(&self) -> &'static str {
23 match self {
24 Self::ThreePointFiveTurbo => "gpt-3.5-turbo-0613",
25 Self::Four => "gpt-4-0613",
26 Self::FourTurbo => "gpt-4-1106-preview",
27 }
28 }
29
30 pub fn short_name(&self) -> &'static str {
31 match self {
32 Self::ThreePointFiveTurbo => "gpt-3.5-turbo",
33 Self::Four => "gpt-4",
34 Self::FourTurbo => "gpt-4-turbo",
35 }
36 }
37
38 pub fn cycle(&self) -> Self {
39 match self {
40 Self::ThreePointFiveTurbo => Self::Four,
41 Self::Four => Self::FourTurbo,
42 Self::FourTurbo => Self::ThreePointFiveTurbo,
43 }
44 }
45}
46
47#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
48#[serde(rename_all = "snake_case")]
49pub enum AssistantDockPosition {
50 Left,
51 Right,
52 Bottom,
53}
54
55#[derive(Debug, Deserialize)]
56pub struct AssistantSettings {
57 /// Whether to show the assistant panel button in the status bar.
58 pub button: bool,
59 /// Where to dock the assistant.
60 pub dock: AssistantDockPosition,
61 /// Default width in pixels when the assistant is docked to the left or right.
62 pub default_width: Pixels,
63 /// Default height in pixels when the assistant is docked to the bottom.
64 pub default_height: Pixels,
65 /// The default OpenAI model to use when starting new conversations.
66 #[deprecated = "Please use `provider.default_model` instead."]
67 pub default_open_ai_model: OpenAiModel,
68 /// OpenAI API base URL to use when starting new conversations.
69 #[deprecated = "Please use `provider.api_url` instead."]
70 pub openai_api_url: String,
71 /// The settings for the AI provider.
72 pub provider: AiProviderSettings,
73}
74
75impl AssistantSettings {
76 pub fn provider_kind(&self) -> anyhow::Result<OpenAiCompletionProviderKind> {
77 match &self.provider {
78 AiProviderSettings::OpenAi(_) => Ok(OpenAiCompletionProviderKind::OpenAi),
79 AiProviderSettings::AzureOpenAi(settings) => {
80 let deployment_id = settings
81 .deployment_id
82 .clone()
83 .ok_or_else(|| anyhow!("no Azure OpenAI deployment ID"))?;
84 let api_version = settings
85 .api_version
86 .ok_or_else(|| anyhow!("no Azure OpenAI API version"))?;
87
88 Ok(OpenAiCompletionProviderKind::AzureOpenAi {
89 deployment_id,
90 api_version,
91 })
92 }
93 }
94 }
95
96 pub fn provider_api_url(&self) -> anyhow::Result<String> {
97 match &self.provider {
98 AiProviderSettings::OpenAi(settings) => Ok(settings
99 .api_url
100 .clone()
101 .unwrap_or_else(|| OPEN_AI_API_URL.to_string())),
102 AiProviderSettings::AzureOpenAi(settings) => settings
103 .api_url
104 .clone()
105 .ok_or_else(|| anyhow!("no Azure OpenAI API URL")),
106 }
107 }
108
109 pub fn provider_model(&self) -> anyhow::Result<OpenAiModel> {
110 match &self.provider {
111 AiProviderSettings::OpenAi(settings) => {
112 Ok(settings.default_model.unwrap_or(OpenAiModel::FourTurbo))
113 }
114 AiProviderSettings::AzureOpenAi(settings) => {
115 let deployment_id = settings
116 .deployment_id
117 .as_deref()
118 .ok_or_else(|| anyhow!("no Azure OpenAI deployment ID"))?;
119
120 match deployment_id {
121 // https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-preview
122 "gpt-4" | "gpt-4-32k" => Ok(OpenAiModel::Four),
123 // https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-35
124 "gpt-35-turbo" | "gpt-35-turbo-16k" | "gpt-35-turbo-instruct" => {
125 Ok(OpenAiModel::ThreePointFiveTurbo)
126 }
127 _ => Err(anyhow!(
128 "no matching OpenAI model found for deployment ID: '{deployment_id}'"
129 )),
130 }
131 }
132 }
133 }
134
135 pub fn provider_model_name(&self) -> anyhow::Result<String> {
136 match &self.provider {
137 AiProviderSettings::OpenAi(settings) => Ok(settings
138 .default_model
139 .unwrap_or(OpenAiModel::FourTurbo)
140 .full_name()
141 .to_string()),
142 AiProviderSettings::AzureOpenAi(settings) => settings
143 .deployment_id
144 .clone()
145 .ok_or_else(|| anyhow!("no Azure OpenAI deployment ID")),
146 }
147 }
148}
149
150impl Settings for AssistantSettings {
151 const KEY: Option<&'static str> = Some("assistant");
152
153 type FileContent = AssistantSettingsContent;
154
155 fn load(
156 default_value: &Self::FileContent,
157 user_values: &[&Self::FileContent],
158 _: &mut gpui::AppContext,
159 ) -> anyhow::Result<Self> {
160 Self::load_via_json_merge(default_value, user_values)
161 }
162}
163
164/// Assistant panel settings
165#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
166pub struct AssistantSettingsContent {
167 /// Whether to show the assistant panel button in the status bar.
168 ///
169 /// Default: true
170 pub button: Option<bool>,
171 /// Where to dock the assistant.
172 ///
173 /// Default: right
174 pub dock: Option<AssistantDockPosition>,
175 /// Default width in pixels when the assistant is docked to the left or right.
176 ///
177 /// Default: 640
178 pub default_width: Option<f32>,
179 /// Default height in pixels when the assistant is docked to the bottom.
180 ///
181 /// Default: 320
182 pub default_height: Option<f32>,
183 /// Deprecated: Please use `provider.default_model` instead.
184 /// The default OpenAI model to use when starting new conversations.
185 ///
186 /// Default: gpt-4-1106-preview
187 #[deprecated = "Please use `provider.default_model` instead."]
188 pub default_open_ai_model: Option<OpenAiModel>,
189 /// Deprecated: Please use `provider.api_url` instead.
190 /// OpenAI API base URL to use when starting new conversations.
191 ///
192 /// Default: https://api.openai.com/v1
193 #[deprecated = "Please use `provider.api_url` instead."]
194 pub openai_api_url: Option<String>,
195 /// The settings for the AI provider.
196 #[serde(default)]
197 pub provider: AiProviderSettingsContent,
198}
199
200#[derive(Debug, Clone, Deserialize)]
201#[serde(tag = "type", rename_all = "snake_case")]
202pub enum AiProviderSettings {
203 /// The settings for the OpenAI provider.
204 #[serde(rename = "openai")]
205 OpenAi(OpenAiProviderSettings),
206 /// The settings for the Azure OpenAI provider.
207 #[serde(rename = "azure_openai")]
208 AzureOpenAi(AzureOpenAiProviderSettings),
209}
210
211/// The settings for the AI provider used by the Zed Assistant.
212#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
213#[serde(tag = "type", rename_all = "snake_case")]
214pub enum AiProviderSettingsContent {
215 /// The settings for the OpenAI provider.
216 #[serde(rename = "openai")]
217 OpenAi(OpenAiProviderSettingsContent),
218 /// The settings for the Azure OpenAI provider.
219 #[serde(rename = "azure_openai")]
220 AzureOpenAi(AzureOpenAiProviderSettingsContent),
221}
222
223impl Default for AiProviderSettingsContent {
224 fn default() -> Self {
225 Self::OpenAi(OpenAiProviderSettingsContent::default())
226 }
227}
228
229#[derive(Debug, Clone, Deserialize)]
230pub struct OpenAiProviderSettings {
231 /// The OpenAI API base URL to use when starting new conversations.
232 pub api_url: Option<String>,
233 /// The default OpenAI model to use when starting new conversations.
234 pub default_model: Option<OpenAiModel>,
235}
236
237#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
238pub struct OpenAiProviderSettingsContent {
239 /// The OpenAI API base URL to use when starting new conversations.
240 ///
241 /// Default: https://api.openai.com/v1
242 pub api_url: Option<String>,
243 /// The default OpenAI model to use when starting new conversations.
244 ///
245 /// Default: gpt-4-1106-preview
246 pub default_model: Option<OpenAiModel>,
247}
248
249#[derive(Debug, Clone, Deserialize)]
250pub struct AzureOpenAiProviderSettings {
251 /// The Azure OpenAI API base URL to use when starting new conversations.
252 pub api_url: Option<String>,
253 /// The Azure OpenAI API version.
254 pub api_version: Option<AzureOpenAiApiVersion>,
255 /// The Azure OpenAI API deployment ID.
256 pub deployment_id: Option<String>,
257}
258
259#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
260pub struct AzureOpenAiProviderSettingsContent {
261 /// The Azure OpenAI API base URL to use when starting new conversations.
262 pub api_url: Option<String>,
263 /// The Azure OpenAI API version.
264 pub api_version: Option<AzureOpenAiApiVersion>,
265 /// The Azure OpenAI deployment ID.
266 pub deployment_id: Option<String>,
267}