#[derive(Debug, Clone)]
pub struct Curator {
interval: u32,
turns_since_nudge: u32,
}
impl Curator {
pub fn new(interval: u32) -> Self {
Self { interval, turns_since_nudge: 0 }
}
pub fn interval(&self) -> u32 {
self.interval
}
pub fn record_user_turn(&mut self) {
self.turns_since_nudge = self.turns_since_nudge.saturating_add(1);
}
pub fn nudge_due(&self) -> bool {
self.interval > 0 && self.turns_since_nudge >= self.interval
}
pub fn nudge_if_due(&mut self) -> Option<&'static str> {
if !self.nudge_due() {
return None;
}
self.turns_since_nudge = 0;
Some(DEFAULT_NUDGE)
}
pub fn reset(&mut self) {
self.turns_since_nudge = 0;
}
}
impl Default for Curator {
fn default() -> Self {
Self::new(20)
}
}
const DEFAULT_NUDGE: &str =
"Take a moment to reflect on the recent conversation: are there any \
facts about the user, their project, their preferences, or how they \
like to work that would be valuable in a future session? If so, save \
them to memory using the `memory` tool. Skip if there is nothing \
worth keeping — quality over quantity.";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nudges_at_the_configured_interval() {
let mut c = Curator::new(3);
for _ in 0..2 {
c.record_user_turn();
assert!(c.nudge_if_due().is_none());
}
c.record_user_turn();
assert!(c.nudge_if_due().is_some(), "should nudge on the 3rd turn");
c.record_user_turn();
assert!(c.nudge_if_due().is_none());
}
#[test]
fn interval_zero_disables_nudging() {
let mut c = Curator::new(0);
for _ in 0..1000 {
c.record_user_turn();
}
assert!(c.nudge_if_due().is_none());
}
#[test]
fn reset_clears_counter_without_returning_nudge() {
let mut c = Curator::new(2);
c.record_user_turn();
c.record_user_turn();
assert!(c.nudge_due());
c.reset();
assert!(!c.nudge_due());
}
}