@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
73 lines (72 loc) • 3.3 kB
JavaScript
/**
* Vertex authentication helpers for the realtime voice agent.
*
* The Gemini Live WebSocket authenticates to Vertex via Application Default
* Credentials (ADC). These helpers materialise ADC from the split
* `GOOGLE_AUTH_*` env fields when no credentials file is configured, and remove
* any Gemini Developer API key from the environment so `@google/genai` uses
* Vertex/ADC auth (not an API key) for the realtime WebSocket.
*
* See docs/features/livekit-voice-agent.md.
*/
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { logger } from "../../utils/logger.js";
/**
* Materialise Vertex ADC from the split `GOOGLE_AUTH_*` env fields.
*
* The google realtime plugin authenticates Vertex via ADC (it does not accept
* inline credentials), so this writes a temp service-account JSON and points
* `GOOGLE_APPLICATION_CREDENTIALS` at it — unless ADC is already configured.
* No-op when `GOOGLE_APPLICATION_CREDENTIALS` is set or the `GOOGLE_AUTH_*`
* fields are absent (auth then relies on ambient ADC).
*/
export function ensureVertexAdc() {
if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
return;
}
const clientEmail = process.env.GOOGLE_AUTH_CLIENT_EMAIL;
const rawPrivateKey = process.env.GOOGLE_AUTH_PRIVATE_KEY;
if (!clientEmail || !rawPrivateKey) {
logger.warn("[RealtimeVoiceAgent] No GOOGLE_APPLICATION_CREDENTIALS and no GOOGLE_AUTH_* fields — Vertex auth will rely on ambient ADC.");
return;
}
const credentials = {
type: process.env.GOOGLE_AUTH_TYPE ?? "service_account",
project_id: process.env.GOOGLE_AUTH_BREEZE_PROJECT_ID ??
process.env.GOOGLE_CLOUD_PROJECT_ID,
private_key_id: process.env.GOOGLE_AUTH_PRIVATE_KEY_ID,
private_key: rawPrivateKey.replace(/\\n/g, "\n"),
client_email: clientEmail,
token_uri: process.env.GOOGLE_AUTH_TOKEN_URI ??
"https://oauth2.googleapis.com/token",
};
const credentialsDir = mkdtempSync(path.join(os.tmpdir(), "vertex-adc-"));
const credentialsPath = path.join(credentialsDir, "adc.json");
writeFileSync(credentialsPath, JSON.stringify(credentials), {
mode: 0o600,
flag: "wx",
});
process.on("exit", () => {
rmSync(credentialsDir, { recursive: true, force: true });
});
process.env.GOOGLE_APPLICATION_CREDENTIALS = credentialsPath;
logger.info(`[RealtimeVoiceAgent] Vertex ADC written to ${credentialsPath} (project ${credentials.project_id}).`);
}
/**
* Force pure Vertex/ADC auth for the Gemini Live WebSocket.
*
* `@google/genai` 1.52+ uses a Gemini Developer API key for the realtime
* WebSocket auth even when `vertexai: true` and project/location are set, which
* Vertex rejects at the handshake (WS close 1006). The realtime worker only
* ever talks to Vertex, so remove these keys (only affects this process).
*/
export function clearGeminiApiKeyEnv() {
for (const key of ["GOOGLE_API_KEY", "GOOGLE_AI_API_KEY", "GEMINI_API_KEY"]) {
if (process.env[key]) {
delete process.env[key];
logger.info(`[RealtimeVoiceAgent] cleared ${key} so genai uses Vertex/ADC auth (not API key) for the Live WS.`);
}
}
}