Aller au contenu principal

F-07 Synthèse vocale (TTS)

Brique F de la plateforme Enterprise AI. Technologie : Kokoro v1.0 (poids Apache-2.0, kokoro-onnx sur CPU, moteur de référence) et TTS des fournisseurs via un point d'accès compatible OpenAI. Piper n'est pas livré (piper1-gpl est en GPL-3.0). Version anglaise : ../../en/bricks/F-07-text-to-speech.md.

Rôle et responsabilités​

  • Synthétiser la parole en français et en anglais avec des voix approuvées, un sous-ensemble SSML, une fréquence et un format.
  • Tenir un registre des moteurs et voix validés avec statut de licence et de consentement.

Position et interactions​

  • Chemin d'exécution : synchrone ou streaming (morceaux utilisés par F-08).
  • Utilisé par les agents pour répondre à la voix, par les canaux (téléphonie) et par la voix temps réel F-08.

Part open source et part développée​

  • Open source réutilisé : Poids et voix Kokoro v1.0 en ONNX (Apache-2.0) ; phonémiseur espeak-ng (GPL-3.0) isolé dans le processus du worker vocal.
  • Développé dans la plateforme : Registre des moteurs, workflow d'approbation des voix, normalisation fr/en (nombres, dates, montants), découpage en streaming.

Modèle de données​

Tables du Control Plane (schéma eai) : eai.media, eai.multimodal_jobs, eai.usage_events.

Chaque table de tenant a tenant_id uuid NOT NULL et une politique de sécurité au niveau des lignes sur current_setting('app.tenant_id', true)::uuid. ClickHouse usage_events est la réplique analytique de eai.usage_events (ReplacingMergeTree par event_id).

Contrat : contracts/v1/multimodal-contract.schema.json (schema_version 1.0). L'usage est toujours remonté avec contracts/v1/usage-event.schema.json, l'audit avec contracts/v1/audit-event.schema.json.

Endpoints d'API​

MéthodeChemin
POST/v1/multimodal/tts
POST/v1/gateway/audio/speech

POST /v1/multimodal/tts​

Requête :

{
"text": "Votre dossier a bien été enregistré.",
"voice": "ff_siwis",
"model": "kokoro"
}

Réponse :

{
"job_id": "mmj_T01",
"status": "succeeded",
"media_id": "med_TTS1",
"usage": {
"characters": 37,
"audio_seconds": 2.6
}
}

POST /v1/gateway/audio/speech​

Requête :

{
"model": "tts-1",
"input": "Your request is registered.",
"voice": "alloy",
"response_format": "mp3"
}

Réponse :

{
"media_id": "med_TTS2",
"usage": {
"characters": 27
}
}

Exemples SDK​

Python​

from enterprise_ai import EnterpriseAI

client = EnterpriseAI(base_url="<control-plane-url>", api_key="eai_live_xxx", tenant="acme", locale="fr")
client.multimodal.tts.synthesize("Votre dossier a bien été enregistré.", voice="ff_siwis", model="kokoro")
client.gateway.speech(model="tts-1", input="Your request is registered.", voice="alloy")

TypeScript​

import { EnterpriseAI } from "@enterprise-ai/sdk";

const client = new EnterpriseAI({ baseUrl: "<control-plane-url>", apiKey: process.env.EAI_API_KEY, tenant: "acme", locale: "fr" });
await client.multimodal.tts({ text: "Votre dossier a bien été enregistré.", voice: "ff_siwis", model: "kokoro" });
await client.gateway.audio.speech({ model: "tts-1", input: "Your request is registered.", voice: "alloy" });

Java​

EnterpriseAIClient client = EnterpriseAIClient.builder()
.baseUrl("<control-plane-url>")
.apiKey(System.getenv("EAI_API_KEY"))
.tenant("acme")
.locale(Locale.FRENCH)
.build();
client.multimodal().tts(Map.of("text", "Votre dossier a bien été enregistré.", "voice", "ff_siwis", "model", "kokoro"));
byte[] audio = client.gateway().audioSpeech(Map.of("model", "tts-1", "input", "Your request is registered.", "voice", "alloy"));

Go​

client, err := eai.NewClient(eai.WithBaseURL("<control-plane-url>"), eai.WithAPIKey(os.Getenv("EAI_API_KEY")), eai.WithTenant("acme"), eai.WithLocale("fr"))
if err != nil { log.Fatal(err) }
ctx := context.Background()
_, err = client.Multimodal.Tts(ctx, map[string]any{"text": "Votre dossier a bien été enregistré.", "voice": "ff_siwis", "model": "kokoro"})
_, err = client.Gateway.AudioSpeech(ctx, map[string]any{"model": "tts-1", "input": "Your request is registered.", "voice": "alloy"})

// worker package: run a media worker consuming multimodal jobs
w, err := worker.New(worker.Config{Source: jobs, Handler: handle, Client: client, Concurrency: 2})
if err != nil { log.Fatal(err) }
err = w.Run(ctx)

Configuration​

Variables d'environnement : EAI_TTS_MODEL.

Clés de valeurs Helm (deploy/helm/enterprise-ai/values.yaml) : workers.items[name=tts].replicas.

Sécurité​

  • Le clonage de voix est désactivé sauf consentement signé du titulaire de la voix attaché à la fiche voix.
  • Le texte passe par la DLP avant synthèse si le canal est public.
  • Tenant déduit de l'identifiant d'accès, jamais du corps ; sécurité au niveau des lignes PostgreSQL sur chaque table de tenant.
  • Chaque mutation et décision de politique écrit un événement d'audit chaîné par hachage (G-11).
  • Les erreurs utilisent l'enveloppe standard avec message_i18n en et fr.

Observabilité​

  • Délai du premier morceau audio (eai_tts_ttfb_ms) pour l'usage en streaming.
  • traceparent W3C propagé, X-Request-Id renvoyé, les spans portent eai.tenant_id, eai.project, eai.agent_id, eai.run_id.

Unités FinOps​

  • usage_type tts : caractères, audio_tokens si le fournisseur les facture, audio_seconds produites.

Modes de défaillance et résilience​

  • Moteur indisponible -> repli sur une autre voix approuvée de la même langue, signalé dans la réponse.

Adoption autonome​

Un produit peut utiliser le TTS seul pour générer des contenus accessibles.

Tests d'acceptation​

  1. Un texte français avec nombres et montants est normalisé et produit un WAV de la durée attendue.
  2. Les caractères comptés dans l'Usage Event égalent la longueur de l'entrée.

Feuille de route d'implémentation

Preuves vocales du kit L4​

Kokoro v1.0 int8 (Apache-2.0, sha256 épinglée) synthétise la parole de test ; response.create la diffuse en temps réel et une prise de parole l'annule (MULT-006, MULT-010). Voir le guide du runtime vocal.