use anyhow::{Context, Result};
use indicatif::{ProgressBar, ProgressStyle};
use std::sync::Arc;
use crate::config::LociConfig;
use crate::db;
use crate::embedding;
use crate::memory::embedding_to_bytes;
pub async fn re_embed(config: &LociConfig) -> Result<()> {
let db_path = config.resolved_db_path();
let conn = db::open_database(&db_path)
.context("failed to open database")?;
let provider: Arc<dyn embedding::EmbeddingProvider> =
Arc::from(embedding::create_provider(&config.embedding)
.context("failed to create embedding provider")?);
let memories: Vec<(String, String)> = {
let mut stmt = conn.prepare(
"SELECT id, content FROM memories WHERE superseded_by IS NULL"
)?;
let rows = stmt
.query_map([], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
})?
.collect::<Result<Vec<_>, _>>()?;
rows
};
let total = memories.len();
if total == 0 {
println!("No active memories to re-embed.");
return Ok(());
}
println!("Re-embedding {total} memories with model '{}'...", config.embedding.model);
let pb = ProgressBar::new(total as u64);
pb.set_style(
ProgressStyle::default_bar()
.template(" {bar:40.cyan/blue} {pos}/{len} ({eta})")
.expect("valid template")
.progress_chars("##-"),
);
const BATCH_SIZE: usize = 32;
for chunk in memories.chunks(BATCH_SIZE) {
let texts: Vec<String> = chunk.iter().map(|(_, content)| content.clone()).collect();
let provider = Arc::clone(&provider);
let embeddings = tokio::task::spawn_blocking(move || {
let text_refs: Vec<&str> = texts.iter().map(|s| s.as_str()).collect();
provider.embed_batch(&text_refs)
})
.await?
.context("embedding batch failed")?;
for ((id, _), emb) in chunk.iter().zip(embeddings.iter()) {
let bytes = embedding_to_bytes(emb);
conn.execute("DELETE FROM memories_vec WHERE id = ?1", [id])?;
conn.execute(
"INSERT INTO memories_vec (id, embedding) VALUES (?1, ?2)",
rusqlite::params![id, bytes],
)?;
}
pb.inc(chunk.len() as u64);
}
pb.finish_and_clear();
db::migrations::set_embedding_model(&conn, &config.embedding.model)?;
println!("Re-embedded {total} memories with model '{}'.", config.embedding.model);
Ok(())
}