/** * Text → voice-note synthesis (OpenAI TTS). * * Channel-agnostic and framework-free: every dependency (client, enablement, * voice mapping, instructions) is injected by the host, so this module never * reads `process.env` and never imports a transport. * * Distinct from `src/core/voice.ts`, which maps languages to Twilio/Polly * voice identifiers for phone-call TwiML. This module produces Ogg/Opus audio * bytes for voice-note style delivery. * * TTS with `response_format: "opus"` returns mono Ogg/Opus and the duration * falls out of the container (see `ogg.ts`). The synthesizer validates both — a * non-mono or unparseable response is refused rather than delivered as a * broken bubble. */ import type OpenAI from "openai"; export interface VoiceNote { /** Mono Ogg/Opus bytes, sendable as-is with mimetype `audio/ogg; codecs=opus`. */ bytes: Uint8Array; /** Whole seconds for the voice-note bubble, parsed from the Ogg container. */ seconds: number; } export interface SynthesizeOptions { /** Persona id — the host's `voiceFor` decides what it means. */ personaId?: string | null; /** Extra delivery steering appended to the configured base instructions. */ instructions?: string; } export interface SynthesizerConfig { /** Lazy OpenAI client — injected so this module never reads env. */ client: () => OpenAI; /** Master gate; false makes the synthesizer a no-op null. */ enabled: () => boolean; /** TTS model. */ model?: string; /** Voice for a persona id (host-owned mapping). */ voiceFor?: (personaId: string | null | undefined) => string; /** Base delivery instructions prepended to every request. */ baseInstructions?: string; /** Input text clamp — long voice notes are a poor listening experience. */ maxChars?: number; /** Playback speed multiplier (OpenAI speech `speed`, 0.25–4). Default 1. */ speed?: number; } export declare const DEFAULT_MAX_VOICE_TEXT_CHARS = 550; /** Synthesize text into a voice note, or null when it cannot be delivered. */ export type Synthesizer = (text: string, options?: SynthesizeOptions) => Promise; /** * Build a synthesizer: text → VoiceNote | null. * * Returns null (never throws) when disabled, when the text is empty, or when * synthesis or container validation fails — callers always have a text * fallback available. */ export declare function createSynthesizer(config: SynthesizerConfig): Synthesizer;