Lot 33 : préchauffage (PREWARM) tenu par la façade

PREWARM ne garde plus la conversation au Go. La façade tient
l'historique : elle assemble et envoie la requête du prochain tour ; le
Go garde le compteur des requêtes en vol et ce qui l'annule.

Go (facade_prewarm.go) :
- /internal/v1/prewarm/check : moteur libre d'un vrai travail, loin de
  minuit, en bonne santé, slot visé ;
- /internal/v1/prewarm/begin : rechargement SLOT_PERSIST puis inscription
  comme LA requête en vol ; son annulation est relayée à la façade ;
- /internal/v1/prewarm/end : place rendue, cache repris noté ;
- /internal/v1/oai/begin : prewarm_keep pour l'étape qui le prolonge ;
- fin de tâche planifiée : le déclencheur part à la façade.

Rust (chat/prewarm.rs) :
- assemblage par les mêmes fonctions que le tour, sentinelle « . », un
  seul jeton, non streamé ; abstention (capacités inconnues, tour en
  cours, discussion vide, compaction attendue, assemblage inattendu) ;
- déclencheurs : fin de tour, fin de tâche, changement de discussion ou
  de projet (full : après 3 s) ;
- le tour garde le préchauffage qu'il prolonge (empreintes des messages
  et des options de rendu) et coupe tout de suite les autres ;
- télémétrie kind=prewarm.

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:52:44 +00:00
1 parent f79b74dac3
commit ff5c6d4571
21 files changed
+1457 -35

No files matched your search

@@ -0,0 +1,112 @@
# Lot 33 — Préchauffage (PREWARM) tenu par la façade
## Contexte
Certains recalculs sont inévitables :
- le prompt réécrit par une compaction ;
- le dernier message assistant rendu autrement au tour suivant ;
- la discussion reprise après une tâche qui a pris le slot.
Sans la clé, ils tombent sur le premier jeton du message suivant.
`PREWARM` (`chat_prewarm.go`) envoie au moteur local, dès qu'il est libre, la
requête du prochain tour telle qu'elle partira, suivie d'une sentinelle « . »
et avec `max_tokens` 1. Le moteur calcule tout le préfixe ; le vrai tour ne
calcule plus que son propre message.
Le préchauffage ne passe jamais devant un vrai travail :
- toute requête de Loki l'annule, sauf un tour qui le prolonge exactement ;
- un seul est en vol à la fois ;
- aucune erreur ne remonte.
La clé gardait la conversation au Go.
## Le lot
La façade tient l'historique : c'est elle qui assemble et envoie. Le Go
garde le compteur des requêtes en vol (`engineGate`) et ce qui annule.
### Go (`facade_prewarm.go`)
- `/internal/v1/prewarm/check` dit si le préchauffage peut partir :
- clé posée, moteur local ;
- ni travail annexe, ni bench, ni optimiseur en cours ;
- loin de minuit ;
- moteur en bonne santé ;
- slot visé : un seul slot, ou celui de la discussion avec SIDE_SLOT.
- `/internal/v1/prewarm/begin` :
- recharge l'état SLOT_PERSIST de la discussion ;
- inscrit le préchauffage comme LA requête en vol (`prewarmBegin`) ;
- son annulation (une autre requête part) est relayée à la façade
(`/internal/facade/v1/chat/prewarm/cancel`).
- `/internal/v1/prewarm/end` :
- rend la place ;
- note ce que le moteur a repris (`noteEnginePrompt`).
- `/internal/v1/oai/begin` accepte `prewarm_keep` : l'étape d'un tour de la
façade qui prolonge le préchauffage ne l'annule pas.
- Après une tâche planifiée, le déclencheur part à la façade
(`/internal/facade/v1/chat/prewarm`).
- PREWARM ne garde plus la conversation.
### Rust (`chat/prewarm.rs`)
- L'état partagé :
- le préchauffage en vol, son inscription, son annulation ;
- les capacités du dernier tour de chaque discussion (64 au plus).
- L'assemblage : les mêmes fonctions que le tour.
- Vue du Go, historique sans blocs `<context_update>`, préambule.
- Écho, messages d'envoi, corps.
- Sentinelle « . » (rôle `builder` en mode Code), non streamé, un seul jeton.
- On s'abstient dans ces cas :
- capacités inconnues ;
- tour en cours ;
- discussion vide ;
- compaction attendue ;
- assemblage inattendu.
- Déclencheurs :
- fin de tour quand rien n'attend dans la file ;
- fin de tâche (relayée par le Go) ;
- changement de discussion ou de projet : le préchauffage en cours est
annulé ; avec `full`, la nouvelle discussion est préparée après 3 s.
- Le tour :
- `TurnHost::prewarm_keeps` compare les empreintes des messages envoyés
et des champs qui façonnent le rendu (`prewarmOptsHash`) ;
- une requête qui ne prolonge pas le préchauffage le coupe tout de suite,
sans attendre l'aller-retour par le Go.
- Télémétrie : le préchauffage est rangé dans l'anneau (`kind=prewarm`).
## Tests
- `chat/prewarm` :
- mode (jamais pour un preset externe) ;
- empreinte des options (l'échantillonnage n'y entre pas, un champ absent
n'est pas un champ vide) ;
- rôle de la sentinelle ;
- prolongement ;
- état partagé (un seul en vol, garde, annulation, fin) ;
- capacités bornées.
- La boucle du tour : le drapeau transmis à l'envoi, jamais vers une API
externe.
- Le vrai hôte (`tests/lot6.rs`) :
- fin de tour : requête préchauffée (sentinelle, un seul jeton, même
préfixe et mêmes outils que le tour), inscrite au Go, pas de ticket de
la façade ;
- le tour suivant la prolonge et la garde (`prewarm_keep`) ;
- fin rendue avec total et cache, télémétrie ;
- refus du Go : rien ne part ;
- annulation relayée : connexion coupée, place rendue.
- Le Go (`facade_prewarm_test.go`) :
- contrôle : clé, slots, minuit, travail annexe, moteur muet ;
- inscription et deuxième préchauffage refusé ;
- gardé par le tour qui le prolonge, annulé et relayé sinon ;
- fin unique ;
- requête déjà en vol ;
- déclencheur après tâche relayé.
## Écarts assumés
- L'annulation d'un préchauffage pas encore inscrit au Go (aller-retour en
cours) annule celui qui part, quel que soit son numéro : il n'y en a
qu'un.