Wire through title update

This commit is contained in:
Conrad Irwin 2025-08-18 21:03:31 -06:00
parent 3ed2b7691b
commit 8998cdee26
4 changed files with 82 additions and 51 deletions

View file

@ -937,6 +937,7 @@ impl AcpThread {
} }
pub fn update_title(&mut self, title: SharedString, cx: &mut Context<Self>) -> Result<()> { pub fn update_title(&mut self, title: SharedString, cx: &mut Context<Self>) -> Result<()> {
dbg!("update title", &title);
self.title = title; self.title = title;
cx.emit(AcpThreadEvent::TitleUpdated); cx.emit(AcpThreadEvent::TitleUpdated);
Ok(()) Ok(())

View file

@ -244,20 +244,28 @@ impl NativeAgent {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let id = thread.read(cx).id().clone(); let id = thread.read(cx).id().clone();
let weak_thread = acp_thread.downgrade();
self.sessions.insert( self.sessions.insert(
id, id,
Session { Session {
thread: thread.clone(), thread: thread.clone(),
acp_thread: acp_thread.downgrade(), acp_thread: weak_thread.clone(),
save_task: Task::ready(Ok(())), save_task: Task::ready(Ok(())),
_subscriptions: vec![ _subscriptions: vec![
cx.observe_release(&acp_thread, |this, acp_thread, _cx| { cx.observe_release(&acp_thread, |this, acp_thread, _cx| {
this.sessions.remove(acp_thread.session_id()); this.sessions.remove(acp_thread.session_id());
}), }),
cx.observe(&thread, |this, thread, cx| { cx.observe(&thread, move |this, thread, cx| {
thread.update(cx, |thread, cx| { if let Some(response_stream) =
thread.generate_title_if_needed(cx); thread.update(cx, |thread, cx| thread.generate_title_if_needed(cx))
}); {
NativeAgentConnection::handle_thread_events(
response_stream,
weak_thread.clone(),
cx,
)
.detach_and_log_err(cx);
}
this.save_thread(thread.clone(), cx) this.save_thread(thread.clone(), cx)
}), }),
], ],
@ -659,6 +667,7 @@ impl NativeAgentConnection {
})??; })??;
} }
ThreadEvent::TitleUpdate(title) => { ThreadEvent::TitleUpdate(title) => {
dbg!("updating title");
acp_thread acp_thread
.update(cx, |thread, cx| thread.update_title(title, cx))??; .update(cx, |thread, cx| thread.update_title(title, cx))??;
} }

View file

@ -87,6 +87,7 @@ impl HistoryStore {
let history = AgentHistory { let history = AgentHistory {
entries: history.clone(), entries: history.clone(),
_task: cx.spawn(async move |this, cx| { _task: cx.spawn(async move |this, cx| {
dbg!("loaded", history.borrow().as_ref().map(|b| b.len()));
while history.changed().await.is_ok() { while history.changed().await.is_ok() {
this.update(cx, |_, cx| cx.notify()).ok(); this.update(cx, |_, cx| cx.notify()).ok();
} }

View file

@ -458,7 +458,7 @@ pub struct ToolCallAuthorization {
enum ThreadTitle { enum ThreadTitle {
None, None,
Pending(Task<()>), Pending(Shared<Task<()>>),
Done(Result<SharedString>), Done(Result<SharedString>),
} }
@ -1082,24 +1082,33 @@ impl Thread {
Ok(events_rx) Ok(events_rx)
} }
pub fn generate_title_if_needed(&mut self, cx: &mut Context<Self>) { pub fn generate_title_if_needed(
&mut self,
cx: &mut Context<Self>,
) -> Option<mpsc::UnboundedReceiver<Result<ThreadEvent>>> {
if !matches!(self.title, ThreadTitle::None) { if !matches!(self.title, ThreadTitle::None) {
return; return None;
} }
// todo!() copy logic from agent1 re: tool calls, etc.? // todo!() copy logic from agent1 re: tool calls, etc.?
if self.messages.len() < 2 { if self.messages.len() < 2 {
return; return None;
} }
let Some(model) = self.summarization_model.clone() else {
return None;
};
let (tx, rx) = mpsc::unbounded();
self.generate_title(cx); self.generate_title(model, ThreadEventStream(tx), cx);
Some(rx)
} }
fn generate_title(&mut self, cx: &mut Context<Self>) { fn generate_title(
let Some(model) = self.summarization_model.clone() else { &mut self,
println!("No thread summary model"); model: Arc<dyn LanguageModel>,
return; event_stream: ThreadEventStream,
}; cx: &mut Context<Self>,
) {
let mut request = LanguageModelRequest { let mut request = LanguageModelRequest {
intent: Some(CompletionIntent::ThreadSummarization), intent: Some(CompletionIntent::ThreadSummarization),
temperature: AgentSettings::temperature_for_model(&model, cx), temperature: AgentSettings::temperature_for_model(&model, cx),
@ -1116,50 +1125,55 @@ impl Thread {
cache: false, cache: false,
}); });
let task = cx.spawn(async move |this, cx| { let task = cx
let result = async { .spawn(async move |this, cx| {
let mut messages = model.stream_completion(request, &cx).await?; let result: anyhow::Result<SharedString> = async {
let mut messages = model.stream_completion(request, &cx).await?;
let mut new_summary = String::new(); let mut new_summary = String::new();
while let Some(event) = messages.next().await { while let Some(event) = messages.next().await {
let Ok(event) = event else { let Ok(event) = event else {
continue;
};
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!()? not sure if this is the right place to do this.
continue; continue;
};
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!()? not sure if this is the right place to do this.
continue;
}
_ => continue,
};
let mut lines = text.lines();
new_summary.extend(lines.next());
// Stop if the LLM generated multiple lines.
if lines.next().is_some() {
break;
} }
_ => continue,
};
let mut lines = text.lines();
new_summary.extend(lines.next());
// Stop if the LLM generated multiple lines.
if lines.next().is_some() {
break;
} }
anyhow::Ok(new_summary.into())
} }
.await;
anyhow::Ok(new_summary.into()) this.update(cx, |this, cx| {
} if let Ok(title) = &result {
.await; event_stream.send_title_update(title.clone());
}
this.update(cx, |this, cx| { this.title = ThreadTitle::Done(result);
this.title = ThreadTitle::Done(result); cx.notify();
cx.notify(); })
.log_err();
}) })
.log_err(); .shared();
});
self.title = ThreadTitle::Pending(task); self.title = ThreadTitle::Pending(task.clone());
cx.notify() cx.notify()
} }
@ -1746,6 +1760,12 @@ impl ThreadEventStream {
.ok(); .ok();
} }
fn send_title_update(&self, text: SharedString) {
self.0
.unbounded_send(Ok(ThreadEvent::TitleUpdate(text)))
.ok();
}
fn send_thinking(&self, text: &str) { fn send_thinking(&self, text: &str) {
self.0 self.0
.unbounded_send(Ok(ThreadEvent::AgentThinking(text.to_string()))) .unbounded_send(Ok(ThreadEvent::AgentThinking(text.to_string())))