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")]
153 pub enrichment_status: String,
154 #[serde(default = "default_supersede_mode")]
156 pub supersede_mode: String,
157 #[serde(default, skip_serializing_if = "Option::is_none")]
159 pub structured_fields: Option<String>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub retrieval_cue: Option<String>,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub source_text: Option<String>,
166}
167
168fn default_enrichment_status() -> String {
169 "raw".to_string()
170}
171
172fn default_supersede_mode() -> String {
173 "hide".to_string()
174}
175
176impl Default for RawDocument {
177 fn default() -> Self {
178 Self {
179 source: String::new(),
180 source_id: String::new(),
181 title: String::new(),
182 summary: None,
183 content: String::new(),
184 url: None,
185 last_modified: 0,
186 metadata: HashMap::new(),
187 memory_type: None,
188 domain: None,
189 source_agent: None,
190 confidence: None,
191 confirmed: None,
192 stability: None,
193 supersedes: None,
194 pending_revision: false,
195 entity_id: None,
196 quality: None,
197 is_recap: false,
198 enrichment_status: "raw".to_string(),
199 supersede_mode: "hide".to_string(),
200 structured_fields: None,
201 retrieval_cue: None,
202 source_text: None,
203 }
204 }
205}
206
207#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
209#[serde(rename_all = "lowercase")]
210pub enum SourceType {
211 Obsidian,
212 Directory,
213}
214
215impl SourceType {
216 pub fn as_str(&self) -> &'static str {
217 match self {
218 Self::Obsidian => "obsidian",
219 Self::Directory => "directory",
220 }
221 }
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub enum SyncStatus {
227 Active,
228 Paused,
229 Error(String),
230}