@elizaos/plugin-elevenlabs 2.0.3-beta.5 → 2.0.3-beta.7
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/browser/index.browser.js +4 -0
- package/dist/browser/index.browser.js.map +11 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.node.cjs +521 -0
- package/dist/cjs/index.node.cjs.map +11 -0
- package/dist/index.browser.d.ts +3 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.node.d.ts +3 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.node.js +487 -0
- package/dist/node/index.node.js.map +11 -0
- package/package.json +3 -3
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{ElevenLabsClient as v}from"@elevenlabs/elevenlabs-js";import{SpeechToTextConvertRequestModelId as h,SpeechToTextConvertRequestTimestampsGranularity as L,TextToSpeechStreamRequestOutputFormat as _}from"@elevenlabs/elevenlabs-js/api";import{logger as o,ModelType as m,parseBooleanFromText as p}from"@elizaos/core";function R(t){for(let e of Object.values(_))if(e===t)return e;throw Error(`Unsupported ElevenLabs TTS output format: ${t}`)}function B(t){for(let e of Object.values(h))if(e===t)return e;throw Error(`Unsupported ElevenLabs STT model: ${t}`)}function N(t){for(let e of Object.values(L))if(e===t)return e;throw Error(`Unsupported ElevenLabs STT timestamps granularity: ${t}`)}function k(t){if("transcripts"in t)return t.transcripts.map((e)=>e.text).join(`
|
|
2
|
+
`);return t.text}function b(t){return typeof t==="object"&&t!==null&&!Array.isArray(t)}function d(t){return typeof t==="string"?t.trim():""}function U(t){let e=typeof t==="string"?{text:t}:t;if(!b(e)||!d(e.text))throw Error("ElevenLabs TTS text is required");if(e.model!==void 0&&!d(e.model))throw Error("ElevenLabs TTS model must be a non-empty string");if(e.voiceId!==void 0&&!d(e.voiceId))throw Error("ElevenLabs TTS voiceId must be a non-empty string");if(e.format!==void 0&&!d(e.format))throw Error("ElevenLabs TTS format must be a non-empty string");return{...e,text:e.text.trim(),model:e.model===void 0?void 0:e.model.trim(),voiceId:e.voiceId===void 0?void 0:e.voiceId.trim(),format:e.format===void 0?void 0:e.format.trim()}}function T(t){let e=d(t);if(!e)throw Error("ElevenLabs transcription audioUrl is required");let r;try{r=new URL(e)}catch{throw Error("ElevenLabs transcription audioUrl must be a valid URL")}if(r.protocol!=="http:"&&r.protocol!=="https:")throw Error("ElevenLabs transcription audioUrl must use http or https");return r.toString()}function f(){return typeof globalThis.document<"u"}function s(t,e,r){let n=t.getSetting(e),i=n===null||n===void 0?void 0:String(n),a=typeof process<"u"&&process.env&&typeof process.env[e]==="string"?process.env[e]:void 0;return i??a??r}function x(t){let e=t.getSetting("ELEVENLABS_BROWSER_URL"),r=e===null||e===void 0?void 0:String(e);if(f()&&r)return r;return"https://api.elevenlabs.io/v1"}function E(t){let e=t.getSetting("ELEVENLABS_API_KEY"),r=e===null||e===void 0?void 0:String(e),n=typeof process<"u"&&process.env&&typeof process.env.ELEVENLABS_API_KEY==="string"?process.env.ELEVENLABS_API_KEY:void 0;return r??n}function I(t){let e=E(t)?.trim(),r=x(t);if(e)return{apiKey:e,baseUrl:r};if(f()&&r!=="https://api.elevenlabs.io/v1")return{baseUrl:r};throw Error("ELEVENLABS_API_KEY is required unless ELEVENLABS_BROWSER_URL is configured in browser mode")}function c(t){return{apiKey:E(t)||"",voiceId:s(t,"ELEVENLABS_VOICE_ID","EXAVITQu4vr4xnSDxMaL"),model:s(t,"ELEVENLABS_MODEL_ID","eleven_monolingual_v1"),stability:s(t,"ELEVENLABS_VOICE_STABILITY","0.5"),latency:s(t,"ELEVENLABS_OPTIMIZE_STREAMING_LATENCY","0"),outputFormat:s(t,"ELEVENLABS_OUTPUT_FORMAT","mp3_44100_128"),similarity:s(t,"ELEVENLABS_VOICE_SIMILARITY_BOOST","0.75"),style:s(t,"ELEVENLABS_VOICE_STYLE","0"),speakerBoost:p(`${s(t,"ELEVENLABS_VOICE_USE_SPEAKER_BOOST","true")??"true"}`)}}function g(t){let e=s(t,"ELEVENLABS_STT_LANGUAGE_CODE"),r=s(t,"ELEVENLABS_STT_NUM_SPEAKERS"),n=r?Number(r):void 0;if(n!==void 0&&(!Number.isInteger(n)||n<1||n>32))throw Error("ELEVENLABS_STT_NUM_SPEAKERS must be an integer between 1 and 32");return{apiKey:E(t)||"",modelId:s(t,"ELEVENLABS_STT_MODEL_ID","scribe_v1"),languageCode:e||void 0,timestampsGranularity:s(t,"ELEVENLABS_STT_TIMESTAMPS_GRANULARITY","word"),diarize:p(`${s(t,"ELEVENLABS_STT_DIARIZE","false")??"false"}`),numSpeakers:n,tagAudioEvents:p(`${s(t,"ELEVENLABS_STT_TAG_AUDIO_EVENTS","false")??"false"}`)}}function P(t){return typeof Buffer<"u"&&typeof Buffer.isBuffer==="function"&&Buffer.isBuffer(t)}async function S(t){if(f()){if(typeof t.blob==="function")return t.blob();let r=await t.arrayBuffer();return new Blob([r])}let e=await t.arrayBuffer();if(typeof Buffer<"u")return Buffer.from(e);return new Blob([e])}async function V(t){let e=t.getReader(),r=[],n=0;while(!0){let{done:u,value:l}=await e.read();if(u)break;if(!l)continue;r.push(l),n+=l.byteLength}let i=new Uint8Array(n),a=0;for(let u of r)i.set(u,a),a+=u.byteLength;return i}async function w(t,e){try{let n=await new v(I(t)).textToSpeech.stream(e.voiceId,{text:e.text,modelId:e.modelId,outputFormat:R(e.outputFormat),optimizeStreamingLatency:Number(e.latency)||0,voiceSettings:{stability:Number(e.stability)||0,similarityBoost:Number(e.similarity)||0,style:Number(e.style)||0,useSpeakerBoost:!!e.speakerBoost}});if(!n)throw Error("Empty response body from ElevenLabs SDK");return V(n)}catch(r){let n=r instanceof Error?r.message:String(r);throw o.error(`ElevenLabs fetchSpeech error: ${n}`),r instanceof Error?r:Error(n)}}async function C(t,e){try{let r=new v(I(t)),n={modelId:B(e.modelId),file:e.audioFile};if(e.languageCode)n.languageCode=e.languageCode;if(n.timestampsGranularity=N(e.timestampsGranularity),e.diarize){if(n.diarize=!0,e.numSpeakers!==void 0)n.numSpeakers=e.numSpeakers}if(e.tagAudioEvents)n.tagAudioEvents=!0;let i=await r.speechToText.convert(n);if(!i)throw Error("Empty response from ElevenLabs STT API");return k(i)}catch(r){let n=r instanceof Error?r.message:String(r);throw o.error(`ElevenLabs fetchTranscription error: ${n}`),r instanceof Error?r:Error(n)}}var O={name:"elevenLabs",description:"High-quality text-to-speech synthesis and speech-to-text transcription using ElevenLabs API with support for multiple voices, languages, and speaker diarization",models:{[m.TEXT_TO_SPEECH]:async(t,e)=>{let r=U(e),n=c(t),i=r.model||n.model,a=r.voiceId??n.voiceId,u=r.format?r.format==="mp3"?"mp3_44100_128":r.format:n.outputFormat;o.log(`[ElevenLabs] Using TEXT_TO_SPEECH model: ${i}`);try{return await w(t,{text:r.text,voiceId:a,modelId:i,outputFormat:u,stability:n.stability,similarity:n.similarity,style:n.style,speakerBoost:n.speakerBoost,latency:n.latency})}catch(l){let y=l instanceof Error?l.message:String(l);throw o.error(`ElevenLabs model error: ${y}`),l instanceof Error?l:Error(y)}},[m.TRANSCRIPTION]:async(t,e)=>{let r=g(t);o.log(`[ElevenLabs] Using TRANSCRIPTION model: ${r.modelId}`);try{let n;if(typeof e==="string"){let a=T(e),u=await fetch(a);if(!u.ok)throw Error(`Failed to fetch audio from URL: ${a}`);n=await S(u)}else if(P(e))n=e;else if(b(e)&&"audioUrl"in e){let a=T(e.audioUrl),u=await fetch(a);if(!u.ok)throw Error(`Failed to fetch audio from URL: ${a}`);n=await S(u)}else throw Error("Invalid input type for TRANSCRIPTION model");return await C(t,{audioFile:n,modelId:r.modelId,languageCode:r.languageCode,timestampsGranularity:r.timestampsGranularity,diarize:r.diarize,numSpeakers:r.numSpeakers,tagAudioEvents:r.tagAudioEvents})}catch(n){let i=n instanceof Error?n.message:String(n);throw o.error(`ElevenLabs transcription error: ${i}`),n instanceof Error?n:Error(i)}}},tests:[{name:"test eleven labs",tests:[{name:"Eleven Labs API key validation",fn:async(t)=>{if(!c(t).apiKey)throw Error("Missing API key: Please provide a valid Eleven Labs API key.")}},{name:"Voice settings validation",fn:async(t)=>{let e=c(t);if(!e.voiceId)throw Error("Missing voice ID configuration");let r=Number.parseFloat(e.stability);if(Number.isNaN(r)||r<0||r>1)throw Error("Voice stability must be between 0 and 1");let n=Number.parseFloat(e.similarity);if(Number.isNaN(n)||n<0||n>1)throw Error("Voice similarity boost must be between 0 and 1");o.success("Voice settings validated successfully")}},{name:"Eleven Labs API connectivity",fn:async(t)=>{let e=c(t);if(!e.apiKey){o.warn("Skipping API connectivity test - no API key provided");return}try{await w(t,{text:"test",voiceId:e.voiceId,modelId:e.model,outputFormat:e.outputFormat,stability:e.stability,similarity:e.similarity,style:e.style,speakerBoost:e.speakerBoost,latency:e.latency}),o.success("API connectivity test passed")}catch(r){let n=r instanceof Error?r.message:String(r);if(n.includes("QUOTA_EXCEEDED")){o.warn("API quota exceeded - test skipped");return}throw o.error(`API connectivity test failed: ${n}`),Error(`API connectivity test failed: ${n}`)}}},{name:"ElevenLabs TTS Generation (stream exists)",fn:async(t)=>{if(!c(t).apiKey&&!f()){o.warn("Skipping TTS generation test - no API key provided");return}let r="Hello from ElevenLabs test.";try{let n=await t.useModel(m.TEXT_TO_SPEECH,r),i=n instanceof Uint8Array?n:Buffer.isBuffer(n)?new Uint8Array(n):n instanceof ArrayBuffer?new Uint8Array(n):null;if(!i||i.byteLength===0)throw Error("TTS output must be non-empty Uint8Array, Buffer, or ArrayBuffer");o.success("Received TTS binary payload successfully")}catch(n){let i=n instanceof Error?n.message:String(n);if(i.includes("QUOTA_EXCEEDED")){o.warn("[ElevenLabs Test] API quota exceeded - test skipped");return}throw o.error("[ElevenLabs Test] TTS Generation test failed:",i),Error(`TTS Generation test failed: ${i}`)}}},{name:"Output format handling",fn:async(t)=>{let e=c(t),r=["mp3_44100_128","pcm_16000","pcm_22050","pcm_24000","pcm_44100"];for(let n of r)if(n.startsWith("pcm_")){let i=Number.parseInt(n.slice(4),10);if(Number.isNaN(i)||i<=0)throw Error(`Invalid PCM format: ${n}`)}o.success(`Output format validated: ${e.outputFormat}`)}}]},{name:"test eleven labs STT",tests:[{name:"STT settings validation",fn:async(t)=>{let e=g(t);if(!e.modelId)throw Error("Missing STT model ID configuration");if(!["none","word","character"].includes(e.timestampsGranularity))throw Error(`Invalid timestamps granularity: ${e.timestampsGranularity}`);if(e.numSpeakers!==void 0&&(e.numSpeakers<1||e.numSpeakers>32))throw Error("Number of speakers must be between 1 and 32");o.success("STT settings validated successfully")}},{name:"STT configuration defaults",fn:async(t)=>{let e=g(t);if(e.modelId!=="scribe_v1")o.warn(`Using non-default STT model: ${e.modelId}`);if(e.timestampsGranularity!=="word")o.warn(`Using non-default timestamps granularity: ${e.timestampsGranularity}`);o.success("STT configuration defaults checked")}},{name:"STT input handling validation",fn:async(t)=>{let e=[{type:"string URL",valid:!0},{type:"Buffer",valid:!0},{type:"object with audioUrl",valid:!0}];for(let r of e)if(!r.valid)throw Error(`Invalid test case should not be valid: ${r.type}`);o.success("STT input handling validation passed")}}]}]},A=O;var z=A;export{O as elevenLabsPlugin,z as default};
|
|
3
|
+
|
|
4
|
+
//# debugId=94B05DF3D97AE45B64756E2164756E21
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts", "../../src/index.browser.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { ElevenLabsClient } from \"@elevenlabs/elevenlabs-js\";\nimport type {\n BodySpeechToTextV1SpeechToTextPost,\n MultichannelSpeechToTextResponseModel,\n SpeechToTextChunkResponseModel,\n SpeechToTextConvertRequestModelId,\n SpeechToTextConvertRequestTimestampsGranularity,\n TextToSpeechStreamRequestOutputFormat,\n} from \"@elevenlabs/elevenlabs-js/api\";\nimport {\n SpeechToTextConvertRequestModelId as SttModelIdEnum,\n SpeechToTextConvertRequestTimestampsGranularity as SttTimestampsGranularityEnum,\n TextToSpeechStreamRequestOutputFormat as TtsOutputFormatEnum,\n} from \"@elevenlabs/elevenlabs-js/api\";\nimport {\n type IAgentRuntime,\n logger,\n ModelType,\n type Plugin,\n parseBooleanFromText,\n} from \"@elizaos/core\";\n\nfunction parseTtsOutputFormat(\n format: string,\n): TextToSpeechStreamRequestOutputFormat {\n for (const allowed of Object.values(TtsOutputFormatEnum)) {\n if (allowed === format) return allowed;\n }\n throw new Error(`Unsupported ElevenLabs TTS output format: ${format}`);\n}\n\nfunction parseSttModelId(id: string): SpeechToTextConvertRequestModelId {\n for (const allowed of Object.values(SttModelIdEnum)) {\n if (allowed === id) return allowed;\n }\n throw new Error(`Unsupported ElevenLabs STT model: ${id}`);\n}\n\nfunction parseSttTimestampsGranularity(\n value: string,\n): SpeechToTextConvertRequestTimestampsGranularity {\n for (const allowed of Object.values(SttTimestampsGranularityEnum)) {\n if (allowed === value) return allowed;\n }\n throw new Error(\n `Unsupported ElevenLabs STT timestamps granularity: ${value}`,\n );\n}\n\nfunction extractTranscript(\n response:\n | SpeechToTextChunkResponseModel\n | MultichannelSpeechToTextResponseModel,\n): string {\n if (\"transcripts\" in response) {\n return response.transcripts.map((t) => t.text).join(\"\\n\");\n }\n return response.text;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction nonEmptyString(value: unknown): string {\n return typeof value === \"string\" ? value.trim() : \"\";\n}\n\nfunction normalizeTtsInput(\n input:\n | string\n | {\n text: string;\n model?: string;\n voiceId?: string;\n format?: string;\n instructions?: string;\n },\n): {\n text: string;\n model?: string;\n voiceId?: string;\n format?: string;\n instructions?: string;\n} {\n const options = typeof input === \"string\" ? { text: input } : input;\n if (!isRecord(options) || !nonEmptyString(options.text)) {\n throw new Error(\"ElevenLabs TTS text is required\");\n }\n if (options.model !== undefined && !nonEmptyString(options.model)) {\n throw new Error(\"ElevenLabs TTS model must be a non-empty string\");\n }\n if (options.voiceId !== undefined && !nonEmptyString(options.voiceId)) {\n throw new Error(\"ElevenLabs TTS voiceId must be a non-empty string\");\n }\n if (options.format !== undefined && !nonEmptyString(options.format)) {\n throw new Error(\"ElevenLabs TTS format must be a non-empty string\");\n }\n return {\n ...options,\n text: options.text.trim(),\n model: options.model === undefined ? undefined : options.model.trim(),\n voiceId: options.voiceId === undefined ? undefined : options.voiceId.trim(),\n format: options.format === undefined ? undefined : options.format.trim(),\n };\n}\n\nfunction validateAudioUrl(value: unknown): string {\n const url = nonEmptyString(value);\n if (!url) {\n throw new Error(\"ElevenLabs transcription audioUrl is required\");\n }\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new Error(\"ElevenLabs transcription audioUrl must be a valid URL\");\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(\"ElevenLabs transcription audioUrl must use http or https\");\n }\n return parsed.toString();\n}\n\n/**\n * Voice settings configuration for ElevenLabs API\n */\ninterface VoiceSettings {\n apiKey: string;\n voiceId: string;\n model: string;\n stability: string;\n latency: string;\n outputFormat: string;\n similarity: string;\n style: string;\n speakerBoost: boolean;\n}\n\ninterface TranscriptionSettings {\n apiKey: string;\n modelId: string;\n languageCode?: string;\n timestampsGranularity: string;\n diarize: boolean;\n numSpeakers?: number;\n tagAudioEvents: boolean;\n}\n\nfunction isBrowser(): boolean {\n return typeof globalThis.document !== \"undefined\";\n}\n\nfunction getSetting(runtime: IAgentRuntime, key: string): string | undefined;\nfunction getSetting(\n runtime: IAgentRuntime,\n key: string,\n fallback: string,\n): string;\nfunction getSetting(\n runtime: IAgentRuntime,\n key: string,\n fallback?: string,\n): string | undefined {\n const rawRuntime = runtime.getSetting(key);\n const fromRuntime =\n rawRuntime === null || rawRuntime === undefined\n ? undefined\n : String(rawRuntime);\n\n const envValue =\n typeof process !== \"undefined\" &&\n process.env &&\n typeof process.env[key] === \"string\"\n ? process.env[key]\n : undefined;\n\n return fromRuntime ?? envValue ?? fallback;\n}\n\nfunction getBaseURL(runtime: IAgentRuntime): string {\n const browserRaw = runtime.getSetting(\"ELEVENLABS_BROWSER_URL\");\n const browserURL =\n browserRaw === null || browserRaw === undefined\n ? undefined\n : String(browserRaw);\n if (isBrowser() && browserURL) return browserURL;\n return \"https://api.elevenlabs.io/v1\";\n}\n\nfunction getApiKey(runtime: IAgentRuntime): string | undefined {\n const raw = runtime.getSetting(\"ELEVENLABS_API_KEY\");\n const fromRuntime =\n raw === null || raw === undefined ? undefined : String(raw);\n const fromEnv =\n typeof process !== \"undefined\" &&\n process.env &&\n typeof process.env.ELEVENLABS_API_KEY === \"string\"\n ? process.env.ELEVENLABS_API_KEY\n : undefined;\n return fromRuntime ?? fromEnv;\n}\n\nfunction getElevenLabsClientConfig(runtime: IAgentRuntime): {\n apiKey?: string;\n baseUrl: string;\n} {\n const apiKey = getApiKey(runtime)?.trim();\n const baseUrl = getBaseURL(runtime);\n if (apiKey) return { apiKey, baseUrl };\n if (isBrowser() && baseUrl !== \"https://api.elevenlabs.io/v1\") {\n return { baseUrl };\n }\n throw new Error(\n \"ELEVENLABS_API_KEY is required unless ELEVENLABS_BROWSER_URL is configured in browser mode\",\n );\n}\n\n/**\n * Function to retrieve voice settings based on runtime and environment variables.\n * @param {IAgentRuntime} runtime - The agent runtime object.\n * @returns {VoiceSettings} - Object containing various voice settings.\n */\nfunction getVoiceSettings(runtime: IAgentRuntime): VoiceSettings {\n return {\n apiKey: getApiKey(runtime) || \"\",\n voiceId: getSetting(runtime, \"ELEVENLABS_VOICE_ID\", \"EXAVITQu4vr4xnSDxMaL\"),\n model: getSetting(runtime, \"ELEVENLABS_MODEL_ID\", \"eleven_monolingual_v1\"),\n stability: getSetting(runtime, \"ELEVENLABS_VOICE_STABILITY\", \"0.5\"),\n latency: getSetting(runtime, \"ELEVENLABS_OPTIMIZE_STREAMING_LATENCY\", \"0\"),\n // Use mp3 by default to be browser-safe and align with OpenAI plugin behavior\n outputFormat: getSetting(\n runtime,\n \"ELEVENLABS_OUTPUT_FORMAT\",\n \"mp3_44100_128\",\n ),\n similarity: getSetting(\n runtime,\n \"ELEVENLABS_VOICE_SIMILARITY_BOOST\",\n \"0.75\",\n ),\n style: getSetting(runtime, \"ELEVENLABS_VOICE_STYLE\", \"0\"),\n speakerBoost: parseBooleanFromText(\n `${getSetting(runtime, \"ELEVENLABS_VOICE_USE_SPEAKER_BOOST\", \"true\") ?? \"true\"}`,\n ),\n };\n}\n\nfunction getTranscriptionSettings(\n runtime: IAgentRuntime,\n): TranscriptionSettings {\n const languageCode = getSetting(runtime, \"ELEVENLABS_STT_LANGUAGE_CODE\");\n const numSpeakersStr = getSetting(runtime, \"ELEVENLABS_STT_NUM_SPEAKERS\");\n const numSpeakers = numSpeakersStr ? Number(numSpeakersStr) : undefined;\n if (\n numSpeakers !== undefined &&\n (!Number.isInteger(numSpeakers) || numSpeakers < 1 || numSpeakers > 32)\n ) {\n throw new Error(\n \"ELEVENLABS_STT_NUM_SPEAKERS must be an integer between 1 and 32\",\n );\n }\n\n return {\n apiKey: getApiKey(runtime) || \"\",\n modelId: getSetting(runtime, \"ELEVENLABS_STT_MODEL_ID\", \"scribe_v1\"),\n languageCode: languageCode || undefined,\n timestampsGranularity: getSetting(\n runtime,\n \"ELEVENLABS_STT_TIMESTAMPS_GRANULARITY\",\n \"word\",\n ),\n diarize: parseBooleanFromText(\n `${getSetting(runtime, \"ELEVENLABS_STT_DIARIZE\", \"false\") ?? \"false\"}`,\n ),\n numSpeakers,\n tagAudioEvents: parseBooleanFromText(\n `${getSetting(runtime, \"ELEVENLABS_STT_TAG_AUDIO_EVENTS\", \"false\") ?? \"false\"}`,\n ),\n };\n}\n\nfunction isBufferInput(input: unknown): input is Buffer {\n return (\n typeof Buffer !== \"undefined\" &&\n typeof Buffer.isBuffer === \"function\" &&\n Buffer.isBuffer(input)\n );\n}\n\nasync function responseToAudioFile(response: Response): Promise<Buffer | Blob> {\n if (isBrowser()) {\n if (typeof response.blob === \"function\") {\n return response.blob();\n }\n const arrayBuffer = await response.arrayBuffer();\n return new Blob([arrayBuffer]);\n }\n const arrayBuffer = await response.arrayBuffer();\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(arrayBuffer);\n }\n return new Blob([arrayBuffer]);\n}\n\n/**\n * Fetch speech from ElevenLabs API using official SDK.\n * Returns an in-memory binary payload to satisfy the core TTS contract.\n * @param {IAgentRuntime} runtime - The runtime interface containing necessary data for the API call.\n * @param {string} text - The text to be converted into speech.\n * @returns {Promise<Uint8Array>}\n */\nasync function readStreamToUint8Array(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let totalLength = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n chunks.push(value);\n totalLength += value.byteLength;\n }\n\n const output = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n output.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return output;\n}\n\nasync function fetchSpeech(\n runtime: IAgentRuntime,\n params: {\n text: string;\n voiceId: string;\n modelId: string;\n outputFormat: string;\n stability: string;\n similarity: string;\n style: string;\n speakerBoost: boolean;\n latency: string;\n },\n): Promise<Uint8Array> {\n try {\n const client = new ElevenLabsClient(getElevenLabsClientConfig(runtime));\n\n const stream = await client.textToSpeech.stream(params.voiceId, {\n text: params.text,\n modelId: params.modelId,\n outputFormat: parseTtsOutputFormat(params.outputFormat),\n optimizeStreamingLatency: Number(params.latency) || 0,\n voiceSettings: {\n stability: Number(params.stability) || 0,\n similarityBoost: Number(params.similarity) || 0,\n style: Number(params.style) || 0,\n useSpeakerBoost: !!params.speakerBoost,\n },\n });\n\n if (!stream) {\n throw new Error(\"Empty response body from ElevenLabs SDK\");\n }\n\n return readStreamToUint8Array(stream);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.error(`ElevenLabs fetchSpeech error: ${msg}`);\n throw error instanceof Error ? error : new Error(msg);\n }\n}\n\nasync function fetchTranscription(\n runtime: IAgentRuntime,\n params: {\n audioFile: File | Buffer | Blob;\n modelId: string;\n languageCode?: string;\n timestampsGranularity: string;\n diarize: boolean;\n numSpeakers?: number;\n tagAudioEvents: boolean;\n },\n): Promise<string> {\n try {\n const client = new ElevenLabsClient(getElevenLabsClientConfig(runtime));\n\n const body: BodySpeechToTextV1SpeechToTextPost = {\n modelId: parseSttModelId(params.modelId),\n file: params.audioFile,\n };\n\n if (params.languageCode) {\n body.languageCode = params.languageCode;\n }\n\n body.timestampsGranularity = parseSttTimestampsGranularity(\n params.timestampsGranularity,\n );\n\n if (params.diarize) {\n body.diarize = true;\n if (params.numSpeakers !== undefined) {\n body.numSpeakers = params.numSpeakers;\n }\n }\n\n if (params.tagAudioEvents) {\n body.tagAudioEvents = true;\n }\n\n const response = await client.speechToText.convert(body);\n\n if (!response) {\n throw new Error(\"Empty response from ElevenLabs STT API\");\n }\n\n return extractTranscript(response);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.error(`ElevenLabs fetchTranscription error: ${msg}`);\n throw error instanceof Error ? error : new Error(msg);\n }\n}\n\n// Note: WAV header utilities removed to ensure browser safety. Prefer mp3 output.\n\n/**\n * Represents the ElevenLabs plugin.\n * This plugin provides text-to-speech and speech-to-text functionality using the ElevenLabs API.\n *\n * Features:\n * - High-quality voice synthesis (TTS)\n * - High-accuracy speech transcription (STT) with Scribe v1 model\n * - Support for multiple voice models and settings\n * - Configurable voice parameters (stability, similarity, style)\n * - Stream-based audio output for efficient memory usage\n * - Speaker diarization (up to 32 speakers)\n * - Multi-language support (99 languages for STT)\n * - Audio event detection (laughter, applause, etc.)\n *\n * Required environment variables:\n * - ELEVENLABS_API_KEY: Your ElevenLabs API key\n *\n * Optional TTS environment variables:\n * - ELEVENLABS_VOICE_ID: Voice ID to use (default: EXAVITQu4vr4xnSDxMaL)\n * - ELEVENLABS_MODEL_ID: Model to use (default: eleven_monolingual_v1)\n * - ELEVENLABS_VOICE_STABILITY: Voice stability 0-1 (default: 0.5)\n * - ELEVENLABS_VOICE_SIMILARITY_BOOST: Voice similarity 0-1 (default: 0.75)\n * - ELEVENLABS_VOICE_STYLE: Voice style 0-1 (default: 0)\n * - ELEVENLABS_VOICE_USE_SPEAKER_BOOST: Enable speaker boost (default: true)\n * - ELEVENLABS_OPTIMIZE_STREAMING_LATENCY: Latency optimization 0-4 (default: 0)\n * - ELEVENLABS_OUTPUT_FORMAT: Output format (default: mp3_44100_128)\n *\n * Optional STT environment variables:\n * - ELEVENLABS_STT_MODEL_ID: STT model ID (default: scribe_v1)\n * - ELEVENLABS_STT_LANGUAGE_CODE: Language code for transcription (auto-detect if not set)\n * - ELEVENLABS_STT_TIMESTAMPS_GRANULARITY: Timestamp level (default: word)\n * - ELEVENLABS_STT_DIARIZE: Enable speaker diarization (default: false)\n * - ELEVENLABS_STT_NUM_SPEAKERS: Expected number of speakers (1-32)\n * - ELEVENLABS_STT_TAG_AUDIO_EVENTS: Tag audio events (default: false)\n *\n * @type {Plugin}\n */\nexport const elevenLabsPlugin: Plugin = {\n name: \"elevenLabs\",\n description:\n \"High-quality text-to-speech synthesis and speech-to-text transcription using ElevenLabs API with support for multiple voices, languages, and speaker diarization\",\n models: {\n [ModelType.TEXT_TO_SPEECH]: async (\n runtime: IAgentRuntime,\n input:\n | string\n | {\n text: string;\n model?: string;\n voiceId?: string;\n format?: string;\n instructions?: string;\n },\n ) => {\n const options = normalizeTtsInput(input);\n const settings = getVoiceSettings(runtime);\n const resolvedModel = options.model || settings.model;\n // Prefer explicit ElevenLabs voiceId param; fall back to configured voiceId.\n const resolvedVoiceId = options.voiceId ?? settings.voiceId;\n // Honor explicit caller-provided format (e.g., \"pcm_16000\", \"mp3_22050_64\").\n // Gracefully map generic \"mp3\" to a valid ElevenLabs enum, otherwise pass through.\n // Only default to settings.outputFormat when absent.\n const outputFormat = options.format\n ? options.format === \"mp3\"\n ? \"mp3_44100_128\"\n : (options.format as string)\n : settings.outputFormat;\n\n logger.log(`[ElevenLabs] Using TEXT_TO_SPEECH model: ${resolvedModel}`);\n try {\n const stream = await fetchSpeech(runtime, {\n text: options.text,\n voiceId: resolvedVoiceId,\n modelId: resolvedModel,\n outputFormat,\n stability: settings.stability,\n similarity: settings.similarity,\n style: settings.style,\n speakerBoost: settings.speakerBoost,\n latency: settings.latency,\n });\n return stream;\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.error(`ElevenLabs model error: ${msg}`);\n throw error instanceof Error ? error : new Error(msg);\n }\n },\n [ModelType.TRANSCRIPTION]: async (\n runtime: IAgentRuntime,\n input: string | Buffer | { audioUrl: string; prompt?: string },\n ) => {\n const settings = getTranscriptionSettings(runtime);\n\n logger.log(`[ElevenLabs] Using TRANSCRIPTION model: ${settings.modelId}`);\n\n try {\n let audioFile: Buffer | File | Blob;\n\n if (typeof input === \"string\") {\n const audioUrl = validateAudioUrl(input);\n const response = await fetch(audioUrl);\n if (!response.ok) {\n throw new Error(`Failed to fetch audio from URL: ${audioUrl}`);\n }\n audioFile = await responseToAudioFile(response);\n } else if (isBufferInput(input)) {\n audioFile = input;\n } else if (isRecord(input) && \"audioUrl\" in input) {\n const audioUrl = validateAudioUrl(input.audioUrl);\n const response = await fetch(audioUrl);\n if (!response.ok) {\n throw new Error(`Failed to fetch audio from URL: ${audioUrl}`);\n }\n audioFile = await responseToAudioFile(response);\n } else {\n throw new Error(\"Invalid input type for TRANSCRIPTION model\");\n }\n\n const transcript = await fetchTranscription(runtime, {\n audioFile,\n modelId: settings.modelId,\n languageCode: settings.languageCode,\n timestampsGranularity: settings.timestampsGranularity,\n diarize: settings.diarize,\n numSpeakers: settings.numSpeakers,\n tagAudioEvents: settings.tagAudioEvents,\n });\n\n return transcript;\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logger.error(`ElevenLabs transcription error: ${msg}`);\n throw error instanceof Error ? error : new Error(msg);\n }\n },\n },\n tests: [\n {\n name: \"test eleven labs\",\n tests: [\n {\n name: \"Eleven Labs API key validation\",\n fn: async (runtime: IAgentRuntime) => {\n const settings = getVoiceSettings(runtime);\n if (!settings.apiKey) {\n throw new Error(\n \"Missing API key: Please provide a valid Eleven Labs API key.\",\n );\n }\n },\n },\n {\n name: \"Voice settings validation\",\n fn: async (runtime: IAgentRuntime) => {\n const settings = getVoiceSettings(runtime);\n\n // Validate that all required settings are present\n if (!settings.voiceId) {\n throw new Error(\"Missing voice ID configuration\");\n }\n\n // Validate numeric settings\n const stability = Number.parseFloat(settings.stability);\n if (Number.isNaN(stability) || stability < 0 || stability > 1) {\n throw new Error(\"Voice stability must be between 0 and 1\");\n }\n\n const similarity = Number.parseFloat(settings.similarity);\n if (Number.isNaN(similarity) || similarity < 0 || similarity > 1) {\n throw new Error(\"Voice similarity boost must be between 0 and 1\");\n }\n\n logger.success(\"Voice settings validated successfully\");\n },\n },\n // WAV header generation test removed; we favor mp3 streaming for browser safety\n {\n name: \"Eleven Labs API connectivity\",\n fn: async (runtime: IAgentRuntime) => {\n const settings = getVoiceSettings(runtime);\n if (!settings.apiKey) {\n logger.warn(\n \"Skipping API connectivity test - no API key provided\",\n );\n return;\n }\n\n try {\n await fetchSpeech(runtime, {\n text: \"test\",\n voiceId: settings.voiceId,\n modelId: settings.model,\n outputFormat: settings.outputFormat,\n stability: settings.stability,\n similarity: settings.similarity,\n style: settings.style,\n speakerBoost: settings.speakerBoost,\n latency: settings.latency,\n });\n logger.success(\"API connectivity test passed\");\n } catch (error: unknown) {\n const msg =\n error instanceof Error ? error.message : String(error);\n if (msg.includes(\"QUOTA_EXCEEDED\")) {\n logger.warn(\"API quota exceeded - test skipped\");\n return;\n }\n logger.error(`API connectivity test failed: ${msg}`);\n throw new Error(`API connectivity test failed: ${msg}`);\n }\n },\n },\n {\n name: \"ElevenLabs TTS Generation (stream exists)\",\n fn: async (runtime: IAgentRuntime) => {\n const settings = getVoiceSettings(runtime);\n if (!settings.apiKey && !isBrowser()) {\n logger.warn(\"Skipping TTS generation test - no API key provided\");\n return;\n }\n\n const testText = \"Hello from ElevenLabs test.\";\n try {\n const audio = await runtime.useModel(\n ModelType.TEXT_TO_SPEECH,\n testText,\n );\n\n const bytes: Uint8Array | null =\n audio instanceof Uint8Array\n ? audio\n : Buffer.isBuffer(audio)\n ? new Uint8Array(audio)\n : audio instanceof ArrayBuffer\n ? new Uint8Array(audio)\n : null;\n\n if (!bytes || bytes.byteLength === 0) {\n throw new Error(\n \"TTS output must be non-empty Uint8Array, Buffer, or ArrayBuffer\",\n );\n }\n logger.success(\"Received TTS binary payload successfully\");\n } catch (error: unknown) {\n const msg =\n error instanceof Error ? error.message : String(error);\n if (msg.includes(\"QUOTA_EXCEEDED\")) {\n logger.warn(\n \"[ElevenLabs Test] API quota exceeded - test skipped\",\n );\n return;\n }\n logger.error(\n \"[ElevenLabs Test] TTS Generation test failed:\",\n msg,\n );\n throw new Error(`TTS Generation test failed: ${msg}`);\n }\n },\n },\n {\n name: \"Output format handling\",\n fn: async (runtime: IAgentRuntime) => {\n const settings = getVoiceSettings(runtime);\n\n // Test supported formats list includes common entries\n const pcmFormats = [\n \"mp3_44100_128\",\n \"pcm_16000\",\n \"pcm_22050\",\n \"pcm_24000\",\n \"pcm_44100\",\n ];\n for (const format of pcmFormats) {\n if (format.startsWith(\"pcm_\")) {\n const sampleRate = Number.parseInt(format.slice(4), 10);\n if (Number.isNaN(sampleRate) || sampleRate <= 0) {\n throw new Error(`Invalid PCM format: ${format}`);\n }\n }\n }\n\n // Test current output format\n logger.success(`Output format validated: ${settings.outputFormat}`);\n },\n },\n ],\n },\n {\n name: \"test eleven labs STT\",\n tests: [\n {\n name: \"STT settings validation\",\n fn: async (runtime: IAgentRuntime) => {\n const settings = getTranscriptionSettings(runtime);\n\n if (!settings.modelId) {\n throw new Error(\"Missing STT model ID configuration\");\n }\n\n const validGranularities = [\"none\", \"word\", \"character\"];\n if (!validGranularities.includes(settings.timestampsGranularity)) {\n throw new Error(\n `Invalid timestamps granularity: ${settings.timestampsGranularity}`,\n );\n }\n\n if (\n settings.numSpeakers !== undefined &&\n (settings.numSpeakers < 1 || settings.numSpeakers > 32)\n ) {\n throw new Error(\"Number of speakers must be between 1 and 32\");\n }\n\n logger.success(\"STT settings validated successfully\");\n },\n },\n {\n name: \"STT configuration defaults\",\n fn: async (runtime: IAgentRuntime) => {\n const settings = getTranscriptionSettings(runtime);\n\n if (settings.modelId !== \"scribe_v1\") {\n logger.warn(`Using non-default STT model: ${settings.modelId}`);\n }\n\n if (settings.timestampsGranularity !== \"word\") {\n logger.warn(\n `Using non-default timestamps granularity: ${settings.timestampsGranularity}`,\n );\n }\n\n logger.success(\"STT configuration defaults checked\");\n },\n },\n {\n name: \"STT input handling validation\",\n fn: async (_runtime: IAgentRuntime) => {\n const testCases = [\n { type: \"string URL\", valid: true },\n { type: \"Buffer\", valid: true },\n { type: \"object with audioUrl\", valid: true },\n ];\n\n for (const testCase of testCases) {\n if (!testCase.valid) {\n throw new Error(\n `Invalid test case should not be valid: ${testCase.type}`,\n );\n }\n }\n\n logger.success(\"STT input handling validation passed\");\n },\n },\n ],\n },\n ],\n};\nexport default elevenLabsPlugin;\n",
|
|
6
|
+
"export * from \"./index\";\n\nimport elevenLabsPlugin from \"./index\";\n\nexport default elevenLabsPlugin;\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "94B05DF3D97AE45B64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
8
|
+
var __toCommonJS = (from) => {
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
10
|
+
if (entry)
|
|
11
|
+
return entry;
|
|
12
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
__moduleCache.set(from, entry);
|
|
22
|
+
return entry;
|
|
23
|
+
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
29
|
+
var __export = (target, all) => {
|
|
30
|
+
for (var name in all)
|
|
31
|
+
__defProp(target, name, {
|
|
32
|
+
get: all[name],
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/index.node.ts
|
|
40
|
+
var exports_index_node = {};
|
|
41
|
+
__export(exports_index_node, {
|
|
42
|
+
elevenLabsPlugin: () => elevenLabsPlugin,
|
|
43
|
+
default: () => index_node_default
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(exports_index_node);
|
|
46
|
+
|
|
47
|
+
// src/index.ts
|
|
48
|
+
var import_elevenlabs_js = require("@elevenlabs/elevenlabs-js");
|
|
49
|
+
var import_api = require("@elevenlabs/elevenlabs-js/api");
|
|
50
|
+
var import_core = require("@elizaos/core");
|
|
51
|
+
function parseTtsOutputFormat(format) {
|
|
52
|
+
for (const allowed of Object.values(import_api.TextToSpeechStreamRequestOutputFormat)) {
|
|
53
|
+
if (allowed === format)
|
|
54
|
+
return allowed;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Unsupported ElevenLabs TTS output format: ${format}`);
|
|
57
|
+
}
|
|
58
|
+
function parseSttModelId(id) {
|
|
59
|
+
for (const allowed of Object.values(import_api.SpeechToTextConvertRequestModelId)) {
|
|
60
|
+
if (allowed === id)
|
|
61
|
+
return allowed;
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Unsupported ElevenLabs STT model: ${id}`);
|
|
64
|
+
}
|
|
65
|
+
function parseSttTimestampsGranularity(value) {
|
|
66
|
+
for (const allowed of Object.values(import_api.SpeechToTextConvertRequestTimestampsGranularity)) {
|
|
67
|
+
if (allowed === value)
|
|
68
|
+
return allowed;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Unsupported ElevenLabs STT timestamps granularity: ${value}`);
|
|
71
|
+
}
|
|
72
|
+
function extractTranscript(response) {
|
|
73
|
+
if ("transcripts" in response) {
|
|
74
|
+
return response.transcripts.map((t) => t.text).join(`
|
|
75
|
+
`);
|
|
76
|
+
}
|
|
77
|
+
return response.text;
|
|
78
|
+
}
|
|
79
|
+
function isRecord(value) {
|
|
80
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
81
|
+
}
|
|
82
|
+
function nonEmptyString(value) {
|
|
83
|
+
return typeof value === "string" ? value.trim() : "";
|
|
84
|
+
}
|
|
85
|
+
function normalizeTtsInput(input) {
|
|
86
|
+
const options = typeof input === "string" ? { text: input } : input;
|
|
87
|
+
if (!isRecord(options) || !nonEmptyString(options.text)) {
|
|
88
|
+
throw new Error("ElevenLabs TTS text is required");
|
|
89
|
+
}
|
|
90
|
+
if (options.model !== undefined && !nonEmptyString(options.model)) {
|
|
91
|
+
throw new Error("ElevenLabs TTS model must be a non-empty string");
|
|
92
|
+
}
|
|
93
|
+
if (options.voiceId !== undefined && !nonEmptyString(options.voiceId)) {
|
|
94
|
+
throw new Error("ElevenLabs TTS voiceId must be a non-empty string");
|
|
95
|
+
}
|
|
96
|
+
if (options.format !== undefined && !nonEmptyString(options.format)) {
|
|
97
|
+
throw new Error("ElevenLabs TTS format must be a non-empty string");
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
...options,
|
|
101
|
+
text: options.text.trim(),
|
|
102
|
+
model: options.model === undefined ? undefined : options.model.trim(),
|
|
103
|
+
voiceId: options.voiceId === undefined ? undefined : options.voiceId.trim(),
|
|
104
|
+
format: options.format === undefined ? undefined : options.format.trim()
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function validateAudioUrl(value) {
|
|
108
|
+
const url = nonEmptyString(value);
|
|
109
|
+
if (!url) {
|
|
110
|
+
throw new Error("ElevenLabs transcription audioUrl is required");
|
|
111
|
+
}
|
|
112
|
+
let parsed;
|
|
113
|
+
try {
|
|
114
|
+
parsed = new URL(url);
|
|
115
|
+
} catch {
|
|
116
|
+
throw new Error("ElevenLabs transcription audioUrl must be a valid URL");
|
|
117
|
+
}
|
|
118
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
119
|
+
throw new Error("ElevenLabs transcription audioUrl must use http or https");
|
|
120
|
+
}
|
|
121
|
+
return parsed.toString();
|
|
122
|
+
}
|
|
123
|
+
function isBrowser() {
|
|
124
|
+
return typeof globalThis.document !== "undefined";
|
|
125
|
+
}
|
|
126
|
+
function getSetting(runtime, key, fallback) {
|
|
127
|
+
const rawRuntime = runtime.getSetting(key);
|
|
128
|
+
const fromRuntime = rawRuntime === null || rawRuntime === undefined ? undefined : String(rawRuntime);
|
|
129
|
+
const envValue = typeof process !== "undefined" && process.env && typeof process.env[key] === "string" ? process.env[key] : undefined;
|
|
130
|
+
return fromRuntime ?? envValue ?? fallback;
|
|
131
|
+
}
|
|
132
|
+
function getBaseURL(runtime) {
|
|
133
|
+
const browserRaw = runtime.getSetting("ELEVENLABS_BROWSER_URL");
|
|
134
|
+
const browserURL = browserRaw === null || browserRaw === undefined ? undefined : String(browserRaw);
|
|
135
|
+
if (isBrowser() && browserURL)
|
|
136
|
+
return browserURL;
|
|
137
|
+
return "https://api.elevenlabs.io/v1";
|
|
138
|
+
}
|
|
139
|
+
function getApiKey(runtime) {
|
|
140
|
+
const raw = runtime.getSetting("ELEVENLABS_API_KEY");
|
|
141
|
+
const fromRuntime = raw === null || raw === undefined ? undefined : String(raw);
|
|
142
|
+
const fromEnv = typeof process !== "undefined" && process.env && typeof process.env.ELEVENLABS_API_KEY === "string" ? process.env.ELEVENLABS_API_KEY : undefined;
|
|
143
|
+
return fromRuntime ?? fromEnv;
|
|
144
|
+
}
|
|
145
|
+
function getElevenLabsClientConfig(runtime) {
|
|
146
|
+
const apiKey = getApiKey(runtime)?.trim();
|
|
147
|
+
const baseUrl = getBaseURL(runtime);
|
|
148
|
+
if (apiKey)
|
|
149
|
+
return { apiKey, baseUrl };
|
|
150
|
+
if (isBrowser() && baseUrl !== "https://api.elevenlabs.io/v1") {
|
|
151
|
+
return { baseUrl };
|
|
152
|
+
}
|
|
153
|
+
throw new Error("ELEVENLABS_API_KEY is required unless ELEVENLABS_BROWSER_URL is configured in browser mode");
|
|
154
|
+
}
|
|
155
|
+
function getVoiceSettings(runtime) {
|
|
156
|
+
return {
|
|
157
|
+
apiKey: getApiKey(runtime) || "",
|
|
158
|
+
voiceId: getSetting(runtime, "ELEVENLABS_VOICE_ID", "EXAVITQu4vr4xnSDxMaL"),
|
|
159
|
+
model: getSetting(runtime, "ELEVENLABS_MODEL_ID", "eleven_monolingual_v1"),
|
|
160
|
+
stability: getSetting(runtime, "ELEVENLABS_VOICE_STABILITY", "0.5"),
|
|
161
|
+
latency: getSetting(runtime, "ELEVENLABS_OPTIMIZE_STREAMING_LATENCY", "0"),
|
|
162
|
+
outputFormat: getSetting(runtime, "ELEVENLABS_OUTPUT_FORMAT", "mp3_44100_128"),
|
|
163
|
+
similarity: getSetting(runtime, "ELEVENLABS_VOICE_SIMILARITY_BOOST", "0.75"),
|
|
164
|
+
style: getSetting(runtime, "ELEVENLABS_VOICE_STYLE", "0"),
|
|
165
|
+
speakerBoost: import_core.parseBooleanFromText(`${getSetting(runtime, "ELEVENLABS_VOICE_USE_SPEAKER_BOOST", "true") ?? "true"}`)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function getTranscriptionSettings(runtime) {
|
|
169
|
+
const languageCode = getSetting(runtime, "ELEVENLABS_STT_LANGUAGE_CODE");
|
|
170
|
+
const numSpeakersStr = getSetting(runtime, "ELEVENLABS_STT_NUM_SPEAKERS");
|
|
171
|
+
const numSpeakers = numSpeakersStr ? Number(numSpeakersStr) : undefined;
|
|
172
|
+
if (numSpeakers !== undefined && (!Number.isInteger(numSpeakers) || numSpeakers < 1 || numSpeakers > 32)) {
|
|
173
|
+
throw new Error("ELEVENLABS_STT_NUM_SPEAKERS must be an integer between 1 and 32");
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
apiKey: getApiKey(runtime) || "",
|
|
177
|
+
modelId: getSetting(runtime, "ELEVENLABS_STT_MODEL_ID", "scribe_v1"),
|
|
178
|
+
languageCode: languageCode || undefined,
|
|
179
|
+
timestampsGranularity: getSetting(runtime, "ELEVENLABS_STT_TIMESTAMPS_GRANULARITY", "word"),
|
|
180
|
+
diarize: import_core.parseBooleanFromText(`${getSetting(runtime, "ELEVENLABS_STT_DIARIZE", "false") ?? "false"}`),
|
|
181
|
+
numSpeakers,
|
|
182
|
+
tagAudioEvents: import_core.parseBooleanFromText(`${getSetting(runtime, "ELEVENLABS_STT_TAG_AUDIO_EVENTS", "false") ?? "false"}`)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function isBufferInput(input) {
|
|
186
|
+
return typeof Buffer !== "undefined" && typeof Buffer.isBuffer === "function" && Buffer.isBuffer(input);
|
|
187
|
+
}
|
|
188
|
+
async function responseToAudioFile(response) {
|
|
189
|
+
if (isBrowser()) {
|
|
190
|
+
if (typeof response.blob === "function") {
|
|
191
|
+
return response.blob();
|
|
192
|
+
}
|
|
193
|
+
const arrayBuffer2 = await response.arrayBuffer();
|
|
194
|
+
return new Blob([arrayBuffer2]);
|
|
195
|
+
}
|
|
196
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
197
|
+
if (typeof Buffer !== "undefined") {
|
|
198
|
+
return Buffer.from(arrayBuffer);
|
|
199
|
+
}
|
|
200
|
+
return new Blob([arrayBuffer]);
|
|
201
|
+
}
|
|
202
|
+
async function readStreamToUint8Array(stream) {
|
|
203
|
+
const reader = stream.getReader();
|
|
204
|
+
const chunks = [];
|
|
205
|
+
let totalLength = 0;
|
|
206
|
+
while (true) {
|
|
207
|
+
const { done, value } = await reader.read();
|
|
208
|
+
if (done)
|
|
209
|
+
break;
|
|
210
|
+
if (!value)
|
|
211
|
+
continue;
|
|
212
|
+
chunks.push(value);
|
|
213
|
+
totalLength += value.byteLength;
|
|
214
|
+
}
|
|
215
|
+
const output = new Uint8Array(totalLength);
|
|
216
|
+
let offset = 0;
|
|
217
|
+
for (const chunk of chunks) {
|
|
218
|
+
output.set(chunk, offset);
|
|
219
|
+
offset += chunk.byteLength;
|
|
220
|
+
}
|
|
221
|
+
return output;
|
|
222
|
+
}
|
|
223
|
+
async function fetchSpeech(runtime, params) {
|
|
224
|
+
try {
|
|
225
|
+
const client = new import_elevenlabs_js.ElevenLabsClient(getElevenLabsClientConfig(runtime));
|
|
226
|
+
const stream = await client.textToSpeech.stream(params.voiceId, {
|
|
227
|
+
text: params.text,
|
|
228
|
+
modelId: params.modelId,
|
|
229
|
+
outputFormat: parseTtsOutputFormat(params.outputFormat),
|
|
230
|
+
optimizeStreamingLatency: Number(params.latency) || 0,
|
|
231
|
+
voiceSettings: {
|
|
232
|
+
stability: Number(params.stability) || 0,
|
|
233
|
+
similarityBoost: Number(params.similarity) || 0,
|
|
234
|
+
style: Number(params.style) || 0,
|
|
235
|
+
useSpeakerBoost: !!params.speakerBoost
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
if (!stream) {
|
|
239
|
+
throw new Error("Empty response body from ElevenLabs SDK");
|
|
240
|
+
}
|
|
241
|
+
return readStreamToUint8Array(stream);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
244
|
+
import_core.logger.error(`ElevenLabs fetchSpeech error: ${msg}`);
|
|
245
|
+
throw error instanceof Error ? error : new Error(msg);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function fetchTranscription(runtime, params) {
|
|
249
|
+
try {
|
|
250
|
+
const client = new import_elevenlabs_js.ElevenLabsClient(getElevenLabsClientConfig(runtime));
|
|
251
|
+
const body = {
|
|
252
|
+
modelId: parseSttModelId(params.modelId),
|
|
253
|
+
file: params.audioFile
|
|
254
|
+
};
|
|
255
|
+
if (params.languageCode) {
|
|
256
|
+
body.languageCode = params.languageCode;
|
|
257
|
+
}
|
|
258
|
+
body.timestampsGranularity = parseSttTimestampsGranularity(params.timestampsGranularity);
|
|
259
|
+
if (params.diarize) {
|
|
260
|
+
body.diarize = true;
|
|
261
|
+
if (params.numSpeakers !== undefined) {
|
|
262
|
+
body.numSpeakers = params.numSpeakers;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (params.tagAudioEvents) {
|
|
266
|
+
body.tagAudioEvents = true;
|
|
267
|
+
}
|
|
268
|
+
const response = await client.speechToText.convert(body);
|
|
269
|
+
if (!response) {
|
|
270
|
+
throw new Error("Empty response from ElevenLabs STT API");
|
|
271
|
+
}
|
|
272
|
+
return extractTranscript(response);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
275
|
+
import_core.logger.error(`ElevenLabs fetchTranscription error: ${msg}`);
|
|
276
|
+
throw error instanceof Error ? error : new Error(msg);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
var elevenLabsPlugin = {
|
|
280
|
+
name: "elevenLabs",
|
|
281
|
+
description: "High-quality text-to-speech synthesis and speech-to-text transcription using ElevenLabs API with support for multiple voices, languages, and speaker diarization",
|
|
282
|
+
models: {
|
|
283
|
+
[import_core.ModelType.TEXT_TO_SPEECH]: async (runtime, input) => {
|
|
284
|
+
const options = normalizeTtsInput(input);
|
|
285
|
+
const settings = getVoiceSettings(runtime);
|
|
286
|
+
const resolvedModel = options.model || settings.model;
|
|
287
|
+
const resolvedVoiceId = options.voiceId ?? settings.voiceId;
|
|
288
|
+
const outputFormat = options.format ? options.format === "mp3" ? "mp3_44100_128" : options.format : settings.outputFormat;
|
|
289
|
+
import_core.logger.log(`[ElevenLabs] Using TEXT_TO_SPEECH model: ${resolvedModel}`);
|
|
290
|
+
try {
|
|
291
|
+
const stream = await fetchSpeech(runtime, {
|
|
292
|
+
text: options.text,
|
|
293
|
+
voiceId: resolvedVoiceId,
|
|
294
|
+
modelId: resolvedModel,
|
|
295
|
+
outputFormat,
|
|
296
|
+
stability: settings.stability,
|
|
297
|
+
similarity: settings.similarity,
|
|
298
|
+
style: settings.style,
|
|
299
|
+
speakerBoost: settings.speakerBoost,
|
|
300
|
+
latency: settings.latency
|
|
301
|
+
});
|
|
302
|
+
return stream;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
305
|
+
import_core.logger.error(`ElevenLabs model error: ${msg}`);
|
|
306
|
+
throw error instanceof Error ? error : new Error(msg);
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
[import_core.ModelType.TRANSCRIPTION]: async (runtime, input) => {
|
|
310
|
+
const settings = getTranscriptionSettings(runtime);
|
|
311
|
+
import_core.logger.log(`[ElevenLabs] Using TRANSCRIPTION model: ${settings.modelId}`);
|
|
312
|
+
try {
|
|
313
|
+
let audioFile;
|
|
314
|
+
if (typeof input === "string") {
|
|
315
|
+
const audioUrl = validateAudioUrl(input);
|
|
316
|
+
const response = await fetch(audioUrl);
|
|
317
|
+
if (!response.ok) {
|
|
318
|
+
throw new Error(`Failed to fetch audio from URL: ${audioUrl}`);
|
|
319
|
+
}
|
|
320
|
+
audioFile = await responseToAudioFile(response);
|
|
321
|
+
} else if (isBufferInput(input)) {
|
|
322
|
+
audioFile = input;
|
|
323
|
+
} else if (isRecord(input) && "audioUrl" in input) {
|
|
324
|
+
const audioUrl = validateAudioUrl(input.audioUrl);
|
|
325
|
+
const response = await fetch(audioUrl);
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
throw new Error(`Failed to fetch audio from URL: ${audioUrl}`);
|
|
328
|
+
}
|
|
329
|
+
audioFile = await responseToAudioFile(response);
|
|
330
|
+
} else {
|
|
331
|
+
throw new Error("Invalid input type for TRANSCRIPTION model");
|
|
332
|
+
}
|
|
333
|
+
const transcript = await fetchTranscription(runtime, {
|
|
334
|
+
audioFile,
|
|
335
|
+
modelId: settings.modelId,
|
|
336
|
+
languageCode: settings.languageCode,
|
|
337
|
+
timestampsGranularity: settings.timestampsGranularity,
|
|
338
|
+
diarize: settings.diarize,
|
|
339
|
+
numSpeakers: settings.numSpeakers,
|
|
340
|
+
tagAudioEvents: settings.tagAudioEvents
|
|
341
|
+
});
|
|
342
|
+
return transcript;
|
|
343
|
+
} catch (error) {
|
|
344
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
345
|
+
import_core.logger.error(`ElevenLabs transcription error: ${msg}`);
|
|
346
|
+
throw error instanceof Error ? error : new Error(msg);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
tests: [
|
|
351
|
+
{
|
|
352
|
+
name: "test eleven labs",
|
|
353
|
+
tests: [
|
|
354
|
+
{
|
|
355
|
+
name: "Eleven Labs API key validation",
|
|
356
|
+
fn: async (runtime) => {
|
|
357
|
+
const settings = getVoiceSettings(runtime);
|
|
358
|
+
if (!settings.apiKey) {
|
|
359
|
+
throw new Error("Missing API key: Please provide a valid Eleven Labs API key.");
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "Voice settings validation",
|
|
365
|
+
fn: async (runtime) => {
|
|
366
|
+
const settings = getVoiceSettings(runtime);
|
|
367
|
+
if (!settings.voiceId) {
|
|
368
|
+
throw new Error("Missing voice ID configuration");
|
|
369
|
+
}
|
|
370
|
+
const stability = Number.parseFloat(settings.stability);
|
|
371
|
+
if (Number.isNaN(stability) || stability < 0 || stability > 1) {
|
|
372
|
+
throw new Error("Voice stability must be between 0 and 1");
|
|
373
|
+
}
|
|
374
|
+
const similarity = Number.parseFloat(settings.similarity);
|
|
375
|
+
if (Number.isNaN(similarity) || similarity < 0 || similarity > 1) {
|
|
376
|
+
throw new Error("Voice similarity boost must be between 0 and 1");
|
|
377
|
+
}
|
|
378
|
+
import_core.logger.success("Voice settings validated successfully");
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: "Eleven Labs API connectivity",
|
|
383
|
+
fn: async (runtime) => {
|
|
384
|
+
const settings = getVoiceSettings(runtime);
|
|
385
|
+
if (!settings.apiKey) {
|
|
386
|
+
import_core.logger.warn("Skipping API connectivity test - no API key provided");
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
try {
|
|
390
|
+
await fetchSpeech(runtime, {
|
|
391
|
+
text: "test",
|
|
392
|
+
voiceId: settings.voiceId,
|
|
393
|
+
modelId: settings.model,
|
|
394
|
+
outputFormat: settings.outputFormat,
|
|
395
|
+
stability: settings.stability,
|
|
396
|
+
similarity: settings.similarity,
|
|
397
|
+
style: settings.style,
|
|
398
|
+
speakerBoost: settings.speakerBoost,
|
|
399
|
+
latency: settings.latency
|
|
400
|
+
});
|
|
401
|
+
import_core.logger.success("API connectivity test passed");
|
|
402
|
+
} catch (error) {
|
|
403
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
404
|
+
if (msg.includes("QUOTA_EXCEEDED")) {
|
|
405
|
+
import_core.logger.warn("API quota exceeded - test skipped");
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
import_core.logger.error(`API connectivity test failed: ${msg}`);
|
|
409
|
+
throw new Error(`API connectivity test failed: ${msg}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: "ElevenLabs TTS Generation (stream exists)",
|
|
415
|
+
fn: async (runtime) => {
|
|
416
|
+
const settings = getVoiceSettings(runtime);
|
|
417
|
+
if (!settings.apiKey && !isBrowser()) {
|
|
418
|
+
import_core.logger.warn("Skipping TTS generation test - no API key provided");
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const testText = "Hello from ElevenLabs test.";
|
|
422
|
+
try {
|
|
423
|
+
const audio = await runtime.useModel(import_core.ModelType.TEXT_TO_SPEECH, testText);
|
|
424
|
+
const bytes = audio instanceof Uint8Array ? audio : Buffer.isBuffer(audio) ? new Uint8Array(audio) : audio instanceof ArrayBuffer ? new Uint8Array(audio) : null;
|
|
425
|
+
if (!bytes || bytes.byteLength === 0) {
|
|
426
|
+
throw new Error("TTS output must be non-empty Uint8Array, Buffer, or ArrayBuffer");
|
|
427
|
+
}
|
|
428
|
+
import_core.logger.success("Received TTS binary payload successfully");
|
|
429
|
+
} catch (error) {
|
|
430
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
431
|
+
if (msg.includes("QUOTA_EXCEEDED")) {
|
|
432
|
+
import_core.logger.warn("[ElevenLabs Test] API quota exceeded - test skipped");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
import_core.logger.error("[ElevenLabs Test] TTS Generation test failed:", msg);
|
|
436
|
+
throw new Error(`TTS Generation test failed: ${msg}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: "Output format handling",
|
|
442
|
+
fn: async (runtime) => {
|
|
443
|
+
const settings = getVoiceSettings(runtime);
|
|
444
|
+
const pcmFormats = [
|
|
445
|
+
"mp3_44100_128",
|
|
446
|
+
"pcm_16000",
|
|
447
|
+
"pcm_22050",
|
|
448
|
+
"pcm_24000",
|
|
449
|
+
"pcm_44100"
|
|
450
|
+
];
|
|
451
|
+
for (const format of pcmFormats) {
|
|
452
|
+
if (format.startsWith("pcm_")) {
|
|
453
|
+
const sampleRate = Number.parseInt(format.slice(4), 10);
|
|
454
|
+
if (Number.isNaN(sampleRate) || sampleRate <= 0) {
|
|
455
|
+
throw new Error(`Invalid PCM format: ${format}`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
import_core.logger.success(`Output format validated: ${settings.outputFormat}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
]
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "test eleven labs STT",
|
|
466
|
+
tests: [
|
|
467
|
+
{
|
|
468
|
+
name: "STT settings validation",
|
|
469
|
+
fn: async (runtime) => {
|
|
470
|
+
const settings = getTranscriptionSettings(runtime);
|
|
471
|
+
if (!settings.modelId) {
|
|
472
|
+
throw new Error("Missing STT model ID configuration");
|
|
473
|
+
}
|
|
474
|
+
const validGranularities = ["none", "word", "character"];
|
|
475
|
+
if (!validGranularities.includes(settings.timestampsGranularity)) {
|
|
476
|
+
throw new Error(`Invalid timestamps granularity: ${settings.timestampsGranularity}`);
|
|
477
|
+
}
|
|
478
|
+
if (settings.numSpeakers !== undefined && (settings.numSpeakers < 1 || settings.numSpeakers > 32)) {
|
|
479
|
+
throw new Error("Number of speakers must be between 1 and 32");
|
|
480
|
+
}
|
|
481
|
+
import_core.logger.success("STT settings validated successfully");
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: "STT configuration defaults",
|
|
486
|
+
fn: async (runtime) => {
|
|
487
|
+
const settings = getTranscriptionSettings(runtime);
|
|
488
|
+
if (settings.modelId !== "scribe_v1") {
|
|
489
|
+
import_core.logger.warn(`Using non-default STT model: ${settings.modelId}`);
|
|
490
|
+
}
|
|
491
|
+
if (settings.timestampsGranularity !== "word") {
|
|
492
|
+
import_core.logger.warn(`Using non-default timestamps granularity: ${settings.timestampsGranularity}`);
|
|
493
|
+
}
|
|
494
|
+
import_core.logger.success("STT configuration defaults checked");
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
name: "STT input handling validation",
|
|
499
|
+
fn: async (_runtime) => {
|
|
500
|
+
const testCases = [
|
|
501
|
+
{ type: "string URL", valid: true },
|
|
502
|
+
{ type: "Buffer", valid: true },
|
|
503
|
+
{ type: "object with audioUrl", valid: true }
|
|
504
|
+
];
|
|
505
|
+
for (const testCase of testCases) {
|
|
506
|
+
if (!testCase.valid) {
|
|
507
|
+
throw new Error(`Invalid test case should not be valid: ${testCase.type}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
import_core.logger.success("STT input handling validation passed");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
]
|
|
514
|
+
}
|
|
515
|
+
]
|
|
516
|
+
};
|
|
517
|
+
var src_default = elevenLabsPlugin;
|
|
518
|
+
// src/index.node.ts
|
|
519
|
+
var index_node_default = src_default;
|
|
520
|
+
|
|
521
|
+
//# debugId=AFADBC5F6E97695364756E2164756E21
|