SDK de React

useScribe: transcripción de voz a texto en tiempo real en React

Para obtener una visión general de Scribe y sus capacidades, consulta la visión general de Voz a Texto . Para ver guías de uso paso a paso, consulta la transmisión del lado del cliente.

Instalación

npm install @elevenlabs/react
# or
yarn add @elevenlabs/react
# or
pnpm install @elevenlabs/react

Usa la skill de voz a texto de ElevenLabs para transcribir audio desde tu asistente de programación con IA:

npx skills add elevenlabs/skills --skill speech-to-text

@elevenlabs/react vuelve a exportar todo desde @elevenlabs/client, así que no necesitas instalar ambos paquetes.

Uso

Aquí tienes un ejemplo funcional mínimo que se conecta a Scribe y muestra la transcripción en tiempo real:

import { useScribe } from "@elevenlabs/react";
import { useEffect } from "react";
function MyComponent() {
const scribe = useScribe({
modelId: "scribe_v2_realtime",
onPartialTranscript: (data) => {
console.log("Partial:", data.text);
},
onCommittedTranscript: (data) => {
console.log("Committed:", data.text);
},
});
// Start recording
const handleStart = async () => {
try {
const token = await fetchTokenFromServer();
await scribe.connect({
token,
microphone: {
echoCancellation: true,
noiseSuppression: true,
},
});
} catch (err) {
console.error("Failed to start recording:", err);
}
};
// Stop recording
const handleDisconnect = () => {
scribe.disconnect();
};
// Disconnect on unmount
useEffect(() => {
return () => {
if (scribe.isConnected) {
scribe.disconnect();
}
};
}, [scribe]);
return (
<div>
<button onClick={handleStart} disabled={scribe.isConnected}>
Start Recording
</button>
<button onClick={handleDisconnect} disabled={!scribe.isConnected}>
Stop
</button>
{scribe.partialTranscript && <p>Live: {scribe.partialTranscript}</p>}
<div>
{scribe.committedTranscripts.map((t) => (
<p key={t.id}>{t.text}</p>
))}
</div>
</div>
);
}

Obtener un token

Scribe requiere un token de un solo uso para la autenticación. Crea una ruta de API en tu servidor:

// Node.js server
app.get("/scribe-token", yourAuthMiddleware, async (req, res) => {
const response = await fetch("https://api.elevenlabs.io/v1/single-use-token/realtime_scribe", {
method: "POST",
headers: {
"xi-api-key": process.env.ELEVENLABS_API_KEY,
},
});
const data = await response.json();
res.json({ token: data.token });
});

Tu clave de API de ElevenLabs es confidencial. No la expongas nunca al cliente. Genera siempre el token en el servidor.

// Client
const fetchToken = async () => {
const response = await fetch("/scribe-token");
const { token } = await response.json();
return token;
};

Opciones del hook

Configura el hook con opciones y callbacks predeterminados:

