mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-30 10:41:20 +00:00
fix(gradium): treat blank env API key as unconfigured in synthesis paths (#108800)
* fix(gradium): treat blank env API key as unconfigured in synthesis paths The isGradiumProviderConfigured helper already uses trimToUndefined on GRADIUM_API_KEY, but synthesize and synthesizeTelephony still used the bare env var. This made whitespace-only credentials pass the missing-key guard and send unusable bearer tokens. - Wrap process.env.GRADIUM_API_KEY with trimToUndefined in synthesize - Wrap process.env.GRADIUM_API_KEY with trimToUndefined in synthesizeTelephony - Add test for whitespace-only env key rejection in synthesize This completes the blank-credential guard for the Gradium provider, matching the pattern from OpenAI (#108212), Xiaomi (#108558), Deepgram (#108565), and Inworld (#108783). Co-Authored-By: Claude <noreply@anthropic.com> * fix(gradium): centralize speech API key resolution --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -18,6 +18,7 @@ describe("gradium speech provider", () => {
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.unstubAllEnvs();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
@@ -213,4 +214,36 @@ describe("gradium speech provider", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a blank environment key before normal or telephony requests", async () => {
|
||||
vi.stubEnv("GRADIUM_API_KEY", " ");
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
expect(provider.isConfigured({ providerConfig: {}, timeoutMs: 5_000 })).toBe(false);
|
||||
await expect(
|
||||
provider.synthesize({
|
||||
text: "test",
|
||||
cfg: {} as never,
|
||||
providerConfig: {},
|
||||
target: "audio-file",
|
||||
timeoutMs: 5_000,
|
||||
}),
|
||||
).rejects.toThrow("Gradium API key missing");
|
||||
|
||||
const synthesizeTelephony = provider.synthesizeTelephony;
|
||||
if (!synthesizeTelephony) {
|
||||
throw new Error("Expected Gradium provider synthesizeTelephony");
|
||||
}
|
||||
await expect(
|
||||
synthesizeTelephony({
|
||||
text: "test",
|
||||
cfg: {} as never,
|
||||
providerConfig: {},
|
||||
timeoutMs: 5_000,
|
||||
}),
|
||||
).rejects.toThrow("Gradium API key missing");
|
||||
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
SpeechProviderPlugin,
|
||||
} from "openclaw/plugin-sdk/speech";
|
||||
import { asObject, trimToUndefined } from "openclaw/plugin-sdk/speech";
|
||||
import { resolveSpeechProviderApiKey } from "openclaw/plugin-sdk/speech-core";
|
||||
import { DEFAULT_GRADIUM_VOICE_ID, GRADIUM_VOICES, normalizeGradiumBaseUrl } from "./shared.js";
|
||||
import { gradiumTTS } from "./tts.js";
|
||||
|
||||
@@ -39,8 +40,12 @@ function readGradiumProviderConfig(config: SpeechProviderConfig): GradiumProvide
|
||||
};
|
||||
}
|
||||
|
||||
function resolveGradiumApiKey(configApiKey: unknown): string | undefined {
|
||||
return resolveSpeechProviderApiKey(trimToUndefined(configApiKey), process.env.GRADIUM_API_KEY);
|
||||
}
|
||||
|
||||
function isGradiumProviderConfigured(config: SpeechProviderConfig): boolean {
|
||||
const apiKey = trimToUndefined(config.apiKey) ?? trimToUndefined(process.env.GRADIUM_API_KEY);
|
||||
const apiKey = resolveGradiumApiKey(config.apiKey);
|
||||
if (!apiKey) {
|
||||
return false;
|
||||
}
|
||||
@@ -99,7 +104,7 @@ export function buildGradiumSpeechProvider(): SpeechProviderPlugin {
|
||||
synthesize: async (req) => {
|
||||
const config = readGradiumProviderConfig(req.providerConfig);
|
||||
const overrides = req.providerOverrides ?? {};
|
||||
const apiKey = config.apiKey || process.env.GRADIUM_API_KEY;
|
||||
const apiKey = resolveGradiumApiKey(config.apiKey);
|
||||
if (!apiKey) {
|
||||
throw new Error("Gradium API key missing");
|
||||
}
|
||||
@@ -124,7 +129,7 @@ export function buildGradiumSpeechProvider(): SpeechProviderPlugin {
|
||||
synthesizeTelephony: async (req) => {
|
||||
const config = readGradiumProviderConfig(req.providerConfig);
|
||||
const overrides = req.providerOverrides ?? {};
|
||||
const apiKey = config.apiKey || process.env.GRADIUM_API_KEY;
|
||||
const apiKey = resolveGradiumApiKey(config.apiKey);
|
||||
if (!apiKey) {
|
||||
throw new Error("Gradium API key missing");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user