ceylon_core/
memory.rs

1use async_trait::async_trait;
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::HashMap;
6
7use crate::error::Result;
8
9/// Entry stored in memory
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct MemoryEntry {
12    /// Unique identifier
13    pub id: String,
14
15    /// Content to store
16    pub content: String,
17
18    /// Optional metadata for filtering
19    pub metadata: HashMap<String, Value>,
20
21    /// Optional embedding vector for semantic search
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub embedding: Option<Vec<f32>>,
24
25    /// Timestamp when created
26    pub created_at: DateTime<Utc>,
27
28    /// Optional expiration time (TTL)
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub expires_at: Option<DateTime<Utc>>,
31}
32
33impl MemoryEntry {
34    /// Create a new memory entry with generated ID
35    pub fn new(content: impl Into<String>) -> Self {
36        Self {
37            id: uuid::Uuid::new_v4().to_string(),
38            content: content.into(),
39            metadata: HashMap::new(),
40            embedding: None,
41            created_at: Utc::now(),
42            expires_at: None,
43        }
44    }
45
46    /// Create with specific ID
47    pub fn with_id(id: impl Into<String>, content: impl Into<String>) -> Self {
48        Self {
49            id: id.into(),
50            content: content.into(),
51            metadata: HashMap::new(),
52            embedding: None,
53            created_at: Utc::now(),
54            expires_at: None,
55        }
56    }
57
58    /// Add metadata
59    pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
60        self.metadata.insert(key.into(), value);
61        self
62    }
63
64    /// Set embedding vector
65    pub fn with_embedding(mut self, embedding: Vec<f32>) -> Self {
66        self.embedding = Some(embedding);
67        self
68    }
69
70    /// Set expiration time
71    pub fn with_expiration(mut self, expires_at: DateTime<Utc>) -> Self {
72        self.expires_at = Some(expires_at);
73        self
74    }
75
76    /// Set TTL in seconds from now
77    pub fn with_ttl_seconds(mut self, seconds: i64) -> Self {
78        self.expires_at = Some(Utc::now() + chrono::Duration::seconds(seconds));
79        self
80    }
81
82    /// Check if entry has expired
83    pub fn is_expired(&self) -> bool {
84        if let Some(expires_at) = self.expires_at {
85            expires_at < Utc::now()
86        } else {
87            false
88        }
89    }
90}
91
92/// Query filter for memory retrieval
93#[derive(Debug, Clone, Default)]
94pub struct MemoryQuery {
95    /// Metadata filters (exact match)
96    pub filters: HashMap<String, Value>,
97
98    /// Limit number of results
99    pub limit: Option<usize>,
100
101    /// Semantic search query (if vector backend)
102    pub semantic_query: Option<String>,
103
104    /// Similarity threshold for vector search (0.0 to 1.0)
105    pub similarity_threshold: Option<f32>,
106}
107
108impl MemoryQuery {
109    /// Create a new empty query
110    pub fn new() -> Self {
111        Self::default()
112    }
113
114    /// Add a metadata filter
115    pub fn with_filter(mut self, key: impl Into<String>, value: Value) -> Self {
116        self.filters.insert(key.into(), value);
117        self
118    }
119
120    /// Set result limit
121    pub fn with_limit(mut self, limit: usize) -> Self {
122        self.limit = Some(limit);
123        self
124    }
125
126    /// Set semantic search query
127    pub fn with_semantic_query(mut self, query: impl Into<String>) -> Self {
128        self.semantic_query = Some(query.into());
129        self
130    }
131
132    /// Set similarity threshold
133    pub fn with_threshold(mut self, threshold: f32) -> Self {
134        self.similarity_threshold = Some(threshold);
135        self
136    }
137}
138
139/// Base memory trait - all backends must implement this
140#[async_trait]
141pub trait Memory: Send + Sync {
142    /// Store an entry
143    async fn store(&self, entry: MemoryEntry) -> Result<String>;
144
145    /// Retrieve by ID
146    async fn get(&self, id: &str) -> Result<Option<MemoryEntry>>;
147
148    /// Search with filters
149    async fn search(&self, query: MemoryQuery) -> Result<Vec<MemoryEntry>>;
150
151    /// Delete an entry
152    async fn delete(&self, id: &str) -> Result<bool>;
153
154    /// Clear all entries
155    async fn clear(&self) -> Result<()>;
156
157    /// Count entries
158    async fn count(&self) -> Result<usize>;
159}
160
161/// Extended trait for vector-based semantic search
162#[async_trait]
163pub trait VectorMemory: Memory {
164    /// Generate embedding for text
165    async fn embed(&self, text: &str) -> Result<Vec<f32>>;
166
167    /// Semantic similarity search
168    /// Returns entries with similarity scores (higher = more similar)
169    async fn similarity_search(
170        &self,
171        query: &str,
172        limit: Option<usize>,
173        threshold: Option<f32>,
174    ) -> Result<Vec<(MemoryEntry, f32)>>;
175}