Files
Loki/rust/loki-rs/tests/recall.rs
T
Claude 92e9adee7d Lot 46 : outils recall et recall_search exécutés par la façade
Les deux outils qui rappellent un bloc archivé par la compaction ne passent
plus par /tool/exec du Go : src/recall.rs porte toolRecall et
toolRecallSearch (id tolérant comme recallIDToSeq, mots-clés en minuscules
rune par rune comme strings.ToLower, score libellé double et plus récent
d'abord, limites, extraits et textes du Go). L'archive reste au Go, seul à
tenir la séquence de la base, qui la sert à la façade
(GET /internal/v1/recall/block?seq=N et /internal/v1/recall/blocks, blocs
chiffrés sous une mémoire verrouillée exclus) ; ses propres outils passent
par les mêmes lectures. Go injoignable : l'appel passe par lui.

Parité : journal de 41 appels sur une archive de 35 blocs tiré du Go
(parity_recall_test.go → tests/parity_recall/recall.json), rejoué par
tests/recall.rs ; un tour de la façade qui rappelle et cherche
(tests/lot6.rs) ; TestRecallInterneLecture côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 06:54:35 +00:00

59 lines
2.3 KiB
Rust

//! Lot 46 : outils `recall` et `recall_search` de l'agent exécutés par la
//! façade, rejoués sur le journal du Go (`internal/loki/parity_recall_test.go`
//! → `tests/parity_recall/recall.json`) : la même archive, servie par le faux
//! Go comme le vrai la sert, les mêmes appels, les mêmes textes.
mod common;
use common::fakego;
use loki_rs::store::Store;
use serde_json::{Map, Value};
#[tokio::test]
async fn recall_comme_le_go() {
let fx: Value =
serde_json::from_str(include_str!("parity_recall/recall.json")).expect("journal");
let tmp = tempfile::tempdir().unwrap();
let (_, go, _stop) = fakego::start(tmp.path().join("home")).await;
*go.recall_blocks.lock().unwrap() = fx["blocks"].as_array().unwrap().clone();
let backend = go.backend.lock().unwrap().unwrap();
let store = Store::new(backend, fakego::JETON.into());
for st in fx["steps"].as_array().unwrap() {
let name = st["name"].as_str().unwrap();
let args: Map<String, Value> = serde_json::from_value(st["args"].clone()).unwrap();
let got = loki_rs::recall::exec(&store, st["tool"].as_str().unwrap(), &args)
.await
.expect("le Go répond");
assert_eq!(got, st["result"].as_str().unwrap(), "{name}");
}
// Une recherche lit l'archive une fois ; un id mal formé ne la lit pas.
let log = go.recall_log.lock().unwrap().clone();
assert!(log.contains(&"block 7".to_string()), "{log:?}");
assert!(!log.iter().any(|l| l == "block "), "{log:?}");
}
/// Go injoignable : `None`, l'appel passe alors par lui (`/tool/exec`).
#[tokio::test]
async fn go_injoignable() {
let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
drop(l);
let store = Store::new(addr, "x".into());
let mut args = Map::new();
args.insert("id".into(), Value::from("r1"));
assert_eq!(loki_rs::recall::exec(&store, "recall", &args).await, None);
let mut args = Map::new();
args.insert("query".into(), Value::from("train"));
assert_eq!(
loki_rs::recall::exec(&store, "recall_search", &args).await,
None
);
// Les refus avant lecture, eux, ne demandent rien au Go.
assert_eq!(
loki_rs::recall::exec(&store, "recall", &Map::new())
.await
.as_deref(),
Some("[erreur] id manquant")
);
}