
























@@ -105,24 +105,36 @@ function readExtraBody(value: unknown): Record<string, unknown> | undefined {
105105return body;
106106}
107107108+function normalizeOpenAISpeechSpeed(value: unknown, baseUrl?: string): number | undefined {
109+const speed = asFiniteNumber(value);
110+if (speed === undefined) {
111+return undefined;
112+}
113+if (baseUrl !== undefined && normalizeOpenAITtsBaseUrl(baseUrl) !== DEFAULT_OPENAI_BASE_URL) {
114+return speed;
115+}
116+return speed >= 0.25 && speed <= 4 ? speed : undefined;
117+}
118+108119function normalizeOpenAIProviderConfig(
109120rawConfig: Record<string, unknown>,
110121): OpenAITtsProviderConfig {
111122const raw = resolveOpenAIProviderConfigRecord(rawConfig);
112123const extraBody = readExtraBody(raw?.extraBody) ?? readExtraBody(raw?.extra_body);
124+const baseUrl = normalizeOpenAITtsBaseUrl(
125+trimToUndefined(raw?.baseUrl) ??
126+trimToUndefined(process.env.OPENAI_TTS_BASE_URL) ??
127+DEFAULT_OPENAI_BASE_URL,
128+);
113129return {
114130apiKey: normalizeResolvedSecretInputString({
115131value: raw?.apiKey,
116132path: "messages.tts.providers.openai.apiKey",
117133}),
118-baseUrl: normalizeOpenAITtsBaseUrl(
119-trimToUndefined(raw?.baseUrl) ??
120-trimToUndefined(process.env.OPENAI_TTS_BASE_URL) ??
121-DEFAULT_OPENAI_BASE_URL,
122-),
134+ baseUrl,
123135model: trimToUndefined(raw?.model) ?? "gpt-4o-mini-tts",
124136voice: trimToUndefined(raw?.voice) ?? "coral",
125-speed: asFiniteNumber(raw?.speed),
137+speed: normalizeOpenAISpeechSpeed(raw?.speed, baseUrl),
126138instructions: trimToUndefined(raw?.instructions),
127139responseFormat: normalizeOpenAISpeechResponseFormat(raw?.responseFormat),
128140 extraBody,
@@ -136,7 +148,11 @@ function readOpenAIProviderConfig(config: SpeechProviderConfig): OpenAITtsProvid
136148baseUrl: trimToUndefined(config.baseUrl) ?? normalized.baseUrl,
137149model: trimToUndefined(config.model) ?? normalized.model,
138150voice: trimToUndefined(config.voice) ?? normalized.voice,
139-speed: asFiniteNumber(config.speed) ?? normalized.speed,
151+speed:
152+normalizeOpenAISpeechSpeed(
153+config.speed,
154+trimToUndefined(config.baseUrl) ?? normalized.baseUrl,
155+) ?? normalized.speed,
140156instructions: trimToUndefined(config.instructions) ?? normalized.instructions,
141157responseFormat:
142158normalizeOpenAISpeechResponseFormat(config.responseFormat) ?? normalized.responseFormat,
@@ -146,14 +162,15 @@ function readOpenAIProviderConfig(config: SpeechProviderConfig): OpenAITtsProvid
146162147163function readOpenAIOverrides(
148164overrides: SpeechProviderOverrides | undefined,
165+baseUrl: string,
149166): OpenAITtsProviderOverrides {
150167if (!overrides) {
151168return {};
152169}
153170return {
154171model: trimToUndefined(overrides.model),
155172voice: trimToUndefined(overrides.voice),
156-speed: asFiniteNumber(overrides.speed),
173+speed: normalizeOpenAISpeechSpeed(overrides.speed, baseUrl),
157174};
158175}
159176@@ -232,6 +249,8 @@ export function buildOpenAISpeechProvider(): SpeechProviderPlugin {
232249resolveTalkConfig: ({ baseTtsConfig, talkProviderConfig }) => {
233250const base = normalizeOpenAIProviderConfig(baseTtsConfig);
234251const responseFormat = normalizeOpenAISpeechResponseFormat(talkProviderConfig.responseFormat);
252+const baseUrl = trimToUndefined(talkProviderConfig.baseUrl) ?? base.baseUrl;
253+const speed = normalizeOpenAISpeechSpeed(talkProviderConfig.speed, baseUrl);
235254return {
236255 ...base,
237256 ...(talkProviderConfig.apiKey === undefined
@@ -242,18 +261,14 @@ export function buildOpenAISpeechProvider(): SpeechProviderPlugin {
242261path: "talk.providers.openai.apiKey",
243262}),
244263}),
245- ...(trimToUndefined(talkProviderConfig.baseUrl) == null
246- ? {}
247- : { baseUrl: trimToUndefined(talkProviderConfig.baseUrl) }),
264+ ...(trimToUndefined(talkProviderConfig.baseUrl) == null ? {} : { baseUrl }),
248265 ...(trimToUndefined(talkProviderConfig.modelId) == null
249266 ? {}
250267 : { model: trimToUndefined(talkProviderConfig.modelId) }),
251268 ...(trimToUndefined(talkProviderConfig.voiceId) == null
252269 ? {}
253270 : { voice: trimToUndefined(talkProviderConfig.voiceId) }),
254- ...(asFiniteNumber(talkProviderConfig.speed) == null
255- ? {}
256- : { speed: asFiniteNumber(talkProviderConfig.speed) }),
271+ ...(speed == null ? {} : { speed }),
257272 ...(trimToUndefined(talkProviderConfig.instructions) == null
258273 ? {}
259274 : { instructions: trimToUndefined(talkProviderConfig.instructions) }),
@@ -291,7 +306,7 @@ export function buildOpenAISpeechProvider(): SpeechProviderPlugin {
291306},
292307synthesize: async (req) => {
293308const config = readOpenAIProviderConfig(req.providerConfig);
294-const overrides = readOpenAIOverrides(req.providerOverrides);
309+const overrides = readOpenAIOverrides(req.providerOverrides, config.baseUrl);
295310const apiKey = config.apiKey || process.env.OPENAI_API_KEY;
296311if (!apiKey) {
297312throw new Error("OpenAI API key missing");
@@ -322,7 +337,7 @@ export function buildOpenAISpeechProvider(): SpeechProviderPlugin {
322337},
323338synthesizeTelephony: async (req) => {
324339const config = readOpenAIProviderConfig(req.providerConfig);
325-const overrides = readOpenAIOverrides(req.providerOverrides);
340+const overrides = readOpenAIOverrides(req.providerOverrides, config.baseUrl);
326341const apiKey = config.apiKey || process.env.OPENAI_API_KEY;
327342if (!apiKey) {
328343throw new Error("OpenAI API key missing");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。