Dedupe AssistantSettings (#23190)

This PR dedupes the `AssistantSettings` so we can use the same settings
for both Assistant1 and Assistant2.

We originally forked them so we could change the Assistant2 settings
freely, but given our rollout strategy for the new Assistant, I don't
think that makes sense.

This also fixes the issue where the JSON language server would show a
"Matches multiple schemas when only one must validate" warning in
`settings.json`.

Closes #23171.

Release Notes:

- Fixed the "Matches multiple schemas when only one must validate"
warning for the `assistant` setting.
This commit is contained in:
Marshall Bowers 2025-01-15 13:52:54 -05:00 committed by GitHub
parent 22f5fd53ca
commit e215ca1d99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 91 additions and 568 deletions

33
Cargo.lock generated
View file

@ -372,9 +372,9 @@ dependencies = [
name = "assistant" name = "assistant"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anthropic",
"anyhow", "anyhow",
"assets", "assets",
"assistant_settings",
"assistant_slash_command", "assistant_slash_command",
"assistant_tool", "assistant_tool",
"async-watch", "async-watch",
@ -406,13 +406,11 @@ dependencies = [
"language_model_selector", "language_model_selector",
"language_models", "language_models",
"languages", "languages",
"lmstudio",
"log", "log",
"lsp", "lsp",
"markdown", "markdown",
"menu", "menu",
"multi_buffer", "multi_buffer",
"ollama",
"open_ai", "open_ai",
"ordered-float 2.10.1", "ordered-float 2.10.1",
"parking_lot", "parking_lot",
@ -457,9 +455,9 @@ dependencies = [
name = "assistant2" name = "assistant2"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anthropic",
"anyhow", "anyhow",
"assets", "assets",
"assistant_settings",
"assistant_tool", "assistant_tool",
"async-watch", "async-watch",
"chrono", "chrono",
@ -485,14 +483,11 @@ dependencies = [
"language_model", "language_model",
"language_model_selector", "language_model_selector",
"language_models", "language_models",
"lmstudio",
"log", "log",
"lsp", "lsp",
"markdown", "markdown",
"menu", "menu",
"multi_buffer", "multi_buffer",
"ollama",
"open_ai",
"ordered-float 2.10.1", "ordered-float 2.10.1",
"parking_lot", "parking_lot",
"paths", "paths",
@ -501,10 +496,8 @@ dependencies = [
"proto", "proto",
"rand 0.8.5", "rand 0.8.5",
"rope", "rope",
"schemars",
"serde", "serde",
"serde_json", "serde_json",
"serde_json_lenient",
"settings", "settings",
"similar", "similar",
"smol", "smol",
@ -523,6 +516,27 @@ dependencies = [
"zed_actions", "zed_actions",
] ]
[[package]]
name = "assistant_settings"
version = "0.1.0"
dependencies = [
"anthropic",
"anyhow",
"feature_flags",
"fs",
"gpui",
"language_model",
"lmstudio",
"log",
"ollama",
"open_ai",
"paths",
"schemars",
"serde",
"serde_json_lenient",
"settings",
]
[[package]] [[package]]
name = "assistant_slash_command" name = "assistant_slash_command"
version = "0.1.0" version = "0.1.0"
@ -16110,6 +16124,7 @@ dependencies = [
"assets", "assets",
"assistant", "assistant",
"assistant2", "assistant2",
"assistant_settings",
"assistant_tools", "assistant_tools",
"async-watch", "async-watch",
"audio", "audio",

View file

@ -6,6 +6,7 @@ members = [
"crates/assets", "crates/assets",
"crates/assistant", "crates/assistant",
"crates/assistant2", "crates/assistant2",
"crates/assistant_settings",
"crates/assistant_slash_command", "crates/assistant_slash_command",
"crates/assistant_tool", "crates/assistant_tool",
"crates/assistant_tools", "crates/assistant_tools",
@ -194,6 +195,7 @@ anthropic = { path = "crates/anthropic" }
assets = { path = "crates/assets" } assets = { path = "crates/assets" }
assistant = { path = "crates/assistant" } assistant = { path = "crates/assistant" }
assistant2 = { path = "crates/assistant2" } assistant2 = { path = "crates/assistant2" }
assistant_settings = { path = "crates/assistant_settings" }
assistant_slash_command = { path = "crates/assistant_slash_command" } assistant_slash_command = { path = "crates/assistant_slash_command" }
assistant_tool = { path = "crates/assistant_tool" } assistant_tool = { path = "crates/assistant_tool" }
assistant_tools = { path = "crates/assistant_tools" } assistant_tools = { path = "crates/assistant_tools" }

View file

@ -21,9 +21,9 @@ test-support = [
] ]
[dependencies] [dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true anyhow.workspace = true
assets.workspace = true assets.workspace = true
assistant_settings.workspace = true
assistant_slash_command.workspace = true assistant_slash_command.workspace = true
assistant_tool.workspace = true assistant_tool.workspace = true
async-watch.workspace = true async-watch.workspace = true
@ -52,13 +52,11 @@ language.workspace = true
language_model.workspace = true language_model.workspace = true
language_model_selector.workspace = true language_model_selector.workspace = true
language_models.workspace = true language_models.workspace = true
lmstudio = { workspace = true, features = ["schemars"] }
log.workspace = true log.workspace = true
lsp.workspace = true lsp.workspace = true
markdown.workspace = true markdown.workspace = true
menu.workspace = true menu.workspace = true
multi_buffer.workspace = true multi_buffer.workspace = true
ollama = { workspace = true, features = ["schemars"] }
open_ai = { workspace = true, features = ["schemars"] } open_ai = { workspace = true, features = ["schemars"] }
ordered-float.workspace = true ordered-float.workspace = true
parking_lot.workspace = true parking_lot.workspace = true

View file

@ -1,7 +1,6 @@
#![cfg_attr(target_os = "windows", allow(unused, dead_code))] #![cfg_attr(target_os = "windows", allow(unused, dead_code))]
pub mod assistant_panel; pub mod assistant_panel;
pub mod assistant_settings;
mod context; mod context;
pub mod context_store; pub mod context_store;
mod inline_assistant; mod inline_assistant;

View file

@ -1,7 +1,6 @@
use crate::slash_command::file_command::codeblock_fence_for_path; use crate::slash_command::file_command::codeblock_fence_for_path;
use crate::slash_command_working_set::SlashCommandWorkingSet; use crate::slash_command_working_set::SlashCommandWorkingSet;
use crate::{ use crate::{
assistant_settings::{AssistantDockPosition, AssistantSettings},
humanize_token_count, humanize_token_count,
prompt_library::open_prompt_library, prompt_library::open_prompt_library,
prompts::PromptBuilder, prompts::PromptBuilder,
@ -21,6 +20,7 @@ use crate::{
ToggleModelSelector, ToggleModelSelector,
}; };
use anyhow::Result; use anyhow::Result;
use assistant_settings::{AssistantDockPosition, AssistantSettings};
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection}; use assistant_slash_command::{SlashCommand, SlashCommandOutputSection};
use assistant_tool::ToolWorkingSet; use assistant_tool::ToolWorkingSet;
use client::{proto, zed_urls, Client, Status}; use client::{proto, zed_urls, Client, Status};

View file

@ -1,9 +1,10 @@
use crate::{ use crate::{
assistant_settings::AssistantSettings, humanize_token_count, prompts::PromptBuilder, humanize_token_count, prompts::PromptBuilder, AssistantPanel, AssistantPanelEvent,
AssistantPanel, AssistantPanelEvent, CharOperation, CycleNextInlineAssist, CharOperation, CycleNextInlineAssist, CyclePreviousInlineAssist, LineDiff, LineOperation,
CyclePreviousInlineAssist, LineDiff, LineOperation, RequestType, StreamingDiff, RequestType, StreamingDiff,
}; };
use anyhow::{anyhow, Context as _, Result}; use anyhow::{anyhow, Context as _, Result};
use assistant_settings::AssistantSettings;
use client::{telemetry::Telemetry, ErrorExt}; use client::{telemetry::Telemetry, ErrorExt};
use collections::{hash_map, HashMap, HashSet, VecDeque}; use collections::{hash_map, HashMap, HashSet, VecDeque};
use editor::{ use editor::{

View file

@ -1,9 +1,9 @@
use crate::assistant_settings::AssistantSettings;
use crate::{ use crate::{
humanize_token_count, prompts::PromptBuilder, AssistantPanel, AssistantPanelEvent, RequestType, humanize_token_count, prompts::PromptBuilder, AssistantPanel, AssistantPanelEvent, RequestType,
DEFAULT_CONTEXT_LINES, DEFAULT_CONTEXT_LINES,
}; };
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use assistant_settings::AssistantSettings;
use client::telemetry::Telemetry; use client::telemetry::Telemetry;
use collections::{HashMap, VecDeque}; use collections::{HashMap, VecDeque};
use editor::{ use editor::{

View file

@ -13,9 +13,9 @@ path = "src/assistant.rs"
doctest = false doctest = false
[dependencies] [dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true anyhow.workspace = true
assets.workspace = true assets.workspace = true
assistant_settings.workspace = true
assistant_tool.workspace = true assistant_tool.workspace = true
async-watch.workspace = true async-watch.workspace = true
chrono.workspace = true chrono.workspace = true
@ -45,9 +45,6 @@ lsp.workspace = true
markdown.workspace = true markdown.workspace = true
menu.workspace = true menu.workspace = true
multi_buffer.workspace = true multi_buffer.workspace = true
ollama = { workspace = true, features = ["schemars"] }
lmstudio = { workspace = true, features = ["schemars"] }
open_ai = { workspace = true, features = ["schemars"] }
ordered-float.workspace = true ordered-float.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
paths.workspace = true paths.workspace = true
@ -55,10 +52,8 @@ picker.workspace = true
project.workspace = true project.workspace = true
proto.workspace = true proto.workspace = true
rope.workspace = true rope.workspace = true
schemars.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true settings.workspace = true
similar.workspace = true similar.workspace = true
smol.workspace = true smol.workspace = true

View file

@ -1,7 +1,6 @@
mod active_thread; mod active_thread;
mod assistant_model_selector; mod assistant_model_selector;
mod assistant_panel; mod assistant_panel;
mod assistant_settings;
mod buffer_codegen; mod buffer_codegen;
mod context; mod context;
mod context_picker; mod context_picker;
@ -21,6 +20,7 @@ mod ui;
use std::sync::Arc; use std::sync::Arc;
use assistant_settings::AssistantSettings;
use client::Client; use client::Client;
use command_palette_hooks::CommandPaletteFilter; use command_palette_hooks::CommandPaletteFilter;
use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt}; use feature_flags::{Assistant2FeatureFlag, FeatureFlagAppExt};
@ -31,7 +31,6 @@ use settings::Settings as _;
use util::ResultExt; use util::ResultExt;
pub use crate::assistant_panel::AssistantPanel; pub use crate::assistant_panel::AssistantPanel;
use crate::assistant_settings::AssistantSettings;
pub use crate::inline_assistant::InlineAssistant; pub use crate::inline_assistant::InlineAssistant;
actions!( actions!(

View file

@ -1,3 +1,4 @@
use assistant_settings::AssistantSettings;
use fs::Fs; use fs::Fs;
use gpui::{FocusHandle, View}; use gpui::{FocusHandle, View};
use language_model::LanguageModelRegistry; use language_model::LanguageModelRegistry;
@ -6,7 +7,7 @@ use settings::update_settings_file;
use std::sync::Arc; use std::sync::Arc;
use ui::{prelude::*, ButtonLike, PopoverMenuHandle, Tooltip}; use ui::{prelude::*, ButtonLike, PopoverMenuHandle, Tooltip};
use crate::{assistant_settings::AssistantSettings, ToggleModelSelector}; use crate::ToggleModelSelector;
pub struct AssistantModelSelector { pub struct AssistantModelSelector {
selector: View<LanguageModelSelector>, selector: View<LanguageModelSelector>,

View file

@ -1,6 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use assistant_settings::{AssistantDockPosition, AssistantSettings};
use assistant_tool::ToolWorkingSet; use assistant_tool::ToolWorkingSet;
use client::zed_urls; use client::zed_urls;
use fs::Fs; use fs::Fs;
@ -17,7 +18,6 @@ use workspace::dock::{DockPosition, Panel, PanelEvent};
use workspace::Workspace; use workspace::Workspace;
use crate::active_thread::ActiveThread; use crate::active_thread::ActiveThread;
use crate::assistant_settings::{AssistantDockPosition, AssistantSettings};
use crate::message_editor::MessageEditor; use crate::message_editor::MessageEditor;
use crate::thread::{Thread, ThreadError, ThreadId}; use crate::thread::{Thread, ThreadError, ThreadId};
use crate::thread_history::{PastThread, ThreadHistory}; use crate::thread_history::{PastThread, ThreadHistory};

View file

@ -1,526 +0,0 @@
use std::sync::Arc;
use ::open_ai::Model as OpenAiModel;
use anthropic::Model as AnthropicModel;
use gpui::Pixels;
use language_model::{CloudModel, LanguageModel};
use lmstudio::Model as LmStudioModel;
use ollama::Model as OllamaModel;
use schemars::{schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AssistantDockPosition {
Left,
#[default]
Right,
Bottom,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(tag = "name", rename_all = "snake_case")]
pub enum AssistantProviderContentV1 {
#[serde(rename = "zed.dev")]
ZedDotDev { default_model: Option<CloudModel> },
#[serde(rename = "openai")]
OpenAi {
default_model: Option<OpenAiModel>,
api_url: Option<String>,
available_models: Option<Vec<OpenAiModel>>,
},
#[serde(rename = "anthropic")]
Anthropic {
default_model: Option<AnthropicModel>,
api_url: Option<String>,
},
#[serde(rename = "ollama")]
Ollama {
default_model: Option<OllamaModel>,
api_url: Option<String>,
},
#[serde(rename = "lmstudio")]
LmStudio {
default_model: Option<LmStudioModel>,
api_url: Option<String>,
},
}
#[derive(Debug, Default)]
pub struct AssistantSettings {
pub enabled: bool,
pub button: bool,
pub dock: AssistantDockPosition,
pub default_width: Pixels,
pub default_height: Pixels,
pub default_model: LanguageModelSelection,
pub inline_alternatives: Vec<LanguageModelSelection>,
pub using_outdated_settings_version: bool,
pub enable_experimental_live_diffs: bool,
}
/// Assistant panel settings
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum AssistantSettingsContent {
Versioned(VersionedAssistantSettingsContent),
Legacy(LegacyAssistantSettingsContent),
}
impl JsonSchema for AssistantSettingsContent {
fn schema_name() -> String {
VersionedAssistantSettingsContent::schema_name()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> Schema {
VersionedAssistantSettingsContent::json_schema(gen)
}
fn is_referenceable() -> bool {
VersionedAssistantSettingsContent::is_referenceable()
}
}
impl Default for AssistantSettingsContent {
fn default() -> Self {
Self::Versioned(VersionedAssistantSettingsContent::default())
}
}
impl AssistantSettingsContent {
pub fn is_version_outdated(&self) -> bool {
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(_) => true,
VersionedAssistantSettingsContent::V2(_) => false,
},
AssistantSettingsContent::Legacy(_) => true,
}
}
fn upgrade(&self) -> AssistantSettingsContentV2 {
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => AssistantSettingsContentV2 {
enabled: settings.enabled,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_width,
default_model: settings
.provider
.clone()
.and_then(|provider| match provider {
AssistantProviderContentV1::ZedDotDev { default_model } => {
default_model.map(|model| LanguageModelSelection {
provider: "zed.dev".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::OpenAi { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "openai".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Anthropic { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "anthropic".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::Ollama { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "ollama".to_string(),
model: model.id().to_string(),
})
}
AssistantProviderContentV1::LmStudio { default_model, .. } => {
default_model.map(|model| LanguageModelSelection {
provider: "lmstudio".to_string(),
model: model.id().to_string(),
})
}
}),
inline_alternatives: None,
enable_experimental_live_diffs: None,
},
VersionedAssistantSettingsContent::V2(settings) => settings.clone(),
},
AssistantSettingsContent::Legacy(settings) => AssistantSettingsContentV2 {
enabled: None,
button: settings.button,
dock: settings.dock,
default_width: settings.default_width,
default_height: settings.default_height,
default_model: Some(LanguageModelSelection {
provider: "openai".to_string(),
model: settings
.default_open_ai_model
.clone()
.unwrap_or_default()
.id()
.to_string(),
}),
inline_alternatives: None,
enable_experimental_live_diffs: None,
},
}
}
pub fn set_dock(&mut self, dock: AssistantDockPosition) {
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => {
settings.dock = Some(dock);
}
VersionedAssistantSettingsContent::V2(settings) => {
settings.dock = Some(dock);
}
},
AssistantSettingsContent::Legacy(settings) => {
settings.dock = Some(dock);
}
}
}
pub fn set_model(&mut self, language_model: Arc<dyn LanguageModel>) {
let model = language_model.id().0.to_string();
let provider = language_model.provider_id().0.to_string();
match self {
AssistantSettingsContent::Versioned(settings) => match settings {
VersionedAssistantSettingsContent::V1(settings) => match provider.as_ref() {
"zed.dev" => {
log::warn!("attempted to set zed.dev model on outdated settings");
}
"anthropic" => {
let api_url = match &settings.provider {
Some(AssistantProviderContentV1::Anthropic { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AssistantProviderContentV1::Anthropic {
default_model: AnthropicModel::from_id(&model).ok(),
api_url,
});
}
"ollama" => {
let api_url = match &settings.provider {
Some(AssistantProviderContentV1::Ollama { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AssistantProviderContentV1::Ollama {
default_model: Some(ollama::Model::new(&model, None, None)),
api_url,
});
}
"lmstudio" => {
let api_url = match &settings.provider {
Some(AssistantProviderContentV1::LmStudio { api_url, .. }) => {
api_url.clone()
}
_ => None,
};
settings.provider = Some(AssistantProviderContentV1::LmStudio {
default_model: Some(lmstudio::Model::new(&model, None, None)),
api_url,
});
}
"openai" => {
let (api_url, available_models) = match &settings.provider {
Some(AssistantProviderContentV1::OpenAi {
api_url,
available_models,
..
}) => (api_url.clone(), available_models.clone()),
_ => (None, None),
};
settings.provider = Some(AssistantProviderContentV1::OpenAi {
default_model: OpenAiModel::from_id(&model).ok(),
api_url,
available_models,
});
}
_ => {}
},
VersionedAssistantSettingsContent::V2(settings) => {
settings.default_model = Some(LanguageModelSelection { provider, model });
}
},
AssistantSettingsContent::Legacy(settings) => {
if let Ok(model) = OpenAiModel::from_id(&language_model.id().0) {
settings.default_open_ai_model = Some(model);
}
}
}
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
#[serde(tag = "version")]
pub enum VersionedAssistantSettingsContent {
#[serde(rename = "1")]
V1(AssistantSettingsContentV1),
#[serde(rename = "2")]
V2(AssistantSettingsContentV2),
}
impl Default for VersionedAssistantSettingsContent {
fn default() -> Self {
Self::V2(AssistantSettingsContentV2 {
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
default_model: None,
inline_alternatives: None,
enable_experimental_live_diffs: None,
})
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV2 {
/// Whether the Assistant is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the assistant.
///
/// Default: right
dock: Option<AssistantDockPosition>,
/// Default width in pixels when the assistant is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the assistant is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The default model to use when creating new chats.
default_model: Option<LanguageModelSelection>,
/// Additional models with which to generate alternatives when performing inline assists.
inline_alternatives: Option<Vec<LanguageModelSelection>>,
/// Enable experimental live diffs in the assistant panel.
///
/// Default: false
enable_experimental_live_diffs: Option<bool>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct LanguageModelSelection {
#[schemars(schema_with = "providers_schema")]
pub provider: String,
pub model: String,
}
fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
enum_values: Some(vec![
"anthropic".into(),
"google".into(),
"lmstudio".into(),
"ollama".into(),
"openai".into(),
"zed.dev".into(),
"copilot_chat".into(),
]),
..Default::default()
}
.into()
}
impl Default for LanguageModelSelection {
fn default() -> Self {
Self {
provider: "openai".to_string(),
model: "gpt-4".to_string(),
}
}
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContentV1 {
/// Whether the Assistant is enabled.
///
/// Default: true
enabled: Option<bool>,
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
button: Option<bool>,
/// Where to dock the assistant.
///
/// Default: right
dock: Option<AssistantDockPosition>,
/// Default width in pixels when the assistant is docked to the left or right.
///
/// Default: 640
default_width: Option<f32>,
/// Default height in pixels when the assistant is docked to the bottom.
///
/// Default: 320
default_height: Option<f32>,
/// The provider of the assistant service.
///
/// This can be "openai", "anthropic", "ollama", "zed.dev"
/// each with their respective default models and configurations.
provider: Option<AssistantProviderContentV1>,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema, Debug)]
pub struct LegacyAssistantSettingsContent {
/// Whether to show the assistant panel button in the status bar.
///
/// Default: true
pub button: Option<bool>,
/// Where to dock the assistant.
///
/// Default: right
pub dock: Option<AssistantDockPosition>,
/// Default width in pixels when the assistant is docked to the left or right.
///
/// Default: 640
pub default_width: Option<f32>,
/// Default height in pixels when the assistant is docked to the bottom.
///
/// Default: 320
pub default_height: Option<f32>,
/// The default OpenAI model to use when creating new chats.
///
/// Default: gpt-4-1106-preview
pub default_open_ai_model: Option<OpenAiModel>,
/// OpenAI API base URL to use when creating new chats.
///
/// Default: https://api.openai.com/v1
pub openai_api_url: Option<String>,
}
impl Settings for AssistantSettings {
const KEY: Option<&'static str> = Some("assistant");
const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
type FileContent = AssistantSettingsContent;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::AppContext,
) -> anyhow::Result<Self> {
let mut settings = AssistantSettings::default();
for value in sources.defaults_and_customizations() {
if value.is_version_outdated() {
settings.using_outdated_settings_version = true;
}
let value = value.upgrade();
merge(&mut settings.enabled, value.enabled);
merge(&mut settings.button, value.button);
merge(&mut settings.dock, value.dock);
merge(
&mut settings.default_width,
value.default_width.map(Into::into),
);
merge(
&mut settings.default_height,
value.default_height.map(Into::into),
);
merge(&mut settings.default_model, value.default_model);
merge(&mut settings.inline_alternatives, value.inline_alternatives);
merge(
&mut settings.enable_experimental_live_diffs,
value.enable_experimental_live_diffs,
);
}
Ok(settings)
}
}
fn merge<T>(target: &mut T, value: Option<T>) {
if let Some(value) = value {
*target = value;
}
}
#[cfg(test)]
mod tests {
use fs::Fs;
use gpui::{ReadGlobal, TestAppContext};
use super::*;
#[gpui::test]
async fn test_deserialize_assistant_settings_with_version(cx: &mut TestAppContext) {
let fs = fs::FakeFs::new(cx.executor().clone());
fs.create_dir(paths::settings_file().parent().unwrap())
.await
.unwrap();
cx.update(|cx| {
let test_settings = settings::SettingsStore::test(cx);
cx.set_global(test_settings);
AssistantSettings::register(cx);
});
cx.update(|cx| {
assert!(!AssistantSettings::get_global(cx).using_outdated_settings_version);
assert_eq!(
AssistantSettings::get_global(cx).default_model,
LanguageModelSelection {
provider: "zed.dev".into(),
model: "claude-3-5-sonnet".into(),
}
);
});
cx.update(|cx| {
settings::SettingsStore::global(cx).update_settings_file::<AssistantSettings>(
fs.clone(),
|settings, _| {
*settings = AssistantSettingsContent::Versioned(
VersionedAssistantSettingsContent::V2(AssistantSettingsContentV2 {
default_model: Some(LanguageModelSelection {
provider: "test-provider".into(),
model: "gpt-99".into(),
}),
inline_alternatives: None,
enabled: None,
button: None,
dock: None,
default_width: None,
default_height: None,
enable_experimental_live_diffs: None,
}),
)
},
);
});
cx.run_until_parked();
let raw_settings_value = fs.load(paths::settings_file()).await.unwrap();
assert!(raw_settings_value.contains(r#""version": "2""#));
#[derive(Debug, Deserialize)]
struct AssistantSettingsTest {
assistant: AssistantSettingsContent,
}
let assistant_settings: AssistantSettingsTest =
serde_json_lenient::from_str(&raw_settings_value).unwrap();
assert!(!assistant_settings.assistant.is_version_outdated());
}
}

View file

@ -1,13 +1,11 @@
use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent}; use std::cmp;
use crate::context_store::ContextStore; use std::mem;
use crate::inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent}; use std::ops::Range;
use crate::thread_store::ThreadStore; use std::rc::Rc;
use crate::AssistantPanel; use std::sync::Arc;
use crate::{
assistant_settings::AssistantSettings, prompts::PromptBuilder,
terminal_inline_assistant::TerminalInlineAssistant,
};
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use assistant_settings::AssistantSettings;
use client::telemetry::Telemetry; use client::telemetry::Telemetry;
use collections::{hash_map, HashMap, HashSet, VecDeque}; use collections::{hash_map, HashMap, HashSet, VecDeque};
use editor::{ use editor::{
@ -21,8 +19,6 @@ use editor::{
}; };
use feature_flags::{Assistant2FeatureFlag, FeatureFlagViewExt as _}; use feature_flags::{Assistant2FeatureFlag, FeatureFlagViewExt as _};
use fs::Fs; use fs::Fs;
use util::ResultExt;
use gpui::{ use gpui::{
point, AppContext, FocusableView, Global, HighlightStyle, Model, Subscription, Task, point, AppContext, FocusableView, Global, HighlightStyle, Model, Subscription, Task,
UpdateGlobal, View, ViewContext, WeakModel, WeakView, WindowContext, UpdateGlobal, View, ViewContext, WeakModel, WeakView, WindowContext,
@ -34,15 +30,22 @@ use multi_buffer::MultiBufferRow;
use parking_lot::Mutex; use parking_lot::Mutex;
use project::{CodeAction, ProjectTransaction}; use project::{CodeAction, ProjectTransaction};
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use std::{cmp, mem, ops::Range, rc::Rc, sync::Arc};
use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase}; use telemetry_events::{AssistantEvent, AssistantKind, AssistantPhase};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView}; use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
use text::{OffsetRangeExt, ToPoint as _}; use text::{OffsetRangeExt, ToPoint as _};
use ui::prelude::*; use ui::prelude::*;
use util::RangeExt; use util::RangeExt;
use util::ResultExt;
use workspace::{dock::Panel, ShowConfiguration}; use workspace::{dock::Panel, ShowConfiguration};
use workspace::{notifications::NotificationId, ItemHandle, Toast, Workspace}; use workspace::{notifications::NotificationId, ItemHandle, Toast, Workspace};
use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent};
use crate::context_store::ContextStore;
use crate::inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent};
use crate::thread_store::ThreadStore;
use crate::AssistantPanel;
use crate::{prompts::PromptBuilder, terminal_inline_assistant::TerminalInlineAssistant};
pub fn init( pub fn init(
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
prompt_builder: Arc<PromptBuilder>, prompt_builder: Arc<PromptBuilder>,

View file

@ -0,0 +1,33 @@
[package]
name = "assistant_settings"
version = "0.1.0"
edition = "2021"
publish = false
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/assistant_settings.rs"
[dependencies]
anthropic = { workspace = true, features = ["schemars"] }
anyhow.workspace = true
feature_flags.workspace = true
gpui.workspace = true
language_model.workspace = true
lmstudio = { workspace = true, features = ["schemars"] }
log.workspace = true
ollama = { workspace = true, features = ["schemars"] }
open_ai = { workspace = true, features = ["schemars"] }
schemars.workspace = true
serde.workspace = true
settings.workspace = true
[dev-dependencies]
fs.workspace = true
gpui = { workspace = true, features = ["test-support"] }
paths.workspace = true
serde_json_lenient.workspace = true
settings = { workspace = true, features = ["test-support"] }

View file

@ -0,0 +1 @@
../../LICENSE-GPL

View file

@ -41,6 +41,7 @@ pub enum AssistantProviderContentV1 {
default_model: Option<OllamaModel>, default_model: Option<OllamaModel>,
api_url: Option<String>, api_url: Option<String>,
}, },
#[serde(rename = "lmstudio")]
LmStudio { LmStudio {
default_model: Option<LmStudioModel>, default_model: Option<LmStudioModel>,
api_url: Option<String>, api_url: Option<String>,
@ -335,8 +336,8 @@ fn providers_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema:
enum_values: Some(vec![ enum_values: Some(vec![
"anthropic".into(), "anthropic".into(),
"google".into(), "google".into(),
"ollama".into(),
"lmstudio".into(), "lmstudio".into(),
"ollama".into(),
"openai".into(), "openai".into(),
"zed.dev".into(), "zed.dev".into(),
"copilot_chat".into(), "copilot_chat".into(),

View file

@ -20,6 +20,7 @@ anyhow.workspace = true
assets.workspace = true assets.workspace = true
assistant.workspace = true assistant.workspace = true
assistant2.workspace = true assistant2.workspace = true
assistant_settings.workspace = true
assistant_tools.workspace = true assistant_tools.workspace = true
async-watch.workspace = true async-watch.workspace = true
audio.workspace = true audio.workspace = true

View file

@ -1,8 +1,8 @@
mod markdown_preview; mod markdown_preview;
mod repl_menu; mod repl_menu;
use assistant::assistant_settings::AssistantSettings;
use assistant::AssistantPanel; use assistant::AssistantPanel;
use assistant_settings::AssistantSettings;
use editor::actions::{ use editor::actions::{
AddSelectionAbove, AddSelectionBelow, DuplicateLineDown, GoToDiagnostic, GoToHunk, AddSelectionAbove, AddSelectionBelow, DuplicateLineDown, GoToDiagnostic, GoToHunk,
GoToPrevDiagnostic, GoToPrevHunk, MoveLineDown, MoveLineUp, SelectAll, SelectLargerSyntaxNode, GoToPrevDiagnostic, GoToPrevHunk, MoveLineDown, MoveLineUp, SelectAll, SelectLargerSyntaxNode,