| name | lib-gradium |
| description | Teaches the agent how to use the `@agsk/lib-gradium` browser SDK in this monorepo to add real-time Gradium STT to any browser app. Use when integrating voice transcription into the pi-reviewer extension (or any other browser UI) instead of calling the raw Gradium WebSocket directly. Also documents the local-proxy requirement. |
@agsk/lib-gradium
Browser SDK for Gradium streaming speech-to-text. Lives at libs/lib-gradium/ in this workspace and is imported as @agsk/lib-gradium from any consumer (e.g. pi-reviewer). For the underlying wire protocol, see the gradium-sdk skill.
What it handles for you
- Mic capture via
getUserMedia + AudioContext(sampleRate: 24000) + AudioWorklet (24 kHz, 16-bit PCM, 1920-sample = 80 ms frames).
- WebSocket lifecycle: opens, sends the
setup message, waits for the server's ready before starting audio capture (critical — sending audio early causes "Message validation error").
- Server message dispatch:
text / end_text / step (VAD) / error normalized into TranscriptionEvent, VadEvent, and Error callbacks.
Proxy requirement (browser-only constraint)
Gradium authenticates with an x-api-key HTTP header on the WebSocket handshake. Browser WebSocket cannot set headers. So wsUrl must point at a Node proxy that accepts a local WebSocket and re-opens to Gradium with the header attached.
A reference proxy ships as a Vite plugin at libs/lib-gradium/demo/vite.config.ts — copy it into any Vite-based consumer, or port it to whatever host process runs the UI.
Running in a Node/Bun/Electron host? You can skip the proxy and pass the real Gradium URL as wsUrl, but you'll also need to monkey-patch WebSocket with a version that supports headers (e.g. the ws npm package).
Install
Already in the workspace — pi-reviewer/package.json depends on it via file:../libs/lib-gradium and imports as:
import { GradiumSTT, type TranscriptionEvent, type VadEvent } from '@agsk/lib-gradium';
If adding to a new package in this monorepo, declare "@agsk/lib-gradium": "file:../../libs/lib-gradium" in its package.json and run npm install at the repo root.
API
class GradiumSTT {
constructor(config: { apiKey: string; wsUrl?: string });
onTranscription: (e: { type: 'transcription'; text: string; is_final: boolean }) => void;
onVad: (e: { type: 'vad'; confidence: number }) => void;
onError: (err: Error) => void;
onRaw: (msg: unknown) => void;
start(): Promise<void>;
stop(): void;
}
Transcription semantics
is_final: false — a live partial. text is the accumulated transcript for the current turn (SDK concatenates incoming text server messages). Safe to render as-is.
is_final: true — fires once per turn, when the server sends end_text. text is the final utterance. After this, the accumulator resets.
Usage
import { GradiumSTT } from '@agsk/lib-gradium';
const stt = new GradiumSTT({
apiKey: import.meta.env.GRADIUM_API_KEY,
wsUrl: 'ws://127.0.0.1:5174',
});
stt.onTranscription = (e) => {
if (e.is_final) console.log('final:', e.text);
else console.log('partial:', e.text);
};
stt.onVad = (e) => {
if (e.confidence > 0.5) console.log('speaker paused');
};
stt.onError = (err) => console.error(err);
document.getElementById('start')!.onclick = () => stt.start();
document.getElementById('stop')!.onclick = () => stt.stop();
Demo
A working end-to-end demo is at libs/lib-gradium/demo/ — Vite dev server + embedded proxy + minimal HTML harness. Run:
npm run dev -w @agsk/lib-gradium-demo
The demo reads GRADIUM_API_KEY from the monorepo-root .env. Verify any changes to the SDK here before wiring them into pi-reviewer.
Best practices
- Call
start() from a user gesture. Browsers block getUserMedia outside of user-initiated event handlers.
- Let the SDK gate on
ready. Don't send audio or any other frames yourself — the SDK waits for the server's ready message before connecting the audio graph.
- Don't bundle the API key into the browser. The demo does for local convenience, but any shipped app should terminate auth at the proxy (the proxy injects the header from a server-side secret; the browser talks to the proxy with no key).
- Use
onRaw for debugging unknown server behavior. If Gradium starts sending a new message type, it'll show up there before the SDK's dispatch is updated.