mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Show short turn durations in TUI completion footers (#48807)
## Why Completion footers hid known durations unless they exceeded 60 seconds, leaving short turns without elapsed-time information. ## What changed Show all known durations for live and restored turns, rendering sub-second durations as `Worked for <1s`. Separate completion metadata with `•` instead of `·`. ## Testing Update live completion, history replay, and separator tests to cover short durations, missing timestamps, and the revised footer format. Refresh affected TUI snapshots. GitOrigin-RevId: 666e765db5426c5f9cc00d73f71cab649ad34947
This commit is contained in:
+7
-8
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: tui/src/app/turn_tips_tests.rs
|
||||
assertion_line: 337
|
||||
expression: "crate::chatwidget::tests::helpers::normalize_snapshot_paths(screens.join(\"\\n\\n\"))"
|
||||
---
|
||||
working=true, response rows=1, 80x12
|
||||
@@ -31,7 +30,7 @@ working=false, response rows=1, 80x12
|
||||
• Done.
|
||||
└ Tip: Try /help.
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
|
||||
› Ask Codex to do anything
|
||||
@@ -45,7 +44,7 @@ working=false, response rows=30, 80x12
|
||||
• Done.
|
||||
└ Tip: Try /help.
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
|
||||
› Ask Codex to do anything
|
||||
@@ -56,7 +55,7 @@ working=false, response rows=30, 80x12
|
||||
working=false, response rows=1, 80x9
|
||||
• Done.
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
|
||||
› Ask Codex to do anything
|
||||
@@ -68,7 +67,7 @@ working=false, response rows=1, 80x10
|
||||
› Show a response.
|
||||
• Done.
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
|
||||
› Ask Codex to do anything
|
||||
@@ -81,7 +80,7 @@ working=false, response rows=1, 80x11
|
||||
• Done.
|
||||
└ Tip: Try /help.
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
|
||||
› Ask Codex to do anything
|
||||
@@ -90,11 +89,11 @@ working=false, response rows=1, 80x11
|
||||
? for shortcuts
|
||||
|
||||
working=false, response rows=1, 12x12
|
||||
Response row
|
||||
0
|
||||
|
||||
• Done.
|
||||
|
||||
Worked
|
||||
for <1s •
|
||||
[completion time]
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
---
|
||||
source: tui/src/app/tests/pagination_completion_tests.rs
|
||||
expression: "snapshots.join(\"\\n\\n--- next overlapping page ---\\n\\n\")"
|
||||
expression: "afternoon_clock.replace_all(&snapshots.join(\"\\n\\n--- next overlapping page ---\\n\\n\"),\n\"at 2:$1 PM\",)"
|
||||
---
|
||||
Earlier messages are available — press ctrl+t to view the full transcript
|
||||
• Middle answer
|
||||
|
||||
Worked for 2m 5s · Sep 6, 2000 at 2:33 PM
|
||||
Worked for 2m 5s • Sep 6, 2000 at 2:33 PM
|
||||
|
||||
--- next overlapping page ---
|
||||
|
||||
@@ -15,7 +15,7 @@ Earlier messages are available — press ctrl+t to view the full transcript
|
||||
|
||||
• Oldest answer
|
||||
|
||||
Worked for 2m 5s · Sep 6, 2000 at 2:32 PM
|
||||
Worked for 2m 5s • Sep 6, 2000 at 2:32 PM
|
||||
|
||||
|
||||
› Middle prompt
|
||||
@@ -23,4 +23,4 @@ Earlier messages are available — press ctrl+t to view the full transcript
|
||||
|
||||
• Middle answer
|
||||
|
||||
Worked for 2m 5s · Sep 6, 2000 at 2:33 PM
|
||||
Worked for 2m 5s • Sep 6, 2000 at 2:33 PM
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
---
|
||||
source: tui/src/app/tests/safety_buffering.rs
|
||||
assertion_line: 960
|
||||
expression: rendered_retry
|
||||
---
|
||||
› Handle the safety-buffered request
|
||||
Keep the accepted steer
|
||||
⚠ Full-history hydration is deprecated for paginated threads; omit `includeTurns` or set it to `false`, then page with `thread/turns/list` and `thread/items/list`.• done [completion time]
|
||||
⚠ Full-history hydration is deprecated for paginated threads; omit `includeTurns` or set it to `false`, then page with `thread/turns/list` and `thread/items/list`.• done Worked for [duration] • [completion time]
|
||||
|
||||
@@ -17,7 +17,7 @@ fn notification(method: &str, thread: ThreadId, turn: usize, status: &str) -> Se
|
||||
"method": method,
|
||||
"params": { "threadId": thread.to_string(), "turn": {
|
||||
"id": turn.to_string(), "items": [], "itemsView": "full", "status": status,
|
||||
"error": null, "startedAt": null, "completedAt": null, "durationMs": null,
|
||||
"error": null, "startedAt": null, "completedAt": null, "durationMs": 500,
|
||||
}},
|
||||
}))
|
||||
.unwrap()
|
||||
|
||||
@@ -28,8 +28,7 @@ impl ChatWidget {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter(|seconds| *seconds > 60);
|
||||
});
|
||||
let completed_at = turn
|
||||
.completed_at
|
||||
.and_then(|timestamp| chrono::DateTime::from_timestamp(timestamp, /*nsecs*/ 0))
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/status_and_layout.rs
|
||||
expression: "normalize_completion_timestamps(&\n(normalize_snapshot_paths(term.backend().vt100().screen().contents())))"
|
||||
assertion_line: 5824
|
||||
expression: normalize_snapshot_paths(term.backend().vt100().screen().contents())
|
||||
---
|
||||
|
||||
|
||||
@@ -50,4 +51,4 @@ expression: "normalize_completion_timestamps(&\n(normalize_snapshot_paths(term.b
|
||||
"path": "C:\\Program Files\\App",
|
||||
"regex": "^foo.*(bar)?$"
|
||||
}
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/completion_styling_tests.rs
|
||||
expression: normalize_completion_timestamps(& (text))
|
||||
expression: text
|
||||
---
|
||||
• The change is ready.
|
||||
|
||||
Worked for [duration] · [completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/completion_styling_tests.rs
|
||||
expression: normalize_completion_timestamps(& (text))
|
||||
expression: text
|
||||
---
|
||||
• Ran echo ready
|
||||
└ ready
|
||||
|
||||
• The change is ready.
|
||||
|
||||
Worked for [duration] · [completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/exec_flow.rs
|
||||
expression: normalize_completion_timestamps(& (combined))
|
||||
expression: combined
|
||||
---
|
||||
• Ran echo preparing
|
||||
└ preparing
|
||||
|
||||
• Final response.
|
||||
|
||||
Worked for [duration] · [completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/slash_commands.rs
|
||||
expression: "normalize_completion_timestamps(&\n(cells.iter().map(|cell|\nlines_to_single_string(cell).trim().to_string()).collect::<Vec<_>>().join(\"\\n\")))"
|
||||
assertion_line: 2193
|
||||
expression: "cells.iter().map(|cell|\nlines_to_single_string(cell).trim().to_string()).collect::<Vec<_>>().join(\"\\n\")"
|
||||
---
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
• Saved conversation to conversation.md
|
||||
• Copied conversation to clipboard
|
||||
■ Copy failed: clipboard unavailable
|
||||
|
||||
+3
-2
@@ -1,10 +1,11 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/exec_flow.rs
|
||||
expression: normalize_completion_timestamps(& (combined))
|
||||
assertion_line: 1309
|
||||
expression: combined
|
||||
---
|
||||
↳ Interacted with background terminal · just fix
|
||||
└ pwd
|
||||
|
||||
• Waited for background terminal · just fix
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/exec_flow.rs
|
||||
expression: normalize_completion_timestamps(& (combined))
|
||||
assertion_line: 1068
|
||||
expression: combined
|
||||
---
|
||||
• Waited for background terminal · cargo test -p codex-core
|
||||
|
||||
• Final response.
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/exec_flow.rs
|
||||
expression: normalize_completion_timestamps(& (combined))
|
||||
assertion_line: 1092
|
||||
expression: combined
|
||||
---
|
||||
• Waited for background terminal · cargo test -p codex-core
|
||||
|
||||
• Streaming response.
|
||||
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
+3
-2
@@ -1,10 +1,11 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/exec_flow.rs
|
||||
assertion_line: 1229
|
||||
expression: combined
|
||||
---
|
||||
Chat:
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
Transcript:
|
||||
• Waited for background terminal · just fix
|
||||
[completion time]
|
||||
Worked for [duration] • [completion time]
|
||||
|
||||
@@ -936,7 +936,10 @@ async fn live_app_server_turn_completed_clears_working_status_after_answer_item(
|
||||
.iter()
|
||||
.map(|lines| lines_to_single_string(lines).trim().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(completion_cells, vec!["[completion time]"]);
|
||||
assert_eq!(
|
||||
completion_cells,
|
||||
vec!["Worked for [duration] • [completion time]"]
|
||||
);
|
||||
assert!(!chat.bottom_pane.is_task_running());
|
||||
assert!(chat.bottom_pane.status_widget().is_none());
|
||||
assert_eq!(
|
||||
|
||||
@@ -93,15 +93,15 @@ async fn completion_follows_plain_and_streamed_tool_answers() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn completion_live_applies_duration_threshold_and_preserves_timestamp_fallback() {
|
||||
async fn completion_live_shows_known_durations_and_preserves_timestamp_fallback() {
|
||||
for (duration_ms, completed_at, prefix) in [
|
||||
(598, Some(COMPLETED_AT), ""),
|
||||
(1_000, Some(COMPLETED_AT), ""),
|
||||
(60_000, Some(COMPLETED_AT), ""),
|
||||
(60_999, Some(COMPLETED_AT), ""),
|
||||
(61_000, Some(COMPLETED_AT), "Worked for 1m 1s · "),
|
||||
(125_000, Some(COMPLETED_AT), "Worked for 2m 5s · "),
|
||||
(1_000, None, ""),
|
||||
(598, Some(COMPLETED_AT), "Worked for <1s • "),
|
||||
(1_000, Some(COMPLETED_AT), "Worked for 1s • "),
|
||||
(60_000, Some(COMPLETED_AT), "Worked for 1m 0s • "),
|
||||
(60_999, Some(COMPLETED_AT), "Worked for 1m 0s • "),
|
||||
(61_000, Some(COMPLETED_AT), "Worked for 1m 1s • "),
|
||||
(125_000, Some(COMPLETED_AT), "Worked for 2m 5s • "),
|
||||
(1_000, None, "Worked for 1s • "),
|
||||
] {
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
handle_turn_started(&mut chat, "turn-1");
|
||||
@@ -131,17 +131,26 @@ async fn completion_replay_preserves_metadata_and_input_without_live_side_effect
|
||||
(
|
||||
Some(125_000),
|
||||
Some(COMPLETED_AT),
|
||||
format!("Worked for 2m 5s · {done}"),
|
||||
format!("Worked for 2m 5s • {done}"),
|
||||
),
|
||||
(None, None, String::new()),
|
||||
(Some(598), Some(COMPLETED_AT), done.clone()),
|
||||
(Some(60_000), Some(COMPLETED_AT), done.clone()),
|
||||
(None, Some(COMPLETED_AT), done.clone()),
|
||||
(
|
||||
Some(598),
|
||||
Some(COMPLETED_AT),
|
||||
format!("Worked for <1s • {done}"),
|
||||
),
|
||||
(
|
||||
Some(60_000),
|
||||
Some(COMPLETED_AT),
|
||||
format!("Worked for 1m 0s • {done}"),
|
||||
),
|
||||
(
|
||||
Some(61_000),
|
||||
Some(COMPLETED_AT),
|
||||
format!("Worked for 1m 1s · {done}"),
|
||||
format!("Worked for 1m 1s • {done}"),
|
||||
),
|
||||
(Some(60_000), None, String::new()),
|
||||
(Some(60_000), None, "Worked for 1m 0s".to_string()),
|
||||
(Some(125_000), None, "Worked for 2m 5s".to_string()),
|
||||
] {
|
||||
for replay_kind in [
|
||||
@@ -190,7 +199,10 @@ async fn completion_replay_waits_for_older_turn_items_to_load() {
|
||||
let newer = completed_turn(Some(1_000), Some(COMPLETED_AT));
|
||||
|
||||
chat.replay_thread_turns(vec![unloaded, newer], ReplayKind::ResumeInitialMessages);
|
||||
assert_eq!(completion_labels(&mut rx), saved_completion_label());
|
||||
assert_eq!(
|
||||
completion_labels(&mut rx),
|
||||
format!("Worked for 1s • {}", saved_completion_label())
|
||||
);
|
||||
|
||||
chat.replay_thread_turns(vec![older], ReplayKind::ThreadSnapshot);
|
||||
let older_time = Local
|
||||
@@ -203,7 +215,7 @@ async fn completion_replay_waits_for_older_turn_items_to_load() {
|
||||
.to_string();
|
||||
assert_eq!(
|
||||
completion_labels(&mut rx),
|
||||
format!("Worked for 2m 5s · {older_time}"),
|
||||
format!("Worked for 2m 5s • {older_time}"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -265,7 +277,7 @@ fn completion_snapshot_normalization_preserves_clock_only_message_lines() {
|
||||
&footer,
|
||||
lines_to_single_string(&footer.display_lines(/*width*/ 80))
|
||||
),
|
||||
" Worked for [duration] · [completion time]\n"
|
||||
" Worked for [duration] • [completion time]\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1829,7 +1829,7 @@ pub(crate) fn normalize_completion_timestamps(
|
||||
}
|
||||
static COMPLETION_FOOTER: std::sync::LazyLock<regex_lite::Regex> = std::sync::LazyLock::new(
|
||||
|| {
|
||||
regex_lite::Regex::new(r"(?m)^(?P<indent>[ \t]*)(?P<duration>Worked for (?:[0-9]+h )?(?:[0-9]+m )?[0-9]+s · )?(?:[A-Z][a-z]{2} [0-9]{1,2}(?:, [0-9]{4})? at )?[0-9]{1,2}:[0-9]{2}(?: (?:AM|PM))?(?P<padding>[ \t]*)$")
|
||||
regex_lite::Regex::new(r"(?m)^(?P<indent>[ \t]*)(?P<duration>Worked for (?:<1s|(?:[0-9]+h )?(?:[0-9]+m )?[0-9]+s) • )?(?:[A-Z][a-z]{2} [0-9]{1,2}(?:, [0-9]{4})? at )?[0-9]{1,2}:[0-9]{2}(?: (?:AM|PM))?(?P<padding>[ \t]*)$")
|
||||
.expect("valid completion footer pattern")
|
||||
},
|
||||
);
|
||||
@@ -1838,7 +1838,7 @@ pub(crate) fn normalize_completion_timestamps(
|
||||
let indent = &captures["indent"];
|
||||
let padding = &captures["padding"];
|
||||
let duration = if captures.name("duration").is_some() {
|
||||
"Worked for [duration] · "
|
||||
"Worked for [duration] • "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ use chrono::NaiveDate;
|
||||
///
|
||||
/// The timestamp records when the turn actually finished, including when restored from history.
|
||||
/// Times use the host's clock preference; other local days include the date and other years the year.
|
||||
/// Durations are shown only above sixty seconds; shorter turns still show their timestamp.
|
||||
/// Known durations are always shown; sub-second durations display as less than one second.
|
||||
/// Absent metadata occupies no transcript rows.
|
||||
/// The display date is fixed at construction so crossing midnight cannot invalidate cached heights;
|
||||
/// restoring the conversation constructs new cells and refreshes whether the timestamp needs a date.
|
||||
@@ -58,7 +58,7 @@ impl FinalMessageSeparator {
|
||||
|
||||
fn label(&self, today: NaiveDate) -> Option<String> {
|
||||
let mut label_parts = Vec::new();
|
||||
if let Some(elapsed_seconds) = self.elapsed_seconds.filter(|seconds| *seconds > 60) {
|
||||
if let Some(elapsed_seconds) = self.elapsed_seconds {
|
||||
let hours = elapsed_seconds / 3_600;
|
||||
let minutes = (elapsed_seconds % 3_600) / 60;
|
||||
let seconds = elapsed_seconds % 60;
|
||||
@@ -66,6 +66,8 @@ impl FinalMessageSeparator {
|
||||
format!("{hours}h {minutes}m {seconds}s")
|
||||
} else if minutes > 0 {
|
||||
format!("{minutes}m {seconds}s")
|
||||
} else if seconds == 0 {
|
||||
"<1s".to_string()
|
||||
} else {
|
||||
format!("{seconds}s")
|
||||
};
|
||||
@@ -88,7 +90,7 @@ impl FinalMessageSeparator {
|
||||
if let Some(metrics_label) = self.runtime_metrics.and_then(runtime_metrics_label) {
|
||||
label_parts.push(metrics_label);
|
||||
}
|
||||
(!label_parts.is_empty()).then(|| label_parts.join(" · "))
|
||||
(!label_parts.is_empty()).then(|| label_parts.join(" • "))
|
||||
}
|
||||
}
|
||||
impl HistoryCell for FinalMessageSeparator {
|
||||
|
||||
@@ -19,7 +19,7 @@ fn completed_at() -> DateTime<Local> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completion_label_shows_duration_only_above_sixty_seconds() {
|
||||
fn completion_label_shows_known_durations_including_short_turns() {
|
||||
let completed_at = completed_at();
|
||||
let labels = [
|
||||
None,
|
||||
@@ -41,12 +41,12 @@ fn completion_label_shows_duration_only_above_sixty_seconds() {
|
||||
|
||||
insta::assert_snapshot!(labels.join("\n"), @"
|
||||
2:32 PM
|
||||
2:32 PM
|
||||
2:32 PM
|
||||
2:32 PM
|
||||
Worked for 1m 1s · 2:32 PM
|
||||
Worked for 2m 5s · 2:32 PM
|
||||
Worked for 1h 0m 5s · 2:32 PM
|
||||
Worked for <1s • 2:32 PM
|
||||
Worked for 12s • 2:32 PM
|
||||
Worked for 1m 0s • 2:32 PM
|
||||
Worked for 1m 1s • 2:32 PM
|
||||
Worked for 2m 5s • 2:32 PM
|
||||
Worked for 1h 0m 5s • 2:32 PM
|
||||
");
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ fn completion_label_includes_date_when_viewed_on_another_day() {
|
||||
.with_completed_at(completed_at, ClockFormat::TwelveHour);
|
||||
let tomorrow = completed_at.date_naive().succ_opt().expect("next day");
|
||||
|
||||
insta::assert_snapshot!(cell.label(tomorrow).expect("completion label"), @"Worked for 2m 5s · Sep 6 at 2:32 PM");
|
||||
insta::assert_snapshot!(cell.label(tomorrow).expect("completion label"), @"Worked for 2m 5s • Sep 6 at 2:32 PM");
|
||||
let next_year = tomorrow.with_year(/*year*/ 2001).expect("valid next year");
|
||||
insta::assert_snapshot!(cell.label(next_year).expect("completion label"), @"Worked for 2m 5s · Sep 6, 2000 at 2:32 PM");
|
||||
insta::assert_snapshot!(cell.label(next_year).expect("completion label"), @"Worked for 2m 5s • Sep 6, 2000 at 2:32 PM");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -149,12 +149,12 @@ fn completion_wraps_metadata_and_preserves_unwrapped_raw_text() {
|
||||
let rendered = lines.iter().map(ToString::to_string).collect::<Vec<_>>();
|
||||
|
||||
insta::assert_snapshot!(rendered.join("\n"), @r"
|
||||
Worked for 2m 5s · Sep
|
||||
6, 2000 at 2:32 PM ·
|
||||
Worked for 2m 5s • Sep
|
||||
6, 2000 at 2:32 PM •
|
||||
Local tools: 3 calls
|
||||
(2.5s)
|
||||
");
|
||||
insta::assert_snapshot!(cell.raw_lines()[0].to_string(), @"Worked for 2m 5s · Sep 6, 2000 at 2:32 PM · Local tools: 3 calls (2.5s)");
|
||||
insta::assert_snapshot!(cell.raw_lines()[0].to_string(), @"Worked for 2m 5s • Sep 6, 2000 at 2:32 PM • Local tools: 3 calls (2.5s)");
|
||||
for width in [0, 1, 5, 24] {
|
||||
assert!(
|
||||
cell.display_lines(width)
|
||||
|
||||
+12
-13
@@ -1,20 +1,19 @@
|
||||
---
|
||||
source: tui/src/history_cell/separators_tests.rs
|
||||
assertion_line: 101
|
||||
expression: "labels.join(\"\\n\")"
|
||||
---
|
||||
Worked for 2m 5s · 00:25
|
||||
Worked for 2m 5s · Sep 6 at 00:25
|
||||
Worked for 2m 5s · Sep 6, 2000 at 00:25
|
||||
Worked for 2m 5s · Sep
|
||||
Worked for 2m 5s • 00:25
|
||||
Worked for 2m 5s • Sep 6 at 00:25
|
||||
Worked for 2m 5s • Sep 6, 2000 at 00:25
|
||||
Worked for 2m 5s • Sep
|
||||
6, 2000 at 00:25
|
||||
Worked for 2m 5s · 12:25
|
||||
Worked for 2m 5s · Sep 6 at 12:25
|
||||
Worked for 2m 5s · Sep 6, 2000 at 12:25
|
||||
Worked for 2m 5s · Sep
|
||||
Worked for 2m 5s • 12:25
|
||||
Worked for 2m 5s • Sep 6 at 12:25
|
||||
Worked for 2m 5s • Sep 6, 2000 at 12:25
|
||||
Worked for 2m 5s • Sep
|
||||
6, 2000 at 12:25
|
||||
Worked for 2m 5s · 23:25
|
||||
Worked for 2m 5s · Sep 6 at 23:25
|
||||
Worked for 2m 5s · Sep 6, 2000 at 23:25
|
||||
Worked for 2m 5s · Sep
|
||||
Worked for 2m 5s • 23:25
|
||||
Worked for 2m 5s • Sep 6 at 23:25
|
||||
Worked for 2m 5s • Sep 6, 2000 at 23:25
|
||||
Worked for 2m 5s • Sep
|
||||
6, 2000 at 23:25
|
||||
|
||||
@@ -696,7 +696,7 @@ fn final_message_separator_preserves_runtime_metrics_for_short_turns() {
|
||||
let rendered = render_lines(&cell.display_lines(/*width*/ 600));
|
||||
|
||||
assert_eq!(rendered.len(), 1);
|
||||
assert!(rendered[0].starts_with(" Local tools:"));
|
||||
assert!(rendered[0].starts_with(" Worked for 12s • Local tools:"));
|
||||
assert!(rendered[0].contains("Local tools: 3 calls (2.5s)"));
|
||||
assert!(rendered[0].contains("Inference: 2 calls (1.2s)"));
|
||||
assert!(rendered[0].contains("WebSocket: 1 events send (700ms)"));
|
||||
|
||||
Reference in New Issue
Block a user