writerslogic_warm/core/memory_api/
json.rs1use anyhow::Result;
10use serde::de::DeserializeOwned;
11use serde_json::{json, Map, Value};
12
13use super::types::*;
14use super::{Memory, MemoryError};
15
16pub const OPERATIONS: &[&str] = &[
18 "capabilities",
19 "remember",
20 "recall",
21 "explain",
22 "forget",
23 "get",
24 "history",
25 "applyForgetting",
26 "consolidate",
27];
28
29fn parse<T: DeserializeOwned>(value: Value) -> Result<T> {
30 serde_json::from_value(value).map_err(|error| {
31 anyhow::Error::new(MemoryError::Invalid(format!("invalid request: {error}")))
32 })
33}
34
35fn object(value: Value) -> Result<Map<String, Value>> {
36 match value {
37 Value::Object(map) => Ok(map),
38 Value::Null => Ok(Map::new()),
39 _ => Err(MemoryError::Invalid("the request must be a JSON object".into()).into()),
40 }
41}
42
43fn take_string(map: &mut Map<String, Value>, key: &str) -> Result<String> {
44 match map.remove(key) {
45 Some(Value::String(value)) => Ok(value),
46 _ => Err(MemoryError::Invalid(format!("{key} is required and must be a string")).into()),
47 }
48}
49
50fn integral_floats(value: &mut Value) {
53 match value {
54 Value::Number(number) if number.is_f64() => {
55 if let Some(float) = number.as_f64() {
56 if float.fract() == 0.0 && float.abs() < 9_007_199_254_740_992.0 {
57 *number = (float as i64).into();
58 }
59 }
60 }
61 Value::Array(items) => items.iter_mut().for_each(integral_floats),
62 Value::Object(map) => map.values_mut().for_each(integral_floats),
63 _ => {}
64 }
65}
66
67fn dry_run_by_default(request: Value) -> Result<Value> {
69 let mut map = object(request)?;
70 map.entry("dryRun").or_insert(Value::Bool(true));
71 Ok(Value::Object(map))
72}
73
74fn to_value<T: serde::Serialize>(value: &T) -> Result<Value> {
75 Ok(serde_json::to_value(value)?)
76}
77
78impl Memory {
79 pub fn capabilities(&self) -> MemoryCapabilities {
80 MemoryCapabilities {
81 contract_version: 1,
82 profile: "temporal-memory-v1".into(),
83 operations: OPERATIONS.iter().map(|op| (*op).to_owned()).collect(),
84 temporal_queries: true,
85 exact_metadata_filters: true,
86 subject_entity_filters: true,
87 document_pagination: false,
88 committed_error_code: "mutation_committed".into(),
89 }
90 }
91
92 pub fn call(&self, op: &str, mut request: Value) -> Result<Value> {
108 integral_floats(&mut request);
109 match op {
110 "capabilities" => {
111 let request: CapabilitiesRequest = parse(request)?;
112 check!(
113 request.contract_version.is_none_or(|version| version == 1),
114 "unsupported memory contractVersion"
115 );
116 Ok(json!({"capabilities": self.capabilities()}))
117 }
118 "remember" => {
119 let mut map = object(request)?;
120 let inputs: Vec<RememberInput> = match map.remove("facts") {
121 Some(facts) if map.is_empty() => parse(facts)?,
122 Some(_) => {
123 return Err(MemoryError::Invalid(
124 "send either a fact or {\"facts\": [..]}, not both".into(),
125 )
126 .into())
127 }
128 None => vec![parse(Value::Object(map))?],
129 };
130 Ok(json!({ "remembered": self.remember_many(inputs)? }))
131 }
132 "recall" => {
133 let mut map = object(request)?;
134 let query = take_string(&mut map, "query")?;
135 let options: RecallOptions = parse(Value::Object(map))?;
136 Ok(json!({ "results": self.recall(&query, &options)? }))
137 }
138 "explain" => {
139 let mut map = object(request)?;
140 let query = take_string(&mut map, "query")?;
141 let id = take_string(&mut map, "id")?;
142 let options: RecallOptions = parse(Value::Object(map))?;
143 Ok(json!({ "explanation": self.explain(&query, &id, &options)? }))
144 }
145 "forget" => {
146 let mut map = object(request)?;
147 let scope: ForgetScope = parse(map.remove("scope").unwrap_or(Value::Null))?;
148 let options: ForgetOptions = parse(Value::Object(map))?;
149 to_value(&self.forget(&scope, &options)?)
150 }
151 "get" => {
152 let id = take_string(&mut object(request)?, "id")?;
153 Ok(json!({ "fact": self.get(&id)? }))
154 }
155 "history" => {
156 let id = take_string(&mut object(request)?, "id")?;
157 Ok(json!({ "history": self.history(&id)? }))
158 }
159 "applyForgetting" => {
160 let options: ForgetOptions = parse(dry_run_by_default(request)?)?;
161 to_value(&self.apply_forgetting(&options)?)
162 }
163 "consolidate" => {
164 let options: ConsolidateOptions = parse(dry_run_by_default(request)?)?;
165 to_value(&self.consolidate(&options)?)
166 }
167 other => Err(MemoryError::Invalid(format!(
168 "unknown memory operation {other:?}; expected one of {OPERATIONS:?}"
169 ))
170 .into()),
171 }
172 }
173}