use super::{DistanceMetric, VelesCollection, VelesDatabase, VelesError, VelesPoint};
#[derive(Debug, Clone, uniffi::Record)]
pub struct SemanticResult {
pub id: u64,
pub score: f32,
pub content: String,
}
#[derive(uniffi::Object)]
pub struct VelesSemanticMemory {
collection: std::sync::Arc<VelesCollection>,
}
impl VelesSemanticMemory {
fn content_from_payload(payload: Option<&String>) -> String {
payload
.and_then(|p| serde_json::from_str::<serde_json::Value>(p).ok())
.as_ref()
.and_then(|v| v.get("content"))
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_string()
}
}
#[uniffi::export]
impl VelesSemanticMemory {
#[uniffi::constructor]
pub fn new(db: &VelesDatabase, dimension: u32) -> Result<Self, VelesError> {
let collection_name = "_semantic_memory";
let collection = match db.get_collection(collection_name.to_string())? {
Some(coll) => coll,
None => {
db.create_collection(
collection_name.to_string(),
dimension,
DistanceMetric::Cosine,
)?;
db.get_collection(collection_name.to_string())?
.ok_or(VelesError::database(
"Failed to retrieve collection after creation".to_string(),
))?
}
};
Ok(Self { collection })
}
pub fn store(&self, id: u64, content: String, embedding: Vec<f32>) -> Result<(), VelesError> {
let payload = serde_json::to_string(&serde_json::json!({ "content": content }))
.map_err(|e| VelesError::database(format!("Failed to encode content payload: {e}")))?;
let point = VelesPoint {
id,
vector: embedding,
payload: Some(payload),
};
self.collection.upsert(point)?;
Ok(())
}
pub fn query(
&self,
embedding: Vec<f32>,
top_k: u32,
) -> Result<Vec<SemanticResult>, VelesError> {
let results = self.collection.search(embedding, top_k)?;
let ids: Vec<u64> = results.iter().map(|r| r.id).collect();
let contents: std::collections::HashMap<u64, String> = self
.collection
.get(ids)
.into_iter()
.map(|p| (p.id, Self::content_from_payload(p.payload.as_ref())))
.collect();
Ok(results
.into_iter()
.map(|r| SemanticResult {
id: r.id,
score: r.score,
content: contents.get(&r.id).cloned().unwrap_or_default(),
})
.collect())
}
pub fn len(&self) -> Result<u64, VelesError> {
Ok(self.collection.count())
}
pub fn is_empty(&self) -> Result<bool, VelesError> {
Ok(self.len()? == 0)
}
pub fn delete(&self, id: u64) -> Result<(), VelesError> {
self.collection.delete(id)
}
pub fn remove(&self, id: u64) -> Result<(), VelesError> {
self.delete(id)
}
pub fn clear(&self) -> Result<(), VelesError> {
for id in self.collection.all_ids() {
let _ = self.collection.delete(id);
}
Ok(())
}
pub fn dimension(&self) -> u32 {
self.collection.dimension()
}
}
#[cfg(test)]
#[path = "agent_tests.rs"]
mod tests;