Skip to main content

mem_types/
dto.rs

1//! Request and response DTOs compatible with MemOS product API.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Single chat message (user/assistant).
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Message {
9    pub role: String,
10    pub content: String,
11}
12
13/// Add-memory request (MemOS APIADDRequest).
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ApiAddRequest {
16    pub user_id: String,
17    #[serde(default)]
18    pub session_id: Option<String>,
19    #[serde(default)]
20    pub task_id: Option<String>,
21    #[serde(default)]
22    pub writable_cube_ids: Option<Vec<String>>,
23    #[serde(default)]
24    pub mem_cube_id: Option<String>,
25    #[serde(default = "default_async_mode")]
26    pub async_mode: String,
27    #[serde(default)]
28    pub messages: Option<Vec<Message>>,
29    #[serde(default)]
30    pub memory_content: Option<String>,
31    #[serde(default)]
32    pub chat_history: Option<Vec<Message>>,
33    #[serde(default)]
34    pub custom_tags: Option<Vec<String>>,
35    #[serde(default)]
36    pub info: Option<HashMap<String, serde_json::Value>>,
37    /// Optional graph relations to existing memories while adding this new memory.
38    #[serde(default)]
39    pub relations: Option<Vec<AddMemoryRelation>>,
40    #[serde(default)]
41    pub is_feedback: bool,
42}
43
44fn default_async_mode() -> String {
45    "sync".to_string()
46}
47
48impl ApiAddRequest {
49    /// Resolve cube ids to write to: writable_cube_ids or [user_id].
50    pub fn writable_cube_ids(&self) -> Vec<String> {
51        if let Some(ref ids) = self.writable_cube_ids {
52            if !ids.is_empty() {
53                return ids.clone();
54            }
55        }
56        if let Some(ref id) = self.mem_cube_id {
57            return vec![id.clone()];
58        }
59        vec![self.user_id.clone()]
60    }
61
62    /// Content to store: from messages or memory_content.
63    pub fn content_to_store(&self) -> Option<String> {
64        if let Some(ref msgs) = self.messages {
65            if !msgs.is_empty() {
66                let parts: Vec<String> = msgs
67                    .iter()
68                    .map(|m| format!("{}: {}", m.role, m.content))
69                    .collect();
70                return Some(parts.join("\n"));
71            }
72        }
73        self.memory_content.clone()
74    }
75}
76
77/// Search-memory request (MemOS APISearchRequest).
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct ApiSearchRequest {
80    pub query: String,
81    pub user_id: String,
82    #[serde(default)]
83    pub readable_cube_ids: Option<Vec<String>>,
84    #[serde(default)]
85    pub mem_cube_id: Option<String>,
86    #[serde(default = "default_top_k")]
87    pub top_k: u32,
88    #[serde(default)]
89    pub session_id: Option<String>,
90    #[serde(default)]
91    pub relativity: f64,
92    #[serde(default)]
93    pub include_preference: bool,
94    #[serde(default)]
95    pub pref_top_k: u32,
96    #[serde(default)]
97    pub filter: Option<HashMap<String, serde_json::Value>>,
98}
99
100fn default_top_k() -> u32 {
101    10
102}
103
104impl ApiSearchRequest {
105    /// Resolve cube ids to read from: readable_cube_ids or mem_cube_id or [user_id].
106    pub fn readable_cube_ids(&self) -> Vec<String> {
107        if let Some(ref ids) = self.readable_cube_ids {
108            if !ids.is_empty() {
109                return ids.clone();
110            }
111        }
112        if let Some(ref id) = self.mem_cube_id {
113            return vec![id.clone()];
114        }
115        vec![self.user_id.clone()]
116    }
117}
118
119/// Base response envelope.
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct BaseResponse<T> {
122    #[serde(default = "default_code")]
123    pub code: i32,
124    pub message: String,
125    #[serde(default)]
126    pub data: Option<T>,
127}
128
129fn default_code() -> i32 {
130    200
131}
132
133/// Add-memory response (MemOS MemoryResponse).
134pub type MemoryResponse = BaseResponse<Vec<serde_json::Value>>;
135
136/// Single memory item as returned in search (id, memory, metadata).
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct MemoryItem {
139    pub id: String,
140    pub memory: String,
141    #[serde(default)]
142    pub metadata: HashMap<String, serde_json::Value>,
143}
144
145/// One bucket of memories (e.g. WorkingMemory, LongTermMemory).
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct MemoryBucket {
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub name: Option<String>,
150    pub memories: Vec<MemoryItem>,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub total_nodes: Option<usize>,
153}
154
155/// Search result data: text_mem and optional pref_mem.
156#[derive(Debug, Clone, Default, Serialize, Deserialize)]
157pub struct SearchResponseData {
158    #[serde(default)]
159    pub text_mem: Vec<MemoryBucket>,
160    #[serde(default)]
161    pub pref_mem: Vec<MemoryBucket>,
162}
163
164/// Search response (MemOS SearchResponse).
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct SearchResponse {
167    #[serde(default = "default_code")]
168    pub code: i32,
169    pub message: String,
170    #[serde(default)]
171    pub data: Option<SearchResponseData>,
172}
173
174/// Request to update an existing memory (partial fields).
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct UpdateMemoryRequest {
177    pub memory_id: String,
178    pub user_id: String,
179    #[serde(default)]
180    pub mem_cube_id: Option<String>,
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub memory: Option<String>,
183    #[serde(default)]
184    pub metadata: Option<HashMap<String, serde_json::Value>>,
185}
186
187/// Request to forget (soft or hard delete) a memory.
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct ForgetMemoryRequest {
190    pub memory_id: String,
191    pub user_id: String,
192    #[serde(default)]
193    pub mem_cube_id: Option<String>,
194    /// If true, soft delete (mark tombstone); else hard delete.
195    #[serde(default)]
196    pub soft: bool,
197}
198
199/// Response for update_memory / forget_memory (same envelope as add).
200pub type UpdateMemoryResponse = BaseResponse<Vec<serde_json::Value>>;
201pub type ForgetMemoryResponse = BaseResponse<Vec<serde_json::Value>>;
202
203/// Optional relation spec used by add-memory request.
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct AddMemoryRelation {
206    /// Existing memory id to connect with the newly added memory.
207    pub memory_id: String,
208    pub relation: String,
209    /// Edge direction relative to the newly added memory node.
210    /// `outbound`: new -> memory_id; `inbound`: memory_id -> new; `both`: write both edges.
211    #[serde(default)]
212    pub direction: GraphDirection,
213    #[serde(default)]
214    pub metadata: HashMap<String, serde_json::Value>,
215}
216
217/// Request to get a single memory by id.
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct GetMemoryRequest {
220    pub memory_id: String,
221    pub user_id: String,
222    #[serde(default)]
223    pub mem_cube_id: Option<String>,
224    /// If true, return memories marked tombstone (soft-deleted). Default false.
225    #[serde(default)]
226    pub include_deleted: bool,
227}
228
229/// Response for get_memory: optional MemoryItem.
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct GetMemoryResponse {
232    #[serde(default = "default_code")]
233    pub code: i32,
234    pub message: String,
235    #[serde(default)]
236    pub data: Option<MemoryItem>,
237}
238
239/// Internal memory node (id, memory, metadata, optional embedding).
240#[derive(Debug, Clone)]
241pub struct MemoryNode {
242    pub id: String,
243    pub memory: String,
244    pub metadata: HashMap<String, serde_json::Value>,
245    pub embedding: Option<Vec<f32>>,
246}
247
248/// Graph edge between two memory nodes.
249#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct MemoryEdge {
251    pub id: String,
252    pub from: String,
253    pub to: String,
254    pub relation: String,
255    #[serde(default)]
256    pub metadata: HashMap<String, serde_json::Value>,
257}
258
259/// Neighbor item for graph traversal response.
260#[derive(Debug, Clone)]
261pub struct GraphNeighbor {
262    pub edge: MemoryEdge,
263    pub node: MemoryNode,
264}
265
266/// API request for graph neighbor query.
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct GraphNeighborsRequest {
269    pub memory_id: String,
270    pub user_id: String,
271    #[serde(default)]
272    pub mem_cube_id: Option<String>,
273    #[serde(default)]
274    pub relation: Option<String>,
275    #[serde(default)]
276    pub direction: GraphDirection,
277    #[serde(default = "default_graph_limit")]
278    pub limit: u32,
279    /// Opaque cursor token from previous response for pagination.
280    #[serde(default)]
281    pub cursor: Option<String>,
282    #[serde(default)]
283    pub include_embedding: bool,
284    #[serde(default)]
285    pub include_deleted: bool,
286}
287
288fn default_graph_limit() -> u32 {
289    10
290}
291
292/// API response item for one graph neighbor.
293#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct GraphNeighborItem {
295    pub edge: MemoryEdge,
296    pub memory: MemoryItem,
297}
298
299/// API response payload for graph neighbor query.
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct GraphNeighborsData {
302    pub items: Vec<GraphNeighborItem>,
303    #[serde(skip_serializing_if = "Option::is_none")]
304    pub next_cursor: Option<String>,
305}
306
307/// API response for graph neighbor query.
308pub type GraphNeighborsResponse = BaseResponse<GraphNeighborsData>;
309
310/// Internal shortest-path result.
311#[derive(Debug, Clone)]
312pub struct GraphPath {
313    pub node_ids: Vec<String>,
314    pub edges: Vec<MemoryEdge>,
315}
316
317/// API request for graph path query.
318#[derive(Debug, Clone, Serialize, Deserialize)]
319pub struct GraphPathRequest {
320    pub source_memory_id: String,
321    pub target_memory_id: String,
322    pub user_id: String,
323    #[serde(default)]
324    pub mem_cube_id: Option<String>,
325    #[serde(default)]
326    pub relation: Option<String>,
327    #[serde(default)]
328    pub direction: GraphDirection,
329    #[serde(default = "default_graph_max_depth")]
330    pub max_depth: u32,
331    #[serde(default)]
332    pub include_deleted: bool,
333}
334
335fn default_graph_max_depth() -> u32 {
336    6
337}
338
339/// API response payload for graph path query.
340#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct GraphPathData {
342    pub hops: u32,
343    pub nodes: Vec<MemoryItem>,
344    pub edges: Vec<MemoryEdge>,
345}
346
347/// API response for graph path query.
348pub type GraphPathResponse = BaseResponse<GraphPathData>;
349
350/// API request for multi-path graph query.
351#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct GraphPathsRequest {
353    pub source_memory_id: String,
354    pub target_memory_id: String,
355    pub user_id: String,
356    #[serde(default)]
357    pub mem_cube_id: Option<String>,
358    #[serde(default)]
359    pub relation: Option<String>,
360    #[serde(default)]
361    pub direction: GraphDirection,
362    #[serde(default = "default_graph_max_depth")]
363    pub max_depth: u32,
364    #[serde(default = "default_graph_top_k_paths")]
365    pub top_k_paths: u32,
366    #[serde(default)]
367    pub include_deleted: bool,
368}
369
370fn default_graph_top_k_paths() -> u32 {
371    3
372}
373
374/// API response for multi-path query.
375pub type GraphPathsResponse = BaseResponse<Vec<GraphPathData>>;
376
377/// Traversal direction for graph neighbor query.
378#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
379#[serde(rename_all = "snake_case")]
380pub enum GraphDirection {
381    Outbound,
382    Inbound,
383    Both,
384}
385
386impl Default for GraphDirection {
387    fn default() -> Self {
388        Self::Outbound
389    }
390}
391
392/// Scope for memory (MemOS: WorkingMemory, LongTermMemory, UserMemory).
393#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
394pub enum MemoryScope {
395    WorkingMemory,
396    LongTermMemory,
397    UserMemory,
398}
399
400impl MemoryScope {
401    pub fn as_str(self) -> &'static str {
402        match self {
403            MemoryScope::WorkingMemory => "WorkingMemory",
404            MemoryScope::LongTermMemory => "LongTermMemory",
405            MemoryScope::UserMemory => "UserMemory",
406        }
407    }
408}
409
410impl std::fmt::Display for MemoryScope {
411    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412        f.write_str(self.as_str())
413    }
414}