From 7ffdcecb8f8936aec1dbc8886ad532db0e1440ee Mon Sep 17 00:00:00 2001 From: kevin zhao Date: Mon, 17 Nov 2025 11:35:51 -0800 Subject: [PATCH] fetching even if timeout --- codex-rs/tui/src/app.rs | 3 ++ codex-rs/tui/src/app_event.rs | 4 ++ codex-rs/tui/src/chatwidget.rs | 91 ++++++++++++++++++++-------------- 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 4aa295a323..9f996007f8 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -496,6 +496,9 @@ impl App { AppEvent::FileSearchResult { query, matches } => { self.chat_widget.apply_file_search_result(query, matches); } + AppEvent::RateLimitSnapshotFetched(snapshot) => { + self.chat_widget.on_rate_limit_snapshot(Some(snapshot)); + } AppEvent::UpdateReasoningEffort(effort) => { self.on_update_reasoning_effort(effort); } diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index 39485faa93..98a18cb30c 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -4,6 +4,7 @@ use codex_common::approval_presets::ApprovalPreset; use codex_common::model_presets::ModelPreset; use codex_core::protocol::ConversationPathResponseEvent; use codex_core::protocol::Event; +use codex_core::protocol::RateLimitSnapshot; use codex_file_search::FileMatch; use crate::bottom_pane::ApprovalRequest; @@ -41,6 +42,9 @@ pub(crate) enum AppEvent { matches: Vec, }, + /// Result of refreshing rate limits (e.g., after `/status`). + RateLimitSnapshotFetched(RateLimitSnapshot), + /// Result of computing a `/diff` command. DiffResult(String), diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index c2b04f020c..a5b9f6670d 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -65,6 +65,7 @@ use ratatui::text::Line; use ratatui::widgets::Paragraph; use ratatui::widgets::Wrap; use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::oneshot; use tracing::debug; use crate::app_event::AppEvent; @@ -119,6 +120,7 @@ use codex_common::approval_presets::builtin_approval_presets; use codex_common::model_presets::ModelPreset; use codex_common::model_presets::builtin_model_presets; use codex_core::AuthManager; +use codex_core::CodexAuth; use codex_core::ConversationManager; use codex_core::protocol::AskForApproval; use codex_core::protocol::SandboxPolicy; @@ -497,7 +499,7 @@ impl ChatWidget { } } - fn on_rate_limit_snapshot(&mut self, snapshot: Option) { + pub(crate) fn on_rate_limit_snapshot(&mut self, snapshot: Option) { if let Some(snapshot) = snapshot { let warnings = self.rate_limit_warnings.take_warnings( snapshot @@ -1724,62 +1726,61 @@ impl ChatWidget { } pub(crate) fn add_status_output(&mut self) { - let default_usage = TokenUsage::default(); let (total_usage, context_usage) = if let Some(ti) = &self.token_info { - (&ti.total_token_usage, Some(&ti.last_token_usage)) + ( + ti.total_token_usage.clone(), + Some(ti.last_token_usage.clone()), + ) } else { - (&default_usage, Some(&default_usage)) + let usage = TokenUsage::default(); + (usage.clone(), Some(usage)) }; - self.rate_limit_snapshot = self - .fetch_rate_limits_from_usage() - .or(self.rate_limit_snapshot.clone()); + let snapshot_for_display = self + .refresh_rate_limits_from_usage() + .or_else(|| self.rate_limit_snapshot.clone()); self.add_to_history(crate::status::new_status_output( &self.config, self.auth_manager.as_ref(), - total_usage, - context_usage, + &total_usage, + context_usage.as_ref(), &self.conversation_id, - self.rate_limit_snapshot.as_ref(), + snapshot_for_display.as_ref(), Local::now(), )); } - fn fetch_rate_limits_from_usage(&self) -> Option { + fn refresh_rate_limits_from_usage(&mut self) -> Option { let auth = self.auth_manager.auth()?; if auth.mode != AuthMode::ChatGPT { return None; } let base_url = self.config.chatgpt_base_url.clone(); - let auth = auth; - let result = tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - let fetch = async { - let client = BackendClient::from_auth(base_url, &auth).await?; - client.get_rate_limits().await - }; - tokio::time::timeout(Duration::from_secs(1), fetch).await - }) + let app_event_tx = self.app_event_tx.clone(); + let (tx, rx) = oneshot::channel(); + + tokio::spawn(async move { + let snapshot = fetch_rate_limits(base_url, auth).await; + if let Some(snapshot) = snapshot { + app_event_tx.send(AppEvent::RateLimitSnapshotFetched(snapshot.clone())); + let _ = tx.send(Some(snapshot)); + } else { + let _ = tx.send(None); + } }); - match result { - Ok(Ok(snapshot)) => Some(crate::status::rate_limit_snapshot_display( - &snapshot, - Local::now(), - )), - Ok(Err(err)) => { - tracing::debug!( - error = ?err, - "failed to fetch rate limits from /usage within timeout" - ); - None - } - Err(_) => { - tracing::debug!("timed out fetching rate limits from /usage"); - None - } - } + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + match tokio::time::timeout(Duration::from_secs(1), rx).await { + Ok(Ok(Some(snapshot))) => Some(crate::status::rate_limit_snapshot_display( + &snapshot, + Local::now(), + )), + Ok(Ok(None)) | Ok(Err(_)) | Err(_) => None, + } + }) + }) } fn lower_cost_preset(&self) -> Option { @@ -2936,6 +2937,22 @@ fn extract_first_bold(s: &str) -> Option { None } +async fn fetch_rate_limits(base_url: String, auth: CodexAuth) -> Option { + match BackendClient::from_auth(base_url, &auth).await { + Ok(client) => match client.get_rate_limits().await { + Ok(snapshot) => Some(snapshot), + Err(err) => { + debug!(error = ?err, "failed to fetch rate limits from /usage"); + None + } + }, + Err(err) => { + debug!(error = ?err, "failed to construct backend client for rate limits"); + None + } + } +} + #[cfg(test)] pub(crate) fn show_review_commit_picker_with_entries( chat: &mut ChatWidget,