use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MemoryType {
Identity,
Preference,
Decision,
Lesson,
Gotcha,
Fact,
}
impl MemoryType {
pub fn all_values() -> &'static [&'static str] {
&[
"identity",
"preference",
"decision",
"lesson",
"gotcha",
"fact",
]
}
pub fn is_profile_alias(s: &str) -> bool {
s.eq_ignore_ascii_case("profile")
}
pub fn is_knowledge_alias(s: &str) -> bool {
s.eq_ignore_ascii_case("knowledge")
}
}
impl std::fmt::Display for MemoryType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Identity => "identity",
Self::Preference => "preference",
Self::Decision => "decision",
Self::Lesson => "lesson",
Self::Gotcha => "gotcha",
Self::Fact => "fact",
};
f.write_str(s)
}
}
impl std::str::FromStr for MemoryType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"identity" => Ok(Self::Identity),
"preference" => Ok(Self::Preference),
"decision" => Ok(Self::Decision),
"lesson" => Ok(Self::Lesson),
"gotcha" => Ok(Self::Gotcha),
"fact" => Ok(Self::Fact),
"goal" => Ok(Self::Identity),
"profile" => Err(
"profile requires sub-classification into identity or preference -- use classify_memory_type".to_string()
),
"knowledge" => Err(
"knowledge requires sub-classification into fact, lesson, or gotcha -- use classify_memory_type".to_string()
),
"correction" | "custom" | "recap" => Ok(Self::Fact),
_ => Err(format!(
"invalid memory_type '{}', valid values: {}",
s,
Self::all_values().join(", ")
)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StabilityTier {
Protected,
Standard,
Ephemeral,
}
pub fn stability_tier(memory_type: Option<&str>) -> StabilityTier {
match memory_type {
Some("identity") | Some("preference") => StabilityTier::Protected,
Some("fact") | Some("decision") | Some("lesson") | Some("gotcha") => {
StabilityTier::Standard
}
Some("goal") => StabilityTier::Protected,
_ => StabilityTier::Ephemeral,
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawDocument {
pub source: String,
pub source_id: String,
pub title: String,
pub summary: Option<String>,
pub content: String,
pub url: Option<String>,
pub last_modified: i64,
pub metadata: HashMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_type: Option<String>,
#[serde(default, alias = "domain", skip_serializing_if = "Option::is_none")]
pub space: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_agent: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidence: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confirmed: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stability: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub supersedes: Option<String>,
#[serde(default)]
pub pending_revision: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entity_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub quality: Option<String>,
#[serde(default)]
pub is_recap: bool,
#[serde(default = "default_enrichment_status")]
pub enrichment_status: String,
#[serde(default = "default_supersede_mode")]
pub supersede_mode: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub structured_fields: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retrieval_cue: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_text: Option<String>,
}
fn default_enrichment_status() -> String {
"raw".to_string()
}
fn default_supersede_mode() -> String {
"hide".to_string()
}
impl Default for RawDocument {
fn default() -> Self {
Self {
source: String::new(),
source_id: String::new(),
title: String::new(),
summary: None,
content: String::new(),
url: None,
last_modified: 0,
metadata: HashMap::new(),
memory_type: None,
space: None,
source_agent: None,
confidence: None,
confirmed: None,
stability: None,
supersedes: None,
pending_revision: false,
entity_id: None,
quality: None,
is_recap: false,
enrichment_status: "raw".to_string(),
supersede_mode: "hide".to_string(),
structured_fields: None,
retrieval_cue: None,
source_text: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SourceType {
Obsidian,
Directory,
}
impl SourceType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Obsidian => "obsidian",
Self::Directory => "directory",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SyncStatus {
Active,
Paused,
Error(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceStatus {
pub name: String,
pub connected: bool,
pub requires_auth: bool,
pub last_sync: Option<i64>,
pub document_count: u64,
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Source {
pub id: String,
pub source_type: SourceType,
pub path: std::path::PathBuf,
#[serde(default = "default_sync_status")]
pub status: SyncStatus,
pub last_sync: Option<i64>,
#[serde(default)]
pub file_count: u64,
#[serde(default)]
pub memory_count: u64,
#[serde(default)]
pub last_sync_errors: u64,
#[serde(default)]
pub last_sync_error_detail: Option<String>,
}
fn default_sync_status() -> SyncStatus {
SyncStatus::Active
}