Add limits to /status (#4053)
Add limits to status <img width="579" height="430" alt="image" src="https://github.com/user-attachments/assets/d3794d92-ffca-47be-8011-b4452223cc89" />
This commit is contained in:
@@ -1368,6 +1368,7 @@ impl ChatWidget {
|
||||
&self.config,
|
||||
usage_ref,
|
||||
&self.conversation_id,
|
||||
self.rate_limit_snapshot.as_ref(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@ use std::time::Instant;
|
||||
use tracing::error;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
const STATUS_LIMIT_BAR_SEGMENTS: usize = 20;
|
||||
const STATUS_LIMIT_BAR_FILLED: &str = "█";
|
||||
const STATUS_LIMIT_BAR_EMPTY: &str = " ";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct CommandOutput {
|
||||
pub(crate) exit_code: i32,
|
||||
@@ -1123,6 +1127,7 @@ pub(crate) fn new_status_output(
|
||||
config: &Config,
|
||||
usage: &TokenUsage,
|
||||
session_id: &Option<ConversationId>,
|
||||
rate_limits: Option<&RateLimitSnapshotEvent>,
|
||||
) -> PlainHistoryCell {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
lines.push("/status".magenta().into());
|
||||
@@ -1283,6 +1288,9 @@ pub(crate) fn new_status_output(
|
||||
format_with_separators(usage.blended_total()).into(),
|
||||
]));
|
||||
|
||||
lines.push("".into());
|
||||
lines.extend(build_status_limit_lines(rate_limits));
|
||||
|
||||
PlainHistoryCell { lines }
|
||||
}
|
||||
|
||||
@@ -1640,6 +1648,62 @@ fn format_mcp_invocation<'a>(invocation: McpInvocation) -> Line<'a> {
|
||||
invocation_spans.into()
|
||||
}
|
||||
|
||||
fn build_status_limit_lines(snapshot: Option<&RateLimitSnapshotEvent>) -> Vec<Line<'static>> {
|
||||
let mut lines: Vec<Line<'static>> =
|
||||
vec![vec![padded_emoji("⏱️").into(), "Usage Limits".bold()].into()];
|
||||
|
||||
match snapshot {
|
||||
Some(snapshot) => {
|
||||
let rows = [
|
||||
("5h limit".to_string(), snapshot.primary_used_percent),
|
||||
("Weekly limit".to_string(), snapshot.weekly_used_percent),
|
||||
];
|
||||
let label_width = rows
|
||||
.iter()
|
||||
.map(|(label, _)| UnicodeWidthStr::width(label.as_str()))
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
for (label, percent) in rows {
|
||||
lines.push(build_status_limit_line(&label, percent, label_width));
|
||||
}
|
||||
}
|
||||
None => lines.push(" • Rate limit data not available yet.".dim().into()),
|
||||
}
|
||||
|
||||
lines
|
||||
}
|
||||
|
||||
fn build_status_limit_line(label: &str, percent_used: f64, label_width: usize) -> Line<'static> {
|
||||
let clamped_percent = percent_used.clamp(0.0, 100.0);
|
||||
let progress = render_status_limit_progress_bar(clamped_percent);
|
||||
let summary = format_status_limit_summary(clamped_percent);
|
||||
|
||||
let mut spans: Vec<Span<'static>> = Vec::with_capacity(5);
|
||||
let padded_label = format!("{label:<label_width$}");
|
||||
spans.push(format!(" • {padded_label}: ").into());
|
||||
spans.push(progress.into());
|
||||
spans.push(" ".into());
|
||||
spans.push(summary.into());
|
||||
|
||||
Line::from(spans)
|
||||
}
|
||||
|
||||
fn render_status_limit_progress_bar(percent_used: f64) -> String {
|
||||
let ratio = (percent_used / 100.0).clamp(0.0, 1.0);
|
||||
let filled = (ratio * STATUS_LIMIT_BAR_SEGMENTS as f64).round() as usize;
|
||||
let filled = filled.min(STATUS_LIMIT_BAR_SEGMENTS);
|
||||
let empty = STATUS_LIMIT_BAR_SEGMENTS.saturating_sub(filled);
|
||||
format!(
|
||||
"[{}{}]",
|
||||
STATUS_LIMIT_BAR_FILLED.repeat(filled),
|
||||
STATUS_LIMIT_BAR_EMPTY.repeat(empty)
|
||||
)
|
||||
}
|
||||
|
||||
fn format_status_limit_summary(percent_used: f64) -> String {
|
||||
format!("{percent_used:.0}% used")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user