Introduce the ability to cycle between alternative inline assists (#18098)

Release Notes:

- Added a new `assistant.inline_alternatives` setting to configure
additional models that will be used to perform inline assists in
parallel.

---------

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Roy <roy@anthropic.com>
Co-authored-by: Adam <wolffiex@anthropic.com>
This commit is contained in:
Antonio Scandurra 2024-09-19 17:50:00 -06:00 committed by GitHub
parent ae34872f73
commit 15b4130fa5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 642 additions and 178 deletions

View file

@ -520,6 +520,13 @@
"alt-enter": "editor::Newline" "alt-enter": "editor::Newline"
} }
}, },
{
"context": "PromptEditor",
"bindings": {
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist"
}
},
{ {
"context": "ProjectSearchBar && !in_replace", "context": "ProjectSearchBar && !in_replace",
"bindings": { "bindings": {

View file

@ -527,6 +527,13 @@
"ctrl-enter": "assistant::InlineAssist" "ctrl-enter": "assistant::InlineAssist"
} }
}, },
{
"context": "PromptEditor",
"bindings": {
"ctrl-[": "assistant::CyclePreviousInlineAssist",
"ctrl-]": "assistant::CycleNextInlineAssist"
}
},
{ {
"context": "ProjectSearchBar && !in_replace", "context": "ProjectSearchBar && !in_replace",
"bindings": { "bindings": {

View file

@ -69,6 +69,8 @@ actions!(
ConfirmCommand, ConfirmCommand,
NewContext, NewContext,
ToggleModelSelector, ToggleModelSelector,
CycleNextInlineAssist,
CyclePreviousInlineAssist
] ]
); );
@ -359,8 +361,19 @@ fn update_active_language_model_from_settings(cx: &mut AppContext) {
let settings = AssistantSettings::get_global(cx); let settings = AssistantSettings::get_global(cx);
let provider_name = LanguageModelProviderId::from(settings.default_model.provider.clone()); let provider_name = LanguageModelProviderId::from(settings.default_model.provider.clone());
let model_id = LanguageModelId::from(settings.default_model.model.clone()); let model_id = LanguageModelId::from(settings.default_model.model.clone());
let inline_alternatives = settings
.inline_alternatives
.iter()
.map(|alternative| {
(
LanguageModelProviderId::from(alternative.provider.clone()),
LanguageModelId::from(alternative.model.clone()),
)
})
.collect::<Vec<_>>();
LanguageModelRegistry::global(cx).update(cx, |registry, cx| { LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
registry.select_active_model(&provider_name, &model_id, cx); registry.select_active_model(&provider_name, &model_id, cx);
registry.select_inline_alternative_models(inline_alternatives, cx);
}); });
} }

View file

@ -59,6 +59,7 @@ pub struct AssistantSettings {
pub default_width: Pixels, pub default_width: Pixels,
pub default_height: Pixels, pub default_height: Pixels,
pub default_model: LanguageModelSelection, pub default_model: LanguageModelSelection,
pub inline_alternatives: Vec<LanguageModelSelection>,
pub using_outdated_settings_version: bool, pub using_outdated_settings_version: bool,
} }
@ -236,6 +237,7 @@ impl AssistantSettingsContent {
}) })
} }
}), }),
inline_alternatives: None,
}, },
VersionedAssistantSettingsContent::V2(settings) => settings.clone(), VersionedAssistantSettingsContent::V2(settings) => settings.clone(),
}, },
@ -254,6 +256,7 @@ impl AssistantSettingsContent {
.id() .id()
.to_string(), .to_string(),
}), }),
inline_alternatives: None,
}, },
} }
} }
@ -369,6 +372,7 @@ impl Default for VersionedAssistantSettingsContent {
default_width: None, default_width: None,
default_height: None, default_height: None,
default_model: None, default_model: None,
inline_alternatives: None,
}) })
} }
} }
@ -397,6 +401,8 @@ pub struct AssistantSettingsContentV2 {
default_height: Option<f32>, default_height: Option<f32>,
/// The default model to use when creating new contexts. /// The default model to use when creating new contexts.
default_model: Option<LanguageModelSelection>, default_model: Option<LanguageModelSelection>,
/// Additional models with which to generate alternatives when performing inline assists.
inline_alternatives: Option<Vec<LanguageModelSelection>>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
@ -517,10 +523,8 @@ impl Settings for AssistantSettings {
&mut settings.default_height, &mut settings.default_height,
value.default_height.map(Into::into), value.default_height.map(Into::into),
); );
merge( merge(&mut settings.default_model, value.default_model);
&mut settings.default_model, merge(&mut settings.inline_alternatives, value.inline_alternatives);
value.default_model.map(Into::into),
);
// merge(&mut settings.infer_context, value.infer_context); TODO re-enable this once we ship context inference // merge(&mut settings.infer_context, value.infer_context); TODO re-enable this once we ship context inference
} }
@ -574,6 +578,7 @@ mod tests {
provider: "test-provider".into(), provider: "test-provider".into(),
model: "gpt-99".into(), model: "gpt-99".into(),
}), }),
inline_alternatives: None,
enabled: None, enabled: None,
button: None, button: None,
dock: None, dock: None,

