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