Files
Loki/docs/superpowers/specs/2026-10-15-rust-migration-lot16-moteur-design.md
T
Claude 2da2efc1fd Lot 16 : pilotage du moteur tenu par la façade (LOKI_ENGINE=rust)
La façade sert /api/start, /api/stop, /api/restart, /api/vram/unload et
/api/vram/reload quand LOKI_ENGINE=rust (expérimental ; le Go reste le
défaut). Elle lance elle-même « loki-rs engine <action> », le superviseur
que le Go appelait déjà en conteneur, avec l'environnement qu'il lui
passait et de quoi joindre l'API interne du Go ; sa dernière ligne
d'erreur devient l'erreur rendue. Le contrôle d'avant démarrage
(preflightEngine : preset externe, Strata, BIN, MODEL, tranches) et le
bilan de VRAM du déchargement (gpuSettle) sont portés.

La façade ne tient ce lot qu'en conteneur : sous systemd ou avec
LOKI_ENGINE_SUPERVISOR=go, loki-rs ne passe pas la variable au Go.

Restent au Go derrière son API interne (facade_engine.go) : le verrou de
l'optimiseur avant tout démarrage (/internal/v1/service/prestart), la
réponse en cours quand il tient la conversation
(/internal/v1/chat/generating) et l'arrêt de la dictée quand il la tient
(/internal/v1/dictate/stop).

Parité : parity_engine_test.go tire du Go dix bilans de VRAM, vingt-six
installations passées au préflight, les messages du superviseur et dix
réponses de route, rejoués par tests/enginectl.rs ; démarrage, arrêt,
redémarrage, déchargement chiffré et rechargement vérifiés de bout en bout
avec un faux superviseur et un faux nvidia-smi ; TestFacadeMoteur côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 18:33:31 +00:00

4.2 KiB

Lot 16 — pilotage du moteur tenu par la façade

Contexte

Le Go pilotait le moteur depuis l'interface :

  • svcHandler (web_api.go) pour /api/start, /api/stop et /api/restart ;
  • handleVramUnload et handleVramReload (web_vram.go) pour rendre la carte graphique à la machine, puis la reprendre ;
  • serviceAction (sys_service.go) : le verrou de l'optimiseur, puis, en conteneur, le superviseur Rust loki-rs engine <action> (lot 3, sys_service_rust_linux.go) ;
  • preflightEngine : ce sans quoi le moteur ne peut pas démarrer, dit avant de le lancer.

Lot 16 : la façade tient ces routes quand LOKI_ENGINE=rust (enginectl.rs, expérimental). Sans la variable, rien ne change.

Route Rôle
/api/start, /api/stop, /api/restart pilotage du moteur ; réponse {ok, out} comme le Go
POST /api/vram/unload arrête le moteur et la dictée, rapporte la VRAM rendue ; refusé pendant une réponse en cours sauf {force: true}
POST /api/vram/reload relance le moteur après le contrôle d'avant démarrage ; {already: true} s'il tourne

La façade lance elle-même loki-rs engine <action>, avec l'environnement que le Go lui passait (LOKI_GO_BIN, LOKI_HOME, LOKI_SERVICE) et de quoi joindre l'API interne du Go pour loki-rs engine serve (LOKI_GO_ADDR, LOKI_INTERNAL_TOKEN). Sa dernière ligne d'erreur devient l'erreur rendue, sans le préfixe du binaire. Un preset externe ne lance rien ; un redémarrage arrête alors le moteur local.

Le bilan de VRAM suit gpuSettle :

  • une lecture ratée est sautée, jamais comptée « 0 Mo » ;
  • un palier ne conclut qu'une fois la baisse commencée ;
  • les jauges du moniteur sont invalidées après coup.

Quand la façade ne tient pas ce lot

La façade ne prend ce lot qu'en conteneur, avec son superviseur. Sous systemd, ou avec LOKI_ENGINE_SUPERVISOR=go, loki-rs ne passe pas LOKI_ENGINE au Go, qui garde les routes (décidé au démarrage, raison dans le journal).

Restent au Go

Le Go expose trois routes internes (facade_engine.go) :

  • POST /internal/v1/service/prestart : avant tout démarrage, refus pendant une optimisation, puis ménage d'un verrou périmé (essai orphelin arrêté, application interrompue défaite). Le verrou de l'optimiseur suit le processus qui le tient, il reste au Go jusqu'au lot de l'optimiseur ;
  • GET /internal/v1/chat/generating : la réponse en cours, quand le Go tient la conversation (sinon la façade lit la sienne) ;
  • POST /internal/v1/dictate/stop : l'arrêt de la dictée, quand le Go la tient (sinon la façade arrête la sienne).

GET /internal/v1/tune/guard (lot 15) sert le refus 409 du rechargement.

Les autres appelants de serviceAction côté Go (bascule de preset, optimiseur, mises à jour du moteur) passent toujours par le Go. Les deux voies lancent le même superviseur, sur les mêmes fichiers PID et journal.

Écarts assumés

  • engineNeedsStop regarde aussi les états transitoires de systemd. En conteneur, seul cas où la façade tient ce lot, il se réduit au PID vivant.
  • Un superviseur tué par un signal est décrit comme par le Go (« signal: killed ») pour les signaux courants, par son numéro sinon.

Parité

internal/loki/parity_engine_test.go tire du Go :

  • dix bilans de VRAM (palier avant la baisse, lectures ratées, VRAM en hausse…) ;
  • la dernière ligne d'erreur du superviseur et le retrait des couleurs ;
  • vingt-six installations passées au contrôle d'avant démarrage (preset externe, Strata, BIN absent ou relatif, MODEL absent, hors des dossiers, remontant, en tranches incomplètes…) ;
  • dix réponses de route qui ne lancent rien (GET refusé avec Allow: POST, optimisation ou réponse en cours, moteur déjà arrêté, contrôle en échec).

tests/enginectl.rs rejoue le tout par la façade. Il vérifie aussi de bout en bout, avec un faux superviseur et un faux nvidia-smi :

  • démarrage, arrêt, redémarrage, refus pendant une optimisation ;
  • échec du démarrage, preset externe ;
  • déchargement chiffré, arrêt en échec, VRAM en hausse ;
  • rechargement.

Une mutation de chaque règle fait échouer les tests.

Côté Go, TestFacadeMoteur vérifie le relais et les trois routes internes.