mirror of
https://github.com/openai/codex.git
synced 2026-05-17 17:53:06 +00:00
## Why Goal mode shows elapsed time in compact hour/minute form. That is easy to scan for shorter runs, but once a goal runs past 24 hours, large hour counts become harder to read at a glance. ## What changed Updated `codex-rs/tui/src/goal_display.rs` so unbudgeted goal elapsed time keeps the existing compact format below one day, then switches to a day-aware format once the elapsed time reaches 24 hours: - `23h 59m` - `1d 0h 0m` - `2d 23h 42m` The formatter now covers the 24-hour boundary in unit tests, and the TUI status-line snapshot for a completed elapsed goal now exercises the multi-day display. ## Verification - `cargo test -p codex-tui` Here's my longest-running test task: <img width="186" height="23" alt="image" src="https://github.com/user-attachments/assets/cedfcdab-7f6e-44e6-8495-8a39f63973fb" />
108 lines
3.5 KiB
Rust
108 lines
3.5 KiB
Rust
use crate::status::format_tokens_compact;
|
|
use codex_app_server_protocol::ThreadGoal;
|
|
use codex_app_server_protocol::ThreadGoalStatus;
|
|
|
|
pub(crate) fn format_goal_elapsed_seconds(seconds: i64) -> String {
|
|
let seconds = seconds.max(0) as u64;
|
|
if seconds < 60 {
|
|
return format!("{seconds}s");
|
|
}
|
|
|
|
let minutes = seconds / 60;
|
|
if minutes < 60 {
|
|
return format!("{minutes}m");
|
|
}
|
|
|
|
let hours = minutes / 60;
|
|
let remaining_minutes = minutes % 60;
|
|
if hours >= 24 {
|
|
let days = hours / 24;
|
|
let remaining_hours = hours % 24;
|
|
return format!("{days}d {remaining_hours}h {remaining_minutes}m");
|
|
}
|
|
|
|
if remaining_minutes == 0 {
|
|
format!("{hours}h")
|
|
} else {
|
|
format!("{hours}h {remaining_minutes}m")
|
|
}
|
|
}
|
|
|
|
pub(crate) fn goal_status_label(status: ThreadGoalStatus) -> &'static str {
|
|
match status {
|
|
ThreadGoalStatus::Active => "active",
|
|
ThreadGoalStatus::Paused => "paused",
|
|
ThreadGoalStatus::BudgetLimited => "limited by budget",
|
|
ThreadGoalStatus::Complete => "complete",
|
|
}
|
|
}
|
|
|
|
pub(crate) fn goal_usage_summary(goal: &ThreadGoal) -> String {
|
|
let mut parts = vec![format!("Objective: {}", goal.objective)];
|
|
if goal.time_used_seconds > 0 {
|
|
parts.push(format!(
|
|
"Time: {}.",
|
|
format_goal_elapsed_seconds(goal.time_used_seconds)
|
|
));
|
|
}
|
|
if let Some(token_budget) = goal.token_budget {
|
|
parts.push(format!(
|
|
"Tokens: {}/{}.",
|
|
format_tokens_compact(goal.tokens_used),
|
|
format_tokens_compact(token_budget)
|
|
));
|
|
}
|
|
parts.join(" ")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use codex_app_server_protocol::ThreadGoal;
|
|
use codex_app_server_protocol::ThreadGoalStatus;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[test]
|
|
fn format_goal_elapsed_seconds_is_compact() {
|
|
assert_eq!(format_goal_elapsed_seconds(/*seconds*/ 0), "0s");
|
|
assert_eq!(format_goal_elapsed_seconds(/*seconds*/ 59), "59s");
|
|
assert_eq!(format_goal_elapsed_seconds(/*seconds*/ 60), "1m");
|
|
assert_eq!(format_goal_elapsed_seconds(30 * 60), "30m");
|
|
assert_eq!(format_goal_elapsed_seconds(90 * 60), "1h 30m");
|
|
assert_eq!(format_goal_elapsed_seconds(2 * 60 * 60), "2h");
|
|
let just_before_one_day = 24 * 60 * 60 - 1;
|
|
assert_eq!(format_goal_elapsed_seconds(just_before_one_day), "23h 59m");
|
|
|
|
let one_day = 24 * 60 * 60;
|
|
assert_eq!(format_goal_elapsed_seconds(one_day), "1d 0h 0m");
|
|
|
|
let almost_three_days = 2 * 24 * 60 * 60 + 23 * 60 * 60 + 42 * 60;
|
|
assert_eq!(format_goal_elapsed_seconds(almost_three_days), "2d 23h 42m");
|
|
}
|
|
|
|
fn test_thread_goal(token_budget: Option<i64>, tokens_used: i64) -> ThreadGoal {
|
|
ThreadGoal {
|
|
thread_id: "thread-1".to_string(),
|
|
objective: "Complete the task described in ../gameboy-long-running-prompt5.txt"
|
|
.to_string(),
|
|
status: ThreadGoalStatus::BudgetLimited,
|
|
token_budget,
|
|
tokens_used,
|
|
time_used_seconds: 120,
|
|
created_at: 0,
|
|
updated_at: 0,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn goal_usage_summary_formats_time_and_budgeted_tokens() {
|
|
assert_eq!(
|
|
goal_usage_summary(&test_thread_goal(
|
|
/*token_budget*/ Some(50_000),
|
|
/*tokens_used*/ 63_876,
|
|
)),
|
|
"Objective: Complete the task described in ../gameboy-long-running-prompt5.txt Time: 2m. Tokens: 63.9K/50K."
|
|
);
|
|
}
|
|
}
|