File diff suppressed because it is too large Load diff

View file

@ -76,6 +76,7 @@ impl Global for GlobalLanguageModelRegistry {}
pub struct LanguageModelRegistry { pub struct LanguageModelRegistry {
active_model: Option<ActiveModel>, active_model: Option<ActiveModel>,
providers: BTreeMap<LanguageModelProviderId, Arc<dyn LanguageModelProvider>>, providers: BTreeMap<LanguageModelProviderId, Arc<dyn LanguageModelProvider>>,
inline_alternatives: Vec<Arc<dyn LanguageModel>>,
} }
pub struct ActiveModel { pub struct ActiveModel {
@ -229,6 +230,37 @@ impl LanguageModelRegistry {
pub fn active_model(&self) -> Option<Arc<dyn LanguageModel>> { pub fn active_model(&self) -> Option<Arc<dyn LanguageModel>> {
self.active_model.as_ref()?.model.clone() self.active_model.as_ref()?.model.clone()
} }
/// Selects and sets the inline alternatives for language models based on
/// provider name and id.
pub fn select_inline_alternative_models(
&mut self,
alternatives: impl IntoIterator<Item = (LanguageModelProviderId, LanguageModelId)>,
cx: &mut ModelContext<Self>,
) {
let mut selected_alternatives = Vec::new();
for (provider_id, model_id) in alternatives {
if let Some(provider) = self.providers.get(&provider_id) {
if let Some(model) = provider
.provided_models(cx)
.iter()
.find(|m| m.id() == model_id)
{
selected_alternatives.push(model.clone());
}
}
}
self.inline_alternatives = selected_alternatives;
}
/// The models to use for inline assists. Returns the union of the active
/// model and all inline alternatives. When there are multiple models, the
/// user will be able to cycle through results.
pub fn inline_alternative_models(&self) -> &[Arc<dyn LanguageModel>] {
&self.inline_alternatives
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1106,6 +1106,26 @@ impl MultiBuffer {
} }
} }
pub fn forget_transaction(
&mut self,
transaction_id: TransactionId,
cx: &mut ModelContext<Self>,
) {
if let Some(buffer) = self.as_singleton() {
buffer.update(cx, |buffer, _| {
buffer.forget_transaction(transaction_id);
});
} else if let Some(transaction) = self.history.forget(transaction_id) {
for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions {
if let Some(state) = self.buffers.borrow_mut().get_mut(&buffer_id) {
state.buffer.update(cx, |buffer, _| {
buffer.forget_transaction(buffer_transaction_id);
});
}
}
}
}
pub fn stream_excerpts_with_context_lines( pub fn stream_excerpts_with_context_lines(
&mut self, &mut self,
buffer: Model<Buffer>, buffer: Model<Buffer>,

View file

@ -20,6 +20,7 @@ To further customize providers, you can use `settings.json` to do that as follow
- [Configuring endpoints](#custom-endpoint) - [Configuring endpoints](#custom-endpoint)
- [Configuring timeouts](#provider-timeout) - [Configuring timeouts](#provider-timeout)
- [Configuring default model](#default-model) - [Configuring default model](#default-model)
- [Configuring alternative models for inline assists](#alternative-assists)
### Zed AI {#zed-ai} ### Zed AI {#zed-ai}
@ -264,6 +265,31 @@ You can also manually edit the `default_model` object in your settings:
} }
``` ```
#### Configuring alternative models for inline assists {#alternative-assists}
You can configure additional models that will be used to perform inline assists in parallel. When you do this,
the inline assist UI will surface controls to cycle between the alternatives generated by each model. The models
you specify here are always used in _addition_ to your default model. For example, the following configuration
will generate two outputs for every assist. One with Claude 3.5 Sonnet, and one with GPT-4o.
```json
{
"assistant": {
"default_model": {
"provider": "zed.dev",
"model": "claude-3-5-sonnet"
},
"inline_alternatives": [
{
"provider": "zed.dev",
"model": "gpt-4o"
}
],
"version": "2"
}
}
```
#### Common Panel Settings #### Common Panel Settings
| key | type | default | description | | key | type | default | description |