Skip to main content

p_memory/
types.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3use std::fmt;
4
5pub type Metadata = Map<String, Value>;
6pub fn default_namespace() -> String { "default".into() }
7pub fn public_scope() -> String { "public".into() }
8pub fn default_limit() -> usize { 50 }
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum RecordKind { Memory, Entity, Relation, Event, Note, Chunk }
13
14impl RecordKind {
15    pub fn as_str(self) -> &'static str {
16        match self {
17            Self::Memory => "memory", Self::Entity => "entity", Self::Relation => "relation",
18            Self::Event => "event", Self::Note => "note", Self::Chunk => "chunk",
19        }
20    }
21    /// 固定枚举的整数编码,直接落库,不建字典表。
22    pub fn code(self) -> i64 {
23        match self {
24            Self::Memory => 0, Self::Entity => 1, Self::Relation => 2,
25            Self::Event => 3, Self::Note => 4, Self::Chunk => 5,
26        }
27    }
28    pub fn from_code(code: i64) -> Option<Self> {
29        Some(match code {
30            0 => Self::Memory, 1 => Self::Entity, 2 => Self::Relation,
31            3 => Self::Event, 4 => Self::Note, 5 => Self::Chunk, _ => return None,
32        })
33    }
34}
35
36impl fmt::Display for RecordKind {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) }
38}
39
40/// 内部记录键:单一自增整数,与任何外部 ID 无关。
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
42pub struct RecordKey {
43    pub id: i64,
44}
45
46impl RecordKey {
47    pub fn new(id: i64) -> Self { Self { id } }
48    pub(crate) fn index_key(&self) -> String { self.id.to_string() }
49}
50
51/// Source locations are snapshots; they do not change when note chunks are replaced.
52#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
53pub struct Evidence {
54    pub source: String,
55    #[serde(default)]
56    pub source_revision: Option<String>,
57    #[serde(default)]
58    pub chunk_id: Option<i64>,
59    /// `offset` 为 1 起始的起始行,`limit` 为行数。结束行 = offset + limit - 1。
60    #[serde(default)]
61    pub offset: Option<usize>,
62    #[serde(default)]
63    pub limit: Option<usize>,
64    #[serde(default)]
65    pub quote: String,
66    #[serde(default)]
67    pub metadata: Metadata,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct RecordInput {
72    /// None 表示新建;Some(id) 表示更新既有记录。
73    #[serde(default)]
74    pub id: Option<i64>,
75    #[serde(default = "default_namespace")]
76    pub namespace: String,
77    #[serde(default = "public_scope")]
78    pub scope: String,
79    #[serde(default)]
80    pub tags: Vec<String>,
81    #[serde(default)]
82    pub evidence: Vec<Evidence>,
83    #[serde(default)]
84    pub metadata: Metadata,
85    #[serde(default)]
86    pub created_at_us: Option<i64>,
87    #[serde(default)]
88    pub updated_at_us: Option<i64>,
89    #[serde(default)]
90    pub expected_revision: Option<i64>,
91}
92
93impl Default for RecordInput {
94    fn default() -> Self {
95        Self { id: None, namespace: default_namespace(), scope: public_scope(), tags: vec![],
96            evidence: vec![], metadata: Metadata::new(), created_at_us: None,
97            updated_at_us: None, expected_revision: None }
98    }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct RecordHeader {
103    pub id: i64,
104    pub namespace: String,
105    pub kind: RecordKind,
106    pub scope: String,
107    pub tags: Vec<String>,
108    pub evidence: Vec<Evidence>,
109    pub metadata: Metadata,
110    pub created_at_us: i64,
111    pub updated_at_us: i64,
112    pub revision: i64,
113}
114
115impl RecordHeader {
116    pub fn key(&self) -> RecordKey { RecordKey { id: self.id } }
117    pub fn as_input(&self) -> RecordInput {
118        RecordInput { id: Some(self.id), namespace: self.namespace.clone(), scope: self.scope.clone(), tags: self.tags.clone(),
119            evidence: self.evidence.clone(), metadata: self.metadata.clone(), created_at_us: Some(self.created_at_us),
120            updated_at_us: None, expected_revision: Some(self.revision) }
121    }
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct ReadFilter {
126    #[serde(default = "default_namespace")]
127    pub namespace: String,
128    #[serde(default = "default_scopes")]
129    pub scopes: Vec<String>,
130    /// All listed tags must match. Tag matching is case-insensitive.
131    #[serde(default)]
132    pub tags: Vec<String>,
133}
134
135fn default_scopes() -> Vec<String> { vec![public_scope()] }
136impl Default for ReadFilter {
137    fn default() -> Self { Self { namespace: default_namespace(), scopes: default_scopes(), tags: vec![] } }
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct PageRequest {
142    #[serde(default)]
143    pub filter: ReadFilter,
144    #[serde(default = "default_limit")]
145    pub limit: usize,
146    #[serde(default)]
147    pub after: Option<String>,
148}
149
150impl Default for PageRequest {
151    fn default() -> Self { Self { filter: ReadFilter::default(), limit: default_limit(), after: None } }
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct Page<T> { pub items: Vec<T>, pub next_cursor: Option<String> }
156
157/// 一次已提交的写入。派生索引不在写入路径上追平——由使用方在合适时机调用
158/// `update_index` 一趟索引完;期间读取走自愈兜底,保证仍查得到。
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct WriteReceipt<T> {
161    pub value: T,
162    pub revision: i64,
163}
164
165/// 一次检索实际落在了哪一档。多档降级要求「结果为什么变差」可被宿主读到。
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
167#[serde(rename_all = "snake_case")]
168pub enum Degrade {
169    /// 该 namespace 关闭了向量化:只走全文。
170    NamespaceDisabled,
171    /// 目标空间没有注册嵌入回调:只走全文。
172    NoEmbedder,
173    /// 嵌入回调调用失败:只走全文。
174    EmbedFailed,
175    /// 该领域的向量还没补齐:只走全文,补齐后自动放行。
176    VectorNotReady,
177    /// 重排回调调用失败或返回不符:按融合分排序。
178    RerankFailed,
179    /// 全文派生索引不可用:退到 SQLite 直查。
180    TextIndexUnavailable,
181}
182
183/// 一次检索的可观测信息:走了哪几条路、是否重排、重排截断了几条、落在了哪一档。
184#[derive(Debug, Clone, Default, Serialize, Deserialize)]
185pub struct SearchDiagnostics {
186    pub text_used: bool,
187    pub vector_used: bool,
188    pub reranked: bool,
189    /// 实际送入重排回调的候选数。
190    pub rerank_candidates: usize,
191    /// 因重排回调声明的 `max_candidates` / `max_tokens_total` 预算而未送入重排的候选数。
192    pub rerank_truncated: usize,
193    #[serde(default)]
194    pub degraded: Vec<Degrade>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct HealthReport {
199    pub schema_version: i64,
200    pub revision: i64,
201    pub indexed_revision: i64,
202    pub record_count: usize,
203    pub index_document_count: usize,
204    pub pending_index_updates: usize,
205    pub sqlite_integrity: String,
206    pub foreign_key_errors: usize,
207    pub counts: std::collections::BTreeMap<String, usize>,
208    /// 已注册嵌入回调的向量空间。
209    pub embedder_spaces: Vec<String>,
210    pub reranker_registered: bool,
211    /// 最近观察到的降级档位,去重后保留少量。
212    pub last_degraded: Vec<Degrade>,
213}
214
215/// 一次查询限定在哪一列命中。`All` 是既有行为:正文列与名字列任一命中都算,
216/// 名字命中另加固定加权;其余取值只认那一列,用于把「书名」「内容」「目录」分开取。
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
218#[serde(rename_all = "snake_case")]
219pub enum MatchField {
220    /// 正文列 + 名字列(默认)。
221    #[default]
222    All,
223    /// 只正文列。
224    Text,
225    /// 只名字列(实体规范名、笔记文件名)。
226    Name,
227    /// 只目录列(笔记所在目录段),供「书名块不够时」的路径兜底使用。
228    Path,
229}
230
231/// 全文索引重建的进度快照。宿主可在重建进行时从另一线程轮询:
232/// `active` 为真表示正在重建,`processed`/`total` 是已索引记录数与待索引记录总数。
233#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
234pub struct RebuildProgressReport {
235    pub active: bool,
236    pub processed: u64,
237    pub total: u64,
238}