@mevdragon/vidfarm-devcli 0.19.1 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.agents/skills/music/SKILL.md +416 -0
  2. package/.agents/skills/music/references/api_reference.md +519 -0
  3. package/.agents/skills/music/references/installation.md +65 -0
  4. package/.agents/skills/text-to-speech/SKILL.md +226 -0
  5. package/.agents/skills/text-to-speech/references/installation.md +90 -0
  6. package/.agents/skills/text-to-speech/references/streaming.md +307 -0
  7. package/.agents/skills/text-to-speech/references/voice-settings.md +115 -0
  8. package/.agents/skills/vidfarm-media/SKILL.md +25 -11
  9. package/.agents/skills/vidfarm-media/references/tts.md +41 -3
  10. package/SKILL.director.md +31 -13
  11. package/SKILL.platform.md +2 -2
  12. package/demo/dist/app.css +1 -1
  13. package/demo/dist/app.js +75 -75
  14. package/dist/src/app.js +129 -41
  15. package/dist/src/cli.js +162 -5
  16. package/dist/src/config.js +12 -0
  17. package/dist/src/devcli/clips.js +7 -2
  18. package/dist/src/domain.js +3 -0
  19. package/dist/src/editor-chat.js +5 -1
  20. package/dist/src/frontend/file-directory.js +41 -18
  21. package/dist/src/primitive-context.js +14 -0
  22. package/dist/src/primitive-registry.js +140 -18
  23. package/dist/src/reskin/chat-page.js +1 -1
  24. package/dist/src/reskin/inpaint-clipper-page.js +446 -205
  25. package/dist/src/reskin/library-page.js +7 -1
  26. package/dist/src/reskin/portfolio-page.js +9 -9
  27. package/dist/src/reskin/settings-page.js +4 -4
  28. package/dist/src/reskin/theme.js +8 -4
  29. package/dist/src/services/billing.js +5 -0
  30. package/dist/src/services/clip-curation/ffmpeg.js +48 -0
  31. package/dist/src/services/clip-curation/hunt.js +2 -0
  32. package/dist/src/services/clip-curation/index.js +1 -1
  33. package/dist/src/services/clip-curation/scan.js +29 -16
  34. package/dist/src/services/elevenlabs.js +222 -0
  35. package/dist/src/services/providers.js +216 -2
  36. package/dist/src/services/swipe-customize.js +5 -2
  37. package/package.json +1 -1
  38. package/public/assets/file-directory-app.js +3 -2
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: text-to-speech
3
+ description: Convert text to speech using ElevenLabs voice AI. Use when generating audio from text, creating voiceovers, building voice apps, or synthesizing speech in 70+ languages.
4
+ license: MIT
5
+ compatibility: Requires internet access and an ElevenLabs API key (ELEVENLABS_API_KEY).
6
+ metadata: {"openclaw": {"requires": {"env": ["ELEVENLABS_API_KEY"]}, "primaryEnv": "ELEVENLABS_API_KEY"}}
7
+ ---
8
+
9
+ # ElevenLabs Text-to-Speech
10
+
11
+ Generate natural speech from text - supports 70+ languages, multiple models for quality vs latency tradeoffs.
12
+
13
+ > **Setup:** See [Installation Guide](references/installation.md). For JavaScript, use `@elevenlabs/*` packages only.
14
+
15
+ ## Quick Start
16
+
17
+ ### Python
18
+
19
+ ```python
20
+ from elevenlabs import ElevenLabs
21
+
22
+ client = ElevenLabs()
23
+
24
+ audio = client.text_to_speech.convert(
25
+ text="Hello, welcome to ElevenLabs!",
26
+ voice_id="JBFqnCBsd6RMkjVDRZzb", # George
27
+ model_id="eleven_multilingual_v2"
28
+ )
29
+
30
+ with open("output.mp3", "wb") as f:
31
+ for chunk in audio:
32
+ f.write(chunk)
33
+ ```
34
+
35
+ ### JavaScript
36
+
37
+ ```javascript
38
+ import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
39
+ import { createWriteStream } from "fs";
40
+
41
+ const client = new ElevenLabsClient();
42
+ const audio = await client.textToSpeech.convert("JBFqnCBsd6RMkjVDRZzb", {
43
+ text: "Hello, welcome to ElevenLabs!",
44
+ modelId: "eleven_multilingual_v2",
45
+ });
46
+ audio.pipe(createWriteStream("output.mp3"));
47
+ ```
48
+
49
+ ### cURL
50
+
51
+ ```bash
52
+ curl -X POST "https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb" \
53
+ -H "xi-api-key: $ELEVENLABS_API_KEY" -H "Content-Type: application/json" \
54
+ -d '{"text": "Hello!", "model_id": "eleven_multilingual_v2"}' --output output.mp3
55
+ ```
56
+
57
+ ## Models
58
+
59
+ | Model ID | Languages | Latency | Best For |
60
+ |----------|-----------|---------|----------|
61
+ | `eleven_v3` | 70+ | Standard | Highest quality, emotional range |
62
+ | `eleven_multilingual_v2` | 29 | Standard | High quality, long-form content |
63
+ | `eleven_flash_v2_5` | 32 | ~75ms | Ultra-low latency, real-time |
64
+ | `eleven_flash_v2` | English | ~75ms | English-only, fastest |
65
+ | `eleven_turbo_v2_5` | 32 | ~250-300ms | Balanced quality/speed |
66
+ | `eleven_turbo_v2` | English | ~250-300ms | English-only, balanced |
67
+
68
+ ## Voice IDs
69
+
70
+ Use pre-made voices or create custom voices in the dashboard.
71
+
72
+ **Popular voices:**
73
+ - `JBFqnCBsd6RMkjVDRZzb` - George (male, narrative)
74
+ - `EXAVITQu4vr4xnSDxMaL` - Sarah (female, soft)
75
+ - `onwK4e9ZLuTAKqWW03F9` - Daniel (male, authoritative)
76
+ - `XB0fDUnXU5powFXDhCwa` - Charlotte (female, conversational)
77
+
78
+ ```python
79
+ voices = client.voices.get_all()
80
+ for voice in voices.voices:
81
+ print(f"{voice.voice_id}: {voice.name}")
82
+ ```
83
+
84
+ ## Voice Settings
85
+
86
+ Fine-tune how the voice sounds:
87
+
88
+ - **Stability**: How consistent the voice stays. Lower values = more emotional range and variation, but can sound unstable. Higher = steady, predictable delivery.
89
+ - **Similarity boost**: How closely to match the original voice sample. Higher values sound more like the original but may amplify audio artifacts.
90
+ - **Style**: Exaggerates the voice's unique style characteristics (only works with v2+ models).
91
+ - **Speaker boost**: Post-processing that enhances clarity and voice similarity.
92
+
93
+ ```python
94
+ from elevenlabs import VoiceSettings
95
+
96
+ audio = client.text_to_speech.convert(
97
+ text="Customize my voice settings.",
98
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
99
+ voice_settings=VoiceSettings(
100
+ stability=0.5,
101
+ similarity_boost=0.75,
102
+ style=0.5,
103
+ speed=1.0, # 0.25 to 4.0 (default 1.0)
104
+ use_speaker_boost=True
105
+ )
106
+ )
107
+ ```
108
+
109
+ ## Language Selection
110
+
111
+ Use `language_code` with models that support language enforcement to guide pronunciation and text normalization. Unsupported language codes are ignored, and `language_code` is not supported on `eleven_multilingual_v2`.
112
+
113
+ ```python
114
+ audio = client.text_to_speech.convert(
115
+ text="Bonjour, comment allez-vous?",
116
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
117
+ model_id="eleven_v3",
118
+ language_code="fr" # ISO 639-1 code
119
+ )
120
+ ```
121
+
122
+ ## Text Normalization
123
+
124
+ Controls how numbers, dates, and abbreviations are converted to spoken words. For example, "01/15/2026" becomes "January fifteenth, twenty twenty-six":
125
+
126
+ - `"auto"` (default): Model decides based on context
127
+ - `"on"`: Always normalize (use when you want natural speech)
128
+ - `"off"`: Speak literally (use when you want "zero one slash one five...")
129
+
130
+ ```python
131
+ audio = client.text_to_speech.convert(
132
+ text="Call 1-800-555-0123 on 01/15/2026",
133
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
134
+ apply_text_normalization="on"
135
+ )
136
+ ```
137
+
138
+ ## Request Stitching
139
+
140
+ When generating long audio in multiple requests, the audio can have pops, unnatural pauses, or tone shifts at the boundaries. Request stitching solves this by letting each request know what comes before/after it:
141
+
142
+ ```python
143
+ # First request
144
+ audio1 = client.text_to_speech.convert(
145
+ text="This is the first part.",
146
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
147
+ next_text="And this continues the story."
148
+ )
149
+
150
+ # Second request using previous context
151
+ audio2 = client.text_to_speech.convert(
152
+ text="And this continues the story.",
153
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
154
+ previous_text="This is the first part."
155
+ )
156
+ ```
157
+
158
+ ## Output Formats
159
+
160
+ | Format | Description |
161
+ |--------|-------------|
162
+ | `mp3_44100_128` | MP3 44.1kHz 128kbps (default) - compressed, good for web/apps |
163
+ | `mp3_44100_192` | MP3 44.1kHz 192kbps (Creator+) - higher quality compressed |
164
+ | `mp3_44100_64` | MP3 44.1kHz 64kbps - lower quality, smaller files |
165
+ | `mp3_22050_32` | MP3 22.05kHz 32kbps - smallest MP3 files |
166
+ | `pcm_16000` | Raw PCM 16kHz - use for real-time processing |
167
+ | `pcm_22050` | Raw PCM 22.05kHz |
168
+ | `pcm_24000` | Raw PCM 24kHz - good balance for streaming |
169
+ | `pcm_44100` | Raw PCM 44.1kHz (Pro+) - CD quality |
170
+ | `pcm_48000` | Raw PCM 48kHz (Pro+) - highest quality |
171
+ | `ulaw_8000` | μ-law 8kHz - standard for phone systems (Twilio, telephony) |
172
+ | `alaw_8000` | A-law 8kHz - telephony (alternative to μ-law) |
173
+ | `opus_48000_64` | Opus 48kHz 64kbps - efficient streaming codec |
174
+ | `wav_44100` | WAV 44.1kHz - uncompressed with headers |
175
+
176
+ ## Streaming
177
+
178
+ For real-time applications, use the `stream` method (returns audio chunks as they're generated):
179
+
180
+ ```python
181
+ audio_stream = client.text_to_speech.stream(
182
+ text="This text will be streamed as audio.",
183
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
184
+ model_id="eleven_flash_v2_5" # Ultra-low latency
185
+ )
186
+
187
+ for chunk in audio_stream:
188
+ play_audio(chunk)
189
+ ```
190
+
191
+ See [references/streaming.md](references/streaming.md) for WebSocket streaming.
192
+
193
+ ## Error Handling
194
+
195
+ ```python
196
+ try:
197
+ audio = client.text_to_speech.convert(
198
+ text="Generate speech",
199
+ voice_id="invalid-voice-id"
200
+ )
201
+ except Exception as e:
202
+ print(f"API error: {e}")
203
+ ```
204
+
205
+ Common errors:
206
+ - **401**: Invalid API key
207
+ - **422**: Invalid parameters (check voice_id, model_id)
208
+ - **429**: Rate limit exceeded
209
+
210
+ ## Tracking Costs
211
+
212
+ Monitor character usage via response headers (`x-character-count`, `request-id`):
213
+
214
+ ```python
215
+ response = client.text_to_speech.convert.with_raw_response(
216
+ text="Hello!", voice_id="JBFqnCBsd6RMkjVDRZzb", model_id="eleven_multilingual_v2"
217
+ )
218
+ audio = response.parse()
219
+ print(f"Characters used: {response.headers.get('x-character-count')}")
220
+ ```
221
+
222
+ ## References
223
+
224
+ - [Installation Guide](references/installation.md)
225
+ - [Streaming Audio](references/streaming.md)
226
+ - [Voice Settings](references/voice-settings.md)
@@ -0,0 +1,90 @@
1
+ # Installation
2
+
3
+ ## JavaScript / TypeScript
4
+
5
+ ```bash
6
+ npm install @elevenlabs/elevenlabs-js
7
+ ```
8
+
9
+ > **Important:** Always use `@elevenlabs/elevenlabs-js`. The old `elevenlabs` npm package (v1.x) is deprecated and should not be used.
10
+
11
+ ```javascript
12
+ import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
13
+
14
+ // Option 1: Environment variable (recommended)
15
+ // Set ELEVENLABS_API_KEY in your environment
16
+ const client = new ElevenLabsClient();
17
+
18
+ // Option 2: Pass directly
19
+ const client = new ElevenLabsClient({ apiKey: "your-api-key" });
20
+ ```
21
+
22
+ ### Migrating from deprecated packages
23
+
24
+ If you have old packages installed, remove them:
25
+
26
+ ```bash
27
+ # Remove deprecated packages
28
+ npm uninstall elevenlabs
29
+
30
+ # Install the current packages
31
+ npm install @elevenlabs/elevenlabs-js
32
+ ```
33
+
34
+ **Import changes:**
35
+ ```javascript
36
+ // OLD (deprecated)
37
+ import { ElevenLabsClient } from "elevenlabs";
38
+
39
+ // NEW (current)
40
+ import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
41
+ ```
42
+
43
+ ## Python
44
+
45
+ ```bash
46
+ pip install elevenlabs
47
+ ```
48
+
49
+ ```python
50
+ from elevenlabs import ElevenLabs
51
+
52
+ # Option 1: Environment variable (recommended)
53
+ # Set ELEVENLABS_API_KEY in your environment
54
+ client = ElevenLabs()
55
+
56
+ # Option 2: Pass directly
57
+ client = ElevenLabs(api_key="your-api-key")
58
+ ```
59
+
60
+ ## cURL / REST API
61
+
62
+ Set your API key as an environment variable:
63
+
64
+ ```bash
65
+ export ELEVENLABS_API_KEY="your-api-key"
66
+ ```
67
+
68
+ Include in requests via the `xi-api-key` header:
69
+
70
+ ```bash
71
+ curl -X POST "https://api.elevenlabs.io/v1/text-to-speech/{voice_id}" \
72
+ -H "xi-api-key: $ELEVENLABS_API_KEY" \
73
+ -H "Content-Type: application/json" \
74
+ -d '{"text": "Hello world", "model_id": "eleven_multilingual_v2"}'
75
+ ```
76
+
77
+ ## Getting an API Key
78
+
79
+ 1. Sign up at [elevenlabs.io](https://elevenlabs.io)
80
+ 2. Go to [API Keys](https://elevenlabs.io/app/settings/api-keys)
81
+ 3. Click **Create API Key**
82
+ 4. Copy and store securely
83
+
84
+ Or use the `setup-api-key` skill for guided setup.
85
+
86
+ ## Environment Variables
87
+
88
+ | Variable | Description |
89
+ |----------|-------------|
90
+ | `ELEVENLABS_API_KEY` | Your ElevenLabs API key (required) |
@@ -0,0 +1,307 @@
1
+ # Streaming Audio
2
+
3
+ Stream audio chunks as they're generated for lower latency.
4
+
5
+ ## Model Selection for Streaming
6
+
7
+ | Model | Latency | Use Case |
8
+ |-------|---------|----------|
9
+ | `eleven_flash_v2_5` | ~75ms | Lowest latency, 32 languages |
10
+ | `eleven_flash_v2` | ~75ms | Lowest latency, English only |
11
+ | `eleven_turbo_v2_5` | Low | Balanced quality/speed |
12
+
13
+ ## Python Streaming
14
+
15
+ ```python
16
+ from elevenlabs import ElevenLabs
17
+
18
+ client = ElevenLabs()
19
+
20
+ audio_stream = client.text_to_speech.stream(
21
+ text="This is a streaming example with ultra-low latency.",
22
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
23
+ model_id="eleven_flash_v2_5"
24
+ )
25
+
26
+ with open("output.mp3", "wb") as f:
27
+ for chunk in audio_stream:
28
+ f.write(chunk)
29
+ ```
30
+
31
+ ### Real-Time Playback
32
+
33
+ ```python
34
+ import subprocess
35
+
36
+ def play_stream(audio_stream):
37
+ process = subprocess.Popen(
38
+ ["ffplay", "-nodisp", "-autoexit", "-"],
39
+ stdin=subprocess.PIPE
40
+ )
41
+ for chunk in audio_stream:
42
+ process.stdin.write(chunk)
43
+ process.stdin.close()
44
+ process.wait()
45
+
46
+ audio_stream = client.text_to_speech.stream(
47
+ text="Playing this audio in real-time.",
48
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
49
+ model_id="eleven_flash_v2_5"
50
+ )
51
+ play_stream(audio_stream)
52
+ ```
53
+
54
+ ## JavaScript Streaming
55
+
56
+ ```javascript
57
+ import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
58
+ import { createWriteStream } from "fs";
59
+
60
+ const client = new ElevenLabsClient();
61
+
62
+ const audioStream = await client.textToSpeech.convert("JBFqnCBsd6RMkjVDRZzb", {
63
+ text: "Streaming audio in JavaScript.",
64
+ modelId: "eleven_flash_v2_5",
65
+ });
66
+
67
+ // Write to file
68
+ const writeStream = createWriteStream("output.mp3");
69
+ audioStream.pipe(writeStream);
70
+
71
+ // Or process chunks
72
+ for await (const chunk of audioStream) {
73
+ console.log(`Received ${chunk.length} bytes`);
74
+ }
75
+ ```
76
+
77
+ ## WebSocket Streaming
78
+
79
+ For text-streaming input where you send text chunks as they arrive (e.g., from an LLM).
80
+
81
+ ### Connection
82
+
83
+ ```
84
+ wss://api.elevenlabs.io/v1/text-to-speech/{voiceId}/stream-input?model_id={modelId}
85
+ ```
86
+
87
+ **Note:** WebSockets are unavailable for the `eleven_v3` model. Use `eleven_flash_v2_5` for lowest latency.
88
+
89
+ ### Message Flow
90
+
91
+ 1. **Initialize** - Send voice settings and configuration
92
+ 2. **Send text** - Stream text chunks as they arrive
93
+ 3. **Close** - Send empty string to signal completion
94
+ 4. **Receive** - Process audio chunks as they're generated
95
+
96
+ ### Python WebSocket
97
+
98
+ ```python
99
+ import asyncio
100
+ import json
101
+ import base64
102
+ import os
103
+ import websockets
104
+ from dotenv import load_dotenv
105
+
106
+ load_dotenv()
107
+
108
+ ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
109
+
110
+ async def text_to_speech_ws_streaming(voice_id: str, model_id: str):
111
+ uri = f"wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input?model_id={model_id}"
112
+
113
+ async with websockets.connect(uri) as websocket:
114
+ # Initialize connection
115
+ await websocket.send(json.dumps({
116
+ "text": " ",
117
+ "voice_settings": {
118
+ "stability": 0.5,
119
+ "similarity_boost": 0.8
120
+ },
121
+ "generation_config": {
122
+ "chunk_length_schedule": [120, 160, 250, 290]
123
+ },
124
+ "xi_api_key": ELEVENLABS_API_KEY
125
+ }))
126
+
127
+ # Send text chunks
128
+ await websocket.send(json.dumps({"text": "Hello, "}))
129
+ await websocket.send(json.dumps({"text": "this is streaming text "}))
130
+ await websocket.send(json.dumps({"text": "from a WebSocket connection."}))
131
+
132
+ # Close stream (empty text signals completion)
133
+ await websocket.send(json.dumps({"text": ""}))
134
+
135
+ # Receive and process audio chunks
136
+ audio_chunks = []
137
+ while True:
138
+ message = await websocket.recv()
139
+ data = json.loads(message)
140
+ if data.get("audio"):
141
+ audio_chunks.append(base64.b64decode(data["audio"]))
142
+ elif data.get("isFinal"):
143
+ break
144
+
145
+ return b"".join(audio_chunks)
146
+
147
+ async def main():
148
+ audio = await text_to_speech_ws_streaming(
149
+ voice_id="JBFqnCBsd6RMkjVDRZzb",
150
+ model_id="eleven_flash_v2_5"
151
+ )
152
+ with open("output.mp3", "wb") as f:
153
+ f.write(audio)
154
+
155
+ if __name__ == "__main__":
156
+ asyncio.run(main())
157
+ ```
158
+
159
+ ### JavaScript WebSocket
160
+
161
+ ```javascript
162
+ import "dotenv/config";
163
+ import WebSocket from "ws";
164
+ import * as fs from "node:fs";
165
+
166
+ const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY;
167
+
168
+ async function textToSpeechWsStreaming(voiceId, modelId) {
169
+ const uri = `wss://api.elevenlabs.io/v1/text-to-speech/${voiceId}/stream-input?model_id=${modelId}`;
170
+
171
+ return new Promise((resolve, reject) => {
172
+ const websocket = new WebSocket(uri, {
173
+ headers: { "xi-api-key": ELEVENLABS_API_KEY },
174
+ });
175
+
176
+ const audioChunks = [];
177
+
178
+ websocket.on("open", () => {
179
+ // Initialize connection
180
+ websocket.send(
181
+ JSON.stringify({
182
+ text: " ",
183
+ voice_settings: {
184
+ stability: 0.5,
185
+ similarity_boost: 0.8,
186
+ },
187
+ generation_config: {
188
+ chunk_length_schedule: [120, 160, 250, 290],
189
+ },
190
+ })
191
+ );
192
+
193
+ // Send text chunks
194
+ websocket.send(JSON.stringify({ text: "Hello, " }));
195
+ websocket.send(JSON.stringify({ text: "this is streaming text " }));
196
+ websocket.send(JSON.stringify({ text: "from a WebSocket connection." }));
197
+
198
+ // Close stream
199
+ websocket.send(JSON.stringify({ text: "" }));
200
+ });
201
+
202
+ websocket.on("message", (event) => {
203
+ const data = JSON.parse(event.toString());
204
+ if (data.audio) {
205
+ audioChunks.push(Buffer.from(data.audio, "base64"));
206
+ } else if (data.isFinal) {
207
+ websocket.close();
208
+ resolve(Buffer.concat(audioChunks));
209
+ }
210
+ });
211
+
212
+ websocket.on("error", reject);
213
+ });
214
+ }
215
+
216
+ const audio = await textToSpeechWsStreaming(
217
+ "JBFqnCBsd6RMkjVDRZzb",
218
+ "eleven_flash_v2_5"
219
+ );
220
+ fs.writeFileSync("output.mp3", audio);
221
+ ```
222
+
223
+ ### Input Messages
224
+
225
+ **Initialization (first message):**
226
+
227
+ ```json
228
+ {
229
+ "text": " ",
230
+ "voice_settings": {
231
+ "stability": 0.5,
232
+ "similarity_boost": 0.8,
233
+ "use_speaker_boost": false
234
+ },
235
+ "generation_config": {
236
+ "chunk_length_schedule": [120, 160, 250, 290]
237
+ },
238
+ "xi_api_key": "your_api_key"
239
+ }
240
+ ```
241
+
242
+ **Text chunks:**
243
+
244
+ ```json
245
+ { "text": "Your text content here" }
246
+ ```
247
+
248
+ **Force flush (generate audio immediately):**
249
+
250
+ ```json
251
+ { "text": "End of sentence.", "flush": true }
252
+ ```
253
+
254
+ **Close connection:**
255
+
256
+ ```json
257
+ { "text": "" }
258
+ ```
259
+
260
+ ### Output Messages
261
+
262
+ **Audio chunk:**
263
+
264
+ ```json
265
+ {
266
+ "audio": "base64_encoded_audio_data"
267
+ }
268
+ ```
269
+
270
+ **Stream complete:**
271
+
272
+ ```json
273
+ {
274
+ "isFinal": true
275
+ }
276
+ ```
277
+
278
+ ### Key Parameters
279
+
280
+ | Parameter | Description |
281
+ |-----------|-------------|
282
+ | `chunk_length_schedule` | Array of character counts that trigger audio generation. The model waits until it has this many characters before generating audio, which improves quality but adds latency. Lower values = faster response, higher values = better prosody. Example: `[120, 160, 250, 290]` means generate after 120 chars, then after 160 more, etc. |
283
+ | `flush` | Set `true` to force immediate audio generation without waiting for the character threshold. Use at the end of sentences or when you need audio NOW. |
284
+ | `voice_settings` | Adjustable per-message: `stability`, `similarity_boost`, `use_speaker_boost` |
285
+
286
+ ### Important Notes
287
+
288
+ - **Inactivity timeout**: Connection closes after 20 seconds without activity. Send a space `" "` to keep alive.
289
+ - **TTFB (Time to First Byte)**: How long until audio starts playing. Affected by `chunk_length_schedule` - the model waits for enough text before generating.
290
+ - **Model limitation**: WebSockets are unavailable for `eleven_v3`.
291
+ - **Best practice**: Use `flush: true` at conversation turn endings to ensure the buffered text gets spoken.
292
+ - **Alignment data**: Word-level timestamps available via `alignment` field for lip-sync or captions.
293
+
294
+ ## Best Practices
295
+
296
+ 1. **Use Flash models** for real-time:
297
+ - `eleven_flash_v2_5` for multilingual (~75ms)
298
+ - `eleven_flash_v2` for English-only (~75ms)
299
+
300
+ 2. **Buffer audio** before playback to prevent choppy output
301
+
302
+ 3. **Handle disconnections** gracefully in WebSocket streams
303
+
304
+ 4. **Choose output format based on use case**:
305
+ - `pcm_24000` - lowest latency processing
306
+ - `mp3_44100_128` - direct playback
307
+ - `ulaw_8000` - telephony/Twilio integration