/** * SpeechModule * * NestJS module for speech-to-text (STT) and text-to-speech (TTS) services. * Provides a provider abstraction layer with graceful fallback for TTS tiers. * * TTS providers are created dynamically based on configuration: * - default: Kokoro-FastAPI (CPU, always available) * - premium: Chatterbox (GPU, voice cloning) * - fallback: Piper via OpenedAI Speech (ultra-lightweight CPU) * * Imports: * - ConfigModule.forFeature(speechConfig) for speech configuration * - AuthModule for WebSocket authentication * - PrismaModule for workspace membership queries * * Providers: * - SpeechService: High-level speech operations with provider selection * - SpeechGateway: WebSocket gateway for streaming transcription (Issue #397) * - TTS_PROVIDERS: Map populated by factory based on config * * Exports: * - SpeechService for use by other modules (e.g., controllers, brain) * * Issue #389, #390, #391, #397 */ import { Module, type OnModuleInit, Logger } from "@nestjs/common"; import { ConfigModule, ConfigService } from "@nestjs/config"; import { speechConfig, validateSpeechConfig, isSttEnabled, type SpeechConfig, } from "./speech.config"; import { SpeechService } from "./speech.service"; import { SpeechController } from "./speech.controller"; import { SpeechGateway } from "./speech.gateway"; import { STT_PROVIDER, TTS_PROVIDERS } from "./speech.constants"; import { SpeachesSttProvider } from "./providers/speaches-stt.provider"; import { createTTSProviders } from "./providers/tts-provider.factory"; import { AuthModule } from "../auth/auth.module"; import { PrismaModule } from "../prisma/prisma.module"; @Module({ imports: [ConfigModule.forFeature(speechConfig), AuthModule, PrismaModule], controllers: [SpeechController], providers: [ SpeechService, SpeechGateway, // STT provider: conditionally register SpeachesSttProvider when STT is enabled ...(isSttEnabled() ? [ { provide: STT_PROVIDER, useClass: SpeachesSttProvider, }, ] : []), { provide: TTS_PROVIDERS, useFactory: (configService: ConfigService) => { const config = configService.get("speech"); if (!config) { return new Map(); } return createTTSProviders(config); }, inject: [ConfigService], }, ], exports: [SpeechService], }) export class SpeechModule implements OnModuleInit { private readonly logger = new Logger(SpeechModule.name); onModuleInit(): void { // Validate configuration at startup (fail fast) validateSpeechConfig(); this.logger.log("Speech module initialized"); } }