Skip to main content

remem/api/
types.rs

1use std::collections::BTreeMap;
2use std::sync::{Arc, Mutex};
3
4use serde::{ser::SerializeStruct, Deserialize, Serialize};
5
6#[derive(Clone, Copy, Default)]
7pub struct DbState;
8
9#[derive(Clone, Default)]
10pub(super) struct StatusCache {
11    pub(super) entry: Arc<Mutex<Option<StatusCacheEntry>>>,
12}
13
14#[derive(Clone)]
15pub(super) struct StatusCacheEntry {
16    pub(super) generated_at_epoch: i64,
17    pub(super) payload: serde_json::Value,
18}
19
20#[derive(Deserialize, Default)]
21pub(super) struct StatusParams {
22    pub refresh: Option<bool>,
23}
24
25#[derive(Deserialize)]
26pub(super) struct SearchParams {
27    pub query: Option<String>,
28    pub project: Option<String>,
29    #[serde(rename = "type")]
30    pub memory_type: Option<String>,
31    pub limit: Option<i64>,
32    pub offset: Option<i64>,
33    pub include_stale: Option<bool>,
34    pub include_suppressed: Option<bool>,
35    pub branch: Option<String>,
36    pub multi_hop: Option<bool>,
37    pub explain: Option<bool>,
38}
39
40#[derive(Serialize)]
41pub(super) struct SearchResponse {
42    pub data: Vec<MemoryItem>,
43    pub meta: Meta,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub multi_hop: Option<MultiHopInfo>,
46    /// Raw archive hits attached as fallback when curated memories are sparse.
47    /// Only present when the underlying service returned non-empty raw_hits.
48    #[serde(skip_serializing_if = "Vec::is_empty")]
49    pub raw_hits: Vec<RawHitItem>,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub raw_hits_error: Option<String>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub explain: Option<crate::retrieval::search::SearchExplain>,
54}
55
56#[derive(Serialize)]
57pub(super) struct RawHitItem {
58    pub id: i64,
59    pub session_id: String,
60    pub project: String,
61    pub role: String,
62    pub preview: String,
63    pub source: String,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub branch: Option<String>,
66    pub created_at_epoch: i64,
67}
68
69#[derive(Serialize)]
70pub(super) struct MultiHopInfo {
71    pub hops: u8,
72    pub entities_discovered: Vec<String>,
73}
74
75#[derive(Serialize)]
76pub(super) struct MemoryItem {
77    pub id: i64,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub version: Option<i64>,
80    pub title: String,
81    pub content: String,
82    pub memory_type: String,
83    pub project: String,
84    pub scope: String,
85    pub status: String,
86    pub staleness: crate::memory::MemoryStalenessLabel,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub topic_key: Option<String>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub branch: Option<String>,
91    pub created_at_epoch: i64,
92    pub updated_at_epoch: i64,
93}
94
95#[derive(Serialize)]
96pub(super) struct Meta {
97    pub count: usize,
98    pub has_more: bool,
99    pub limit: i64,
100    pub offset: i64,
101}
102
103#[derive(Serialize)]
104pub(super) struct ErrorResponse {
105    pub error: ErrorDetail,
106}
107
108#[derive(Serialize)]
109pub(super) struct ErrorDetail {
110    pub code: String,
111    pub message: String,
112}
113
114#[derive(Serialize)]
115pub(super) struct CapabilitiesResponse {
116    pub version: &'static str,
117    pub schema_version: i64,
118    pub api_version: u16,
119    pub features: CapabilitiesFeatures,
120    pub endpoints: BTreeMap<&'static str, &'static str>,
121}
122
123#[derive(Serialize)]
124pub(super) struct CapabilitiesFeatures {
125    pub health: bool,
126    pub status: bool,
127    pub stats: bool,
128    pub search: bool,
129    pub search_explain: bool,
130    pub memory_list: bool,
131    pub memory_detail: bool,
132    pub save_memory: bool,
133    pub memory_archive: bool,
134    pub memory_restore: bool,
135    pub memory_delete: bool,
136    pub candidate_rows: bool,
137    pub candidate_filters: bool,
138    pub candidate_review: bool,
139    pub candidate_detail: bool,
140    pub candidate_evidence: bool,
141    pub candidate_review_safe: bool,
142    pub observations: bool,
143    pub sessions: bool,
144    pub workstreams: bool,
145    pub events: bool,
146    pub tasks: bool,
147    pub graph: bool,
148    pub user_recall: bool,
149    pub user_recall_usage_policy: bool,
150}
151
152#[derive(Serialize)]
153pub(super) struct HealthResponse {
154    pub ok: bool,
155    pub version: &'static str,
156    pub api_version: u16,
157    pub schema_version: i64,
158}
159
160#[derive(Deserialize)]
161pub(super) struct SaveMemoryRequest {
162    pub text: String,
163    #[serde(default)]
164    pub title: Option<String>,
165    #[serde(default)]
166    pub project: Option<String>,
167    #[serde(default)]
168    pub session_id: Option<String>,
169    #[serde(default)]
170    pub host: Option<String>,
171    #[serde(default)]
172    pub topic_key: Option<String>,
173    #[serde(default)]
174    pub memory_type: Option<String>,
175    #[serde(default)]
176    pub files: Option<Vec<String>>,
177    #[serde(default)]
178    pub scope: Option<String>,
179    #[serde(default)]
180    pub reference_time_epoch: Option<i64>,
181    #[serde(default)]
182    pub created_at_epoch: Option<i64>,
183    #[serde(default)]
184    pub branch: Option<String>,
185    #[serde(default)]
186    pub local_path: Option<String>,
187    #[serde(default)]
188    pub local_copy_enabled: Option<bool>,
189    #[serde(default)]
190    pub claim_enabled: Option<bool>,
191    #[serde(default)]
192    pub claim_source: Option<String>,
193    #[serde(default)]
194    pub acknowledge_pattern: Option<String>,
195}
196
197#[derive(Deserialize)]
198pub(super) struct UserRecallRequest {
199    pub query: String,
200    #[serde(default)]
201    pub project: Option<String>,
202    #[serde(default)]
203    pub cwd: Option<String>,
204    #[serde(default)]
205    pub task_intent: Option<String>,
206    #[serde(default)]
207    pub current_files: Vec<String>,
208    #[serde(default)]
209    pub host: Option<String>,
210    #[serde(default)]
211    pub owner_scope: Option<String>,
212    #[serde(default)]
213    pub owner_key: Option<String>,
214    #[serde(default)]
215    pub state_keys: Vec<String>,
216    #[serde(default)]
217    pub include_sensitive: bool,
218    #[serde(default)]
219    pub include_suppressed: bool,
220    #[serde(default)]
221    pub limit: Option<i64>,
222    #[serde(default)]
223    pub budget_chars: Option<usize>,
224}
225
226#[derive(Serialize)]
227pub(super) struct SaveMemoryResponse {
228    pub id: i64,
229    pub status: String,
230    pub memory_type: String,
231    pub project: String,
232    pub scope: String,
233    pub topic_key: Option<String>,
234    pub branch: Option<String>,
235    pub operation: String,
236    pub created_at_epoch: i64,
237    pub reference_time_epoch: i64,
238    pub updated_at_epoch: i64,
239    pub upserted: bool,
240    pub local_copy: LocalCopyResponse,
241    pub local_status: String,
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub local_path: Option<String>,
244    pub claim_status: String,
245    pub claim_id: Option<i64>,
246    pub claim_error: Option<String>,
247    pub next_step: SaveMemoryNextStepResponse,
248}
249
250#[derive(Serialize)]
251pub(super) struct LocalCopyResponse {
252    pub status: String,
253    pub path: Option<String>,
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub reason: Option<String>,
256}
257
258#[derive(Serialize)]
259pub(super) struct SaveMemoryNextStepResponse {
260    pub tool: String,
261    pub ids: Vec<i64>,
262    pub source: String,
263    pub reason: String,
264}
265
266#[derive(Deserialize)]
267pub(super) struct ShowParams {
268    pub id: i64,
269    pub include_suppressed: Option<bool>,
270}
271
272#[derive(Deserialize)]
273pub(super) struct MemoryDetailParams {
274    pub include_suppressed: Option<bool>,
275}
276// ===== remem-web 只读端点类型 =====
277
278#[derive(Deserialize)]
279pub(super) struct ListParams {
280    pub project: Option<String>,
281    #[serde(rename = "type")]
282    pub memory_type: Option<String>,
283    pub scope: Option<String>,
284    pub status: Option<String>,
285    pub branch: Option<String>,
286    pub q: Option<String>,
287    pub include_suppressed: Option<bool>,
288    pub limit: Option<i64>,
289    pub offset: Option<i64>,
290}
291
292pub(super) struct ListMeta {
293    pub count: usize,
294    pub total: i64,
295    pub limit: i64,
296    pub offset: i64,
297}
298
299impl Serialize for ListMeta {
300    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301    where
302        S: serde::Serializer,
303    {
304        let count = i64::try_from(self.count).unwrap_or(i64::MAX);
305        let consumed = self.offset.saturating_add(count);
306        let has_more = consumed < self.total;
307        let next_offset = has_more.then_some(consumed);
308
309        let mut state = serializer.serialize_struct("ListMeta", 6)?;
310        state.serialize_field("count", &self.count)?;
311        state.serialize_field("total", &self.total)?;
312        state.serialize_field("limit", &self.limit)?;
313        state.serialize_field("offset", &self.offset)?;
314        state.serialize_field("has_more", &has_more)?;
315        state.serialize_field("next_offset", &next_offset)?;
316        state.end()
317    }
318}
319
320#[derive(Serialize)]
321pub(super) struct ListResponse<T: Serialize> {
322    pub data: Vec<T>,
323    pub meta: ListMeta,
324}
325
326#[derive(Deserialize)]
327pub(super) struct CandidateParams {
328    pub project: Option<String>,
329    pub status: Option<String>,
330    #[serde(rename = "type")]
331    pub memory_type: Option<String>,
332    pub block_reason: Option<String>,
333    pub topic_key: Option<String>,
334    pub contains: Option<String>,
335    pub min_confidence: Option<f64>,
336    pub older_than_days: Option<i64>,
337    pub limit: Option<i64>,
338    pub offset: Option<i64>,
339}
340
341#[derive(Deserialize)]
342pub(super) struct BlockedParams {
343    pub project: Option<String>,
344}
345
346#[derive(Serialize)]
347pub(super) struct BlockedReasonItem {
348    #[serde(skip_serializing_if = "Option::is_none")]
349    pub reason: Option<String>,
350    pub pending: i64,
351    pub example_ids: Vec<i64>,
352}
353
354#[derive(Serialize)]
355pub(super) struct CandidateItem {
356    pub id: i64,
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub project: Option<String>,
359    pub memory_type: String,
360    pub text: String,
361    pub scope: String,
362    pub confidence: f64,
363    pub risk_class: String,
364    pub review_status: String,
365    pub evidence_count: i64,
366    pub created_at_epoch: i64,
367}
368
369#[derive(Deserialize)]
370pub(super) struct CandidateEditRequest {
371    #[serde(default)]
372    pub scope: Option<String>,
373    #[serde(default, rename = "memory_type")]
374    pub memory_type: Option<String>,
375    #[serde(default)]
376    pub topic_key: Option<String>,
377    #[serde(default)]
378    pub text: Option<String>,
379}
380
381#[derive(Deserialize, Default)]
382pub(super) struct CandidateApproveRequest {
383    #[serde(default)]
384    pub acknowledge_pattern: Option<String>,
385}
386
387#[derive(Serialize)]
388pub(super) struct CandidateReviewResponse {
389    pub candidate_id: i64,
390    pub status: String,
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub memory_id: Option<i64>,
393}
394
395#[derive(Serialize)]
396pub(super) struct CandidateDetailResponse {
397    pub data: CandidateDetailItem,
398    pub evidence: Vec<CandidateEvidenceItem>,
399    pub decision: CandidateReviewDecision,
400}
401
402#[derive(Serialize)]
403pub(super) struct CandidateDetailItem {
404    pub id: i64,
405    pub project: Option<String>,
406    pub scope: String,
407    pub memory_type: String,
408    pub topic_key: String,
409    pub text: String,
410    pub source_kind: Option<String>,
411    pub source_project: Option<String>,
412    pub target_project: Option<String>,
413    pub owner_scope: Option<String>,
414    pub owner_key: Option<String>,
415    pub topic_domain: Option<String>,
416    pub routing_confidence: Option<f64>,
417    pub routing_reason: Option<String>,
418    pub context_class: Option<String>,
419    pub confidence: f64,
420    pub risk_class: String,
421    pub review_status: String,
422    pub auto_promote_block_reason: Option<String>,
423    pub source_trust_class: String,
424    pub quarantine_pattern_id: Option<String>,
425    pub quarantine_pattern_version: Option<i64>,
426    pub version: i64,
427    pub created_at_epoch: i64,
428    pub updated_at_epoch: i64,
429}
430
431#[derive(Serialize)]
432pub(super) struct CandidateEvidenceItem {
433    pub source_kind: &'static str,
434    pub source_id: i64,
435    pub event_type: Option<String>,
436    pub role: Option<String>,
437    pub tool_name: Option<String>,
438    pub created_at_epoch: Option<i64>,
439    pub summary: String,
440    pub preview: String,
441    pub provenance_status: String,
442    pub redacted: bool,
443}
444
445#[derive(Serialize)]
446pub(super) struct CandidateReviewDecision {
447    pub can_review: bool,
448    pub blocked_reasons: Vec<String>,
449}
450
451#[derive(Debug, Clone, Deserialize)]
452#[serde(deny_unknown_fields)]
453pub(super) struct CandidateSafeApproveRequest {
454    pub reason: String,
455    pub expected_version: i64,
456    pub idempotency_key: String,
457    #[serde(default)]
458    pub acknowledge_pattern: Option<String>,
459}
460
461#[derive(Debug, Clone, Deserialize)]
462#[serde(deny_unknown_fields)]
463pub(super) struct CandidateSafeRejectRequest {
464    pub reason: String,
465    pub expected_version: i64,
466    pub idempotency_key: String,
467}
468
469#[derive(Debug, Clone, Deserialize)]
470#[serde(deny_unknown_fields)]
471pub(super) struct CandidateSafeEditRequest {
472    pub reason: String,
473    pub expected_version: i64,
474    pub idempotency_key: String,
475    #[serde(default)]
476    pub scope: Option<String>,
477    #[serde(default, rename = "memory_type")]
478    pub memory_type: Option<String>,
479    #[serde(default)]
480    pub topic_key: Option<String>,
481    #[serde(default)]
482    pub text: Option<String>,
483}
484
485#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
486pub(super) struct CandidateSafeReviewResponse {
487    pub response_schema_version: i64,
488    pub operation_id: String,
489    pub audit_id: i64,
490    pub candidate_id: i64,
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub memory_id: Option<i64>,
493    pub action: String,
494    pub before_status: String,
495    pub after_status: String,
496    pub version: i64,
497    pub occurred_at_epoch: i64,
498    pub replayed: bool,
499}
500
501#[derive(Debug, Clone, Deserialize)]
502#[serde(deny_unknown_fields)]
503pub(super) struct MemorySafeGovernanceRequest {
504    pub reason: String,
505    pub expected_version: i64,
506    pub idempotency_key: String,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
510pub(super) struct MemorySafeGovernanceResponse {
511    pub response_schema_version: i64,
512    pub operation_id: String,
513    pub audit_id: i64,
514    pub memory_id: i64,
515    pub action: String,
516    pub before_status: String,
517    pub after_status: String,
518    pub version: i64,
519    pub occurred_at_epoch: i64,
520    pub replayed: bool,
521}
522
523#[derive(Serialize)]
524pub(super) struct SafeMutationErrorResponse {
525    pub error: SafeMutationErrorDetail,
526}
527
528#[derive(Serialize)]
529pub(super) struct SafeMutationErrorDetail {
530    pub code: String,
531    pub message: String,
532    #[serde(skip_serializing_if = "Option::is_none")]
533    pub operation_id: Option<String>,
534}
535
536#[derive(Deserialize)]
537pub(super) struct GraphParams {
538    pub project: Option<String>,
539    pub include_suppressed: Option<bool>,
540    pub limit: Option<i64>,
541}
542
543#[derive(Serialize)]
544pub(super) struct GraphNodeItem {
545    pub id: i64,
546    pub name: String,
547    pub entity_type: Option<String>,
548    pub mention_count: i64,
549    pub mems: Vec<i64>,
550}
551
552#[derive(Serialize)]
553pub(super) struct GraphEdgeItem {
554    pub a: i64,
555    pub b: i64,
556    pub w: i64,
557}
558
559#[derive(Serialize)]
560pub(super) struct GraphResponse {
561    pub nodes: Vec<GraphNodeItem>,
562    pub edges: Vec<GraphEdgeItem>,
563}
564
565#[derive(Serialize)]
566pub(super) struct MemoryEdgeItem {
567    pub id: i64,
568    pub edge_type: String,
569    pub from_memory_id: Option<i64>,
570    pub to_memory_id: Option<i64>,
571    pub confidence: Option<f64>,
572}
573
574#[derive(Serialize)]
575pub(super) struct MemoryDetailResponse {
576    #[serde(flatten)]
577    pub memory: MemoryItem,
578    pub entities: Vec<String>,
579    pub edges: Vec<MemoryEdgeItem>,
580}
581
582#[derive(Serialize)]
583pub(super) struct TypeCount {
584    pub memory_type: String,
585    pub count: i64,
586}
587
588#[derive(Serialize)]
589pub(super) struct StatsResponse {
590    pub active_memories: i64,
591    pub total_memories: i64,
592    pub pending_candidates: i64,
593    pub captured_events: i64,
594    pub pending_extraction_tasks: i64,
595    pub ai_calls: i64,
596    pub ai_cost_usd: f64,
597    pub ai_total_tokens: i64,
598    pub type_distribution: Vec<TypeCount>,
599}