Files
openclaw/src/plugin-sdk/speech.ts
2026-04-05 09:07:33 +01:00

99 lines
2.9 KiB
TypeScript

import { rmSync } from "node:fs";
// Public speech helpers for bundled or third-party plugins.
//
// Keep this surface provider-facing: types, validation, directive parsing, and
// registry helpers. Runtime synthesis lives on `api.runtime.tts` or narrower
// core/runtime seams, not here.
export type { SpeechProviderPlugin } from "../plugins/types.js";
export type {
SpeechDirectiveTokenParseContext,
SpeechDirectiveTokenParseResult,
SpeechListVoicesRequest,
SpeechModelOverridePolicy,
SpeechProviderConfig,
SpeechProviderConfiguredContext,
SpeechProviderResolveConfigContext,
SpeechProviderResolveTalkConfigContext,
SpeechProviderResolveTalkOverridesContext,
SpeechProviderOverrides,
SpeechSynthesisRequest,
SpeechTelephonySynthesisRequest,
SpeechVoiceOption,
TtsDirectiveOverrides,
TtsDirectiveParseResult,
} from "../tts/provider-types.js";
export { parseTtsDirectives } from "../tts/directives.js";
export {
canonicalizeSpeechProviderId,
getSpeechProvider,
listSpeechProviders,
normalizeSpeechProviderId,
} from "../tts/provider-registry.js";
export { normalizeTtsAutoMode, TTS_AUTO_MODES } from "../tts/tts-auto-mode.js";
export {
asObject,
readResponseTextLimited,
trimToUndefined,
truncateErrorDetail,
} from "../tts/provider-error-utils.js";
const TEMP_FILE_CLEANUP_DELAY_MS = 5 * 60 * 1000; // 5 minutes
export function requireInRange(value: number, min: number, max: number, label: string): void {
if (!Number.isFinite(value) || value < min || value > max) {
throw new Error(`${label} must be between ${min} and ${max}`);
}
}
export function normalizeLanguageCode(code?: string): string | undefined {
const trimmed = code?.trim();
if (!trimmed) {
return undefined;
}
const normalized = trimmed.toLowerCase();
if (!/^[a-z]{2}$/.test(normalized)) {
throw new Error("languageCode must be a 2-letter ISO 639-1 code (e.g. en, de, fr)");
}
return normalized;
}
export function normalizeApplyTextNormalization(mode?: string): "auto" | "on" | "off" | undefined {
const trimmed = mode?.trim();
if (!trimmed) {
return undefined;
}
const normalized = trimmed.toLowerCase();
if (normalized === "auto" || normalized === "on" || normalized === "off") {
return normalized;
}
throw new Error("applyTextNormalization must be one of: auto, on, off");
}
export function normalizeSeed(seed?: number): number | undefined {
if (seed == null) {
return undefined;
}
const next = Math.floor(seed);
if (!Number.isFinite(next) || next < 0 || next > 4_294_967_295) {
throw new Error("seed must be between 0 and 4294967295");
}
return next;
}
export function scheduleCleanup(
tempDir: string,
delayMs: number = TEMP_FILE_CLEANUP_DELAY_MS,
): void {
const timer = setTimeout(() => {
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
// ignore cleanup errors
}
}, delayMs);
timer.unref();
}