From deb462f0d9c87488c19d83bce57853a5cd2b1920 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 22 Jun 2026 20:00:07 +0800 Subject: [PATCH] fix(ui): correct rounding boundary comment from 999,500 to 999,950 --- extensions/elevenlabs/speech-provider.ts | 128 +++++++++-------------- src/gateway/server-methods/cron.ts | 27 ++++- 2 files changed, 74 insertions(+), 81 deletions(-) diff --git a/extensions/elevenlabs/speech-provider.ts b/extensions/elevenlabs/speech-provider.ts index d0fdad1c179..063ade2bb96 100644 --- a/extensions/elevenlabs/speech-provider.ts +++ b/extensions/elevenlabs/speech-provider.ts @@ -8,6 +8,7 @@ import type { SpeechProviderConfig, SpeechProviderOverrides, SpeechProviderPlugin, + SpeechSynthesisRequest, SpeechVoiceOption, } from "openclaw/plugin-sdk/speech"; import { @@ -389,6 +390,40 @@ async function listElevenLabsVoices(params: { } } +type ElevenLabsSynthesisRequest = Pick< + SpeechSynthesisRequest, + "providerConfig" | "providerOverrides" | "text" | "timeoutMs" +>; + +function resolveElevenLabsTtsRequest( + req: ElevenLabsSynthesisRequest, + options: Pick[0], "outputFormat" | "latencyTier">, +): Parameters[0] { + const config = readElevenLabsProviderConfig(req.providerConfig); + const overrides = req.providerOverrides ?? {}; + const apiKey = + config.apiKey || resolveElevenLabsApiKeyWithProfileFallback() || process.env.XI_API_KEY; + if (!apiKey) { + throw new Error("ElevenLabs API key missing"); + } + return { + text: req.text, + apiKey, + baseUrl: config.baseUrl, + voiceId: trimToUndefined(overrides.voiceId) ?? config.voiceId, + modelId: normalizeElevenLabsTtsModelId(trimToUndefined(overrides.modelId)) ?? config.modelId, + outputFormat: options.outputFormat, + seed: normalizeElevenLabsSeed(overrides.seed) ?? config.seed, + applyTextNormalization: + (trimToUndefined(overrides.applyTextNormalization) as "auto" | "on" | "off" | undefined) ?? + config.applyTextNormalization, + languageCode: trimToUndefined(overrides.languageCode) ?? config.languageCode, + latencyTier: options.latencyTier, + voiceSettings: resolveVoiceSettingsOverride(config.voiceSettings, overrides.voiceSettings), + timeoutMs: req.timeoutMs, + }; +} + export function buildElevenLabsSpeechProvider(): SpeechProviderPlugin { return { id: "elevenlabs", @@ -509,37 +544,16 @@ export function buildElevenLabsSpeechProvider(): SpeechProviderPlugin { process.env.XI_API_KEY, ), synthesize: async (req) => { - const config = readElevenLabsProviderConfig(req.providerConfig); const overrides = req.providerOverrides ?? {}; - const apiKey = - config.apiKey || resolveElevenLabsApiKeyWithProfileFallback() || process.env.XI_API_KEY; - if (!apiKey) { - throw new Error("ElevenLabs API key missing"); - } const outputFormat = trimToUndefined(overrides.outputFormat) ?? (req.target === "voice-note" ? "opus_48000_64" : "mp3_44100_128"); - const latencyTier = normalizeElevenLabsLatencyTier(overrides.latencyTier); - const audioBuffer = await elevenLabsTTS({ - text: req.text, - apiKey, - baseUrl: config.baseUrl, - voiceId: trimToUndefined(overrides.voiceId) ?? config.voiceId, - modelId: - normalizeElevenLabsTtsModelId(trimToUndefined(overrides.modelId)) ?? config.modelId, - outputFormat, - seed: normalizeElevenLabsSeed(overrides.seed) ?? config.seed, - applyTextNormalization: - (trimToUndefined(overrides.applyTextNormalization) as - | "auto" - | "on" - | "off" - | undefined) ?? config.applyTextNormalization, - languageCode: trimToUndefined(overrides.languageCode) ?? config.languageCode, - latencyTier, - voiceSettings: resolveVoiceSettingsOverride(config.voiceSettings, overrides.voiceSettings), - timeoutMs: req.timeoutMs, - }); + const audioBuffer = await elevenLabsTTS( + resolveElevenLabsTtsRequest(req, { + outputFormat, + latencyTier: normalizeElevenLabsLatencyTier(overrides.latencyTier), + }), + ); return { audioBuffer, outputFormat, @@ -548,37 +562,16 @@ export function buildElevenLabsSpeechProvider(): SpeechProviderPlugin { }; }, streamSynthesize: async (req) => { - const config = readElevenLabsProviderConfig(req.providerConfig); const overrides = req.providerOverrides ?? {}; - const apiKey = - config.apiKey || resolveElevenLabsApiKeyWithProfileFallback() || process.env.XI_API_KEY; - if (!apiKey) { - throw new Error("ElevenLabs API key missing"); - } const outputFormat = trimToUndefined(overrides.outputFormat) ?? (req.target === "voice-note" ? "opus_48000_64" : "mp3_44100_128"); - const latencyTier = normalizeElevenLabsLatencyTier(overrides.latencyTier); - const stream = await elevenLabsTTSStream({ - text: req.text, - apiKey, - baseUrl: config.baseUrl, - voiceId: trimToUndefined(overrides.voiceId) ?? config.voiceId, - modelId: - normalizeElevenLabsTtsModelId(trimToUndefined(overrides.modelId)) ?? config.modelId, - outputFormat, - seed: normalizeElevenLabsSeed(overrides.seed) ?? config.seed, - applyTextNormalization: - (trimToUndefined(overrides.applyTextNormalization) as - | "auto" - | "on" - | "off" - | undefined) ?? config.applyTextNormalization, - languageCode: trimToUndefined(overrides.languageCode) ?? config.languageCode, - latencyTier, - voiceSettings: resolveVoiceSettingsOverride(config.voiceSettings, overrides.voiceSettings), - timeoutMs: req.timeoutMs, - }); + const stream = await elevenLabsTTSStream( + resolveElevenLabsTtsRequest(req, { + outputFormat, + latencyTier: normalizeElevenLabsLatencyTier(overrides.latencyTier), + }), + ); return { audioStream: stream.audioStream, outputFormat, @@ -588,34 +581,9 @@ export function buildElevenLabsSpeechProvider(): SpeechProviderPlugin { }; }, synthesizeTelephony: async (req) => { - const config = readElevenLabsProviderConfig(req.providerConfig); - const overrides = req.providerOverrides ?? {}; - const apiKey = - config.apiKey || resolveElevenLabsApiKeyWithProfileFallback() || process.env.XI_API_KEY; - if (!apiKey) { - throw new Error("ElevenLabs API key missing"); - } const outputFormat = "pcm_22050"; const sampleRate = 22_050; - const audioBuffer = await elevenLabsTTS({ - text: req.text, - apiKey, - baseUrl: config.baseUrl, - voiceId: trimToUndefined(overrides.voiceId) ?? config.voiceId, - modelId: - normalizeElevenLabsTtsModelId(trimToUndefined(overrides.modelId)) ?? config.modelId, - outputFormat, - seed: normalizeElevenLabsSeed(overrides.seed) ?? config.seed, - applyTextNormalization: - (trimToUndefined(overrides.applyTextNormalization) as - | "auto" - | "on" - | "off" - | undefined) ?? config.applyTextNormalization, - languageCode: trimToUndefined(overrides.languageCode) ?? config.languageCode, - voiceSettings: resolveVoiceSettingsOverride(config.voiceSettings, overrides.voiceSettings), - timeoutMs: req.timeoutMs, - }); + const audioBuffer = await elevenLabsTTS(resolveElevenLabsTtsRequest(req, { outputFormat })); return { audioBuffer, outputFormat, sampleRate }; }, }; diff --git a/src/gateway/server-methods/cron.ts b/src/gateway/server-methods/cron.ts index a574e4aa096..26a9e7a0197 100644 --- a/src/gateway/server-methods/cron.ts +++ b/src/gateway/server-methods/cron.ts @@ -34,7 +34,10 @@ import { } from "../../infra/outbound/channel-target-prefix.js"; import { isSubagentSessionKey } from "../../routing/session-key.js"; import { parseAgentSessionKey } from "../../sessions/session-key-utils.js"; -import { normalizeMessageChannel } from "../../utils/message-channel.js"; +import { + isDeliverableMessageChannel, + normalizeMessageChannel, +} from "../../utils/message-channel.js"; import type { GatewayRequestHandlers, RespondFn } from "./types.js"; type CronJobIdParams = { id?: string; jobId?: string }; @@ -67,6 +70,22 @@ async function listConfiguredAnnounceChannelIds(cfg: OpenClawConfig): Promise { + if (channelId === "defaults" || channelId === "modelByChannel") { + return false; + } + if (!entry || typeof entry !== "object" || Array.isArray(entry)) { + return false; + } + return Object.keys(entry).length > 0; + }); +} + async function assertConfiguredAnnounceChannel(params: { cfg: OpenClawConfig; channel?: string; @@ -90,6 +109,12 @@ async function assertConfiguredAnnounceChannel(params: { } if (configuredChannels.length === 0) { + if (!hasExplicitChannelConfigEntry(params.cfg)) { + if (!isDeliverableMessageChannel(normalizedChannel)) { + throw new Error(`${params.field} is not a known channel: ${normalizedChannel}`); + } + return; + } throw new Error(`${params.field} is not configured: ${normalizedChannel}`); }