use std::sync::Arc;
use async_trait::async_trait;
use rskit_errors::{AppError, ErrorCode};
use rskit_llm::types::Message;
use super::Memory;
pub struct SlidingWindowMemory {
inner: Arc<dyn Memory>,
max_messages: usize,
}
impl SlidingWindowMemory {
pub fn new(inner: Arc<dyn Memory>, max_messages: usize) -> Result<Self, AppError> {
if max_messages == 0 {
return Err(AppError::new(
ErrorCode::InvalidInput,
"max_messages must be at least 1",
));
}
Ok(Self {
inner,
max_messages,
})
}
fn trim(&self, messages: &[Message]) -> Vec<Message> {
if messages.len() <= self.max_messages {
return messages.to_vec();
}
let mut result = Vec::with_capacity(self.max_messages + 1);
let has_system = matches!(messages.first(), Some(Message::System(_)));
if has_system {
result.push(messages[0].clone());
}
let non_system = if has_system { &messages[1..] } else { messages };
let start = non_system.len().saturating_sub(self.max_messages);
result.extend_from_slice(&non_system[start..]);
result
}
}
#[async_trait]
impl Memory for SlidingWindowMemory {
async fn load(&self, session_id: &str) -> Result<Vec<Message>, AppError> {
let messages = self.inner.load(session_id).await?;
Ok(self.trim(&messages))
}
async fn save(&self, session_id: &str, messages: &[Message]) -> Result<(), AppError> {
let trimmed = self.trim(messages);
self.inner.save(session_id, &trimmed).await
}
async fn append(&self, session_id: &str, messages: &[Message]) -> Result<(), AppError> {
self.inner.append(session_id, messages).await?;
let all = self.inner.load(session_id).await?;
let trimmed = self.trim(&all);
self.inner.save(session_id, &trimmed).await
}
async fn clear(&self, session_id: &str) -> Result<(), AppError> {
self.inner.clear(session_id).await
}
}