ACP history mentions (#36551)
- **TEMP** - **Update @-mentions to use new history** Closes #ISSUE Release Notes: - N/A
This commit is contained in:
parent
159b5e9fb5
commit
5d2bb2466e
17 changed files with 581 additions and 392 deletions
|
@ -8,6 +8,9 @@ license = "GPL-3.0-or-later"
|
|||
[lib]
|
||||
path = "src/agent2.rs"
|
||||
|
||||
[features]
|
||||
test-support = ["db/test-support"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
|
@ -72,6 +75,7 @@ ctor.workspace = true
|
|||
client = { workspace = true, "features" = ["test-support"] }
|
||||
clock = { workspace = true, "features" = ["test-support"] }
|
||||
context_server = { workspace = true, "features" = ["test-support"] }
|
||||
db = { workspace = true, "features" = ["test-support"] }
|
||||
editor = { workspace = true, "features" = ["test-support"] }
|
||||
env_logger.workspace = true
|
||||
fs = { workspace = true, "features" = ["test-support"] }
|
||||
|
|
|
@ -536,6 +536,28 @@ impl NativeAgent {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn thread_summary(
|
||||
&mut self,
|
||||
id: acp::SessionId,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<SharedString>> {
|
||||
let thread = self.open_thread(id.clone(), cx);
|
||||
cx.spawn(async move |this, cx| {
|
||||
let acp_thread = thread.await?;
|
||||
let result = this
|
||||
.update(cx, |this, cx| {
|
||||
this.sessions
|
||||
.get(&id)
|
||||
.unwrap()
|
||||
.thread
|
||||
.update(cx, |thread, cx| thread.summary(cx))
|
||||
})?
|
||||
.await?;
|
||||
drop(acp_thread);
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
|
||||
fn save_thread(&mut self, thread: Entity<Thread>, cx: &mut Context<Self>) {
|
||||
let database_future = ThreadsDatabase::connect(cx);
|
||||
let (id, db_thread) =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{AgentMessage, AgentMessageContent, UserMessage, UserMessageContent};
|
||||
use acp_thread::UserMessageId;
|
||||
use agent::thread_store;
|
||||
use agent::{thread::DetailedSummaryState, thread_store};
|
||||
use agent_client_protocol as acp;
|
||||
use agent_settings::{AgentProfileId, CompletionMode};
|
||||
use anyhow::{Result, anyhow};
|
||||
|
@ -20,7 +20,7 @@ use std::sync::Arc;
|
|||
use ui::{App, SharedString};
|
||||
|
||||
pub type DbMessage = crate::Message;
|
||||
pub type DbSummary = agent::thread::DetailedSummaryState;
|
||||
pub type DbSummary = DetailedSummaryState;
|
||||
pub type DbLanguageModel = thread_store::SerializedLanguageModel;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -37,7 +37,7 @@ pub struct DbThread {
|
|||
pub messages: Vec<DbMessage>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
#[serde(default)]
|
||||
pub summary: DbSummary,
|
||||
pub detailed_summary: Option<SharedString>,
|
||||
#[serde(default)]
|
||||
pub initial_project_snapshot: Option<Arc<agent::thread::ProjectSnapshot>>,
|
||||
#[serde(default)]
|
||||
|
@ -185,7 +185,12 @@ impl DbThread {
|
|||
title: thread.summary,
|
||||
messages,
|
||||
updated_at: thread.updated_at,
|
||||
summary: thread.detailed_summary_state,
|
||||
detailed_summary: match thread.detailed_summary_state {
|
||||
DetailedSummaryState::NotGenerated | DetailedSummaryState::Generating { .. } => {
|
||||
None
|
||||
}
|
||||
DetailedSummaryState::Generated { text, .. } => Some(text),
|
||||
},
|
||||
initial_project_snapshot: thread.initial_project_snapshot,
|
||||
cumulative_token_usage: thread.cumulative_token_usage,
|
||||
request_token_usage,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{DbThreadMetadata, ThreadsDatabase};
|
||||
use acp_thread::MentionUri;
|
||||
use agent_client_protocol as acp;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use assistant_context::SavedContextMetadata;
|
||||
use assistant_context::{AssistantContext, SavedContextMetadata};
|
||||
use chrono::{DateTime, Utc};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
|
||||
|
@ -38,6 +39,19 @@ impl HistoryEntry {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mention_uri(&self) -> MentionUri {
|
||||
match self {
|
||||
HistoryEntry::AcpThread(thread) => MentionUri::Thread {
|
||||
id: thread.id.clone(),
|
||||
name: thread.title.to_string(),
|
||||
},
|
||||
HistoryEntry::TextThread(context) => MentionUri::TextThread {
|
||||
path: context.path.as_ref().to_owned(),
|
||||
name: context.title.to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &SharedString {
|
||||
match self {
|
||||
HistoryEntry::AcpThread(thread) if thread.title.is_empty() => DEFAULT_TITLE,
|
||||
|
@ -48,7 +62,7 @@ impl HistoryEntry {
|
|||
}
|
||||
|
||||
/// Generic identifier for a history entry.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum HistoryEntryId {
|
||||
AcpThread(acp::SessionId),
|
||||
TextThread(Arc<Path>),
|
||||
|
@ -120,6 +134,16 @@ impl HistoryStore {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn load_text_thread(
|
||||
&self,
|
||||
path: Arc<Path>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Entity<AssistantContext>>> {
|
||||
self.context_store.update(cx, |context_store, cx| {
|
||||
context_store.open_local_context(path, cx)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reload(&self, cx: &mut Context<Self>) {
|
||||
let database_future = ThreadsDatabase::connect(cx);
|
||||
cx.spawn(async move |this, cx| {
|
||||
|
@ -149,7 +173,7 @@ impl HistoryStore {
|
|||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
pub fn entries(&self, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
|
||||
pub fn entries(&self, cx: &App) -> Vec<HistoryEntry> {
|
||||
let mut history_entries = Vec::new();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
@ -180,10 +204,6 @@ impl HistoryStore {
|
|||
.is_none()
|
||||
}
|
||||
|
||||
pub fn recent_entries(&self, limit: usize, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
|
||||
self.entries(cx).into_iter().take(limit).collect()
|
||||
}
|
||||
|
||||
pub fn recently_opened_entries(&self, cx: &App) -> Vec<HistoryEntry> {
|
||||
#[cfg(debug_assertions)]
|
||||
if std::env::var("ZED_SIMULATE_NO_THREAD_HISTORY").is_ok() {
|
||||
|
@ -246,6 +266,10 @@ impl HistoryStore {
|
|||
cx.background_executor()
|
||||
.timer(SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE)
|
||||
.await;
|
||||
|
||||
if cfg!(any(feature = "test-support", test)) {
|
||||
return;
|
||||
}
|
||||
KEY_VALUE_STORE
|
||||
.write_kvp(RECENTLY_OPENED_THREADS_KEY.to_owned(), content)
|
||||
.await
|
||||
|
@ -255,6 +279,9 @@ impl HistoryStore {
|
|||
|
||||
fn load_recently_opened_entries(cx: &AsyncApp) -> Task<Result<VecDeque<HistoryEntryId>>> {
|
||||
cx.background_spawn(async move {
|
||||
if cfg!(any(feature = "test-support", test)) {
|
||||
anyhow::bail!("history store does not persist in tests");
|
||||
}
|
||||
let json = KEY_VALUE_STORE
|
||||
.read_kvp(RECENTLY_OPENED_THREADS_KEY)?
|
||||
.unwrap_or("[]".to_string());
|
||||
|
|
|
@ -6,9 +6,12 @@ use crate::{
|
|||
};
|
||||
use acp_thread::{MentionUri, UserMessageId};
|
||||
use action_log::ActionLog;
|
||||
use agent::thread::{DetailedSummaryState, GitState, ProjectSnapshot, WorktreeSnapshot};
|
||||
use agent::thread::{GitState, ProjectSnapshot, WorktreeSnapshot};
|
||||
use agent_client_protocol as acp;
|
||||
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode, SUMMARIZE_THREAD_PROMPT};
|
||||
use agent_settings::{
|
||||
AgentProfileId, AgentSettings, CompletionMode, SUMMARIZE_THREAD_DETAILED_PROMPT,
|
||||
SUMMARIZE_THREAD_PROMPT,
|
||||
};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use assistant_tool::adapt_schema_to_format;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
@ -499,8 +502,7 @@ pub struct Thread {
|
|||
prompt_id: PromptId,
|
||||
updated_at: DateTime<Utc>,
|
||||
title: Option<SharedString>,
|
||||
#[allow(unused)]
|
||||
summary: DetailedSummaryState,
|
||||
summary: Option<SharedString>,
|
||||
messages: Vec<Message>,
|
||||
completion_mode: CompletionMode,
|
||||
/// Holds the task that handles agent interaction until the end of the turn.
|
||||
|
@ -541,7 +543,7 @@ impl Thread {
|
|||
prompt_id: PromptId::new(),
|
||||
updated_at: Utc::now(),
|
||||
title: None,
|
||||
summary: DetailedSummaryState::default(),
|
||||
summary: None,
|
||||
messages: Vec::new(),
|
||||
completion_mode: AgentSettings::get_global(cx).preferred_completion_mode,
|
||||
running_turn: None,
|
||||
|
@ -691,7 +693,7 @@ impl Thread {
|
|||
} else {
|
||||
Some(db_thread.title.clone())
|
||||
},
|
||||
summary: db_thread.summary,
|
||||
summary: db_thread.detailed_summary,
|
||||
messages: db_thread.messages,
|
||||
completion_mode: db_thread.completion_mode.unwrap_or_default(),
|
||||
running_turn: None,
|
||||
|
@ -719,7 +721,7 @@ impl Thread {
|
|||
title: self.title.clone().unwrap_or_default(),
|
||||
messages: self.messages.clone(),
|
||||
updated_at: self.updated_at,
|
||||
summary: self.summary.clone(),
|
||||
detailed_summary: self.summary.clone(),
|
||||
initial_project_snapshot: None,
|
||||
cumulative_token_usage: self.cumulative_token_usage,
|
||||
request_token_usage: self.request_token_usage.clone(),
|
||||
|
@ -976,7 +978,7 @@ impl Thread {
|
|||
Message::Agent(_) | Message::Resume => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.summary = None;
|
||||
cx.notify();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1047,6 +1049,7 @@ impl Thread {
|
|||
let event_stream = ThreadEventStream(events_tx);
|
||||
let message_ix = self.messages.len().saturating_sub(1);
|
||||
self.tool_use_limit_reached = false;
|
||||
self.summary = None;
|
||||
self.running_turn = Some(RunningTurn {
|
||||
event_stream: event_stream.clone(),
|
||||
_task: cx.spawn(async move |this, cx| {
|
||||
|
@ -1507,6 +1510,63 @@ impl Thread {
|
|||
self.title.clone().unwrap_or("New Thread".into())
|
||||
}
|
||||
|
||||
pub fn summary(&mut self, cx: &mut Context<Self>) -> Task<Result<SharedString>> {
|
||||
if let Some(summary) = self.summary.as_ref() {
|
||||
return Task::ready(Ok(summary.clone()));
|
||||
}
|
||||
let Some(model) = self.summarization_model.clone() else {
|
||||
return Task::ready(Err(anyhow!("No summarization model available")));
|
||||
};
|
||||
let mut request = LanguageModelRequest {
|
||||
intent: Some(CompletionIntent::ThreadSummarization),
|
||||
temperature: AgentSettings::temperature_for_model(&model, cx),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for message in &self.messages {
|
||||
request.messages.extend(message.to_request());
|
||||
}
|
||||
|
||||
request.messages.push(LanguageModelRequestMessage {
|
||||
role: Role::User,
|
||||
content: vec![SUMMARIZE_THREAD_DETAILED_PROMPT.into()],
|
||||
cache: false,
|
||||
});
|
||||
cx.spawn(async move |this, cx| {
|
||||
let mut summary = String::new();
|
||||
let mut messages = model.stream_completion(request, cx).await?;
|
||||
while let Some(event) = messages.next().await {
|
||||
let event = event?;
|
||||
let text = match event {
|
||||
LanguageModelCompletionEvent::Text(text) => text,
|
||||
LanguageModelCompletionEvent::StatusUpdate(
|
||||
CompletionRequestStatus::UsageUpdated { .. },
|
||||
) => {
|
||||
// this.update(cx, |thread, cx| {
|
||||
// thread.update_model_request_usage(amount as u32, limit, cx);
|
||||
// })?;
|
||||
// TODO: handle usage update
|
||||
continue;
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let mut lines = text.lines();
|
||||
summary.extend(lines.next());
|
||||
}
|
||||
|
||||
log::info!("Setting summary: {}", summary);
|
||||
let summary = SharedString::from(summary);
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
this.summary = Some(summary.clone());
|
||||
cx.notify()
|
||||
})?;
|
||||
|
||||
Ok(summary)
|
||||
})
|
||||
}
|
||||
|
||||
fn update_title(
|
||||
&mut self,
|
||||
event_stream: &ThreadEventStream,
|
||||
|
@ -1617,6 +1677,7 @@ impl Thread {
|
|||
|
||||
self.messages.push(Message::Agent(message));
|
||||
self.updated_at = Utc::now();
|
||||
self.summary = None;
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue