use anyhow::{Context, Result};
use rusqlite::params;
use serde::Deserialize;
use std::path::Path;
use std::sync::Arc;
use crate::config::LociConfig;
use crate::memory::types::{EntityRelation, Memory};
#[derive(Debug, Deserialize)]
struct ImportData {
memories: Vec<Memory>,
#[serde(default)]
relations: Vec<EntityRelation>,
}
pub async fn import(config: &LociConfig, file: &Path) -> Result<()> {
let json = std::fs::read_to_string(file)
.with_context(|| format!("failed to read import file: {}", file.display()))?;
let data: ImportData =
serde_json::from_str(&json).context("failed to parse import JSON")?;
let db_path = config.resolved_db_path();
let mut conn = crate::db::open_database(&db_path)?;
let provider = crate::embedding::create_provider(&config.embedding)?;
let embedding_provider: Arc<dyn crate::embedding::EmbeddingProvider> = Arc::from(provider);
let mut imported = 0u64;
let mut skipped = 0u64;
println!(
"Importing {} memories and {} relations...",
data.memories.len(),
data.relations.len()
);
for memory in &data.memories {
let exists: bool = conn.query_row(
"SELECT COUNT(*) > 0 FROM memories WHERE id = ?1",
params![memory.id],
|row| row.get(0),
)?;
if exists {
skipped += 1;
continue;
}
let ep = Arc::clone(&embedding_provider);
let content = memory.content.clone();
let embedding = tokio::task::spawn_blocking(move || ep.embed(&content)).await??;
crate::memory::store::store_memory(
&mut conn,
&memory.content,
memory.memory_type,
memory.scope,
memory.source_group.as_deref(),
memory.confidence,
memory.metadata.as_ref(),
None, &embedding,
1.0,
)?;
imported += 1;
}
let mut relations_created = 0u64;
let mut relations_skipped = 0u64;
for rel in &data.relations {
let subject_exists: bool = conn.query_row(
"SELECT COUNT(*) > 0 FROM memories WHERE id = ?1",
params![rel.subject_id],
|row| row.get(0),
)?;
let object_exists: bool = conn.query_row(
"SELECT COUNT(*) > 0 FROM memories WHERE id = ?1",
params![rel.object_id],
|row| row.get(0),
)?;
if subject_exists && object_exists {
match crate::memory::relations::store_relation(
&conn,
&rel.subject_id,
&rel.predicate,
&rel.object_id,
) {
Ok(_) => relations_created += 1,
Err(e) => {
eprintln!("Warning: failed to create relation: {e}");
relations_skipped += 1;
}
}
} else {
relations_skipped += 1;
}
}
println!("Import complete:");
println!(" Memories imported: {imported}");
println!(" Memories skipped: {skipped} (already exist)");
println!(" Relations created: {relations_created}");
if relations_skipped > 0 {
println!(" Relations skipped: {relations_skipped}");
}
Ok(())
}