1use super::{DistanceMetric, VelesCollection, VelesDatabase, VelesError, VelesPoint};
6
7#[derive(Debug, Clone, uniffi::Record)]
9pub struct SemanticResult {
10 pub id: u64,
12 pub score: f32,
14 pub content: String,
16}
17
18#[derive(uniffi::Object)]
33pub struct VelesSemanticMemory {
34 collection: std::sync::Arc<VelesCollection>,
35}
36
37impl VelesSemanticMemory {
38 fn content_from_payload(payload: Option<&String>) -> String {
40 payload
41 .and_then(|p| serde_json::from_str::<serde_json::Value>(p).ok())
42 .as_ref()
43 .and_then(|v| v.get("content"))
44 .and_then(serde_json::Value::as_str)
45 .unwrap_or_default()
46 .to_string()
47 }
48}
49
50#[uniffi::export]
51impl VelesSemanticMemory {
52 #[uniffi::constructor]
54 pub fn new(db: &VelesDatabase, dimension: u32) -> Result<Self, VelesError> {
55 let collection_name = "_semantic_memory";
56
57 let collection = match db.get_collection(collection_name.to_string())? {
59 Some(coll) => coll,
60 None => {
61 db.create_collection(
62 collection_name.to_string(),
63 dimension,
64 DistanceMetric::Cosine,
65 )?;
66 db.get_collection(collection_name.to_string())?
67 .ok_or(VelesError::database(
68 "Failed to retrieve collection after creation".to_string(),
69 ))?
70 }
71 };
72
73 Ok(Self { collection })
74 }
75
76 pub fn store(&self, id: u64, content: String, embedding: Vec<f32>) -> Result<(), VelesError> {
81 let payload = serde_json::to_string(&serde_json::json!({ "content": content }))
82 .map_err(|e| VelesError::database(format!("Failed to encode content payload: {e}")))?;
83 let point = VelesPoint {
84 id,
85 vector: embedding,
86 payload: Some(payload),
87 };
88 self.collection.upsert(point)?;
89 Ok(())
90 }
91
92 pub fn query(
96 &self,
97 embedding: Vec<f32>,
98 top_k: u32,
99 ) -> Result<Vec<SemanticResult>, VelesError> {
100 let results = self.collection.search(embedding, top_k)?;
101
102 let ids: Vec<u64> = results.iter().map(|r| r.id).collect();
103 let contents: std::collections::HashMap<u64, String> = self
104 .collection
105 .get(ids)
106 .into_iter()
107 .map(|p| (p.id, Self::content_from_payload(p.payload.as_ref())))
108 .collect();
109
110 Ok(results
111 .into_iter()
112 .map(|r| SemanticResult {
113 id: r.id,
114 score: r.score,
115 content: contents.get(&r.id).cloned().unwrap_or_default(),
116 })
117 .collect())
118 }
119
120 pub fn len(&self) -> Result<u64, VelesError> {
122 Ok(self.collection.count())
123 }
124
125 pub fn is_empty(&self) -> Result<bool, VelesError> {
127 Ok(self.len()? == 0)
128 }
129
130 pub fn delete(&self, id: u64) -> Result<(), VelesError> {
132 self.collection.delete(id)
133 }
134
135 pub fn remove(&self, id: u64) -> Result<(), VelesError> {
140 self.delete(id)
141 }
142
143 pub fn clear(&self) -> Result<(), VelesError> {
148 for id in self.collection.all_ids() {
149 let _ = self.collection.delete(id);
150 }
151 Ok(())
152 }
153
154 pub fn dimension(&self) -> u32 {
156 self.collection.dimension()
157 }
158}
159
160#[cfg(test)]
161#[path = "agent_tests.rs"]
162mod tests;