Skip to main content

zeph_memory/
types.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4/// Memory tier classification for the AOI three-layer architecture.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum MemoryTier {
8    /// Current conversation window. Virtual tier — not stored in the DB.
9    Working,
10    /// Session-bound messages. Default tier for all persisted messages.
11    Episodic,
12    /// Cross-session distilled facts. Promoted from Episodic when a fact
13    /// appears in `promotion_min_sessions`+ distinct sessions.
14    Semantic,
15}
16
17impl MemoryTier {
18    #[must_use]
19    pub fn as_str(self) -> &'static str {
20        match self {
21            Self::Working => "working",
22            Self::Episodic => "episodic",
23            Self::Semantic => "semantic",
24        }
25    }
26}
27
28impl std::fmt::Display for MemoryTier {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.write_str(self.as_str())
31    }
32}
33
34impl std::str::FromStr for MemoryTier {
35    type Err = String;
36    fn from_str(s: &str) -> Result<Self, Self::Err> {
37        match s {
38            "working" => Ok(Self::Working),
39            "episodic" => Ok(Self::Episodic),
40            "semantic" => Ok(Self::Semantic),
41            other => Err(format!("unknown memory tier: {other}")),
42        }
43    }
44}
45
46/// Strongly typed wrapper for conversation row IDs.
47#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type)]
48#[sqlx(transparent)]
49pub struct ConversationId(pub i64);
50
51/// Strongly typed wrapper for message row IDs.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type)]
53#[sqlx(transparent)]
54pub struct MessageId(pub i64);
55
56/// Strongly typed wrapper for `mem_scene` row IDs.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type)]
58#[sqlx(transparent)]
59pub struct MemSceneId(pub i64);
60
61impl std::fmt::Display for MemSceneId {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "{}", self.0)
64    }
65}
66
67impl std::fmt::Display for ConversationId {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        write!(f, "{}", self.0)
70    }
71}
72
73impl std::fmt::Display for MessageId {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        write!(f, "{}", self.0)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn memory_tier_round_trip() {
85        for tier in [
86            MemoryTier::Working,
87            MemoryTier::Episodic,
88            MemoryTier::Semantic,
89        ] {
90            let s = tier.as_str();
91            let parsed: MemoryTier = s.parse().expect("should parse");
92            assert_eq!(parsed, tier);
93            assert_eq!(format!("{tier}"), s);
94        }
95    }
96
97    #[test]
98    fn memory_tier_unknown_string_errors() {
99        assert!("unknown".parse::<MemoryTier>().is_err());
100    }
101
102    #[test]
103    fn memory_tier_serde_round_trip() {
104        let json = serde_json::to_string(&MemoryTier::Semantic).unwrap();
105        assert_eq!(json, "\"semantic\"");
106        let parsed: MemoryTier = serde_json::from_str(&json).unwrap();
107        assert_eq!(parsed, MemoryTier::Semantic);
108    }
109
110    #[test]
111    fn conversation_id_display() {
112        let id = ConversationId(42);
113        assert_eq!(format!("{id}"), "42");
114    }
115
116    #[test]
117    fn message_id_display() {
118        let id = MessageId(7);
119        assert_eq!(format!("{id}"), "7");
120    }
121
122    #[test]
123    fn conversation_id_eq() {
124        assert_eq!(ConversationId(1), ConversationId(1));
125        assert_ne!(ConversationId(1), ConversationId(2));
126    }
127
128    #[test]
129    fn message_id_copy() {
130        let id = MessageId(5);
131        let copied = id;
132        assert_eq!(id, copied);
133    }
134}