diff --git a/extensions/elevenlabs/realtime-transcription-provider.test.ts b/extensions/elevenlabs/realtime-transcription-provider.test.ts index 295aa429a785..e13f5015cf28 100644 --- a/extensions/elevenlabs/realtime-transcription-provider.test.ts +++ b/extensions/elevenlabs/realtime-transcription-provider.test.ts @@ -2,9 +2,18 @@ import { createServer } from "node:http"; import type { AddressInfo } from "node:net"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import type WebSocket from "ws"; import { WebSocketServer } from "ws"; + +const { resolveElevenLabsApiKeyWithProfileFallbackMock } = vi.hoisted(() => ({ + resolveElevenLabsApiKeyWithProfileFallbackMock: vi.fn(), +})); + +vi.mock("./config-api.js", () => ({ + resolveElevenLabsApiKeyWithProfileFallback: resolveElevenLabsApiKeyWithProfileFallbackMock, +})); + import { buildElevenLabsRealtimeTranscriptionProvider } from "./realtime-transcription-provider.js"; let cleanup: (() => Promise) | undefined; @@ -48,6 +57,8 @@ describe("buildElevenLabsRealtimeTranscriptionProvider", () => { afterEach(async () => { await cleanup?.(); cleanup = undefined; + vi.unstubAllEnvs(); + resolveElevenLabsApiKeyWithProfileFallbackMock.mockReset(); }); it("normalizes nested provider config", () => { @@ -160,4 +171,16 @@ describe("buildElevenLabsRealtimeTranscriptionProvider", () => { expect(requests[0]?.searchParams.get("commit_strategy")).toBe("vad"); expect(requests[0]?.searchParams.get("language_code")).toBe("en"); }); + + it("rejects whitespace-only environment keys before session creation", () => { + resolveElevenLabsApiKeyWithProfileFallbackMock.mockReturnValue(null); + vi.stubEnv("ELEVENLABS_API_KEY", ""); + vi.stubEnv("XI_API_KEY", " "); + const provider = buildElevenLabsRealtimeTranscriptionProvider(); + + expect(provider.isConfigured({ cfg: {} as OpenClawConfig, providerConfig: {} })).toBe(false); + expect(() => provider.createSession({ providerConfig: {} })).toThrow( + "ElevenLabs API key missing", + ); + }); }); diff --git a/extensions/elevenlabs/realtime-transcription-provider.ts b/extensions/elevenlabs/realtime-transcription-provider.ts index 07ee3d6b73f2..048a2afd4a46 100644 --- a/extensions/elevenlabs/realtime-transcription-provider.ts +++ b/extensions/elevenlabs/realtime-transcription-provider.ts @@ -247,6 +247,16 @@ function createElevenLabsRealtimeTranscriptionSession( }); } +function resolveElevenLabsRealtimeApiKey( + config: ElevenLabsRealtimeTranscriptionProviderConfig, +): string | null | undefined { + return ( + config.apiKey ?? + resolveElevenLabsApiKeyWithProfileFallback() ?? + normalizeOptionalString(process.env.XI_API_KEY) + ); +} + export function buildElevenLabsRealtimeTranscriptionProvider(): RealtimeTranscriptionProviderPlugin { return { id: "elevenlabs", @@ -256,15 +266,10 @@ export function buildElevenLabsRealtimeTranscriptionProvider(): RealtimeTranscri autoSelectOrder: 40, resolveConfig: ({ rawConfig }) => normalizeProviderConfig(rawConfig), isConfigured: ({ providerConfig }) => - Boolean( - normalizeProviderConfig(providerConfig).apiKey || - resolveElevenLabsApiKeyWithProfileFallback() || - process.env.XI_API_KEY, - ), + Boolean(resolveElevenLabsRealtimeApiKey(normalizeProviderConfig(providerConfig))), createSession: (req) => { const config = normalizeProviderConfig(req.providerConfig); - const apiKey = - config.apiKey || resolveElevenLabsApiKeyWithProfileFallback() || process.env.XI_API_KEY; + const apiKey = resolveElevenLabsRealtimeApiKey(config); if (!apiKey) { throw new Error("ElevenLabs API key missing"); }