1use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "lowercase")]
16pub enum MemoryType {
17 Identity,
18 Preference,
19 Decision,
20 Fact,
21 Goal,
22}
23
24impl MemoryType {
25 pub fn all_values() -> &'static [&'static str] {
27 &["identity", "preference", "decision", "fact", "goal"]
28 }
29
30 pub fn is_profile_alias(s: &str) -> bool {
33 s.eq_ignore_ascii_case("profile")
34 }
35}
36
37impl std::fmt::Display for MemoryType {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 let s = match self {
40 Self::Identity => "identity",
41 Self::Preference => "preference",
42 Self::Decision => "decision",
43 Self::Fact => "fact",
44 Self::Goal => "goal",
45 };
46 f.write_str(s)
47 }
48}
49
50impl std::str::FromStr for MemoryType {
51 type Err = String;
52 fn from_str(s: &str) -> Result<Self, Self::Err> {
53 match s.to_lowercase().as_str() {
54 "identity" => Ok(Self::Identity),
55 "preference" => Ok(Self::Preference),
56 "decision" => Ok(Self::Decision),
57 "fact" => Ok(Self::Fact),
58 "goal" => Ok(Self::Goal),
59 "knowledge" => Ok(Self::Fact),
61 "profile" => Err(
63 "profile requires sub-classification into identity, preference, or goal -- use classify_memory_type".to_string()
64 ),
65 "correction" | "custom" | "recap" => Ok(Self::Fact),
67 _ => Err(format!(
68 "invalid memory_type '{}', valid values: {}",
69 s,
70 Self::all_values().join(", ")
71 )),
72 }
73 }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum StabilityTier {
79 Protected,
81 Standard,
83 Ephemeral,
85}
86
87pub fn stability_tier(memory_type: Option<&str>) -> StabilityTier {
89 match memory_type {
90 Some("identity") | Some("preference") => StabilityTier::Protected,
91 Some("fact") | Some("decision") => StabilityTier::Standard,
92 _ => StabilityTier::Ephemeral,
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct RawDocument {
99 pub source: String,
101 pub source_id: String,
103 pub title: String,
105 pub summary: Option<String>,
107 pub content: String,
109 pub url: Option<String>,
111 pub last_modified: i64,
113 pub metadata: HashMap<String, String>,
115
116 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub memory_type: Option<String>,
120 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub domain: Option<String>,
123 #[serde(default, skip_serializing_if = "Option::is_none")]
125 pub source_agent: Option<String>,
126 #[serde(default, skip_serializing_if = "Option::is_none")]
128 pub confidence: Option<f32>,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub confirmed: Option<bool>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub stability: Option<String>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub supersedes: Option<String>,
138 #[serde(default)]
140 pub pending_revision: bool,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub entity_id: Option<String>,
144 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub quality: Option<String>,
147 #[serde(default)]
149 pub is_recap: bool,
150 #[serde(default = "default_enrichment_status")]
152 pub enrichment_status: String,
153 #[serde(default = "default_supersede_mode")]
155 pub supersede_mode: String,
156 #[serde(default, skip_serializing_if = "Option::is_none")]
158 pub structured_fields: Option<String>,
159 #[serde(default, skip_serializing_if = "Option::is_none")]
161 pub retrieval_cue: Option<String>,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
164 pub source_text: Option<String>,
165}
166
167fn default_enrichment_status() -> String {
168 "raw".to_string()
169}
170
171fn default_supersede_mode() -> String {
172 "hide".to_string()
173}
174
175impl Default for RawDocument {
176 fn default() -> Self {
177 Self {
178 source: String::new(),
179 source_id: String::new(),
180 title: String::new(),
181 summary: None,
182 content: String::new(),
183 url: None,
184 last_modified: 0,
185 metadata: HashMap::new(),
186 memory_type: None,
187 domain: None,
188 source_agent: None,
189 confidence: None,
190 confirmed: None,
191 stability: None,
192 supersedes: None,
193 pending_revision: false,
194 entity_id: None,
195 quality: None,
196 is_recap: false,
197 enrichment_status: "raw".to_string(),
198 supersede_mode: "hide".to_string(),
199 structured_fields: None,
200 retrieval_cue: None,
201 source_text: None,
202 }
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
208#[serde(rename_all = "lowercase")]
209pub enum SourceType {
210 Obsidian,
211 Directory,
212}
213
214impl SourceType {
215 pub fn as_str(&self) -> &'static str {
216 match self {
217 Self::Obsidian => "obsidian",
218 Self::Directory => "directory",
219 }
220 }
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
225pub enum SyncStatus {
226 Active,
227 Paused,
228 Error(String),
229}