const scribe = useScribe({
// Connection options (can be overridden in connect())
token: "optional-default-token",
modelId: "scribe_v2_realtime",
baseUri: "wss://api.elevenlabs.io",
// VAD options
commitStrategy: CommitStrategy.VAD,
vadSilenceThresholdSecs: 0.5,
vadThreshold: 0.5,
minSpeechDurationMs: 100,
minSilenceDurationMs: 500,
languageCode: "en",
// Microphone options (for automatic mode)
microphone: {
deviceId: "optional-device-id",
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
// Manual audio options (for file transcription)
audioFormat: AudioFormat.PCM_16000,
sampleRate: 16000,
// Auto-connect on mount
autoConnect: false,
// Event callbacks
onSessionStarted: () => console.log("Session started"),
onPartialTranscript: (data) => console.log("Partial:", data.text),
onCommittedTranscript: (data) => console.log("Committed:", data.text),
onCommittedTranscriptWithTimestamps: (data) => console.log("With timestamps:", data),
onError: (error) => console.error("Error:", error),
onAuthError: (data) => console.error("Auth error:", data.error),
onQuotaExceededError: (data) => console.error("Quota exceeded:", data.error),
onConnect: () => console.log("Connected"),
onDisconnect: () => console.log("Disconnected"),
});

Opciones de conexión

PropiedadTipoDescripción
tokenstringToken de un solo uso para la autenticación mediante WebSocket.
modelIdstringID del modelo (p. ej., "scribe_v2_realtime").
baseUristringURI base de WebSocket personalizada. El valor predeterminado es wss://api.elevenlabs.io.

Opciones de VAD

Estas opciones controlan cuándo se confirman automáticamente las transcripciones al usar la estrategia de confirmación VAD.

PropiedadTipoPredeterminadoDescripción
commitStrategyCommitStrategy"manual""manual" o "vad".
vadSilenceThresholdSecsnumber1.5Segundos de silencio antes de que VAD confirme (0.3-3.0).
vadThresholdnumber0.4Sensibilidad de VAD (0.1-0.9; cuanto menor, más sensible).
minSpeechDurationMsnumber100Duración mínima de voz en ms (50-2000).
minSilenceDurationMsnumber100Duración mínima de silencio en ms (50-2000).

Opciones de audio

PropiedadTipoDescripción
languageCodestringCódigo de idioma ISO-639-1 o ISO-639-3. Déjalo vacío para la detección automática.
microphoneobjectAjustes del micrófono para el modo micrófono. Consulta más abajo.
audioFormatAudioFormatFormato de codificación de audio para el modo manual (p. ej., AudioFormat.PCM_16000).
sampleRatenumberFrecuencia de muestreo para el modo manual. Debe coincidir con audioFormat.

El objeto microphone acepta:

PropiedadTipoDescripción
deviceIdstringID de un dispositivo de micrófono específico.
echoCancellationbooleanActiva la cancelación de eco.
noiseSuppressionbooleanActiva la supresión de ruido.
autoGainControlbooleanActiva el control automático de ganancia.

Opciones de comportamiento

PropiedadTipoPredeterminadoDescripción
autoConnectbooleanfalseSe conecta automáticamente al montar el componente.
includeTimestampsbooleanfalseRecibe marcas de tiempo a nivel de palabra. Se activa automáticamente cuando se proporciona onCommittedTranscriptWithTimestamps.

Callbacks

Todos los callbacks de eventos son opcionales y se pueden proporcionar como opciones del hook:

  • onConnect - controlador que se llama cuando se establece la conexión WebSocket.
  • onDisconnect - controlador que se llama cuando se cierra la conexión WebSocket.
  • onSessionStarted - controlador que se llama cuando se inicia la sesión de Scribe.
  • onPartialTranscript - controlador que se llama con resultados de transcripción provisionales. Recibe { text: string }.
  • onCommittedTranscript - controlador que se llama con resultados de transcripción finalizados. Recibe { text: string }.
  • onCommittedTranscriptWithTimestamps - controlador que se llama con resultados de transcripción finalizados, incluido el tiempo a nivel de palabra. Recibe { text: string; words?: { start: number; end: number }[] }.
  • onError - controlador de errores genérico para todos los errores. Recibe Error | Event.
  • onAuthError - controlador que se llama cuando se producen errores de autenticación. Recibe { error: string }.

Callbacks de error

El callback genérico onError se activa para todos los errores. También hay callbacks de error específicos disponibles para una gestión más detallada. Todos los callbacks de error específicos reciben { error: string }.

CallbackDescripción
onErrorControlador de errores genérico para todos los errores.
onAuthErrorError de autenticación.
onQuotaExceededErrorSe ha superado la cuota de uso.
onCommitThrottledErrorSolicitud de confirmación limitada.
onTranscriberErrorError del motor de transcripción.
onUnacceptedTermsErrorNo se han aceptado los términos de servicio.
onRateLimitedErrorLímite de frecuencia alcanzado.
onInputErrorFormato de entrada no válido.
onQueueOverflowErrorCola de procesamiento llena.
onResourceExhaustedErrorRecursos del servidor al límite de capacidad.
onSessionTimeLimitExceededErrorSe ha alcanzado el tiempo máximo de sesión.
onChunkSizeExceededErrorFragmento de audio demasiado grande.
onInsufficientAudioActivityErrorNo hay suficiente actividad de audio para mantener la conexión.

Modo micrófono

Transmite audio directamente desde el micrófono del usuario:

function MicrophoneTranscription() {
const scribe = useScribe({
modelId: "scribe_v2_realtime",
});
const startRecording = async () => {
const token = await fetchToken();
await scribe.connect({
token,
microphone: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
});
};
return (
<div>
<button onClick={startRecording} disabled={scribe.isConnected}>
{scribe.status === "connecting" ? "Connecting..." : "Start"}
</button>
<button onClick={scribe.disconnect} disabled={!scribe.isConnected}>
Stop
</button>
{scribe.partialTranscript && (
<div>
<strong>Speaking:</strong> {scribe.partialTranscript}
</div>
)}
{scribe.committedTranscripts.map((transcript) => (
<div key={transcript.id}>{transcript.text}</div>
))}
</div>
);
}

Modo de audio manual (transcripción de archivos)

Transcribe archivos de audio pregrabados:

import { useScribe, AudioFormat } from "@elevenlabs/react";
import { useState } from "react";
function FileTranscription() {
const [file, setFile] = useState<File | null>(null);
const scribe = useScribe({
modelId: "scribe_v2_realtime",
audioFormat: AudioFormat.PCM_16000,
sampleRate: 16000,
});
const transcribeFile = async () => {
if (!file) return;
const token = await fetchToken();
await scribe.connect({ token });
// Decode audio file
const arrayBuffer = await file.arrayBuffer();
const audioContext = new AudioContext({ sampleRate: 16000 });
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
// Convert to PCM16
const channelData = audioBuffer.getChannelData(0);
const pcmData = new Int16Array(channelData.length);
for (let i = 0; i < channelData.length; i++) {
const sample = Math.max(-1, Math.min(1, channelData[i]));
pcmData[i] = sample < 0 ? sample * 32768 : sample * 32767;
}
// Send in chunks
const chunkSize = 4096;
for (let offset = 0; offset < pcmData.length; offset += chunkSize) {
const chunk = pcmData.slice(offset, offset + chunkSize);
const bytes = new Uint8Array(chunk.buffer);
const base64 = btoa(String.fromCharCode(...bytes));
scribe.sendAudio(base64);
await new Promise((resolve) => setTimeout(resolve, 50));
}
// Commit transcription
scribe.commit();
};
return (
<div>
<input type="file" accept="audio/*" onChange={(e) => setFile(e.target.files?.[0] || null)} />
<button onClick={transcribeFile} disabled={!file || scribe.isConnected}>
Transcribe
</button>
{scribe.committedTranscripts.map((transcript) => (
<div key={transcript.id}>{transcript.text}</div>
))}
</div>
);
}

Valores devueltos

Estado

  • status - estado actual de la conexión: "disconnected", "connecting", "connected", "transcribing" o "error".
  • isConnected - valor booleano que indica si está conectado.
  • isTranscribing - valor booleano que indica si está transcribiendo activamente.
  • partialTranscript - cadena de la transcripción parcial (provisional) actual.
  • committedTranscripts - matriz de objetos TranscriptSegment (consulta más abajo).
  • error - mensaje de error actual o null.
const scribe = useScribe(/* options */);
console.log(scribe.status); // "connected"
console.log(scribe.isConnected); // true
console.log(scribe.partialTranscript); // "hello world"
console.log(scribe.committedTranscripts); // [{ id: "...", text: "...", words: ..., isFinal: true }]
console.log(scribe.error); // null or error string

Cada segmento de transcripción confirmado tiene la siguiente estructura:

interface TranscriptSegment {
id: string; // Unique identifier
text: string; // Transcript text
timestamp: number; // Unix timestamp
isFinal: boolean; // Always true for committed transcripts
}

Métodos

connect(options?)

Conéctate a Scribe. Las opciones proporcionadas aquí reemplazan los valores predeterminados del hook:

await scribe.connect({
token: "your-token", // Required
microphone: {
/* ... */
}, // For microphone mode
// OR
audioFormat: AudioFormat.PCM_16000, // For manual mode
sampleRate: 16000,
});

disconnect()

Desconecta y libera recursos:

scribe.disconnect();

sendAudio(audioBase64, options?)

Envía datos de audio (solo modo manual):

scribe.sendAudio(base64AudioChunk, {
commit: false, // Optional: commit immediately
sampleRate: 16000, // Optional: override sample rate
previousText: "Previous transcription text", // Optional: context from a previous transcription. Can only be sent in the first audio chunk.
});

El campo previousText solo se puede enviar en el primer fragmento de audio de una sesión. Enviarlo en fragmentos posteriores provoca un error.

commit()

Confirma manualmente la transcripción actual:

scribe.commit();

clearTranscripts()

Borra todas las transcripciones del estado:

scribe.clearTranscripts();

getConnection()

Obtén la instancia de conexión subyacente:

const connection = scribe.getConnection();
// Returns RealtimeConnection | null

Estrategias de confirmación

Controla cuándo se confirman las transcripciones:

import { CommitStrategy } from '@elevenlabs/react';
// Manual (default) - you control when to commit
const scribe = useScribe({
commitStrategy: CommitStrategy.MANUAL,
});
// Later...
scribe.commit(); // Commit transcription
// Voice Activity Detection - model detects silences and automatically commits
const scribe = useScribe({
commitStrategy: CommitStrategy.VAD,
});

Para obtener más información, consulta Transcripciones y estrategias de confirmación.

Ejemplo completo

Aquí tienes un ejemplo completo de un componente de React que utiliza el hook useScribe con una estrategia de confirmación basada en VAD:

import { useScribe, CommitStrategy } from "@elevenlabs/react";
import { useEffect } from "react";
function ScribeDemo() {
const scribe = useScribe({
modelId: "scribe_v2_realtime",
commitStrategy: CommitStrategy.VAD,
onSessionStarted: () => console.log("Started"),
onCommittedTranscript: (data) => console.log("Committed:", data.text),
onError: (error) => console.error("Error:", error),
});
const startMicrophone = async () => {
const token = await fetchToken();
await scribe.connect({
token,
microphone: {
echoCancellation: true,
noiseSuppression: true,
},
});
};
const handleDisconnect = () => scribe.disconnect();
const handleClearTranscripts = () => scribe.clearTranscripts();
useEffect(() => {
return () => {
handleDisconnect();
};
}, []);
return (
<div>
<h1>Scribe Demo</h1>
{/* Status */}
<div>
Status: {scribe.status}
{scribe.error && <span>Error: {scribe.error}</span>}
</div>
{/* Controls */}
<div>
{!scribe.isConnected ? (
<button onClick={startMicrophone}>Start Recording</button>
) : (
<button onClick={handleDisconnect}>Stop</button>
)}
<button onClick={handleClearTranscripts}>Clear</button>
</div>
{/* Live Transcript */}
{scribe.partialTranscript && (
<div>
<strong>Live:</strong> {scribe.partialTranscript}
</div>
)}
{/* Committed Transcripts */}
<div>
<h2>Transcripts ({scribe.committedTranscripts.length})</h2>
{scribe.committedTranscripts.map((t) => (
<div key={t.id}>
<span>{new Date(t.timestamp).toLocaleTimeString()}</span>
<p>{t.text}</p>
</div>
))}
</div>
</div>
);
}