Skip to main content

public_01_remember_recall/
01_remember_recall.rs

1#[path = "../support/utils.rs"]
2mod _utils;
3
4use _utils::{boxed_error, cleanup_run, create_client, new_run_id, print_summary, require};
5use mubit_sdk::{RecallOptions, RememberOptions, TransportMode};
6use serde_json::json;
7use std::error::Error;
8use std::time::Instant;
9
10#[tokio::main(flavor = "current_thread")]
11async fn main() -> Result<(), Box<dyn Error>> {
12    let name = "public_01_remember_recall";
13    let started = Instant::now();
14    let client = create_client().await?;
15    let run_id = new_run_id("public_01_remember_recall");
16    client.set_run_id(Some(run_id.clone()));
17    client.set_transport(TransportMode::Http);
18
19    let mut passed = true;
20    let mut detail = "validated helper remember -> recall flow with upsert_key idempotency".to_string();
21    let mut metrics = json!({});
22
23    let scenario = async {
24        let upsert_key = format!("remember-recall-{run_id}");
25        let mut remember = RememberOptions::new(
26            "Before retrying cache rebuilds, verify the token signer has rotated cleanly.",
27        );
28        remember.run_id = Some(run_id.clone());
29        remember.agent_id = Some("helper-example".to_string());
30        remember.intent = Some("lesson".to_string());
31        remember.lesson_type = Some("success".to_string());
32        remember.lesson_scope = Some("session".to_string());
33        remember.upsert_key = Some(upsert_key.clone());
34        remember.metadata = Some(json!({ "source": "rust-helper-example" }));
35
36        let remembered = client.remember(remember).await?;
37        require(
38            remembered
39                .get("done")
40                .and_then(|value| value.as_bool())
41                .unwrap_or(false),
42            format!("remember should wait for ingest completion: {remembered}"),
43        )?;
44
45        let mut updated = RememberOptions::new(
46            "Before retrying cache rebuilds, verify the signer rotation completed and invalidate stale cache tokens.",
47        );
48        updated.run_id = Some(run_id.clone());
49        updated.agent_id = Some("helper-example".to_string());
50        updated.intent = Some("lesson".to_string());
51        updated.lesson_type = Some("success".to_string());
52        updated.lesson_scope = Some("session".to_string());
53        updated.upsert_key = Some(upsert_key.clone());
54        updated.metadata = Some(json!({ "source": "rust-helper-example", "revision": 2 }));
55
56        let updated_response = client.remember(updated).await?;
57        require(
58            updated_response
59                .get("done")
60                .and_then(|value| value.as_bool())
61                .unwrap_or(false),
62            format!("upsert remember should wait for ingest completion: {updated_response}"),
63        )?;
64
65        let mut recall = RecallOptions::new("What should I verify before retrying cache rebuilds?");
66        recall.run_id = Some(run_id.clone());
67        recall.entry_types = vec!["lesson".to_string(), "fact".to_string()];
68        let answer = client.recall(recall).await?;
69        let evidence = answer
70            .get("evidence")
71            .and_then(|value| value.as_array())
72            .ok_or_else(|| boxed_error(format!("evidence missing: {answer}")))?;
73        require(!evidence.is_empty(), format!("evidence missing: {answer}"))?;
74        let rendered = evidence
75            .iter()
76            .filter_map(|item| item.get("content").and_then(|value| value.as_str()))
77            .collect::<Vec<_>>()
78            .join(" ");
79        require(
80            rendered.contains("invalidate stale cache tokens"),
81            format!("upserted lesson should be recalled: {answer}"),
82        )?;
83        require(
84            answer
85                .get("final_answer")
86                .and_then(|value| value.as_str())
87                .is_some(),
88            format!("final answer missing: {answer}"),
89        )?;
90
91        metrics = json!({
92            "run_id": run_id,
93            "evidence_count": evidence.len(),
94            "confidence": answer.get("confidence").cloned().unwrap_or(serde_json::Value::Null),
95            "upsert_key": upsert_key,
96        });
97
98        Ok::<(), Box<dyn Error>>(())
99    }
100    .await;
101
102    if let Err(err) = scenario {
103        passed = false;
104        detail = err.to_string();
105    }
106
107    let cleanup_ok = cleanup_run(&client, &run_id).await;
108    if !cleanup_ok {
109        passed = false;
110        detail = format!("{detail} | cleanup failures");
111    }
112
113    print_summary(
114        name,
115        passed,
116        &detail,
117        &metrics,
118        started.elapsed().as_secs_f64(),
119        cleanup_ok,
120    );
121
122    if passed {
123        Ok(())
124    } else {
125        Err(boxed_error(detail))
126    }
127}