//! Memory embedding tests (EM-07 ~ EM-08) //! //! Validates memory retrieval with embedding enhancement and configuration hot-update. use std::sync::Arc; use async_trait::async_trait; use zclaw_growth::{ EmbeddingClient, MemoryEntry, MemoryRetriever, MemoryType, SqliteStorage, VikingAdapter, }; use zclaw_types::AgentId; /// Mock embedding client that returns deterministic 128-dim vectors. struct MockEmbeddingClient { dim: usize, } impl MockEmbeddingClient { fn new() -> Self { Self { dim: 128 } } } #[async_trait::async_trait] impl EmbeddingClient for MockEmbeddingClient { async fn embed(&self, text: &str) -> Result, String> { let mut vec = vec![0.0f32; self.dim]; for (i, b) in text.as_bytes().iter().enumerate() { vec[i % self.dim] += (*b as f32) / 255.0; } let norm: f32 = vec.iter().map(|v| v * v).sum::().sqrt().max(1e-8); for v in vec.iter_mut() { *v /= norm; } Ok(vec) } fn is_available(&self) -> bool { true } } /// EM-07: Memory retrieval with embedding enhancement. #[tokio::test] async fn em07_memory_retrieval_embedding_enhanced() { let storage = Arc::new(SqliteStorage::in_memory().await); let adapter = Arc::new(VikingAdapter::new(storage)); let agent_id = AgentId::new(); // Store 20 mixed Chinese/English memories let entries = vec![ ("pref-theme", MemoryType::Preference, "用户偏好深色模式"), ("pref-language", MemoryType::Preference, "用户使用中文沟通"), ("know-rust", MemoryType::Knowledge, "Rust async programming with tokio"), ("know-python", MemoryType::Knowledge, "Python data science with pandas"), ("exp-report", MemoryType::Experience, "月度报表生成经验:使用Excel宏自动化"), ("know-react", MemoryType::Knowledge, "React hooks patterns"), ("pref-editor", MemoryType::Preference, "偏好 VS Code 编辑器"), ("exp-schedule", MemoryType::Experience, "排班冲突解决方案:协商调换"), ("know-sql", MemoryType::Knowledge, "SQL query optimization techniques"), ("exp-deploy", MemoryType::Experience, "部署失败经验:端口冲突检测"), ("know-docker", MemoryType::Knowledge, "Docker container networking"), ("pref-font", MemoryType::Preference, "字体大小偏好 14px"), ("know-tokio", MemoryType::Knowledge, "Tokio runtime configuration"), ("exp-review", MemoryType::Experience, "代码审查经验:关注错误处理"), ("know-git", MemoryType::Knowledge, "Git rebase vs merge strategies"), ("exp-perf", MemoryType::Experience, "性能优化经验:数据库索引"), ("pref-timezone", MemoryType::Preference, "时区 UTC+8"), ("know-linux", MemoryType::Knowledge, "Linux system administration basics"), ("exp-test", MemoryType::Experience, "测试经验:TDD方法论实践"), ("know-api", MemoryType::Knowledge, "RESTful API design principles"), ]; for (key, mtype, content) in &entries { let entry = MemoryEntry::new( &agent_id.to_string(), *mtype, key, content.to_string(), ); adapter.store(&entry).await.unwrap(); } // Create retriever with embedding let retriever = MemoryRetriever::new(adapter); retriever.set_embedding_client(Arc::new(MockEmbeddingClient::new())); // Retrieve memories about user preferences let result = retriever .retrieve(&agent_id, "我之前说过什么偏好?") .await .unwrap(); let total = result.knowledge.len() + result.preferences.len() + result.experience.len(); assert!( total > 0, "embedding-enhanced retrieval should find memories" ); assert!( result.preferences.len() > 0, "should find preference memories" ); } /// EM-08: Embedding configuration hot update — no panic, no disruption. #[tokio::test] async fn em08_embedding_hot_update() { let storage = Arc::new(SqliteStorage::in_memory().await); let adapter = Arc::new(VikingAdapter::new(storage)); let agent_id = AgentId::new(); // Store a memory let entry = MemoryEntry::new( &agent_id.to_string(), MemoryType::Knowledge, "rust-async", "Tokio runtime uses work-stealing scheduler".to_string(), ); adapter.store(&entry).await.unwrap(); // Start without embedding let retriever = MemoryRetriever::new(adapter); // Retrieve without embedding — should not panic let _result_before = retriever .retrieve(&agent_id, "async runtime") .await .unwrap(); // Hot-update with embedding — should not disrupt ongoing operations retriever.set_embedding_client(Arc::new(MockEmbeddingClient::new())); // Retrieve with embedding — should not panic let _result_after = retriever .retrieve(&agent_id, "async runtime") .await .unwrap(); // Key assertion: hot-update does not panic or disrupt }