@zyther/voice
v0.1.0
Published
Provider-agnostic TypeScript core for text-to-speech and speech-to-text.
Readme
@zyther/voice
Núcleo TypeScript provider-agnostic para Text-to-Speech (TTS) e Speech-to-Text (STT).
Estado atual: fundação do MVP com providers OpenAI, ElevenLabs e Deepgram. O Deepgram inclui transcrição REST e streaming WebSocket.
Instalação
npm install @zyther/voiceUso
import { createVoice } from "@zyther/voice";
import { MyProvider } from "./my-provider.js";
const voice = createVoice({ provider: new MyProvider() });
const audio = await voice.tts({
text: "Welcome to Zyther!",
language: "en-US",
voice: "default",
format: "mp3",
});
const transcription = await voice.stt({
audio: audio.audio,
language: "en-US",
format: audio.format,
});
console.log(transcription.text);Provider OpenAI
O provider real usa os endpoints oficiais /v1/audio/speech e /v1/audio/transcriptions. A chave pode ser fornecida explicitamente ou pela variável OPENAI_API_KEY.
import { createVoice, OpenAIVoiceProvider } from "@zyther/voice";
const voice = createVoice({
provider: new OpenAIVoiceProvider({
// Opcional quando OPENAI_API_KEY está configurada.
apiKey: process.env.OPENAI_API_KEY,
ttsModel: "gpt-4o-mini-tts",
sttModel: "gpt-transcribe",
}),
});O TTS aceita mp3, opus, wav e pcm. Para STT, esta primeira integração aceita mp3 e wav, conforme os formatos documentados para transcrição de arquivos. O campo language é usado como dica no STT; no TTS, o idioma é inferido do texto pelo serviço.
Os testes do provider usam um fetchImpl injetado e não fazem chamadas à OpenAI. A aplicação deve tratar VoiceProviderError, inclusive respostas como autenticação, limite de uso e indisponibilidade.
OpenAI GPT Realtime
O mesmo provider também expõe streamSTT() e streamTTS() através do WebSocket Realtime. A transcrição usa uma sessão type: "transcription" com gpt-live-transcribe; a síntese usa uma sessão type: "realtime" com gpt-4o-realtime-preview por padrão. Os modelos, voz, URL e sample rate são configuráveis.
const realtimeVoice = new OpenAIVoiceProvider({
apiKey: process.env.OPENAI_API_KEY,
realtimeModel: "gpt-4o-realtime-preview",
realtimeTranscriptionModel: "gpt-live-transcribe",
realtimeVoice: "alloy",
realtimeSampleRate: 24000,
});
async function* pcmInput() {
yield pcmChunk1;
yield pcmChunk2;
}
for await (const partial of realtimeVoice.streamSTT(pcmInput(), { language: "pt-BR" })) {
console.log("transcrição:", partial.text);
}
for await (const pcm of realtimeVoice.streamTTS({ text: "Olá!", format: "pcm" })) {
playPcm(pcm);
}O áudio Realtime deve ser PCM mono little-endian na taxa configurada, normalmente 24 kHz, e é enviado em Base64 dentro dos eventos JSON. Em browser ou mobile, não exponha uma chave persistente: use uma chave efêmera emitida pelo backend ou prefira WebRTC. A opção webSocketImpl permite injetar um transporte que implemente headers de servidor quando necessário.
Provider ElevenLabs
O provider ElevenLabs usa síntese avançada com vozes selecionáveis e os endpoints /v1/text-to-speech/{voice_id}, /v1/speech-to-text e /v1/voices.
import { createVoice, ElevenLabsVoiceProvider } from "@zyther/voice";
const voice = createVoice({
provider: new ElevenLabsVoiceProvider({
// Também pode ser definido em ELEVENLABS_API_KEY.
apiKey: process.env.ELEVENLABS_API_KEY,
voiceId: process.env.ELEVENLABS_VOICE_ID,
ttsModel: "eleven_multilingual_v2",
}),
});
const audio = await voice.tts({
text: "Olá, bem-vindo à Zyther!",
language: "pt-BR",
voice: "voice-id-opcional",
format: "mp3",
});
const voices = await voice.getVoices("pt-BR");O TTS aceita mp3, opus, wav e pcm. O formato detalhado enviado à ElevenLabs pode ser customizado por outputFormat, por exemplo mp3_44100_128. O STT usa o modelo scribe_v2 por padrão e aceita os formatos definidos pelo contrato do package. A ElevenLabs pode retornar confiança de idioma, que é normalizada em STTResponse.confidence.
Provider Deepgram
O provider Deepgram é especializado em transcrição: usa POST /v1/listen para arquivos completos e o WebSocket wss://api.deepgram.com/v1/listen para áudio em tempo real. A autenticação REST usa Authorization: Token ...; no WebSocket, o provider usa o subprotocolo token, API_KEY, compatível com browser e Node.
import { createVoice, DeepgramVoiceProvider } from "@zyther/voice";
const voice = createVoice({
provider: new DeepgramVoiceProvider({
// Também pode ser definido em DEEPGRAM_API_KEY.
apiKey: process.env.DEEPGRAM_API_KEY,
model: "nova-3",
interimResults: true,
smartFormat: true,
}),
});
const transcript = await voice.stt({
audio: wavBytes,
format: "wav",
language: "pt-BR",
});
async function* microphone() {
yield firstPcmChunk;
yield secondPcmChunk;
}
for await (const partial of voice.streamSTT(microphone(), { language: "pt-BR" })) {
console.log(partial.text);
}streamSTT() encaminha chunks binários, emite resultados parciais e finais e envia Finalize/CloseStream ao terminar a fonte. webSocketImpl pode ser injetado para runtimes específicos ou testes. O provider Deepgram atualmente não oferece TTS; chamar tts() retorna um erro explícito de capability.
Criando um provider
Um provider implementa VoiceProvider. O contrato mínimo exige tts() e stt(); streaming, listagem de vozes e detecção de idioma são capacidades opcionais. Assim, uma integração real pode evoluir sem acoplar a aplicação ao fornecedor.
import type { VoiceProvider } from "@zyther/voice";
export class MyProvider implements VoiceProvider {
readonly name = "my-provider";
async tts(request) {
// Chamada ao fornecedor e normalização da resposta.
return { audio: new Uint8Array(), format: request.format ?? "mp3", provider: this.name };
}
async stt(request) {
// Chamada ao fornecedor e normalização da transcrição.
return { text: "...", provider: this.name };
}
}O MockVoiceProvider existe apenas para testes locais e não produz áudio real.
Scripts
Na pasta do package:
npm run test:unit # testes unitários, sem chamadas externas
npm run test:integration # testes live, requer credenciais válidas
npm run typecheck
npm run build # dist + declarações TypeScriptOs testes live são opt-in e podem consumir créditos. Configure OPENAI_API_KEY, ELEVENLABS_API_KEY + ELEVENLABS_VOICE_ID e/ou DEEPGRAM_API_KEY antes de executá-los.
Próximos passos
Adicionar adapters de Web Streams/Node streams, testes de integração opcionais e o serviço HTTP da Zyther Voice API. Consulte ../../ROADMAP.md.
