From 4c8ac935813a41dac2b383ce9b759f11a973a2fe Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Thu, 15 Oct 2026 21:06:27 +0200 Subject: [PATCH] =?UTF-8?q?Lot=206,=20=C3=A9tape=206=20:=20la=20conversati?= =?UTF-8?q?on=20tenue=20par=20la=20fa=C3=A7ade=20Rust=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sans LOKI_CHAT, c'est désormais la façade qui tient la conversation ; LOKI_CHAT=go la rend au Go (porte de sortie, aussi listée dans le compose Unraid). Le Go la garde de toute façon si un réglage que la façade ne sait pas encore honorer est posé (PROJ_SNAPSHOT, KEEP_TURN_IMAGES, NUDGE_IN_TOOL, SIDE_SLOT, SLOT_PERSIST, PREWARM, motifs retry_patterns), décidé au démarrage comme avant. Vérifié de bout en bout avec les vrais binaires et un faux moteur, dans les deux modes : deux tours (texte, puis appel d'outil), même état, même journal rejoué, discussion intacte après relance, et un panneau perf identique (complétions, tours, pertes, sonde du gabarit). Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- docker-compose.unraid.yml | 3 ++ ...6-10-14-rust-migration-lot6-chat-design.md | 2 +- rust/loki-rs/src/main.rs | 37 ++++++++++++++++--- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9b4fec9..e97567b 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ redémarrages (volume `/data`). Variables d'environnement du conteneur : | `LOKI_AUTO_UPDATE` | `0` : pas de mise à jour des binaires au démarrage | `1` | | `LOKI_FACADE` | `0` : sans façade Rust, le Go sert directement le port public (porte de sortie) | `1` | | `LOKI_GO_PORT` | port local (127.0.0.1) du serveur Go derrière la façade | `8091` | -| `LOKI_CHAT` | `rust` : la conversation est tenue par la façade Rust (expérimental, migration en cours), outils fichiers et shell compris. Le Go garde les autres outils, les pièces jointes, les fichiers et l'export. Avec `PROJ_SNAPSHOT`, `KEEP_TURN_IMAGES`, `NUDGE_IN_TOOL`, `SIDE_SLOT`, `SLOT_PERSIST`, `PREWARM` ou des motifs `retry_patterns`, le Go garde la conversation (décidé au démarrage) | Go | +| `LOKI_CHAT` | qui tient la conversation : la façade Rust par défaut (outils fichiers, shell et git compris ; le Go garde les autres outils, les pièces jointes, les fichiers et l'export). `go` : la rend au Go (porte de sortie). Avec `PROJ_SNAPSHOT`, `KEEP_TURN_IMAGES`, `NUDGE_IN_TOOL`, `SIDE_SLOT`, `SLOT_PERSIST`, `PREWARM` ou des motifs `retry_patterns`, le Go la garde de toute façon (décidé au démarrage) | Rust | **Accès par nom de domaine.** Sans clé de pilotage, l'API n'accepte que les hôtes locaux (IP, `localhost`, nom sans point, `.local`/`.lan`…) et refuse diff --git a/docker-compose.unraid.yml b/docker-compose.unraid.yml index 961718e..314d245 100644 --- a/docker-compose.unraid.yml +++ b/docker-compose.unraid.yml @@ -41,6 +41,9 @@ services: # sinon l'API répond 403 (protection contre les sites tiers / DNS # rebinding). Inutile pour un accès par IP ou nom local. - LOKI_TRUSTED_HOSTS= + # Conversation tenue par la façade Rust (défaut). « go » la rend au Go si + # quelque chose ne va pas — puis Compose Down / Up. + # - LOKI_CHAT=go volumes: # PERFORMANCE — /mnt/user/… passe par la couche FUSE des partages # Unraid (shfs) : chaque écriture de la base et chaque page d'un gros diff --git a/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md b/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md index e65d244..ac0ad24 100644 --- a/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md +++ b/docs/superpowers/specs/2026-10-14-rust-migration-lot6-chat-design.md @@ -1,6 +1,6 @@ # Migration Rust — lot 6 : le cœur chat -Date : 2026-10-14 · Statut : **brouillon de conception** (aucun code) · +Date : 2026-10-14 · Statut : **livré** (étapes 1 à 6 ; défaut Rust depuis le 2026-10-15, `LOKI_CHAT=go` pour revenir au Go) · Prérequis : lots 1-5 (façade, auth, store via API interne, `/v1`). ## Pourquoi c'est un lot à part diff --git a/rust/loki-rs/src/main.rs b/rust/loki-rs/src/main.rs index 1d735ca..a63fffb 100644 --- a/rust/loki-rs/src/main.rs +++ b/rust/loki-rs/src/main.rs @@ -114,9 +114,9 @@ fn serve() -> ExitCode { return ExitCode::FAILURE; } }; - // LOKI_CHAT=rust : la conversation est tenue par la façade. Le Go le - // sait (il remet alors à la façade le trafic de chat du tunnel) et - // connaît son adresse locale. + // Conversation tenue par la façade (défaut ; LOKI_CHAT=go pour la + // rendre au Go). Le Go le sait (il remet alors à la façade le trafic + // de chat du tunnel) et connaît son adresse locale. let chat_rust = chat_held_by_facade(); let mut env = vec![(auth::INTERNAL_ENV.to_string(), token.clone())]; if chat_rust { @@ -184,9 +184,19 @@ fn serve() -> ExitCode { }) } -/// `LOKI_CHAT=rust` : conversation tenue par la façade (Go par défaut). +/// Conversation tenue par la façade : c'est le défaut. `LOKI_CHAT=go` (ou +/// 0/off/false/no) la rend au Go — porte de sortie. Le Go la garde de toute +/// façon si un réglage que la façade ne sait pas honorer est posé +/// (`facadeChatBlocker`). fn chat_held_by_facade() -> bool { - std::env::var("LOKI_CHAT").is_ok_and(|v| v.trim().eq_ignore_ascii_case("rust")) + chat_wanted(std::env::var("LOKI_CHAT").ok().as_deref()) +} + +fn chat_wanted(v: Option<&str>) -> bool { + !matches!( + v.map(|v| v.trim().to_ascii_lowercase()).as_deref(), + Some("go" | "0" | "off" | "false" | "no") + ) } /// Le Go garde-t-il la conversation ? (quelques essais : il vient de démarrer) @@ -218,7 +228,7 @@ async fn start_chat( ); s.load().await?; let shared = ChatShared::for_sessions(&s, store, home.join("chatimg")); - eprintln!("[loki-rs] conversation tenue par la façade (LOKI_CHAT=rust)"); + eprintln!("[loki-rs] conversation tenue par la façade (LOKI_CHAT=go pour la rendre au Go)"); Ok(ChatService::new( std::sync::Arc::new(s), std::sync::Arc::new(shared), @@ -265,3 +275,18 @@ async fn shutdown_signal() { let _ = tokio::signal::ctrl_c().await; } } + +#[cfg(test)] +mod tests { + #[test] + fn conversation_tenue_par_la_facade_par_defaut() { + use super::chat_wanted; + assert!(chat_wanted(None)); + assert!(chat_wanted(Some(""))); + assert!(chat_wanted(Some("rust"))); + assert!(chat_wanted(Some(" RUST "))); + for v in ["go", "GO", "0", "off", "false", "no"] { + assert!(!chat_wanted(Some(v)), "{v}"); + } + } +}