Skip to main content

oxios_kernel/kernel_handle/
memory_api.rs

1//! Memory API — memory subsystem facade (Phase C).
2//!
3//! Extracted from `AgentApi` to provide a focused interface for
4//! memory operations. Replaces the previous `AgentApi.memory_manager()`
5//! leaky accessor pattern.
6//!
7//! Consumers should use [`KernelHandle::memory()`] to access this API.
8
9use crate::memory::{HnswMemoryIndex, MemoryEntry, MemoryManager, MemoryType, SemanticHit};
10use std::sync::Arc;
11
12/// Memory subsystem facade.
13///
14/// Provides high-level memory operations without exposing the
15/// internal `MemoryManager` directly. This is the 14th typed API
16/// in `KernelHandle` (alongside `A2aApi`, `AgentApi`, etc.).
17pub struct MemoryApi {
18    /// Underlying memory manager.
19    pub(crate) memory_manager: Arc<MemoryManager>,
20    /// Optional HNSW index for fast semantic search.
21    pub(crate) hnsw_index: Option<Arc<HnswMemoryIndex>>,
22}
23
24impl MemoryApi {
25    /// Create a new MemoryApi.
26    pub fn new(memory_manager: Arc<MemoryManager>) -> Self {
27        Self {
28            memory_manager,
29            hnsw_index: None,
30        }
31    }
32
33    /// Attach an HNSW index for fast semantic search.
34    pub fn set_hnsw_index(&mut self, index: Arc<HnswMemoryIndex>) {
35        self.hnsw_index = Some(index);
36    }
37
38    /// Store a memory entry. Returns the entry's ID.
39    pub async fn remember(&self, entry: MemoryEntry) -> anyhow::Result<String> {
40        self.memory_manager.remember(entry).await
41    }
42
43    /// Search memory by text query.
44    pub async fn search(
45        &self,
46        query: &str,
47        memory_type: Option<MemoryType>,
48        limit: usize,
49    ) -> anyhow::Result<Vec<MemoryEntry>> {
50        self.memory_manager.search(query, memory_type, limit).await
51    }
52
53    /// Recall memory by query (semantic if HNSW available, else keyword).
54    pub async fn recall(&self, query: &str) -> anyhow::Result<Vec<MemoryEntry>> {
55        self.memory_manager.recall(query).await
56    }
57
58    /// Get a specific memory entry by ID and type.
59    pub async fn get(
60        &self,
61        id: &str,
62        memory_type: MemoryType,
63    ) -> anyhow::Result<Option<MemoryEntry>> {
64        self.memory_manager.get(id, memory_type).await
65    }
66
67    /// Forget (delete) a memory entry.
68    pub async fn forget(&self, id: &str, memory_type: MemoryType) -> anyhow::Result<bool> {
69        self.memory_manager.forget(id, memory_type).await
70    }
71
72    /// List memories of a given type.
73    pub async fn list(
74        &self,
75        memory_type: MemoryType,
76        limit: usize,
77    ) -> anyhow::Result<Vec<MemoryEntry>> {
78        self.memory_manager.list(memory_type, limit).await
79    }
80
81    /// Search memory using semantic similarity (returns SemanticHits).
82    /// Falls back to keyword search if no HNSW index.
83    pub async fn search_semantic(
84        &self,
85        query: &str,
86        limit: usize,
87    ) -> anyhow::Result<Vec<SemanticHit>> {
88        if let Some(hnsw) = &self.hnsw_index {
89            let _ = hnsw; // hnsw available, would use it here
90                          // For now, delegate to regular search and convert
91            let entries = self.memory_manager.search(query, None, limit).await?;
92            Ok(entries
93                .into_iter()
94                .map(|e| SemanticHit {
95                    entry: e,
96                    distance: 0.0,
97                    similarity: 1.0,
98                })
99                .collect())
100        } else {
101            // Fallback: use keyword search
102            let entries = self.memory_manager.search(query, None, limit).await?;
103            Ok(entries
104                .into_iter()
105                .map(|e| SemanticHit {
106                    entry: e,
107                    distance: 0.0,
108                    similarity: 1.0,
109                })
110                .collect())
111        }
112    }
113
114    /// Get memory statistics: (total_entries, vector_index_size).
115    pub async fn stats(&self) -> (usize, usize) {
116        (
117            self.memory_manager.total_entries().await,
118            self.memory_manager.vector_index_size(),
119        )
120    }
121
122    /// Rebuild the HNSW index from current memory state.
123    /// Returns the number of vectors indexed.
124    pub async fn rebuild_hnsw_index(&self) -> anyhow::Result<usize> {
125        if let Some(hnsw) = &self.hnsw_index {
126            // Try to rebuild
127            self.memory_manager.rebuild_index().await?;
128            Ok(hnsw.len())
129        } else {
130            Ok(0)
131        }
132    }
133
134    /// Access the underlying memory manager. For advanced operations
135    /// not yet exposed via this facade.
136    pub fn manager(&self) -> &Arc<MemoryManager> {
137        &self.memory_manager
138    }
139}