SDKsSDK ReactIndex

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-sdk

Authentification

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 frontend

Option 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

OptionTypeDéfautDescription
websocketUrlstringrequisURL de base du serveur WebSocket (ex: "wss://api.ephia.ai")
apiPrefixstring"/api/v1"Préfixe de l’API
tokenstringToken API (optionnel si passé dans l’URL)
specialtyMedicalSpecialty"general"Domaine ("general", "radiology", "cardiology", "legal")
sampleRatenumber16000Taux d’échantillonnage audio
channelsnumber1Nombre de canaux audio
presetstringConfiguration prédéfinie ("realtime", "balanced", "quality", etc.)
audioDurationThresholdnumberSeuil de durée audio avant déclenchement (secondes)
silenceThresholdnumberSeuil de silence pour détection de fin de phrase (secondes)
maxBufferSizenumberTaille maximale du buffer de chunks
flushTimeoutnumberTimeout avant flush automatique (secondes)
gladiaEndpointingnumberParamètre d’endpointing pour détection de fin de phrase (secondes)
enableFinalProofreadingbooleantrueActive la relecture finale pour améliorer la qualité
autoInjectbooleanfalseInjection automatique du texte dans un élément
clearOnStartbooleantrueEffacer la transcription au démarrage
debugbooleanfalseAfficher le panneau de debug
onEventfunctionCallback appelé pour chaque événement

État retourné

PropriétéTypeDescription
start()() => Promise<void>Démarre l’enregistrement
stop()() => Promise<void>Arrête l’enregistrement
pause()() => voidMet en pause l’enregistrement
resume()() => voidReprend l’enregistrement
clear()() => voidEfface la transcription
clearDebugMessages()() => voidEfface les messages de debug
textstringTexte complet (final + draft)
finalTextstringTexte final uniquement
draftTextstringTexte partiel en cours (dernier segment)
displayTextstringTexte recommandé pour affichage (final + draft)
segmentsSegment[]Liste ordonnée des segments
isRecordingbooleanEnregistrement en cours
status"idle" | "listening" | "processing" | "ready"Statut actuel
audioLevelnumberNiveau audio (0-1)
sessionIdstring | nullID de session
errorError | nullDernière erreur rencontrée
debugMessagesArray<{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 backendId peut être mis à jour tant que status !== '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énementTypePayloadDescription
readyready{ version: string; config: SDKConfig }SDK prêt
recordingrecording{ sessionId: string }Enregistrement démarré
stoppedstopped{ sessionId: string }Enregistrement arrêté
transcriptiontranscription{ text: string; isFinal: boolean; ... }Nouvelle transcription
errorerror{ message: string; code: string }Erreur WebSocket ou audio
commandcommand{ 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

CodeDescriptionAction recommandée
AUTH_ERRORToken invalide/expiréVérifier le token
PROTOCOL_ERRORViolation du protocoleVérifier l’ordre des appels
CONFIG_ERRORErreur de configurationVé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

  1. Backend init : toujours générer l’URL WebSocket côté backend pour sécuriser la clé API.
  2. Une connexion = une session : ne pas ouvrir plusieurs WebSocket en parallèle.
  3. Gestion des segments : afficher visuellement la distinction entre streaming, interim et final.
  4. Erreurs micro : toujours gérer le cas où l’utilisateur refuse l’accès au microphone.
  5. Cleanup : le hook gère automatiquement le nettoyage au unmount.

Support