openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
226 lines (225 loc) • 8.66 kB
JavaScript
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { t as getActiveRuntimePluginRegistry } from "./active-runtime-registry-C5SJbS7x.js";
import { t as buildCapabilityProviderIndex } from "./provider-registry-shared-DVSyKRD5.js";
import { t as extractTtsDirectiveFacts } from "./directive-facts-D2qVKMk7.js";
import { i as resolvePluginCapabilityProviders, r as resolvePluginCapabilityProvider } from "./capability-provider-runtime-DOgjx571.js";
import { n as createSpeechProviderRegistry, t as compareSpeechProviderOrder } from "./provider-registry-core-XM5mq12w.js";
//#region src/tts/provider-registry.ts
/** Resolve speech providers from configured plugin capabilities. */
function resolveSpeechProviderPluginEntries(cfg) {
return resolvePluginCapabilityProviders({
key: "speechProviders",
cfg
});
}
/** Config-aware registry used by setup/status/runtime paths before plugins are loaded. */
const defaultSpeechProviderRegistry = createSpeechProviderRegistry({
getProvider: (providerId, cfg) => resolvePluginCapabilityProvider({
key: "speechProviders",
providerId,
cfg
}),
listProviders: resolveSpeechProviderPluginEntries
});
/** List configured speech providers using manifest/capability discovery. */
const listSpeechProviders = defaultSpeechProviderRegistry.listSpeechProviders;
/** List currently loaded speech providers from the active runtime registry. */
function listLoadedSpeechProviders(_cfg) {
const providers = (getActiveRuntimePluginRegistry()?.speechProviders ?? []).map((entry) => entry.provider);
return [...buildCapabilityProviderIndex(providers, "canonical").values()];
}
/** Resolve a configured speech provider by canonical ID or alias. */
const getSpeechProvider = defaultSpeechProviderRegistry.getSpeechProvider;
/** Resolve an input provider ID or alias to the provider's canonical ID. */
const canonicalizeSpeechProviderId = defaultSpeechProviderRegistry.canonicalizeSpeechProviderId;
//#endregion
//#region src/tts/directives.ts
function resolveDirectiveProviders(options) {
return (options?.providers ?? listSpeechProviders(options?.cfg)).toSorted(compareSpeechProviderOrder);
}
function resolveDirectiveProviderConfig(provider, options) {
return options?.providerConfigs?.[provider.id];
}
function prioritizeProvider(providers, providerId) {
if (!providerId) return [...providers];
const preferredProvider = resolveDirectiveProvider(providers, providerId);
if (!preferredProvider) return [...providers];
return [preferredProvider, ...providers.filter((provider) => provider.id !== preferredProvider.id)];
}
function resolveDirectiveProvider(providers, providerId) {
const normalized = normalizeLowercaseStringOrEmpty(providerId);
if (!normalized) return;
return providers.find((provider) => provider.id === normalized || provider.aliases?.some((alias) => normalizeLowercaseStringOrEmpty(alias) === normalized));
}
function parseGenericSpeakerDirective(params) {
if (!params.policy.allowVoice) return;
switch (params.key) {
case "speakervoice":
case "speaker_voice": return {
...params.currentOverrides,
speakerVoice: params.value,
voice: params.value,
voiceName: params.value
};
case "speakervoiceid":
case "speaker_voice_id": return {
...params.currentOverrides,
speakerVoiceId: params.value,
voiceId: params.value
};
default: return;
}
}
function normalizeTtsTagBody(body) {
return body.trim().replace(/\s+/g, "").toLowerCase();
}
function classifyTtsTag(body) {
const normalized = normalizeTtsTagBody(body);
if (normalized === "tts:text") return "hidden-open";
if (normalized === "/tts:text") return "hidden-close";
if (normalized === "tts" || normalized.startsWith("tts:") || normalized === "/tts" || normalized.startsWith("/tts:")) return "tts";
return "other";
}
/** Create an incremental cleaner for hiding [[tts:*]] directive text while streaming. */
function createTtsDirectiveTextStreamCleaner() {
let pending = "";
let insideHiddenTextBlock = false;
return {
push(text) {
const input = pending + text;
pending = "";
let output = "";
let index = 0;
while (index < input.length) {
const tagStart = input.indexOf("[[", index);
if (tagStart === -1) {
if (!insideHiddenTextBlock) output += input.slice(index);
break;
}
if (!insideHiddenTextBlock) output += input.slice(index, tagStart);
const tagEnd = input.indexOf("]]", tagStart + 2);
if (tagEnd === -1) {
pending = input.slice(tagStart);
break;
}
const rawTag = input.slice(tagStart, tagEnd + 2);
const tag = classifyTtsTag(input.slice(tagStart + 2, tagEnd));
if (tag === "hidden-open") insideHiddenTextBlock = true;
else if (tag === "hidden-close") insideHiddenTextBlock = false;
else if (tag === "other" && !insideHiddenTextBlock) output += rawTag;
index = tagEnd + 2;
}
return output;
},
flush() {
const tail = pending;
pending = "";
return insideHiddenTextBlock ? "" : tail;
},
hasBufferedDirectiveText() {
return pending.length > 0 || insideHiddenTextBlock;
}
};
}
/** Resolve persisted TTS facts against the active model/provider policy. */
function resolveTtsDirectiveFacts(facts, policy, options) {
if (!facts || !policy.enabled) return {
overrides: {},
warnings: [],
hasDirective: false
};
let providers;
const getProviders = () => {
providers ??= resolveDirectiveProviders(options);
return providers;
};
const overrides = {};
const warnings = [];
if (policy.allowText && facts.text != null) overrides.ttsText = facts.text;
for (const directive of facts.directives ?? []) {
const declaredProviderId = policy.allowProvider ? directive.provider : void 0;
if (declaredProviderId) overrides.provider = declaredProviderId;
let directiveProviders;
const getDirectiveProviders = () => {
if (directiveProviders) return directiveProviders;
if (declaredProviderId) {
const declaredProvider = resolveDirectiveProvider(getProviders(), declaredProviderId);
if (!declaredProvider) {
warnings.push(`unknown provider "${declaredProviderId}"`);
directiveProviders = [];
return directiveProviders;
}
directiveProviders = [declaredProvider];
return directiveProviders;
}
directiveProviders = prioritizeProvider(getProviders(), normalizeLowercaseStringOrEmpty(options?.preferredProviderId));
return directiveProviders;
};
for (const [key, value] of Object.entries(directive.values)) {
let handled = false;
const directiveProvidersLocal = getDirectiveProviders();
for (const provider of directiveProvidersLocal) {
const genericSpeakerOverrides = parseGenericSpeakerDirective({
key,
value,
policy,
currentOverrides: overrides.providerOverrides?.[provider.id]
});
if (genericSpeakerOverrides) {
overrides.providerOverrides = {
...overrides.providerOverrides,
[provider.id]: {
...overrides.providerOverrides?.[provider.id],
...genericSpeakerOverrides
}
};
handled = true;
break;
}
const parsed = provider.parseDirectiveToken?.({
key,
value,
policy,
selectedProvider: declaredProviderId ? provider.id : void 0,
providerConfig: resolveDirectiveProviderConfig(provider, options),
currentOverrides: overrides.providerOverrides?.[provider.id]
});
if (!parsed?.handled) continue;
if (parsed.overrides) overrides.providerOverrides = {
...overrides.providerOverrides,
[provider.id]: {
...overrides.providerOverrides?.[provider.id],
...parsed.overrides
}
};
if (parsed.warnings?.length) warnings.push(...parsed.warnings);
handled = true;
break;
}
if (!handled && declaredProviderId && directiveProvidersLocal.length > 0) warnings.push(`unsupported ${declaredProviderId} directive key "${key}"`);
}
}
return {
ttsText: overrides.ttsText,
hasDirective: true,
overrides,
warnings
};
}
/** Parse TTS directives from final message text, leaving markdown code spans unchanged. */
function parseTtsDirectives(text, policy, options) {
if (!policy.enabled) return {
cleanedText: text,
overrides: {},
warnings: [],
hasDirective: false
};
const extracted = extractTtsDirectiveFacts(text);
const resolved = resolveTtsDirectiveFacts(extracted.facts, policy, options);
return {
cleanedText: extracted.cleanedText,
...resolved
};
}
//#endregion
export { getSpeechProvider as a, canonicalizeSpeechProviderId as i, parseTtsDirectives as n, listLoadedSpeechProviders as o, resolveTtsDirectiveFacts as r, listSpeechProviders as s, createTtsDirectiveTextStreamCleaner as t };