openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
199 lines (198 loc) • 8.51 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { c as asFiniteNumberInRange } from "./number-coercion-CJQ8TR--.js";
import { u as normalizeResolvedSecretInputString } from "./types.secrets-_0JOMGE5.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import { i as isProviderAuthProfileConfigured } from "./provider-auth-DAOC_qI9.js";
import "./secret-input-DVCzFGxN.js";
import { a as resolveApiKeyForProvider } from "./provider-auth-runtime-D2xYnWtb.js";
import "./speech-DeKvI3F1.js";
import "./model-definitions-whru6Ebx.js";
import { a as xaiTTS, i as normalizeXaiTtsBaseUrl, n as isValidXaiTtsVoice, r as normalizeXaiLanguageCode, t as XAI_TTS_VOICES } from "./tts-DzHdlRc3.js";
//#region extensions/xai/speech-provider.ts
const XAI_SPEECH_RESPONSE_FORMATS = [
"mp3",
"wav",
"pcm",
"mulaw",
"alaw"
];
const DEFAULT_GENERATED_AUDIO_MAX_BYTES = 16 * 1024 * 1024;
function normalizeXaiSpeechSpeed(value) {
return asFiniteNumberInRange(value, {
min: .7,
max: 1.5
});
}
function normalizeXaiSpeechResponseFormat(value) {
const next = normalizeLowercaseStringOrEmpty(value);
if (!next) return;
if (XAI_SPEECH_RESPONSE_FORMATS.some((format) => format === next)) return next;
throw new Error(`Invalid xAI speech responseFormat: ${next}`);
}
function resolveSpeechResponseFormat(_target, configuredFormat) {
if (configuredFormat) return configuredFormat;
return "mp3";
}
function responseFormatToFileExtension(format) {
switch (format) {
case "wav": return ".wav";
case "pcm": return ".pcm";
case "mulaw": return ".mulaw";
case "alaw": return ".alaw";
default: return ".mp3";
}
}
function normalizeXaiProviderConfig(rawConfig) {
const xai = (rawConfig?.providers)?.xai ?? rawConfig?.xai ?? rawConfig;
return {
apiKey: normalizeResolvedSecretInputString({
value: xai?.apiKey,
path: "messages.tts.providers.xai.apiKey"
}),
baseUrl: normalizeXaiTtsBaseUrl(normalizeOptionalString(xai?.baseUrl) ?? normalizeOptionalString(process.env.XAI_BASE_URL) ?? "https://api.x.ai/v1"),
voiceId: normalizeOptionalString(xai?.voiceId ?? xai?.voice) ?? "eve",
language: normalizeXaiLanguageCode(normalizeOptionalString(xai?.language ?? xai?.languageCode)),
speed: normalizeXaiSpeechSpeed(xai?.speed),
responseFormat: normalizeXaiSpeechResponseFormat(xai?.responseFormat)
};
}
function readXaiProviderConfig(config) {
const normalized = normalizeXaiProviderConfig({});
return {
apiKey: normalizeOptionalString(config.apiKey) ?? normalized.apiKey,
baseUrl: normalizeOptionalString(config.baseUrl) ?? normalized.baseUrl,
voiceId: normalizeOptionalString(config.voiceId ?? config.voice) ?? normalized.voiceId,
language: normalizeXaiLanguageCode(normalizeOptionalString(config.language ?? config.languageCode)) ?? normalized.language,
speed: normalizeXaiSpeechSpeed(config.speed) ?? normalized.speed,
responseFormat: normalizeXaiSpeechResponseFormat(config.responseFormat) ?? normalized.responseFormat
};
}
function readXaiOverrides(overrides) {
if (!overrides) return {};
return {
voiceId: normalizeOptionalString(overrides.voiceId ?? overrides.voice),
language: normalizeXaiLanguageCode(normalizeOptionalString(overrides.language)),
speed: normalizeXaiSpeechSpeed(overrides.speed)
};
}
function resolveGeneratedAudioMaxBytes(req) {
const configured = req.cfg.agents?.defaults?.mediaMaxMb;
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * 1024 * 1024);
return DEFAULT_GENERATED_AUDIO_MAX_BYTES;
}
function parseDirectiveToken(ctx) {
const providerConfig = ctx.providerConfig;
const baseUrl = normalizeOptionalString(providerConfig?.baseUrl);
switch (ctx.key) {
case "voice":
case "voice_id":
case "voiceid":
case "xai_voice":
case "xaivoice":
if (!ctx.policy.allowVoice) return { handled: true };
if (!isValidXaiTtsVoice(ctx.value, baseUrl)) return {
handled: true,
warnings: [`invalid xAI voice "${ctx.value}"`]
};
return {
handled: true,
overrides: { voiceId: ctx.value }
};
default: return { handled: false };
}
}
function buildXaiSpeechProvider() {
return {
id: "xai",
label: "xAI",
autoSelectOrder: 25,
models: [],
voices: XAI_TTS_VOICES,
resolveConfig: ({ rawConfig }) => normalizeXaiProviderConfig(rawConfig),
parseDirectiveToken,
resolveTalkConfig: ({ baseTtsConfig, talkProviderConfig }) => {
const base = normalizeXaiProviderConfig(baseTtsConfig);
const responseFormat = normalizeXaiSpeechResponseFormat(talkProviderConfig.responseFormat);
return {
...base,
...talkProviderConfig.apiKey === void 0 ? {} : { apiKey: normalizeResolvedSecretInputString({
value: talkProviderConfig.apiKey,
path: "talk.providers.xai.apiKey"
}) },
...normalizeOptionalString(talkProviderConfig.baseUrl) == null ? {} : { baseUrl: normalizeXaiTtsBaseUrl(normalizeOptionalString(talkProviderConfig.baseUrl)) },
...normalizeOptionalString(talkProviderConfig.voiceId) == null ? {} : { voiceId: normalizeOptionalString(talkProviderConfig.voiceId) },
...normalizeXaiLanguageCode(normalizeOptionalString(talkProviderConfig.language ?? talkProviderConfig.languageCode)) == null ? {} : { language: normalizeXaiLanguageCode(normalizeOptionalString(talkProviderConfig.language ?? talkProviderConfig.languageCode)) },
...normalizeXaiSpeechSpeed(talkProviderConfig.speed) == null ? {} : { speed: normalizeXaiSpeechSpeed(talkProviderConfig.speed) },
...responseFormat == null ? {} : { responseFormat }
};
},
resolveTalkOverrides: ({ params }) => ({
...normalizeOptionalString(params.voiceId ?? params.voice) == null ? {} : { voiceId: normalizeOptionalString(params.voiceId ?? params.voice) },
...normalizeXaiLanguageCode(normalizeOptionalString(params.language ?? params.languageCode)) == null ? {} : { language: normalizeXaiLanguageCode(normalizeOptionalString(params.language ?? params.languageCode)) },
...normalizeXaiSpeechSpeed(params.speed) == null ? {} : { speed: normalizeXaiSpeechSpeed(params.speed) }
}),
listVoices: async () => XAI_TTS_VOICES.map((voice) => ({
id: voice,
name: voice
})),
isConfigured: ({ providerConfig, cfg }) => Boolean(readXaiProviderConfig(providerConfig).apiKey || process.env.XAI_API_KEY) || isProviderAuthProfileConfigured({
provider: "xai",
cfg
}),
synthesize: async (req) => {
const config = readXaiProviderConfig(req.providerConfig);
const overrides = readXaiOverrides(req.providerOverrides);
const apiKey = await resolveXaiAudioApiKey(config.apiKey, req.cfg);
const responseFormat = resolveSpeechResponseFormat(req.target, config.responseFormat);
return {
audioBuffer: await xaiTTS({
text: req.text,
apiKey,
baseUrl: config.baseUrl,
voiceId: overrides.voiceId ?? config.voiceId,
language: overrides.language ?? config.language,
speed: overrides.speed ?? config.speed,
responseFormat,
timeoutMs: req.timeoutMs,
maxBytes: resolveGeneratedAudioMaxBytes(req)
}),
outputFormat: responseFormat,
fileExtension: responseFormatToFileExtension(responseFormat),
voiceCompatible: false
};
},
synthesizeTelephony: async (req) => {
const config = readXaiProviderConfig(req.providerConfig);
const overrides = readXaiOverrides(req.providerOverrides);
const apiKey = await resolveXaiAudioApiKey(config.apiKey, req.cfg);
const outputFormat = "pcm";
return {
audioBuffer: await xaiTTS({
text: req.text,
apiKey,
baseUrl: config.baseUrl,
voiceId: overrides.voiceId ?? config.voiceId,
language: overrides.language ?? config.language,
speed: overrides.speed ?? config.speed,
responseFormat: outputFormat,
timeoutMs: req.timeoutMs,
maxBytes: resolveGeneratedAudioMaxBytes(req)
}),
outputFormat,
sampleRate: 24e3
};
}
};
}
async function resolveXaiAudioApiKey(configApiKey, cfg) {
const direct = normalizeOptionalString(configApiKey) ?? normalizeOptionalString(process.env.XAI_API_KEY);
if (direct) return direct;
const oauthKey = normalizeOptionalString((await resolveApiKeyForProvider({
provider: "xai",
cfg
}))?.apiKey);
if (oauthKey) return oauthKey;
throw new Error("xAI credentials missing for TTS. Sign in with `openclaw onboard --auth-choice xai-oauth`, or run `openclaw onboard --auth-choice xai-api-key`, or set XAI_API_KEY.");
}
//#endregion
export { buildXaiSpeechProvider as t };