From a4c95f5fbdb22f165c41d31a16c365b3edbeff97 Mon Sep 17 00:00:00 2001 From: Jarrett Ye Date: Sat, 21 Jun 2025 01:59:35 +0800 Subject: [PATCH] include decay in ComputeMemoryStateResponse (#4102) * include decay in ComputeMemoryStateResponse * Add decay attribute to ComputedMemoryState and update Collection methods * Refactor decay calculation into a helper function for improved readability and maintainability in memory state management * format & clippy --- proto/anki/scheduler.proto | 1 + pylib/anki/collection.py | 7 +++++- rslib/src/scheduler/fsrs/memory_state.rs | 28 +++++++++++++++--------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/proto/anki/scheduler.proto b/proto/anki/scheduler.proto index ea483d3db..1b7d44a83 100644 --- a/proto/anki/scheduler.proto +++ b/proto/anki/scheduler.proto @@ -450,6 +450,7 @@ message EvaluateParamsResponse { message ComputeMemoryStateResponse { optional cards.FsrsMemoryState state = 1; float desired_retention = 2; + float decay = 3; } message FuzzDeltaRequest { diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 17ee08e2f..6cf38174c 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -122,6 +122,7 @@ class ComputedMemoryState: desired_retention: float stability: float | None = None difficulty: float | None = None + decay: float | None = None @dataclass @@ -1189,9 +1190,13 @@ class Collection(DeprecatedNamesMixin): desired_retention=resp.desired_retention, stability=resp.state.stability, difficulty=resp.state.difficulty, + decay=resp.decay, ) else: - return ComputedMemoryState(desired_retention=resp.desired_retention) + return ComputedMemoryState( + desired_retention=resp.desired_retention, + decay=resp.decay, + ) def fuzz_delta(self, card_id: CardId, interval: int) -> int: "The delta days of fuzz applied if reviewing the card in v3." diff --git a/rslib/src/scheduler/fsrs/memory_state.rs b/rslib/src/scheduler/fsrs/memory_state.rs index 787fa212d..f5af97674 100644 --- a/rslib/src/scheduler/fsrs/memory_state.rs +++ b/rslib/src/scheduler/fsrs/memory_state.rs @@ -30,6 +30,18 @@ pub struct ComputeMemoryProgress { pub total_cards: u32, } +/// Helper function to determine the appropriate decay value based on FSRS +/// parameters +fn get_decay_from_params(params: &[f32]) -> f32 { + if params.is_empty() { + FSRS6_DEFAULT_DECAY // default decay for FSRS-6 + } else if params.len() < 21 { + FSRS5_DEFAULT_DECAY // default decay for FSRS-4.5 and FSRS-5 + } else { + params[20] + } +} + #[derive(Debug)] pub(crate) struct UpdateMemoryStateRequest { pub params: Params, @@ -77,15 +89,7 @@ impl Collection { .then(|| Rescheduler::new(self)) .transpose()?; let fsrs = FSRS::new(req.as_ref().map(|w| &w.params[..]).or(Some([].as_slice())))?; - let decay = req.as_ref().map(|w| { - if w.params.is_empty() { - FSRS6_DEFAULT_DECAY // default decay for FSRS-6 - } else if w.params.len() < 21 { - FSRS5_DEFAULT_DECAY // default decay for FSRS-4.5 and FSRS-5 - } else { - w.params[20] - } - }); + let decay = req.as_ref().map(|w| get_decay_from_params(&w.params)); let historical_retention = req.as_ref().map(|w| w.historical_retention); let items = fsrs_items_for_memory_states( &fsrs, @@ -190,7 +194,9 @@ impl Collection { .or_not_found(conf_id)?; let desired_retention = config.inner.desired_retention; let historical_retention = config.inner.historical_retention; - let fsrs = FSRS::new(Some(config.fsrs_params()))?; + let params = config.fsrs_params(); + let decay = get_decay_from_params(params); + let fsrs = FSRS::new(Some(params))?; let revlog = self.revlog_for_srs(SearchNode::CardIds(card.id.to_string()))?; let item = fsrs_item_for_memory_state( &fsrs, @@ -204,6 +210,7 @@ impl Collection { Ok(ComputeMemoryStateResponse { state: card.memory_state.map(Into::into), desired_retention, + decay, }) } else { card.memory_state = None; @@ -211,6 +218,7 @@ impl Collection { Ok(ComputeMemoryStateResponse { state: None, desired_retention, + decay, }) } }