use crate::error::SwarmResult;
use crate::memory::vector::{InMemoryVectorStore, MemoryEntry, RetrievalPolicy, VectorMemory};
use async_trait::async_trait;
use serde_json::Value;
pub struct SqliteVssMemory {
inner: InMemoryVectorStore,
#[allow(dead_code)]
db_path: Option<String>,
persistent: bool,
}
impl SqliteVssMemory {
pub fn open(db_path: impl Into<String>) -> SwarmResult<Self> {
let path = db_path.into();
#[cfg(feature = "sqlite-vec")]
{
Err(crate::error::SwarmError::ConfigError(format!(
"sqlite-vec support for '{}' is not implemented yet; \
use SqliteVssMemory::in_memory() or disable the feature",
path
)))
}
#[cfg(not(feature = "sqlite-vec"))]
{
Err(crate::error::SwarmError::ConfigError(format!(
"SqliteVssMemory::open('{}') requires the 'sqlite-vec' feature; \
use SqliteVssMemory::in_memory() for a non-persistent store",
path
)))
}
}
pub fn in_memory() -> Self {
Self {
inner: InMemoryVectorStore::new(),
db_path: None,
persistent: false,
}
}
pub fn is_persistent(&self) -> bool {
self.persistent
}
}
#[async_trait]
impl VectorMemory for SqliteVssMemory {
async fn store(
&self,
id: &str,
text: &str,
embedding: Vec<f32>,
metadata: Value,
) -> SwarmResult<()> {
self.inner.store(id, text, embedding, metadata).await
}
async fn search(
&self,
query_embedding: Vec<f32>,
policy: RetrievalPolicy,
) -> SwarmResult<Vec<MemoryEntry>> {
self.inner.search(query_embedding, policy).await
}
async fn delete(&self, id: &str) -> SwarmResult<()> {
self.inner.delete(id).await
}
async fn len(&self) -> SwarmResult<usize> {
self.inner.len().await
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn vec2(x: f32, y: f32) -> Vec<f32> {
vec![x, y]
}
#[tokio::test]
async fn test_fallback_store_and_search() {
let store = SqliteVssMemory::in_memory();
store
.store("a", "hello", vec2(1.0, 0.0), json!({}))
.await
.unwrap();
let results = store
.search(vec2(1.0, 0.0), RetrievalPolicy::default())
.await
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].id, "a");
}
#[cfg(not(feature = "sqlite-vec"))]
#[test]
fn test_open_requires_feature() {
let err = match SqliteVssMemory::open("memory.db") {
Ok(_) => panic!("open without feature must fail"),
Err(e) => e,
};
assert!(err.to_string().contains("sqlite-vec' feature"));
}
#[cfg(feature = "sqlite-vec")]
#[test]
fn test_open_returns_error_until_backend_is_implemented() {
let error = match SqliteVssMemory::open("memory.db") {
Ok(_) => panic!("feature path is not implemented"),
Err(error) => error,
};
assert!(error.to_string().contains("not implemented yet"));
}
}