Skip to main content

construct/memory/
backend.rs

1#[derive(Debug, Clone, Copy, Eq, PartialEq)]
2pub enum MemoryBackendKind {
3    Kumiho,
4    None,
5    Unknown,
6}
7
8#[allow(clippy::struct_excessive_bools)]
9#[derive(Debug, Clone, Copy, Eq, PartialEq)]
10pub struct MemoryBackendProfile {
11    pub key: &'static str,
12    pub label: &'static str,
13    pub auto_save_default: bool,
14    pub uses_sqlite_hygiene: bool,
15    pub sqlite_based: bool,
16    pub optional_dependency: bool,
17}
18
19const KUMIHO_PROFILE: MemoryBackendProfile = MemoryBackendProfile {
20    key: "kumiho",
21    label: "Kumiho — graph-native cognitive memory (recommended)",
22    auto_save_default: true,
23    uses_sqlite_hygiene: false,
24    sqlite_based: false,
25    optional_dependency: false,
26};
27
28const NONE_PROFILE: MemoryBackendProfile = MemoryBackendProfile {
29    key: "none",
30    label: "None — disable persistent memory",
31    auto_save_default: false,
32    uses_sqlite_hygiene: false,
33    sqlite_based: false,
34    optional_dependency: false,
35};
36
37const CUSTOM_PROFILE: MemoryBackendProfile = MemoryBackendProfile {
38    key: "custom",
39    label: "Custom backend — extension point",
40    auto_save_default: true,
41    uses_sqlite_hygiene: false,
42    sqlite_based: false,
43    optional_dependency: false,
44};
45
46/// Construct: Kumiho (via MCP) is the recommended backend, None for stateless.
47const SELECTABLE_MEMORY_BACKENDS: [MemoryBackendProfile; 2] = [KUMIHO_PROFILE, NONE_PROFILE];
48
49pub fn selectable_memory_backends() -> &'static [MemoryBackendProfile] {
50    &SELECTABLE_MEMORY_BACKENDS
51}
52
53/// Construct: Kumiho is the default persistent memory backend.
54pub fn default_memory_backend_key() -> &'static str {
55    KUMIHO_PROFILE.key
56}
57
58pub fn classify_memory_backend(backend: &str) -> MemoryBackendKind {
59    match backend {
60        "kumiho" => MemoryBackendKind::Kumiho,
61        "none" => MemoryBackendKind::None,
62        _ => MemoryBackendKind::Unknown,
63    }
64}
65
66pub fn memory_backend_profile(backend: &str) -> MemoryBackendProfile {
67    match classify_memory_backend(backend) {
68        MemoryBackendKind::Kumiho => KUMIHO_PROFILE,
69        MemoryBackendKind::None => NONE_PROFILE,
70        MemoryBackendKind::Unknown => CUSTOM_PROFILE,
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn classify_known_backends() {
80        assert_eq!(classify_memory_backend("kumiho"), MemoryBackendKind::Kumiho);
81        assert_eq!(classify_memory_backend("none"), MemoryBackendKind::None);
82    }
83
84    #[test]
85    fn classify_removed_backends_are_unknown() {
86        for name in ["sqlite", "lucid", "markdown", "qdrant"] {
87            assert_eq!(
88                classify_memory_backend(name),
89                MemoryBackendKind::Unknown,
90                "'{name}' should be treated as Unknown after removal"
91            );
92        }
93    }
94
95    #[test]
96    fn classify_unknown_backend() {
97        assert_eq!(classify_memory_backend("redis"), MemoryBackendKind::Unknown);
98    }
99
100    #[test]
101    fn default_backend_is_kumiho() {
102        assert_eq!(default_memory_backend_key(), "kumiho");
103    }
104
105    #[test]
106    fn selectable_backends_are_kumiho_and_none() {
107        let backends = selectable_memory_backends();
108        assert_eq!(backends.len(), 2);
109        assert_eq!(backends[0].key, "kumiho");
110        assert_eq!(backends[1].key, "none");
111    }
112
113    #[test]
114    fn unknown_profile_preserves_extensibility_defaults() {
115        let profile = memory_backend_profile("custom-memory");
116        assert_eq!(profile.key, "custom");
117        assert!(profile.auto_save_default);
118        assert!(!profile.uses_sqlite_hygiene);
119    }
120}