agent: Render edit tool error as markdown (#30325)
Release Notes: - agent: Render edit tool error as markdown and allow selecting it
This commit is contained in:
parent
05a6c31ad8
commit
c512d43e8c
3 changed files with 98 additions and 68 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -676,6 +676,7 @@ dependencies = [
|
||||||
"language_models",
|
"language_models",
|
||||||
"linkme",
|
"linkme",
|
||||||
"log",
|
"log",
|
||||||
|
"markdown",
|
||||||
"open",
|
"open",
|
||||||
"paths",
|
"paths",
|
||||||
"portable-pty",
|
"portable-pty",
|
||||||
|
|
|
@ -17,14 +17,14 @@ eval = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick.workspace = true
|
aho-corasick.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
assistant_tool.workspace = true
|
|
||||||
assistant_settings.workspace = true
|
assistant_settings.workspace = true
|
||||||
|
assistant_tool.workspace = true
|
||||||
buffer_diff.workspace = true
|
buffer_diff.workspace = true
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
component.workspace = true
|
component.workspace = true
|
||||||
editor.workspace = true
|
|
||||||
derive_more.workspace = true
|
derive_more.workspace = true
|
||||||
|
editor.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
|
@ -35,8 +35,9 @@ indoc.workspace = true
|
||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
language_model.workspace = true
|
language_model.workspace = true
|
||||||
log.workspace = true
|
|
||||||
linkme.workspace = true
|
linkme.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
markdown.workspace = true
|
||||||
open.workspace = true
|
open.workspace = true
|
||||||
paths.workspace = true
|
paths.workspace = true
|
||||||
portable-pty.workspace = true
|
portable-pty.workspace = true
|
||||||
|
|
|
@ -20,6 +20,7 @@ use language::{
|
||||||
language_settings::SoftWrap,
|
language_settings::SoftWrap,
|
||||||
};
|
};
|
||||||
use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat};
|
use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat};
|
||||||
|
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -335,7 +336,7 @@ pub struct EditFileToolCard {
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
diff_task: Option<Task<Result<()>>>,
|
diff_task: Option<Task<Result<()>>>,
|
||||||
preview_expanded: bool,
|
preview_expanded: bool,
|
||||||
error_expanded: bool,
|
error_expanded: Option<Entity<Markdown>>,
|
||||||
full_height_expanded: bool,
|
full_height_expanded: bool,
|
||||||
total_lines: Option<u32>,
|
total_lines: Option<u32>,
|
||||||
editor_unique_id: EntityId,
|
editor_unique_id: EntityId,
|
||||||
|
@ -378,7 +379,7 @@ impl EditFileToolCard {
|
||||||
multibuffer,
|
multibuffer,
|
||||||
diff_task: None,
|
diff_task: None,
|
||||||
preview_expanded: true,
|
preview_expanded: true,
|
||||||
error_expanded: false,
|
error_expanded: None,
|
||||||
full_height_expanded: false,
|
full_height_expanded: false,
|
||||||
total_lines: None,
|
total_lines: None,
|
||||||
}
|
}
|
||||||
|
@ -435,9 +436,9 @@ impl ToolCard for EditFileToolCard {
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let (failed, error_message) = match status {
|
let error_message = match status {
|
||||||
ToolUseStatus::Error(err) => (true, Some(err.to_string())),
|
ToolUseStatus::Error(err) => Some(err),
|
||||||
_ => (false, None),
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let path_label_button = h_flex()
|
let path_label_button = h_flex()
|
||||||
|
@ -525,9 +526,11 @@ impl ToolCard for EditFileToolCard {
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.rounded_t_md()
|
.rounded_t_md()
|
||||||
.when(!failed, |header| header.bg(codeblock_header_bg))
|
.when(error_message.is_none(), |header| {
|
||||||
|
header.bg(codeblock_header_bg)
|
||||||
|
})
|
||||||
.child(path_label_button)
|
.child(path_label_button)
|
||||||
.when(failed, |header| {
|
.when_some(error_message, |header, error_message| {
|
||||||
header.child(
|
header.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
|
@ -539,19 +542,28 @@ impl ToolCard for EditFileToolCard {
|
||||||
.child(
|
.child(
|
||||||
Disclosure::new(
|
Disclosure::new(
|
||||||
("edit-file-error-disclosure", self.editor_unique_id),
|
("edit-file-error-disclosure", self.editor_unique_id),
|
||||||
self.error_expanded,
|
self.error_expanded.is_some(),
|
||||||
)
|
)
|
||||||
.opened_icon(IconName::ChevronUp)
|
.opened_icon(IconName::ChevronUp)
|
||||||
.closed_icon(IconName::ChevronDown)
|
.closed_icon(IconName::ChevronDown)
|
||||||
.on_click(cx.listener(
|
.on_click(cx.listener({
|
||||||
move |this, _event, _window, _cx| {
|
let error_message = error_message.clone();
|
||||||
this.error_expanded = !this.error_expanded;
|
|
||||||
},
|
move |this, _event, _window, cx| {
|
||||||
)),
|
if this.error_expanded.is_some() {
|
||||||
|
this.error_expanded.take();
|
||||||
|
} else {
|
||||||
|
this.error_expanded = Some(cx.new(|cx| {
|
||||||
|
Markdown::new(error_message.clone(), None, None, cx)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
})),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when(!failed && self.has_diff(), |header| {
|
.when(error_message.is_none() && self.has_diff(), |header| {
|
||||||
header.child(
|
header.child(
|
||||||
Disclosure::new(
|
Disclosure::new(
|
||||||
("edit-file-disclosure", self.editor_unique_id),
|
("edit-file-disclosure", self.editor_unique_id),
|
||||||
|
@ -658,12 +670,12 @@ impl ToolCard for EditFileToolCard {
|
||||||
v_flex()
|
v_flex()
|
||||||
.mb_2()
|
.mb_2()
|
||||||
.border_1()
|
.border_1()
|
||||||
.when(failed, |card| card.border_dashed())
|
.when(error_message.is_some(), |card| card.border_dashed())
|
||||||
.border_color(border_color)
|
.border_color(border_color)
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(codeblock_header)
|
.child(codeblock_header)
|
||||||
.when(failed && self.error_expanded, |card| {
|
.when_some(self.error_expanded.as_ref(), |card, error_markdown| {
|
||||||
card.child(
|
card.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.p_2()
|
.p_2()
|
||||||
|
@ -683,65 +695,81 @@ impl ToolCard for EditFileToolCard {
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
.text_ui_sm(cx)
|
.text_ui_sm(cx)
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.children(
|
.child(MarkdownElement::new(
|
||||||
error_message
|
error_markdown.clone(),
|
||||||
.map(|error| div().child(error).into_any_element()),
|
markdown_style(window, cx),
|
||||||
),
|
)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when(!self.has_diff() && !failed, |card| {
|
.when(!self.has_diff() && error_message.is_none(), |card| {
|
||||||
card.child(waiting_for_diff)
|
card.child(waiting_for_diff)
|
||||||
})
|
})
|
||||||
.when(
|
.when(self.preview_expanded && self.has_diff(), |card| {
|
||||||
!failed && self.preview_expanded && self.has_diff(),
|
card.child(
|
||||||
|card| {
|
v_flex()
|
||||||
|
.relative()
|
||||||
|
.h_full()
|
||||||
|
.when(!self.full_height_expanded, |editor_container| {
|
||||||
|
editor_container
|
||||||
|
.max_h(DEFAULT_COLLAPSED_LINES as f32 * editor_line_height)
|
||||||
|
})
|
||||||
|
.overflow_hidden()
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(border_color)
|
||||||
|
.bg(cx.theme().colors().editor_background)
|
||||||
|
.child(editor)
|
||||||
|
.when(
|
||||||
|
!self.full_height_expanded && is_collapsible,
|
||||||
|
|editor_container| editor_container.child(gradient_overlay),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.when(is_collapsible, |card| {
|
||||||
card.child(
|
card.child(
|
||||||
v_flex()
|
h_flex()
|
||||||
.relative()
|
.id(("expand-button", self.editor_unique_id))
|
||||||
.h_full()
|
.flex_none()
|
||||||
.when(!self.full_height_expanded, |editor_container| {
|
.cursor_pointer()
|
||||||
editor_container
|
.h_5()
|
||||||
.max_h(DEFAULT_COLLAPSED_LINES as f32 * editor_line_height)
|
.justify_center()
|
||||||
})
|
|
||||||
.overflow_hidden()
|
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
|
.rounded_b_md()
|
||||||
.border_color(border_color)
|
.border_color(border_color)
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.child(editor)
|
.hover(|style| style.bg(cx.theme().colors().element_hover.opacity(0.1)))
|
||||||
.when(
|
.child(
|
||||||
!self.full_height_expanded && is_collapsible,
|
Icon::new(full_height_icon)
|
||||||
|editor_container| editor_container.child(gradient_overlay),
|
.size(IconSize::Small)
|
||||||
),
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.tooltip(Tooltip::text(full_height_tooltip_label))
|
||||||
|
.on_click(cx.listener(move |this, _event, _window, _cx| {
|
||||||
|
this.full_height_expanded = !this.full_height_expanded;
|
||||||
|
})),
|
||||||
)
|
)
|
||||||
.when(is_collapsible, |card| {
|
})
|
||||||
card.child(
|
})
|
||||||
h_flex()
|
}
|
||||||
.id(("expand-button", self.editor_unique_id))
|
}
|
||||||
.flex_none()
|
|
||||||
.cursor_pointer()
|
fn markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
|
||||||
.h_5()
|
let theme_settings = ThemeSettings::get_global(cx);
|
||||||
.justify_center()
|
let ui_font_size = TextSize::Default.rems(cx);
|
||||||
.border_t_1()
|
let mut text_style = window.text_style();
|
||||||
.rounded_b_md()
|
|
||||||
.border_color(border_color)
|
text_style.refine(&TextStyleRefinement {
|
||||||
.bg(cx.theme().colors().editor_background)
|
font_family: Some(theme_settings.ui_font.family.clone()),
|
||||||
.hover(|style| {
|
font_fallbacks: theme_settings.ui_font.fallbacks.clone(),
|
||||||
style.bg(cx.theme().colors().element_hover.opacity(0.1))
|
font_features: Some(theme_settings.ui_font.features.clone()),
|
||||||
})
|
font_size: Some(ui_font_size.into()),
|
||||||
.child(
|
color: Some(cx.theme().colors().text),
|
||||||
Icon::new(full_height_icon)
|
..Default::default()
|
||||||
.size(IconSize::Small)
|
});
|
||||||
.color(Color::Muted),
|
|
||||||
)
|
MarkdownStyle {
|
||||||
.tooltip(Tooltip::text(full_height_tooltip_label))
|
base_text_style: text_style.clone(),
|
||||||
.on_click(cx.listener(move |this, _event, _window, _cx| {
|
selection_background_color: cx.theme().players().local().selection,
|
||||||
this.full_height_expanded = !this.full_height_expanded;
|
..Default::default()
|
||||||
})),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue