1use serde::{Deserialize, Serialize};
7use serde_json::{Map, Value};
8
9use crate::domain::memory::{
10 MemoryAggregateRequest, MemoryFindRequest, MemoryRecallRequest, MemoryScope,
11};
12
13pub const SALIENCE_RUBRIC: [&str; 4] = [
15 "none: memory would not change the outcome",
16 "background: memory is optional color",
17 "relevant: memory should be read or written",
18 "blocking: the next step is wrong if memory is skipped",
19];
20
21pub const MEMORY_ESCALATE_TOPIC: &str = "locus.memory.escalate";
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum MemoryAction {
28 Ignore,
29 Recall,
30 Find,
31 Persist,
32 Explain,
33 Aggregate,
34}
35
36impl MemoryAction {
37 pub fn as_str(self) -> &'static str {
38 match self {
39 Self::Ignore => "ignore",
40 Self::Recall => "recall",
41 Self::Find => "find",
42 Self::Persist => "persist",
43 Self::Explain => "explain",
44 Self::Aggregate => "aggregate",
45 }
46 }
47
48 pub fn parse(value: &str) -> Option<Self> {
49 match value {
50 "ignore" => Some(Self::Ignore),
51 "recall" => Some(Self::Recall),
52 "find" => Some(Self::Find),
53 "persist" => Some(Self::Persist),
54 "explain" => Some(Self::Explain),
55 "aggregate" => Some(Self::Aggregate),
56 _ => None,
57 }
58 }
59
60 pub fn topic(self) -> &'static str {
62 match self {
63 Self::Ignore => "locus.memory.ignore",
64 Self::Recall => "locus.memory.recall",
65 Self::Find => "locus.memory.find",
66 Self::Persist => "locus.memory.persist",
67 Self::Explain => "locus.memory.explain",
68 Self::Aggregate => "locus.memory.aggregate",
69 }
70 }
71
72 pub fn is_read(self) -> bool {
73 matches!(
74 self,
75 Self::Recall | Self::Find | Self::Explain | Self::Aggregate
76 )
77 }
78
79 pub fn all() -> &'static [Self] {
80 &[
81 Self::Ignore,
82 Self::Recall,
83 Self::Find,
84 Self::Persist,
85 Self::Explain,
86 Self::Aggregate,
87 ]
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(rename_all = "snake_case")]
94pub enum MemoryReflexKind {
95 Dispatch,
97 Ignore,
99 Escalate,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
105#[serde(rename_all = "snake_case")]
106pub enum ReflexGate {
107 Accepted,
108 BlankStimulus,
109 BelowSalience,
110 LowConfidence,
111 PropositionDisagreement,
112 System2Required,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct MemoryPropositions {
119 pub references_prior: f32,
120 pub should_persist: f32,
121 pub needs_system2: f32,
122}
123
124impl Default for MemoryPropositions {
125 fn default() -> Self {
126 Self {
127 references_prior: 0.0,
128 should_persist: 0.0,
129 needs_system2: 0.0,
130 }
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136#[serde(rename_all = "camelCase")]
137pub struct MemoryPersistHint {
138 pub text: String,
139 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub role: Option<String>,
141}
142
143#[derive(Debug, Clone, Default, Serialize, Deserialize)]
145#[serde(rename_all = "camelCase")]
146pub struct MemoryStimulus {
147 #[serde(default, skip_serializing_if = "Option::is_none")]
148 pub id: Option<String>,
149 pub text: String,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub role: Option<String>,
152 #[serde(default)]
153 pub scope: MemoryScope,
154 #[serde(default)]
155 pub metadata: Map<String, Value>,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
160#[serde(rename_all = "camelCase")]
161pub struct ReflexPolicy {
162 pub min_choice_confidence: f32,
164 pub min_salience: f32,
166 pub read_floor: f32,
168 pub write_floor: f32,
170 pub escalate_at: f32,
172 pub page_limit: usize,
174}
175
176impl Default for ReflexPolicy {
177 fn default() -> Self {
178 Self {
179 min_choice_confidence: 0.55,
180 min_salience: 0.34,
181 read_floor: 0.45,
182 write_floor: 0.45,
183 escalate_at: 0.70,
184 page_limit: 8,
185 }
186 }
187}
188
189#[derive(Debug, Clone)]
195pub struct MemoryReflex {
196 pub schema_version: String,
197 pub stimulus_id: String,
198 pub stimulus_text: String,
199 pub role: Option<String>,
200 pub scope: MemoryScope,
201 pub kind: MemoryReflexKind,
202 pub action: MemoryAction,
203 pub topic: String,
204 pub salience: f32,
205 pub salience_label: String,
206 pub salience_confidence: f32,
207 pub confidence: f32,
208 pub propositions: MemoryPropositions,
209 pub gate: ReflexGate,
210 pub companions: Vec<MemoryAction>,
211 pub recall: Option<MemoryRecallRequest>,
212 pub find: Option<MemoryFindRequest>,
213 pub aggregate: Option<MemoryAggregateRequest>,
214 pub persist: Option<MemoryPersistHint>,
215 pub decider_id: String,
216 pub checkpoint: Option<String>,
217 pub metadata: Map<String, Value>,
218}