Kazeia-central/docs/PROFILE_LANGUAGE_SPEC.md

4.3 KiB

PROFILE_LANGUAGE_SPEC — langue par profil patient (pipeline STT/LLM/TTS)

Destinataire : dev de l'app patiente com.kazeia. Auteur : Kazeia-central. Stack figée (FROZEN.md) → changement décrit ici, livré.

Objet : la langue du pipeline doit être choisie par patient (porté par le profil), pas figée en "fr". Kazeia-central gère/édite la langue par profil et la pousse ; l'app doit l'exposer (provider) et l'appliquer (pipeline).


0. État observé (vérifié)

  • Le pipeline est déjà paramétré par langue : KazeiaPipeline passe context.language à sttEngine.transcribe(audio, context.language) et ttsEngine.synthesizeAndPlay(text, context.language, …). OmniVoiceTtsEngine.langCode() résout un nom/alias → code (646 langues). WhisperHybridEngine.transcribe(audio, language) prend aussi la langue.
  • MAIS la langue est codée en dur "fr" : KazeiaService appelle stt.transcribe(audioSnapshot, language = "fr") (≈ lignes 1100 et 1351) ; core/Pipeline.kt a language: String = "fr" par défaut. La langue n'est pas lue depuis le profil.
  • Profile (profiles/Profile.kt) n'a aucun champ langue : id, displayName, avatarColor, voiceId, systemPromptOverride, pinHash, createdAt, lastUsedAt, notes, isDefault.
  • KazeiaService a déjà l'accès au profil actif et y réagit : ProfileStore.get(ctx).activeProfile() ; onProfileStoreChanged() applique déjà activeProfile().voiceId → setVoice. C'est exactement là qu'il faut résoudre la langue.

1. Adaptation demandée

1.1 Profile — nouveau champ language

profiles/Profile.kt : ajouter

val language: String = "fr",   // code/nom résolu par OmniVoiceTtsEngine.langCode() (déf. fr)
  • toJson() : put("language", language).
  • fromJson() : language = js.optString("language", "fr") (rétro-compat : profils existants sans le champ → "fr").
  • Valeur : un code court (fr, en, de, es, it, pt, ar, zh, …) ou un nom que langCode() sait résoudre. Kazeia-central enverra un code court.

1.2 Provider /profiles — exposer + accepter language

  • voicesCursor/profilesCursor : ajouter la colonne language (déf. "fr").
  • upsertProfile(ContentValues) : lire la clé language si présente (sinon conserver l'existant / défaut "fr"). Ajout additif → compat ascendante.
  • (Le canal d'écriture riche profile_upsert_json du PROVIDER_RPC_SPEC §4.4 doit aussi inclure language quand il sera implémenté ; en attendant, update /profiles avec la clé language suffit pour ce champ simple.)

1.3 KazeiaService — appliquer la langue du profil actif

Remplacer le "fr" figé par la langue du profil actif (avec repli "fr") :

val lang = ProfileStore.get(applicationContext).activeProfile()?.language ?: "fr"
// … construire le PipelineContext avec language = lang ;
// … stt.transcribe(audio, language = lang) aux ~l.1100 et ~l.1351.
  • À recalculer sur changement de profil actif (onProfileStoreChanged(), déjà le point où la voix est ré-appliquée) — pour qu'un changement de patient change la langue à chaud.

2. Ce que fait Kazeia-central (pour info — pas côté app)

  • Édite la langue par profil et la pousse (via update /profiles / futur profile_upsert_json).
  • Tient la fiche patient complète (identité civile, contact/référent, clinique) dans son store chiffré, clé (serial, profile_id). Cette fiche ne va PAS sur la tablette (PII clinique, minimisation on-device). Seul l'opérationnel (langue, voix, prompt) concerne le device.

3. Récap

# Adaptation app Criticité
1 Profile.language (+ toJson/fromJson, déf. "fr") requis
2 /profiles expose + accepte language requis
3 KazeiaService : context.language = activeProfile.language ?: "fr" (remplace le "fr" figé), ré-appliqué au changement de profil requis

Changement borné : le pipeline sait déjà gérer la langue ; il ne manque que la relier au profil.


Source de vérité : Profile.kt, KazeiaService.kt (~l.1100/1351), KazeiaPipeline.kt, OmniVoiceTtsEngine.langCode(), WhisperHybridEngine.transcribe(). Vérifié le 2026-06-23.