Kazeia-central/docs/PROFILE_LANGUAGE_SPEC.md

91 lines
4.3 KiB
Markdown

# 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
```kotlin
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"`) :
```kotlin
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.*