mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-22 14:38:12 +02:00
Lot 34 : bloc projet figé (PROJ_SNAPSHOT) dans le tour de la façade
PROJ_SNAPSHOT était le dernier réglage qui gardait la conversation au Go : la façade la tient désormais dans tous les cas (LOKI_CHAT=go reste la porte de sortie). facadeChatBlocker et la décision prise une fois par processus disparaissent. Go (facade_projsnap.go) : - /internal/v1/turn/view, avec la clé et l'agent : tête du tour portant le bloc figé (ou le vivant à rafraîchir), préambule sans date ni dossier, et la décision projsnap (rafraîchir, mise à jour à poser, instantané à ranger, clé et système du tour) ; dry pour le préchauffage ; - /internal/v1/turn/projsnap/refresh : bloc vivant et instantané qu'il devient, pour une compaction en plein tour. Rust : - ChatService::assemble_turn, partagé par le tour et la relance du builder : instantané rangé avec la discussion, mise à jour posée en tête du dernier message utilisateur (prependCtxUpdate), historique nettoyé au rafraîchissement ; - TurnHost::project_refresh après une compaction ou une réduction en plein tour (replaceProjectHead) ; l'instantané posé devient celui de la discussion ; - le préchauffage demande la vue en dry et garde les mises à jour tant que le bloc n'est pas rafraîchi. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
This commit is contained in:
19 files changed
+1063
-171
No files matched your search
@@ -52,6 +52,9 @@ struct Fake {
|
||||
/// a reçu.
|
||||
prewarm_match: Mutex<bool>,
|
||||
sent_keeps: Mutex<Vec<bool>>,
|
||||
/// PROJ_SNAPSHOT : clé posée pour ce tour ; rafraîchissements demandés.
|
||||
snap_on: Mutex<bool>,
|
||||
refreshes: Mutex<usize>,
|
||||
}
|
||||
|
||||
impl Fake {
|
||||
@@ -173,6 +176,13 @@ impl TurnHost for Fake {
|
||||
async fn keep_images(&self) -> Option<i64> {
|
||||
*self.keep.lock().unwrap()
|
||||
}
|
||||
async fn project_refresh(&self, mut msgs: Vec<Message>) -> Vec<Message> {
|
||||
if *self.snap_on.lock().unwrap() {
|
||||
*self.refreshes.lock().unwrap() += 1;
|
||||
msgs.insert(0, text_msg("system", "BLOC VIVANT"));
|
||||
}
|
||||
msgs
|
||||
}
|
||||
fn prewarm_keeps(&self, prefix: &[u64], payload: &Value) -> bool {
|
||||
// Ce que l'hôte reçoit : les empreintes des messages envoyés et le
|
||||
// corps de la requête.
|
||||
@@ -1094,3 +1104,53 @@ async fn prechauffage_garde_par_la_requete_qui_le_prolonge() {
|
||||
assert_eq!(r.err, None);
|
||||
assert_eq!(*host.sent_keeps.lock().unwrap(), [false]);
|
||||
}
|
||||
|
||||
/// PROJ_SNAPSHOT : une compaction ou une réduction en plein tour fait
|
||||
/// rafraîchir le bloc projet (le début du prompt change de toute façon) ;
|
||||
/// l'historique publié et la requête rejouée le portent. Le retrait d'images
|
||||
/// gardées, lui, ne touche pas au début du prompt.
|
||||
#[tokio::test]
|
||||
async fn bloc_projet_rafraichi_par_la_compaction() {
|
||||
let host = Fake::new(vec![
|
||||
Script::Status(400, "the request exceeds the available context size".into()),
|
||||
answer("après compaction"),
|
||||
]);
|
||||
*host.compacted.lock().unwrap() = Some(vec![um("résumé")]);
|
||||
*host.snap_on.lock().unwrap() = true;
|
||||
let (r, evs) = run(&host, &config(&[]), &Cancel::never()).await;
|
||||
assert_eq!(r.err, None, "{:?}", host.bodies.lock().unwrap());
|
||||
assert_eq!(*host.refreshes.lock().unwrap(), 1);
|
||||
assert!(evs.iter().any(|e| matches!(e, StreamEvent::NewHistory(h)
|
||||
if h.first().and_then(|m| m.content.as_ref()) == Some(&json!("BLOC VIVANT")))));
|
||||
assert_eq!(host.body(1)["messages"][0]["content"], "BLOC VIVANT");
|
||||
// Réduction forcée (pas de résumé possible) : rafraîchi aussi.
|
||||
let host = Fake::new(vec![
|
||||
Script::Status(
|
||||
400,
|
||||
"request (400000 tokens) exceeds the available context size (32768 tokens)".into(),
|
||||
),
|
||||
answer("ok"),
|
||||
]);
|
||||
*host.snap_on.lock().unwrap() = true;
|
||||
let hist: Vec<Message> = (0..12)
|
||||
.map(|i| um(&format!("message {i} {}", "x".repeat(400))))
|
||||
.collect();
|
||||
let (r, _) = run_hist(&host, &config(&[]), hist).await;
|
||||
assert_eq!(r.err, None);
|
||||
assert_eq!(*host.refreshes.lock().unwrap(), 1);
|
||||
assert!(
|
||||
host.phases.lock().unwrap().len() == 1,
|
||||
"résumé tenté puis réduction"
|
||||
);
|
||||
assert_eq!(host.body(1)["messages"][0]["content"], "BLOC VIVANT");
|
||||
// Images gardées retirées au filet réactif : pas de rafraîchissement.
|
||||
let host = Fake::new(vec![
|
||||
Script::Status(400, "the request exceeds the available context size".into()),
|
||||
answer_u("fini", 0),
|
||||
]);
|
||||
*host.snap_on.lock().unwrap() = true;
|
||||
let hist = vec![um("q"), tool_res("[ok]"), kept_relay(1000), um("suite")];
|
||||
let (r, _) = run_hist(&host, &config(&[]), hist).await;
|
||||
assert_eq!(r.err, None);
|
||||
assert_eq!(*host.refreshes.lock().unwrap(), 0);
|
||||
}
|
||||
Reference in new issue
Block a user