use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub trait Embedder: Send + Sync {
fn embed(
&self,
text: &str,
) -> std::result::Result<Vec<f32>, Box<dyn std::error::Error + Send + Sync>>;
fn embed_batch(
&self,
texts: &[&str],
) -> std::result::Result<Vec<Vec<f32>>, Box<dyn std::error::Error + Send + Sync>> {
texts.iter().map(|t| self.embed(t)).collect()
}
fn dim(&self) -> usize;
fn fingerprint(&self) -> Option<String> {
None
}
fn name(&self) -> Option<String> {
None
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Memory {
pub rid: String,
pub memory_type: String,
pub text: String,
pub created_at: f64,
pub importance: f64,
pub valence: f64,
pub half_life: f64,
pub last_access: f64,
pub access_count: u32,
pub consolidation_status: String,
pub storage_tier: String,
pub consolidated_into: Option<String>,
pub metadata: serde_json::Value,
pub namespace: String,
pub certainty: f64,
pub domain: String,
pub source: String,
pub emotional_state: Option<String>,
pub session_id: Option<String>,
pub due_at: Option<f64>,
pub temporal_kind: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScoreBreakdown {
pub similarity: f64,
pub decay: f64,
pub recency: f64,
pub importance: f64,
pub graph_proximity: f64,
pub contributions: ScoreContributions,
pub valence_multiplier: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScoreContributions {
pub similarity: f64,
pub decay: f64,
pub recency: f64,
pub importance: f64,
pub graph_proximity: f64,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum RecordStatus {
#[default]
Active,
Superseded,
}
impl RecordStatus {
pub fn as_str(&self) -> &'static str {
match self {
RecordStatus::Active => "active",
RecordStatus::Superseded => "superseded",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecallResult {
pub rid: String,
pub memory_type: String,
pub text: String,
pub created_at: f64,
pub importance: f64,
pub valence: f64,
pub score: f64,
pub scores: ScoreBreakdown,
pub why_retrieved: Vec<String>,
pub metadata: serde_json::Value,
pub namespace: String,
pub certainty: f64,
pub domain: String,
pub source: String,
pub emotional_state: Option<String>,
#[serde(default)]
pub current_status: RecordStatus,
#[serde(default)]
pub superseded_by: Option<String>,
#[serde(default)]
pub disputed_with: Vec<String>,
#[serde(default)]
pub aged_last_verified: Option<f64>,
#[serde(default)]
pub best_span: Option<(usize, usize)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExplainLaneReport {
pub status: String,
pub candidates: usize,
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExplainPoolRow {
pub rid: String,
pub score_q: f64,
pub similarity: f64,
pub lex: Option<f64>,
pub lanes_admitted: Vec<String>,
pub rank_pre_fusion: usize,
pub rank_post_fusion: usize,
pub selected: bool,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RecallExplain {
pub comparator: String,
pub score_algebra: String,
pub query_sentiment: f64,
pub bm25_near_best_fraction: Option<f64>,
pub lanes: std::collections::BTreeMap<String, ExplainLaneReport>,
pub pool: Vec<ExplainPoolRow>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecallResponse {
pub results: Vec<RecallResult>,
pub confidence: f64,
pub certainty_reasons: Vec<String>,
pub retrieval_summary: RetrievalSummary,
pub hints: Vec<RefinementHint>,
#[serde(default)]
pub coverage: SearchCoverage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetrievalSummary {
pub top_similarity: f64,
pub score_spread: f64,
pub sources_used: Vec<String>,
pub candidate_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchCoverage {
pub namespace: Option<String>,
pub memory_type: Option<String>,
pub candidate_count: usize,
pub threshold_tau: f64,
pub top_similarity: f64,
pub outcome: CoverageOutcome,
#[serde(default)]
pub label_request: Vec<String>,
}
impl Default for SearchCoverage {
fn default() -> Self {
Self {
namespace: None,
memory_type: None,
candidate_count: 0,
threshold_tau: 0.0,
top_similarity: 0.0,
outcome: CoverageOutcome::NoMatchingRecord,
label_request: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum CoverageOutcome {
Matched,
BelowThreshold,
NoMatchingRecord,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefinementHint {
pub hint_type: String,
pub suggestion: String,
pub related_entities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Edge {
pub edge_id: String,
pub src: String,
pub dst: String,
pub rel_type: String,
pub weight: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entity {
pub name: String,
pub entity_type: String,
pub first_seen: f64,
pub last_seen: f64,
pub mention_count: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Stats {
pub active_memories: i64,
pub consolidated_memories: i64,
pub tombstoned_memories: i64,
pub archived_memories: i64,
pub edges: i64,
pub entities: i64,
pub operations: i64,
pub open_conflicts: i64,
pub resolved_conflicts: i64,
pub pending_triggers: i64,
pub active_patterns: i64,
pub scoring_cache_entries: usize,
pub vec_index_entries: usize,
pub graph_index_entities: usize,
pub graph_index_edges: usize,
#[serde(default)]
pub status_read_policy: String,
#[serde(default)]
pub superseded_records: i64,
#[serde(default)]
pub superseded_served_since_boot: u64,
#[serde(default)]
pub provenance_gate_mode: String,
#[serde(default)]
pub provenance_flagged_since_boot: u64,
#[serde(default)]
pub embedder_window_chars: Option<usize>,
#[serde(default)]
pub embedder_truncated_writes: u64,
#[serde(default)]
pub embedder_chunked_writes: u64,
#[serde(default)]
pub chunk_vectors: u64,
#[serde(default)]
pub apostrophe_entities: u64,
#[serde(default)]
pub possessive_aliases: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Trigger {
pub trigger_type: String,
pub reason: String,
pub urgency: f64,
pub source_rids: Vec<String>,
pub suggested_action: String,
pub context: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsolidationResult {
pub consolidated_rid: String,
pub source_rids: Vec<String>,
pub cluster_size: usize,
pub summary: String,
pub importance: f64,
pub entities_linked: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsolidationPreview {
pub cluster_size: usize,
pub texts: Vec<String>,
pub preview_summary: String,
pub source_rids: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct MemoryWithEmbedding {
pub rid: String,
pub memory_type: String,
pub text: String,
pub embedding: Vec<f32>,
pub created_at: f64,
pub importance: f64,
pub valence: f64,
pub half_life: f64,
pub last_access: f64,
pub metadata: serde_json::Value,
pub namespace: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecayedMemory {
pub rid: String,
pub text: String,
pub memory_type: String,
pub original_importance: f64,
pub current_score: f64,
pub days_since_access: f64,
}
#[derive(Debug, Clone)]
pub struct ScoringRow {
pub created_at: f64,
pub importance: f64,
pub half_life: f64,
pub last_access: f64,
pub access_count: u32,
pub valence: f64,
pub consolidation_status: String,
pub memory_type: String,
pub namespace: String,
pub certainty: f64,
pub domain: String,
pub source: String,
pub emotional_state: Option<String>,
}
#[derive(Debug, Clone)]
pub struct RecordInput {
pub text: String,
pub memory_type: String,
pub importance: f64,
pub valence: f64,
pub half_life: f64,
pub metadata: serde_json::Value,
pub embedding: Vec<f32>,
pub namespace: String,
pub certainty: f64,
pub domain: String,
pub source: String,
pub emotional_state: Option<String>,
pub idempotency_key: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConflictType {
IdentityFact,
Preference,
Temporal,
Consolidation,
Minor,
}
impl ConflictType {
pub fn as_str(&self) -> &'static str {
match self {
ConflictType::IdentityFact => "identity_fact",
ConflictType::Preference => "preference",
ConflictType::Temporal => "temporal",
ConflictType::Consolidation => "consolidation",
ConflictType::Minor => "minor",
}
}
pub fn from_str(s: &str) -> Self {
match s {
"identity_fact" => ConflictType::IdentityFact,
"preference" => ConflictType::Preference,
"temporal" => ConflictType::Temporal,
"consolidation" => ConflictType::Consolidation,
_ => ConflictType::Minor,
}
}
pub fn default_priority(&self) -> &'static str {
match self {
ConflictType::IdentityFact => "critical",
ConflictType::Preference => "high",
ConflictType::Temporal => "high",
ConflictType::Consolidation => "medium",
ConflictType::Minor => "low",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Conflict {
pub conflict_id: String,
pub conflict_type: String,
pub priority: String,
pub status: String,
pub memory_a: String,
pub memory_b: String,
pub entity: Option<String>,
pub rel_type: Option<String>,
pub detected_at: f64,
pub detected_by: String,
pub detection_reason: String,
pub resolved_at: Option<f64>,
pub resolved_by: Option<String>,
pub strategy: Option<String>,
pub winner_rid: Option<String>,
pub resolution_note: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConflictResolutionResult {
pub conflict_id: String,
pub strategy: String,
pub winner_rid: Option<String>,
pub loser_tombstoned: bool,
pub new_memory_rid: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrectionResult {
pub original_rid: String,
pub corrected_rid: String,
pub original_tombstoned: bool,
pub revision_num: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecordRevision {
pub revision_id: String,
pub rid: String,
pub revision_num: i64,
pub prior_text: String,
pub prior_metadata: serde_json::Value,
pub prior_importance: f64,
pub prior_valence: f64,
pub reason: String,
pub applied_at: f64,
pub origin_actor: String,
#[serde(default)]
pub prior_embedding_model: Option<String>,
#[serde(default)]
pub prior_embedding_hash: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LinkType {
Advances,
Supersedes,
Contradicts,
Supports,
Questions,
DerivedFrom,
Custom(String),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LinkRecallPolarity {
pub neighbor_factor: f64,
pub demote_self_as_target: f64,
}
impl LinkType {
pub fn as_str(&self) -> String {
match self {
LinkType::Advances => "advances".to_string(),
LinkType::Supersedes => "supersedes".to_string(),
LinkType::Contradicts => "contradicts".to_string(),
LinkType::Supports => "supports".to_string(),
LinkType::Questions => "questions".to_string(),
LinkType::DerivedFrom => "derived_from".to_string(),
LinkType::Custom(name) => format!("custom:{name}"),
}
}
pub fn from_str_lenient(s: &str) -> LinkType {
match s {
"advances" => LinkType::Advances,
"supersedes" => LinkType::Supersedes,
"contradicts" => LinkType::Contradicts,
"supports" => LinkType::Supports,
"questions" => LinkType::Questions,
"derived_from" => LinkType::DerivedFrom,
other => {
let name = other.strip_prefix("custom:").unwrap_or(other);
LinkType::Custom(name.to_string())
}
}
}
pub fn is_symmetric(&self) -> bool {
matches!(self, LinkType::Contradicts)
}
pub fn recall_polarity(&self) -> LinkRecallPolarity {
match self {
LinkType::Supports | LinkType::Advances => LinkRecallPolarity {
neighbor_factor: 1.0,
demote_self_as_target: 1.0,
},
LinkType::DerivedFrom => LinkRecallPolarity {
neighbor_factor: 0.6,
demote_self_as_target: 1.0,
},
LinkType::Supersedes => LinkRecallPolarity {
neighbor_factor: 1.0,
demote_self_as_target: 0.5,
},
LinkType::Contradicts => LinkRecallPolarity {
neighbor_factor: 0.3,
demote_self_as_target: 1.0,
},
LinkType::Questions => LinkRecallPolarity {
neighbor_factor: 0.3,
demote_self_as_target: 1.0,
},
LinkType::Custom(_) => LinkRecallPolarity {
neighbor_factor: 0.5,
demote_self_as_target: 1.0,
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RecordLink {
pub target_rid: String,
pub link_type: LinkType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LinkDirection {
Outbound,
Inbound,
Both,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LinkedRecord {
pub rid: String,
pub link_type: String,
pub created_at: f64,
pub direction: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LinkResult {
Inserted {
target_rid: String,
link_type: String,
},
AlreadyExists {
target_rid: String,
link_type: String,
},
Failed {
target_rid: String,
link_type: String,
error: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LeakAuditReport {
pub window_floor: Option<f64>,
pub candidate_count: usize,
pub candidate_rids: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TriggerType {
DecayReview,
ConsolidationReady,
ConflictEscalation,
TemporalDrift,
Redundancy,
RelationshipInsight,
ValenceTrend,
EntityAnomaly,
PatternDiscovered,
}
impl TriggerType {
pub fn as_str(&self) -> &'static str {
match self {
TriggerType::DecayReview => "decay_review",
TriggerType::ConsolidationReady => "consolidation_ready",
TriggerType::ConflictEscalation => "conflict_escalation",
TriggerType::TemporalDrift => "temporal_drift",
TriggerType::Redundancy => "redundancy",
TriggerType::RelationshipInsight => "relationship_insight",
TriggerType::ValenceTrend => "valence_trend",
TriggerType::EntityAnomaly => "entity_anomaly",
TriggerType::PatternDiscovered => "pattern_discovered",
}
}
pub fn from_str(s: &str) -> Self {
match s {
"decay_review" => TriggerType::DecayReview,
"consolidation_ready" => TriggerType::ConsolidationReady,
"conflict_escalation" => TriggerType::ConflictEscalation,
"temporal_drift" => TriggerType::TemporalDrift,
"redundancy" => TriggerType::Redundancy,
"relationship_insight" => TriggerType::RelationshipInsight,
"valence_trend" => TriggerType::ValenceTrend,
"entity_anomaly" => TriggerType::EntityAnomaly,
"pattern_discovered" => TriggerType::PatternDiscovered,
_ => TriggerType::DecayReview,
}
}
pub fn default_cooldown_secs(&self) -> f64 {
match self {
TriggerType::DecayReview => 86400.0 * 3.0,
TriggerType::ConsolidationReady => 86400.0,
TriggerType::ConflictEscalation => 86400.0 * 2.0,
TriggerType::TemporalDrift => 86400.0 * 14.0,
TriggerType::Redundancy => 86400.0,
TriggerType::RelationshipInsight => 86400.0 * 7.0,
TriggerType::ValenceTrend => 86400.0 * 7.0,
TriggerType::EntityAnomaly => 86400.0 * 7.0,
TriggerType::PatternDiscovered => 86400.0 * 7.0,
}
}
pub fn default_expiry_secs(&self) -> f64 {
match self {
TriggerType::DecayReview => 86400.0 * 7.0,
TriggerType::ConsolidationReady => 86400.0 * 3.0,
TriggerType::ConflictEscalation => 86400.0 * 14.0,
_ => 86400.0 * 7.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinkConfig {
pub importance_threshold: f64,
pub decay_threshold: f64,
pub max_triggers: usize,
pub run_consolidation: bool,
pub run_conflict_scan: bool,
pub run_pattern_mining: bool,
pub consolidation_sim_threshold: f64,
pub consolidation_time_window_days: f64,
pub consolidation_min_cluster: usize,
pub consolidation_limit: usize,
pub consolidation_require_entity_overlap: bool,
pub min_active_memories: i64,
pub run_personality: bool,
pub extract_attribute_claims: bool,
}
impl Default for ThinkConfig {
fn default() -> Self {
Self {
importance_threshold: 0.5,
decay_threshold: 0.1,
max_triggers: 10,
run_consolidation: true,
run_conflict_scan: true,
run_pattern_mining: false,
consolidation_sim_threshold: 0.6,
consolidation_time_window_days: 7.0,
consolidation_min_cluster: 2,
consolidation_limit: 5,
consolidation_require_entity_overlap: true,
min_active_memories: 10,
run_personality: true,
extract_attribute_claims: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinkResult {
pub triggers: Vec<Trigger>,
pub consolidation_count: usize,
pub conflicts_found: usize,
pub patterns_new: usize,
pub patterns_updated: usize,
pub expired_triggers: usize,
pub personality_updated: bool,
pub duration_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistedTrigger {
pub trigger_id: String,
pub trigger_type: String,
pub urgency: f64,
pub status: String,
pub reason: String,
pub suggested_action: String,
pub source_rids: Vec<String>,
pub context: serde_json::Value,
pub created_at: f64,
pub delivered_at: Option<f64>,
pub acknowledged_at: Option<f64>,
pub acted_at: Option<f64>,
pub expires_at: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pattern {
pub pattern_id: String,
pub pattern_type: String,
pub status: String,
pub confidence: f64,
pub description: String,
pub evidence_rids: Vec<String>,
pub entity_names: Vec<String>,
pub context: serde_json::Value,
pub first_seen: f64,
pub last_confirmed: f64,
pub occurrence_count: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PatternMiningResult {
pub new_patterns: usize,
pub updated_patterns: usize,
pub stale_patterns: usize,
}
#[derive(Debug, Clone)]
pub struct PatternConfig {
pub co_occurrence_min_count: usize,
pub temporal_cluster_min_events: usize,
pub valence_trend_delta_threshold: f64,
pub topic_cluster_sim_threshold: f64,
pub topic_cluster_time_window_days: f64,
pub entity_hub_min_degree: usize,
pub max_patterns: usize,
pub cross_domain_candidates_per_domain: usize,
pub cross_domain_sim_threshold: f64,
pub cross_domain_max_per_pair: usize,
pub entity_bridge_min_domains: usize,
pub entity_bridge_min_mentions_per_domain: usize,
pub run_cross_domain: bool,
}
#[cfg(feature = "profiling")]
#[derive(Debug, Clone)]
pub struct RecallTimings {
pub vec_search_ms: f64,
pub cache_score_ms: f64,
pub fetch_ms: f64,
pub scoring_ms: f64,
pub graph_ms: f64,
pub reinforce_ms: f64,
pub sort_truncate_ms: f64,
pub total_ms: f64,
pub candidate_count: usize,
pub graph_expansion_count: usize,
}
#[cfg(feature = "profiling")]
#[derive(Debug, Clone)]
pub struct RecallProfiledResult {
pub results: Vec<RecallResult>,
pub timings: RecallTimings,
}
#[derive(Debug, Clone)]
pub struct RecallQuery {
pub embedding: Vec<f32>,
pub top_k: usize,
pub time_window: Option<(f64, f64)>,
pub memory_type: Option<String>,
pub include_consolidated: bool,
pub expand_entities: bool,
pub query_text: Option<String>,
pub skip_reinforce: bool,
pub namespace: Option<String>,
pub domain: Option<String>,
pub source: Option<String>,
pub certainty_min: Option<f64>,
pub order: Option<String>,
pub include_superseded: bool,
}
impl RecallQuery {
pub fn new(embedding: Vec<f32>) -> Self {
Self {
embedding,
top_k: 10,
time_window: None,
memory_type: None,
include_consolidated: false,
expand_entities: false,
query_text: None,
skip_reinforce: false,
namespace: None,
domain: None,
source: None,
certainty_min: None,
order: None,
include_superseded: false,
}
}
pub fn include_superseded(mut self) -> Self {
self.include_superseded = true;
self
}
pub fn certainty_min(mut self, min: f64) -> Self {
self.certainty_min = Some(min);
self
}
pub fn order(mut self, order: &str) -> Self {
self.order = Some(order.to_string());
self
}
pub fn top_k(mut self, k: usize) -> Self {
self.top_k = k;
self
}
pub fn memory_type(mut self, mt: &str) -> Self {
self.memory_type = Some(mt.to_string());
self
}
pub fn namespace(mut self, ns: &str) -> Self {
self.namespace = Some(ns.to_string());
self
}
pub fn time_window(mut self, start: f64, end: f64) -> Self {
self.time_window = Some((start, end));
self
}
pub fn expand_entities(mut self, query_text: &str) -> Self {
self.expand_entities = true;
self.query_text = Some(query_text.to_string());
self
}
pub fn include_consolidated(mut self) -> Self {
self.include_consolidated = true;
self
}
pub fn skip_reinforce(mut self) -> Self {
self.skip_reinforce = true;
self
}
pub fn domain(mut self, d: &str) -> Self {
self.domain = Some(d.to_string());
self
}
pub fn source(mut self, s: &str) -> Self {
self.source = Some(s.to_string());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearnedWeights {
pub w_sim: f64,
pub w_decay: f64,
pub w_recency: f64,
pub gate_tau: f64,
pub alpha_imp: f64,
pub keyword_boost: f64,
pub generation: i64,
}
impl Default for LearnedWeights {
fn default() -> Self {
Self {
w_sim: 0.50,
w_decay: 0.20,
w_recency: 0.30,
gate_tau: 0.25,
alpha_imp: 0.80,
keyword_boost: 0.31,
generation: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersonalityTrait {
pub trait_name: String,
pub score: f64,
pub confidence: f64,
pub sample_count: i64,
pub updated_at: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersonalityProfile {
pub traits: Vec<PersonalityTrait>,
pub updated_at: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub session_id: String,
pub namespace: String,
pub client_id: String,
pub status: String,
pub started_at: f64,
pub ended_at: Option<f64>,
pub summary: Option<String>,
pub avg_valence: Option<f64>,
pub memory_count: i64,
pub topics: Vec<String>,
pub metadata: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionSummary {
pub session_id: String,
pub duration_secs: f64,
pub memory_count: i64,
pub avg_valence: f64,
pub topics: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityProfile {
pub entity: String,
pub entity_type: String,
pub mention_count: i64,
pub session_count: i64,
pub domains: Vec<DomainCount>,
pub avg_valence: f64,
pub valence_trend: f64,
pub dominant_emotion: Option<String>,
pub interaction_frequency: f64,
pub last_mentioned_at: f64,
pub first_seen: f64,
pub window_days: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DomainCount {
pub domain: String,
pub count: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossDomainLink {
pub rid_a: String,
pub rid_b: String,
pub domain_a: String,
pub domain_b: String,
pub similarity: f64,
pub text_a: String,
pub text_b: String,
pub score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityBridge {
pub entity: String,
pub domains: Vec<DomainCount>,
pub bridge_score: f64,
pub total_mentions: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RelationshipDepth {
pub entity: String,
pub entity_type: String,
pub sessions_together: i64,
pub memories_mentioning: i64,
pub avg_valence: f64,
pub domains_spanning: Vec<String>,
pub relationship_types: Vec<String>,
pub connection_count: i64,
pub depth_score: f64,
pub first_seen: f64,
pub last_seen: f64,
pub interaction_frequency: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubstitutionCategory {
pub id: String,
pub name: String,
pub conflict_mode: String,
pub status: String,
pub member_count: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubstitutionMember {
pub id: String,
pub category_name: String,
pub token_normalized: String,
pub token_display: String,
pub confidence: f64,
pub source: String,
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReclassifyResult {
pub conflict_id: String,
pub old_type: String,
pub new_type: String,
pub learned_members: Vec<LearnedMember>,
pub category_created: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LearnedMember {
pub token: String,
pub category_name: String,
pub is_new: bool,
}
impl Default for PatternConfig {
fn default() -> Self {
Self {
co_occurrence_min_count: 3,
temporal_cluster_min_events: 3,
valence_trend_delta_threshold: 0.3,
topic_cluster_sim_threshold: 0.55,
topic_cluster_time_window_days: 30.0,
entity_hub_min_degree: 5,
max_patterns: 50,
cross_domain_candidates_per_domain: 15,
cross_domain_sim_threshold: 0.50,
cross_domain_max_per_pair: 3,
entity_bridge_min_domains: 2,
entity_bridge_min_mentions_per_domain: 3,
run_cross_domain: true,
}
}
}