Voice
Add voice and realtime audio capabilities to Cogitator agents with @cogitator-ai/voice — speech-to-text, text-to-speech, voice activity detection, and native speech-to-speech via OpenAI Realtime and Gemini Live APIs.
What is @cogitator-ai/voice?
@cogitator-ai/voice brings voice capabilities to any Cogitator agent. It supports two fundamentally different approaches to voice interaction:
Pipeline mode routes audio through a classic STT -> Agent -> TTS loop. Your agent receives text, responds with text, and the voice package handles all audio conversion. This works with any LLM backend — OpenAI, Anthropic, Google, Ollama, or anything else Cogitator supports.
Realtime mode uses native speech-to-speech APIs (OpenAI Realtime or Gemini Live) where the model directly processes and generates audio. Lower latency, more natural prosody, but limited to providers that offer realtime APIs.
Pipeline Mode:
┌──────┐ ┌─────┐ ┌───────┐ ┌─────┐ ┌──────┐
│ Mic │───>│ STT │───>│ Agent │───>│ TTS │───>│ Spkr │
└──────┘ └─────┘ └───────┘ └─────┘ └──────┘
Realtime Mode:
┌──────┐ ┌───────────────────┐ ┌──────┐
│ Mic │───>│ Realtime LLM API │───>│ Spkr │
└──────┘ └───────────────────┘ └──────┘Installation
pnpm add @cogitator-ai/voice
pnpm add openai # only for OpenAISTT / OpenAITTS
pnpm add onnxruntime-node # only for Silero VADopenai is an optional peer dependency, needed only for OpenAISTT and OpenAITTS. They load it on first use, so the package works without it when you use Deepgram, ElevenLabs or the realtime providers; if it is missing, the first transcribe / synthesize call throws with an install hint. @cogitator-ai/core is needed for createCogitatorRunner() with a Cogitator agent.
Quick Start — Pipeline
import { Cogitator, Agent } from '@cogitator-ai/core';
import {
VoiceAgent,
OpenAISTT,
OpenAITTS,
EnergyVAD,
createCogitatorRunner,
} from '@cogitator-ai/voice';
const cogitator = new Cogitator({ memory: { adapter: 'memory' } });
const agent = new Agent({
name: 'assistant',
model: 'openai/gpt-6-luna',
instructions: 'You are a helpful voice assistant. Keep answers short.',
});
const voiceAgent = new VoiceAgent({
mode: 'pipeline',
agent: createCogitatorRunner(cogitator, agent),
stt: new OpenAISTT({ apiKey: process.env.OPENAI_API_KEY! }),
tts: new OpenAITTS({ apiKey: process.env.OPENAI_API_KEY! }),
vad: new EnergyVAD(),
});
await voiceAgent.listen(8080);Connect from any WebSocket client at ws://localhost:8080/voice — send binary PCM16 audio frames, receive audio and JSON events back (see WebSocket protocol).
createCogitatorRunner() adapts a Cogitator runtime and Agent to the VoiceAgentRunner interface (run(input, { sessionId, signal })): each voice session gets its own memory thread (voice:<sessionId>) and interrupted turns abort the run. Pass { threadId: (ctx) => ... } as the third argument to choose threads yourself, e.g. to continue a user's chat history:
const runner = createCogitatorRunner(cogitator, agent, {
threadId: ({ sessionId }) => `call:${sessionId}`,
});Any object with run(input, context?) => Promise<{ content }> (and optionally instructions) works as the runner.
Quick Start — Realtime
import { VoiceAgent, createCogitatorRunner } from '@cogitator-ai/voice';
const voiceAgent = new VoiceAgent({
agent: createCogitatorRunner(cogitator, agent),
mode: 'realtime',
realtimeProvider: 'gemini',
realtimeApiKey: process.env.GOOGLE_API_KEY!,
voice: 'Puck',
});
await voiceAgent.listen(8080);Components
| Component | Description |
|---|---|
| STT Providers | OpenAI and Deepgram speech-to-text |
| TTS Providers | OpenAI and ElevenLabs text-to-speech |
| VAD | Energy-based and Silero neural voice activity detection |
| Pipeline | Batch and streaming STT -> Agent -> TTS processing |
| Realtime | Native speech-to-speech with OpenAI and Gemini |
Audio Format
All raw audio in the voice package is PCM16: 16-bit signed integers, little-endian, mono.
| Path | Input | Output |
|---|---|---|
| Pipeline (STT streams, VAD) | 16kHz (Deepgram and VAD take a sampleRate) | MP3 by default, or ttsOptions.format |
| OpenAI Realtime | 24kHz | 24kHz |
| Gemini Live | 16kHz | 24kHz |
STT providers also accept containerized files (wav, mp3, ogg, flac, webm, mp4); the container is detected from magic bytes. The pipeline passes its ttsOptions to every TTS call; without them it gets the provider's default format (MP3). Set ttsOptions: { format: 'pcm16' } on the pipeline or VoiceAgent for raw 24kHz PCM (see TTS options).
The package includes audio conversion utilities:
import {
float32ToPcm16,
pcm16ToFloat32,
pcmToWav,
wavToPcm,
resample,
calculateRMS,
detectAudioFormat,
} from '@cogitator-ai/voice';Voice Tools
voiceTools({ stt, tts }) returns two tools for text agents that handle audio as data: transcribe_audio ({ audioBase64, language? } → { text, language, duration }) and speak_text ({ text, voice?, format? } → { audioBase64, format }). They are plain { name, description, parameters, execute } objects with Zod parameters — wrap them with tool() from @cogitator-ai/core:
import { Agent, tool } from '@cogitator-ai/core';
import { voiceTools, DeepgramSTT, ElevenLabsTTS } from '@cogitator-ai/voice';
const [transcribe, speak] = voiceTools({
stt: new DeepgramSTT({ apiKey: process.env.DEEPGRAM_API_KEY! }),
tts: new ElevenLabsTTS({ apiKey: process.env.ELEVENLABS_API_KEY! }),
});
const agent = new Agent({
name: 'media-assistant',
model: 'openai/gpt-5.5',
instructions: 'Transcribe audio the user sends and answer with speech when asked.',
tools: [tool(transcribe), tool(speak)],
});transcribeTool(stt) and speakTool(tts) create them one at a time.
Next Steps
- STT Providers — configure speech recognition
- TTS Providers — configure speech synthesis
- VAD — detect when users start and stop speaking
- Pipeline Mode — batch and streaming voice processing
- Realtime Mode — native speech-to-speech APIs