mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Preserve usage limit windows in turn and compaction analytics (#48174)
## Why Usage limit errors discarded the server-selected window responsible for the limit, leaving turn and compaction analytics unable to distinguish five-hour limits from weekly limits. ## What changed Preserve optional `limit_window_minutes` from usage limit error responses in `UsageLimitReachedError` and report it as `usage_limit_window_minutes` in turn and compaction events. Accept unsigned integers that fit in `u16`; treat missing, null, malformed, or out-of-range values as unknown. Events for other error kinds report `null`. ## Testing Add coverage for HTTP and wrapped WebSocket error mapping, analytics serialization and reduction, and app-server propagation to turn and local/remote compaction events. Cases include five-hour and weekly windows, missing or invalid values, and unrelated errors. GitOrigin-RevId: a32066a007559d91a3c868a756bfd0f976777176
This commit is contained in:
@@ -676,6 +676,8 @@ impl std::fmt::Display for RetryLimitReachedError {
|
||||
pub struct UsageLimitReachedError {
|
||||
pub plan_type: Option<PlanType>,
|
||||
pub resets_at: Option<DateTime<Utc>>,
|
||||
/// Server-selected window responsible for the limit, in minutes.
|
||||
pub limit_window_minutes: Option<u16>,
|
||||
pub rate_limits: Option<Box<RateLimitSnapshot>>,
|
||||
pub promo_message: Option<String>,
|
||||
pub rate_limit_reached_type: Option<RateLimitReachedType>,
|
||||
|
||||
@@ -156,6 +156,7 @@ fn with_now_override<T>(now: DateTime<Utc>, f: impl FnOnce() -> T) -> T {
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_plus_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Plus)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -195,6 +196,7 @@ fn usage_limit_reached_error_formats_rate_limit_reached_types() {
|
||||
|
||||
for (rate_limit_reached_type, expected) in cases {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Plus)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -318,6 +320,7 @@ fn sandbox_denied_reports_exit_code_when_no_output_available() {
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_free_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Free)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -333,6 +336,7 @@ fn usage_limit_reached_error_formats_free_plan() {
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_go_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Go)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -348,6 +352,7 @@ fn usage_limit_reached_error_formats_go_plan() {
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_default_when_none() {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: None,
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -367,6 +372,7 @@ fn usage_limit_reached_error_formats_team_plan() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Team)),
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -388,6 +394,7 @@ fn usage_limit_reached_error_formats_business_plan_without_reset() {
|
||||
KnownPlan::EnterpriseCbpAutomation,
|
||||
] {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(plan)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -404,6 +411,7 @@ fn usage_limit_reached_error_formats_business_plan_without_reset() {
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_self_serve_business_prolite_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::SelfServeBusinessProLite)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -419,6 +427,7 @@ fn usage_limit_reached_error_formats_self_serve_business_prolite_plan() {
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_self_serve_business_usage_based_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::SelfServeBusinessUsageBased)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -434,6 +443,7 @@ fn usage_limit_reached_error_formats_self_serve_business_usage_based_plan() {
|
||||
#[test]
|
||||
fn usage_limit_reached_error_formats_enterprise_cbp_usage_based_plan() {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::EnterpriseCbpUsageBased)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -455,6 +465,7 @@ fn usage_limit_reached_error_formats_default_for_other_plans() {
|
||||
KnownPlan::EduPro,
|
||||
] {
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(plan)),
|
||||
resets_at: None,
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -475,6 +486,7 @@ fn usage_limit_reached_error_formats_pro_plan_with_reset() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Pro)),
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -495,6 +507,7 @@ fn usage_limit_reached_error_hides_upsell_for_non_codex_limit_name() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Plus)),
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(RateLimitSnapshot {
|
||||
@@ -522,6 +535,7 @@ fn usage_limit_reached_includes_minutes_when_available() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: None,
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -666,6 +680,7 @@ fn usage_limit_reached_includes_hours_and_minutes() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: Some(PlanType::Known(KnownPlan::Plus)),
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -687,6 +702,7 @@ fn usage_limit_reached_includes_days_hours_minutes() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: None,
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -705,6 +721,7 @@ fn usage_limit_reached_less_than_minute() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: None,
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
@@ -723,6 +740,7 @@ fn usage_limit_reached_with_promo_message() {
|
||||
with_now_override(base, move || {
|
||||
let expected_time = format_retry_timestamp(&resets_at);
|
||||
let err = UsageLimitReachedError {
|
||||
limit_window_minutes: None,
|
||||
plan_type: None,
|
||||
resets_at: Some(resets_at),
|
||||
rate_limits: Some(Box::new(rate_limit_snapshot())),
|
||||
|
||||
Reference in New Issue
Block a user