[codex] Propagate rate limit reached type (#18227)

## Summary

First PR in the split from #17956.

- adds the core/app-server `RateLimitReachedType` shape
- maps backend `rate_limit_reached_type` into Codex rate-limit snapshots
- carries the field through app-server notifications/responses and
generated schemas
- updates existing constructors/tests for the new optional field

## Validation

- `cargo test -p codex-backend-client`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server rate_limits`
- `cargo test -p codex-tui workspace_`
- `cargo test -p codex-tui status_`
- `just fmt`
- `just fix -p codex-backend-client`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-app-server`
- `just fix -p codex-tui`
This commit is contained in:
richardopenai
2026-04-17 13:37:25 -07:00
committed by GitHub
parent f017a23835
commit 139fa8b8f2
27 changed files with 371 additions and 6 deletions
+1
View File
@@ -1188,6 +1188,7 @@ pub(crate) fn app_server_rate_limit_snapshot_to_core(
secondary: snapshot.secondary.map(app_server_rate_limit_window_to_core),
credits: snapshot.credits.map(app_server_credits_snapshot_to_core),
plan_type: snapshot.plan_type,
rate_limit_reached_type: snapshot.rate_limit_reached_type.map(Into::into),
}
}
@@ -106,6 +106,7 @@ pub(super) fn snapshot(percent: f64) -> RateLimitSnapshot {
secondary: None,
credits: None,
plan_type: None,
rate_limit_reached_type: None,
}
}
@@ -235,6 +235,7 @@ async fn rate_limit_snapshot_keeps_prior_credits_when_missing_from_headers() {
balance: Some("17.5".to_string()),
}),
plan_type: None,
rate_limit_reached_type: None,
}));
let initial_balance = chat
.rate_limit_snapshots_by_limit_id
@@ -254,6 +255,7 @@ async fn rate_limit_snapshot_keeps_prior_credits_when_missing_from_headers() {
secondary: None,
credits: None,
plan_type: None,
rate_limit_reached_type: None,
}));
let display = chat
@@ -292,6 +294,7 @@ async fn rate_limit_snapshot_updates_and_retains_plan_type() {
}),
credits: None,
plan_type: Some(PlanType::Plus),
rate_limit_reached_type: None,
}));
assert_eq!(chat.plan_type, Some(PlanType::Plus));
@@ -310,6 +313,7 @@ async fn rate_limit_snapshot_updates_and_retains_plan_type() {
}),
credits: None,
plan_type: Some(PlanType::Pro),
rate_limit_reached_type: None,
}));
assert_eq!(chat.plan_type, Some(PlanType::Pro));
@@ -328,6 +332,7 @@ async fn rate_limit_snapshot_updates_and_retains_plan_type() {
}),
credits: None,
plan_type: None,
rate_limit_reached_type: None,
}));
assert_eq!(chat.plan_type, Some(PlanType::Pro));
}
@@ -351,6 +356,7 @@ async fn rate_limit_snapshots_keep_separate_entries_per_limit_id() {
balance: Some("5.00".to_string()),
}),
plan_type: Some(PlanType::Pro),
rate_limit_reached_type: None,
}));
chat.on_rate_limit_snapshot(Some(RateLimitSnapshot {
@@ -364,6 +370,7 @@ async fn rate_limit_snapshots_keep_separate_entries_per_limit_id() {
secondary: None,
credits: None,
plan_type: Some(PlanType::Pro),
rate_limit_reached_type: None,
}));
let codex = chat
@@ -416,6 +423,7 @@ async fn rate_limit_switch_prompt_skips_non_codex_limit() {
secondary: None,
credits: None,
plan_type: None,
rate_limit_reached_type: None,
}));
assert!(matches!(
+13
View File
@@ -139,6 +139,7 @@ async fn status_snapshot_includes_reasoning_details() {
}),
credits: None,
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
@@ -322,6 +323,7 @@ async fn status_snapshot_includes_monthly_limit() {
secondary: None,
credits: None,
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
@@ -373,6 +375,7 @@ async fn status_snapshot_shows_unlimited_credits() {
balance: None,
}),
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
let model_slug = crate::legacy_core::test_support::get_model_offline(config.model.as_deref());
@@ -422,6 +425,7 @@ async fn status_snapshot_shows_positive_credits() {
balance: Some("12.5".to_string()),
}),
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
let model_slug = crate::legacy_core::test_support::get_model_offline(config.model.as_deref());
@@ -471,6 +475,7 @@ async fn status_snapshot_hides_zero_credits() {
balance: Some("0".to_string()),
}),
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
let model_slug = crate::legacy_core::test_support::get_model_offline(config.model.as_deref());
@@ -518,6 +523,7 @@ async fn status_snapshot_hides_when_has_no_credits_flag() {
balance: None,
}),
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
let model_slug = crate::legacy_core::test_support::get_model_offline(config.model.as_deref());
@@ -623,6 +629,7 @@ async fn status_snapshot_truncates_in_narrow_terminal() {
secondary: None,
credits: None,
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
@@ -786,6 +793,7 @@ async fn status_snapshot_shows_refreshing_limits_notice() {
}),
credits: None,
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
@@ -856,6 +864,7 @@ async fn status_snapshot_includes_credits_and_limits() {
balance: Some("37.5".to_string()),
}),
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
@@ -909,6 +918,7 @@ async fn status_snapshot_shows_unavailable_limits_message() {
secondary: None,
credits: None,
plan_type: None,
rate_limit_reached_type: None,
};
let captured_at = chrono::Local
.with_ymd_and_hms(2024, 6, 7, 8, 9, 10)
@@ -965,6 +975,7 @@ async fn status_snapshot_treats_refreshing_empty_limits_as_unavailable() {
secondary: None,
credits: None,
plan_type: None,
rate_limit_reached_type: None,
};
let captured_at = chrono::Local
.with_ymd_and_hms(2024, 6, 7, 8, 9, 10)
@@ -1035,6 +1046,7 @@ async fn status_snapshot_shows_stale_limits_message() {
}),
credits: None,
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
let now = captured_at + ChronoDuration::minutes(20);
@@ -1105,6 +1117,7 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() {
balance: Some("80".to_string()),
}),
plan_type: None,
rate_limit_reached_type: None,
};
let rate_display = rate_limit_snapshot_display(&snapshot, captured_at);
let now = captured_at + ChronoDuration::minutes(20);