Skip to main content

laser_wire/
memory.rs

1use serde::{Deserialize, Serialize};
2
3/// A memory record as it rides the memory topic: the on-log audit of one scope's
4/// changes. This is the shape the SDK's memory facade writes and the shape a
5/// deployment folds into the versioned key-value read view, so both sides agree
6/// on the bytes without the reader importing the SDK. Ids are the text form of
7/// the SDK's memory id, kinds the snake-case kind word, so the encoding is
8/// byte-identical to the SDK's own record. Runtime-free and portable, like the
9/// rest of the wire contract.
10#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
12pub enum MemoryRecord {
13    /// A remembered item: its id, kind word, and body.
14    Item {
15        id: String,
16        kind: String,
17        body: Vec<u8>,
18        #[serde(default, skip_serializing_if = "Option::is_none")]
19        origin: Option<crate::graph::SourceRef>,
20        #[serde(default, skip_serializing_if = "Option::is_none")]
21        producer: Option<crate::graph::ProducerInfo>,
22    },
23    /// A tombstone removing an item from recall. With `conversation`, it
24    /// removes the item only when the item was remembered in that conversation.
25    Forget {
26        target: String,
27        #[serde(default, skip_serializing_if = "Option::is_none")]
28        conversation: Option<String>,
29    },
30    /// A feedback signal reweighting an item's recall rank. With
31    /// `conversation`, it counts only for an item remembered in that
32    /// conversation.
33    Feedback {
34        target: String,
35        weight: f32,
36        #[serde(default, skip_serializing_if = "Option::is_none")]
37        conversation: Option<String>,
38    },
39}
40
41#[cfg(all(test, feature = "cbor"))]
42mod tests {
43    use super::*;
44    use crate::framing::{decode_named, encode_named};
45
46    #[test]
47    fn given_each_variant_when_round_tripped_then_should_preserve_fields() {
48        for record in [
49            MemoryRecord::Item {
50                id: "01KWM3K3XEP3NP5TN850J17YBP".to_owned(),
51                kind: "fact".to_owned(),
52                body: b"auth is slow".to_vec(),
53                origin: None,
54                producer: None,
55            },
56            MemoryRecord::Forget {
57                target: "01KWM3K3XEP3NP5TN850J17YBP".to_owned(),
58                conversation: Some("01KWM3K3XEP3NP5TN850J17YBQ".to_owned()),
59            },
60            MemoryRecord::Feedback {
61                target: "01KWM3K3XEP3NP5TN850J17YBP".to_owned(),
62                weight: 1.5,
63                conversation: None,
64            },
65        ] {
66            let bytes = encode_named(&record).expect("encodes");
67            let back: MemoryRecord = decode_named(&bytes).expect("decodes");
68            assert_eq!(back, record);
69        }
70    }
71}