STT
Charivo's STT layer combines @charivo/stt with a concrete transcriber.
For production browser apps, use the remote transcriber with a server route
backed by @charivo/server/openai.
Recommended Stack
@charivo/stt
@charivo/stt/remote
your /api/stt route
@charivo/server/openai
The browser records locally. The backend handles transcription.
Basic Setup
import { Charivo } from "@charivo/core";
import { createSTTManager } from "@charivo/stt";
import { createRemoteSTTTranscriber } from "@charivo/stt/remote";
const charivo = new Charivo();
charivo.attachSTT(
createSTTManager(createRemoteSTTTranscriber({ apiEndpoint: "/api/stt" })),
);
await charivo.getSTTManager()?.start({ language: "en" });
const text = await charivo.getSTTManager()?.stop();
Transcriber Choices
Remote
@charivo/stt/remote- records in the browser and sends audio to your route as multipart form data
- best default for production browser apps
Direct OpenAI
@charivo/stt/openai- useful for local development and testing
- exposes credentials to the browser
Browser-Native
@charivo/stt/web- built on the Web Speech API
- useful for prototypes and zero-server flows
- browser support varies
Streaming (live)
@charivo/stt/openai-realtime- WebRTC transcriber backed by an OpenAI Realtime transcription session (
gpt-realtime-whisper) - credential-free: the app supplies a
bootstrap(request) => Promise<{ answerSdp }>function that owns credentials and the SDP exchange - transcript deltas stream live as the user speaks, each carrying its own spacing
- drafts relay via
stt:partialby plain concatenation of those deltas - on
stop(), the transcriber disables the mic, sends a singleinput_audio_buffer.commit, and resolves with the joined authoritative final transcript
Limitations:
- no server VAD — press-to-start / press-to-stop only
- language auto-detects unless
STTOptions.languageis set - calling
stop()before connect finishes cancels the pending start and resolves with an empty transcript, releasing the microphone and any partially-opened connection; the pendingstart()call itself rejects - a mid-session failure does not push an event on its own — it surfaces the next time the app calls
stop(), which rejects and emitsstt:error - a stop that times out also rejects
stop()and emitsstt:error— a partial draft is never returned as a successfulstt:stop - a small RTP-vs-data-channel tail race means the last fraction of a second of audio may rarely be truncated
- packaged Electron apps behind UDP-blocking proxies may fail to establish the WebRTC connection
What @charivo/stt Owns
- recording lifecycle
- interaction with the transcriber implementation
- STT lifecycle and error events back into core
- relaying interim transcript drafts (
stt:partial) from streaming transcribers
STTManager intentionally uses setEventEmitter(...) rather than the full
event bus.
Provider Route
The remote transcriber usually pairs with @charivo/server/openai on the
server:
const provider = createOpenAISTTProvider({
apiKey: process.env.OPENAI_API_KEY!,
defaultModel: "whisper-1",
});
const text = await provider.transcribe(audioBlob, {
language: "en",
});
Alternatives
- Use
@charivo/stt/webwhen you want the fewest moving parts and browser support is good enough. - Use
@charivo/stt/openaiwhen you are testing direct vendor behavior. - Move to Realtime when you want continuous session-based voice interaction instead of turn-based transcription.