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::SearchExplainDetails>,
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 classification: crate::truth::MemoryVisibilityClass,
87    pub current_context_eligible: bool,
88    pub classification_reason: String,
89    pub staleness: crate::memory::MemoryStalenessLabel,
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub topic_key: Option<String>,
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub branch: Option<String>,
94    pub created_at_epoch: i64,
95    pub updated_at_epoch: i64,
96}
97
98#[derive(Serialize)]
99pub(super) struct Meta {
100    pub count: usize,
101    pub has_more: bool,
102    pub limit: i64,
103    pub offset: i64,
104}
105
106#[derive(Serialize)]
107pub(super) struct ErrorResponse {
108    pub error: ErrorDetail,
109}
110
111#[derive(Serialize)]
112pub(super) struct ErrorDetail {
113    pub code: String,
114    pub message: String,
115}
116
117#[derive(Serialize)]
118pub(super) struct CapabilitiesResponse {
119    pub version: &'static str,
120    pub schema_version: i64,
121    pub api_version: u16,
122    pub features: CapabilitiesFeatures,
123    pub endpoints: BTreeMap<&'static str, &'static str>,
124}
125
126#[derive(Serialize)]
127pub(super) struct CapabilitiesFeatures {
128    pub health: bool,
129    pub status: bool,
130    pub stats: bool,
131    pub search: bool,
132    pub search_explain: bool,
133    pub memory_list: bool,
134    pub memory_detail: bool,
135    pub save_memory: bool,
136    pub memory_archive: bool,
137    pub memory_restore: bool,
138    pub memory_delete: bool,
139    pub candidate_rows: bool,
140    pub candidate_filters: bool,
141    pub candidate_review: bool,
142    pub candidate_detail: bool,
143    pub candidate_evidence: bool,
144    pub candidate_review_safe: bool,
145    pub observations: bool,
146    pub sessions: bool,
147    pub session_activity: bool,
148    pub workstreams: bool,
149    pub events: bool,
150    pub tasks: bool,
151    pub graph: bool,
152    pub user_recall: bool,
153    pub user_recall_usage_policy: bool,
154}
155
156#[derive(Serialize)]
157pub(super) struct HealthResponse {
158    pub ok: bool,
159    pub version: &'static str,
160    pub api_version: u16,
161    pub schema_version: i64,
162}
163
164#[derive(Deserialize)]
165#[serde(deny_unknown_fields)]
166pub(super) struct SaveMemoryRequest {
167    pub text: String,
168    #[serde(default)]
169    pub title: Option<String>,
170    #[serde(default)]
171    pub project: Option<String>,
172    #[serde(default)]
173    pub session_id: Option<String>,
174    #[serde(default)]
175    pub host: Option<String>,
176    #[serde(default)]
177    pub topic_key: Option<String>,
178    #[serde(default)]
179    pub memory_type: Option<String>,
180    #[serde(default)]
181    pub files: Option<Vec<String>>,
182    #[serde(default)]
183    pub scope: Option<String>,
184    #[serde(default)]
185    pub reference_time_epoch: Option<i64>,
186    #[serde(default)]
187    pub created_at_epoch: Option<i64>,
188    #[serde(default)]
189    pub branch: Option<String>,
190    #[serde(default)]
191    pub local_path: Option<String>,
192    #[serde(default)]
193    pub local_copy_enabled: Option<bool>,
194    #[serde(default)]
195    pub claim_enabled: Option<bool>,
196    #[serde(default)]
197    pub claim_source: Option<String>,
198    #[serde(default)]
199    pub idempotency_key: Option<String>,
200}
201
202#[derive(Deserialize)]
203pub(super) struct UserRecallRequest {
204    pub query: String,
205    #[serde(default)]
206    pub project: Option<String>,
207    #[serde(default)]
208    pub cwd: Option<String>,
209    #[serde(default)]
210    pub task_intent: Option<String>,
211    #[serde(default)]
212    pub current_files: Vec<String>,
213    #[serde(default)]
214    pub host: Option<String>,
215    #[serde(default)]
216    pub owner_scope: Option<String>,
217    #[serde(default)]
218    pub owner_key: Option<String>,
219    #[serde(default)]
220    pub state_keys: Vec<String>,
221    #[serde(default)]
222    pub include_sensitive: bool,
223    #[serde(default)]
224    pub include_suppressed: bool,
225    #[serde(default)]
226    pub limit: Option<i64>,
227    #[serde(default)]
228    pub budget_chars: Option<usize>,
229}
230
231#[derive(Serialize)]
232pub(super) struct SaveMemoryResponse {
233    pub id: i64,
234    pub status: String,
235    pub memory_type: String,
236    pub project: String,
237    pub scope: String,
238    pub topic_key: Option<String>,
239    pub branch: Option<String>,
240    pub operation: String,
241    pub created_at_epoch: i64,
242    pub reference_time_epoch: i64,
243    pub updated_at_epoch: i64,
244    pub upserted: bool,
245    pub local_copy: LocalCopyResponse,
246    pub local_status: String,
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub local_path: Option<String>,
249    pub claim_status: String,
250    pub claim_id: Option<i64>,
251    pub claim_error: Option<String>,
252    pub next_step: SaveMemoryNextStepResponse,
253}
254
255#[derive(Serialize)]
256pub(super) struct LocalCopyResponse {
257    pub status: String,
258    pub path: Option<String>,
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub reason: Option<String>,
261}
262
263#[derive(Serialize)]
264pub(super) struct SaveMemoryNextStepResponse {
265    pub tool: String,
266    pub ids: Vec<i64>,
267    pub source: String,
268    pub reason: String,
269}
270
271#[derive(Deserialize)]
272pub(super) struct ShowParams {
273    pub id: i64,
274    pub include_suppressed: Option<bool>,
275}
276
277#[derive(Deserialize)]
278pub(super) struct MemoryDetailParams {
279    pub include_suppressed: Option<bool>,
280}
281// ===== remem-web 只读端点类型 =====
282
283#[derive(Deserialize)]
284pub(super) struct ListParams {
285    pub project: Option<String>,
286    #[serde(rename = "type")]
287    pub memory_type: Option<String>,
288    pub scope: Option<String>,
289    pub status: Option<String>,
290    pub branch: Option<String>,
291    pub q: Option<String>,
292    pub include_suppressed: Option<bool>,
293    pub limit: Option<i64>,
294    pub offset: Option<i64>,
295}
296
297pub(super) struct ListMeta {
298    pub count: usize,
299    pub total: i64,
300    pub limit: i64,
301    pub offset: i64,
302}
303
304impl Serialize for ListMeta {
305    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
306    where
307        S: serde::Serializer,
308    {
309        let count = i64::try_from(self.count).unwrap_or(i64::MAX);
310        let consumed = self.offset.saturating_add(count);
311        let has_more = consumed < self.total;
312        let next_offset = has_more.then_some(consumed);
313
314        let mut state = serializer.serialize_struct("ListMeta", 6)?;
315        state.serialize_field("count", &self.count)?;
316        state.serialize_field("total", &self.total)?;
317        state.serialize_field("limit", &self.limit)?;
318        state.serialize_field("offset", &self.offset)?;
319        state.serialize_field("has_more", &has_more)?;
320        state.serialize_field("next_offset", &next_offset)?;
321        state.end()
322    }
323}
324
325#[derive(Serialize)]
326pub(super) struct ListResponse<T: Serialize> {
327    pub data: Vec<T>,
328    pub meta: ListMeta,
329}
330
331#[derive(Deserialize)]
332pub(super) struct CandidateParams {
333    pub project: Option<String>,
334    pub status: Option<String>,
335    #[serde(rename = "type")]
336    pub memory_type: Option<String>,
337    pub block_reason: Option<String>,
338    pub topic_key: Option<String>,
339    pub contains: Option<String>,
340    pub min_confidence: Option<f64>,
341    pub older_than_days: Option<i64>,
342    pub limit: Option<i64>,
343    pub offset: Option<i64>,
344}
345
346#[derive(Deserialize)]
347pub(super) struct BlockedParams {
348    pub project: Option<String>,
349}
350
351#[derive(Serialize)]
352pub(super) struct BlockedReasonItem {
353    #[serde(skip_serializing_if = "Option::is_none")]
354    pub reason: Option<String>,
355    pub pending: i64,
356    pub example_ids: Vec<i64>,
357}
358
359#[derive(Serialize)]
360pub(super) struct CandidateItem {
361    pub id: i64,
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub project: Option<String>,
364    pub memory_type: String,
365    pub text: String,
366    pub scope: String,
367    pub confidence: f64,
368    pub risk_class: String,
369    pub review_status: String,
370    pub evidence_count: i64,
371    pub created_at_epoch: i64,
372}
373
374#[derive(Deserialize)]
375pub(super) struct CandidateEditRequest {
376    #[serde(default)]
377    pub scope: Option<String>,
378    #[serde(default, rename = "memory_type")]
379    pub memory_type: Option<String>,
380    #[serde(default)]
381    pub topic_key: Option<String>,
382    #[serde(default)]
383    pub text: Option<String>,
384}
385
386#[derive(Deserialize, Default)]
387pub(super) struct CandidateApproveRequest {
388    #[serde(default)]
389    pub acknowledge_pattern: Option<String>,
390}
391
392#[derive(Serialize)]
393pub(super) struct CandidateReviewResponse {
394    pub candidate_id: i64,
395    pub status: String,
396    #[serde(skip_serializing_if = "Option::is_none")]
397    pub memory_id: Option<i64>,
398}
399
400#[derive(Serialize)]
401pub(super) struct CandidateDetailResponse {
402    pub data: CandidateDetailItem,
403    pub evidence: Vec<CandidateEvidenceItem>,
404    #[serde(skip_serializing_if = "Option::is_none")]
405    pub provenance: Option<CandidateDreamProvenance>,
406    pub decision: CandidateReviewDecision,
407}
408
409#[derive(Serialize)]
410pub(super) struct CandidateDreamProvenance {
411    pub kind: &'static str,
412    pub review_token: Option<String>,
413    pub authorized_supersede_ids: Vec<i64>,
414    pub artifacts: Vec<CandidateDreamArtifact>,
415}
416
417#[derive(Serialize)]
418pub(super) struct CandidateDreamArtifact {
419    pub artifact_id: i64,
420    pub version: i64,
421    pub project: String,
422    pub cluster_signature: String,
423    pub member_ids: Vec<i64>,
424    pub decision_kind: String,
425    pub decision_ids: Vec<i64>,
426    pub decision_payload_sha256: String,
427    pub intended_superseded_ids: Vec<i64>,
428    pub generated_topic_key: Option<String>,
429    pub generated_memory_type: Option<String>,
430    pub generated_title: Option<String>,
431    pub generated_content: Option<String>,
432    pub generated_field: String,
433    pub pattern_id: String,
434    pub pattern_version: i64,
435    pub source_operation: String,
436    pub source_trust_class: String,
437    pub occurrence_count: i64,
438    pub created_at_epoch: i64,
439    pub updated_at_epoch: i64,
440}
441
442#[derive(Serialize)]
443pub(super) struct CandidateDetailItem {
444    pub id: i64,
445    pub project: Option<String>,
446    pub scope: String,
447    pub memory_type: String,
448    pub topic_key: String,
449    pub text: String,
450    pub source_kind: Option<String>,
451    pub source_project: Option<String>,
452    pub target_project: Option<String>,
453    pub owner_scope: Option<String>,
454    pub owner_key: Option<String>,
455    pub topic_domain: Option<String>,
456    pub routing_confidence: Option<f64>,
457    pub routing_reason: Option<String>,
458    pub context_class: Option<String>,
459    pub confidence: f64,
460    pub risk_class: String,
461    pub review_status: String,
462    pub auto_promote_block_reason: Option<String>,
463    pub source_trust_class: String,
464    pub quarantine_pattern_id: Option<String>,
465    pub quarantine_pattern_version: Option<i64>,
466    pub version: i64,
467    pub created_at_epoch: i64,
468    pub updated_at_epoch: i64,
469}
470
471#[derive(Serialize)]
472pub(super) struct CandidateEvidenceItem {
473    pub source_kind: &'static str,
474    pub source_id: i64,
475    pub event_type: Option<String>,
476    pub role: Option<String>,
477    pub tool_name: Option<String>,
478    pub created_at_epoch: Option<i64>,
479    pub summary: String,
480    pub preview: String,
481    pub provenance_status: String,
482    pub redacted: bool,
483}
484
485#[derive(Serialize)]
486pub(super) struct CandidateReviewDecision {
487    pub can_review: bool,
488    pub blocked_reasons: Vec<String>,
489    pub actions: CandidateReviewActionDecisions,
490}
491
492#[derive(Serialize)]
493pub(super) struct CandidateReviewActionDecisions {
494    pub approve: CandidateReviewActionDecision,
495    pub reject: CandidateReviewActionDecision,
496    pub edit: CandidateReviewActionDecision,
497}
498
499#[derive(Serialize)]
500pub(super) struct CandidateReviewActionDecision {
501    pub allowed: bool,
502    pub blocked_reasons: Vec<String>,
503}
504
505#[derive(Debug, Clone, Deserialize)]
506#[serde(deny_unknown_fields)]
507pub(super) struct CandidateSafeApproveRequest {
508    pub reason: String,
509    pub expected_version: i64,
510    pub idempotency_key: String,
511    #[serde(default)]
512    pub acknowledge_pattern: Option<String>,
513    #[serde(default)]
514    pub acknowledge_dream_review_token: Option<String>,
515}
516
517#[derive(Debug, Clone, Deserialize)]
518#[serde(deny_unknown_fields)]
519pub(super) struct CandidateSafeRejectRequest {
520    pub reason: String,
521    pub expected_version: i64,
522    pub idempotency_key: String,
523}
524
525#[derive(Debug, Clone, Deserialize)]
526#[serde(deny_unknown_fields)]
527pub(super) struct CandidateSafeEditRequest {
528    pub reason: String,
529    pub expected_version: i64,
530    pub idempotency_key: String,
531    #[serde(default)]
532    pub scope: Option<String>,
533    #[serde(default, rename = "memory_type")]
534    pub memory_type: Option<String>,
535    #[serde(default)]
536    pub topic_key: Option<String>,
537    #[serde(default)]
538    pub text: Option<String>,
539}
540
541#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
542pub(super) struct CandidateSafeReviewResponse {
543    pub response_schema_version: i64,
544    pub operation_id: String,
545    pub audit_id: i64,
546    pub candidate_id: i64,
547    #[serde(skip_serializing_if = "Option::is_none")]
548    pub memory_id: Option<i64>,
549    pub action: String,
550    pub before_status: String,
551    pub after_status: String,
552    pub version: i64,
553    pub occurred_at_epoch: i64,
554    pub replayed: bool,
555}
556
557#[derive(Debug, Clone, Deserialize)]
558#[serde(deny_unknown_fields)]
559pub(super) struct MemorySafeGovernanceRequest {
560    pub reason: String,
561    pub expected_version: i64,
562    pub idempotency_key: String,
563}
564
565#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
566pub(super) struct MemorySafeGovernanceResponse {
567    pub response_schema_version: i64,
568    pub operation_id: String,
569    pub audit_id: i64,
570    pub memory_id: i64,
571    pub action: String,
572    pub before_status: String,
573    pub after_status: String,
574    pub version: i64,
575    pub occurred_at_epoch: i64,
576    pub replayed: bool,
577}
578
579#[derive(Serialize)]
580pub(super) struct SafeMutationErrorResponse {
581    pub error: SafeMutationErrorDetail,
582}
583
584#[derive(Serialize)]
585pub(super) struct SafeMutationErrorDetail {
586    pub code: String,
587    pub message: String,
588    #[serde(skip_serializing_if = "Option::is_none")]
589    pub operation_id: Option<String>,
590}
591
592#[derive(Deserialize)]
593pub(super) struct GraphParams {
594    pub project: Option<String>,
595    pub include_suppressed: Option<bool>,
596    pub limit: Option<i64>,
597}
598
599#[derive(Serialize)]
600pub(super) struct GraphNodeItem {
601    pub id: i64,
602    pub name: String,
603    pub entity_type: Option<String>,
604    pub mention_count: i64,
605    pub mems: Vec<i64>,
606}
607
608#[derive(Serialize)]
609pub(super) struct GraphEdgeItem {
610    pub a: i64,
611    pub b: i64,
612    pub w: i64,
613}
614
615#[derive(Serialize)]
616pub(super) struct GraphResponse {
617    pub nodes: Vec<GraphNodeItem>,
618    pub edges: Vec<GraphEdgeItem>,
619}
620
621#[derive(Serialize)]
622pub(super) struct MemoryEdgeItem {
623    pub id: i64,
624    pub edge_type: String,
625    pub from_memory_id: Option<i64>,
626    pub to_memory_id: Option<i64>,
627    pub confidence: Option<f64>,
628}
629
630#[derive(Serialize)]
631pub(super) struct MemoryDetailResponse {
632    #[serde(flatten)]
633    pub memory: MemoryItem,
634    pub entities: Vec<String>,
635    pub edges: Vec<MemoryEdgeItem>,
636}
637
638#[derive(Serialize)]
639pub(super) struct TypeCount {
640    pub memory_type: String,
641    pub count: i64,
642}
643
644#[derive(Serialize)]
645pub(super) struct StatsResponse {
646    pub active_memories: i64,
647    pub total_memories: i64,
648    pub pending_candidates: i64,
649    pub captured_events: i64,
650    pub pending_extraction_tasks: i64,
651    pub ai_calls: i64,
652    pub ai_cost_usd: f64,
653    pub ai_total_tokens: i64,
654    pub type_distribution: Vec<TypeCount>,
655}