
























@@ -0,0 +1,189 @@
1+import type { OpenClawConfig } from "../config/types.openclaw.js";
2+import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
3+4+const TTS_PROVIDER_CONFIG_RESERVED_KEYS = new Set([
5+"auto",
6+"enabled",
7+"maxTextLength",
8+"mode",
9+"modelOverrides",
10+"persona",
11+"personas",
12+"prefsPath",
13+"provider",
14+"providers",
15+"summaryModel",
16+"timeoutMs",
17+]);
18+19+function isRecord(value: unknown): value is Record<string, unknown> {
20+return Boolean(value && typeof value === "object" && !Array.isArray(value));
21+}
22+23+function isConfigActivationValueEnabled(value: unknown): boolean {
24+if (value === false) {
25+return false;
26+}
27+if (isRecord(value) && value.enabled === false) {
28+return false;
29+}
30+return true;
31+}
32+33+export function normalizeConfiguredSpeechProviderIdForStartup(value: unknown): string | undefined {
34+if (typeof value !== "string") {
35+return undefined;
36+}
37+const normalized = normalizeOptionalLowercaseString(value);
38+if (!normalized) {
39+return undefined;
40+}
41+return normalized === "edge" ? "microsoft" : normalized;
42+}
43+44+function resolveProviderConfigActivation(
45+ttsConfig: Record<string, unknown>,
46+providerId: string,
47+): boolean | undefined {
48+let fromProviders: boolean | undefined;
49+if (isRecord(ttsConfig.providers)) {
50+for (const [key, providerConfig] of Object.entries(ttsConfig.providers)) {
51+if (normalizeConfiguredSpeechProviderIdForStartup(key) === providerId) {
52+fromProviders = isConfigActivationValueEnabled(providerConfig);
53+}
54+}
55+}
56+if (fromProviders !== undefined) {
57+return fromProviders;
58+}
59+60+for (const [key, providerConfig] of Object.entries(ttsConfig)) {
61+if (TTS_PROVIDER_CONFIG_RESERVED_KEYS.has(key) || !isRecord(providerConfig)) {
62+continue;
63+}
64+if (normalizeConfiguredSpeechProviderIdForStartup(key) === providerId) {
65+return isConfigActivationValueEnabled(providerConfig);
66+}
67+}
68+return undefined;
69+}
70+71+function addProviderIfEnabled(
72+target: Set<string>,
73+ttsConfig: Record<string, unknown>,
74+providerId: unknown,
75+): void {
76+const normalized = normalizeConfiguredSpeechProviderIdForStartup(providerId);
77+if (!normalized) {
78+return;
79+}
80+if (resolveProviderConfigActivation(ttsConfig, normalized) !== false) {
81+target.add(normalized);
82+}
83+}
84+85+function findActivePersona(
86+ttsConfig: Record<string, unknown>,
87+): Record<string, unknown> | undefined {
88+const personaId = normalizeOptionalLowercaseString(
89+typeof ttsConfig.persona === "string" ? ttsConfig.persona : undefined,
90+);
91+if (!personaId || !isRecord(ttsConfig.personas)) {
92+return undefined;
93+}
94+for (const [id, persona] of Object.entries(ttsConfig.personas)) {
95+if (normalizeOptionalLowercaseString(id) === personaId && isRecord(persona)) {
96+return persona;
97+}
98+}
99+return undefined;
100+}
101+102+function addActivePersonaProvider(target: Set<string>, ttsConfig: Record<string, unknown>): void {
103+const persona = findActivePersona(ttsConfig);
104+if (!persona) {
105+return;
106+}
107+const provider = normalizeConfiguredSpeechProviderIdForStartup(persona.provider);
108+if (!provider) {
109+return;
110+}
111+const rootActivation = resolveProviderConfigActivation(ttsConfig, provider);
112+const personaActivation = resolveProviderConfigActivation(persona, provider);
113+if ((personaActivation ?? rootActivation) !== false) {
114+target.add(provider);
115+}
116+}
117+118+function addConfiguredTtsProviderIds(target: Set<string>, value: unknown): void {
119+if (!isRecord(value)) {
120+return;
121+}
122+addProviderIfEnabled(target, value, value.provider);
123+addActivePersonaProvider(target, value);
124+125+if (isRecord(value.providers)) {
126+for (const [providerId, providerConfig] of Object.entries(value.providers)) {
127+if (isConfigActivationValueEnabled(providerConfig)) {
128+addProviderIfEnabled(target, value, providerId);
129+}
130+}
131+}
132+for (const [key, providerConfig] of Object.entries(value)) {
133+if (TTS_PROVIDER_CONFIG_RESERVED_KEYS.has(key) || !isRecord(providerConfig)) {
134+continue;
135+}
136+if (isConfigActivationValueEnabled(providerConfig)) {
137+addProviderIfEnabled(target, value, key);
138+}
139+}
140+}
141+142+export function collectConfiguredSpeechProviderIds(config: OpenClawConfig): ReadonlySet<string> {
143+const configured = new Set<string>();
144+addConfiguredTtsProviderIds(configured, config.messages?.tts);
145+146+const agents = config.agents;
147+if (isRecord(agents) && Array.isArray(agents.list)) {
148+for (const agent of agents.list) {
149+if (isRecord(agent)) {
150+addConfiguredTtsProviderIds(configured, agent.tts);
151+}
152+}
153+}
154+155+const channels = config.channels;
156+if (isRecord(channels)) {
157+for (const channelConfig of Object.values(channels)) {
158+if (!isRecord(channelConfig)) {
159+continue;
160+}
161+addConfiguredTtsProviderIds(configured, channelConfig.tts);
162+if (isRecord(channelConfig.voice)) {
163+addConfiguredTtsProviderIds(configured, channelConfig.voice.tts);
164+}
165+if (isRecord(channelConfig.accounts)) {
166+for (const accountConfig of Object.values(channelConfig.accounts)) {
167+if (!isRecord(accountConfig)) {
168+continue;
169+}
170+addConfiguredTtsProviderIds(configured, accountConfig.tts);
171+if (isRecord(accountConfig.voice)) {
172+addConfiguredTtsProviderIds(configured, accountConfig.voice.tts);
173+}
174+}
175+}
176+}
177+}
178+179+const pluginEntries = config.plugins?.entries;
180+if (isRecord(pluginEntries)) {
181+for (const entry of Object.values(pluginEntries)) {
182+if (isRecord(entry) && isRecord(entry.config)) {
183+addConfiguredTtsProviderIds(configured, entry.config.tts);
184+}
185+}
186+}
187+188+return configured;
189+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。