Skip to main content

cognee_http_server/dto/
remember_entry.rs

1//! Request DTO for `POST /api/v1/remember/entry` (E-02).
2//!
3//! Wire shape mirrors Python's `RememberEntryRequest` model at
4//! `cognee/api/v1/remember/routers/get_remember_router.py:101-113`.
5//!
6//! The `entry` field re-uses [`cognee_models::memory::MemoryEntry`] directly
7//! (the LIB-01 type) — no separate wrapper DTO is needed because that type
8//! already carries:
9//! - the discriminated-union `serde(tag = "type")` shape (`"qa"` /
10//!   `"trace"` / `"feedback"`);
11//! - `serde(rename_all = "camelCase")` on every inner struct;
12//! - per-field `serde(alias = "<snake_form>")` for Python's
13//!   `populate_by_name=True` parity.
14//!
15//! See the round-trip tests at `crates/models/src/memory.rs:142-365`.
16
17use cognee_models::memory::MemoryEntry;
18use serde::Deserialize;
19use utoipa::ToSchema;
20
21/// JSON body for `POST /api/v1/remember/entry`.
22///
23/// Wire is camelCase per Decision 10. snake_case input forms are also
24/// accepted via per-field aliases for compatibility with Python's
25/// `populate_by_name=True`.
26#[derive(Debug, Clone, Deserialize, ToSchema)]
27#[serde(rename_all = "camelCase")]
28pub struct RememberEntryRequestDTO {
29    /// Discriminated union: `{"type": "qa"|"trace"|"feedback", ...}`.
30    /// Type defined in `cognee_models::memory::MemoryEntry`.
31    ///
32    /// The OpenAPI schema is documented as `serde_json::Value` because
33    /// `MemoryEntry` lives in `cognee-models` (not annotated with
34    /// `ToSchema`). Full discriminated-union schema documentation is
35    /// deferred to a follow-up doc-only task.
36    #[schema(value_type = serde_json::Value)]
37    pub entry: MemoryEntry,
38
39    /// Target dataset name. Defaults to `"main_dataset"` to match Python.
40    #[serde(default = "default_dataset_name", alias = "dataset_name")]
41    pub dataset_name: String,
42
43    /// Required session id. Empty strings are rejected by the handler
44    /// with the Python validation envelope (`{"detail":[{"loc":["body",
45    /// "session_id"], "msg":"...", "type":"value_error"}]}`).
46    #[serde(alias = "session_id")]
47    pub session_id: String,
48}
49
50fn default_dataset_name() -> String {
51    "main_dataset".to_string()
52}
53
54#[cfg(test)]
55#[allow(
56    clippy::unwrap_used,
57    clippy::expect_used,
58    reason = "test code — panics are acceptable failures"
59)]
60mod tests {
61    use super::*;
62    use cognee_models::memory::QAEntry;
63
64    #[test]
65    fn parses_camel_case_payload() {
66        let raw = r#"{
67            "entry": {"type": "qa", "question": "Q?", "answer": "A."},
68            "datasetName": "ds",
69            "sessionId": "s1"
70        }"#;
71        let dto: RememberEntryRequestDTO = serde_json::from_str(raw).expect("parse");
72        assert_eq!(dto.dataset_name, "ds");
73        assert_eq!(dto.session_id, "s1");
74        match dto.entry {
75            MemoryEntry::Qa(QAEntry {
76                question, answer, ..
77            }) => {
78                assert_eq!(question, "Q?");
79                assert_eq!(answer, "A.");
80            }
81            other => panic!("expected qa, got {other:?}"),
82        }
83    }
84
85    #[test]
86    fn parses_snake_case_aliases() {
87        let raw = r#"{
88            "entry": {"type": "feedback", "qa_id": "qa-1"},
89            "dataset_name": "ds2",
90            "session_id": "s2"
91        }"#;
92        let dto: RememberEntryRequestDTO = serde_json::from_str(raw).expect("parse");
93        assert_eq!(dto.dataset_name, "ds2");
94        assert_eq!(dto.session_id, "s2");
95        assert!(matches!(dto.entry, MemoryEntry::Feedback(_)));
96    }
97
98    #[test]
99    fn dataset_name_defaults_to_main_dataset() {
100        let raw = r#"{
101            "entry": {"type": "qa", "question": "q", "answer": "a"},
102            "sessionId": "s"
103        }"#;
104        let dto: RememberEntryRequestDTO = serde_json::from_str(raw).expect("parse");
105        assert_eq!(dto.dataset_name, "main_dataset");
106    }
107
108    #[test]
109    fn unknown_entry_type_fails_to_parse() {
110        let raw = r#"{
111            "entry": {"type": "bogus"},
112            "sessionId": "s"
113        }"#;
114        let result: Result<RememberEntryRequestDTO, _> = serde_json::from_str(raw);
115        assert!(result.is_err());
116    }
117}