/** * Factory: createOpenAiCompatibleSpeechProvider * * Most TTS providers (OpenAI, MiniMax, ElevenLabs, OpenRouter, ...) speak the * OpenAI `/audio/speech` POST shape. This factory eliminates the boilerplate so * a new compatible vendor is ~30 lines of declaration instead of a full provider * implementation. * * Ported from openclaw/src/tts/openai-compatible-speech-provider.ts (commit * baseline 2026-05-08), with the following INTENTIONAL OMISSIONS per * docs/voice-rearchitecture.md §15.3: * - resolveTalkConfig / resolveTalkOverrides (talk-mode is excluded) * - Persona resolution context (persona system is excluded) * - Custom dispatcherPolicy (xopc uses Node 22 native fetch — see ssrf-guard.ts) * * DECISION: We use `Record` for the extra-config slot rather * than openclaw's generic `>`. The * generic adds significant TypeScript complexity but no runtime safety; we * declare per-provider config types in each provider file's own normalizer. * * DECISION: `voiceCompatibleResponseFormats` defaults to `['opus']` if omitted, * because Telegram voice notes require opus/ogg. Providers that natively support * opus output (OpenAI, ElevenLabs) can rely on this; others (MiniMax mp3-only) * pass an empty array and downstream ffmpeg-compresses to opus. */ import type { SpeechProviderPlugin } from './speech-provider-types.js'; interface OpenAiCompatibleBaseConfig { apiKey?: string; baseUrl?: string; model: string; voice: string; speed?: number; responseFormat?: string; } export type OpenAiCompatibleSpeechProviderConfig = OpenAiCompatibleBaseConfig & Record; export interface OpenAiCompatibleSpeechProviderExtraJsonBodyField { /** Key to read from normalized config. */ configKey: string; /** Key to send in the HTTP body. Defaults to `configKey`. */ requestKey?: string; } export interface OpenAiCompatibleSpeechProviderOptions { /** Canonical provider id (e.g. "openai", "minimax"). */ id: string; /** Optional aliases for provider lookup. */ aliases?: readonly string[]; /** Human-readable label for error messages and logs (e.g. "OpenAI", "MiniMax"). */ label: string; /** Sort key for UI auto-fallback ordering. Lower = higher priority. */ autoSelectOrder: number; /** Allowed model ids. Used by listVoices and validation. */ models: readonly string[]; /** Allowed voice ids. */ voices: readonly string[]; /** Default model when config does not specify. */ defaultModel: string; /** Default voice. */ defaultVoice: string; /** Default base URL (e.g. "https://api.openai.com/v1"). */ defaultBaseUrl: string; /** Env var name for the api key (e.g. "OPENAI_API_KEY"). */ envKey: string; /** Allowed response formats. */ responseFormats: readonly string[]; /** Default response format (e.g. "opus" for OpenAI, "mp3" for MiniMax). */ defaultResponseFormat: string; /** Response formats that produce a voice-note-compatible buffer (no ffmpeg needed). */ voiceCompatibleResponseFormats?: readonly string[]; /** Extra static headers (e.g. `{ "OpenAI-Beta": "..." }`). Auth header is added automatically. */ extraHeaders?: Record; /** Read provider-specific extra config keys (e.g. MiniMax's `groupId`). */ readExtraConfig?: (raw: Record | undefined) => Record; /** Provider-specific extra body fields to copy from config into request JSON. */ extraJsonBodyFields?: readonly OpenAiCompatibleSpeechProviderExtraJsonBodyField[]; /** Override the API error label (defaults to `${label} TTS API error`). */ apiErrorLabel?: string; /** Override the missing-key error message. */ missingApiKeyError?: string; } export declare function createOpenAiCompatibleSpeechProvider(options: OpenAiCompatibleSpeechProviderOptions): SpeechProviderPlugin; export {};