use std::collections::HashMap; use parking_lot::RwLock; /// per-session 的用户模型覆盖选择存储。 /// /// 与 ExpertRuntime 的 session_experts 平级独立,职责单一: /// 只负责存储 session_id -> (provider, model) 的映射,不依赖任何业务模块。 #[derive(Debug, Default)] pub struct ModelSelectionStore { selections: RwLock, Option)>>, } impl ModelSelectionStore { pub fn new() -> Self { Self::default() } /// 设置 session 的用户模型覆盖。provider 和 model 均为 None 时清除该 session 的选择。 pub fn set(&self, session_id: &str, provider: Option, model: Option) { let mut selections = self .selections .write(); if provider.is_none() && model.is_none() { selections.remove(session_id); } else { selections.insert(session_id.to_string(), (provider, model)); } } /// 读取 session 的用户模型覆盖。 pub fn get(&self, session_id: &str) -> Option<(Option, Option)> { self.selections .read() .get(session_id) .cloned() } } #[cfg(test)] mod tests { use super::*; #[test] fn set_and_get() { let store = ModelSelectionStore::new(); store.set("s1", Some("p1".to_string()), Some("m1".to_string())); assert_eq!( store.get("s1"), Some((Some("p1".to_string()), Some("m1".to_string()))) ); } #[test] fn get_missing_returns_none() { let store = ModelSelectionStore::new(); assert_eq!(store.get("missing"), None); } #[test] fn set_none_none_removes_entry() { let store = ModelSelectionStore::new(); store.set("s1", Some("p1".to_string()), Some("m1".to_string())); assert!(store.get("s1").is_some()); store.set("s1", None, None); assert!(store.get("s1").is_none()); } #[test] fn set_only_provider_keeps_entry() { let store = ModelSelectionStore::new(); store.set("s1", Some("p1".to_string()), None); assert_eq!(store.get("s1"), Some((Some("p1".to_string()), None))); } }