Skip to main content

symbi_runtime/context/
mod.rs

1//! Context management module for agent memory and knowledge systems
2//!
3//! This module provides the core infrastructure for managing agent contexts,
4//! including hierarchical memory systems, knowledge bases, and persistent
5//! context storage with session continuity.
6//!
7//! # Architecture
8//!
9//! The context system is built around several key components:
10//!
11//! - **AgentContext**: The main container for an agent's memory and knowledge
12//! - **HierarchicalMemory**: Multi-layered memory system (short-term, long-term, working, episodic, semantic)
13//! - **KnowledgeBase**: Structured storage for facts, procedures, and learned patterns
14//! - **ContextManager**: Trait and implementation for context operations
15//!
16//! # Usage
17//!
18//! ```rust,no_run
19//! use symbi_runtime::context::{StandardContextManager, ContextManagerConfig};
20//! use symbi_runtime::types::AgentId;
21//!
22//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
23//! let config = ContextManagerConfig::default();
24//! let context_manager = StandardContextManager::new(config, "my-agent").await?;
25//! context_manager.initialize().await?;
26//!
27//! let agent_id = AgentId::new();
28//! let session_id = context_manager.create_session(agent_id).await?;
29//! # Ok(())
30//! # }
31//! ```
32//!
33//! # Features
34//!
35//! - **Hierarchical Memory**: Multiple memory types with different retention policies
36//! - **Knowledge Management**: Structured storage and retrieval of facts, procedures, and patterns
37//! - **Semantic Search**: Vector-based similarity search across memory and knowledge
38//! - **Session Management**: Persistent context across agent sessions
39//! - **Knowledge Sharing**: Secure sharing of knowledge between agents
40//! - **Retention Policies**: Automatic archiving and cleanup of old context data
41//! - **Access Control**: Policy-driven access control for context operations
42
43pub mod compaction;
44pub mod embedding;
45pub mod manager;
46pub mod markdown_memory;
47pub mod token_counter;
48pub mod types;
49pub mod vector_db;
50pub mod vector_db_factory;
51#[cfg(feature = "vector-lancedb")]
52pub mod vector_db_lance;
53pub mod vector_db_trait;
54
55// Re-export commonly used types and traits
56pub use types::{
57    AccessLevel, AgentContext, ContextError, ContextId, ContextPersistence, ContextQuery,
58    FilePersistenceConfig, HierarchicalMemory, Knowledge, KnowledgeBase, KnowledgeId,
59    KnowledgeItem, KnowledgeSource, KnowledgeType, MemoryItem, MemoryType, QueryType,
60    RetentionPolicy, SessionId, StorageStats, VectorBatchItem, VectorBatchOperation,
61    VectorContentType, VectorId, VectorMetadata, VectorOperationType, VectorSearchResult,
62};
63
64pub use manager::{ContextManager, ContextManagerConfig, FilePersistence, StandardContextManager};
65
66pub use markdown_memory::MarkdownMemoryStore;
67
68pub use embedding::{
69    create_embedding_service, create_embedding_service_from_env, EmbeddingConfig,
70    EmbeddingProvider, OllamaEmbeddingService, OpenAiEmbeddingService,
71};
72
73pub use vector_db::{
74    EmbeddingService, MockEmbeddingService, NoOpVectorDatabase, QdrantConfig, QdrantDistance,
75    TfIdfEmbeddingService, VectorDatabase, VectorDatabaseStats,
76};
77
78#[cfg(feature = "vector-qdrant")]
79pub use vector_db::QdrantClientWrapper;
80
81pub use compaction::{CompactionConfig, CompactionResult, CompactionTier};
82pub use token_counter::{
83    context_limit_for_model, create_token_counter, HeuristicTokenCounter, TiktokenCounter,
84    TokenCounter,
85};
86pub use vector_db_factory::{create_vector_backend, resolve_vector_config, VectorBackendConfig};
87#[cfg(feature = "vector-lancedb")]
88pub use vector_db_lance::{LanceDbBackend, LanceDbConfig};
89pub use vector_db_trait::{DistanceMetric, VectorDb};
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use crate::types::AgentId;
95    use std::time::SystemTime;
96
97    /// Create a test config with an isolated temp directory to avoid
98    /// cross-test interference from shared `~/.symbiont/data`.
99    fn test_config() -> (ContextManagerConfig, tempfile::TempDir) {
100        let tmp = tempfile::tempdir().unwrap();
101        let mut config = ContextManagerConfig::default();
102        config.persistence_config.root_data_dir = tmp.path().to_path_buf();
103        (config, tmp)
104    }
105
106    #[tokio::test]
107    async fn test_context_manager_creation() {
108        let (config, _tmp) = test_config();
109        let agent_id = AgentId::new();
110        let manager = StandardContextManager::new(config, &agent_id.to_string())
111            .await
112            .unwrap();
113        assert!(manager.initialize().await.is_ok());
114    }
115
116    #[tokio::test]
117    async fn test_session_creation() {
118        let (config, _tmp) = test_config();
119        let agent_id = AgentId::new();
120        let manager = StandardContextManager::new(config, &agent_id.to_string())
121            .await
122            .unwrap();
123        manager.initialize().await.unwrap();
124
125        let session_id = manager.create_session(agent_id).await.unwrap();
126
127        // Verify session was created
128        let context = manager
129            .retrieve_context(agent_id, Some(session_id))
130            .await
131            .unwrap();
132        assert!(context.is_some());
133        assert_eq!(context.unwrap().session_id, session_id);
134    }
135
136    #[tokio::test]
137    async fn test_memory_operations() {
138        let (config, _tmp) = test_config();
139        let agent_id = AgentId::new();
140        let manager = StandardContextManager::new(config, &agent_id.to_string())
141            .await
142            .unwrap();
143        manager.initialize().await.unwrap();
144
145        let _session_id = manager.create_session(agent_id).await.unwrap();
146
147        // Test memory updates
148        let memory_updates = vec![types::MemoryUpdate {
149            target: types::MemoryTarget::Working("test_key".to_string()),
150            operation: types::UpdateOperation::Add,
151            data: serde_json::Value::String("test_value".to_string()),
152        }];
153
154        assert!(manager
155            .update_memory(agent_id, memory_updates)
156            .await
157            .is_ok());
158    }
159
160    #[tokio::test]
161    async fn test_knowledge_operations() {
162        let (config, _tmp) = test_config();
163        let agent_id = AgentId::new();
164        let manager = StandardContextManager::new(config, &agent_id.to_string())
165            .await
166            .unwrap();
167        manager.initialize().await.unwrap();
168
169        let _session_id = manager.create_session(agent_id).await.unwrap();
170
171        // Test adding knowledge
172        let fact = types::KnowledgeFact {
173            id: KnowledgeId::new(),
174            subject: "test".to_string(),
175            predicate: "is".to_string(),
176            object: "example".to_string(),
177            confidence: 0.9,
178            source: types::KnowledgeSource::UserProvided,
179            created_at: SystemTime::now(),
180            verified: true,
181        };
182
183        let _knowledge_id = manager
184            .add_knowledge(agent_id, Knowledge::Fact(fact))
185            .await
186            .unwrap();
187
188        // Test searching knowledge
189        let results = manager
190            .search_knowledge(agent_id, "test", 10)
191            .await
192            .unwrap();
193        assert!(!results.is_empty());
194    }
195
196    #[tokio::test]
197    async fn test_context_query() {
198        let (config, _tmp) = test_config();
199        let agent_id = AgentId::new();
200        let manager = StandardContextManager::new(config, &agent_id.to_string())
201            .await
202            .unwrap();
203        manager.initialize().await.unwrap();
204
205        let _session_id = manager.create_session(agent_id).await.unwrap();
206
207        // Test context query
208        let query = ContextQuery {
209            query_type: QueryType::Semantic,
210            search_terms: vec!["test".to_string()],
211            max_results: 10,
212            time_range: None,
213            memory_types: vec![],
214            relevance_threshold: 0.7,
215            include_embeddings: false,
216        };
217
218        let results = manager.query_context(agent_id, query).await.unwrap();
219        // Results may be empty for a new context, but the operation should succeed
220        assert!(results.len() <= 10);
221    }
222
223    #[tokio::test]
224    async fn test_context_stats() {
225        let (config, _tmp) = test_config();
226        let agent_id = AgentId::new();
227        let manager = StandardContextManager::new(config, &agent_id.to_string())
228            .await
229            .unwrap();
230        manager.initialize().await.unwrap();
231
232        let _session_id = manager.create_session(agent_id).await.unwrap();
233
234        let stats = manager.get_context_stats(agent_id).await.unwrap();
235        assert_eq!(stats.total_memory_items, 0);
236        assert_eq!(stats.total_knowledge_items, 0);
237        assert_eq!(stats.total_conversations, 0);
238    }
239
240    #[tokio::test]
241    async fn test_secrets_integration() {
242        let (config, _tmp) = test_config();
243        let agent_id = AgentId::new();
244        let manager = StandardContextManager::new(config, &agent_id.to_string())
245            .await
246            .unwrap();
247
248        // Test that secrets accessor is available
249        let _secrets = manager.secrets();
250        // This test just verifies the secrets integration compiles and is accessible
251    }
252}