agent2: Show retry button for errors
Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
parent
11545c669e
commit
07592cac9a
5 changed files with 70 additions and 20 deletions
|
@ -1373,6 +1373,10 @@ impl AcpThread {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn can_resume(&self, cx: &App) -> bool {
|
||||||
|
self.connection.resume(&self.session_id, cx).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resume(&mut self, cx: &mut Context<Self>) -> BoxFuture<'static, Result<()>> {
|
pub fn resume(&mut self, cx: &mut Context<Self>) -> BoxFuture<'static, Result<()>> {
|
||||||
self.run_turn(cx, async move |this, cx| {
|
self.run_turn(cx, async move |this, cx| {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
|
@ -2659,7 +2663,7 @@ mod tests {
|
||||||
fn truncate(
|
fn truncate(
|
||||||
&self,
|
&self,
|
||||||
session_id: &acp::SessionId,
|
session_id: &acp::SessionId,
|
||||||
_cx: &mut App,
|
_cx: &App,
|
||||||
) -> Option<Rc<dyn AgentSessionTruncate>> {
|
) -> Option<Rc<dyn AgentSessionTruncate>> {
|
||||||
Some(Rc::new(FakeAgentSessionEditor {
|
Some(Rc::new(FakeAgentSessionEditor {
|
||||||
_session_id: session_id.clone(),
|
_session_id: session_id.clone(),
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub trait AgentConnection {
|
||||||
fn resume(
|
fn resume(
|
||||||
&self,
|
&self,
|
||||||
_session_id: &acp::SessionId,
|
_session_id: &acp::SessionId,
|
||||||
_cx: &mut App,
|
_cx: &App,
|
||||||
) -> Option<Rc<dyn AgentSessionResume>> {
|
) -> Option<Rc<dyn AgentSessionResume>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ pub trait AgentConnection {
|
||||||
fn truncate(
|
fn truncate(
|
||||||
&self,
|
&self,
|
||||||
_session_id: &acp::SessionId,
|
_session_id: &acp::SessionId,
|
||||||
_cx: &mut App,
|
_cx: &App,
|
||||||
) -> Option<Rc<dyn AgentSessionTruncate>> {
|
) -> Option<Rc<dyn AgentSessionTruncate>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ pub trait AgentConnection {
|
||||||
fn set_title(
|
fn set_title(
|
||||||
&self,
|
&self,
|
||||||
_session_id: &acp::SessionId,
|
_session_id: &acp::SessionId,
|
||||||
_cx: &mut App,
|
_cx: &App,
|
||||||
) -> Option<Rc<dyn AgentSessionSetTitle>> {
|
) -> Option<Rc<dyn AgentSessionSetTitle>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -439,7 +439,7 @@ mod test_support {
|
||||||
fn truncate(
|
fn truncate(
|
||||||
&self,
|
&self,
|
||||||
_session_id: &agent_client_protocol::SessionId,
|
_session_id: &agent_client_protocol::SessionId,
|
||||||
_cx: &mut App,
|
_cx: &App,
|
||||||
) -> Option<Rc<dyn AgentSessionTruncate>> {
|
) -> Option<Rc<dyn AgentSessionTruncate>> {
|
||||||
Some(Rc::new(StubAgentSessionEditor))
|
Some(Rc::new(StubAgentSessionEditor))
|
||||||
}
|
}
|
||||||
|
|
|
@ -936,7 +936,7 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
|
||||||
fn resume(
|
fn resume(
|
||||||
&self,
|
&self,
|
||||||
session_id: &acp::SessionId,
|
session_id: &acp::SessionId,
|
||||||
_cx: &mut App,
|
_cx: &App,
|
||||||
) -> Option<Rc<dyn acp_thread::AgentSessionResume>> {
|
) -> Option<Rc<dyn acp_thread::AgentSessionResume>> {
|
||||||
Some(Rc::new(NativeAgentSessionResume {
|
Some(Rc::new(NativeAgentSessionResume {
|
||||||
connection: self.clone(),
|
connection: self.clone(),
|
||||||
|
@ -956,9 +956,9 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
|
||||||
fn truncate(
|
fn truncate(
|
||||||
&self,
|
&self,
|
||||||
session_id: &agent_client_protocol::SessionId,
|
session_id: &agent_client_protocol::SessionId,
|
||||||
cx: &mut App,
|
cx: &App,
|
||||||
) -> Option<Rc<dyn acp_thread::AgentSessionTruncate>> {
|
) -> Option<Rc<dyn acp_thread::AgentSessionTruncate>> {
|
||||||
self.0.update(cx, |agent, _cx| {
|
self.0.read_with(cx, |agent, _cx| {
|
||||||
agent.sessions.get(session_id).map(|session| {
|
agent.sessions.get(session_id).map(|session| {
|
||||||
Rc::new(NativeAgentSessionEditor {
|
Rc::new(NativeAgentSessionEditor {
|
||||||
thread: session.thread.clone(),
|
thread: session.thread.clone(),
|
||||||
|
@ -971,7 +971,7 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
|
||||||
fn set_title(
|
fn set_title(
|
||||||
&self,
|
&self,
|
||||||
session_id: &acp::SessionId,
|
session_id: &acp::SessionId,
|
||||||
_cx: &mut App,
|
_cx: &App,
|
||||||
) -> Option<Rc<dyn acp_thread::AgentSessionSetTitle>> {
|
) -> Option<Rc<dyn acp_thread::AgentSessionSetTitle>> {
|
||||||
Some(Rc::new(NativeAgentSessionSetTitle {
|
Some(Rc::new(NativeAgentSessionSetTitle {
|
||||||
connection: self.clone(),
|
connection: self.clone(),
|
||||||
|
|
|
@ -27,7 +27,8 @@ use futures::{
|
||||||
};
|
};
|
||||||
use git::repository::DiffType;
|
use git::repository::DiffType;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity,
|
App, AppContext, AsyncApp, Context, Entity, EventEmitter, FutureExt as _, SharedString, Task,
|
||||||
|
WeakEntity,
|
||||||
};
|
};
|
||||||
use language_model::{
|
use language_model::{
|
||||||
LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelExt,
|
LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelExt,
|
||||||
|
@ -1085,11 +1086,6 @@ impl Thread {
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Result<mpsc::UnboundedReceiver<Result<ThreadEvent>>> {
|
) -> Result<mpsc::UnboundedReceiver<Result<ThreadEvent>>> {
|
||||||
anyhow::ensure!(
|
|
||||||
self.tool_use_limit_reached,
|
|
||||||
"can only resume after tool use limit is reached"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.messages.push(Message::Resume);
|
self.messages.push(Message::Resume);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
|
||||||
|
@ -1216,12 +1212,13 @@ impl Thread {
|
||||||
cx: &mut AsyncApp,
|
cx: &mut AsyncApp,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
log::debug!("Stream completion started successfully");
|
log::debug!("Stream completion started successfully");
|
||||||
let request = this.update(cx, |this, cx| {
|
|
||||||
this.build_completion_request(completion_intent, cx)
|
|
||||||
})??;
|
|
||||||
|
|
||||||
let mut attempt = None;
|
let mut attempt = None;
|
||||||
'retry: loop {
|
'retry: loop {
|
||||||
|
let request = this.update(cx, |this, cx| {
|
||||||
|
this.build_completion_request(completion_intent, cx)
|
||||||
|
})??;
|
||||||
|
|
||||||
telemetry::event!(
|
telemetry::event!(
|
||||||
"Agent Thread Completion",
|
"Agent Thread Completion",
|
||||||
thread_id = this.read_with(cx, |this, _| this.id.to_string())?,
|
thread_id = this.read_with(cx, |this, _| this.id.to_string())?,
|
||||||
|
@ -1236,7 +1233,7 @@ impl Thread {
|
||||||
attempt.unwrap_or(0)
|
attempt.unwrap_or(0)
|
||||||
);
|
);
|
||||||
let mut events = model
|
let mut events = model
|
||||||
.stream_completion(request.clone(), cx)
|
.stream_completion(request, cx)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| anyhow!(error))?;
|
.map_err(|error| anyhow!(error))?;
|
||||||
let mut tool_results = FuturesUnordered::new();
|
let mut tool_results = FuturesUnordered::new();
|
||||||
|
@ -1291,6 +1288,7 @@ impl Thread {
|
||||||
started_at: Instant::now(),
|
started_at: Instant::now(),
|
||||||
duration: delay,
|
duration: delay,
|
||||||
});
|
});
|
||||||
|
this.update(cx, |this, cx| this.flush_pending_message(cx))?;
|
||||||
|
|
||||||
cx.background_executor().timer(delay).await;
|
cx.background_executor().timer(delay).await;
|
||||||
continue 'retry;
|
continue 'retry;
|
||||||
|
@ -1737,6 +1735,10 @@ impl Thread {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if message.content.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for content in &message.content {
|
for content in &message.content {
|
||||||
let AgentMessageContent::ToolUse(tool_use) = content else {
|
let AgentMessageContent::ToolUse(tool_use) = content else {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -820,6 +820,9 @@ impl AcpThreadView {
|
||||||
let Some(thread) = self.thread() else {
|
let Some(thread) = self.thread() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if !thread.read(cx).can_resume(cx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let task = thread.update(cx, |thread, cx| thread.resume(cx));
|
let task = thread.update(cx, |thread, cx| thread.resume(cx));
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
|
@ -4469,12 +4472,53 @@ impl AcpThreadView {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_any_thread_error(&self, error: SharedString, cx: &mut Context<'_, Self>) -> Callout {
|
fn render_any_thread_error(&self, error: SharedString, cx: &mut Context<'_, Self>) -> Callout {
|
||||||
|
let can_resume = self
|
||||||
|
.thread()
|
||||||
|
.map_or(false, |thread| thread.read(cx).can_resume(cx));
|
||||||
|
|
||||||
|
let can_enable_burn_mode = self.as_native_thread(cx).map_or(false, |thread| {
|
||||||
|
let thread = thread.read(cx);
|
||||||
|
let supports_burn_mode = thread
|
||||||
|
.model()
|
||||||
|
.map_or(false, |model| model.supports_burn_mode());
|
||||||
|
supports_burn_mode && thread.completion_mode() == CompletionMode::Normal
|
||||||
|
});
|
||||||
|
|
||||||
Callout::new()
|
Callout::new()
|
||||||
.severity(Severity::Error)
|
.severity(Severity::Error)
|
||||||
.title("Error")
|
.title("Error")
|
||||||
.icon(IconName::XCircle)
|
.icon(IconName::XCircle)
|
||||||
.description(error.clone())
|
.description(error.clone())
|
||||||
.actions_slot(self.create_copy_button(error.to_string()))
|
.actions_slot(
|
||||||
|
h_flex()
|
||||||
|
.gap_0p5()
|
||||||
|
.when(can_resume && can_enable_burn_mode, |this| {
|
||||||
|
this.child(
|
||||||
|
Button::new("enable-burn-mode-and-retry", "Enable Burn Mode and Retry")
|
||||||
|
.icon(IconName::ZedBurnMode)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.label_size(LabelSize::Small)
|
||||||
|
.on_click(cx.listener(|this, _, window, cx| {
|
||||||
|
this.toggle_burn_mode(&ToggleBurnMode, window, cx);
|
||||||
|
this.resume_chat(cx);
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.when(can_resume, |this| {
|
||||||
|
this.child(
|
||||||
|
Button::new("retry", "Retry")
|
||||||
|
.icon(IconName::RotateCw)
|
||||||
|
.icon_position(IconPosition::Start)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.label_size(LabelSize::Small)
|
||||||
|
.on_click(cx.listener(|this, _, _window, cx| {
|
||||||
|
this.resume_chat(cx);
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.child(self.create_copy_button(error.to_string())),
|
||||||
|
)
|
||||||
.dismiss_action(self.dismiss_error_button(cx))
|
.dismiss_action(self.dismiss_error_button(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue