@shogo-ai/voice 1.10.0 → 2.4.2
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/README.md +89 -0
- package/dist/{chunk-S3S2RNO6.js → chunk-BI6SL35K.js} +1 -184
- package/dist/chunk-IOFWML5L.js +1078 -0
- package/dist/chunk-KMSL5PWK.js +540 -0
- package/dist/chunk-ME3SIZZU.js +581 -0
- package/dist/{chunk-T3T4EBWY.js → chunk-RALZRMFL.js} +6 -2
- package/dist/chunk-TQOBJMEK.js +17 -0
- package/dist/chunk-U5UC7ZVS.js +988 -0
- package/dist/chunk-UW7VQ3S6.js +148 -0
- package/dist/chunk-ZAW6F3YT.js +172 -0
- package/dist/index.cjs +748 -25
- package/dist/index.d.cts +396 -3
- package/dist/index.d.ts +396 -3
- package/dist/index.js +63 -3
- package/dist/live/client.cjs +173 -0
- package/dist/live/client.d.cts +132 -0
- package/dist/live/client.d.ts +132 -0
- package/dist/live/client.js +9 -0
- package/dist/native/index.d.cts +5 -2
- package/dist/native/index.d.ts +5 -2
- package/dist/native/index.js +15 -11
- package/dist/{organicParticlesShaders-DIva0vBO.d.ts → organicParticlesShaders-seZq7jYX.d.cts} +1 -421
- package/dist/{organicParticlesShaders-DIva0vBO.d.cts → organicParticlesShaders-seZq7jYX.d.ts} +1 -421
- package/dist/particlesConfig-0YbdPOdY.d.cts +223 -0
- package/dist/particlesConfig-0YbdPOdY.d.ts +223 -0
- package/dist/react/conversation.cjs +789 -0
- package/dist/react/conversation.d.cts +134 -0
- package/dist/react/conversation.d.ts +134 -0
- package/dist/react/conversation.js +13 -0
- package/dist/react/index.cjs +374 -36
- package/dist/react/index.d.cts +10 -141
- package/dist/react/index.d.ts +10 -141
- package/dist/react/index.js +23 -770
- package/dist/react/visuals.cjs +1048 -0
- package/dist/react/visuals.d.cts +70 -0
- package/dist/react/visuals.d.ts +70 -0
- package/dist/react/visuals.js +10 -0
- package/dist/route/agent.cjs +1047 -85
- package/dist/route/agent.js +3 -3
- package/dist/route/audio-tags.cjs +1047 -85
- package/dist/route/audio-tags.js +3 -3
- package/dist/route/index.cjs +1051 -85
- package/dist/route/index.d.cts +19 -3
- package/dist/route/index.d.ts +19 -3
- package/dist/route/index.js +3 -3
- package/dist/route/music.cjs +1047 -85
- package/dist/route/music.js +3 -3
- package/dist/route/op.cjs +2127 -0
- package/dist/route/op.d.cts +43 -0
- package/dist/route/op.d.ts +43 -0
- package/dist/route/op.js +27 -0
- package/dist/route/signed-url.cjs +1047 -85
- package/dist/route/signed-url.js +3 -3
- package/dist/route/tts-preview.cjs +1047 -85
- package/dist/route/tts-preview.d.cts +6 -4
- package/dist/route/tts-preview.d.ts +6 -4
- package/dist/route/tts-preview.js +3 -3
- package/dist/server.cjs +1047 -85
- package/dist/server.d.cts +40 -8
- package/dist/server.d.ts +40 -8
- package/dist/server.js +2 -2
- package/dist/types-Bfhu-aQH.d.cts +199 -0
- package/dist/types-Bfhu-aQH.d.ts +199 -0
- package/dist/{types-W9y6eMj3.d.ts → types-CadjTXNH.d.cts} +193 -12
- package/dist/{types-W9y6eMj3.d.cts → types-CadjTXNH.d.ts} +193 -12
- package/package.json +21 -1
- package/dist/chunk-B4JC3DK2.js +0 -529
- package/dist/chunk-O362EJQR.js +0 -524
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,10 @@ __export(index_exports, {
|
|
|
28
28
|
DEFAULT_ELEVENLABS_BASE_URL: () => DEFAULT_ELEVENLABS_BASE_URL,
|
|
29
29
|
DEFAULT_MEMORY_BLOCK: () => DEFAULT_MEMORY_BLOCK,
|
|
30
30
|
DEFAULT_MUSIC_MODEL: () => DEFAULT_MUSIC_MODEL,
|
|
31
|
+
DEFAULT_SOUND_GENERATION_MODEL: () => DEFAULT_SOUND_GENERATION_MODEL,
|
|
32
|
+
DEFAULT_SPEECH_TO_TEXT_MODEL: () => DEFAULT_SPEECH_TO_TEXT_MODEL,
|
|
33
|
+
DEFAULT_STS_MODEL: () => DEFAULT_STS_MODEL,
|
|
34
|
+
DEFAULT_TTS_MODEL: () => DEFAULT_TTS_MODEL,
|
|
31
35
|
DEFAULT_TWILIO_BASE_URL: () => DEFAULT_TWILIO_BASE_URL,
|
|
32
36
|
DEFAULT_VOICE_SETTINGS: () => DEFAULT_VOICE_SETTINGS,
|
|
33
37
|
DirectTelephonyClient: () => DirectTelephonyClient,
|
|
@@ -38,28 +42,53 @@ __export(index_exports, {
|
|
|
38
42
|
ElevenLabsClient: () => ElevenLabsClient,
|
|
39
43
|
HostedRuntimeTokenClient: () => HostedRuntimeTokenClient,
|
|
40
44
|
HostedTelephonyClient: () => HostedTelephonyClient,
|
|
45
|
+
LiveSession: () => LiveSession,
|
|
41
46
|
MEMORY_CLIENT_TOOLS: () => MEMORY_CLIENT_TOOLS,
|
|
42
47
|
MockTelephonyClient: () => MockTelephonyClient,
|
|
48
|
+
ShogoLiveClient: () => ShogoLiveClient,
|
|
49
|
+
TTS_FALLBACK_MODEL: () => TTS_FALLBACK_MODEL,
|
|
43
50
|
TelephonyApiError: () => TelephonyApiError,
|
|
44
51
|
TelephonyConfigError: () => TelephonyConfigError,
|
|
45
52
|
TwilioApiError: () => TwilioApiError,
|
|
46
53
|
TwilioClient: () => TwilioClient,
|
|
54
|
+
VOICE_OPS: () => VOICE_OPS,
|
|
55
|
+
VOICE_OP_IDS: () => VOICE_OP_IDS,
|
|
56
|
+
VOICE_SOUND_GENERATION_MODEL_IDS: () => VOICE_SOUND_GENERATION_MODEL_IDS,
|
|
57
|
+
VOICE_SPEECH_TO_TEXT_MODEL_IDS: () => VOICE_SPEECH_TO_TEXT_MODEL_IDS,
|
|
58
|
+
VOICE_STS_MODEL_IDS: () => VOICE_STS_MODEL_IDS,
|
|
59
|
+
VOICE_TTS_MODELS: () => VOICE_TTS_MODELS,
|
|
60
|
+
VOICE_TTS_MODEL_IDS: () => VOICE_TTS_MODEL_IDS,
|
|
61
|
+
WORKSPACE_RUNTIME_TOKEN_MESSAGE: () => WORKSPACE_RUNTIME_TOKEN_MESSAGE,
|
|
47
62
|
buildPreviewLine: () => buildPreviewLine,
|
|
48
63
|
composeAgentPrompt: () => composeAgentPrompt,
|
|
49
64
|
composeExpressivityBlock: () => composeExpressivityBlock,
|
|
50
65
|
createTelephonyClient: () => createTelephonyClient,
|
|
51
66
|
extractBasePrompt: () => extractBasePrompt,
|
|
67
|
+
getVoiceOp: () => getVoiceOp,
|
|
68
|
+
getVoiceTtsModel: () => getVoiceTtsModel,
|
|
69
|
+
isModelRelatedTtsFailure: () => isModelRelatedTtsFailure,
|
|
52
70
|
isVoiceMockEnv: () => isVoiceMockEnv,
|
|
53
71
|
normalizeAudioTags: () => normalizeAudioTags,
|
|
54
72
|
normalizeExpressivity: () => normalizeExpressivity,
|
|
55
73
|
normalizeVoiceSettings: () => normalizeVoiceSettings,
|
|
74
|
+
parseVoiceOpForm: () => parseVoiceOpForm,
|
|
75
|
+
parseVoiceOpJsonBody: () => parseVoiceOpJsonBody,
|
|
56
76
|
readAudioTags: () => readAudioTags,
|
|
57
77
|
readExpressivity: () => readExpressivity,
|
|
78
|
+
readRuntimeAuthSecret: () => readRuntimeAuthSecret,
|
|
58
79
|
readVoiceSettings: () => readVoiceSettings,
|
|
59
80
|
resolveConvaiTtsModel: () => resolveConvaiTtsModel,
|
|
81
|
+
resolveConvaiTtsModelDetailed: () => resolveConvaiTtsModelDetailed,
|
|
60
82
|
stripAudioTags: () => stripAudioTags,
|
|
61
83
|
stripExpressivityBlock: () => stripExpressivityBlock,
|
|
62
|
-
stripMemoryBlock: () => stripMemoryBlock
|
|
84
|
+
stripMemoryBlock: () => stripMemoryBlock,
|
|
85
|
+
voiceOpCredentialError: () => voiceOpCredentialError,
|
|
86
|
+
voiceOpForwardPath: () => voiceOpForwardPath,
|
|
87
|
+
voiceOpIsMultipart: () => voiceOpIsMultipart,
|
|
88
|
+
voiceOpPathParams: () => voiceOpPathParams,
|
|
89
|
+
voiceOpReturnsAudio: () => voiceOpReturnsAudio,
|
|
90
|
+
voiceOpUpstreamBody: () => voiceOpUpstreamBody,
|
|
91
|
+
voiceOpUpstreamPath: () => voiceOpUpstreamPath
|
|
63
92
|
});
|
|
64
93
|
module.exports = __toCommonJS(index_exports);
|
|
65
94
|
|
|
@@ -237,19 +266,74 @@ function composeAgentPrompt(basePrompt, options = {}) {
|
|
|
237
266
|
|
|
238
267
|
// src/elevenlabs.ts
|
|
239
268
|
var DEFAULT_ELEVENLABS_BASE_URL = "https://api.elevenlabs.io";
|
|
240
|
-
var
|
|
241
|
-
"
|
|
242
|
-
"
|
|
243
|
-
"
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
269
|
+
var VOICE_TTS_MODELS = [
|
|
270
|
+
{ id: "eleven_v4", label: "Eleven v4", realtime: false, maxChars: 1e4 },
|
|
271
|
+
{ id: "eleven_v4_turbo", label: "Eleven v4 Turbo", realtime: true, maxChars: 1e4 },
|
|
272
|
+
{ id: "eleven_v3", label: "Eleven v3", realtime: false, maxChars: 5e3 },
|
|
273
|
+
{
|
|
274
|
+
id: "eleven_v3_conversational",
|
|
275
|
+
label: "Eleven v3 Conversational",
|
|
276
|
+
realtime: true,
|
|
277
|
+
maxChars: 5e3
|
|
278
|
+
},
|
|
279
|
+
{ id: "eleven_multilingual_v2", label: "Eleven Multilingual v2", realtime: false, maxChars: 1e4 },
|
|
280
|
+
{ id: "eleven_flash_v2_5", label: "Eleven Flash v2.5", realtime: true, maxChars: 4e4 },
|
|
281
|
+
{ id: "eleven_flash_v2", label: "Eleven Flash v2", realtime: true, maxChars: 3e4 },
|
|
282
|
+
{ id: "eleven_turbo_v2_5", label: "Eleven Turbo v2.5", realtime: true, maxChars: 4e4 },
|
|
283
|
+
{ id: "eleven_turbo_v2", label: "Eleven Turbo v2", realtime: true, maxChars: 3e4, deprecated: true }
|
|
284
|
+
];
|
|
285
|
+
var VOICE_TTS_MODEL_IDS = VOICE_TTS_MODELS.map((m) => m.id);
|
|
286
|
+
var DEFAULT_TTS_MODEL = "eleven_v4";
|
|
287
|
+
var CONVAI_SUPPORTED_TTS_MODELS = new Set(
|
|
288
|
+
VOICE_TTS_MODELS.filter((m) => m.realtime).map((m) => m.id)
|
|
289
|
+
);
|
|
290
|
+
var CONVAI_TTS_MODEL_FALLBACK = "eleven_v4_turbo";
|
|
291
|
+
var TTS_FALLBACK_MODEL = "eleven_flash_v2_5";
|
|
292
|
+
function getVoiceTtsModel(modelId) {
|
|
293
|
+
if (!modelId) return void 0;
|
|
294
|
+
return VOICE_TTS_MODELS.find((m) => m.id === modelId);
|
|
295
|
+
}
|
|
296
|
+
var loggedDowngrades = /* @__PURE__ */ new Set();
|
|
297
|
+
function resolveConvaiTtsModelDetailed(requested) {
|
|
298
|
+
if (requested && CONVAI_SUPPORTED_TTS_MODELS.has(requested)) {
|
|
299
|
+
return { modelId: requested, requested, downgraded: false };
|
|
300
|
+
}
|
|
301
|
+
const known = getVoiceTtsModel(requested);
|
|
302
|
+
const reason = !requested ? void 0 : known ? `${requested} is not supported by the convai realtime runtime` : `unknown TTS model ${requested}`;
|
|
303
|
+
if (reason && !loggedDowngrades.has(requested)) {
|
|
304
|
+
loggedDowngrades.add(requested);
|
|
305
|
+
try {
|
|
306
|
+
console.warn(
|
|
307
|
+
`[voice] convai TTS model ${requested} is unavailable (${reason}); using ${CONVAI_TTS_MODEL_FALLBACK}`
|
|
308
|
+
);
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
modelId: CONVAI_TTS_MODEL_FALLBACK,
|
|
314
|
+
requested: requested ?? null,
|
|
315
|
+
downgraded: Boolean(requested),
|
|
316
|
+
...reason ? { reason } : {}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
248
319
|
function resolveConvaiTtsModel(requested) {
|
|
249
|
-
|
|
250
|
-
|
|
320
|
+
return resolveConvaiTtsModelDetailed(requested).modelId;
|
|
321
|
+
}
|
|
322
|
+
function isModelRelatedTtsFailure(status, body) {
|
|
323
|
+
if (status === 402) return true;
|
|
324
|
+
if (status !== 400 && status !== 403 && status !== 404 && status !== 422) return false;
|
|
325
|
+
return /model|plan|permission/i.test(body);
|
|
251
326
|
}
|
|
252
327
|
var DEFAULT_MUSIC_MODEL = "music_v1";
|
|
328
|
+
var VOICE_STS_MODEL_IDS = [
|
|
329
|
+
"eleven_multilingual_sts_v2",
|
|
330
|
+
"eleven_english_sts_v2"
|
|
331
|
+
];
|
|
332
|
+
var DEFAULT_STS_MODEL = "eleven_multilingual_sts_v2";
|
|
333
|
+
var VOICE_SPEECH_TO_TEXT_MODEL_IDS = ["scribe_v1"];
|
|
334
|
+
var DEFAULT_SPEECH_TO_TEXT_MODEL = "scribe_v1";
|
|
335
|
+
var VOICE_SOUND_GENERATION_MODEL_IDS = ["eleven_text_to_sound_v2"];
|
|
336
|
+
var DEFAULT_SOUND_GENERATION_MODEL = "eleven_text_to_sound_v2";
|
|
253
337
|
var ElevenLabsApiError = class extends Error {
|
|
254
338
|
constructor(message, status, body) {
|
|
255
339
|
super(message);
|
|
@@ -414,12 +498,18 @@ var ElevenLabsClient = class {
|
|
|
414
498
|
return data.signed_url;
|
|
415
499
|
}
|
|
416
500
|
/**
|
|
417
|
-
* One-shot TTS. Returns the raw audio bytes plus the model
|
|
418
|
-
*
|
|
419
|
-
*
|
|
501
|
+
* One-shot TTS. Returns the raw audio bytes plus the model that actually
|
|
502
|
+
* produced them.
|
|
503
|
+
*
|
|
504
|
+
* The requested model is never swapped silently: we only retry on
|
|
505
|
+
* {@link TTS_FALLBACK_MODEL} when the failure is plausibly about
|
|
506
|
+
* the model (400/403/404/422 whose body mentions model/plan/permission,
|
|
507
|
+
* or 402 for an out-of-plan model). Auth (401), rate limit (429), 5xx and
|
|
508
|
+
* network errors are surfaced as-is. When a fallback does happen the
|
|
509
|
+
* result reports `requestedModel`, `fellBack` and `fallbackReason`.
|
|
420
510
|
*/
|
|
421
511
|
async textToSpeech(params) {
|
|
422
|
-
const
|
|
512
|
+
const requestedModel = params.modelId && params.modelId.length > 0 ? params.modelId : DEFAULT_TTS_MODEL;
|
|
423
513
|
const accept = params.accept ?? "audio/mpeg";
|
|
424
514
|
const attempt = async (m) => {
|
|
425
515
|
const res2 = await this.fetchImpl(
|
|
@@ -436,19 +526,39 @@ var ElevenLabsClient = class {
|
|
|
436
526
|
);
|
|
437
527
|
return res2;
|
|
438
528
|
};
|
|
439
|
-
let res = await attempt(
|
|
440
|
-
let used =
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
used = "eleven_turbo_v2_5";
|
|
444
|
-
}
|
|
529
|
+
let res = await attempt(requestedModel);
|
|
530
|
+
let used = requestedModel;
|
|
531
|
+
let fellBack = false;
|
|
532
|
+
let fallbackReason;
|
|
445
533
|
if (!res.ok) {
|
|
446
|
-
const
|
|
447
|
-
|
|
534
|
+
const body = await res.text();
|
|
535
|
+
if (requestedModel !== TTS_FALLBACK_MODEL && isModelRelatedTtsFailure(res.status, body)) {
|
|
536
|
+
fallbackReason = `elevenlabs ${res.status} for ${requestedModel}: ${body.slice(0, 200)}`;
|
|
537
|
+
res = await attempt(TTS_FALLBACK_MODEL);
|
|
538
|
+
used = TTS_FALLBACK_MODEL;
|
|
539
|
+
fellBack = true;
|
|
540
|
+
if (!res.ok) {
|
|
541
|
+
const retryBody = await res.text();
|
|
542
|
+
throw new ElevenLabsApiError(
|
|
543
|
+
`textToSpeech failed: ${res.status}`,
|
|
544
|
+
res.status,
|
|
545
|
+
retryBody
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
} else {
|
|
549
|
+
throw new ElevenLabsApiError(`textToSpeech failed: ${res.status}`, res.status, body);
|
|
550
|
+
}
|
|
448
551
|
}
|
|
449
552
|
const audio = await res.arrayBuffer();
|
|
450
553
|
const contentType = res.headers.get("content-type") ?? accept;
|
|
451
|
-
return {
|
|
554
|
+
return {
|
|
555
|
+
audio,
|
|
556
|
+
modelId: used,
|
|
557
|
+
requestedModel,
|
|
558
|
+
fellBack,
|
|
559
|
+
...fallbackReason ? { fallbackReason } : {},
|
|
560
|
+
contentType
|
|
561
|
+
};
|
|
452
562
|
}
|
|
453
563
|
/**
|
|
454
564
|
* Low-level escape hatch for any ElevenLabs endpoint not covered by a
|
|
@@ -554,6 +664,119 @@ var ElevenLabsClient = class {
|
|
|
554
664
|
}
|
|
555
665
|
return { audio, contentType };
|
|
556
666
|
}
|
|
667
|
+
/**
|
|
668
|
+
* Convert speech to speech — the "voice changer": keep the performance,
|
|
669
|
+
* swap the voice. `POST /v1/speech-to-speech/{voiceId}` with a multipart
|
|
670
|
+
* audio part. Returns the converted audio bytes.
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* ```ts
|
|
674
|
+
* const { audio } = await el.speechToSpeech({
|
|
675
|
+
* voiceId: '21m00Tcm4TlvDq8ikWAM',
|
|
676
|
+
* audio: await fetch(myRecording).then((r) => r.blob()),
|
|
677
|
+
* modelId: 'eleven_multilingual_sts_v2',
|
|
678
|
+
* })
|
|
679
|
+
* ```
|
|
680
|
+
*/
|
|
681
|
+
async speechToSpeech(params) {
|
|
682
|
+
if (!params.voiceId) throw new Error("speechToSpeech: voiceId is required");
|
|
683
|
+
if (!params.audio) throw new Error("speechToSpeech: audio is required");
|
|
684
|
+
const form = new FormData();
|
|
685
|
+
form.set("audio", params.audio, params.fileName ?? "audio");
|
|
686
|
+
form.set("model_id", params.modelId ?? DEFAULT_STS_MODEL);
|
|
687
|
+
if (params.voiceSettings) {
|
|
688
|
+
form.set("voice_settings", JSON.stringify(params.voiceSettings));
|
|
689
|
+
}
|
|
690
|
+
if (params.outputFormat) form.set("output_format", params.outputFormat);
|
|
691
|
+
if (params.fileFormat) form.set("file_format", params.fileFormat);
|
|
692
|
+
if (params.seed !== void 0) form.set("seed", String(params.seed));
|
|
693
|
+
if (params.removeBackgroundNoise !== void 0) {
|
|
694
|
+
form.set("remove_background_noise", String(params.removeBackgroundNoise));
|
|
695
|
+
}
|
|
696
|
+
const { audio, contentType } = await this.request({
|
|
697
|
+
method: "POST",
|
|
698
|
+
path: `/v1/speech-to-speech/${encodeURIComponent(params.voiceId)}`,
|
|
699
|
+
body: form,
|
|
700
|
+
accept: "audio/mpeg"
|
|
701
|
+
});
|
|
702
|
+
if (!audio) {
|
|
703
|
+
throw new ElevenLabsApiError(
|
|
704
|
+
"speechToSpeech: expected audio bytes but got a JSON response",
|
|
705
|
+
500,
|
|
706
|
+
""
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
return { audio, contentType };
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Generate a sound effect from a text prompt. `POST /v1/sound-generation`.
|
|
713
|
+
*
|
|
714
|
+
* @example
|
|
715
|
+
* ```ts
|
|
716
|
+
* const { audio } = await el.soundEffects({ text: 'heavy wooden door creaking' })
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
async soundEffects(params) {
|
|
720
|
+
if (!params.text || !params.text.trim()) {
|
|
721
|
+
throw new Error("soundEffects: text is required");
|
|
722
|
+
}
|
|
723
|
+
const body = {
|
|
724
|
+
text: params.text,
|
|
725
|
+
model_id: params.modelId ?? DEFAULT_SOUND_GENERATION_MODEL
|
|
726
|
+
};
|
|
727
|
+
if (params.durationSeconds !== void 0) body.duration_seconds = params.durationSeconds;
|
|
728
|
+
if (params.promptInfluence !== void 0) body.prompt_influence = params.promptInfluence;
|
|
729
|
+
if (params.loop !== void 0) body.loop = params.loop;
|
|
730
|
+
const { audio, contentType } = await this.request({
|
|
731
|
+
method: "POST",
|
|
732
|
+
path: "/v1/sound-generation",
|
|
733
|
+
body,
|
|
734
|
+
accept: "audio/mpeg",
|
|
735
|
+
...params.outputFormat ? { query: { output_format: params.outputFormat } } : {}
|
|
736
|
+
});
|
|
737
|
+
if (!audio) {
|
|
738
|
+
throw new ElevenLabsApiError(
|
|
739
|
+
"soundEffects: expected audio bytes but got a JSON response",
|
|
740
|
+
500,
|
|
741
|
+
""
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
return { audio, contentType };
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Transcribe audio with Scribe. `POST /v1/speech-to-text` (multipart).
|
|
748
|
+
*
|
|
749
|
+
* @example
|
|
750
|
+
* ```ts
|
|
751
|
+
* const { text } = await el.transcribe({ file: recording })
|
|
752
|
+
* ```
|
|
753
|
+
*/
|
|
754
|
+
async transcribe(params) {
|
|
755
|
+
if (!params.file) throw new Error("transcribe: file is required");
|
|
756
|
+
const form = new FormData();
|
|
757
|
+
form.set("file", params.file, params.fileName ?? "audio");
|
|
758
|
+
form.set("model_id", params.modelId ?? DEFAULT_SPEECH_TO_TEXT_MODEL);
|
|
759
|
+
if (params.languageCode) form.set("language_code", params.languageCode);
|
|
760
|
+
if (params.diarize !== void 0) form.set("diarize", String(params.diarize));
|
|
761
|
+
if (params.tagAudioEvents !== void 0) {
|
|
762
|
+
form.set("tag_audio_events", String(params.tagAudioEvents));
|
|
763
|
+
}
|
|
764
|
+
if (params.numSpeakers !== void 0) form.set("num_speakers", String(params.numSpeakers));
|
|
765
|
+
const { json } = await this.request({
|
|
766
|
+
method: "POST",
|
|
767
|
+
path: "/v1/speech-to-text",
|
|
768
|
+
body: form,
|
|
769
|
+
accept: "application/json"
|
|
770
|
+
});
|
|
771
|
+
if (!json || typeof json !== "object") {
|
|
772
|
+
throw new ElevenLabsApiError(
|
|
773
|
+
"transcribe: expected a JSON transcription but got audio",
|
|
774
|
+
500,
|
|
775
|
+
""
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
return json;
|
|
779
|
+
}
|
|
557
780
|
/** Probe whether a given voice id still exists on the caller's account. */
|
|
558
781
|
async voiceExists(voiceId) {
|
|
559
782
|
const res = await this.fetchImpl(
|
|
@@ -717,6 +940,325 @@ var MEMORY_CLIENT_TOOLS = [
|
|
|
717
940
|
}
|
|
718
941
|
];
|
|
719
942
|
|
|
943
|
+
// src/ops.ts
|
|
944
|
+
var MAX_AUDIO_BYTES = 25 * 1024 * 1024;
|
|
945
|
+
var MAX_JSON_BYTES = 8 * 1024;
|
|
946
|
+
var MAX_TEXT_BYTES = 256 * 1024;
|
|
947
|
+
var VOICE_OPS = [
|
|
948
|
+
{
|
|
949
|
+
id: "text-to-speech",
|
|
950
|
+
method: "POST",
|
|
951
|
+
hostedPath: "/voice/op/text-to-speech",
|
|
952
|
+
legacyHostedPath: "/voice/tts",
|
|
953
|
+
upstreamPath: "/v1/text-to-speech/{voiceId}",
|
|
954
|
+
pathParams: ["voiceId"],
|
|
955
|
+
transport: "json-binary",
|
|
956
|
+
call: "textToSpeech",
|
|
957
|
+
permission: "project:read",
|
|
958
|
+
maxBodyBytes: MAX_TEXT_BYTES,
|
|
959
|
+
cost: { unit: "character", per: 1 },
|
|
960
|
+
models: VOICE_TTS_MODEL_IDS,
|
|
961
|
+
defaultModel: CONVAI_TTS_MODEL_FALLBACK,
|
|
962
|
+
defaultVoiceId: true,
|
|
963
|
+
forward: {
|
|
964
|
+
text: "text",
|
|
965
|
+
voiceId: "voice_id",
|
|
966
|
+
modelId: "model_id",
|
|
967
|
+
voiceSettings: "voice_settings"
|
|
968
|
+
},
|
|
969
|
+
required: ["text"],
|
|
970
|
+
responseHeaders: ["x-tts-model-used", "x-tts-model-requested", "warning"],
|
|
971
|
+
accept: "audio/mpeg",
|
|
972
|
+
errorLabel: "TTS failed"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
id: "text-to-speech-with-timestamps",
|
|
976
|
+
method: "POST",
|
|
977
|
+
hostedPath: "/voice/op/text-to-speech-with-timestamps",
|
|
978
|
+
upstreamPath: "/v1/text-to-speech/{voiceId}/with-timestamps",
|
|
979
|
+
pathParams: ["voiceId"],
|
|
980
|
+
transport: "json-json",
|
|
981
|
+
permission: "project:read",
|
|
982
|
+
maxBodyBytes: MAX_TEXT_BYTES,
|
|
983
|
+
cost: { unit: "character", per: 1 },
|
|
984
|
+
models: VOICE_TTS_MODEL_IDS,
|
|
985
|
+
defaultModel: CONVAI_TTS_MODEL_FALLBACK,
|
|
986
|
+
defaultVoiceId: true,
|
|
987
|
+
forward: {
|
|
988
|
+
text: "text",
|
|
989
|
+
voiceId: "voice_id",
|
|
990
|
+
modelId: "model_id",
|
|
991
|
+
voiceSettings: "voice_settings",
|
|
992
|
+
outputFormat: "output_format"
|
|
993
|
+
},
|
|
994
|
+
required: ["text"],
|
|
995
|
+
accept: "application/json"
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
id: "speech-to-speech",
|
|
999
|
+
method: "POST",
|
|
1000
|
+
hostedPath: "/voice/op/speech-to-speech",
|
|
1001
|
+
upstreamPath: "/v1/speech-to-speech/{voiceId}",
|
|
1002
|
+
pathParams: ["voiceId"],
|
|
1003
|
+
transport: "multipart-binary",
|
|
1004
|
+
permission: "project:read",
|
|
1005
|
+
maxBodyBytes: MAX_AUDIO_BYTES,
|
|
1006
|
+
cost: { unit: "second", per: 1 },
|
|
1007
|
+
models: VOICE_STS_MODEL_IDS,
|
|
1008
|
+
defaultModel: DEFAULT_STS_MODEL,
|
|
1009
|
+
defaultVoiceId: true,
|
|
1010
|
+
forward: {
|
|
1011
|
+
audio: "audio",
|
|
1012
|
+
voiceId: "voice_id",
|
|
1013
|
+
modelId: "model_id",
|
|
1014
|
+
voiceSettings: "voice_settings",
|
|
1015
|
+
outputFormat: "output_format",
|
|
1016
|
+
fileFormat: "file_format",
|
|
1017
|
+
seed: "seed",
|
|
1018
|
+
removeBackgroundNoise: "remove_background_noise"
|
|
1019
|
+
},
|
|
1020
|
+
required: ["audio"],
|
|
1021
|
+
accept: "audio/mpeg"
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
id: "speech-to-text",
|
|
1025
|
+
method: "POST",
|
|
1026
|
+
hostedPath: "/voice/op/speech-to-text",
|
|
1027
|
+
upstreamPath: "/v1/speech-to-text",
|
|
1028
|
+
transport: "multipart-json",
|
|
1029
|
+
permission: "project:read",
|
|
1030
|
+
maxBodyBytes: MAX_AUDIO_BYTES,
|
|
1031
|
+
cost: { unit: "second", per: 1 },
|
|
1032
|
+
models: VOICE_SPEECH_TO_TEXT_MODEL_IDS,
|
|
1033
|
+
defaultModel: DEFAULT_SPEECH_TO_TEXT_MODEL,
|
|
1034
|
+
forward: {
|
|
1035
|
+
file: "file",
|
|
1036
|
+
modelId: "model_id",
|
|
1037
|
+
languageCode: "language_code",
|
|
1038
|
+
diarize: "diarize",
|
|
1039
|
+
tagAudioEvents: "tag_audio_events",
|
|
1040
|
+
numSpeakers: "num_speakers"
|
|
1041
|
+
},
|
|
1042
|
+
required: ["file"],
|
|
1043
|
+
accept: "application/json"
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
id: "sound-generation",
|
|
1047
|
+
method: "POST",
|
|
1048
|
+
hostedPath: "/voice/op/sound-generation",
|
|
1049
|
+
upstreamPath: "/v1/sound-generation",
|
|
1050
|
+
transport: "json-binary",
|
|
1051
|
+
permission: "project:read",
|
|
1052
|
+
maxBodyBytes: MAX_JSON_BYTES,
|
|
1053
|
+
cost: { unit: "second", per: 1 },
|
|
1054
|
+
models: VOICE_SOUND_GENERATION_MODEL_IDS,
|
|
1055
|
+
defaultModel: DEFAULT_SOUND_GENERATION_MODEL,
|
|
1056
|
+
forward: {
|
|
1057
|
+
text: "text",
|
|
1058
|
+
durationSeconds: "duration_seconds",
|
|
1059
|
+
promptInfluence: "prompt_influence",
|
|
1060
|
+
loop: "loop",
|
|
1061
|
+
modelId: "model_id"
|
|
1062
|
+
},
|
|
1063
|
+
// `output_format` is a query param on this endpoint, not a body field — the
|
|
1064
|
+
// typed `soundEffects()` client sends it there, and a body field is
|
|
1065
|
+
// silently ignored upstream.
|
|
1066
|
+
queryForward: ["output_format"],
|
|
1067
|
+
required: ["text"],
|
|
1068
|
+
accept: "audio/mpeg"
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
id: "music",
|
|
1072
|
+
method: "POST",
|
|
1073
|
+
hostedPath: "/voice/op/music",
|
|
1074
|
+
legacyHostedPath: "/voice/music",
|
|
1075
|
+
upstreamPath: "/v1/music",
|
|
1076
|
+
transport: "json-binary",
|
|
1077
|
+
call: "composeMusic",
|
|
1078
|
+
permission: "project:read",
|
|
1079
|
+
maxBodyBytes: MAX_JSON_BYTES,
|
|
1080
|
+
cost: { unit: "second", per: 1 },
|
|
1081
|
+
models: [DEFAULT_MUSIC_MODEL],
|
|
1082
|
+
defaultModel: DEFAULT_MUSIC_MODEL,
|
|
1083
|
+
forward: {
|
|
1084
|
+
prompt: "prompt",
|
|
1085
|
+
compositionPlan: "composition_plan",
|
|
1086
|
+
musicLengthMs: "music_length_ms",
|
|
1087
|
+
modelId: "model_id",
|
|
1088
|
+
forceInstrumental: "force_instrumental",
|
|
1089
|
+
outputFormat: "output_format"
|
|
1090
|
+
},
|
|
1091
|
+
accept: "audio/mpeg",
|
|
1092
|
+
errorLabel: "Music generation failed",
|
|
1093
|
+
validate: (fields) => {
|
|
1094
|
+
const hasPrompt = typeof fields.prompt === "string" && fields.prompt.trim().length > 0;
|
|
1095
|
+
const hasPlan = fields.compositionPlan != null && typeof fields.compositionPlan === "object";
|
|
1096
|
+
return hasPrompt === hasPlan ? "Provide exactly one of `prompt` or `compositionPlan`" : null;
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
id: "voices",
|
|
1101
|
+
method: "GET",
|
|
1102
|
+
hostedPath: "/voice/op/voices",
|
|
1103
|
+
upstreamPath: "/v1/voices",
|
|
1104
|
+
transport: "json-json",
|
|
1105
|
+
permission: "project:read",
|
|
1106
|
+
maxBodyBytes: 0,
|
|
1107
|
+
cost: { unit: "request", per: 1 },
|
|
1108
|
+
queryForward: ["page_size", "search", "voice_type", "category", "next_page_token"],
|
|
1109
|
+
accept: "application/json"
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
id: "models",
|
|
1113
|
+
method: "GET",
|
|
1114
|
+
hostedPath: "/voice/op/models",
|
|
1115
|
+
upstreamPath: "/v1/models",
|
|
1116
|
+
transport: "json-json",
|
|
1117
|
+
permission: "project:read",
|
|
1118
|
+
maxBodyBytes: 0,
|
|
1119
|
+
cost: { unit: "request", per: 1 },
|
|
1120
|
+
accept: "application/json"
|
|
1121
|
+
}
|
|
1122
|
+
];
|
|
1123
|
+
var VOICE_OP_IDS = VOICE_OPS.map((op) => op.id);
|
|
1124
|
+
function getVoiceOp(id) {
|
|
1125
|
+
if (!id) return void 0;
|
|
1126
|
+
return VOICE_OPS.find((op) => op.id === id);
|
|
1127
|
+
}
|
|
1128
|
+
function voiceOpForwardPath(op) {
|
|
1129
|
+
return op.legacyHostedPath ?? op.hostedPath;
|
|
1130
|
+
}
|
|
1131
|
+
function voiceOpIsMultipart(op) {
|
|
1132
|
+
return op.transport === "multipart-binary" || op.transport === "multipart-json";
|
|
1133
|
+
}
|
|
1134
|
+
function voiceOpReturnsAudio(op) {
|
|
1135
|
+
return op.transport === "json-binary" || op.transport === "multipart-binary";
|
|
1136
|
+
}
|
|
1137
|
+
function voiceOpUpstreamPath(op, params) {
|
|
1138
|
+
let path = op.upstreamPath;
|
|
1139
|
+
for (const name of op.pathParams ?? []) {
|
|
1140
|
+
const value = params[name];
|
|
1141
|
+
if (!value) {
|
|
1142
|
+
return { error: `Missing \`${name}\` (required path parameter for ${op.id})` };
|
|
1143
|
+
}
|
|
1144
|
+
path = path.split(`{${name}}`).join(encodeURIComponent(value));
|
|
1145
|
+
}
|
|
1146
|
+
return path;
|
|
1147
|
+
}
|
|
1148
|
+
function voiceOpCredentialError(op, credential) {
|
|
1149
|
+
if (op.byoOnly && credential === "pooled") {
|
|
1150
|
+
return {
|
|
1151
|
+
status: 403,
|
|
1152
|
+
error: "byo_key_required",
|
|
1153
|
+
detail: `${op.id} runs on your own ElevenLabs key, not Shogo's pooled key \u2014 pass it to ElevenLabsClient and call request() directly`
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
return null;
|
|
1157
|
+
}
|
|
1158
|
+
var MODEL_FIELD = "modelId";
|
|
1159
|
+
function parseVoiceOpJsonBody(op, raw) {
|
|
1160
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1161
|
+
return { ok: false, status: 400, error: "Invalid JSON body" };
|
|
1162
|
+
}
|
|
1163
|
+
const body = raw;
|
|
1164
|
+
const fields = {};
|
|
1165
|
+
for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
|
|
1166
|
+
if (upstreamField && body[callerField] !== void 0) fields[callerField] = body[callerField];
|
|
1167
|
+
}
|
|
1168
|
+
for (const name of op.required ?? []) {
|
|
1169
|
+
const value = fields[name];
|
|
1170
|
+
if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
|
|
1171
|
+
return { ok: false, status: 400, error: `Missing \`${name}\`` };
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
const modelCheck = checkVoiceOpModel(op, fields);
|
|
1175
|
+
if ("error" in modelCheck) return { ok: false, status: 400, ...modelCheck };
|
|
1176
|
+
if (op.validate) {
|
|
1177
|
+
const message = op.validate(fields);
|
|
1178
|
+
if (message) return { ok: false, status: 400, error: message };
|
|
1179
|
+
}
|
|
1180
|
+
return { ok: true, fields };
|
|
1181
|
+
}
|
|
1182
|
+
function parseVoiceOpForm(op, form) {
|
|
1183
|
+
const fields = {};
|
|
1184
|
+
for (const callerField of Object.keys(op.forward ?? {})) {
|
|
1185
|
+
const value = form.get(callerField);
|
|
1186
|
+
if (value !== null) fields[callerField] = value;
|
|
1187
|
+
}
|
|
1188
|
+
for (const name of op.required ?? []) {
|
|
1189
|
+
const value = fields[name];
|
|
1190
|
+
if (!(value instanceof Blob)) {
|
|
1191
|
+
return {
|
|
1192
|
+
ok: false,
|
|
1193
|
+
status: 400,
|
|
1194
|
+
error: `Missing \`${name}\` file part`,
|
|
1195
|
+
detail: value === void 0 ? `send a multipart body with a \`${name}\` file part` : `\`${name}\` must be a file part, not a text field`
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
if (value.size === 0) {
|
|
1199
|
+
return { ok: false, status: 400, error: `Empty \`${name}\` file part` };
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
const modelCheck = checkVoiceOpModel(op, fields);
|
|
1203
|
+
if ("error" in modelCheck) return { ok: false, status: 400, ...modelCheck };
|
|
1204
|
+
const forward = op.forward ?? {};
|
|
1205
|
+
const pathParams = new Set(op.pathParams ?? []);
|
|
1206
|
+
const out = new FormData();
|
|
1207
|
+
for (const [callerField, upstreamField] of Object.entries(forward)) {
|
|
1208
|
+
if (pathParams.has(callerField)) continue;
|
|
1209
|
+
const value = fields[callerField];
|
|
1210
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
1211
|
+
if (value instanceof Blob) {
|
|
1212
|
+
const name = value instanceof File ? value.name : callerField;
|
|
1213
|
+
out.set(upstreamField, value, name);
|
|
1214
|
+
} else if (typeof value === "string") {
|
|
1215
|
+
out.set(upstreamField, value);
|
|
1216
|
+
} else {
|
|
1217
|
+
out.set(upstreamField, JSON.stringify(value));
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
const modelField = forward[MODEL_FIELD];
|
|
1221
|
+
if (op.defaultModel && modelField && out.get(modelField) === null) {
|
|
1222
|
+
out.set(modelField, op.defaultModel);
|
|
1223
|
+
}
|
|
1224
|
+
return { ok: true, form: out, fields };
|
|
1225
|
+
}
|
|
1226
|
+
function voiceOpUpstreamBody(op, fields) {
|
|
1227
|
+
const pathParams = new Set(op.pathParams ?? []);
|
|
1228
|
+
const body = {};
|
|
1229
|
+
for (const [callerField, upstreamField] of Object.entries(op.forward ?? {})) {
|
|
1230
|
+
if (pathParams.has(callerField)) continue;
|
|
1231
|
+
const value = fields[callerField];
|
|
1232
|
+
if (value === void 0 || value === null) continue;
|
|
1233
|
+
body[upstreamField] = value;
|
|
1234
|
+
}
|
|
1235
|
+
return body;
|
|
1236
|
+
}
|
|
1237
|
+
function voiceOpPathParams(op, fields, query) {
|
|
1238
|
+
const out = {};
|
|
1239
|
+
for (const name of op.pathParams ?? []) {
|
|
1240
|
+
const fromBody = fields[name];
|
|
1241
|
+
const value = typeof fromBody === "string" && fromBody.length > 0 ? fromBody : query.get(name);
|
|
1242
|
+
if (value) out[name] = value;
|
|
1243
|
+
}
|
|
1244
|
+
return out;
|
|
1245
|
+
}
|
|
1246
|
+
function checkVoiceOpModel(op, fields) {
|
|
1247
|
+
const requested = fields[MODEL_FIELD];
|
|
1248
|
+
if (requested === void 0 || requested === null || requested === "") return { ok: true };
|
|
1249
|
+
if (typeof requested !== "string") {
|
|
1250
|
+
return { error: `\`${MODEL_FIELD}\` must be a string` };
|
|
1251
|
+
}
|
|
1252
|
+
if (op.models && !op.models.includes(requested)) {
|
|
1253
|
+
return {
|
|
1254
|
+
error: "unknown_model",
|
|
1255
|
+
detail: `Unknown model '${requested}' for ${op.id}`,
|
|
1256
|
+
allowed: op.models
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
return { ok: true };
|
|
1260
|
+
}
|
|
1261
|
+
|
|
720
1262
|
// src/twilio.ts
|
|
721
1263
|
var DEFAULT_TWILIO_BASE_URL = "https://api.twilio.com";
|
|
722
1264
|
var TwilioApiError = class extends Error {
|
|
@@ -1377,6 +1919,158 @@ function isVoiceMockEnv() {
|
|
|
1377
1919
|
const v = value.toLowerCase();
|
|
1378
1920
|
return v === "mock" || v === "demo" || v === "fake";
|
|
1379
1921
|
}
|
|
1922
|
+
|
|
1923
|
+
// src/live/client.ts
|
|
1924
|
+
function encodeAudio(audio) {
|
|
1925
|
+
if (typeof btoa === "function") {
|
|
1926
|
+
let binary = "";
|
|
1927
|
+
for (let i = 0; i < audio.length; i += 32768) {
|
|
1928
|
+
binary += String.fromCharCode(...audio.subarray(i, i + 32768));
|
|
1929
|
+
}
|
|
1930
|
+
return btoa(binary);
|
|
1931
|
+
}
|
|
1932
|
+
const BufferImpl = globalThis.Buffer;
|
|
1933
|
+
if (BufferImpl) return BufferImpl.from(audio).toString("base64");
|
|
1934
|
+
throw new Error("Base64 encoding is unavailable in this runtime");
|
|
1935
|
+
}
|
|
1936
|
+
function normalizeBaseUrl(value) {
|
|
1937
|
+
return value.replace(/\/+$/, "");
|
|
1938
|
+
}
|
|
1939
|
+
var LiveSession = class {
|
|
1940
|
+
constructor(socket, initialSession) {
|
|
1941
|
+
this.socket = socket;
|
|
1942
|
+
this.initialSession = initialSession;
|
|
1943
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
1944
|
+
this.closed = false;
|
|
1945
|
+
this.latestUsageSeconds = 0;
|
|
1946
|
+
this.startedPromise = new Promise((resolve, reject) => {
|
|
1947
|
+
this.resolveStarted = resolve;
|
|
1948
|
+
this.rejectStarted = reject;
|
|
1949
|
+
});
|
|
1950
|
+
this.started = this.startedPromise;
|
|
1951
|
+
this.closedPromise = new Promise((resolve) => {
|
|
1952
|
+
this.resolveClosed = resolve;
|
|
1953
|
+
});
|
|
1954
|
+
socket.addEventListener("message", (message) => {
|
|
1955
|
+
const raw = typeof message.data === "string" ? message.data : "";
|
|
1956
|
+
if (!raw) return;
|
|
1957
|
+
let event;
|
|
1958
|
+
try {
|
|
1959
|
+
event = JSON.parse(raw);
|
|
1960
|
+
} catch {
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
if (event.type === "session.started") this.resolveStarted(event);
|
|
1964
|
+
if (event.type === "session.usage.updated") {
|
|
1965
|
+
const seconds = event.usage?.seconds;
|
|
1966
|
+
if (typeof seconds === "number") this.latestUsageSeconds = seconds;
|
|
1967
|
+
}
|
|
1968
|
+
if (event.type === "session.closed") {
|
|
1969
|
+
this.closed = true;
|
|
1970
|
+
this.resolveClosed(event);
|
|
1971
|
+
}
|
|
1972
|
+
if (event.type === "error" && !this.closed) {
|
|
1973
|
+
const messageText = String(event.error?.message || "Live session error");
|
|
1974
|
+
this.rejectStarted(new Error(messageText));
|
|
1975
|
+
}
|
|
1976
|
+
for (const handler of this.handlers.get(String(event.type)) ?? []) handler(event);
|
|
1977
|
+
for (const handler of this.handlers.get("*") ?? []) handler(event);
|
|
1978
|
+
});
|
|
1979
|
+
socket.addEventListener("close", () => {
|
|
1980
|
+
if (!this.closed) this.resolveClosed();
|
|
1981
|
+
});
|
|
1982
|
+
socket.addEventListener("error", () => {
|
|
1983
|
+
if (!this.closed) this.rejectStarted(new Error("Live session WebSocket error"));
|
|
1984
|
+
});
|
|
1985
|
+
socket.addEventListener("open", () => {
|
|
1986
|
+
if (initialSession) this.send({ type: "session.start", session: initialSession });
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
on(eventType, handler) {
|
|
1990
|
+
const handlers = this.handlers.get(eventType) ?? /* @__PURE__ */ new Set();
|
|
1991
|
+
handlers.add(handler);
|
|
1992
|
+
this.handlers.set(eventType, handlers);
|
|
1993
|
+
return () => handlers.delete(handler);
|
|
1994
|
+
}
|
|
1995
|
+
send(event) {
|
|
1996
|
+
if (this.closed) throw new Error("Live session is closed");
|
|
1997
|
+
this.socket.send(JSON.stringify(event));
|
|
1998
|
+
}
|
|
1999
|
+
appendAudio(audio) {
|
|
2000
|
+
this.send({
|
|
2001
|
+
type: "session.input_audio.append",
|
|
2002
|
+
audio: typeof audio === "string" ? audio : encodeAudio(audio)
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
async close(timeoutMs = 15e3) {
|
|
2006
|
+
if (this.closed) return;
|
|
2007
|
+
if (this.socket.readyState === 1) this.send({ type: "session.close" });
|
|
2008
|
+
const timeout = new Promise((resolve) => setTimeout(resolve, timeoutMs));
|
|
2009
|
+
const result = await Promise.race([this.closedPromise, timeout]);
|
|
2010
|
+
if (!this.closed) this.socket.close();
|
|
2011
|
+
return result;
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
var ShogoLiveClient = class {
|
|
2015
|
+
constructor(options) {
|
|
2016
|
+
if (!!options.apiKey === !!options.runtimeToken) {
|
|
2017
|
+
throw new Error("ShogoLiveClient requires exactly one of apiKey or runtimeToken.");
|
|
2018
|
+
}
|
|
2019
|
+
this.apiUrl = normalizeBaseUrl(options.apiUrl);
|
|
2020
|
+
this.token = options.apiKey ?? options.runtimeToken;
|
|
2021
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
2022
|
+
this.WebSocketImpl = options.WebSocket ?? globalThis.WebSocket;
|
|
2023
|
+
if (typeof this.fetchImpl !== "function") throw new Error("fetch is unavailable");
|
|
2024
|
+
if (typeof this.WebSocketImpl !== "function") throw new Error("WebSocket is unavailable");
|
|
2025
|
+
}
|
|
2026
|
+
httpUrl(path) {
|
|
2027
|
+
return `${this.apiUrl}${path}`;
|
|
2028
|
+
}
|
|
2029
|
+
wsUrl(path) {
|
|
2030
|
+
return this.httpUrl(path).replace(/^http/, "ws");
|
|
2031
|
+
}
|
|
2032
|
+
protocols() {
|
|
2033
|
+
return ["shogo-live", `shogo-insecure-api-key.${this.token}`];
|
|
2034
|
+
}
|
|
2035
|
+
connect(session) {
|
|
2036
|
+
const socket = new this.WebSocketImpl(this.wsUrl("/api/ai/v1/live/sessions"), this.protocols());
|
|
2037
|
+
return new LiveSession(socket, session);
|
|
2038
|
+
}
|
|
2039
|
+
attach(sessionId) {
|
|
2040
|
+
const socket = new this.WebSocketImpl(
|
|
2041
|
+
this.wsUrl(`/api/ai/v1/live/sessions/${encodeURIComponent(sessionId)}/attach`),
|
|
2042
|
+
this.protocols()
|
|
2043
|
+
);
|
|
2044
|
+
return new LiveSession(socket);
|
|
2045
|
+
}
|
|
2046
|
+
async createWebRtcSession(input) {
|
|
2047
|
+
const response = await this.fetchImpl(this.httpUrl("/api/ai/v1/live/sessions"), {
|
|
2048
|
+
method: "POST",
|
|
2049
|
+
headers: {
|
|
2050
|
+
Authorization: `Bearer ${this.token}`,
|
|
2051
|
+
"Content-Type": "application/json"
|
|
2052
|
+
},
|
|
2053
|
+
body: JSON.stringify({
|
|
2054
|
+
session: input.session,
|
|
2055
|
+
transport: { type: "webrtc", sdp: input.sdp }
|
|
2056
|
+
})
|
|
2057
|
+
});
|
|
2058
|
+
const body = await response.json();
|
|
2059
|
+
if (!response.ok) {
|
|
2060
|
+
throw new Error(body?.error?.message || `Live session creation failed (${response.status})`);
|
|
2061
|
+
}
|
|
2062
|
+
return body;
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
|
|
2066
|
+
// src/runtime-token.ts
|
|
2067
|
+
var WORKSPACE_RUNTIME_TOKEN_MESSAGE = "RUNTIME_AUTH_SECRET is the workspace runtime token; app code needs its project token (rt_v1_\u2026). Start the app through the preview (server_sync / restart), not from a workspace shell.";
|
|
2068
|
+
function readRuntimeAuthSecret(env = typeof process === "undefined" ? void 0 : process.env) {
|
|
2069
|
+
const token = env?.RUNTIME_AUTH_SECRET;
|
|
2070
|
+
if (!token) return void 0;
|
|
2071
|
+
if (token.startsWith("wrt_v1_")) throw new Error(WORKSPACE_RUNTIME_TOKEN_MESSAGE);
|
|
2072
|
+
return token;
|
|
2073
|
+
}
|
|
1380
2074
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1381
2075
|
0 && (module.exports = {
|
|
1382
2076
|
AUDIO_TAGS,
|
|
@@ -1387,6 +2081,10 @@ function isVoiceMockEnv() {
|
|
|
1387
2081
|
DEFAULT_ELEVENLABS_BASE_URL,
|
|
1388
2082
|
DEFAULT_MEMORY_BLOCK,
|
|
1389
2083
|
DEFAULT_MUSIC_MODEL,
|
|
2084
|
+
DEFAULT_SOUND_GENERATION_MODEL,
|
|
2085
|
+
DEFAULT_SPEECH_TO_TEXT_MODEL,
|
|
2086
|
+
DEFAULT_STS_MODEL,
|
|
2087
|
+
DEFAULT_TTS_MODEL,
|
|
1390
2088
|
DEFAULT_TWILIO_BASE_URL,
|
|
1391
2089
|
DEFAULT_VOICE_SETTINGS,
|
|
1392
2090
|
DirectTelephonyClient,
|
|
@@ -1397,27 +2095,52 @@ function isVoiceMockEnv() {
|
|
|
1397
2095
|
ElevenLabsClient,
|
|
1398
2096
|
HostedRuntimeTokenClient,
|
|
1399
2097
|
HostedTelephonyClient,
|
|
2098
|
+
LiveSession,
|
|
1400
2099
|
MEMORY_CLIENT_TOOLS,
|
|
1401
2100
|
MockTelephonyClient,
|
|
2101
|
+
ShogoLiveClient,
|
|
2102
|
+
TTS_FALLBACK_MODEL,
|
|
1402
2103
|
TelephonyApiError,
|
|
1403
2104
|
TelephonyConfigError,
|
|
1404
2105
|
TwilioApiError,
|
|
1405
2106
|
TwilioClient,
|
|
2107
|
+
VOICE_OPS,
|
|
2108
|
+
VOICE_OP_IDS,
|
|
2109
|
+
VOICE_SOUND_GENERATION_MODEL_IDS,
|
|
2110
|
+
VOICE_SPEECH_TO_TEXT_MODEL_IDS,
|
|
2111
|
+
VOICE_STS_MODEL_IDS,
|
|
2112
|
+
VOICE_TTS_MODELS,
|
|
2113
|
+
VOICE_TTS_MODEL_IDS,
|
|
2114
|
+
WORKSPACE_RUNTIME_TOKEN_MESSAGE,
|
|
1406
2115
|
buildPreviewLine,
|
|
1407
2116
|
composeAgentPrompt,
|
|
1408
2117
|
composeExpressivityBlock,
|
|
1409
2118
|
createTelephonyClient,
|
|
1410
2119
|
extractBasePrompt,
|
|
2120
|
+
getVoiceOp,
|
|
2121
|
+
getVoiceTtsModel,
|
|
2122
|
+
isModelRelatedTtsFailure,
|
|
1411
2123
|
isVoiceMockEnv,
|
|
1412
2124
|
normalizeAudioTags,
|
|
1413
2125
|
normalizeExpressivity,
|
|
1414
2126
|
normalizeVoiceSettings,
|
|
2127
|
+
parseVoiceOpForm,
|
|
2128
|
+
parseVoiceOpJsonBody,
|
|
1415
2129
|
readAudioTags,
|
|
1416
2130
|
readExpressivity,
|
|
2131
|
+
readRuntimeAuthSecret,
|
|
1417
2132
|
readVoiceSettings,
|
|
1418
2133
|
resolveConvaiTtsModel,
|
|
2134
|
+
resolveConvaiTtsModelDetailed,
|
|
1419
2135
|
stripAudioTags,
|
|
1420
2136
|
stripExpressivityBlock,
|
|
1421
|
-
stripMemoryBlock
|
|
2137
|
+
stripMemoryBlock,
|
|
2138
|
+
voiceOpCredentialError,
|
|
2139
|
+
voiceOpForwardPath,
|
|
2140
|
+
voiceOpIsMultipart,
|
|
2141
|
+
voiceOpPathParams,
|
|
2142
|
+
voiceOpReturnsAudio,
|
|
2143
|
+
voiceOpUpstreamBody,
|
|
2144
|
+
voiceOpUpstreamPath
|
|
1422
2145
|
});
|
|
1423
2146
|
//# sourceMappingURL=index.cjs.map
|