1use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::collections::HashMap;
7use std::path::PathBuf;
8use std::time::{Duration, SystemTime};
9use uuid::Uuid;
10
11use crate::types::AgentId;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct SessionId(pub Uuid);
16
17impl SessionId {
18 pub fn new() -> Self {
19 Self(Uuid::new_v4())
20 }
21}
22
23impl std::fmt::Display for SessionId {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "{}", self.0)
26 }
27}
28
29impl Default for SessionId {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37pub struct ContextId(pub Uuid);
38
39impl ContextId {
40 pub fn new() -> Self {
41 Self(Uuid::new_v4())
42 }
43}
44
45impl std::fmt::Display for ContextId {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 write!(f, "{}", self.0)
48 }
49}
50
51impl Default for ContextId {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
59pub struct KnowledgeId(pub Uuid);
60
61impl KnowledgeId {
62 pub fn new() -> Self {
63 Self(Uuid::new_v4())
64 }
65}
66
67impl std::fmt::Display for KnowledgeId {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "{}", self.0)
70 }
71}
72
73impl Default for KnowledgeId {
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
81pub struct VectorId(pub Uuid);
82
83impl VectorId {
84 pub fn new() -> Self {
85 Self(Uuid::new_v4())
86 }
87}
88
89impl Default for VectorId {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl std::fmt::Display for VectorId {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 write!(f, "{}", self.0)
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct AgentContext {
104 pub agent_id: AgentId,
105 pub session_id: SessionId,
106 pub memory: HierarchicalMemory,
107 pub knowledge_base: KnowledgeBase,
108 pub conversation_history: Vec<ConversationItem>,
109 pub metadata: HashMap<String, String>,
110 pub created_at: SystemTime,
111 pub updated_at: SystemTime,
112 pub retention_policy: RetentionPolicy,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, Default)]
117pub struct HierarchicalMemory {
118 pub working_memory: WorkingMemory,
119 pub short_term: Vec<MemoryItem>,
120 pub long_term: Vec<MemoryItem>,
121 pub episodic_memory: Vec<Episode>,
122 pub semantic_memory: Vec<SemanticMemoryItem>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, Default)]
127pub struct WorkingMemory {
128 pub variables: HashMap<String, Value>,
129 pub active_goals: Vec<String>,
130 pub current_context: Option<String>,
131 pub attention_focus: Vec<String>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct MemoryItem {
137 pub id: ContextId,
138 pub content: String,
139 pub memory_type: MemoryType,
140 pub importance: f32,
141 pub access_count: u32,
142 pub last_accessed: SystemTime,
143 pub created_at: SystemTime,
144 pub embedding: Option<Vec<f32>>,
145 pub metadata: HashMap<String, String>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
150pub enum MemoryType {
151 Factual,
152 Procedural,
153 Episodic,
154 Semantic,
155 Working,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct SemanticMemoryItem {
161 pub id: ContextId,
162 pub concept: String,
163 pub relationships: Vec<ConceptRelationship>,
164 pub properties: HashMap<String, Value>,
165 pub confidence: f32,
166 pub created_at: SystemTime,
167 pub updated_at: SystemTime,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct ConceptRelationship {
173 pub relation_type: RelationType,
174 pub target_concept: String,
175 pub strength: f32,
176 pub bidirectional: bool,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181pub enum RelationType {
182 IsA,
183 PartOf,
184 RelatedTo,
185 Causes,
186 Enables,
187 Requires,
188 Similar,
189 Opposite,
190 Custom(String),
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct Episode {
196 pub id: ContextId,
197 pub title: String,
198 pub description: String,
199 pub events: Vec<EpisodeEvent>,
200 pub outcome: Option<String>,
201 pub lessons_learned: Vec<String>,
202 pub timestamp: SystemTime,
203 pub importance: f32,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct EpisodeEvent {
209 pub action: String,
210 pub result: String,
211 pub timestamp: SystemTime,
212 pub context: HashMap<String, String>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, Default)]
217pub struct KnowledgeBase {
218 pub facts: Vec<KnowledgeFact>,
219 pub procedures: Vec<Procedure>,
220 pub learned_patterns: Vec<Pattern>,
221 pub shared_knowledge: Vec<SharedKnowledgeRef>,
222 pub domain_expertise: HashMap<String, ExpertiseLevel>,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct KnowledgeFact {
228 pub id: KnowledgeId,
229 pub subject: String,
230 pub predicate: String,
231 pub object: String,
232 pub confidence: f32,
233 pub source: KnowledgeSource,
234 pub created_at: SystemTime,
235 pub verified: bool,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct Procedure {
241 pub id: KnowledgeId,
242 pub name: String,
243 pub description: String,
244 pub steps: Vec<ProcedureStep>,
245 pub preconditions: Vec<String>,
246 pub postconditions: Vec<String>,
247 pub success_rate: f32,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct ProcedureStep {
253 pub order: u32,
254 pub action: String,
255 pub expected_result: String,
256 pub error_handling: Option<String>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct Pattern {
262 pub id: KnowledgeId,
263 pub name: String,
264 pub description: String,
265 pub conditions: Vec<String>,
266 pub outcomes: Vec<String>,
267 pub confidence: f32,
268 pub occurrences: u32,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct SharedKnowledgeRef {
274 pub knowledge_id: KnowledgeId,
275 pub source_agent: AgentId,
276 pub shared_at: SystemTime,
277 pub access_level: AccessLevel,
278 pub trust_score: f32,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub enum KnowledgeSource {
284 Experience,
285 Learning,
286 SharedFromAgent(AgentId),
287 ExternalDocument(String),
288 UserProvided,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
293pub enum ExpertiseLevel {
294 Novice,
295 Intermediate,
296 Advanced,
297 Expert,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
302pub enum AccessLevel {
303 Public,
304 Restricted,
305 Confidential,
306 Secret,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct ConversationItem {
312 pub id: ContextId,
313 pub role: ConversationRole,
314 pub content: String,
315 pub timestamp: SystemTime,
316 pub context_used: Vec<ContextId>,
317 pub knowledge_used: Vec<KnowledgeId>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
322pub enum ConversationRole {
323 User,
324 Agent,
325 System,
326 Tool,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct RetentionPolicy {
332 pub session_retention: Duration,
333 pub memory_retention: Duration,
334 pub knowledge_retention: Duration,
335 pub auto_archive: bool,
336 pub encryption_required: bool,
337}
338
339impl Default for RetentionPolicy {
340 fn default() -> Self {
341 Self {
342 session_retention: Duration::from_secs(86400), memory_retention: Duration::from_secs(604800), knowledge_retention: Duration::from_secs(2592000), auto_archive: true,
346 encryption_required: true,
347 }
348 }
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct ContextQuery {
354 pub query_type: QueryType,
355 pub search_terms: Vec<String>,
356 pub time_range: Option<TimeRange>,
357 pub memory_types: Vec<MemoryType>,
358 pub relevance_threshold: f32,
359 pub max_results: usize,
360 pub include_embeddings: bool,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
365pub enum QueryType {
366 Semantic,
367 Keyword,
368 Temporal,
369 Similarity,
370 Hybrid,
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct TimeRange {
376 pub start: SystemTime,
377 pub end: SystemTime,
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct ContextItem {
383 pub id: ContextId,
384 pub content: String,
385 pub item_type: ContextItemType,
386 pub relevance_score: f32,
387 pub timestamp: SystemTime,
388 pub metadata: HashMap<String, String>,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
393pub enum ContextItemType {
394 Memory(MemoryType),
395 Knowledge(KnowledgeType),
396 Conversation,
397 Episode,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize)]
402pub enum KnowledgeType {
403 Fact,
404 Procedure,
405 Pattern,
406 Shared,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
411pub struct MemoryUpdate {
412 pub operation: UpdateOperation,
413 pub target: MemoryTarget,
414 pub data: Value,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
419pub enum UpdateOperation {
420 Add,
421 Update,
422 Delete,
423 Increment,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
428pub enum MemoryTarget {
429 ShortTerm(ContextId),
430 LongTerm(ContextId),
431 Working(String),
432 Episodic(ContextId),
433 Semantic(ContextId),
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
438pub struct KnowledgeItem {
439 pub id: KnowledgeId,
440 pub content: String,
441 pub knowledge_type: KnowledgeType,
442 pub confidence: f32,
443 pub relevance_score: f32,
444 pub source: KnowledgeSource,
445 pub created_at: SystemTime,
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize)]
450pub enum Knowledge {
451 Fact(KnowledgeFact),
452 Procedure(Procedure),
453 Pattern(Pattern),
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct ContextStats {
459 pub total_memory_items: usize,
460 pub total_knowledge_items: usize,
461 pub total_conversations: usize,
462 pub total_episodes: usize,
463 pub memory_size_bytes: usize,
464 pub last_activity: SystemTime,
465 pub retention_status: RetentionStatus,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct RetentionStatus {
471 pub items_to_archive: usize,
472 pub items_to_delete: usize,
473 pub next_cleanup: SystemTime,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct VectorSearchResult {
479 pub id: VectorId,
480 pub content: String,
481 pub score: f32,
482 pub metadata: HashMap<String, String>,
483 pub embedding: Option<Vec<f32>>,
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize)]
488pub struct VectorMetadata {
489 pub agent_id: AgentId,
490 pub content_type: VectorContentType,
491 pub source_id: String,
492 pub created_at: SystemTime,
493 pub updated_at: SystemTime,
494 pub tags: Vec<String>,
495 pub custom_fields: HashMap<String, String>,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
500pub enum VectorContentType {
501 Memory(MemoryType),
502 Knowledge(KnowledgeType),
503 Conversation,
504 Document,
505 Custom(String),
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct VectorBatchOperation {
511 pub operation_type: VectorOperationType,
512 pub items: Vec<VectorBatchItem>,
513}
514
515#[derive(Debug, Clone, Serialize, Deserialize)]
517pub enum VectorOperationType {
518 Insert,
519 Update,
520 Delete,
521 Search,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct VectorBatchItem {
527 pub id: Option<VectorId>,
528 pub content: String,
529 pub embedding: Option<Vec<f32>>,
530 pub metadata: VectorMetadata,
531}
532
533#[derive(Debug, Clone, Serialize, Deserialize)]
535pub struct VectorDatabaseConfig {
536 pub provider: VectorDatabaseProvider,
537 pub connection_string: String,
538 pub collection_name: String,
539 pub vector_dimension: usize,
540 pub distance_metric: String,
541 pub batch_size: usize,
542 pub max_connections: usize,
543 pub timeout_seconds: u64,
544}
545
546#[derive(Debug, Clone, Serialize, Deserialize)]
548pub enum VectorDatabaseProvider {
549 Qdrant,
550 LanceDb,
551 ChromaDB,
552 Pinecone,
553 Weaviate,
554}
555
556#[derive(Debug, Clone, thiserror::Error)]
558pub enum ContextError {
559 #[error("Context not found: {id}")]
560 NotFound { id: ContextId },
561
562 #[error("Knowledge not found: {id}")]
563 KnowledgeNotFound { id: KnowledgeId },
564
565 #[error("Session not found: {id}")]
566 SessionNotFound { id: SessionId },
567
568 #[error("Storage error: {reason}")]
569 StorageError { reason: String },
570
571 #[error("Serialization error: {reason}")]
572 SerializationError { reason: String },
573
574 #[error("Query error: {reason}")]
575 QueryError { reason: String },
576
577 #[error("Policy violation: {reason}")]
578 PolicyViolation { reason: String },
579
580 #[error("Access denied: {reason}")]
581 AccessDenied { reason: String },
582
583 #[error("Invalid operation: {reason}")]
584 InvalidOperation { reason: String },
585
586 #[error("System error: {reason}")]
587 SystemError { reason: String },
588
589 #[error("Vector database error: {reason}")]
590 VectorDatabaseError { reason: String },
591
592 #[error("Embedding generation error: {reason}")]
593 EmbeddingError { reason: String },
594
595 #[error("Batch operation error: {reason}")]
596 BatchOperationError { reason: String },
597
598 #[error("Vector not found: {id}")]
599 VectorNotFound { id: VectorId },
600}
601
602impl Default for ContextQuery {
603 fn default() -> Self {
604 Self {
605 query_type: QueryType::Semantic,
606 search_terms: Vec::new(),
607 time_range: None,
608 memory_types: Vec::new(),
609 relevance_threshold: 0.7,
610 max_results: 10,
611 include_embeddings: false,
612 }
613 }
614}
615
616#[async_trait]
618pub trait ContextPersistence: Send + Sync {
619 async fn save_context(
621 &self,
622 agent_id: AgentId,
623 context: &AgentContext,
624 ) -> Result<(), ContextError>;
625
626 async fn load_context(&self, agent_id: AgentId) -> Result<Option<AgentContext>, ContextError>;
628
629 async fn delete_context(&self, agent_id: AgentId) -> Result<(), ContextError>;
631
632 async fn list_agent_contexts(&self) -> Result<Vec<AgentId>, ContextError>;
634
635 async fn context_exists(&self, agent_id: AgentId) -> Result<bool, ContextError>;
637
638 async fn get_storage_stats(&self) -> Result<StorageStats, ContextError>;
640
641 fn as_any(&self) -> &dyn std::any::Any;
643}
644
645#[derive(Debug, Clone, Serialize, Deserialize)]
647pub struct StorageStats {
648 pub total_contexts: usize,
649 pub total_size_bytes: u64,
650 pub last_cleanup: SystemTime,
651 pub storage_path: PathBuf,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct FilePersistenceConfig {
657 pub root_data_dir: PathBuf,
659
660 pub state_dir: PathBuf,
662 pub logs_dir: PathBuf,
663 pub prompts_dir: PathBuf,
664 pub vector_db_dir: PathBuf,
665
666 pub enable_compression: bool,
668 pub enable_encryption: bool,
669 pub backup_count: usize,
670 pub auto_save_interval: u64,
671
672 pub auto_create_dirs: bool,
674 pub dir_permissions: Option<u32>,
675}
676
677impl Default for FilePersistenceConfig {
678 fn default() -> Self {
679 let mut root_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
680 root_dir.push(".symbiont");
681 root_dir.push("data");
682
683 Self {
684 root_data_dir: root_dir,
685 state_dir: PathBuf::from("state"),
686 logs_dir: PathBuf::from("logs"),
687 prompts_dir: PathBuf::from("prompts"),
688 vector_db_dir: PathBuf::from("vector_db"),
689 enable_compression: true,
690 enable_encryption: false,
691 backup_count: 3,
692 auto_save_interval: 300,
693 auto_create_dirs: true,
694 dir_permissions: Some(0o755),
695 }
696 }
697}
698
699#[derive(Debug, Clone, thiserror::Error)]
701pub enum MigrationError {
702 #[error("IO error during migration: {reason}")]
703 IOError { reason: String },
704
705 #[error("Context error during migration: {error}")]
706 ContextError { error: ContextError },
707
708 #[error("Migration validation failed: {reason}")]
709 ValidationError { reason: String },
710}
711
712impl From<std::io::Error> for MigrationError {
713 fn from(error: std::io::Error) -> Self {
714 MigrationError::IOError {
715 reason: error.to_string(),
716 }
717 }
718}
719
720impl From<ContextError> for MigrationError {
721 fn from(error: ContextError) -> Self {
722 MigrationError::ContextError { error }
723 }
724}
725
726impl FilePersistenceConfig {
727 pub fn state_path(&self) -> PathBuf {
729 self.root_data_dir.join(&self.state_dir)
730 }
731
732 pub fn logs_path(&self) -> PathBuf {
734 self.root_data_dir.join(&self.logs_dir)
735 }
736
737 pub fn prompts_path(&self) -> PathBuf {
739 self.root_data_dir.join(&self.prompts_dir)
740 }
741
742 pub fn vector_db_path(&self) -> PathBuf {
744 self.root_data_dir.join(&self.vector_db_dir)
745 }
746
747 pub fn agent_contexts_path(&self) -> PathBuf {
749 self.state_path().join("agents")
750 }
751
752 pub fn sessions_path(&self) -> PathBuf {
754 self.state_path().join("sessions")
755 }
756
757 pub async fn ensure_directories(&self) -> Result<(), std::io::Error> {
759 if self.auto_create_dirs {
760 tokio::fs::create_dir_all(self.state_path()).await?;
761 tokio::fs::create_dir_all(self.logs_path()).await?;
762 tokio::fs::create_dir_all(self.prompts_path()).await?;
763 tokio::fs::create_dir_all(self.vector_db_path()).await?;
764
765 tokio::fs::create_dir_all(self.agent_contexts_path()).await?;
767 tokio::fs::create_dir_all(self.sessions_path()).await?;
768 tokio::fs::create_dir_all(self.logs_path().join("system")).await?;
769 tokio::fs::create_dir_all(self.logs_path().join("agents")).await?;
770 tokio::fs::create_dir_all(self.logs_path().join("audit")).await?;
771 tokio::fs::create_dir_all(self.prompts_path().join("templates")).await?;
772 tokio::fs::create_dir_all(self.prompts_path().join("history")).await?;
773 tokio::fs::create_dir_all(self.prompts_path().join("cache")).await?;
774 tokio::fs::create_dir_all(self.vector_db_path().join("collections")).await?;
775 tokio::fs::create_dir_all(self.vector_db_path().join("indexes")).await?;
776 tokio::fs::create_dir_all(self.vector_db_path().join("metadata")).await?;
777 }
778 Ok(())
779 }
780
781 pub async fn migrate_from_legacy(legacy_path: PathBuf) -> Result<Self, MigrationError> {
783 let config = Self::default();
784
785 if legacy_path.exists() {
787 let agents_path = config.agent_contexts_path();
788 config.ensure_directories().await?;
789
790 let mut entries = tokio::fs::read_dir(&legacy_path).await?;
792 while let Some(entry) = entries.next_entry().await? {
793 let file_path = entry.path();
794 if file_path
795 .extension()
796 .is_some_and(|ext| ext == "json" || ext == "gz")
797 {
798 let dest_path = agents_path.join(entry.file_name());
799 tokio::fs::copy(&file_path, &dest_path).await?;
800 }
801 }
802 }
803
804 Ok(config)
805 }
806
807 pub fn is_legacy(&self) -> bool {
809 false
813 }
814}