Build on the supported package contract
Use @absolutejs/voice-google-speech through its supported public entry points.
@absolutejs/voice-google-speechv0.0.1-beta.6betaVoice & MediaGoogle Cloud Speech-to-Text adapter for @absolutejs/voice — buffered-batch (REST) + real-time streaming (gRPC-Web over HTTP/2, no @grpc/grpc-js dep)
bun add @absolutejs/voice-google-speechGoogle Cloud Speech-to-Text adapter for @absolutejs/voice. Ships two complementary modes — pick whichever fits your latency / cost / infrastructure tradeoffs:
googleSpeech(...) — buffered-batch via REST /v1/speech:recognize. Accumulate PCM, POST on flush() / close(), get one final transcript per turn. Good for post-call analysis and short utterances; simplest auth (API key, OAuth, or refresh hook).
googleSpeechStream(...) — real-time bidirectional streaming via Google Speech v2 StreamingRecognize. Speaks gRPC over HTTP/2 directly (node:http2) with hand-rolled protobuf wire-format for the request/response messages and gRPC-Web framing. No @grpc/grpc-js dependency. Use this when you want partial transcripts as the caller is speaking.
OAuth refresh (preferred for long-running deployments):
What this adapter does protocol-wise
Opens a single HTTP/2 stream against https://speech.googleapis.com/google.cloud.speech.v2.Speech/StreamingRecognize.
Headers: Authorization: Bearer , Content-Type: application/grpc-web+proto, X-Grpc-Web: 1, TE: trailers.
Sends the first message as a StreamingRecognizeRequest carrying recognizer = projects/{project}/locations/{location}/recognizers/_ and a streaming_config derived from STTAdapterOpenOptions.format + your options.
Each call to session.send(audio) encodes a StreamingRecognizeRequest { audio = }, wraps it in a 5-byte gRPC frame, and writes it to the HTTP/2 stream.
Reads StreamingRecognizeResponse frames as they arrive, decodes results[].alternatives[0], and emits partial events while is_final = false, final events when is_final = true. result_end_offset is lifted to transcript.endedAtMs, language_code to transcript.language, confidence to transcript.confidence.
speech_event_type values of END_OF_SINGLE_UTTERANCE or SPEECH_ACTIVITY_END emit an endOfTurn (reason: "vendor").
Trailer frame (gRPC-Web high-bit flag) or HTTP/2 trailer with non-zero grpc-status emits an error event with code = and message "Google Speech gRPC status : ".
Streaming options
Option — Required — Default — Notes
accessToken / getAccessToken — one of — — — OAuth Bearer token (or async refresh hook). API keys are not supported on the streaming RPC; use the buffered-batch googleSpeech(...) for API-key flows.
project — yes — — — Google Cloud project id; combined with location to form the recognizer resource path.
See the existing buffered-batch section for the REST-based API key / OAuth flow. The streaming and batch exports can coexist in the same project; pick per-route.
Bun's fetch is half-duplex (request body must finish before the response body starts), which is why the streaming adapter uses node:http2 directly instead of fetch. Bun's node:http2 polyfill supports the bidirectional pattern this adapter needs.
The protobuf encoder/decoder is hand-rolled and covers only the fields actually used by StreamingRecognizeRequest/StreamingRecognizeResponse. If Google adds new fields you want surfaced (alternatives beyond [0], word-level timing, channel tags, etc.), open an issue.
For STTAdapterOpenOptions.format.encoding: pcm_s16le → LINEAR16, mulaw / pcm_mulaw → MULAW, alaw / pcm_alaw → ALAW. Other encodings are rejected at open time.
session.close(reason) ends the request stream cleanly and emits close. Any in-flight transcripts that arrive after close are dropped.
Outcomes
Use @absolutejs/voice-google-speech through its supported public entry points.
Hardening checklist
Follow in order
Working example for Streaming (googleSpeechStream).
import { voice } from "@absolutejs/voice";
import { googleSpeechStream } from "@absolutejs/voice-google-speech";
const app = voice({
stt: googleSpeechStream({
project: process.env.GOOGLE_PROJECT_ID!,
accessToken: await mintBearerToken(),
// optional:
location: "global", // default
model: "latest_long", // default; or 'latest_short' / 'chirp_2' / 'telephony' / ...
language: "en-US",
languages: ["en-US"], // used unless languageStrategy overrides
interimResults: true, // default — partial transcripts emitted
enableVoiceActivityEvents: true, // surface SPEECH_ACTIVITY_END as endOfTurn
enableAutomaticPunctuation: true,
enableWordConfidence: true,
}),
});OAuth refresh (preferred for long-running deployments):
googleSpeechStream({
project,
getAccessToken: async () => await refreshGoogleAccessToken(),
});Supported entry points declared by this package manifest.
Package entry point declared in package.json.
Scripts declared by this package manifest.