Files
Loki/internal/loki/web_internal_turnperf.go
T
Claude 89ed527c96 Fusion de main : conversation Rust par défaut, télémétrie du tour
main a reçu en parallèle la télémétrie du tour tenu par la façade
(/internal/v1/turn/perf), les tickets « en vol » du moteur et la
conversation tenue par la façade par défaut (LOKI_CHAT=go pour revenir
au Go). Le lot 29 faisait la même chose par /internal/v1/perf/record :
un seul chemin est gardé, celui de main.

- TurnHost::perf_record (main) remplace TurnHost::perf ; la nature est
  posée par l'hôte (verify pour la passe isolée seulement, main pour les
  corrections du builder, comme le Go).
- Les résumés de compaction passent par le même point d'entrée, nature
  compact : chat/perf.rs ne garde que le relevé de la réponse entière
  (perfRecFromWire) et l'envoi.
- La sonde du gabarit reste à la façade (lot 23) : /internal/v1/turn/perf
  ne la lance plus côté Go.
- facade_perf.go retiré ; ses cas (perte, intercalé, alerte, preset
  externe, incomplète, GET) rejoignent web_internal_turnperf_test.go.
- README : LOKI_CHAT défaut Rust, avec ce que les lots 19, 23, 29 et 30
  y ajoutent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 01:53:53 +00:00

65 lines
2.5 KiB
Go

package loki
import (
"encoding/json"
"io"
"net/http"
)
// Télémétrie d'une complétion d'un tour tenu par la façade Rust
// (LOKI_CHAT=rust). La boucle Go range chaque complétion dans l'anneau de
// perf_log.go (perfRecord) : c'est lui qui nourrit /api/perf/summary et
// l'alerte « cache perdu » posée sur les stats finales d'une complétion. La
// boucle Rust lui envoie la même chose, ici, au même moment.
//
// POST /internal/v1/turn/perf
// {"kind":"main","conv":"c…","iter":0,"complete":true,"external":false,
// "stats":{…StatsEvent…},"prefix":[…empreintes…]}
// → {"kind":"main","lost":N,"lost_after":"…","lost_alert":true}
//
// kind : main (tour), verify (passe de vérification du mode Code) ou compact
// (résumé de compaction, relevé dans la réponse entière par la façade).
// prefix : empreintes cumulées des messages envoyés (perfPrefix), calculées
// par l'appelant — elles n'ont à être cohérentes qu'avec ses propres envois,
// d'une complétion à la suivante de la même discussion. La sonde du gabarit
// n'en part pas : la façade la tient (facadeTplCaps).
const internalTurnPerfMaxBody = 8 << 20
func handleInternalTurnPerf(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.Header().Set("Allow", http.MethodPost)
sendJSON(w, http.StatusMethodNotAllowed, map[string]any{"error": "POST attendu"})
return
}
var in struct {
Kind string `json:"kind"`
Conv string `json:"conv"`
Iter int `json:"iter"`
Complete bool `json:"complete"`
External bool `json:"external"`
Stats StatsEvent `json:"stats"`
Prefix []uint64 `json:"prefix"`
}
raw, err := io.ReadAll(io.LimitReader(r.Body, internalTurnPerfMaxBody+1))
if err != nil || len(raw) > internalTurnPerfMaxBody || json.Unmarshal(raw, &in) != nil {
sendJSON(w, http.StatusBadRequest, map[string]any{"error": "télémétrie illisible"})
return
}
if in.Kind == "" {
in.Kind = perfMain
}
var prefix []uint64
if in.Complete {
prefix = in.Prefix
}
rec := perfRecord(perfRecFromStats(perfTag{kind: in.Kind, conv: in.Conv}, in.Iter, in.Complete, in.Stats), prefix)
alert := false
if in.Complete && !in.External && ((rec.Lost != nil && *rec.Lost > 0) || (rec.Cached != nil && *rec.Cached == 0)) {
alert = perfAlert(rec, perfLostAlertAt(ReadConfig()))
}
sendJSON(w, http.StatusOK, map[string]any{
"kind": rec.Kind, "lost": rec.Lost, "lost_after": rec.LostAfter, "lost_alert": alert,
})
}