diff --git a/packages/server/src/server/agent/stt-manager.ts b/packages/server/src/server/agent/stt-manager.ts index 6fa69e715..b0c1eaf49 100644 --- a/packages/server/src/server/agent/stt-manager.ts +++ b/packages/server/src/server/agent/stt-manager.ts @@ -1,5 +1,6 @@ import type pino from "pino"; import type { SpeechToTextProvider, TranscriptionResult } from "../speech/speech-provider.js"; +import { toResolver, type Resolvable } from "../speech/provider-resolver.js"; import { maybePersistDebugAudio } from "./stt-debug.js"; import { parsePcm16MonoWav, parsePcmRateFromFormat } from "../speech/audio.js"; import { Pcm16MonoResampler } from "./pcm16-resampler.js"; @@ -44,11 +45,11 @@ export class STTManager { constructor( sessionId: string, logger: pino.Logger, - stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null) + stt: Resolvable ) { this.sessionId = sessionId; this.logger = logger.child({ module: "agent", component: "stt-manager", sessionId }); - this.resolveStt = typeof stt === "function" ? stt : () => stt; + this.resolveStt = toResolver(stt); } /** diff --git a/packages/server/src/server/agent/tts-manager.ts b/packages/server/src/server/agent/tts-manager.ts index a03a6fd87..d07da628c 100644 --- a/packages/server/src/server/agent/tts-manager.ts +++ b/packages/server/src/server/agent/tts-manager.ts @@ -1,6 +1,7 @@ import type pino from "pino"; import { v4 as uuidv4 } from "uuid"; import type { TextToSpeechProvider } from "../speech/speech-provider.js"; +import { toResolver, type Resolvable } from "../speech/provider-resolver.js"; import type { SessionOutboundMessage } from "../messages.js"; interface PendingPlayback { @@ -100,10 +101,10 @@ export class TTSManager { constructor( sessionId: string, logger: pino.Logger, - tts: TextToSpeechProvider | null | (() => TextToSpeechProvider | null) + tts: Resolvable ) { this.logger = logger.child({ module: "agent", component: "tts-manager", sessionId }); - this.resolveTts = typeof tts === "function" ? tts : () => tts; + this.resolveTts = toResolver(tts); } /** diff --git a/packages/server/src/server/dictation/dictation-stream-manager.ts b/packages/server/src/server/dictation/dictation-stream-manager.ts index 32732c34d..a9b0bd1c4 100644 --- a/packages/server/src/server/dictation/dictation-stream-manager.ts +++ b/packages/server/src/server/dictation/dictation-stream-manager.ts @@ -11,6 +11,7 @@ import type { SpeechToTextProvider, StreamingTranscriptionSession, } from "../speech/speech-provider.js"; +import { toResolver, type Resolvable } from "../speech/provider-resolver.js"; import { parsePcmRateFromFormat, pcm16lePeakAbs } from "../speech/audio.js"; const PCM_CHANNELS = 1; @@ -126,19 +127,14 @@ export class DictationStreamManager { logger: pino.Logger; emit: (msg: DictationStreamOutboundMessage) => void; sessionId: string; - stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); + stt: Resolvable; finalTimeoutMs?: number; autoCommitSeconds?: number; }) { this.logger = params.logger.child({ component: "dictation-stream-manager" }); this.emit = params.emit; this.sessionId = params.sessionId; - if (typeof params.stt === "function") { - this.resolveStt = params.stt; - } else { - const sttProvider = params.stt; - this.resolveStt = () => sttProvider; - } + this.resolveStt = toResolver(params.stt); this.finalTimeoutMs = params.finalTimeoutMs ?? DEFAULT_DICTATION_FINAL_TIMEOUT_MS; this.autoCommitSeconds = params.autoCommitSeconds ?? diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index db4d16a7b..ff08eee68 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -130,6 +130,7 @@ import { listLocalSpeechModels, type LocalSpeechModelId, } from "./speech/providers/local/models.js"; +import type { Resolvable } from "./speech/provider-resolver.js"; import type { SpeechReadinessSnapshot } from "./speech/speech-runtime.js"; import type pino from "pino"; @@ -324,8 +325,8 @@ export type SessionOptions = { agentManager: AgentManager; agentStorage: AgentStorage; createAgentMcpTransport: AgentMcpTransportFactory; - stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); - tts: TextToSpeechProvider | null | (() => TextToSpeechProvider | null); + stt: Resolvable; + tts: Resolvable; terminalManager: TerminalManager | null; voice?: { voiceAgentMcpStdio?: VoiceMcpStdioConfig | null; @@ -340,7 +341,7 @@ export type SessionOptions = { }; dictation?: { finalTimeoutMs?: number; - stt?: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); + stt?: Resolvable; localModels?: { modelsDir: string; defaultModelIds: LocalSpeechModelId[]; diff --git a/packages/server/src/server/speech/provider-resolver.ts b/packages/server/src/server/speech/provider-resolver.ts new file mode 100644 index 000000000..8ab3702e3 --- /dev/null +++ b/packages/server/src/server/speech/provider-resolver.ts @@ -0,0 +1,8 @@ +export type Resolvable = T | (() => T); + +export function toResolver(value: Resolvable): () => T { + if (typeof value === "function") { + return value as () => T; + } + return () => value; +} diff --git a/packages/server/src/server/websocket-server.ts b/packages/server/src/server/websocket-server.ts index 7bd527b86..df165f530 100644 --- a/packages/server/src/server/websocket-server.ts +++ b/packages/server/src/server/websocket-server.ts @@ -26,6 +26,7 @@ import type { AgentProviderRuntimeSettingsMap } from "./agent/provider-launch-co import { PushTokenStore } from "./push/token-store.js"; import { PushService } from "./push/push-service.js"; import type { SpeechToTextProvider, TextToSpeechProvider } from "./speech/speech-provider.js"; +import type { Resolvable } from "./speech/provider-resolver.js"; import type { SpeechReadinessSnapshot } from "./speech/speech-runtime.js"; import type { LocalSpeechModelId } from "./speech/providers/local/models.js"; import type { @@ -94,18 +95,12 @@ export class VoiceAssistantWebSocketServer { private readonly pushTokenStore: PushTokenStore; private readonly pushService: PushService; private readonly createAgentMcpTransport: AgentMcpTransportFactory; - private readonly stt: - | SpeechToTextProvider - | null - | (() => SpeechToTextProvider | null); - private readonly tts: - | TextToSpeechProvider - | null - | (() => TextToSpeechProvider | null); + private readonly stt: Resolvable; + private readonly tts: Resolvable; private readonly terminalManager: TerminalManager | null; private readonly dictation: { finalTimeoutMs?: number; - stt?: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); + stt?: Resolvable; localModels?: { modelsDir: string; defaultModelIds: LocalSpeechModelId[]; @@ -135,8 +130,8 @@ export class VoiceAssistantWebSocketServer { createAgentMcpTransport: AgentMcpTransportFactory, wsConfig: WebSocketServerConfig, speech?: { - stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); - tts: TextToSpeechProvider | null | (() => TextToSpeechProvider | null); + stt: Resolvable; + tts: Resolvable; }, terminalManager?: TerminalManager | null, voice?: { @@ -146,7 +141,7 @@ export class VoiceAssistantWebSocketServer { }, dictation?: { finalTimeoutMs?: number; - stt?: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); + stt?: Resolvable; localModels?: { modelsDir: string; defaultModelIds: LocalSpeechModelId[];