Skip to main content

velesdb_memory/
model.rs

1//! Domain data model: the request/response value types of the memory layer.
2//!
3//! These are pure data — the shapes a caller links, recalls, filters on, and
4//! gets back — with no dependency on [`MemoryService`](crate::service::MemoryService)
5//! itself. Keeping them here separates *what the memory layer exchanges* from
6//! *how the service computes it*, and gives every adapter (MCP, bindings) one
7//! canonical place to import the contract from.
8
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12
13/// A typed link from a freshly remembered fact to an existing memory.
14#[derive(Debug, Clone, Deserialize, JsonSchema)]
15pub struct Link {
16    /// Id of the memory being linked to.
17    pub target: u64,
18    /// Relationship label (e.g. `"decided_in"`, `"references"`, `"depends_on"`).
19    pub relation: String,
20}
21
22/// One semantically recalled memory.
23#[derive(Debug, Clone, Serialize, JsonSchema)]
24pub struct Recollection {
25    /// Stable id of the memory.
26    pub id: u64,
27    /// Similarity score (higher is closer).
28    pub score: f32,
29    /// Stored fact content.
30    pub content: String,
31}
32
33/// Comparison operator for a [`ColumnFilter`] in
34/// [`MemoryService::recall_where`](crate::service::MemoryService::recall_where).
35#[derive(Debug, Clone, Copy, Deserialize, JsonSchema)]
36#[serde(rename_all = "lowercase")]
37pub enum ColumnOp {
38    /// `=`
39    Eq,
40    /// `!=`
41    Ne,
42    /// `<`
43    Lt,
44    /// `<=`
45    Le,
46    /// `>`
47    Gt,
48    /// `>=`
49    Ge,
50}
51
52impl ColumnOp {
53    /// The `VelesQL` operator token.
54    pub(crate) fn as_sql(self) -> &'static str {
55        match self {
56            Self::Eq => "=",
57            Self::Ne => "!=",
58            Self::Lt => "<",
59            Self::Le => "<=",
60            Self::Gt => ">",
61            Self::Ge => ">=",
62        }
63    }
64}
65
66/// A structured predicate over a memory's metadata column, for the fused
67/// vector+`ColumnStore` recall
68/// [`MemoryService::recall_where`](crate::service::MemoryService::recall_where).
69/// Unlike the exact-match filter on
70/// [`MemoryService::recall`](crate::service::MemoryService::recall), this supports
71/// ranges and comparisons (e.g. `timestamp >= …`), so temporal and numeric facets
72/// become queryable, not just equal-matchable.
73#[derive(Debug, Clone, Deserialize, JsonSchema)]
74pub struct ColumnFilter {
75    /// Metadata field name (alphanumeric/underscore).
76    pub field: String,
77    /// Comparison operator.
78    pub op: ColumnOp,
79    /// Value to compare against (numbers, strings, booleans).
80    pub value: Value,
81}
82
83/// A node in an [`Explanation`] subgraph.
84#[derive(Debug, Clone, Serialize, JsonSchema)]
85pub struct MemoryNode {
86    /// Stable id of the memory.
87    pub id: u64,
88    /// Stored fact content.
89    pub content: String,
90    /// Distance in hops from the seed memory (the seed is hop `0`).
91    pub hop: usize,
92}
93
94/// A typed edge in an [`Explanation`] subgraph.
95#[derive(Debug, Clone, Serialize, JsonSchema)]
96pub struct MemoryEdge {
97    /// Source memory id.
98    pub from: u64,
99    /// Target memory id.
100    pub to: u64,
101    /// Relationship label.
102    pub relation: String,
103}
104
105/// The connected answer to a `why` question: the best-matching seed memory plus
106/// everything reachable from it within a hop budget. This connected subgraph is
107/// the differentiator — it surfaces related memories a purely vector recall is
108/// blind to (no textual similarity required).
109#[derive(Debug, Clone, Default, Serialize, JsonSchema)]
110pub struct Explanation {
111    /// Memories in the subgraph, seed first.
112    pub nodes: Vec<MemoryNode>,
113    /// Typed edges connecting the nodes.
114    pub edges: Vec<MemoryEdge>,
115}