refactor(server): centralize speech provider resolver typing

This commit is contained in:
Mohamed Boudra
2026-02-14 00:44:08 +07:00
parent cfd061a86d
commit c5d7fa52b4
6 changed files with 28 additions and 26 deletions
@@ -1,5 +1,6 @@
import type pino from "pino"; import type pino from "pino";
import type { SpeechToTextProvider, TranscriptionResult } from "../speech/speech-provider.js"; 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 { maybePersistDebugAudio } from "./stt-debug.js";
import { parsePcm16MonoWav, parsePcmRateFromFormat } from "../speech/audio.js"; import { parsePcm16MonoWav, parsePcmRateFromFormat } from "../speech/audio.js";
import { Pcm16MonoResampler } from "./pcm16-resampler.js"; import { Pcm16MonoResampler } from "./pcm16-resampler.js";
@@ -44,11 +45,11 @@ export class STTManager {
constructor( constructor(
sessionId: string, sessionId: string,
logger: pino.Logger, logger: pino.Logger,
stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null) stt: Resolvable<SpeechToTextProvider | null>
) { ) {
this.sessionId = sessionId; this.sessionId = sessionId;
this.logger = logger.child({ module: "agent", component: "stt-manager", sessionId }); this.logger = logger.child({ module: "agent", component: "stt-manager", sessionId });
this.resolveStt = typeof stt === "function" ? stt : () => stt; this.resolveStt = toResolver(stt);
} }
/** /**
@@ -1,6 +1,7 @@
import type pino from "pino"; import type pino from "pino";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import type { TextToSpeechProvider } from "../speech/speech-provider.js"; import type { TextToSpeechProvider } from "../speech/speech-provider.js";
import { toResolver, type Resolvable } from "../speech/provider-resolver.js";
import type { SessionOutboundMessage } from "../messages.js"; import type { SessionOutboundMessage } from "../messages.js";
interface PendingPlayback { interface PendingPlayback {
@@ -100,10 +101,10 @@ export class TTSManager {
constructor( constructor(
sessionId: string, sessionId: string,
logger: pino.Logger, logger: pino.Logger,
tts: TextToSpeechProvider | null | (() => TextToSpeechProvider | null) tts: Resolvable<TextToSpeechProvider | null>
) { ) {
this.logger = logger.child({ module: "agent", component: "tts-manager", sessionId }); this.logger = logger.child({ module: "agent", component: "tts-manager", sessionId });
this.resolveTts = typeof tts === "function" ? tts : () => tts; this.resolveTts = toResolver(tts);
} }
/** /**
@@ -11,6 +11,7 @@ import type {
SpeechToTextProvider, SpeechToTextProvider,
StreamingTranscriptionSession, StreamingTranscriptionSession,
} from "../speech/speech-provider.js"; } from "../speech/speech-provider.js";
import { toResolver, type Resolvable } from "../speech/provider-resolver.js";
import { parsePcmRateFromFormat, pcm16lePeakAbs } from "../speech/audio.js"; import { parsePcmRateFromFormat, pcm16lePeakAbs } from "../speech/audio.js";
const PCM_CHANNELS = 1; const PCM_CHANNELS = 1;
@@ -126,19 +127,14 @@ export class DictationStreamManager {
logger: pino.Logger; logger: pino.Logger;
emit: (msg: DictationStreamOutboundMessage) => void; emit: (msg: DictationStreamOutboundMessage) => void;
sessionId: string; sessionId: string;
stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); stt: Resolvable<SpeechToTextProvider | null>;
finalTimeoutMs?: number; finalTimeoutMs?: number;
autoCommitSeconds?: number; autoCommitSeconds?: number;
}) { }) {
this.logger = params.logger.child({ component: "dictation-stream-manager" }); this.logger = params.logger.child({ component: "dictation-stream-manager" });
this.emit = params.emit; this.emit = params.emit;
this.sessionId = params.sessionId; this.sessionId = params.sessionId;
if (typeof params.stt === "function") { this.resolveStt = toResolver(params.stt);
this.resolveStt = params.stt;
} else {
const sttProvider = params.stt;
this.resolveStt = () => sttProvider;
}
this.finalTimeoutMs = params.finalTimeoutMs ?? DEFAULT_DICTATION_FINAL_TIMEOUT_MS; this.finalTimeoutMs = params.finalTimeoutMs ?? DEFAULT_DICTATION_FINAL_TIMEOUT_MS;
this.autoCommitSeconds = this.autoCommitSeconds =
params.autoCommitSeconds ?? params.autoCommitSeconds ??
+4 -3
View File
@@ -130,6 +130,7 @@ import {
listLocalSpeechModels, listLocalSpeechModels,
type LocalSpeechModelId, type LocalSpeechModelId,
} from "./speech/providers/local/models.js"; } from "./speech/providers/local/models.js";
import type { Resolvable } from "./speech/provider-resolver.js";
import type { SpeechReadinessSnapshot } from "./speech/speech-runtime.js"; import type { SpeechReadinessSnapshot } from "./speech/speech-runtime.js";
import type pino from "pino"; import type pino from "pino";
@@ -324,8 +325,8 @@ export type SessionOptions = {
agentManager: AgentManager; agentManager: AgentManager;
agentStorage: AgentStorage; agentStorage: AgentStorage;
createAgentMcpTransport: AgentMcpTransportFactory; createAgentMcpTransport: AgentMcpTransportFactory;
stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); stt: Resolvable<SpeechToTextProvider | null>;
tts: TextToSpeechProvider | null | (() => TextToSpeechProvider | null); tts: Resolvable<TextToSpeechProvider | null>;
terminalManager: TerminalManager | null; terminalManager: TerminalManager | null;
voice?: { voice?: {
voiceAgentMcpStdio?: VoiceMcpStdioConfig | null; voiceAgentMcpStdio?: VoiceMcpStdioConfig | null;
@@ -340,7 +341,7 @@ export type SessionOptions = {
}; };
dictation?: { dictation?: {
finalTimeoutMs?: number; finalTimeoutMs?: number;
stt?: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); stt?: Resolvable<SpeechToTextProvider | null>;
localModels?: { localModels?: {
modelsDir: string; modelsDir: string;
defaultModelIds: LocalSpeechModelId[]; defaultModelIds: LocalSpeechModelId[];
@@ -0,0 +1,8 @@
export type Resolvable<T> = T | (() => T);
export function toResolver<T>(value: Resolvable<T>): () => T {
if (typeof value === "function") {
return value as () => T;
}
return () => value;
}
+7 -12
View File
@@ -26,6 +26,7 @@ import type { AgentProviderRuntimeSettingsMap } from "./agent/provider-launch-co
import { PushTokenStore } from "./push/token-store.js"; import { PushTokenStore } from "./push/token-store.js";
import { PushService } from "./push/push-service.js"; import { PushService } from "./push/push-service.js";
import type { SpeechToTextProvider, TextToSpeechProvider } from "./speech/speech-provider.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 { SpeechReadinessSnapshot } from "./speech/speech-runtime.js";
import type { LocalSpeechModelId } from "./speech/providers/local/models.js"; import type { LocalSpeechModelId } from "./speech/providers/local/models.js";
import type { import type {
@@ -94,18 +95,12 @@ export class VoiceAssistantWebSocketServer {
private readonly pushTokenStore: PushTokenStore; private readonly pushTokenStore: PushTokenStore;
private readonly pushService: PushService; private readonly pushService: PushService;
private readonly createAgentMcpTransport: AgentMcpTransportFactory; private readonly createAgentMcpTransport: AgentMcpTransportFactory;
private readonly stt: private readonly stt: Resolvable<SpeechToTextProvider | null>;
| SpeechToTextProvider private readonly tts: Resolvable<TextToSpeechProvider | null>;
| null
| (() => SpeechToTextProvider | null);
private readonly tts:
| TextToSpeechProvider
| null
| (() => TextToSpeechProvider | null);
private readonly terminalManager: TerminalManager | null; private readonly terminalManager: TerminalManager | null;
private readonly dictation: { private readonly dictation: {
finalTimeoutMs?: number; finalTimeoutMs?: number;
stt?: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); stt?: Resolvable<SpeechToTextProvider | null>;
localModels?: { localModels?: {
modelsDir: string; modelsDir: string;
defaultModelIds: LocalSpeechModelId[]; defaultModelIds: LocalSpeechModelId[];
@@ -135,8 +130,8 @@ export class VoiceAssistantWebSocketServer {
createAgentMcpTransport: AgentMcpTransportFactory, createAgentMcpTransport: AgentMcpTransportFactory,
wsConfig: WebSocketServerConfig, wsConfig: WebSocketServerConfig,
speech?: { speech?: {
stt: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); stt: Resolvable<SpeechToTextProvider | null>;
tts: TextToSpeechProvider | null | (() => TextToSpeechProvider | null); tts: Resolvable<TextToSpeechProvider | null>;
}, },
terminalManager?: TerminalManager | null, terminalManager?: TerminalManager | null,
voice?: { voice?: {
@@ -146,7 +141,7 @@ export class VoiceAssistantWebSocketServer {
}, },
dictation?: { dictation?: {
finalTimeoutMs?: number; finalTimeoutMs?: number;
stt?: SpeechToTextProvider | null | (() => SpeechToTextProvider | null); stt?: Resolvable<SpeechToTextProvider | null>;
localModels?: { localModels?: {
modelsDir: string; modelsDir: string;
defaultModelIds: LocalSpeechModelId[]; defaultModelIds: LocalSpeechModelId[];