Introduce recent files ambient context for assistant (#11791)
<img width="1637" alt="image" src="https://github.com/zed-industries/zed/assets/482957/5aaec657-3499-42c9-9528-c83728f2a7a1"> Release Notes: - Added a new ambient context feature that allows showing the model up to three buffers (along with their diagnostics) that the user interacted with recently. --------- Co-authored-by: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
e4c95b25bf
commit
a13a92fbbf
9 changed files with 522 additions and 411 deletions
1
assets/icons/countdown_timer.svg
Normal file
1
assets/icons/countdown_timer.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.15 7.49998C13.15 4.66458 10.9402 1.84998 7.50002 1.84998C4.7217 1.84998 3.34851 3.90636 2.76336 4.99997H4.5C4.77614 4.99997 5 5.22383 5 5.49997C5 5.77611 4.77614 5.99997 4.5 5.99997H1.5C1.22386 5.99997 1 5.77611 1 5.49997V2.49997C1 2.22383 1.22386 1.99997 1.5 1.99997C1.77614 1.99997 2 2.22383 2 2.49997V4.31318C2.70453 3.07126 4.33406 0.849976 7.50002 0.849976C11.5628 0.849976 14.15 4.18537 14.15 7.49998C14.15 10.8146 11.5628 14.15 7.50002 14.15C5.55618 14.15 3.93778 13.3808 2.78548 12.2084C2.16852 11.5806 1.68668 10.839 1.35816 10.0407C1.25306 9.78536 1.37488 9.49315 1.63024 9.38806C1.8856 9.28296 2.17781 9.40478 2.2829 9.66014C2.56374 10.3425 2.97495 10.9745 3.4987 11.5074C4.47052 12.4963 5.83496 13.15 7.50002 13.15C10.9402 13.15 13.15 10.3354 13.15 7.49998ZM7 10V5.00001H8V10H7Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
After Width: | Height: | Size: 974 B |
|
@ -6,11 +6,8 @@ mod prompts;
|
||||||
mod saved_conversation;
|
mod saved_conversation;
|
||||||
mod streaming_diff;
|
mod streaming_diff;
|
||||||
|
|
||||||
mod embedded_scope;
|
|
||||||
|
|
||||||
pub use assistant_panel::AssistantPanel;
|
pub use assistant_panel::AssistantPanel;
|
||||||
use assistant_settings::{AssistantSettings, OpenAiModel, ZedDotDevModel};
|
use assistant_settings::{AssistantSettings, OpenAiModel, ZedDotDevModel};
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
use client::{proto, Client};
|
use client::{proto, Client};
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
pub(crate) use completion_provider::*;
|
pub(crate) use completion_provider::*;
|
||||||
|
@ -26,7 +23,6 @@ use std::{
|
||||||
actions!(
|
actions!(
|
||||||
assistant,
|
assistant,
|
||||||
[
|
[
|
||||||
NewConversation,
|
|
||||||
Assist,
|
Assist,
|
||||||
Split,
|
Split,
|
||||||
CycleMessageRole,
|
CycleMessageRole,
|
||||||
|
@ -35,6 +31,7 @@ actions!(
|
||||||
ResetKey,
|
ResetKey,
|
||||||
InlineAssist,
|
InlineAssist,
|
||||||
ToggleIncludeConversation,
|
ToggleIncludeConversation,
|
||||||
|
ToggleHistory,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -93,8 +90,8 @@ impl LanguageModel {
|
||||||
|
|
||||||
pub fn display_name(&self) -> String {
|
pub fn display_name(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
LanguageModel::OpenAi(model) => format!("openai/{}", model.display_name()),
|
LanguageModel::OpenAi(model) => model.display_name().into(),
|
||||||
LanguageModel::ZedDotDev(model) => format!("zed.dev/{}", model.display_name()),
|
LanguageModel::ZedDotDev(model) => model.display_name().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +175,6 @@ pub struct LanguageModelChoiceDelta {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
struct MessageMetadata {
|
struct MessageMetadata {
|
||||||
role: Role,
|
role: Role,
|
||||||
sent_at: DateTime<Local>,
|
|
||||||
status: MessageStatus,
|
status: MessageStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,91 +0,0 @@
|
||||||
use editor::MultiBuffer;
|
|
||||||
use gpui::{AppContext, Model, ModelContext, Subscription};
|
|
||||||
|
|
||||||
use crate::{assistant_panel::Conversation, LanguageModelRequestMessage, Role};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct EmbeddedScope {
|
|
||||||
active_buffer: Option<Model<MultiBuffer>>,
|
|
||||||
active_buffer_enabled: bool,
|
|
||||||
active_buffer_subscription: Option<Subscription>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EmbeddedScope {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
active_buffer: None,
|
|
||||||
active_buffer_enabled: true,
|
|
||||||
active_buffer_subscription: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_active_buffer(
|
|
||||||
&mut self,
|
|
||||||
buffer: Option<Model<MultiBuffer>>,
|
|
||||||
cx: &mut ModelContext<Conversation>,
|
|
||||||
) {
|
|
||||||
self.active_buffer_subscription.take();
|
|
||||||
|
|
||||||
if let Some(active_buffer) = buffer.clone() {
|
|
||||||
self.active_buffer_subscription =
|
|
||||||
Some(cx.subscribe(&active_buffer, |conversation, _, e, cx| {
|
|
||||||
if let multi_buffer::Event::Edited { .. } = e {
|
|
||||||
conversation.count_remaining_tokens(cx)
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.active_buffer = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn active_buffer(&self) -> Option<&Model<MultiBuffer>> {
|
|
||||||
self.active_buffer.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn active_buffer_enabled(&self) -> bool {
|
|
||||||
self.active_buffer_enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_active_buffer_enabled(&mut self, enabled: bool) {
|
|
||||||
self.active_buffer_enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provide a message for the language model based on the active buffer.
|
|
||||||
pub fn message(&self, cx: &AppContext) -> Option<LanguageModelRequestMessage> {
|
|
||||||
if !self.active_buffer_enabled {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let active_buffer = self.active_buffer.as_ref()?;
|
|
||||||
let buffer = active_buffer.read(cx);
|
|
||||||
|
|
||||||
if let Some(singleton) = buffer.as_singleton() {
|
|
||||||
let singleton = singleton.read(cx);
|
|
||||||
|
|
||||||
let filename = singleton
|
|
||||||
.file()
|
|
||||||
.map(|file| file.path().to_string_lossy())
|
|
||||||
.unwrap_or("Untitled".into());
|
|
||||||
|
|
||||||
let text = singleton.text();
|
|
||||||
|
|
||||||
let language = singleton
|
|
||||||
.language()
|
|
||||||
.map(|l| {
|
|
||||||
let name = l.code_fence_block_name();
|
|
||||||
name.to_string()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let markdown =
|
|
||||||
format!("User's active file `{filename}`:\n\n```{language}\n{text}```\n\n");
|
|
||||||
|
|
||||||
return Some(LanguageModelRequestMessage {
|
|
||||||
role: Role::System,
|
|
||||||
content: markdown,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -200,6 +200,18 @@ impl FocusHandle {
|
||||||
pub fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
|
pub fn contains(&self, other: &Self, cx: &WindowContext) -> bool {
|
||||||
self.id.contains(other.id, cx)
|
self.id.contains(other.id, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dispatch an action on the element that rendered this focus handle
|
||||||
|
pub fn dispatch_action(&self, action: &dyn Action, cx: &mut WindowContext) {
|
||||||
|
if let Some(node_id) = cx
|
||||||
|
.window
|
||||||
|
.rendered_frame
|
||||||
|
.dispatch_tree
|
||||||
|
.focusable_node_id(self.id)
|
||||||
|
{
|
||||||
|
cx.dispatch_action_on_node(node_id, action)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for FocusHandle {
|
impl Clone for FocusHandle {
|
||||||
|
|
|
@ -189,8 +189,8 @@ impl TabSwitcherDelegate {
|
||||||
let pane = pane.read(cx);
|
let pane = pane.read(cx);
|
||||||
let mut history_indices = HashMap::default();
|
let mut history_indices = HashMap::default();
|
||||||
pane.activation_history().iter().rev().enumerate().for_each(
|
pane.activation_history().iter().rev().enumerate().for_each(
|
||||||
|(history_index, entity_id)| {
|
|(history_index, history_entry)| {
|
||||||
history_indices.insert(entity_id, history_index);
|
history_indices.insert(history_entry.entity_id, history_index);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,7 @@ pub enum IconName {
|
||||||
CopilotError,
|
CopilotError,
|
||||||
CopilotInit,
|
CopilotInit,
|
||||||
Copy,
|
Copy,
|
||||||
|
CountdownTimer,
|
||||||
Dash,
|
Dash,
|
||||||
Delete,
|
Delete,
|
||||||
Disconnected,
|
Disconnected,
|
||||||
|
@ -221,6 +222,7 @@ impl IconName {
|
||||||
IconName::CopilotError => "icons/copilot_error.svg",
|
IconName::CopilotError => "icons/copilot_error.svg",
|
||||||
IconName::CopilotInit => "icons/copilot_init.svg",
|
IconName::CopilotInit => "icons/copilot_init.svg",
|
||||||
IconName::Copy => "icons/copy.svg",
|
IconName::Copy => "icons/copy.svg",
|
||||||
|
IconName::CountdownTimer => "icons/countdown_timer.svg",
|
||||||
IconName::Dash => "icons/dash.svg",
|
IconName::Dash => "icons/dash.svg",
|
||||||
IconName::Delete => "icons/delete.svg",
|
IconName::Delete => "icons/delete.svg",
|
||||||
IconName::Disconnected => "icons/disconnected.svg",
|
IconName::Disconnected => "icons/disconnected.svg",
|
||||||
|
|
|
@ -191,7 +191,8 @@ pub struct Pane {
|
||||||
),
|
),
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
items: Vec<Box<dyn ItemHandle>>,
|
items: Vec<Box<dyn ItemHandle>>,
|
||||||
activation_history: Vec<EntityId>,
|
activation_history: Vec<ActivationHistoryEntry>,
|
||||||
|
next_activation_timestamp: Arc<AtomicUsize>,
|
||||||
zoomed: bool,
|
zoomed: bool,
|
||||||
was_focused: bool,
|
was_focused: bool,
|
||||||
active_item_index: usize,
|
active_item_index: usize,
|
||||||
|
@ -219,6 +220,11 @@ pub struct Pane {
|
||||||
double_click_dispatch_action: Box<dyn Action>,
|
double_click_dispatch_action: Box<dyn Action>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ActivationHistoryEntry {
|
||||||
|
pub entity_id: EntityId,
|
||||||
|
pub timestamp: usize,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ItemNavHistory {
|
pub struct ItemNavHistory {
|
||||||
history: NavHistory,
|
history: NavHistory,
|
||||||
item: Arc<dyn WeakItemHandle>,
|
item: Arc<dyn WeakItemHandle>,
|
||||||
|
@ -296,6 +302,7 @@ impl Pane {
|
||||||
focus_handle,
|
focus_handle,
|
||||||
items: Vec::new(),
|
items: Vec::new(),
|
||||||
activation_history: Vec::new(),
|
activation_history: Vec::new(),
|
||||||
|
next_activation_timestamp: next_timestamp.clone(),
|
||||||
was_focused: false,
|
was_focused: false,
|
||||||
zoomed: false,
|
zoomed: false,
|
||||||
active_item_index: 0,
|
active_item_index: 0,
|
||||||
|
@ -506,7 +513,7 @@ impl Pane {
|
||||||
self.active_item_index
|
self.active_item_index
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activation_history(&self) -> &Vec<EntityId> {
|
pub fn activation_history(&self) -> &[ActivationHistoryEntry] {
|
||||||
&self.activation_history
|
&self.activation_history
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -892,10 +899,13 @@ impl Pane {
|
||||||
|
|
||||||
if let Some(newly_active_item) = self.items.get(index) {
|
if let Some(newly_active_item) = self.items.get(index) {
|
||||||
self.activation_history
|
self.activation_history
|
||||||
.retain(|&previously_active_item_id| {
|
.retain(|entry| entry.entity_id != newly_active_item.item_id());
|
||||||
previously_active_item_id != newly_active_item.item_id()
|
self.activation_history.push(ActivationHistoryEntry {
|
||||||
});
|
entity_id: newly_active_item.item_id(),
|
||||||
self.activation_history.push(newly_active_item.item_id());
|
timestamp: self
|
||||||
|
.next_activation_timestamp
|
||||||
|
.fetch_add(1, Ordering::SeqCst),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_toolbar(cx);
|
self.update_toolbar(cx);
|
||||||
|
@ -1211,7 +1221,7 @@ impl Pane {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
self.activation_history
|
self.activation_history
|
||||||
.retain(|&history_entry| history_entry != self.items[item_index].item_id());
|
.retain(|entry| entry.entity_id != self.items[item_index].item_id());
|
||||||
|
|
||||||
if item_index == self.active_item_index {
|
if item_index == self.active_item_index {
|
||||||
let index_to_activate = self
|
let index_to_activate = self
|
||||||
|
@ -1219,7 +1229,7 @@ impl Pane {
|
||||||
.pop()
|
.pop()
|
||||||
.and_then(|last_activated_item| {
|
.and_then(|last_activated_item| {
|
||||||
self.items.iter().enumerate().find_map(|(index, item)| {
|
self.items.iter().enumerate().find_map(|(index, item)| {
|
||||||
(item.item_id() == last_activated_item).then_some(index)
|
(item.item_id() == last_activated_item.entity_id).then_some(index)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// We didn't have a valid activation history entry, so fallback
|
// We didn't have a valid activation history entry, so fallback
|
||||||
|
|
|
@ -532,6 +532,9 @@ impl DelayedDebouncedEditAction {
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
PaneAdded(View<Pane>),
|
PaneAdded(View<Pane>),
|
||||||
|
PaneRemoved,
|
||||||
|
ItemAdded,
|
||||||
|
ItemRemoved,
|
||||||
ActiveItemChanged,
|
ActiveItemChanged,
|
||||||
ContactRequestedJoin(u64),
|
ContactRequestedJoin(u64),
|
||||||
WorkspaceCreated(WeakView<Workspace>),
|
WorkspaceCreated(WeakView<Workspace>),
|
||||||
|
@ -2513,7 +2516,10 @@ impl Workspace {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx),
|
pane::Event::AddItem { item } => {
|
||||||
|
item.added_to_pane(self, pane, cx);
|
||||||
|
cx.emit(Event::ItemAdded);
|
||||||
|
}
|
||||||
pane::Event::Split(direction) => {
|
pane::Event::Split(direction) => {
|
||||||
self.split_and_clone(pane, *direction, cx);
|
self.split_and_clone(pane, *direction, cx);
|
||||||
}
|
}
|
||||||
|
@ -2696,6 +2702,7 @@ impl Workspace {
|
||||||
} else {
|
} else {
|
||||||
self.active_item_path_changed(cx);
|
self.active_item_path_changed(cx);
|
||||||
}
|
}
|
||||||
|
cx.emit(Event::PaneRemoved);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panes(&self) -> &[View<Pane>] {
|
pub fn panes(&self) -> &[View<Pane>] {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue