fix(ui): correct rounding boundary comment from 999,500 to 999,950

This commit is contained in:
Vincent Koc
2026-06-22 20:00:07 +08:00
committed by GitHub
parent d191da1a66
commit deb462f0d9
2 changed files with 74 additions and 81 deletions

View File

@@ -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<Parameters<typeof elevenLabsTTS>[0], "outputFormat" | "latencyTier">,
): Parameters<typeof elevenLabsTTS>[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 };
},
};

View File

@@ -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<st
return await listConfiguredMessageChannels(cfg);
}
function hasExplicitChannelConfigEntry(cfg: OpenClawConfig): boolean {
const channels = cfg.channels;
if (!channels || typeof channels !== "object" || Array.isArray(channels)) {
return false;
}
return Object.entries(channels).some(([channelId, entry]) => {
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}`);
}