SDK React
Le SDK React fournit un hook headless pour intégrer la transcription en temps réel dans vos applications React.
Installation
npm install @ephia/transcribe-sdkAuthentification
Le SDK nécessite une URL WebSocket et un token API. Pour des raisons de sécurité, il est recommandé de générer l’URL WebSocket côté backend avec le token intégré.
Option recommandée : Backend génère l’URL
Votre backend génère l’URL WebSocket avec le token :
// Backend
const wsUrl = `wss://api.ephia.ai/api/v1/audio/live?token=${apiKey}`;
// Transmettre wsUrl au frontendOption dev : Token direct (non recommandé en production)
const config = {
websocketUrl: 'wss://api.ephia.ai',
apiPrefix: '/api/v1',
token: 'YOUR_API_KEY' // ⚠️ Ne pas exposer en production
};Hook headless (useEphia)
Le hook fournit un contrôle total sur le cycle de vie et l’UI.
Exemple minimal
import { useEphia } from '@ephia/transcribe-sdk';
function MyTranscriptionUI() {
const {
start,
stop,
pause,
resume,
clear,
finalText,
draftText,
segments,
isRecording,
status,
audioLevel,
sessionId,
error
} = useEphia({
websocketUrl: 'wss://api.ephia.ai',
apiPrefix: '/api/v1',
token: 'YOUR_API_KEY',
specialty: 'radiology',
onEvent: (type, event) => {
if (type === 'transcription') {
console.log('New transcript:', event.text);
}
}
});
return (
<div>
<p>Status: {status}</p>
{error && <p>Error: {error.message}</p>}
<button onClick={start} disabled={isRecording}>
Start
</button>
<button onClick={pause} disabled={!isRecording}>
Pause
</button>
<button onClick={resume} disabled={isRecording}>
Resume
</button>
<button onClick={stop} disabled={!isRecording}>
Stop
</button>
<button onClick={clear}>Clear</button>
<div>
<strong>Texte final:</strong>
<p>{finalText}</p>
</div>
<div style={{ opacity: 0.6 }}>
<strong>En cours:</strong>
<p>{draftText}</p>
</div>
<div>
{segments.map((segment) => (
<p key={segment.id} style={{ opacity: segment.isFinal ? 1 : 0.6 }}>
{segment.text}
</p>
))}
</div>
</div>
);
}Options du hook
| Option | Type | Défaut | Description |
|---|---|---|---|
websocketUrl | string | requis | URL de base du serveur WebSocket (ex: "wss://api.ephia.ai") |
apiPrefix | string | "/api/v1" | Préfixe de l’API |
token | string | — | Token API (optionnel si passé dans l’URL) |
specialty | MedicalSpecialty | "general" | Domaine ("general", "radiology", "cardiology", "legal") |
sampleRate | number | 16000 | Taux d’échantillonnage audio |
channels | number | 1 | Nombre de canaux audio |
preset | string | — | Configuration prédéfinie ("realtime", "balanced", "quality", etc.) |
audioDurationThreshold | number | — | Seuil de durée audio avant déclenchement (secondes) |
silenceThreshold | number | — | Seuil de silence pour détection de fin de phrase (secondes) |
maxBufferSize | number | — | Taille maximale du buffer de chunks |
flushTimeout | number | — | Timeout avant flush automatique (secondes) |
gladiaEndpointing | number | — | Paramètre d’endpointing pour détection de fin de phrase (secondes) |
enableFinalProofreading | boolean | true | Active la relecture finale pour améliorer la qualité |
autoInject | boolean | false | Injection automatique du texte dans un élément |
clearOnStart | boolean | true | Effacer la transcription au démarrage |
debug | boolean | false | Afficher le panneau de debug |
onEvent | function | — | Callback appelé pour chaque événement |
État retourné
| Propriété | Type | Description |
|---|---|---|
start() | () => Promise<void> | Démarre l’enregistrement |
stop() | () => Promise<void> | Arrête l’enregistrement |
pause() | () => void | Met en pause l’enregistrement |
resume() | () => void | Reprend l’enregistrement |
clear() | () => void | Efface la transcription |
clearDebugMessages() | () => void | Efface les messages de debug |
text | string | Texte complet (final + draft) |
finalText | string | Texte final uniquement |
draftText | string | Texte partiel en cours (dernier segment) |
displayText | string | Texte recommandé pour affichage (final + draft) |
segments | Segment[] | Liste ordonnée des segments |
isRecording | boolean | Enregistrement en cours |
status | "idle" | "listening" | "processing" | "ready" | Statut actuel |
audioLevel | number | Niveau audio (0-1) |
sessionId | string | null | ID de session |
error | Error | null | Dernière erreur rencontrée |
debugMessages | Array<{timestamp: Date, type: string, data: any, direction: 'in' | 'out'}> | Messages de debug |
Types TypeScript
type MedicalSpecialty = 'general' | 'radiology' | 'cardiology' | 'legal';
type RecordingStatus = 'idle' | 'listening' | 'processing' | 'ready';
type SegmentStatus = 'streaming' | 'interim' | 'final';
interface Segment {
id: string; // ID interne du frontend
backendId?: string; // ID externe du backend (UUID)
backendIndex?: number; // Index séquentiel du backend
text: string;
isFinal: boolean; // true seulement pour status='final'
status: SegmentStatus; // streaming | interim | final
timestamp: Date;
start?: number;
end?: number;
confidence?: number;
}
interface SDKEvents {
ready: { version: string; config: SDKConfig };
recording: { sessionId: string };
stopped: { sessionId: string };
transcription: {
text: string;
isFinal: boolean;
sessionId: string;
chunks: TranscriptionChunk[];
};
error: { message: string; code: string };
command: { command: string; executed: boolean };
}Gestion des transcriptions
Le hook gère automatiquement :
- Mises à jour : un segment avec même
backendIdpeut être mis à jour tant questatus !== 'final'. - Ordre : les segments sont triés par
backendIndex. - Replacements : application automatique des événements
batch.replace(remplacement de segments).
Logique côté UI
Le SDK applique automatiquement les événements batch.replace. Vous pouvez distinguer visuellement les segments selon leur statut :
{segments.map((segment) => (
<p
key={segment.id}
style={{
opacity: segment.isFinal ? 1 : 0.6,
fontWeight: segment.isFinal ? 'bold' : 'normal'
}}
>
{segment.text}
</p>
))}Événements
Le SDK expose les événements suivants via le callback onEvent :
| Événement | Type | Payload | Description |
|---|---|---|---|
ready | ready | { version: string; config: SDKConfig } | SDK prêt |
recording | recording | { sessionId: string } | Enregistrement démarré |
stopped | stopped | { sessionId: string } | Enregistrement arrêté |
transcription | transcription | { text: string; isFinal: boolean; ... } | Nouvelle transcription |
error | error | { message: string; code: string } | Erreur WebSocket ou audio |
command | command | { command: string; executed: boolean } | Commande exécutée |
Codes d’erreur
CLIENT_ERROR: Erreur côté client.PROTOCOL_ERROR: Violation du protocole.AUTH_ERROR: Erreur d’authentification.CONFIG_ERROR: Erreur de configuration.
Gestion d’erreurs
Erreurs courantes
| Code | Description | Action recommandée |
|---|---|---|
AUTH_ERROR | Token invalide/expiré | Vérifier le token |
PROTOCOL_ERROR | Violation du protocole | Vérifier l’ordre des appels |
CONFIG_ERROR | Erreur de configuration | Vérifier les paramètres |
Exemple de gestion d’erreur
const { error, start } = useEphia({
// ... config
onEvent: (type, event) => {
if (type === 'error') {
console.error('Erreur:', event.message, event.code);
// Gérer l'erreur selon le code
}
}
});Configuration audio
Le SDK utilise l’API getUserMedia avec des contraintes optimisées par défaut (16kHz, mono). La configuration audio est gérée automatiquement.
Exemples complets
Exemple 1 : Transcription médicale simple
import { useEphia } from '@ephia/transcribe-sdk';
function MedicalTranscription() {
const [report, setReport] = React.useState('');
const { start, stop, finalText, isRecording } = useEphia({
websocketUrl: 'wss://api.ephia.ai',
apiPrefix: '/api/v1',
token: 'YOUR_API_KEY',
specialty: 'radiology',
onEvent: (type, event) => {
if (type === 'transcription' && event.isFinal) {
setReport((prev) => prev + ' ' + event.text);
}
}
});
return (
<div>
<h1>Compte-rendu radiologique</h1>
<button onClick={isRecording ? stop : start}>
{isRecording ? 'Stop' : 'Start'}
</button>
<textarea value={report} onChange={(e) => setReport(e.target.value)} />
</div>
);
}Exemple 2 : UI custom avec segments
import { useEphia } from '@ephia/transcribe-sdk';
function CustomUI() {
const {
start,
stop,
finalText,
draftText,
segments,
isRecording,
status
} = useEphia({
websocketUrl: 'wss://api.ephia.ai',
apiPrefix: '/api/v1',
token: 'YOUR_API_KEY',
specialty: 'radiology'
});
return (
<div>
<button onClick={isRecording ? stop : start}>
{isRecording ? 'Stop' : 'Start'}
</button>
<p>Status: {status}</p>
<div>
<strong>Texte confirmé:</strong>
<p>{finalText}</p>
</div>
<div style={{ opacity: 0.6 }}>
<strong>En cours:</strong>
<p>{draftText}</p>
</div>
<div>
<strong>Segments:</strong>
{segments.map((segment) => (
<div
key={segment.id}
style={{
opacity: segment.isFinal ? 1 : 0.6,
borderLeft: `3px solid ${segment.isFinal ? 'green' : 'orange'}`
}}
>
<span>[{segment.status}]</span> {segment.text}
</div>
))}
</div>
</div>
);
}Bonnes pratiques
- Backend init : toujours générer l’URL WebSocket côté backend pour sécuriser la clé API.
- Une connexion = une session : ne pas ouvrir plusieurs WebSocket en parallèle.
- Gestion des segments : afficher visuellement la distinction entre
streaming,interimetfinal. - Erreurs micro : toujours gérer le cas où l’utilisateur refuse l’accès au microphone.
- Cleanup : le hook gère automatiquement le nettoyage au unmount.