@molecule/api-ai-speech-openai
Provider bond · ai-speech · API (Node) · v1.0.3 · Apache-2.0
OpenAI speech provider for molecule.dev — Whisper STT + TTS
npm install @molecule/api-ai-speech-openainpm · Source on GitHub · Implements @molecule/api-ai-speech
How it works
@molecule/api-ai-speech-openai is a provider bond on the API (Node) side: it implements the ai-speech core interface (@molecule/api-ai-speech) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider, requireProvider } from '@molecule/api-ai-speech'
import { provider } from '@molecule/api-ai-speech-openai'
setProvider(provider) // at startup — lazy; reads OPENAI_API_KEY on first use
const speech = requireProvider()
const { audio, contentType } = await speech.synthesize({ input: 'Hello!', voice: 'alloy' })
const { text } = await speech.transcribe!({ audio: buf, model: 'whisper-1' })Works with: @molecule/api-ai-speech, @molecule/api-secrets
Secrets: OPENAI_API_KEY
Live demos
Running apps built with Molecule that install this package — open the app or study the template.
Reference
OpenAI ai-speech provider for molecule.dev.
Text-to-speech (tts-1 family) plus Whisper speech-to-text: transcription in the source language and direct speech-to-English translation.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider, requireProvider } from '@molecule/api-ai-speech'
import { provider } from '@molecule/api-ai-speech-openai'
setProvider(provider) // at startup — lazy; reads OPENAI_API_KEY on first use
const speech = requireProvider()
const { audio, contentType } = await speech.synthesize({ input: 'Hello!', voice: 'alloy' })
const { text } = await speech.transcribe!({ audio: buf, model: 'whisper-1' })
Type
provider
Installation
npm install @molecule/api-ai-speech-openai @molecule/api-ai-speech @molecule/api-secrets
API
Interfaces
OpenaiSpeechConfig
Configuration for the OpenAI speech provider.
interface OpenaiSpeechConfig {
/** OpenAI API key. Defaults to OPENAI_API_KEY env var. */
apiKey?: string
/** Base URL for the OpenAI API. Defaults to 'https://api.openai.com'. */
baseUrl?: string
/** Default TTS model. Defaults to 'tts-1'. */
defaultTTSModel?: string
/** Default STT model. Defaults to 'whisper-1'. */
defaultSTTModel?: string
/** Default voice for TTS. Defaults to 'alloy'. */
defaultVoice?: string
/** Default audio output format for TTS. Defaults to 'mp3'. */
defaultResponseFormat?: 'mp3' | 'opus' | 'aac' | 'flac' | 'wav' | 'pcm'
/** Default speech speed for TTS (0.25–4.0). Defaults to 1.0. */
defaultSpeed?: number
}
Functions
createProvider(config)
Creates an OpenAI speech provider instance.
function createProvider(config?: OpenaiSpeechConfig): AISpeechProvider
config— OpenAI-specific configuration (API key, models, voice, base URL).
Returns: An AISpeechProvider backed by OpenAI TTS and Whisper APIs.
Constants
provider
The provider implementation.
const provider: AISpeechProvider
aiSpeechOpenaiSecretDefinitions
Secret definitions required by the OpenAI speech bond.
const aiSpeechOpenaiSecretDefinitions: SecretDefinition[]
Core Interface
Implements @molecule/api-ai-speech interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-ai-speech'
import { provider } from '@molecule/api-ai-speech-openai'
export function setupAiSpeechOpenai(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-ai-speech>=1.0.1@molecule/api-secrets^1.0.1
Environment Variables
OPENAI_API_KEY(required) — OpenAI API key- Setup: Create a secret key on the OpenAI platform (API keys page).
- Get it here: https://platform.openai.com/api-keys
- Example:
sk-proj-...
Runtime Dependencies
-
@molecule/api-ai-speech -
@molecule/api-secrets -
Wiring: bond the lazy
providerexport once —setProvider(provider)— orsetProvider(createProvider(config?))to pass explicit config. The core'ssetProviderregisters the'ai-speech'bond; a second provider can run beside it as a NAMED bond (bond('ai-speech', 'name', p)/get('ai-speech', 'name')). -
Subset: implements
synthesize,transcribe, andtranslate. It does NOT implementsynthesizeSpeech/synthesizeStream/listVoices— feature-detect per the core; pick@molecule/api-ai-speech-elevenlabsfor streaming TTS/voice lists. -
Config:
OPENAI_API_KEY(required, SERVER-side only);OPENAI_BASE_URL(optional) overrides the API origin, defaulthttps://api.openai.com.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. The wired provider implements a subset (TTS, STT, or both) — feature-detect and run only the direction(s) it actually exposes:
- TTS: each read-aloud / narration / voice-note flow the app defines
returns real PLAYABLE audio — the response carries an audio Content-Type
(e.g.
audio/mpeg) and non-trivial bytes (not a 0-byte file, not a JSON-encoded blob), and the UI's<audio>/player actually plays it. The spoken audio reflects the input text you sent. - TTS: synthesizing two different strings yields two audibly different clips (distinct byte payloads) — proves the text drives synthesis and you are not hearing a cached/placeholder file.
- TTS: any exposed voice/format/speed option takes effect — switching
voice (
voice/voiceId) or output format (responseFormat/outputFormat) changes the returned audio (audible voice, Content-Type, or file extension); an unknown voice/format surfaces a visible error rather than silently falling back. - STT: transcribing a KNOWN spoken clip returns text that MATCHES the words spoken (not empty, not garbage) and renders in the UI; translating a non-English clip returns English text. Any timestamps the UI shows line up with the audio.
- STT: a silent / empty / too-short clip is handled gracefully — an empty or "no speech detected" result shown in the UI, never a crash or a spinner that hangs forever.
- The audio artifact is served from the app's OWN origin (a stored upload or a route the app serves), NOT a raw expiring provider URL — copy the audio src, reload it later, and confirm it still plays.
- A provider error (bad key, quota, unsupported format/voice, or a capability the wired provider lacks) surfaces as a readable message in the UI, not an unhandled crash or a blank/broken player.
- Synthesis/transcription runs SERVER-SIDE and authorized: the provider key never reaches the browser (check the network tab / client bundle), and no unauthenticated or unbounded endpoint lets a caller fire arbitrary synthesize/transcribe calls — each is billed per character / per minute of audio, so an open endpoint is a cost-DoS vector.