Match usage-limit TUI copy

This commit is contained in:
Richard Lee
2026-04-15 13:54:55 -07:00
parent 8ad0619f11
commit 9ef7712d06
9 changed files with 158 additions and 34 deletions
+2 -1
View File
@@ -4601,7 +4601,8 @@ impl App {
self.refresh_rate_limits(app_server, origin);
}
AppEvent::SendAddCreditsNudgeEmail { credit_type } => {
self.chat_widget.start_add_credits_nudge_email_request();
self.chat_widget
.start_add_credits_nudge_email_request(credit_type);
self.send_add_credits_nudge_email(app_server, credit_type);
}
AppEvent::AddCreditsNudgeEmailFinished { result } => {
+54 -25
View File
@@ -776,7 +776,7 @@ pub(crate) struct ChatWidget {
codex_rate_limit_reached_type: Option<RateLimitReachedType>,
rate_limit_warnings: RateLimitWarningState,
rate_limit_switch_prompt: RateLimitSwitchPromptState,
add_credits_nudge_email_in_flight: bool,
add_credits_nudge_email_in_flight: Option<AddCreditsNudgeCreditType>,
adaptive_chunking: AdaptiveChunkingPolicy,
// Stream lifecycle controller
stream_controller: Option<StreamController>,
@@ -2806,20 +2806,30 @@ impl ChatWidget {
}
fn on_rate_limit_error(&mut self, message: String) {
self.on_error(message);
match self.codex_rate_limit_reached_type {
Some(RateLimitReachedType::WorkspaceOwnerCreditsDepleted) => {
self.on_error(
"You're out of credits. Your workspace is out of credits. Add credits to continue using Codex."
.to_string(),
);
}
Some(RateLimitReachedType::WorkspaceOwnerUsageLimitReached) => {
self.on_error(
"Usage limit reached. You've reached your usage limit. Increase your limits to continue using codex."
.to_string(),
);
}
Some(RateLimitReachedType::WorkspaceMemberCreditsDepleted) => {
self.on_error(message);
self.open_workspace_owner_nudge_prompt(AddCreditsNudgeCreditType::Credits);
}
Some(RateLimitReachedType::WorkspaceMemberUsageLimitReached) => {
self.on_error(message);
self.open_workspace_owner_nudge_prompt(AddCreditsNudgeCreditType::UsageLimit);
}
Some(
RateLimitReachedType::RateLimitReached
| RateLimitReachedType::WorkspaceOwnerCreditsDepleted
| RateLimitReachedType::WorkspaceOwnerUsageLimitReached,
)
| None => {}
Some(RateLimitReachedType::RateLimitReached) | None => {
self.on_error(message);
}
}
}
@@ -4833,7 +4843,7 @@ impl ChatWidget {
codex_rate_limit_reached_type: None,
rate_limit_warnings: RateLimitWarningState::default(),
rate_limit_switch_prompt: RateLimitSwitchPromptState::default(),
add_credits_nudge_email_in_flight: false,
add_credits_nudge_email_in_flight: None,
adaptive_chunking: AdaptiveChunkingPolicy::default(),
stream_controller: None,
plan_stream_controller: None,
@@ -7491,17 +7501,19 @@ impl ChatWidget {
}
fn open_workspace_owner_nudge_prompt(&mut self, credit_type: AddCreditsNudgeCreditType) {
if self.add_credits_nudge_email_in_flight {
if self.add_credits_nudge_email_in_flight.is_some() {
return;
}
let prompt = match credit_type {
AddCreditsNudgeCreditType::Credits => {
"Your workspace is out of credits. Notify your workspace owner?"
}
AddCreditsNudgeCreditType::UsageLimit => {
"Your workspace usage limit has been reached. Request an increase from your workspace owner?"
}
let (title, prompt) = match credit_type {
AddCreditsNudgeCreditType::Credits => (
"You've reached your workspace credit limit",
"Your workspace is out of credits. Ask your workspace owner to add more. Notify owner?",
),
AddCreditsNudgeCreditType::UsageLimit => (
"Usage limit reached",
"Request a limit increase from your owner to continue using codex. Request increase?",
),
};
let send_actions: Vec<SelectionAction> = vec![Box::new(move |tx| {
tx.send(AppEvent::SendAddCreditsNudgeEmail { credit_type });
@@ -7524,7 +7536,7 @@ impl ChatWidget {
];
self.bottom_pane.show_selection_view(SelectionViewParams {
title: Some("Workspace limit reached".to_string()),
title: Some(title.to_string()),
subtitle: Some(prompt.to_string()),
footer_hint: Some(standard_popup_hint_line()),
items,
@@ -7533,23 +7545,40 @@ impl ChatWidget {
});
}
pub(crate) fn start_add_credits_nudge_email_request(&mut self) {
self.add_credits_nudge_email_in_flight = true;
pub(crate) fn start_add_credits_nudge_email_request(
&mut self,
credit_type: AddCreditsNudgeCreditType,
) {
self.add_credits_nudge_email_in_flight = Some(credit_type);
}
pub(crate) fn finish_add_credits_nudge_email_request(
&mut self,
result: AddCreditsNudgeEmailResult,
) {
self.add_credits_nudge_email_in_flight = false;
let message = match result {
AddCreditsNudgeEmailResult::Sent => "Workspace owner notified.",
AddCreditsNudgeEmailResult::CooldownActive => {
let credit_type = self
.add_credits_nudge_email_in_flight
.take()
.unwrap_or(AddCreditsNudgeCreditType::Credits);
let message = match (credit_type, result) {
(AddCreditsNudgeCreditType::Credits, AddCreditsNudgeEmailResult::Sent) => {
"Workspace owner notified."
}
(AddCreditsNudgeCreditType::Credits, AddCreditsNudgeEmailResult::CooldownActive) => {
"Workspace owner was already notified recently."
}
AddCreditsNudgeEmailResult::Failed { .. } => {
(AddCreditsNudgeCreditType::Credits, AddCreditsNudgeEmailResult::Failed { .. }) => {
"Could not notify your workspace owner. Please try again."
}
(AddCreditsNudgeCreditType::UsageLimit, AddCreditsNudgeEmailResult::Sent) => {
"Limit increase requested."
}
(AddCreditsNudgeCreditType::UsageLimit, AddCreditsNudgeEmailResult::CooldownActive) => {
"A limit increase was already requested recently."
}
(AddCreditsNudgeCreditType::UsageLimit, AddCreditsNudgeEmailResult::Failed { .. }) => {
"Could not request a limit increase. Please try again."
}
};
self.add_to_history(history_cell::new_info_event(
message.to_string(),
@@ -2,8 +2,8 @@
source: tui/src/chatwidget/tests/status_and_layout.rs
expression: popup
---
Workspace limit reached
Your workspace is out of credits. Notify your workspace owner?
You've reached your workspace credit limit
Your workspace is out of credits. Ask your workspace owner to add more. Notify owner?
1. Yes (y)
› 2. No (default) (n)
@@ -2,8 +2,8 @@
source: tui/src/chatwidget/tests/status_and_layout.rs
expression: popup
---
Workspace limit reached
Your workspace usage limit has been reached. Request an increase from your workspace owner?
Usage limit reached
Request a limit increase from your owner to continue using codex. Request increase?
1. Yes (y)
› 2. No (default) (n)
@@ -0,0 +1,8 @@
---
source: tui/src/chatwidget/tests/status_and_layout.rs
expression: "rendered_cases.join(\"\\n---\\n\")"
---
■ You're out of credits. Your workspace is out of credits. Add credits to continue using Codex.
---
■ Usage limit reached. You've reached your usage limit. Increase your limits to continue using codex.
@@ -0,0 +1,11 @@
---
source: tui/src/chatwidget/tests/status_and_layout.rs
expression: "rendered_cases.join(\"\\n---\\n\")"
---
• Limit increase requested.
---
• A limit increase was already requested recently.
---
• Could not request a limit increase. Please try again.
+1 -1
View File
@@ -203,7 +203,7 @@ pub(super) async fn make_chatwidget_manual(
codex_rate_limit_reached_type: None,
rate_limit_warnings: RateLimitWarningState::default(),
rate_limit_switch_prompt: RateLimitSwitchPromptState::default(),
add_credits_nudge_email_in_flight: false,
add_credits_nudge_email_in_flight: None,
adaptive_chunking: crate::streaming::chunking::AdaptiveChunkingPolicy::default(),
stream_controller: None,
plan_stream_controller: None,
@@ -540,7 +540,7 @@ async fn workspace_owner_limit_states_do_not_prompt_for_owner_nudge() {
RateLimitReachedType::WorkspaceOwnerUsageLimitReached,
RateLimitReachedType::RateLimitReached,
] {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(limit_type);
chat.on_rate_limit_snapshot(Some(limits));
@@ -548,9 +548,45 @@ async fn workspace_owner_limit_states_do_not_prompt_for_owner_nudge() {
chat.on_rate_limit_error("Usage limit reached.".to_string());
let popup = render_bottom_popup(&chat, /*width*/ 90);
assert!(!popup.contains("workspace owner"));
assert_no_owner_nudge_or_rate_limit_refresh(&mut rx);
}
}
#[tokio::test]
async fn workspace_owner_limit_states_render_state_specific_messages() {
let cases = [
(
RateLimitReachedType::WorkspaceOwnerCreditsDepleted,
"You're out of credits. Your workspace is out of credits. Add credits to continue using Codex.",
),
(
RateLimitReachedType::WorkspaceOwnerUsageLimitReached,
"Usage limit reached. You've reached your usage limit. Increase your limits to continue using codex.",
),
];
let mut rendered_cases = Vec::new();
for (limit_type, expected) in cases {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
let mut limits = snapshot(/*percent*/ 100.0);
limits.rate_limit_reached_type = Some(limit_type);
chat.on_rate_limit_snapshot(Some(limits));
chat.on_rate_limit_error("Usage limit reached.".to_string());
let rendered = drain_insert_history(&mut rx)
.into_iter()
.map(|lines| lines_to_single_string(&lines))
.collect::<String>();
assert!(rendered.contains(expected), "rendered: {rendered}");
rendered_cases.push(rendered);
}
assert_chatwidget_snapshot!(
"workspace_owner_limit_state_messages",
rendered_cases.join("\n---\n")
);
}
#[tokio::test]
async fn missing_rate_limit_reached_type_does_not_prompt_or_refresh() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
@@ -576,7 +612,7 @@ async fn workspace_owner_nudge_default_no_dismisses_without_sending() {
}
#[tokio::test]
async fn workspace_owner_nudge_completion_renders_feedback() {
async fn workspace_owner_credits_nudge_completion_renders_feedback() {
let cases = [
(
AddCreditsNudgeEmailResult::Sent,
@@ -597,6 +633,7 @@ async fn workspace_owner_nudge_completion_renders_feedback() {
let mut rendered_cases = Vec::new();
for (result, expected) in cases {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.start_add_credits_nudge_email_request(AddCreditsNudgeCreditType::Credits);
chat.finish_add_credits_nudge_email_request(result);
let rendered = drain_insert_history(&mut rx)
.into_iter()
@@ -607,7 +644,45 @@ async fn workspace_owner_nudge_completion_renders_feedback() {
}
assert_chatwidget_snapshot!(
"workspace_owner_nudge_completion_feedback",
"workspace_owner_credits_nudge_completion_feedback",
rendered_cases.join("\n---\n")
);
}
#[tokio::test]
async fn workspace_owner_usage_limit_nudge_completion_renders_feedback() {
let cases = [
(
AddCreditsNudgeEmailResult::Sent,
"Limit increase requested.",
),
(
AddCreditsNudgeEmailResult::CooldownActive,
"A limit increase was already requested recently.",
),
(
AddCreditsNudgeEmailResult::Failed {
message: "request failed".to_string(),
},
"Could not request a limit increase. Please try again.",
),
];
let mut rendered_cases = Vec::new();
for (result, expected) in cases {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.start_add_credits_nudge_email_request(AddCreditsNudgeCreditType::UsageLimit);
chat.finish_add_credits_nudge_email_request(result);
let rendered = drain_insert_history(&mut rx)
.into_iter()
.map(|lines| lines_to_single_string(&lines))
.collect::<String>();
assert!(rendered.contains(expected), "rendered: {rendered}");
rendered_cases.push(rendered);
}
assert_chatwidget_snapshot!(
"workspace_owner_usage_limit_nudge_completion_feedback",
rendered_cases.join("\n---\n")
);
}