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
This commit is contained in:
Claude committed 2026-10-16 06:54:35 +00:00
1 parent db1f20c0a4
commit 92e9adee7d
15 files changed
+1416 -25

No files matched your search

+39
View File
@@ -2843,6 +2843,45 @@ async fn job_arriere_plan_par_la_facade() {
assert_eq!(jobs.running(), 0);
}
/// Lot 46 : `recall` et `recall_search` sont exécutés par la façade sur
/// l'archive du Go (rien par /tool/exec).
#[tokio::test]
async fn recall_par_la_facade() {
let calls = sse(&[
serde_json::json!({"choices":[{"delta":{"tool_calls":[
{"index":0,"id":"c1","type":"function","function":{"name":"recall","arguments":"{\"id\":\"R2 \"}"}},
{"index":1,"id":"c2","type":"function","function":{"name":"recall_search","arguments":"{\"query\":\"Gratin\"}"}}
]}}]}),
serde_json::json!({"choices":[{"delta":{},"finish_reason":"tool_calls"}]}),
]);
let (url, seen) = fake_engine(vec![calls, answer("rappelé")]).await;
let (svc, go, _stop, _t) = service_with(Some(&url), |_| {}).await;
*go.recall_blocks.lock().unwrap() = vec![
serde_json::json!({"id":"r1","seq":1,"label":"web_read: gratin","role":"tool","content":"recette du gratin"}),
serde_json::json!({"id":"r2","seq":2,"label":"bash: ls","role":"tool","content":"a.go\nb.go"}),
];
svc.send(serde_json::json!({"message": "rappelle", "cid": "r"}))
.await
.unwrap();
until_done(&svc, 1).await;
assert!(go.tool_log.lock().unwrap().is_empty(), "{:?}", go.tool_log);
let seen = seen.lock().unwrap().clone();
let tools: Vec<String> = seen[1].1["messages"]
.as_array()
.unwrap()
.iter()
.filter(|m| m["role"] == "tool")
.map(|m| m["content"].as_str().unwrap().to_string())
.collect();
assert_eq!(
tools,
[
"[r2 — bash: ls, rappelé verbatim]\na.go\nb.go",
"Blocs trouvés (rappelle le bon avec recall(id)) :\n- r1 — web_read: gratin\n recette du gratin",
]
);
}
/// Lot 45 : `see_image` et `web_screenshot` sont exécutés par la façade
/// (rien par le Go) : sans projecteur, l'image n'est pas lue ; une capture
/// sans url est refusée avant Playwright.