feat: compactのプログレス表示
This commit is contained in:
parent
584cbe406a
commit
41bce21339
|
|
@ -568,9 +568,7 @@ impl App {
|
|||
}
|
||||
Event::ToolCallDone { id, arguments, .. } => {
|
||||
self.current_tool = None;
|
||||
let name = self
|
||||
.find_tool_call_mut(&id)
|
||||
.map(|b| b.name.clone());
|
||||
let name = self.find_tool_call_mut(&id).map(|b| b.name.clone());
|
||||
if let Some(name) = name.as_deref() {
|
||||
self.task_store.apply_tool_call(name, &arguments);
|
||||
}
|
||||
|
|
@ -679,15 +677,47 @@ impl App {
|
|||
self.assistant_streaming = false;
|
||||
}
|
||||
Event::CompactStart => {
|
||||
self.blocks.push(Block::Compact(CompactEvent::Start));
|
||||
self.blocks.push(Block::Compact(CompactEvent::Streaming {
|
||||
started_at: Instant::now(),
|
||||
}));
|
||||
}
|
||||
Event::CompactDone { new_session_id } => {
|
||||
self.blocks
|
||||
.push(Block::Compact(CompactEvent::Done { new_session_id }));
|
||||
if let Some(evt) = self.last_streaming_compact_mut() {
|
||||
let elapsed_secs = match evt {
|
||||
CompactEvent::Streaming { started_at } => {
|
||||
Some(started_at.elapsed().as_secs())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
*evt = CompactEvent::Done {
|
||||
new_session_id,
|
||||
elapsed_secs,
|
||||
};
|
||||
} else {
|
||||
self.blocks.push(Block::Compact(CompactEvent::Done {
|
||||
new_session_id,
|
||||
elapsed_secs: None,
|
||||
}));
|
||||
}
|
||||
}
|
||||
Event::CompactFailed { error } => {
|
||||
self.blocks
|
||||
.push(Block::Compact(CompactEvent::Failed { error }));
|
||||
if let Some(evt) = self.last_streaming_compact_mut() {
|
||||
let elapsed_secs = match evt {
|
||||
CompactEvent::Streaming { started_at } => {
|
||||
Some(started_at.elapsed().as_secs())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
*evt = CompactEvent::Failed {
|
||||
error,
|
||||
elapsed_secs,
|
||||
};
|
||||
} else {
|
||||
self.blocks.push(Block::Compact(CompactEvent::Failed {
|
||||
error,
|
||||
elapsed_secs: None,
|
||||
}));
|
||||
}
|
||||
}
|
||||
Event::Alert(alert) => {
|
||||
self.blocks.push(Block::Alert {
|
||||
|
|
@ -719,6 +749,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
Event::Shutdown => {
|
||||
self.mark_orphan_compacts_incomplete();
|
||||
self.quit = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -775,6 +806,33 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn last_streaming_compact_mut(&mut self) -> Option<&mut CompactEvent> {
|
||||
for b in self.blocks.iter_mut().rev() {
|
||||
match b {
|
||||
Block::Compact(evt) if matches!(evt, CompactEvent::Streaming { .. }) => {
|
||||
return Some(evt);
|
||||
}
|
||||
Block::Compact(_) => return None,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn mark_orphan_compacts_incomplete(&mut self) {
|
||||
for b in self.blocks.iter_mut().rev() {
|
||||
if let Block::Compact(evt) = b {
|
||||
if let CompactEvent::Streaming { started_at } = evt {
|
||||
*evt = CompactEvent::Incomplete {
|
||||
elapsed_secs: Some(started_at.elapsed().as_secs()),
|
||||
};
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_tool_call_mut(&mut self, id: &str) -> Option<&mut ToolCallBlock> {
|
||||
for b in self.blocks.iter_mut().rev() {
|
||||
if let Block::ToolCall(tc) = b
|
||||
|
|
@ -1310,6 +1368,66 @@ mod completion_flow_tests {
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_done_replaces_live_block() {
|
||||
let mut app = App::new("test".into());
|
||||
let id = uuid::Uuid::parse_str("12345678-1234-5678-1234-567812345678").unwrap();
|
||||
|
||||
app.handle_pod_event(Event::CompactStart);
|
||||
app.handle_pod_event(Event::CompactDone { new_session_id: id });
|
||||
|
||||
assert_eq!(compact_block_count(&app), 1);
|
||||
assert!(matches!(
|
||||
app.blocks.as_slice(),
|
||||
[Block::Compact(CompactEvent::Done {
|
||||
new_session_id,
|
||||
elapsed_secs: Some(_),
|
||||
})] if *new_session_id == id
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_failed_replaces_live_block() {
|
||||
let mut app = App::new("test".into());
|
||||
|
||||
app.handle_pod_event(Event::CompactStart);
|
||||
app.handle_pod_event(Event::CompactFailed {
|
||||
error: "provider 429".into(),
|
||||
});
|
||||
|
||||
assert_eq!(compact_block_count(&app), 1);
|
||||
assert!(matches!(
|
||||
app.blocks.as_slice(),
|
||||
[Block::Compact(CompactEvent::Failed {
|
||||
error,
|
||||
elapsed_secs: Some(_),
|
||||
})] if error == "provider 429"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shutdown_marks_live_compact_incomplete() {
|
||||
let mut app = App::new("test".into());
|
||||
|
||||
app.handle_pod_event(Event::CompactStart);
|
||||
app.handle_pod_event(Event::Shutdown);
|
||||
|
||||
assert!(app.quit);
|
||||
assert!(matches!(
|
||||
app.blocks.as_slice(),
|
||||
[Block::Compact(CompactEvent::Incomplete {
|
||||
elapsed_secs: Some(_),
|
||||
})]
|
||||
));
|
||||
}
|
||||
|
||||
fn compact_block_count(app: &App) -> usize {
|
||||
app.blocks
|
||||
.iter()
|
||||
.filter(|block| matches!(block, Block::Compact(_)))
|
||||
.count()
|
||||
}
|
||||
|
||||
fn test_greeting() -> protocol::Greeting {
|
||||
protocol::Greeting {
|
||||
pod_name: "test".into(),
|
||||
|
|
|
|||
|
|
@ -78,9 +78,21 @@ pub enum ThinkingState {
|
|||
}
|
||||
|
||||
pub enum CompactEvent {
|
||||
Start,
|
||||
Done { new_session_id: uuid::Uuid },
|
||||
Failed { error: String },
|
||||
/// Live block: compaction worker is running. `started_at` powers the
|
||||
/// `Compacting... (Xs)` live timer.
|
||||
Streaming { started_at: Instant },
|
||||
/// Compaction ended cleanly with `CompactDone`.
|
||||
Done {
|
||||
new_session_id: uuid::Uuid,
|
||||
elapsed_secs: Option<u64>,
|
||||
},
|
||||
/// Compaction ended with `CompactFailed`.
|
||||
Failed {
|
||||
error: String,
|
||||
elapsed_secs: Option<u64>,
|
||||
},
|
||||
/// The TUI stopped observing events before a terminal compact event.
|
||||
Incomplete { elapsed_secs: Option<u64> },
|
||||
}
|
||||
|
||||
pub struct ToolCallBlock {
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ async fn run_loop(
|
|||
Some(ev) => app.handle_pod_event(ev),
|
||||
None => {
|
||||
app.connected = false;
|
||||
app.mark_orphan_compacts_incomplete();
|
||||
app.push_error("Connection lost");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1011,21 +1011,45 @@ fn fmt_elapsed(secs: u64) -> String {
|
|||
|
||||
fn render_compact(lines: &mut Vec<Line<'static>>, evt: &CompactEvent, width: u16, mode: Mode) {
|
||||
let (text, kind) = match evt {
|
||||
CompactEvent::Start => ("[compact] starting".to_owned(), MessageKind::NoticeWarn),
|
||||
CompactEvent::Done { new_session_id } => {
|
||||
CompactEvent::Streaming { started_at } => {
|
||||
let secs = started_at.elapsed().as_secs();
|
||||
(
|
||||
format!("Compacting... ({})", fmt_elapsed(secs)),
|
||||
MessageKind::NoticeWarn,
|
||||
)
|
||||
}
|
||||
CompactEvent::Done {
|
||||
new_session_id,
|
||||
elapsed_secs,
|
||||
} => {
|
||||
let short = new_session_id
|
||||
.to_string()
|
||||
.chars()
|
||||
.take(8)
|
||||
.collect::<String>();
|
||||
let elapsed = elapsed_suffix(*elapsed_secs);
|
||||
(
|
||||
format!("[compact] done (new session {short})"),
|
||||
format!("[compact] done (new session {short}){elapsed}"),
|
||||
MessageKind::NoticeWarn,
|
||||
)
|
||||
}
|
||||
CompactEvent::Failed { error } => {
|
||||
(format!("[compact error] {error}"), MessageKind::NoticeError)
|
||||
CompactEvent::Failed {
|
||||
error,
|
||||
elapsed_secs,
|
||||
} => {
|
||||
let elapsed = elapsed_suffix(*elapsed_secs);
|
||||
(
|
||||
format!("[compact error] {error}{elapsed}"),
|
||||
MessageKind::NoticeError,
|
||||
)
|
||||
}
|
||||
CompactEvent::Incomplete { elapsed_secs } => match elapsed_secs {
|
||||
Some(s) => (
|
||||
format!("[compact] interrupted ({})", fmt_elapsed(*s)),
|
||||
MessageKind::NoticeError,
|
||||
),
|
||||
None => ("[compact] interrupted".to_owned(), MessageKind::NoticeError),
|
||||
},
|
||||
};
|
||||
match mode {
|
||||
Mode::Overview => push_overview_line(lines, &text, width, kind, ""),
|
||||
|
|
@ -1033,6 +1057,12 @@ fn render_compact(lines: &mut Vec<Line<'static>>, evt: &CompactEvent, width: u16
|
|||
}
|
||||
}
|
||||
|
||||
fn elapsed_suffix(elapsed_secs: Option<u64>) -> String {
|
||||
elapsed_secs
|
||||
.map(|s| format!(" ({})", fmt_elapsed(s)))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn draw_separator(frame: &mut Frame, area: Rect) {
|
||||
let line = "─".repeat(area.width as usize);
|
||||
frame.render_widget(
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user