docs: PROFILE_LANGUAGE_SPEC — langue par profil patient (handoff dev kazeia-android)
Le pipeline est déjà paramétré par langue (context.language → STT/TTS OmniVoice) mais figé sur 'fr' ; Profile n'a pas de champ langue. Spec : ajouter Profile.language (déf. fr), l'exposer/accepter dans /profiles, et le câbler dans KazeiaService (activeProfile.language au lieu du 'fr' codé en dur, ~l.1100/1351). Fiche clinique = central-only, hors app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
783bca1ba4
commit
cdee5151f4
|
|
@ -0,0 +1,90 @@
|
|||
# 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.*
|
||||
Loading…
Reference in New Issue