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    /// 只在这些笔记(notes 的记录 id)内检索。
134    ///
135    /// 空表示不限定,行为与不传该字段完全一致。给出时与 namespace / scopes / tags
136    /// 叠加(与门),且没有笔记归属的记录(记忆、实体、关系、事件)不参与命中。
137    #[serde(default)]
138    pub note_ids: Vec<i64>,
139}
140
141fn default_scopes() -> Vec<String> { vec![public_scope()] }
142impl Default for ReadFilter {
143    fn default() -> Self { Self { namespace: default_namespace(), scopes: default_scopes(), tags: vec![], note_ids: vec![] } }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct PageRequest {
148    #[serde(default)]
149    pub filter: ReadFilter,
150    #[serde(default = "default_limit")]
151    pub limit: usize,
152    #[serde(default)]
153    pub after: Option<String>,
154}
155
156impl Default for PageRequest {
157    fn default() -> Self { Self { filter: ReadFilter::default(), limit: default_limit(), after: None } }
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct Page<T> { pub items: Vec<T>, pub next_cursor: Option<String> }
162
163/// 一次已提交的写入。派生索引不在写入路径上追平——由使用方在合适时机调用
164/// `update_index` 一趟索引完;期间读取走自愈兜底,保证仍查得到。
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct WriteReceipt<T> {
167    pub value: T,
168    pub revision: i64,
169}
170
171/// 一次检索实际落在了哪一档。多档降级要求「结果为什么变差」可被宿主读到。
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
173#[serde(rename_all = "snake_case")]
174pub enum Degrade {
175    /// 该 namespace 关闭了向量化:只走全文。
176    NamespaceDisabled,
177    /// 目标空间没有注册嵌入回调:只走全文。
178    NoEmbedder,
179    /// 嵌入回调调用失败:只走全文。
180    EmbedFailed,
181    /// 该领域的向量还没补齐:只走全文,补齐后自动放行。
182    VectorNotReady,
183    /// 重排回调调用失败或返回不符:按融合分排序。
184    RerankFailed,
185    /// 全文派生索引不可用:退到 SQLite 直查。
186    TextIndexUnavailable,
187}
188
189/// 一次检索的可观测信息:走了哪几条路、是否重排、重排截断了几条、落在了哪一档。
190#[derive(Debug, Clone, Default, Serialize, Deserialize)]
191pub struct SearchDiagnostics {
192    pub text_used: bool,
193    pub vector_used: bool,
194    pub reranked: bool,
195    /// 实际送入重排回调的候选数。
196    pub rerank_candidates: usize,
197    /// 因重排回调声明的 `max_candidates` / `max_tokens_total` 预算而未送入重排的候选数。
198    pub rerank_truncated: usize,
199    #[serde(default)]
200    pub degraded: Vec<Degrade>,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct HealthReport {
205    pub schema_version: i64,
206    pub revision: i64,
207    pub indexed_revision: i64,
208    pub record_count: usize,
209    pub index_document_count: usize,
210    pub pending_index_updates: usize,
211    pub sqlite_integrity: String,
212    pub foreign_key_errors: usize,
213    pub counts: std::collections::BTreeMap<String, usize>,
214    /// 已注册嵌入回调的向量空间。
215    pub embedder_spaces: Vec<String>,
216    pub reranker_registered: bool,
217    /// 最近观察到的降级档位,去重后保留少量。
218    pub last_degraded: Vec<Degrade>,
219}
220
221/// 一次查询限定在哪一列命中。`All` 是既有行为:正文列与名字列任一命中都算,
222/// 名字命中另加固定加权;其余取值只认那一列,用于把「书名」「内容」「目录」分开取。
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
224#[serde(rename_all = "snake_case")]
225pub enum MatchField {
226    /// 正文列 + 名字列(默认)。
227    #[default]
228    All,
229    /// 只正文列。
230    Text,
231    /// 只名字列(实体规范名、笔记文件名)。
232    Name,
233    /// 只目录列(笔记所在目录段),供「书名块不够时」的路径兜底使用。
234    Path,
235}
236
237/// 全文索引重建的进度快照。宿主可在重建进行时从另一线程轮询:
238/// `active` 为真表示正在重建,`processed`/`total` 是已索引记录数与待索引记录总数。
239#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
240pub struct RebuildProgressReport {
241    pub active: bool,
242    pub processed: u64,
243    pub total: u64,
244}