feat: skip memory startup when Codex rate limits are low (#19990)

## Why

Memory startup runs in the background after an eligible turn, but it can
consume Codex backend quota at exactly the wrong time: when the user is
already near a rate-limit boundary. This PR adds a guard so the memory
pipeline backs off when the Codex rate-limit snapshot says the remaining
budget is too low.

## What Changed

- Added `memories.min_rate_limit_remaining_percent` with a default of
`25`, clamped to `0..=100`, and regenerated `core/config.schema.json`.
- Added `codex-rs/memories/write/src/guard.rs`, which fetches Codex
backend rate limits before memory startup and skips phase 1 / phase 2
when the Codex limit is reached or either tracked window is above the
configured usage ceiling.
- Keeps startup best-effort: non-Codex auth or rate-limit fetch/client
failures preserve the existing memory startup behavior.
- Records a `codex.memory.startup` counter with
`status=skipped_rate_limit` when startup is skipped.
- Added config parsing/clamping coverage and guard unit tests.

## Verification

- Added `codex-rs/memories/write/src/guard_tests.rs` for threshold,
primary/secondary window, and reached-limit behavior.
- Added config tests for TOML parsing and clamping.
This commit is contained in:
jif-oai
2026-04-28 17:07:16 +02:00
committed by GitHub
parent 0e8d6b8765
commit 1b74360365
10 changed files with 214 additions and 2 deletions

View File

@@ -32,6 +32,7 @@ pub const DEFAULT_OTEL_ENVIRONMENT: &str = "dev";
pub const DEFAULT_MEMORIES_MAX_ROLLOUTS_PER_STARTUP: usize = 2;
pub const DEFAULT_MEMORIES_MAX_ROLLOUT_AGE_DAYS: i64 = 10;
pub const DEFAULT_MEMORIES_MIN_ROLLOUT_IDLE_HOURS: i64 = 6;
pub const DEFAULT_MEMORIES_MIN_RATE_LIMIT_REMAINING_PERCENT: i64 = 25;
pub const DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION: usize = 256;
pub const DEFAULT_MEMORIES_MAX_UNUSED_DAYS: i64 = 30;
const MIN_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION: usize = 1;
@@ -204,6 +205,9 @@ pub struct MemoriesToml {
pub max_rollouts_per_startup: Option<usize>,
/// Minimum idle time between last thread activity and memory creation (hours). > 12h recommended.
pub min_rollout_idle_hours: Option<i64>,
/// Minimum remaining percentage required in Codex rate-limit windows before memory startup runs.
#[schemars(range(min = 0, max = 100))]
pub min_rate_limit_remaining_percent: Option<i64>,
/// Model used for thread summarisation.
pub extract_model: Option<String>,
/// Model used for memory consolidation.
@@ -221,6 +225,7 @@ pub struct MemoriesConfig {
pub max_rollout_age_days: i64,
pub max_rollouts_per_startup: usize,
pub min_rollout_idle_hours: i64,
pub min_rate_limit_remaining_percent: i64,
pub extract_model: Option<String>,
pub consolidation_model: Option<String>,
}
@@ -236,6 +241,7 @@ impl Default for MemoriesConfig {
max_rollout_age_days: DEFAULT_MEMORIES_MAX_ROLLOUT_AGE_DAYS,
max_rollouts_per_startup: DEFAULT_MEMORIES_MAX_ROLLOUTS_PER_STARTUP,
min_rollout_idle_hours: DEFAULT_MEMORIES_MIN_ROLLOUT_IDLE_HOURS,
min_rate_limit_remaining_percent: DEFAULT_MEMORIES_MIN_RATE_LIMIT_REMAINING_PERCENT,
extract_model: None,
consolidation_model: None,
}
@@ -277,6 +283,10 @@ impl From<MemoriesToml> for MemoriesConfig {
.min_rollout_idle_hours
.unwrap_or(defaults.min_rollout_idle_hours)
.clamp(1, 48),
min_rate_limit_remaining_percent: toml
.min_rate_limit_remaining_percent
.unwrap_or(defaults.min_rate_limit_remaining_percent)
.clamp(0, 100),
extract_model: toml.extract_model,
consolidation_model: toml.consolidation_model,
}

View File

@@ -59,3 +59,30 @@ fn memories_config_clamps_count_limits_to_nonzero_values() {
}
);
}
#[test]
fn memories_config_clamps_rate_limit_remaining_threshold() {
let config = MemoriesConfig::from(MemoriesToml {
min_rate_limit_remaining_percent: Some(101),
..Default::default()
});
assert_eq!(
config,
MemoriesConfig {
min_rate_limit_remaining_percent: 100,
..MemoriesConfig::default()
}
);
let config = MemoriesConfig::from(MemoriesToml {
min_rate_limit_remaining_percent: Some(-1),
..Default::default()
});
assert_eq!(
config,
MemoriesConfig {
min_rate_limit_remaining_percent: 0,
..MemoriesConfig::default()
}
);
}