Skip to main content

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.

@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:partial by plain concatenation of those deltas
  • on stop(), the transcriber disables the mic, sends a single input_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.language is 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 pending start() 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 emits stt:error
  • a stop that times out also rejects stop() and emits stt:error — a partial draft is never returned as a successful stt: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/web when you want the fewest moving parts and browser support is good enough.
  • Use @charivo/stt/openai when you are testing direct vendor behavior.
  • Move to Realtime when you want continuous session-based voice interaction instead of turn-based transcription.

References