Files
Loki/docs/superpowers/specs/2026-10-16-rust-migration-lot33-prechauffage-design.md
T
Claude ff5c6d4571 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
2026-10-16 02:52:44 +00:00

4.4 KiB

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.