From 09a41b2da49d582bd7ffd460f5fd66fb93ba423e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 11 Apr 2026 23:57:52 +0100 Subject: [PATCH] fix(plugin-sdk): untangle tts runtime facade types --- .../reply/reply-dispatcher.runtime-types.ts | 2 + src/plugin-sdk/tts-runtime.ts | 198 ++++++++++++++++-- 2 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 src/auto-reply/reply/reply-dispatcher.runtime-types.ts diff --git a/src/auto-reply/reply/reply-dispatcher.runtime-types.ts b/src/auto-reply/reply/reply-dispatcher.runtime-types.ts new file mode 100644 index 00000000000..f990ea44e92 --- /dev/null +++ b/src/auto-reply/reply/reply-dispatcher.runtime-types.ts @@ -0,0 +1,2 @@ +export type CreateReplyDispatcherWithTyping = + typeof import("./reply-dispatcher.js").createReplyDispatcherWithTyping; diff --git a/src/plugin-sdk/tts-runtime.ts b/src/plugin-sdk/tts-runtime.ts index e4e5fe03821..83986520c6a 100644 --- a/src/plugin-sdk/tts-runtime.ts +++ b/src/plugin-sdk/tts-runtime.ts @@ -1,10 +1,152 @@ -// Manual facade. Keep loader boundary explicit. -type FacadeModule = typeof import("@openclaw/speech-core/runtime-api.js"); +import type { ReplyPayload } from "../auto-reply/reply-payload.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { TtsAutoMode, TtsProvider } from "../config/types.tts.js"; +import type { + SpeechProviderConfig, + SpeechVoiceOption, + TtsDirectiveOverrides, + TtsDirectiveParseResult, +} from "../tts/provider-types.js"; +import type { ResolvedTtsConfig, ResolvedTtsModelOverrides } from "../tts/tts-types.js"; import { createLazyFacadeObjectValue, loadActivatedBundledPluginPublicSurfaceModuleSync, } from "./facade-runtime.js"; +// Manual facade. Keep loader boundary explicit and avoid typing this public SDK +// seam through the bundled speech-core runtime surface. +type TtsAttemptReasonCode = + | "success" + | "no_provider_registered" + | "not_configured" + | "unsupported_for_telephony" + | "timeout" + | "provider_error"; + +type TtsProviderAttempt = { + provider: string; + outcome: "success" | "skipped" | "failed"; + reasonCode: TtsAttemptReasonCode; + latencyMs?: number; + error?: string; +}; + +type TtsStatusEntry = { + timestamp: number; + success: boolean; + textLength: number; + summarized: boolean; + provider?: string; + fallbackFrom?: string; + attemptedProviders?: string[]; + attempts?: TtsProviderAttempt[]; + latencyMs?: number; + error?: string; +}; + +type SummarizeResult = { + summary: string; + latencyMs: number; + inputLength: number; + outputLength: number; +}; + +type ResolveTtsAutoModeParams = { + config: ResolvedTtsConfig; + prefsPath: string; + sessionAuto?: string; +}; + +type ResolveExplicitTtsOverridesParams = { + cfg: OpenClawConfig; + prefsPath?: string; + provider?: string; + modelId?: string; + voiceId?: string; +}; + +type TtsRequestParams = { + text: string; + cfg: OpenClawConfig; + prefsPath?: string; + channel?: string; + overrides?: TtsDirectiveOverrides; + disableFallback?: boolean; +}; + +type TtsTelephonyRequestParams = { + text: string; + cfg: OpenClawConfig; + prefsPath?: string; +}; + +type ListSpeechVoicesParams = { + provider: string; + cfg?: OpenClawConfig; + config?: ResolvedTtsConfig; + apiKey?: string; + baseUrl?: string; +}; + +type MaybeApplyTtsToPayloadParams = { + payload: ReplyPayload; + cfg: OpenClawConfig; + channel?: string; + kind?: "tool" | "block" | "final"; + inboundAudio?: boolean; + ttsAuto?: string; +}; + +type TtsTestFacade = { + parseTtsDirectives: (...args: unknown[]) => TtsDirectiveParseResult; + resolveModelOverridePolicy: (...args: unknown[]) => ResolvedTtsModelOverrides; + supportsNativeVoiceNoteTts: (channel: string | undefined) => boolean; + summarizeText: (...args: unknown[]) => Promise; + getResolvedSpeechProviderConfig: ( + config: ResolvedTtsConfig, + providerId: string, + cfg?: OpenClawConfig, + ) => SpeechProviderConfig; + formatTtsProviderError: (provider: TtsProvider, err: unknown) => string; + sanitizeTtsErrorForLog: (err: unknown) => string; +}; + +type FacadeModule = { + _test: TtsTestFacade; + buildTtsSystemPromptHint: (cfg: OpenClawConfig) => string | undefined; + getLastTtsAttempt: () => TtsStatusEntry | undefined; + getResolvedSpeechProviderConfig: ( + config: ResolvedTtsConfig, + providerId: string, + cfg?: OpenClawConfig, + ) => SpeechProviderConfig; + getTtsMaxLength: (prefsPath: string) => number; + getTtsProvider: (config: ResolvedTtsConfig, prefsPath: string) => TtsProvider; + isSummarizationEnabled: (prefsPath: string) => boolean; + isTtsEnabled: (config: ResolvedTtsConfig, prefsPath: string, sessionAuto?: string) => boolean; + isTtsProviderConfigured: ( + config: ResolvedTtsConfig, + provider: TtsProvider, + cfg?: OpenClawConfig, + ) => boolean; + listSpeechVoices: (params: ListSpeechVoicesParams) => Promise; + maybeApplyTtsToPayload: (params: MaybeApplyTtsToPayloadParams) => Promise; + resolveExplicitTtsOverrides: (params: ResolveExplicitTtsOverridesParams) => TtsDirectiveOverrides; + resolveTtsAutoMode: (params: ResolveTtsAutoModeParams) => TtsAutoMode; + resolveTtsConfig: (cfg: OpenClawConfig) => ResolvedTtsConfig; + resolveTtsPrefsPath: (config: ResolvedTtsConfig) => string; + resolveTtsProviderOrder: (primary: TtsProvider, cfg?: OpenClawConfig) => TtsProvider[]; + setLastTtsAttempt: (entry: TtsStatusEntry | undefined) => void; + setSummarizationEnabled: (prefsPath: string, enabled: boolean) => void; + setTtsAutoMode: (prefsPath: string, mode: TtsAutoMode) => void; + setTtsEnabled: (prefsPath: string, enabled: boolean) => void; + setTtsMaxLength: (prefsPath: string, maxLength: number) => void; + setTtsProvider: (prefsPath: string, provider: TtsProvider) => void; + synthesizeSpeech: (params: TtsRequestParams) => Promise; + textToSpeech: (params: TtsRequestParams) => Promise; + textToSpeechTelephony: (params: TtsTelephonyRequestParams) => Promise; +}; + function loadFacadeModule(): FacadeModule { return loadActivatedBundledPluginPublicSurfaceModuleSync({ dirName: "speech-core", @@ -61,16 +203,48 @@ export const textToSpeech: FacadeModule["textToSpeech"] = createLazyFacadeValue( export const textToSpeechTelephony: FacadeModule["textToSpeechTelephony"] = createLazyFacadeValue("textToSpeechTelephony"); -export type ResolvedTtsConfig = import("@openclaw/speech-core/runtime-api.js").ResolvedTtsConfig; -export type ResolvedTtsModelOverrides = - import("@openclaw/speech-core/runtime-api.js").ResolvedTtsModelOverrides; -export type TtsDirectiveOverrides = - import("@openclaw/speech-core/runtime-api.js").TtsDirectiveOverrides; -export type TtsDirectiveParseResult = - import("@openclaw/speech-core/runtime-api.js").TtsDirectiveParseResult; -export type TtsResult = import("@openclaw/speech-core/runtime-api.js").TtsResult; -export type TtsSynthesisResult = import("@openclaw/speech-core/runtime-api.js").TtsSynthesisResult; -export type TtsTelephonyResult = import("@openclaw/speech-core/runtime-api.js").TtsTelephonyResult; +export type { ResolvedTtsConfig, ResolvedTtsModelOverrides }; +export type { TtsDirectiveOverrides, TtsDirectiveParseResult }; + +export type TtsResult = { + success: boolean; + audioPath?: string; + error?: string; + latencyMs?: number; + provider?: string; + fallbackFrom?: string; + attemptedProviders?: string[]; + attempts?: TtsProviderAttempt[]; + outputFormat?: string; + voiceCompatible?: boolean; +}; + +export type TtsSynthesisResult = { + success: boolean; + audioBuffer?: Buffer; + error?: string; + latencyMs?: number; + provider?: string; + fallbackFrom?: string; + attemptedProviders?: string[]; + attempts?: TtsProviderAttempt[]; + outputFormat?: string; + voiceCompatible?: boolean; + fileExtension?: string; +}; + +export type TtsTelephonyResult = { + success: boolean; + audioBuffer?: Buffer; + error?: string; + latencyMs?: number; + provider?: string; + fallbackFrom?: string; + attemptedProviders?: string[]; + attempts?: TtsProviderAttempt[]; + outputFormat?: string; + sampleRate?: number; +}; function createLazyFacadeValue(key: K): FacadeModule[K] { return ((...args: unknown[]) => {