Detailed changes
@@ -47,7 +47,7 @@ use prompt_store::{
WorktreeContext,
};
use serde::{Deserialize, Serialize};
-use settings::{LanguageModelSelection, update_settings_file};
+use settings::{LanguageModelSelection, Settings as _, update_settings_file};
use std::any::Any;
use std::path::PathBuf;
use std::rc::Rc;
@@ -1423,16 +1423,29 @@ impl acp_thread::AgentModelSelector for NativeAgentModelSelector {
return Task::ready(Err(anyhow!("Invalid model ID {}", model_id)));
};
- // We want to reset the effort level when switching models, as the currently-selected effort level may
- // not be compatible.
- let effort = model
- .default_effort_level()
- .map(|effort_level| effort_level.value.to_string());
+ let favorite = agent_settings::AgentSettings::get_global(cx)
+ .favorite_models
+ .iter()
+ .find(|favorite| {
+ favorite.provider.0 == model.provider_id().0.as_ref()
+ && favorite.model == model.id().0.as_ref()
+ })
+ .cloned();
+
+ let LanguageModelSelection {
+ enable_thinking,
+ effort,
+ speed,
+ ..
+ } = agent_settings::language_model_to_selection(&model, favorite.as_ref());
thread.update(cx, |thread, cx| {
thread.set_model(model.clone(), cx);
thread.set_thinking_effort(effort.clone(), cx);
- thread.set_thinking_enabled(model.supports_thinking(), cx);
+ thread.set_thinking_enabled(enable_thinking, cx);
+ if let Some(speed) = speed {
+ thread.set_speed(speed, cx);
+ }
});
update_settings_file(
@@ -2,11 +2,12 @@ use std::{any::Any, rc::Rc, sync::Arc};
use agent_client_protocol as acp;
use agent_servers::{AgentServer, AgentServerDelegate};
-use agent_settings::AgentSettings;
+use agent_settings::{AgentSettings, language_model_to_selection};
use anyhow::Result;
use collections::HashSet;
use fs::Fs;
use gpui::{App, Entity, Task};
+use language_model::{LanguageModelId, LanguageModelProviderId, LanguageModelRegistry};
use project::{AgentId, Project};
use prompt_store::PromptStore;
use settings::{LanguageModelSelection, Settings as _, update_settings_file};
@@ -76,7 +77,7 @@ impl AgentServer for NativeAgentServer {
fs: Arc<dyn Fs>,
cx: &App,
) {
- let selection = model_id_to_selection(&model_id);
+ let selection = model_id_to_selection(&model_id, cx);
update_settings_file(fs, cx, move |settings, _| {
let agent = settings.agent.get_or_insert_default();
if should_be_favorite {
@@ -89,16 +90,41 @@ impl AgentServer for NativeAgentServer {
}
/// Convert a ModelId (e.g. "anthropic/claude-3-5-sonnet") to a LanguageModelSelection.
-fn model_id_to_selection(model_id: &acp::ModelId) -> LanguageModelSelection {
+fn model_id_to_selection(model_id: &acp::ModelId, cx: &App) -> LanguageModelSelection {
let id = model_id.0.as_ref();
let (provider, model) = id.split_once('/').unwrap_or(("", id));
- LanguageModelSelection {
- provider: provider.to_owned().into(),
- model: model.to_owned(),
- enable_thinking: false,
- effort: None,
- speed: None,
- }
+
+ let provider_id = LanguageModelProviderId(provider.to_string().into());
+ let model_id_typed = LanguageModelId(model.to_string().into());
+ let resolved = LanguageModelRegistry::global(cx)
+ .read(cx)
+ .provider(&provider_id)
+ .and_then(|p| {
+ p.provided_models(cx)
+ .into_iter()
+ .find(|m| m.id() == model_id_typed)
+ });
+
+ let Some(resolved) = resolved else {
+ return LanguageModelSelection {
+ provider: provider.to_owned().into(),
+ model: model.to_owned(),
+ enable_thinking: false,
+ effort: None,
+ speed: None,
+ };
+ };
+
+ let current_user_selection = AgentSettings::get_global(cx)
+ .default_model
+ .as_ref()
+ .filter(|selection| {
+ selection.provider.0 == resolved.provider_id().0.as_ref()
+ && selection.model == resolved.id().0.as_ref()
+ })
+ .cloned();
+
+ language_model_to_selection(&resolved, current_user_selection.as_ref())
}
#[cfg(test)]
@@ -210,7 +210,48 @@ impl AgentSettings {
.map(|sel| ModelId::new(format!("{}/{}", sel.provider.0, sel.model)))
.collect()
}
+}
+
+pub fn language_model_to_selection(
+ model: &Arc<dyn LanguageModel>,
+ override_selection: Option<&LanguageModelSelection>,
+) -> LanguageModelSelection {
+ let provider = model.provider_id().0.to_string().into();
+ let model_name = model.id().0.to_string();
+ match override_selection {
+ Some(current) => LanguageModelSelection {
+ provider,
+ model: model_name,
+ enable_thinking: current.enable_thinking && model.supports_thinking(),
+ effort: current
+ .effort
+ .clone()
+ .filter(|value| {
+ model
+ .supported_effort_levels()
+ .iter()
+ .any(|level| level.value.as_ref() == value.as_str())
+ })
+ .or_else(|| {
+ model
+ .default_effort_level()
+ .map(|effort| effort.value.to_string())
+ }),
+ speed: current.speed.filter(|_| model.supports_fast_mode()),
+ },
+ None => LanguageModelSelection {
+ provider,
+ model: model_name,
+ enable_thinking: model.supports_thinking(),
+ effort: model
+ .default_effort_level()
+ .map(|effort| effort.value.to_string()),
+ speed: None,
+ },
+ }
+}
+impl AgentSettings {
pub fn get_layout(cx: &App) -> WindowLayout {
let store = cx.global::<SettingsStore>();
let merged = store.merged_settings();
@@ -3842,12 +3842,22 @@ impl ThreadView {
let enable_thinking = !thread.thinking_enabled();
thread.set_thinking_enabled(enable_thinking, cx);
+ let favorite_key = thread.model().map(|model| {
+ (model.provider_id().0.to_string(), model.id().0.to_string())
+ });
let fs = thread.project().read(cx).fs().clone();
update_settings_file(fs, cx, move |settings, _| {
- if let Some(agent) = settings.agent.as_mut()
- && let Some(default_model) = agent.default_model.as_mut()
- {
- default_model.enable_thinking = enable_thinking;
+ if let Some(agent) = settings.agent.as_mut() {
+ if let Some(default_model) = agent.default_model.as_mut() {
+ default_model.enable_thinking = enable_thinking;
+ }
+ if let Some((provider_id, model_id)) = &favorite_key {
+ agent.update_favorite_model(
+ provider_id,
+ model_id,
+ |favorite| favorite.enable_thinking = enable_thinking,
+ );
+ }
}
});
});
@@ -3978,14 +3988,33 @@ impl ThreadView {
cx,
);
+ let favorite_key = thread.model().map(|model| {
+ (
+ model.provider_id().0.to_string(),
+ model.id().0.to_string(),
+ )
+ });
let fs = thread.project().read(cx).fs().clone();
update_settings_file(fs, cx, move |settings, _| {
- if let Some(agent) = settings.agent.as_mut()
- && let Some(default_model) =
+ if let Some(agent) = settings.agent.as_mut() {
+ if let Some(default_model) =
agent.default_model.as_mut()
- {
- default_model.effort =
- Some(effort.to_string());
+ {
+ default_model.effort =
+ Some(effort.to_string());
+ }
+ if let Some((provider_id, model_id)) =
+ &favorite_key
+ {
+ agent.update_favorite_model(
+ provider_id,
+ model_id,
+ |favorite| {
+ favorite.effort =
+ Some(effort.to_string())
+ },
+ );
+ }
}
});
});
@@ -8881,12 +8910,20 @@ impl ThreadView {
.unwrap_or(Speed::Fast);
thread.set_speed(new_speed, cx);
+ let favorite_key = thread
+ .model()
+ .map(|model| (model.provider_id().0.to_string(), model.id().0.to_string()));
let fs = thread.project().read(cx).fs().clone();
update_settings_file(fs, cx, move |settings, _| {
- if let Some(agent) = settings.agent.as_mut()
- && let Some(default_model) = agent.default_model.as_mut()
- {
- default_model.speed = Some(new_speed);
+ if let Some(agent) = settings.agent.as_mut() {
+ if let Some(default_model) = agent.default_model.as_mut() {
+ default_model.speed = Some(new_speed);
+ }
+ if let Some((provider_id, model_id)) = &favorite_key {
+ agent.update_favorite_model(provider_id, model_id, |favorite| {
+ favorite.speed = Some(new_speed)
+ });
+ }
}
});
});
@@ -8927,12 +8964,20 @@ impl ThreadView {
thread.update(cx, |thread, cx| {
thread.set_thinking_effort(Some(next_effort.clone()), cx);
+ let favorite_key = thread
+ .model()
+ .map(|model| (model.provider_id().0.to_string(), model.id().0.to_string()));
let fs = thread.project().read(cx).fs().clone();
update_settings_file(fs, cx, move |settings, _| {
- if let Some(agent) = settings.agent.as_mut()
- && let Some(default_model) = agent.default_model.as_mut()
- {
- default_model.effort = Some(next_effort);
+ if let Some(agent) = settings.agent.as_mut() {
+ if let Some(default_model) = agent.default_model.as_mut() {
+ default_model.effort = Some(next_effort.clone());
+ }
+ if let Some((provider_id, model_id)) = &favorite_key {
+ agent.update_favorite_model(provider_id, model_id, |favorite| {
+ favorite.effort = Some(next_effort)
+ });
+ }
}
});
});
@@ -1,27 +1,27 @@
use std::sync::Arc;
+use agent_settings::{AgentSettings, language_model_to_selection};
use fs::Fs;
use language_model::LanguageModel;
-use settings::{LanguageModelSelection, update_settings_file};
+use settings::{Settings as _, update_settings_file};
use ui::App;
-fn language_model_to_selection(model: &Arc<dyn LanguageModel>) -> LanguageModelSelection {
- LanguageModelSelection {
- provider: model.provider_id().to_string().into(),
- model: model.id().0.to_string(),
- enable_thinking: false,
- effort: None,
- speed: None,
- }
-}
-
pub fn toggle_in_settings(
model: Arc<dyn LanguageModel>,
should_be_favorite: bool,
fs: Arc<dyn Fs>,
cx: &mut App,
) {
- let selection = language_model_to_selection(&model);
+ let current_user_selection = AgentSettings::get_global(cx)
+ .default_model
+ .as_ref()
+ .filter(|selection| {
+ selection.provider.0 == model.provider_id().0.as_ref()
+ && selection.model == model.id().0.as_ref()
+ })
+ .cloned();
+
+ let selection = language_model_to_selection(&model, current_user_selection.as_ref());
update_settings_file(fs, cx, move |settings, _| {
let agent = settings.agent.get_or_insert_default();
if should_be_favorite {
@@ -275,13 +275,34 @@ impl AgentSettingsContent {
}
pub fn add_favorite_model(&mut self, model: LanguageModelSelection) {
- if !self.favorite_models.contains(&model) {
+ // Note: this is intentional to not compare using `PartialEq`here.
+ // Full equality would treat entries that differ just in thinking/effort/speed
+ // as distinct and silently produce duplicates.
+ if !self
+ .favorite_models
+ .iter()
+ .any(|m| m.provider == model.provider && m.model == model.model)
+ {
self.favorite_models.push(model);
}
}
pub fn remove_favorite_model(&mut self, model: &LanguageModelSelection) {
- self.favorite_models.retain(|m| m != model);
+ self.favorite_models
+ .retain(|m| !(m.provider == model.provider && m.model == model.model));
+ }
+
+ pub fn update_favorite_model<F>(&mut self, provider: &str, model: &str, f: F)
+ where
+ F: FnOnce(&mut LanguageModelSelection),
+ {
+ if let Some(entry) = self
+ .favorite_models
+ .iter_mut()
+ .find(|m| m.provider.0 == provider && m.model == model)
+ {
+ f(entry);
+ }
}
pub fn set_tool_default_permission(&mut self, tool_id: &str, mode: ToolPermissionMode) {