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.
Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.