feat(llm): improve UTF-8 handling in response processing to prevent infinite loops
feat(shell): enhance output capturing by using threads for stdout and stderr feat(tui): update usage widget to display token counts and provider/model information refactor(tui): simplify status bar rendering by removing unnecessary token calculations
This commit is contained in:
@@ -96,19 +96,40 @@ fn draw_usage_widget(frame: &mut Frame, area: Rect, state: &crate::state::AppSta
|
||||
let lines: Vec<Line> = if let Some(ref rt) = state.session_runtime {
|
||||
let now_ms = chrono::Utc::now().timestamp_millis();
|
||||
let summary = compute_usage_summary(&rt.usage, rt.session_start, now_ms);
|
||||
vec![
|
||||
let max_tokens = crate::state::resolve_context_window(&state.app_config, &state.settings);
|
||||
let current_tokens = state.cached_token_count;
|
||||
|
||||
let mut items = vec![
|
||||
Line::from(Span::styled(
|
||||
format!(" {:>6}: {} tok", "total", summary.total_tokens),
|
||||
Style::default()
|
||||
.fg(Theme::TEXT)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
];
|
||||
|
||||
if summary.self_learning_tokens > 0 {
|
||||
items.push(Line::from(Span::styled(
|
||||
format!(" {:>6}: {} tok", "main", summary.main_tokens),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)));
|
||||
items.push(Line::from(Span::styled(
|
||||
format!(" {:>6}: {} tok", "learn", summary.self_learning_tokens),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)));
|
||||
}
|
||||
|
||||
items.extend(vec![
|
||||
Line::from(Span::styled(
|
||||
format!(" {:>6}: {}/{}", "ctx", current_tokens, max_tokens),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" {:>6}: {} tok", "learn", summary.self_learning_tokens),
|
||||
format!(" {:>6}: {}", "prov", state.settings.provider),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!(" {:>6}: {}", "model", state.settings.model),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
@@ -122,7 +143,9 @@ fn draw_usage_widget(frame: &mut Frame, area: Rect, state: &crate::state::AppSta
|
||||
),
|
||||
Style::default().fg(Theme::TEXT_DIM),
|
||||
)),
|
||||
]
|
||||
]);
|
||||
|
||||
items
|
||||
} else {
|
||||
vec![Line::from(Span::styled(
|
||||
" No active session.",
|
||||
|
||||
@@ -48,32 +48,7 @@ pub fn draw_status_bar(frame: &mut Frame, area: Rect, state: &crate::state::AppS
|
||||
status_badge,
|
||||
];
|
||||
|
||||
let max_tokens = crate::state::resolve_context_window(&state.app_config, &state.settings);
|
||||
|
||||
let right_str = if let Some(ref rt) = state.session_runtime {
|
||||
// Use cached token count — recomputed lazily only when new messages
|
||||
// arrive (token_count_dirty flag), not on every render frame.
|
||||
// This eliminates the expensive tiktoken_rs call from the hot path.
|
||||
let current_tokens = state.cached_token_count;
|
||||
|
||||
let mut parts = Vec::new();
|
||||
if rt.usage.last_tokens_in > 0 || rt.usage.last_tokens_out > 0 {
|
||||
parts.push(format!(
|
||||
"↑{} ↓{}",
|
||||
rt.usage.last_tokens_in, rt.usage.last_tokens_out
|
||||
));
|
||||
}
|
||||
parts.push(format!("{current_tokens}/{max_tokens}"));
|
||||
parts.push(state.settings.provider.clone());
|
||||
parts.push(state.settings.model.clone());
|
||||
|
||||
format!(" {} ", parts.join(" · "))
|
||||
} else {
|
||||
format!(
|
||||
" 0/{max_tokens} · {} · {} ",
|
||||
state.settings.provider, state.settings.model
|
||||
)
|
||||
};
|
||||
let right_str = String::new();
|
||||
|
||||
let left_line = Line::from(left_spans);
|
||||
let right_line = Line::from(Span::styled(
|
||||
|
||||
Reference in New Issue
Block a user