rswarm 0.1.8

A Rust implementation of the Swarm framework
Documentation
pub mod qdrant;
pub mod sqlite_vss;
pub mod vector;

use crate::error::{SwarmError, SwarmResult};
use async_trait::async_trait;
use std::collections::HashMap;

#[async_trait]
pub trait Memory: Send + Sync {
    async fn store(&mut self, key: &str, value: &str) -> Result<(), SwarmError>;
    async fn retrieve(&self, key: &str) -> Result<Option<String>, SwarmError>;
    async fn clear(&mut self) -> Result<(), SwarmError>;
    async fn keys(&self) -> Result<Vec<String>, SwarmError>;
}

pub struct SlidingWindowMemory {
    max_size: usize,
    storage: HashMap<String, String>,
    insertion_order: Vec<String>,
    token_estimates: HashMap<String, usize>,
}

impl SlidingWindowMemory {
    pub fn new(max_size: usize) -> SwarmResult<Self> {
        if max_size == 0 {
            return Err(SwarmError::ValidationError(
                "SlidingWindowMemory max_size must be greater than zero".to_string(),
            ));
        }
        Ok(Self {
            max_size,
            storage: HashMap::new(),
            insertion_order: Vec::new(),
            token_estimates: HashMap::new(),
        })
    }

    pub fn len(&self) -> usize {
        self.storage.len()
    }

    pub fn is_empty(&self) -> bool {
        self.storage.is_empty()
    }

    pub fn is_full(&self) -> bool {
        self.storage.len() >= self.max_size
    }

    pub fn total_tokens(&self) -> usize {
        self.token_estimates.values().sum()
    }

    fn estimate_tokens(key: &str, value: &str) -> usize {
        key.split_whitespace().count() + value.split_whitespace().count()
    }
}

#[async_trait]
impl Memory for SlidingWindowMemory {
    async fn store(&mut self, key: &str, value: &str) -> Result<(), SwarmError> {
        let token_estimate = Self::estimate_tokens(key, value);

        if self.storage.contains_key(key) {
            self.insertion_order.retain(|k| k != key);
            self.insertion_order.push(key.to_string());
            self.storage.insert(key.to_string(), value.to_string());
            self.token_estimates.insert(key.to_string(), token_estimate);
            return Ok(());
        }

        if self.storage.len() >= self.max_size {
            if let Some(evicted_key) = self.insertion_order.first().cloned() {
                self.storage.remove(&evicted_key);
                self.token_estimates.remove(&evicted_key);
                self.insertion_order.remove(0);
            }
        }

        self.storage.insert(key.to_string(), value.to_string());
        self.insertion_order.push(key.to_string());
        self.token_estimates.insert(key.to_string(), token_estimate);

        Ok(())
    }

    async fn retrieve(&self, key: &str) -> Result<Option<String>, SwarmError> {
        Ok(self.storage.get(key).cloned())
    }

    async fn clear(&mut self) -> Result<(), SwarmError> {
        self.storage.clear();
        self.insertion_order.clear();
        self.token_estimates.clear();
        Ok(())
    }

    async fn keys(&self) -> Result<Vec<String>, SwarmError> {
        Ok(self.insertion_order.clone())
    }
}