@elizaos/plugin-tts 0.1.9 → 1.0.0
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.
- package/dist/cjs/index.cjs +560 -0
- package/dist/cjs/index.cjs.map +13 -0
- package/dist/index.js +509 -454
- package/dist/index.js.map +13 -1
- package/package.json +93 -33
- package/LICENSE +0 -21
- package/README.md +0 -173
- package/dist/index.d.ts +0 -5
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
6
|
+
var __toCommonJS = (from) => {
|
|
7
|
+
var entry = __moduleCache.get(from), desc;
|
|
8
|
+
if (entry)
|
|
9
|
+
return entry;
|
|
10
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
13
|
+
get: () => from[key],
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
}));
|
|
16
|
+
__moduleCache.set(from, entry);
|
|
17
|
+
return entry;
|
|
18
|
+
};
|
|
19
|
+
var __export = (target, all) => {
|
|
20
|
+
for (var name in all)
|
|
21
|
+
__defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var exports_src = {};
|
|
31
|
+
__export(exports_src, {
|
|
32
|
+
ttsPlugin: () => ttsPlugin,
|
|
33
|
+
ttsConfigProvider: () => ttsConfigProvider,
|
|
34
|
+
truncateText: () => truncateText,
|
|
35
|
+
synthesize: () => synthesize,
|
|
36
|
+
summarizeForTts: () => summarizeForTts,
|
|
37
|
+
stripTtsDirectives: () => stripTtsDirectives,
|
|
38
|
+
shouldApplyTts: () => shouldApplyTts,
|
|
39
|
+
setTtsConfig: () => setTtsConfig,
|
|
40
|
+
processTextForTts: () => processTextForTts,
|
|
41
|
+
parseTtsDirective: () => parseTtsDirective,
|
|
42
|
+
maybeApplyTts: () => maybeApplyTts,
|
|
43
|
+
isProviderAvailable: () => isProviderAvailable,
|
|
44
|
+
hasTtsDirective: () => hasTtsDirective,
|
|
45
|
+
getTtsText: () => getTtsText,
|
|
46
|
+
getTtsConfig: () => getTtsConfig,
|
|
47
|
+
getBestProvider: () => getBestProvider,
|
|
48
|
+
formatTtsConfig: () => formatTtsConfig,
|
|
49
|
+
default: () => src_default,
|
|
50
|
+
clearTtsConfig: () => clearTtsConfig,
|
|
51
|
+
cleanTextForTts: () => cleanTextForTts,
|
|
52
|
+
TTS_PROVIDER_PRIORITY: () => TTS_PROVIDER_PRIORITY,
|
|
53
|
+
TTS_PROVIDER_API_KEYS: () => TTS_PROVIDER_API_KEYS,
|
|
54
|
+
DEFAULT_TTS_CONFIG: () => DEFAULT_TTS_CONFIG
|
|
55
|
+
});
|
|
56
|
+
module.exports = __toCommonJS(exports_src);
|
|
57
|
+
var import_core = require("@elizaos/core");
|
|
58
|
+
|
|
59
|
+
// src/directive-parser.ts
|
|
60
|
+
var TTS_DIRECTIVE_PATTERN = /\[\[tts(?::([^\]]+))?\]\]/gi;
|
|
61
|
+
var TTS_TEXT_PATTERN = /\[\[tts:text\]\]([\s\S]*?)\[\[\/tts:text\]\]/gi;
|
|
62
|
+
var KEY_VALUE_PATTERN = /(\w+)\s*=\s*([^\s,]+)/g;
|
|
63
|
+
function hasTtsDirective(text) {
|
|
64
|
+
return TTS_DIRECTIVE_PATTERN.test(text) || TTS_TEXT_PATTERN.test(text);
|
|
65
|
+
}
|
|
66
|
+
function parseTtsDirective(text) {
|
|
67
|
+
if (!hasTtsDirective(text)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const directive = {};
|
|
71
|
+
const textMatch = text.match(TTS_TEXT_PATTERN);
|
|
72
|
+
if (textMatch) {
|
|
73
|
+
const fullMatch = textMatch[0];
|
|
74
|
+
const contentStart = fullMatch.indexOf("]]") + 2;
|
|
75
|
+
const contentEnd = fullMatch.lastIndexOf("[[");
|
|
76
|
+
directive.text = fullMatch.slice(contentStart, contentEnd).trim();
|
|
77
|
+
}
|
|
78
|
+
TTS_DIRECTIVE_PATTERN.lastIndex = 0;
|
|
79
|
+
let match;
|
|
80
|
+
while ((match = TTS_DIRECTIVE_PATTERN.exec(text)) !== null) {
|
|
81
|
+
const params = match[1];
|
|
82
|
+
if (params) {
|
|
83
|
+
let kvMatch;
|
|
84
|
+
KEY_VALUE_PATTERN.lastIndex = 0;
|
|
85
|
+
while ((kvMatch = KEY_VALUE_PATTERN.exec(params)) !== null) {
|
|
86
|
+
const key = kvMatch[1].toLowerCase();
|
|
87
|
+
const value = kvMatch[2];
|
|
88
|
+
switch (key) {
|
|
89
|
+
case "provider":
|
|
90
|
+
directive.provider = normalizeProvider(value);
|
|
91
|
+
break;
|
|
92
|
+
case "voice":
|
|
93
|
+
directive.voice = value;
|
|
94
|
+
break;
|
|
95
|
+
case "model":
|
|
96
|
+
directive.model = value;
|
|
97
|
+
break;
|
|
98
|
+
case "speed":
|
|
99
|
+
directive.speed = parseFloat(value);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return directive;
|
|
106
|
+
}
|
|
107
|
+
function normalizeProvider(raw) {
|
|
108
|
+
const normalized = raw.toLowerCase().trim();
|
|
109
|
+
switch (normalized) {
|
|
110
|
+
case "elevenlabs":
|
|
111
|
+
case "eleven":
|
|
112
|
+
case "xi":
|
|
113
|
+
return "elevenlabs";
|
|
114
|
+
case "openai":
|
|
115
|
+
case "oai":
|
|
116
|
+
return "openai";
|
|
117
|
+
case "edge":
|
|
118
|
+
case "microsoft":
|
|
119
|
+
case "ms":
|
|
120
|
+
return "edge";
|
|
121
|
+
case "simple":
|
|
122
|
+
case "simple-voice":
|
|
123
|
+
case "sam":
|
|
124
|
+
return "simple-voice";
|
|
125
|
+
default:
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function stripTtsDirectives(text) {
|
|
130
|
+
let cleaned = text;
|
|
131
|
+
cleaned = cleaned.replace(TTS_TEXT_PATTERN, "");
|
|
132
|
+
cleaned = cleaned.replace(TTS_DIRECTIVE_PATTERN, "");
|
|
133
|
+
return cleaned.replace(/\s+/g, " ").trim();
|
|
134
|
+
}
|
|
135
|
+
function getTtsText(text, directive) {
|
|
136
|
+
if (directive?.text) {
|
|
137
|
+
return directive.text;
|
|
138
|
+
}
|
|
139
|
+
return stripTtsDirectives(text);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/text-processor.ts
|
|
143
|
+
function cleanTextForTts(text) {
|
|
144
|
+
let cleaned = text;
|
|
145
|
+
cleaned = cleaned.replace(/```[\s\S]*?```/g, "[code block]");
|
|
146
|
+
cleaned = cleaned.replace(/`[^`]+`/g, "[code]");
|
|
147
|
+
cleaned = cleaned.replace(/https?:\/\/[^\s]+/g, "[link]");
|
|
148
|
+
cleaned = cleaned.replace(/\*\*([^*]+)\*\*/g, "$1");
|
|
149
|
+
cleaned = cleaned.replace(/\*([^*]+)\*/g, "$1");
|
|
150
|
+
cleaned = cleaned.replace(/__([^_]+)__/g, "$1");
|
|
151
|
+
cleaned = cleaned.replace(/_([^_]+)_/g, "$1");
|
|
152
|
+
cleaned = cleaned.replace(/^#{1,6}\s+/gm, "");
|
|
153
|
+
cleaned = cleaned.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
154
|
+
cleaned = cleaned.replace(/<[^>]+>/g, "");
|
|
155
|
+
cleaned = cleaned.replace(/\n{2,}/g, `
|
|
156
|
+
`);
|
|
157
|
+
cleaned = cleaned.trim();
|
|
158
|
+
return cleaned;
|
|
159
|
+
}
|
|
160
|
+
function truncateText(text, maxLength) {
|
|
161
|
+
if (text.length <= maxLength) {
|
|
162
|
+
return text;
|
|
163
|
+
}
|
|
164
|
+
const truncated = text.slice(0, maxLength);
|
|
165
|
+
const lastSentenceEnd = Math.max(truncated.lastIndexOf(". "), truncated.lastIndexOf("! "), truncated.lastIndexOf("? "), truncated.lastIndexOf(`.
|
|
166
|
+
`), truncated.lastIndexOf(`!
|
|
167
|
+
`), truncated.lastIndexOf(`?
|
|
168
|
+
`));
|
|
169
|
+
if (lastSentenceEnd > maxLength * 0.5) {
|
|
170
|
+
return truncated.slice(0, lastSentenceEnd + 1).trim();
|
|
171
|
+
}
|
|
172
|
+
const lastSpace = truncated.lastIndexOf(" ");
|
|
173
|
+
if (lastSpace > maxLength * 0.8) {
|
|
174
|
+
return truncated.slice(0, lastSpace).trim() + "...";
|
|
175
|
+
}
|
|
176
|
+
return truncated.trim() + "...";
|
|
177
|
+
}
|
|
178
|
+
async function summarizeForTts(runtime, text, maxLength) {
|
|
179
|
+
try {
|
|
180
|
+
const prompt = `Summarize the following text in ${maxLength} characters or less for text-to-speech. Keep the key points and maintain a conversational tone:
|
|
181
|
+
|
|
182
|
+
${text}`;
|
|
183
|
+
const response = await runtime.useModel("TEXT_SMALL", {
|
|
184
|
+
prompt,
|
|
185
|
+
maxTokens: Math.ceil(maxLength / 3)
|
|
186
|
+
});
|
|
187
|
+
if (typeof response === "string") {
|
|
188
|
+
return response.slice(0, maxLength);
|
|
189
|
+
}
|
|
190
|
+
return truncateText(text, maxLength);
|
|
191
|
+
} catch {
|
|
192
|
+
return truncateText(text, maxLength);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function processTextForTts(runtime, text, options) {
|
|
196
|
+
const { maxLength, summarize, minLength = 10 } = options;
|
|
197
|
+
let processed = cleanTextForTts(text);
|
|
198
|
+
if (processed.length < minLength) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
if (processed.length > maxLength) {
|
|
202
|
+
if (summarize) {
|
|
203
|
+
processed = await summarizeForTts(runtime, processed, maxLength);
|
|
204
|
+
} else {
|
|
205
|
+
processed = truncateText(processed, maxLength);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return processed;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/types.ts
|
|
212
|
+
var DEFAULT_TTS_CONFIG = {
|
|
213
|
+
provider: "auto",
|
|
214
|
+
auto: "off",
|
|
215
|
+
maxLength: 1500,
|
|
216
|
+
summarize: true
|
|
217
|
+
};
|
|
218
|
+
var TTS_PROVIDER_PRIORITY = [
|
|
219
|
+
"elevenlabs",
|
|
220
|
+
"openai",
|
|
221
|
+
"edge",
|
|
222
|
+
"simple-voice"
|
|
223
|
+
];
|
|
224
|
+
var TTS_PROVIDER_API_KEYS = {
|
|
225
|
+
elevenlabs: ["ELEVENLABS_API_KEY", "XI_API_KEY"],
|
|
226
|
+
openai: ["OPENAI_API_KEY"],
|
|
227
|
+
edge: [],
|
|
228
|
+
"simple-voice": [],
|
|
229
|
+
auto: []
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/index.ts
|
|
233
|
+
var sessionConfigs = new Map;
|
|
234
|
+
function getTtsConfig(roomId) {
|
|
235
|
+
const session = sessionConfigs.get(roomId);
|
|
236
|
+
return {
|
|
237
|
+
...DEFAULT_TTS_CONFIG,
|
|
238
|
+
...session
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function setTtsConfig(roomId, config) {
|
|
242
|
+
const existing = sessionConfigs.get(roomId) ?? {};
|
|
243
|
+
sessionConfigs.set(roomId, { ...existing, ...config });
|
|
244
|
+
}
|
|
245
|
+
function clearTtsConfig(roomId) {
|
|
246
|
+
sessionConfigs.delete(roomId);
|
|
247
|
+
}
|
|
248
|
+
function isProviderAvailable(runtime, provider) {
|
|
249
|
+
if (provider === "auto")
|
|
250
|
+
return true;
|
|
251
|
+
const requiredKeys = TTS_PROVIDER_API_KEYS[provider];
|
|
252
|
+
if (requiredKeys.length === 0) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
return requiredKeys.some((key) => {
|
|
256
|
+
const value = runtime.getSetting(key);
|
|
257
|
+
return value && String(value).trim() !== "";
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function getBestProvider(runtime, preferred) {
|
|
261
|
+
if (preferred && preferred !== "auto" && isProviderAvailable(runtime, preferred)) {
|
|
262
|
+
return preferred;
|
|
263
|
+
}
|
|
264
|
+
for (const provider of TTS_PROVIDER_PRIORITY) {
|
|
265
|
+
if (isProviderAvailable(runtime, provider)) {
|
|
266
|
+
return provider;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return "simple-voice";
|
|
270
|
+
}
|
|
271
|
+
async function synthesize(runtime, request) {
|
|
272
|
+
const provider = getBestProvider(runtime, request.provider);
|
|
273
|
+
import_core.logger.debug(`[TTS] Synthesizing with provider: ${provider}`);
|
|
274
|
+
const params = {
|
|
275
|
+
text: request.text,
|
|
276
|
+
voice: request.voice,
|
|
277
|
+
model: request.model,
|
|
278
|
+
speed: request.speed,
|
|
279
|
+
provider
|
|
280
|
+
};
|
|
281
|
+
try {
|
|
282
|
+
const audio = await runtime.useModel(import_core.ModelType.TEXT_TO_SPEECH, params);
|
|
283
|
+
return {
|
|
284
|
+
audio: Buffer.isBuffer(audio) ? audio : Buffer.from(audio),
|
|
285
|
+
format: request.format ?? "mp3",
|
|
286
|
+
provider
|
|
287
|
+
};
|
|
288
|
+
} catch (error) {
|
|
289
|
+
import_core.logger.error(`[TTS] Synthesis failed with ${provider}: ${error}`);
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function shouldApplyTts(config, options) {
|
|
294
|
+
const { auto } = config;
|
|
295
|
+
const { inboundAudio, kind, hasDirective } = options;
|
|
296
|
+
if (auto === "off") {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
if (auto === "always") {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
if (auto === "inbound") {
|
|
303
|
+
return Boolean(inboundAudio);
|
|
304
|
+
}
|
|
305
|
+
if (auto === "tagged") {
|
|
306
|
+
return Boolean(hasDirective);
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
async function maybeApplyTts(runtime, roomId, text, options) {
|
|
311
|
+
const config = getTtsConfig(roomId);
|
|
312
|
+
const directive = parseTtsDirective(text);
|
|
313
|
+
const hasDirective = Boolean(directive);
|
|
314
|
+
if (!shouldApplyTts(config, { ...options, hasDirective })) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
const ttsText = getTtsText(text, directive);
|
|
318
|
+
const processed = await processTextForTts(runtime, ttsText, {
|
|
319
|
+
maxLength: config.maxLength,
|
|
320
|
+
summarize: config.summarize
|
|
321
|
+
});
|
|
322
|
+
if (!processed) {
|
|
323
|
+
import_core.logger.debug("[TTS] Text too short or invalid for TTS");
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const result = await synthesize(runtime, {
|
|
328
|
+
text: processed,
|
|
329
|
+
provider: directive?.provider ?? config.provider,
|
|
330
|
+
voice: directive?.voice ?? config.voice,
|
|
331
|
+
model: directive?.model ?? config.model,
|
|
332
|
+
speed: directive?.speed
|
|
333
|
+
});
|
|
334
|
+
return result.audio;
|
|
335
|
+
} catch (error) {
|
|
336
|
+
import_core.logger.error(`[TTS] Failed to apply TTS: ${error}`);
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function formatTtsConfig(config) {
|
|
341
|
+
const lines = [];
|
|
342
|
+
lines.push(`Auto: ${config.auto}`);
|
|
343
|
+
lines.push(`Provider: ${config.provider}`);
|
|
344
|
+
lines.push(`Max length: ${config.maxLength}`);
|
|
345
|
+
lines.push(`Summarize: ${config.summarize ? "yes" : "no"}`);
|
|
346
|
+
if (config.voice) {
|
|
347
|
+
lines.push(`Voice: ${config.voice}`);
|
|
348
|
+
}
|
|
349
|
+
return lines.join(`
|
|
350
|
+
`);
|
|
351
|
+
}
|
|
352
|
+
var ttsConfigProvider = {
|
|
353
|
+
name: "TTS_CONFIG",
|
|
354
|
+
description: "Current text-to-speech configuration",
|
|
355
|
+
dynamic: true,
|
|
356
|
+
async get(runtime, message, _state) {
|
|
357
|
+
const config = getTtsConfig(message.roomId);
|
|
358
|
+
const bestProvider = getBestProvider(runtime, config.provider);
|
|
359
|
+
return {
|
|
360
|
+
text: formatTtsConfig(config),
|
|
361
|
+
values: {
|
|
362
|
+
ttsAuto: config.auto,
|
|
363
|
+
ttsProvider: config.provider,
|
|
364
|
+
ttsActiveProvider: bestProvider,
|
|
365
|
+
ttsMaxLength: config.maxLength,
|
|
366
|
+
ttsSummarize: config.summarize,
|
|
367
|
+
ttsVoice: config.voice ?? ""
|
|
368
|
+
},
|
|
369
|
+
data: { config }
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
var ttsPlugin = {
|
|
374
|
+
name: "tts",
|
|
375
|
+
description: "Text-to-speech coordinator with multi-provider support and [[tts]] directives",
|
|
376
|
+
providers: [ttsConfigProvider],
|
|
377
|
+
config: {
|
|
378
|
+
TTS_AUTO_MODE: "off",
|
|
379
|
+
TTS_DEFAULT_PROVIDER: "auto",
|
|
380
|
+
TTS_MAX_LENGTH: "1500",
|
|
381
|
+
TTS_SUMMARIZE: "true",
|
|
382
|
+
TTS_DEFAULT_VOICE: ""
|
|
383
|
+
},
|
|
384
|
+
tests: [
|
|
385
|
+
{
|
|
386
|
+
name: "tts-directives",
|
|
387
|
+
tests: [
|
|
388
|
+
{
|
|
389
|
+
name: "Detect TTS directive",
|
|
390
|
+
fn: async (_runtime) => {
|
|
391
|
+
if (!hasTtsDirective("Hello [[tts]] world")) {
|
|
392
|
+
throw new Error("Should detect [[tts]] directive");
|
|
393
|
+
}
|
|
394
|
+
if (!hasTtsDirective("[[tts:provider=elevenlabs]] Hello")) {
|
|
395
|
+
throw new Error("Should detect [[tts:provider=...]] directive");
|
|
396
|
+
}
|
|
397
|
+
if (!hasTtsDirective("[[tts:text]]Hello[[/tts:text]]")) {
|
|
398
|
+
throw new Error("Should detect [[tts:text]] directive");
|
|
399
|
+
}
|
|
400
|
+
if (hasTtsDirective("No directive here")) {
|
|
401
|
+
throw new Error("Should not detect directive in plain text");
|
|
402
|
+
}
|
|
403
|
+
import_core.logger.success("TTS directive detection works correctly");
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
name: "Parse TTS directive with options",
|
|
408
|
+
fn: async (_runtime) => {
|
|
409
|
+
const directive = parseTtsDirective("[[tts:provider=elevenlabs voice=alloy speed=1.5]] Hello");
|
|
410
|
+
if (!directive) {
|
|
411
|
+
throw new Error("Should parse directive");
|
|
412
|
+
}
|
|
413
|
+
if (directive.provider !== "elevenlabs") {
|
|
414
|
+
throw new Error(`Expected provider 'elevenlabs', got '${directive.provider}'`);
|
|
415
|
+
}
|
|
416
|
+
if (directive.voice !== "alloy") {
|
|
417
|
+
throw new Error(`Expected voice 'alloy', got '${directive.voice}'`);
|
|
418
|
+
}
|
|
419
|
+
if (directive.speed !== 1.5) {
|
|
420
|
+
throw new Error(`Expected speed 1.5, got ${directive.speed}`);
|
|
421
|
+
}
|
|
422
|
+
import_core.logger.success("TTS directive parsing works correctly");
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: "Parse TTS text block",
|
|
427
|
+
fn: async (_runtime) => {
|
|
428
|
+
const directive = parseTtsDirective("Some text [[tts:text]]This is the TTS text[[/tts:text]] more text");
|
|
429
|
+
if (!directive) {
|
|
430
|
+
throw new Error("Should parse directive");
|
|
431
|
+
}
|
|
432
|
+
if (directive.text !== "This is the TTS text") {
|
|
433
|
+
throw new Error(`Expected text 'This is the TTS text', got '${directive.text}'`);
|
|
434
|
+
}
|
|
435
|
+
import_core.logger.success("TTS text block parsing works correctly");
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: "Strip TTS directives",
|
|
440
|
+
fn: async (_runtime) => {
|
|
441
|
+
const text = "Hello [[tts:provider=elevenlabs]] world [[tts:text]]TTS text[[/tts:text]]";
|
|
442
|
+
const stripped = stripTtsDirectives(text);
|
|
443
|
+
if (stripped !== "Hello world") {
|
|
444
|
+
throw new Error(`Expected 'Hello world', got '${stripped}'`);
|
|
445
|
+
}
|
|
446
|
+
import_core.logger.success("TTS directive stripping works correctly");
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: "Get TTS text with directive",
|
|
451
|
+
fn: async (_runtime) => {
|
|
452
|
+
const text = "Message [[tts:text]]Custom TTS[[/tts:text]]";
|
|
453
|
+
const directive = parseTtsDirective(text);
|
|
454
|
+
const ttsText = getTtsText(text, directive);
|
|
455
|
+
if (ttsText !== "Custom TTS") {
|
|
456
|
+
throw new Error(`Expected 'Custom TTS', got '${ttsText}'`);
|
|
457
|
+
}
|
|
458
|
+
import_core.logger.success("TTS text extraction works correctly");
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
name: "Get TTS text without directive",
|
|
463
|
+
fn: async (_runtime) => {
|
|
464
|
+
const text = "Plain message";
|
|
465
|
+
const directive = parseTtsDirective(text);
|
|
466
|
+
const ttsText = getTtsText(text, directive);
|
|
467
|
+
if (ttsText !== "Plain message") {
|
|
468
|
+
throw new Error(`Expected 'Plain message', got '${ttsText}'`);
|
|
469
|
+
}
|
|
470
|
+
import_core.logger.success("TTS text fallback works correctly");
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: "tts-text-processing",
|
|
477
|
+
tests: [
|
|
478
|
+
{
|
|
479
|
+
name: "Clean text for TTS",
|
|
480
|
+
fn: async (_runtime) => {
|
|
481
|
+
const text = "**Bold** and `code` with https://example.com";
|
|
482
|
+
const cleaned = cleanTextForTts(text);
|
|
483
|
+
if (cleaned !== "Bold and [code] with [link]") {
|
|
484
|
+
throw new Error(`Expected clean text, got '${cleaned}'`);
|
|
485
|
+
}
|
|
486
|
+
import_core.logger.success("Text cleaning works correctly");
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
name: "Truncate text",
|
|
491
|
+
fn: async (_runtime) => {
|
|
492
|
+
const text = "This is a long sentence. Another sentence here. And more text.";
|
|
493
|
+
const truncated = truncateText(text, 40);
|
|
494
|
+
if (truncated.length > 43) {
|
|
495
|
+
throw new Error(`Expected truncated text, got length ${truncated.length}`);
|
|
496
|
+
}
|
|
497
|
+
import_core.logger.success("Text truncation works correctly");
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
]
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
name: "tts-config",
|
|
504
|
+
tests: [
|
|
505
|
+
{
|
|
506
|
+
name: "Session config management",
|
|
507
|
+
fn: async (_runtime) => {
|
|
508
|
+
const roomId = "test-room-tts";
|
|
509
|
+
clearTtsConfig(roomId);
|
|
510
|
+
setTtsConfig(roomId, { auto: "always", provider: "edge" });
|
|
511
|
+
const config = getTtsConfig(roomId);
|
|
512
|
+
if (config.auto !== "always") {
|
|
513
|
+
throw new Error(`Expected auto 'always', got '${config.auto}'`);
|
|
514
|
+
}
|
|
515
|
+
if (config.provider !== "edge") {
|
|
516
|
+
throw new Error(`Expected provider 'edge', got '${config.provider}'`);
|
|
517
|
+
}
|
|
518
|
+
clearTtsConfig(roomId);
|
|
519
|
+
import_core.logger.success("TTS config management works correctly");
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
name: "Should apply TTS logic",
|
|
524
|
+
fn: async (_runtime) => {
|
|
525
|
+
if (shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: "off" }, {})) {
|
|
526
|
+
throw new Error("Should not apply when auto is off");
|
|
527
|
+
}
|
|
528
|
+
if (!shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: "always" }, {})) {
|
|
529
|
+
throw new Error("Should apply when auto is always");
|
|
530
|
+
}
|
|
531
|
+
if (shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: "inbound" }, {})) {
|
|
532
|
+
throw new Error("Should not apply when inbound without audio");
|
|
533
|
+
}
|
|
534
|
+
if (!shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: "inbound" }, { inboundAudio: true })) {
|
|
535
|
+
throw new Error("Should apply when inbound with audio");
|
|
536
|
+
}
|
|
537
|
+
if (shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: "tagged" }, {})) {
|
|
538
|
+
throw new Error("Should not apply when tagged without directive");
|
|
539
|
+
}
|
|
540
|
+
if (!shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: "tagged" }, { hasDirective: true })) {
|
|
541
|
+
throw new Error("Should apply when tagged with directive");
|
|
542
|
+
}
|
|
543
|
+
import_core.logger.success("TTS apply logic works correctly");
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
]
|
|
547
|
+
}
|
|
548
|
+
],
|
|
549
|
+
async init(config, runtime) {
|
|
550
|
+
import_core.logger.log("[plugin-tts] Initializing TTS coordinator");
|
|
551
|
+
const autoMode = config.TTS_AUTO_MODE ?? "off";
|
|
552
|
+
const provider = config.TTS_DEFAULT_PROVIDER ?? "auto";
|
|
553
|
+
import_core.logger.log(`[plugin-tts] Auto mode: ${autoMode}, Default provider: ${provider}`);
|
|
554
|
+
const available = TTS_PROVIDER_PRIORITY.filter((p) => isProviderAvailable(runtime, p));
|
|
555
|
+
import_core.logger.log(`[plugin-tts] Available providers: ${available.join(", ") || "none"}`);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
var src_default = ttsPlugin;
|
|
559
|
+
|
|
560
|
+
//# debugId=049CD93507972CAC64756E2164756E21
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts", "../../src/directive-parser.ts", "../../src/text-processor.ts", "../../src/types.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Plugin TTS - Text-to-Speech coordinator for Eliza agents\n *\n * Provides a unified TTS interface that:\n * - Supports multiple providers (ElevenLabs, OpenAI, Edge, Simple Voice)\n * - Auto-selects providers based on available API keys\n * - Parses [[tts]] directives from messages\n * - Handles text processing and length limits\n * - Manages per-session TTS configuration\n */\n\nimport {\n type IAgentRuntime,\n ModelType,\n type Plugin,\n type Provider,\n type ProviderResult,\n logger,\n} from \"@elizaos/core\";\n\nimport {\n getTtsText,\n hasTtsDirective,\n parseTtsDirective,\n stripTtsDirectives,\n} from \"./directive-parser\";\nimport {\n cleanTextForTts,\n processTextForTts,\n truncateText,\n} from \"./text-processor\";\nimport {\n DEFAULT_TTS_CONFIG,\n TTS_PROVIDER_API_KEYS,\n TTS_PROVIDER_PRIORITY,\n type TtsApplyKind,\n type TtsAutoMode,\n type TtsConfig,\n type TtsDirective,\n type TtsProvider,\n type TtsRequest,\n type TtsResult,\n type TtsSessionConfig,\n} from \"./types\";\n\n// Re-export everything\nexport * from \"./types\";\nexport * from \"./directive-parser\";\nexport * from \"./text-processor\";\n\n// Session configurations\nconst sessionConfigs = new Map<string, TtsSessionConfig>();\n\n/**\n * Get TTS configuration for a session\n */\nexport function getTtsConfig(roomId: string): TtsConfig {\n const session = sessionConfigs.get(roomId);\n return {\n ...DEFAULT_TTS_CONFIG,\n ...session,\n };\n}\n\n/**\n * Set TTS configuration for a session\n */\nexport function setTtsConfig(\n roomId: string,\n config: Partial<TtsSessionConfig>,\n): void {\n const existing = sessionConfigs.get(roomId) ?? {};\n sessionConfigs.set(roomId, { ...existing, ...config });\n}\n\n/**\n * Clear TTS configuration for a session\n */\nexport function clearTtsConfig(roomId: string): void {\n sessionConfigs.delete(roomId);\n}\n\n/**\n * Check if a provider is available (has required API keys)\n */\nexport function isProviderAvailable(\n runtime: IAgentRuntime,\n provider: TtsProvider,\n): boolean {\n if (provider === \"auto\") return true;\n\n const requiredKeys = TTS_PROVIDER_API_KEYS[provider];\n if (requiredKeys.length === 0) {\n return true; // No API key required\n }\n\n return requiredKeys.some((key) => {\n const value = runtime.getSetting(key);\n return value && String(value).trim() !== \"\";\n });\n}\n\n/**\n * Get the best available provider\n */\nexport function getBestProvider(\n runtime: IAgentRuntime,\n preferred?: TtsProvider,\n): TtsProvider {\n // If preferred is specified and available, use it\n if (\n preferred &&\n preferred !== \"auto\" &&\n isProviderAvailable(runtime, preferred)\n ) {\n return preferred;\n }\n\n // Otherwise, find the first available provider in priority order\n for (const provider of TTS_PROVIDER_PRIORITY) {\n if (isProviderAvailable(runtime, provider)) {\n return provider;\n }\n }\n\n // Fallback to simple-voice (always available)\n return \"simple-voice\";\n}\n\n/**\n * Synthesize text to speech\n */\nexport async function synthesize(\n runtime: IAgentRuntime,\n request: TtsRequest,\n): Promise<TtsResult> {\n const provider = getBestProvider(runtime, request.provider);\n\n logger.debug(`[TTS] Synthesizing with provider: ${provider}`);\n\n const params = {\n text: request.text,\n voice: request.voice,\n model: request.model,\n speed: request.speed,\n provider, // Pass provider hint for routing\n };\n\n try {\n const audio = await runtime.useModel(ModelType.TEXT_TO_SPEECH, params);\n\n return {\n audio: Buffer.isBuffer(audio) ? audio : Buffer.from(audio as ArrayBuffer),\n format: request.format ?? \"mp3\",\n provider,\n };\n } catch (error) {\n logger.error(`[TTS] Synthesis failed with ${provider}: ${error}`);\n throw error;\n }\n}\n\n/**\n * Check if TTS should be applied to a reply\n */\nexport function shouldApplyTts(\n config: TtsConfig,\n options: {\n inboundAudio?: boolean;\n kind?: TtsApplyKind;\n hasDirective?: boolean;\n },\n): boolean {\n const { auto } = config;\n const { inboundAudio, kind, hasDirective } = options;\n\n // TTS is disabled\n if (auto === \"off\") {\n return false;\n }\n\n // Always apply TTS\n if (auto === \"always\") {\n return true;\n }\n\n // Only when inbound message had audio\n if (auto === \"inbound\") {\n return Boolean(inboundAudio);\n }\n\n // Only when [[tts]] directive is present\n if (auto === \"tagged\") {\n return Boolean(hasDirective);\n }\n\n return false;\n}\n\n/**\n * Apply TTS to a reply text if configured\n */\nexport async function maybeApplyTts(\n runtime: IAgentRuntime,\n roomId: string,\n text: string,\n options: {\n inboundAudio?: boolean;\n kind?: TtsApplyKind;\n },\n): Promise<Buffer | null> {\n const config = getTtsConfig(roomId);\n const directive = parseTtsDirective(text);\n const hasDirective = Boolean(directive);\n\n // Check if we should apply TTS\n if (!shouldApplyTts(config, { ...options, hasDirective })) {\n return null;\n }\n\n // Get the text to synthesize\n const ttsText = getTtsText(text, directive);\n\n // Process the text (clean, validate length, maybe summarize)\n const processed = await processTextForTts(runtime, ttsText, {\n maxLength: config.maxLength,\n summarize: config.summarize,\n });\n\n if (!processed) {\n logger.debug(\"[TTS] Text too short or invalid for TTS\");\n return null;\n }\n\n // Synthesize\n try {\n const result = await synthesize(runtime, {\n text: processed,\n provider: directive?.provider ?? config.provider,\n voice: directive?.voice ?? config.voice,\n model: directive?.model ?? config.model,\n speed: directive?.speed,\n });\n\n return result.audio;\n } catch (error) {\n logger.error(`[TTS] Failed to apply TTS: ${error}`);\n return null;\n }\n}\n\n/**\n * Format TTS configuration for display\n */\nexport function formatTtsConfig(config: TtsConfig): string {\n const lines: string[] = [];\n lines.push(`Auto: ${config.auto}`);\n lines.push(`Provider: ${config.provider}`);\n lines.push(`Max length: ${config.maxLength}`);\n lines.push(`Summarize: ${config.summarize ? \"yes\" : \"no\"}`);\n if (config.voice) {\n lines.push(`Voice: ${config.voice}`);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Provider that exposes current TTS configuration\n */\nexport const ttsConfigProvider: Provider = {\n name: \"TTS_CONFIG\",\n description: \"Current text-to-speech configuration\",\n dynamic: true,\n\n async get(runtime, message, _state): Promise<ProviderResult> {\n const config = getTtsConfig(message.roomId);\n const bestProvider = getBestProvider(runtime, config.provider);\n\n return {\n text: formatTtsConfig(config),\n values: {\n ttsAuto: config.auto,\n ttsProvider: config.provider,\n ttsActiveProvider: bestProvider,\n ttsMaxLength: config.maxLength,\n ttsSummarize: config.summarize,\n ttsVoice: config.voice ?? \"\",\n },\n data: { config },\n };\n },\n};\n\n/**\n * Plugin TTS\n *\n * Coordinates text-to-speech synthesis across multiple providers.\n * Works with existing TTS plugins (plugin-edge-tts, plugin-elevenlabs, etc.)\n * to provide a unified interface.\n */\nexport const ttsPlugin: Plugin = {\n name: \"tts\",\n description:\n \"Text-to-speech coordinator with multi-provider support and [[tts]] directives\",\n\n providers: [ttsConfigProvider],\n\n config: {\n TTS_AUTO_MODE: \"off\",\n TTS_DEFAULT_PROVIDER: \"auto\",\n TTS_MAX_LENGTH: \"1500\",\n TTS_SUMMARIZE: \"true\",\n TTS_DEFAULT_VOICE: \"\",\n },\n\n tests: [\n {\n name: \"tts-directives\",\n tests: [\n {\n name: \"Detect TTS directive\",\n fn: async (_runtime: IAgentRuntime) => {\n if (!hasTtsDirective(\"Hello [[tts]] world\")) {\n throw new Error(\"Should detect [[tts]] directive\");\n }\n if (!hasTtsDirective(\"[[tts:provider=elevenlabs]] Hello\")) {\n throw new Error(\"Should detect [[tts:provider=...]] directive\");\n }\n if (!hasTtsDirective(\"[[tts:text]]Hello[[/tts:text]]\")) {\n throw new Error(\"Should detect [[tts:text]] directive\");\n }\n if (hasTtsDirective(\"No directive here\")) {\n throw new Error(\"Should not detect directive in plain text\");\n }\n logger.success(\"TTS directive detection works correctly\");\n },\n },\n {\n name: \"Parse TTS directive with options\",\n fn: async (_runtime: IAgentRuntime) => {\n const directive = parseTtsDirective(\n \"[[tts:provider=elevenlabs voice=alloy speed=1.5]] Hello\",\n );\n if (!directive) {\n throw new Error(\"Should parse directive\");\n }\n if (directive.provider !== \"elevenlabs\") {\n throw new Error(\n `Expected provider 'elevenlabs', got '${directive.provider}'`,\n );\n }\n if (directive.voice !== \"alloy\") {\n throw new Error(\n `Expected voice 'alloy', got '${directive.voice}'`,\n );\n }\n if (directive.speed !== 1.5) {\n throw new Error(`Expected speed 1.5, got ${directive.speed}`);\n }\n logger.success(\"TTS directive parsing works correctly\");\n },\n },\n {\n name: \"Parse TTS text block\",\n fn: async (_runtime: IAgentRuntime) => {\n const directive = parseTtsDirective(\n \"Some text [[tts:text]]This is the TTS text[[/tts:text]] more text\",\n );\n if (!directive) {\n throw new Error(\"Should parse directive\");\n }\n if (directive.text !== \"This is the TTS text\") {\n throw new Error(\n `Expected text 'This is the TTS text', got '${directive.text}'`,\n );\n }\n logger.success(\"TTS text block parsing works correctly\");\n },\n },\n {\n name: \"Strip TTS directives\",\n fn: async (_runtime: IAgentRuntime) => {\n const text =\n \"Hello [[tts:provider=elevenlabs]] world [[tts:text]]TTS text[[/tts:text]]\";\n const stripped = stripTtsDirectives(text);\n if (stripped !== \"Hello world\") {\n throw new Error(`Expected 'Hello world', got '${stripped}'`);\n }\n logger.success(\"TTS directive stripping works correctly\");\n },\n },\n {\n name: \"Get TTS text with directive\",\n fn: async (_runtime: IAgentRuntime) => {\n const text = \"Message [[tts:text]]Custom TTS[[/tts:text]]\";\n const directive = parseTtsDirective(text);\n const ttsText = getTtsText(text, directive);\n if (ttsText !== \"Custom TTS\") {\n throw new Error(`Expected 'Custom TTS', got '${ttsText}'`);\n }\n logger.success(\"TTS text extraction works correctly\");\n },\n },\n {\n name: \"Get TTS text without directive\",\n fn: async (_runtime: IAgentRuntime) => {\n const text = \"Plain message\";\n const directive = parseTtsDirective(text);\n const ttsText = getTtsText(text, directive);\n if (ttsText !== \"Plain message\") {\n throw new Error(`Expected 'Plain message', got '${ttsText}'`);\n }\n logger.success(\"TTS text fallback works correctly\");\n },\n },\n ],\n },\n {\n name: \"tts-text-processing\",\n tests: [\n {\n name: \"Clean text for TTS\",\n fn: async (_runtime: IAgentRuntime) => {\n const text = \"**Bold** and `code` with https://example.com\";\n const cleaned = cleanTextForTts(text);\n if (cleaned !== \"Bold and [code] with [link]\") {\n throw new Error(`Expected clean text, got '${cleaned}'`);\n }\n logger.success(\"Text cleaning works correctly\");\n },\n },\n {\n name: \"Truncate text\",\n fn: async (_runtime: IAgentRuntime) => {\n const text =\n \"This is a long sentence. Another sentence here. And more text.\";\n const truncated = truncateText(text, 40);\n if (truncated.length > 43) {\n // +3 for \"...\"\n throw new Error(\n `Expected truncated text, got length ${truncated.length}`,\n );\n }\n logger.success(\"Text truncation works correctly\");\n },\n },\n ],\n },\n {\n name: \"tts-config\",\n tests: [\n {\n name: \"Session config management\",\n fn: async (_runtime: IAgentRuntime) => {\n const roomId = \"test-room-tts\";\n clearTtsConfig(roomId);\n\n setTtsConfig(roomId, { auto: \"always\", provider: \"edge\" });\n\n const config = getTtsConfig(roomId);\n if (config.auto !== \"always\") {\n throw new Error(`Expected auto 'always', got '${config.auto}'`);\n }\n if (config.provider !== \"edge\") {\n throw new Error(\n `Expected provider 'edge', got '${config.provider}'`,\n );\n }\n\n clearTtsConfig(roomId);\n logger.success(\"TTS config management works correctly\");\n },\n },\n {\n name: \"Should apply TTS logic\",\n fn: async (_runtime: IAgentRuntime) => {\n // Off mode\n if (shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: \"off\" }, {})) {\n throw new Error(\"Should not apply when auto is off\");\n }\n\n // Always mode\n if (\n !shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: \"always\" }, {})\n ) {\n throw new Error(\"Should apply when auto is always\");\n }\n\n // Inbound mode\n if (\n shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: \"inbound\" }, {})\n ) {\n throw new Error(\"Should not apply when inbound without audio\");\n }\n if (\n !shouldApplyTts(\n { ...DEFAULT_TTS_CONFIG, auto: \"inbound\" },\n { inboundAudio: true },\n )\n ) {\n throw new Error(\"Should apply when inbound with audio\");\n }\n\n // Tagged mode\n if (shouldApplyTts({ ...DEFAULT_TTS_CONFIG, auto: \"tagged\" }, {})) {\n throw new Error(\"Should not apply when tagged without directive\");\n }\n if (\n !shouldApplyTts(\n { ...DEFAULT_TTS_CONFIG, auto: \"tagged\" },\n { hasDirective: true },\n )\n ) {\n throw new Error(\"Should apply when tagged with directive\");\n }\n\n logger.success(\"TTS apply logic works correctly\");\n },\n },\n ],\n },\n ],\n\n async init(config, runtime) {\n logger.log(\"[plugin-tts] Initializing TTS coordinator\");\n\n const autoMode = (config.TTS_AUTO_MODE as TtsAutoMode) ?? \"off\";\n const provider = (config.TTS_DEFAULT_PROVIDER as TtsProvider) ?? \"auto\";\n\n logger.log(\n `[plugin-tts] Auto mode: ${autoMode}, Default provider: ${provider}`,\n );\n\n // Log available providers\n const available = TTS_PROVIDER_PRIORITY.filter((p) =>\n isProviderAvailable(runtime, p),\n );\n logger.log(\n `[plugin-tts] Available providers: ${available.join(\", \") || \"none\"}`,\n );\n },\n};\n\nexport default ttsPlugin;\n",
|
|
6
|
+
"/**\n * TTS directive parser\n *\n * Parses [[tts]] directives from text:\n * - [[tts]] - Simple marker to enable TTS for this message\n * - [[tts:provider=elevenlabs]] - Specify provider\n * - [[tts:voice=alloy]] - Specify voice\n * - [[tts:text]]...[[/tts:text]] - Specify exact text to synthesize\n */\n\nimport type { TtsDirective, TtsProvider } from \"./types\";\n\n// Regex patterns\nconst TTS_DIRECTIVE_PATTERN = /\\[\\[tts(?::([^\\]]+))?\\]\\]/gi;\nconst TTS_TEXT_PATTERN = /\\[\\[tts:text\\]\\]([\\s\\S]*?)\\[\\[\\/tts:text\\]\\]/gi;\nconst KEY_VALUE_PATTERN = /(\\w+)\\s*=\\s*([^\\s,]+)/g;\n\n/**\n * Check if text contains any TTS directive\n */\nexport function hasTtsDirective(text: string): boolean {\n return TTS_DIRECTIVE_PATTERN.test(text) || TTS_TEXT_PATTERN.test(text);\n}\n\n/**\n * Parse TTS directives from text\n */\nexport function parseTtsDirective(text: string): TtsDirective | null {\n if (!hasTtsDirective(text)) {\n return null;\n }\n\n const directive: TtsDirective = {};\n\n // Extract [[tts:text]]...[[/tts:text]] content\n const textMatch = text.match(TTS_TEXT_PATTERN);\n if (textMatch) {\n // Get the content between tags\n const fullMatch = textMatch[0];\n const contentStart = fullMatch.indexOf(\"]]\") + 2;\n const contentEnd = fullMatch.lastIndexOf(\"[[\");\n directive.text = fullMatch.slice(contentStart, contentEnd).trim();\n }\n\n // Parse [[tts:key=value]] directives\n TTS_DIRECTIVE_PATTERN.lastIndex = 0;\n let match;\n while ((match = TTS_DIRECTIVE_PATTERN.exec(text)) !== null) {\n const params = match[1];\n if (params) {\n let kvMatch;\n KEY_VALUE_PATTERN.lastIndex = 0;\n while ((kvMatch = KEY_VALUE_PATTERN.exec(params)) !== null) {\n const key = kvMatch[1].toLowerCase();\n const value = kvMatch[2];\n\n switch (key) {\n case \"provider\":\n directive.provider = normalizeProvider(value);\n break;\n case \"voice\":\n directive.voice = value;\n break;\n case \"model\":\n directive.model = value;\n break;\n case \"speed\":\n directive.speed = parseFloat(value);\n break;\n }\n }\n }\n }\n\n return directive;\n}\n\n/**\n * Normalize provider name\n */\nfunction normalizeProvider(raw: string): TtsProvider | undefined {\n const normalized = raw.toLowerCase().trim();\n switch (normalized) {\n case \"elevenlabs\":\n case \"eleven\":\n case \"xi\":\n return \"elevenlabs\";\n case \"openai\":\n case \"oai\":\n return \"openai\";\n case \"edge\":\n case \"microsoft\":\n case \"ms\":\n return \"edge\";\n case \"simple\":\n case \"simple-voice\":\n case \"sam\":\n return \"simple-voice\";\n default:\n return undefined;\n }\n}\n\n/**\n * Strip TTS directives from text\n */\nexport function stripTtsDirectives(text: string): string {\n let cleaned = text;\n\n // Remove [[tts:text]]...[[/tts:text]] blocks\n cleaned = cleaned.replace(TTS_TEXT_PATTERN, \"\");\n\n // Remove [[tts:...]] directives\n cleaned = cleaned.replace(TTS_DIRECTIVE_PATTERN, \"\");\n\n // Clean up extra whitespace\n return cleaned.replace(/\\s+/g, \" \").trim();\n}\n\n/**\n * Get text to synthesize from message\n * Returns directive text if specified, otherwise the full cleaned text\n */\nexport function getTtsText(\n text: string,\n directive: TtsDirective | null,\n): string {\n if (directive?.text) {\n return directive.text;\n }\n return stripTtsDirectives(text);\n}\n",
|
|
7
|
+
"/**\n * Text processor for TTS\n *\n * Handles text cleaning, length limits, and summarization\n */\n\nimport type { IAgentRuntime } from \"@elizaos/core\";\n\n/**\n * Clean text for TTS synthesis\n * Removes markdown, code blocks, and other non-speech content\n */\nexport function cleanTextForTts(text: string): string {\n let cleaned = text;\n\n // Remove code blocks\n cleaned = cleaned.replace(/```[\\s\\S]*?```/g, \"[code block]\");\n\n // Remove inline code\n cleaned = cleaned.replace(/`[^`]+`/g, \"[code]\");\n\n // Remove URLs\n cleaned = cleaned.replace(/https?:\\/\\/[^\\s]+/g, \"[link]\");\n\n // Remove markdown bold/italic\n cleaned = cleaned.replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\");\n cleaned = cleaned.replace(/\\*([^*]+)\\*/g, \"$1\");\n cleaned = cleaned.replace(/__([^_]+)__/g, \"$1\");\n cleaned = cleaned.replace(/_([^_]+)_/g, \"$1\");\n\n // Remove markdown headers\n cleaned = cleaned.replace(/^#{1,6}\\s+/gm, \"\");\n\n // Remove markdown links but keep text\n cleaned = cleaned.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\");\n\n // Remove HTML tags\n cleaned = cleaned.replace(/<[^>]+>/g, \"\");\n\n // Convert multiple newlines to single\n cleaned = cleaned.replace(/\\n{2,}/g, \"\\n\");\n\n // Remove leading/trailing whitespace\n cleaned = cleaned.trim();\n\n return cleaned;\n}\n\n/**\n * Truncate text to max length, trying to break at sentence boundaries\n */\nexport function truncateText(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n\n // Try to break at sentence boundary\n const truncated = text.slice(0, maxLength);\n const lastSentenceEnd = Math.max(\n truncated.lastIndexOf(\". \"),\n truncated.lastIndexOf(\"! \"),\n truncated.lastIndexOf(\"? \"),\n truncated.lastIndexOf(\".\\n\"),\n truncated.lastIndexOf(\"!\\n\"),\n truncated.lastIndexOf(\"?\\n\"),\n );\n\n if (lastSentenceEnd > maxLength * 0.5) {\n return truncated.slice(0, lastSentenceEnd + 1).trim();\n }\n\n // Fall back to word boundary\n const lastSpace = truncated.lastIndexOf(\" \");\n if (lastSpace > maxLength * 0.8) {\n return truncated.slice(0, lastSpace).trim() + \"...\";\n }\n\n return truncated.trim() + \"...\";\n}\n\n/**\n * Summarize text using LLM for TTS\n */\nexport async function summarizeForTts(\n runtime: IAgentRuntime,\n text: string,\n maxLength: number,\n): Promise<string> {\n try {\n const prompt = `Summarize the following text in ${maxLength} characters or less for text-to-speech. Keep the key points and maintain a conversational tone:\\n\\n${text}`;\n\n const response = await runtime.useModel(\"TEXT_SMALL\", {\n prompt,\n maxTokens: Math.ceil(maxLength / 3), // Rough estimate\n });\n\n if (typeof response === \"string\") {\n return response.slice(0, maxLength);\n }\n\n // Fallback to truncation\n return truncateText(text, maxLength);\n } catch {\n // Fallback to truncation on error\n return truncateText(text, maxLength);\n }\n}\n\n/**\n * Process text for TTS synthesis\n * Cleans, validates length, and optionally summarizes\n */\nexport async function processTextForTts(\n runtime: IAgentRuntime,\n text: string,\n options: {\n maxLength: number;\n summarize: boolean;\n minLength?: number;\n },\n): Promise<string | null> {\n const { maxLength, summarize, minLength = 10 } = options;\n\n // Clean the text\n let processed = cleanTextForTts(text);\n\n // Check minimum length\n if (processed.length < minLength) {\n return null;\n }\n\n // Check maximum length\n if (processed.length > maxLength) {\n if (summarize) {\n processed = await summarizeForTts(runtime, processed, maxLength);\n } else {\n processed = truncateText(processed, maxLength);\n }\n }\n\n return processed;\n}\n",
|
|
8
|
+
"/**\n * TTS system types\n */\n\nexport type TtsProvider =\n | \"elevenlabs\"\n | \"openai\"\n | \"edge\"\n | \"simple-voice\"\n | \"auto\";\nexport type TtsAutoMode = \"off\" | \"always\" | \"inbound\" | \"tagged\";\nexport type TtsApplyKind = \"tool\" | \"block\" | \"final\";\n\nexport interface TtsConfig {\n provider: TtsProvider;\n auto: TtsAutoMode;\n maxLength: number;\n summarize: boolean;\n voice?: string;\n model?: string;\n speed?: number;\n}\n\nexport interface TtsDirective {\n provider?: TtsProvider;\n voice?: string;\n model?: string;\n speed?: number;\n text?: string; // [[tts:text]]...[[/tts:text]] extracted content\n}\n\nexport interface TtsRequest {\n text: string;\n provider?: TtsProvider;\n voice?: string;\n model?: string;\n speed?: number;\n format?: \"mp3\" | \"opus\" | \"wav\";\n}\n\nexport interface TtsResult {\n audio: Buffer;\n format: string;\n duration?: number;\n provider: TtsProvider;\n}\n\nexport interface TtsSessionConfig {\n auto?: TtsAutoMode;\n provider?: TtsProvider;\n voice?: string;\n maxLength?: number;\n summarize?: boolean;\n}\n\nexport const DEFAULT_TTS_CONFIG: TtsConfig = {\n provider: \"auto\",\n auto: \"off\",\n maxLength: 1500,\n summarize: true,\n};\n\n// Provider priority for auto-selection\nexport const TTS_PROVIDER_PRIORITY: TtsProvider[] = [\n \"elevenlabs\",\n \"openai\",\n \"edge\",\n \"simple-voice\",\n];\n\n// API key environment variable names for each provider\nexport const TTS_PROVIDER_API_KEYS: Record<TtsProvider, string[]> = {\n elevenlabs: [\"ELEVENLABS_API_KEY\", \"XI_API_KEY\"],\n openai: [\"OPENAI_API_KEY\"],\n edge: [], // No API key required\n \"simple-voice\": [], // No API key required\n auto: [], // Not applicable\n};\n"
|
|
9
|
+
],
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "049CD93507972CAC64756E2164756E21",
|
|
12
|
+
"names": []
|
|
13
|
+
}
|