zeta2: Add prompt format option to inspector (#38884)

Adds the new prompt format option to the inspector view


Release Notes:

- N/A

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
This commit is contained in:
Agus Zubiaga
2025-09-25 14:23:58 +00:00
committed by GitHub
co-authored by Bennet Bo Fenner
parent c0f56f500e
commit b2b90b003d
4 changed files with 201 additions and 130 deletions
Generated
+1
View File
@@ -20641,6 +20641,7 @@ dependencies = [
"chrono",
"clap",
"client",
"cloud_llm_client",
"collections",
"edit_prediction_context",
"editor",
@@ -5,6 +5,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
use strum::EnumIter;
use uuid::Uuid;
use crate::PredictEditsGitInfo;
@@ -42,13 +43,28 @@ pub struct PredictEditsRequest {
pub prompt_format: PromptFormat,
}
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize, PartialEq, EnumIter)]
pub enum PromptFormat {
#[default]
MarkedExcerpt,
LabeledSections,
}
impl PromptFormat {
pub fn iter() -> impl Iterator<Item = Self> {
<Self as strum::IntoEnumIterator>::iter()
}
}
impl std::fmt::Display for PromptFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PromptFormat::MarkedExcerpt => write!(f, "Marked Excerpt"),
PromptFormat::LabeledSections => write!(f, "Labeled Sections"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-support"), derive(PartialEq))]
#[serde(tag = "event")]
+1
View File
@@ -14,6 +14,7 @@ path = "src/zeta2_tools.rs"
[dependencies]
chrono.workspace = true
client.workspace = true
cloud_llm_client.workspace = true
collections.workspace = true
edit_prediction_context.workspace = true
editor.workspace = true
+182 -129
View File
@@ -2,6 +2,7 @@ use std::{collections::hash_map::Entry, path::PathBuf, str::FromStr, sync::Arc,
use chrono::TimeDelta;
use client::{Client, UserStore};
use cloud_llm_client::predict_edits_v3::PromptFormat;
use collections::HashMap;
use editor::{Editor, EditorEvent, EditorMode, ExcerptRange, MultiBuffer};
use futures::StreamExt as _;
@@ -11,7 +12,7 @@ use gpui::{
};
use language::{Buffer, DiskState};
use project::{Project, WorktreeId};
use ui::prelude::*;
use ui::{ContextMenu, ContextMenuEntry, DropdownMenu, prelude::*};
use ui_input::SingleLineInput;
use util::{ResultExt, paths::PathStyle, rel_path::RelPath};
use workspace::{Item, SplitDirection, Workspace};
@@ -425,12 +426,162 @@ impl Zeta2Inspector {
});
}
fn render_options(&self, window: &mut Window, cx: &mut Context<Self>) -> Div {
v_flex()
.gap_2()
.child(
h_flex()
.child(Headline::new("Options").size(HeadlineSize::Small))
.justify_between()
.child(
ui::Button::new("reset-options", "Reset")
.disabled(self.zeta.read(cx).options() == &zeta2::DEFAULT_OPTIONS)
.style(ButtonStyle::Outlined)
.size(ButtonSize::Large)
.on_click(cx.listener(|this, _, window, cx| {
this.set_input_options(&zeta2::DEFAULT_OPTIONS, window, cx);
})),
),
)
.child(
v_flex()
.gap_2()
.child(
h_flex()
.gap_2()
.items_end()
.child(self.max_excerpt_bytes_input.clone())
.child(self.min_excerpt_bytes_input.clone())
.child(self.cursor_context_ratio_input.clone()),
)
.child(
h_flex()
.gap_2()
.items_end()
.child(self.max_prompt_bytes_input.clone())
.child(self.render_prompt_format_dropdown(window, cx)),
),
)
}
fn render_prompt_format_dropdown(&self, window: &mut Window, cx: &mut Context<Self>) -> Div {
let active_format = self.zeta.read(cx).options().prompt_format;
let this = cx.weak_entity();
v_flex()
.gap_1p5()
.child(
Label::new("Prompt Format")
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(
DropdownMenu::new(
"ep-prompt-format",
active_format.to_string(),
ContextMenu::build(window, cx, move |mut menu, _window, _cx| {
for prompt_format in PromptFormat::iter() {
menu = menu.item(
ContextMenuEntry::new(prompt_format.to_string())
.toggleable(IconPosition::End, active_format == prompt_format)
.handler({
let this = this.clone();
move |_window, cx| {
this.update(cx, |this, cx| {
let current_options =
this.zeta.read(cx).options().clone();
let options = ZetaOptions {
prompt_format,
..current_options
};
this.set_options(options, cx);
})
.ok();
}
}),
)
}
menu
}),
)
.style(ui::DropdownStyle::Outlined),
)
}
fn render_tabs(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
let Some(LastPredictionState::Success { .. } | LastPredictionState::Replaying { .. }) =
self.last_prediction.as_ref()
else {
return None;
};
Some(
ui::ToggleButtonGroup::single_row(
"prediction",
[
ui::ToggleButtonSimple::new(
"Context",
cx.listener(|this, _, _, cx| {
this.active_view = ActiveView::Context;
cx.notify();
}),
),
ui::ToggleButtonSimple::new(
"Inference",
cx.listener(|this, _, _, cx| {
this.active_view = ActiveView::Inference;
cx.notify();
}),
),
],
)
.style(ui::ToggleButtonGroupStyle::Outlined)
.selected_index(if self.active_view == ActiveView::Context {
0
} else {
1
})
.into_any_element(),
)
}
fn render_stats(&self) -> Option<Div> {
let Some(
LastPredictionState::Success(prediction)
| LastPredictionState::Replaying { prediction, .. },
) = self.last_prediction.as_ref()
else {
return None;
};
Some(
v_flex()
.p_4()
.gap_2()
.min_w(px(160.))
.child(Headline::new("Stats").size(HeadlineSize::Small))
.child(Self::render_duration(
"Context retrieval",
prediction.retrieval_time,
))
.child(Self::render_duration(
"Prompt planning",
prediction.prompt_planning_time,
))
.child(Self::render_duration(
"Inference",
prediction.inference_time,
))
.child(Self::render_duration("Parsing", prediction.parsing_time)),
)
}
fn render_duration(name: &'static str, time: chrono::TimeDelta) -> Div {
h_flex()
.gap_1()
.child(Label::new(name).color(Color::Muted).size(LabelSize::Small))
.child(
Label::new(if time.num_microseconds().unwrap_or(0) > 1000 {
Label::new(if time.num_microseconds().unwrap_or(0) >= 1000 {
format!("{} ms", time.num_milliseconds())
} else {
format!("{} µs", time.num_microseconds().unwrap_or(0))
@@ -439,6 +590,29 @@ impl Zeta2Inspector {
)
}
fn render_content(&self, cx: &mut Context<Self>) -> AnyElement {
match self.last_prediction.as_ref() {
None => v_flex()
.size_full()
.justify_center()
.items_center()
.child(Label::new("No prediction").size(LabelSize::Large))
.into_any(),
Some(LastPredictionState::Success(prediction)) => {
self.render_last_prediction(prediction, cx).into_any()
}
Some(LastPredictionState::Replaying { prediction, _task }) => self
.render_last_prediction(prediction, cx)
.opacity(0.6)
.into_any(),
Some(LastPredictionState::Failed(err)) => v_flex()
.p_4()
.gap_2()
.child(Label::new(err.clone()).buffer_font(cx))
.into_any(),
}
}
fn render_last_prediction(&self, prediction: &LastPrediction, cx: &mut Context<Self>) -> Div {
match &self.active_view {
ActiveView::Context => div().size_full().child(prediction.context_editor.clone()),
@@ -489,28 +663,7 @@ impl Item for Zeta2Inspector {
impl EventEmitter<()> for Zeta2Inspector {}
impl Render for Zeta2Inspector {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let content = match self.last_prediction.as_ref() {
None => v_flex()
.size_full()
.justify_center()
.items_center()
.child(Label::new("No prediction").size(LabelSize::Large))
.into_any(),
Some(LastPredictionState::Success(prediction)) => {
self.render_last_prediction(prediction, cx).into_any()
}
Some(LastPredictionState::Replaying { prediction, _task }) => self
.render_last_prediction(prediction, cx)
.opacity(0.6)
.into_any(),
Some(LastPredictionState::Failed(err)) => v_flex()
.p_4()
.gap_2()
.child(Label::new(err.clone()).buffer_font(cx))
.into_any(),
};
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.size_full()
.bg(cx.theme().colors().editor_background)
@@ -523,114 +676,14 @@ impl Render for Zeta2Inspector {
.p_4()
.h_full()
.justify_between()
.child(
v_flex()
.gap_2()
.child(Headline::new("Options").size(HeadlineSize::Small))
.child(
h_flex()
.gap_2()
.items_end()
.child(self.max_excerpt_bytes_input.clone())
.child(self.min_excerpt_bytes_input.clone())
.child(self.cursor_context_ratio_input.clone())
.child(self.max_prompt_bytes_input.clone())
.child(
ui::Button::new("reset-options", "Reset")
.disabled(
self.zeta.read(cx).options()
== &zeta2::DEFAULT_OPTIONS,
)
.style(ButtonStyle::Outlined)
.size(ButtonSize::Large)
.on_click(cx.listener(
|this, _, window, cx| {
this.set_input_options(
&zeta2::DEFAULT_OPTIONS,
window,
cx,
);
},
)),
),
),
)
.map(|this| {
if let Some(
LastPredictionState::Success { .. }
| LastPredictionState::Replaying { .. },
) = self.last_prediction.as_ref()
{
this.child(
ui::ToggleButtonGroup::single_row(
"prediction",
[
ui::ToggleButtonSimple::new(
"Context",
cx.listener(|this, _, _, cx| {
this.active_view = ActiveView::Context;
cx.notify();
}),
),
ui::ToggleButtonSimple::new(
"Inference",
cx.listener(|this, _, _, cx| {
this.active_view = ActiveView::Inference;
cx.notify();
}),
),
],
)
.style(ui::ToggleButtonGroupStyle::Outlined)
.selected_index(
if self.active_view == ActiveView::Context {
0
} else {
1
},
),
)
} else {
this
}
}),
.child(self.render_options(window, cx))
.gap_4()
.children(self.render_tabs(cx)),
)
.child(ui::vertical_divider())
.map(|this| {
if let Some(
LastPredictionState::Success(prediction)
| LastPredictionState::Replaying { prediction, .. },
) = self.last_prediction.as_ref()
{
this.child(
v_flex()
.p_4()
.gap_2()
.min_w(px(160.))
.child(Headline::new("Stats").size(HeadlineSize::Small))
.child(Self::render_duration(
"Context retrieval",
prediction.retrieval_time,
))
.child(Self::render_duration(
"Prompt planning",
prediction.prompt_planning_time,
))
.child(Self::render_duration(
"Inference",
prediction.inference_time,
))
.child(Self::render_duration(
"Parsing",
prediction.parsing_time,
)),
)
} else {
this
}
}),
.children(self.render_stats()),
)
.child(content)
.child(self.render_content(cx))
}
}