Lot 32 : images gardées (KEEP_TURN_IMAGES) dans le tour de la façade

KEEP_TURN_IMAGES ne garde plus la conversation au Go. L'état du tour vit
dans la façade ; les fichiers et la vision restent au Go.

Go (facade_keepimg.go) :
- /internal/v1/turn/keepimg : vision réellement active, budget des images
  gardées ;
- /internal/v1/turn/keepimg/store : une image relayée rangée par
  référence (refRelayImage), relue à l'octet près.

Rust :
- chat/keepimg.rs : port de keepImages (image à l'essai, mesure par le
  moteur répartie au prorata des octets, budget, publication, fin de tour).
- La boucle du tour : budget demandé pour le fil principal, image rangée
  par le Go, historique réécrit publié sans les relais non gardés ; les
  images gardées partent avant tout résumé (filet réactif, en cours de
  tour, fenêtre pleine).
- Début et fin de tour : retirées si le contexte le demande ou si elles
  ne doivent plus partir (clé retirée, vision absente).

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 02:30:59 +00:00
1 parent a005626e66
commit f79b74dac3
17 files changed
+1435 -37

No files matched your search

+45
View File
@@ -420,6 +420,22 @@ pub async fn engine_begin(store: &Store, main_conv: &str, side: bool) -> Option<
}
}
/// KEEP_TURN_IMAGES (lot 32) : la vision est-elle réellement active
/// (projecteur déclaré et vu par le moteur) et le budget des images gardées.
/// Go injoignable : `None` (images éphémères).
pub async fn keep_images_state(store: &Store) -> Option<(bool, i64)> {
match store
.internal_post("/internal/v1/turn/keepimg", &json!({}))
.await
{
Ok((code, v)) if code.is_success() => Some((
v["vision"].as_bool().unwrap_or(false),
v["budget"].as_i64().unwrap_or(0),
)),
_ => None,
}
}
/// Rend le ticket « en vol » au Go quand la réponse du moteur est lue ou
/// abandonnée (`engineRequestBeginSide` … `end`).
pub struct GateTicket {
@@ -704,6 +720,35 @@ impl TurnHost for GoTurnHost {
self.shared.echo_refusals.refuse(model);
}
async fn keep_images(&self) -> Option<i64> {
// Fil principal d'une discussion seulement : la vérification ne
// persiste pas son historique.
if !super::keepimg::keep_turn_images_on(&self.cfg)
|| self.perf_kind != super::perf::KIND_MAIN
|| self.conv_id.is_empty()
{
return None;
}
match keep_images_state(&self.shared.store).await {
Some((true, budget)) => Some(budget),
_ => None,
}
}
async fn keep_image(&self, m: &Message) -> Option<(Message, i64)> {
let (code, v) = self
.shared
.store
.internal_post("/internal/v1/turn/keepimg/store", &json!({ "msg": m }))
.await
.ok()?;
if !code.is_success() || v["ok"] != true {
return None;
}
let msg: Message = serde_json::from_value(v["msg"].clone()).ok()?;
Some((msg, v["weight"].as_i64().unwrap_or(0)))
}
async fn engine_slot(&self, ep: &ChatEndpoint) -> Option<u8> {
if ep.external {
self.side_slot.store(false, Ordering::SeqCst);