@elevenlabs/client 0.5.0 → 0.5.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.
@@ -1,2 +1,2 @@
1
- import{Room as e,RoomEvent as t,ConnectionState as n,Track as o}from"livekit-client";function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s.apply(null,arguments)}const a=new Uint8Array(0);class i{static getFullOptions(e){return s({clientTools:{},onConnect:()=>{},onDebug:()=>{},onDisconnect:()=>{},onError:()=>{},onMessage:()=>{},onAudio:()=>{},onModeChange:()=>{},onStatusChange:()=>{},onCanSendFeedbackChange:()=>{}},e)}constructor(e,t){var n=this;this.options=void 0,this.connection=void 0,this.lastInterruptTimestamp=0,this.mode="listening",this.status="connecting",this.volume=1,this.currentEventId=1,this.lastFeedbackEventId=0,this.canSendFeedback=!1,this.endSessionWithDetails=async function(e){"connected"!==n.status&&"connecting"!==n.status||(n.updateStatus("disconnecting"),await n.handleEndSession(),n.updateStatus("disconnected"),n.options.onDisconnect(e))},this.onMessage=async function(e){switch(e.type){case"interruption":return void n.handleInterruption(e);case"agent_response":return void n.handleAgentResponse(e);case"user_transcript":return void n.handleUserTranscript(e);case"internal_tentative_agent_response":return void n.handleTentativeAgentResponse(e);case"client_tool_call":return void await n.handleClientToolCall(e);case"audio":return void n.handleAudio(e);case"vad_score":return void n.handleVadScore(e);case"ping":return void n.connection.sendMessage({type:"pong",event_id:e.ping_event.event_id});default:return void n.options.onDebug(e)}},this.setVolume=({volume:e})=>{this.volume=e},this.options=e,this.connection=t,this.options.onConnect({conversationId:t.conversationId}),this.connection.onMessage(this.onMessage),this.connection.onDisconnect(this.endSessionWithDetails),this.connection.onModeChange(e=>this.updateMode(e)),this.updateStatus("connected")}endSession(){return this.endSessionWithDetails({reason:"user"})}async handleEndSession(){this.connection.close()}updateMode(e){e!==this.mode&&(this.mode=e,this.options.onModeChange({mode:e}))}updateStatus(e){e!==this.status&&(this.status=e,this.options.onStatusChange({status:e}))}updateCanSendFeedback(){const e=this.currentEventId!==this.lastFeedbackEventId;this.canSendFeedback!==e&&(this.canSendFeedback=e,this.options.onCanSendFeedbackChange({canSendFeedback:e}))}handleInterruption(e){e.interruption_event&&(this.lastInterruptTimestamp=e.interruption_event.event_id)}handleAgentResponse(e){this.options.onMessage({source:"ai",message:e.agent_response_event.agent_response})}handleUserTranscript(e){this.options.onMessage({source:"user",message:e.user_transcription_event.user_transcript})}handleTentativeAgentResponse(e){this.options.onDebug({type:"tentative_agent_response",response:e.tentative_agent_response_internal_event.tentative_agent_response})}handleVadScore(e){this.options.onVadScore&&this.options.onVadScore({vadScore:e.vad_score_event.vad_score})}async handleClientToolCall(e){if(Object.prototype.hasOwnProperty.call(this.options.clientTools,e.client_tool_call.tool_name))try{var t;const n=null!=(t=await this.options.clientTools[e.client_tool_call.tool_name](e.client_tool_call.parameters))?t:"Client tool execution successful.",o="object"==typeof n?JSON.stringify(n):String(n);this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:o,is_error:!1})}catch(t){this.onError(`Client tool execution failed with following error: ${null==t?void 0:t.message}`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool execution failed: ${null==t?void 0:t.message}`,is_error:!0})}else{if(this.options.onUnhandledClientToolCall)return void this.options.onUnhandledClientToolCall(e.client_tool_call);this.onError(`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,is_error:!0})}}handleAudio(e){}onError(e,t){console.error(e,t),this.options.onError(e,t)}getId(){return this.connection.conversationId}isOpen(){return"connected"===this.status}setMicMuted(e){this.connection.setMicMuted(e)}getInputByteFrequencyData(){return a}getOutputByteFrequencyData(){return a}getInputVolume(){return 0}getOutputVolume(){return 0}sendFeedback(e){this.canSendFeedback?(this.connection.sendMessage({type:"feedback",score:e?"like":"dislike",event_id:this.currentEventId}),this.lastFeedbackEventId=this.currentEventId,this.updateCanSendFeedback()):console.warn(0===this.lastFeedbackEventId?"Cannot send feedback: the conversation has not started yet.":"Cannot send feedback: feedback has already been sent for the current response.")}sendContextualUpdate(e){this.connection.sendMessage({type:"contextual_update",text:e})}sendUserMessage(e){this.connection.sendMessage({type:"user_message",text:e})}sendUserActivity(){this.connection.sendMessage({type:"user_activity"})}sendMCPToolApprovalResult(e,t){this.connection.sendMessage({type:"mcp_tool_approval_result",tool_call_id:e,is_approved:t})}}class r{constructor(e={}){this.queue=[],this.disconnectionDetails=null,this.onDisconnectCallback=null,this.onMessageCallback=null,this.onModeChangeCallback=null,this.onDebug=void 0,this.onDebug=e.onDebug}debug(e){this.onDebug&&this.onDebug(e)}onMessage(e){this.onMessageCallback=e;const t=this.queue;this.queue=[],t.length>0&&queueMicrotask(()=>{t.forEach(e)})}onDisconnect(e){this.onDisconnectCallback=e;const t=this.disconnectionDetails;t&&queueMicrotask(()=>{e(t)})}onModeChange(e){this.onModeChangeCallback=e}updateMode(e){var t;null==(t=this.onModeChangeCallback)||t.call(this,e)}disconnect(e){var t;this.disconnectionDetails||(this.disconnectionDetails=e,null==(t=this.onDisconnectCallback)||t.call(this,e))}handleMessage(e){this.onMessageCallback?this.onMessageCallback(e):this.queue.push(e)}}function c(e){const[t,n]=e.split("_");if(!["pcm","ulaw"].includes(t))throw new Error(`Invalid format: ${e}`);const o=Number.parseInt(n);if(Number.isNaN(o))throw new Error(`Invalid sample rate: ${n}`);return{format:t,sampleRate:o}}const l="0.5.0";function u(e){return!!e.type}const d="conversation_initiation_client_data";function h(e){var t;const n={type:d};var o,s,a,i,r;return e.overrides&&(n.conversation_config_override={agent:{prompt:null==(o=e.overrides.agent)?void 0:o.prompt,first_message:null==(s=e.overrides.agent)?void 0:s.firstMessage,language:null==(a=e.overrides.agent)?void 0:a.language},tts:{voice_id:null==(i=e.overrides.tts)?void 0:i.voiceId},conversation:{text_only:null==(r=e.overrides.conversation)?void 0:r.textOnly}}),e.customLlmExtraBody&&(n.custom_llm_extra_body=e.customLlmExtraBody),e.dynamicVariables&&(n.dynamic_variables=e.dynamicVariables),e.userId&&(n.user_id=e.userId),null!=(t=e.overrides)&&t.client&&(n.source_info={source:e.overrides.client.source,version:e.overrides.client.version}),n}class p extends r{constructor(e,t,n,o){super(),this.socket=void 0,this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.socket=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.socket.addEventListener("error",e=>{setTimeout(()=>this.disconnect({reason:"error",message:"The connection was closed due to a socket error.",context:e}),0)}),this.socket.addEventListener("close",e=>{this.disconnect(1e3===e.code?{reason:"agent",context:e}:{reason:"error",message:e.reason||"The connection was closed by the server.",context:e})}),this.socket.addEventListener("message",e=>{try{const t=JSON.parse(e.data);if(!u(t))return;this.handleMessage(t)}catch(e){}})}static async create(e){let t=null;try{var n,o,s;const a=null!=(n=e.origin)?n:"wss://api.elevenlabs.io";let i;const r=(null==(o=e.overrides)||null==(o=o.client)?void 0:o.version)||l,d=(null==(s=e.overrides)||null==(s=s.client)?void 0:s.source)||"js_sdk";if(e.signedUrl){const t=e.signedUrl.includes("?")?"&":"?";i=`${e.signedUrl}${t}source=${d}&version=${r}`}else i=`${a}/v1/convai/conversation?agent_id=${e.agentId}&source=${d}&version=${r}`;const m=["convai"];e.authorization&&m.push(`bearer.${e.authorization}`),t=new WebSocket(i,m);const v=await new Promise((n,o)=>{t.addEventListener("open",()=>{var n;const o=h(e);null==(n=t)||n.send(JSON.stringify(o))},{once:!0}),t.addEventListener("error",e=>{setTimeout(()=>o(e),0)}),t.addEventListener("close",o),t.addEventListener("message",e=>{const t=JSON.parse(e.data);u(t)&&("conversation_initiation_metadata"===t.type?n(t.conversation_initiation_metadata_event):console.warn("First received message is not conversation metadata."))},{once:!0})}),{conversation_id:g,agent_output_audio_format:f,user_input_audio_format:w}=v,y=c(null!=w?w:"pcm_16000"),_=c(f);return new p(t,g,y,_)}catch(e){var a;throw null==(a=t)||a.close(),e}}close(){this.socket.close()}sendMessage(e){this.socket.send(JSON.stringify(e))}async setMicMuted(e){console.warn(`WebSocket connection setMicMuted called with ${e}, but this is handled by VoiceConversation`)}}function m(e){const t=new Uint8Array(e);return window.btoa(String.fromCharCode(...t))}function v(e){const t=window.atob(e),n=t.length,o=new Uint8Array(n);for(let e=0;e<n;e++)o[e]=t.charCodeAt(e);return o.buffer}const g=new Map;function f(e,t){return async n=>{const o=g.get(e);if(o)return n.addModule(o);const s=new Blob([t],{type:"application/javascript"}),a=URL.createObjectURL(s);try{return await n.addModule(a),void g.set(e,a)}catch(e){URL.revokeObjectURL(a)}try{const o=`data:application/javascript;base64,${btoa(t)}`;await n.addModule(o),g.set(e,o)}catch(t){throw new Error(`Failed to load the ${e} worklet module. Make sure the browser supports AudioWorklets.`)}}}const w=f("raw-audio-processor",'\nconst BIAS = 0x84;\nconst CLIP = 32635;\nconst encodeTable = [\n 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,\n 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7\n];\n\nfunction encodeSample(sample) {\n let sign;\n let exponent;\n let mantissa;\n let muLawSample;\n sign = (sample >> 8) & 0x80;\n if (sign !== 0) sample = -sample;\n sample = sample + BIAS;\n if (sample > CLIP) sample = CLIP;\n exponent = encodeTable[(sample>>7) & 0xFF];\n mantissa = (sample >> (exponent+3)) & 0x0F;\n muLawSample = ~(sign | (exponent << 4) | mantissa);\n \n return muLawSample;\n}\n\nclass RawAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n \n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.isMuted = false;\n this.buffer = []; // Initialize an empty buffer\n this.bufferSize = data.sampleRate / 4;\n this.format = data.format;\n\n if (globalThis.LibSampleRate && sampleRate !== data.sampleRate) {\n globalThis.LibSampleRate.create(1, sampleRate, data.sampleRate).then(resampler => {\n this.resampler = resampler;\n });\n }\n break;\n case "setMuted":\n this.isMuted = data.isMuted;\n break;\n }\n };\n }\n process(inputs) {\n if (!this.buffer) {\n return true;\n }\n \n const input = inputs[0]; // Get the first input node\n if (input.length > 0) {\n let channelData = input[0]; // Get the first channel\'s data\n\n // Resample the audio if necessary\n if (this.resampler) {\n channelData = this.resampler.full(channelData);\n }\n\n // Add channel data to the buffer\n this.buffer.push(...channelData);\n // Get max volume \n let sum = 0.0;\n for (let i = 0; i < channelData.length; i++) {\n sum += channelData[i] * channelData[i];\n }\n const maxVolume = Math.sqrt(sum / channelData.length);\n // Check if buffer size has reached or exceeded the threshold\n if (this.buffer.length >= this.bufferSize) {\n const float32Array = this.isMuted \n ? new Float32Array(this.buffer.length)\n : new Float32Array(this.buffer);\n\n let encodedArray = this.format === "ulaw"\n ? new Uint8Array(float32Array.length)\n : new Int16Array(float32Array.length);\n\n // Iterate through the Float32Array and convert each sample to PCM16\n for (let i = 0; i < float32Array.length; i++) {\n // Clamp the value to the range [-1, 1]\n let sample = Math.max(-1, Math.min(1, float32Array[i]));\n\n // Scale the sample to the range [-32768, 32767]\n let value = sample < 0 ? sample * 32768 : sample * 32767;\n if (this.format === "ulaw") {\n value = encodeSample(Math.round(value));\n }\n\n encodedArray[i] = value;\n }\n\n // Send the buffered data to the main script\n this.port.postMessage([encodedArray, maxVolume]);\n\n // Clear the buffer after sending\n this.buffer = [];\n }\n }\n return true; // Continue processing\n }\n}\nregisterProcessor("raw-audio-processor", RawAudioProcessor);\n');class y extends r{constructor(e,t,n,o,s={}){super(s),this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.room=void 0,this.isConnected=!1,this.audioEventId=1,this.audioCaptureContext=null,this.room=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.setupRoomEventListeners()}static async create(n){let o;if("conversationToken"in n&&n.conversationToken)o=n.conversationToken;else{if(!("agentId"in n)||!n.agentId)throw new Error("Either conversationToken or agentId is required for WebRTC connection");try{var s,a,i;const e=(null==(s=n.overrides)||null==(s=s.client)?void 0:s.version)||l,t=(null==(a=n.overrides)||null==(a=a.client)?void 0:a.source)||"js_sdk",c=`${r=null!=(i=n.origin)?i:"https://api.elevenlabs.io",r.replace(/^wss:\/\//,"https://")}/v1/convai/conversation/token?agent_id=${n.agentId}&source=${t}&version=${e}`,u=await fetch(c);if(!u.ok)throw new Error(`ElevenLabs API returned ${u.status} ${u.statusText}`);if(o=(await u.json()).token,!o)throw new Error("No conversation token received from API")}catch(e){let t=e instanceof Error?e.message:String(e);throw e instanceof Error&&e.message.includes("401")&&(t="Your agent has authentication enabled, but no signed URL or conversation token was provided."),new Error(`Failed to fetch conversation token for agent ${n.agentId}: ${t}`)}}var r;const u=new e;try{const e=`room_${Date.now()}`,s=c("pcm_48000"),a=c("pcm_48000"),i=new y(u,e,s,a,n),r=n.livekitUrl||"wss://livekit.rtc.elevenlabs.io";var p;await u.connect(r,o),await new Promise(e=>{if(i.isConnected)e();else{const n=()=>{u.off(t.Connected,n),e()};u.on(t.Connected,n)}}),u.name&&(i.conversationId=(null==(p=u.name.match(/(conv_[a-zA-Z0-9]+)/))?void 0:p[0])||u.name),await u.localParticipant.setMicrophoneEnabled(!0);const l=h(n);return i.debug({type:d,message:l}),await i.sendMessage(l),i}catch(e){throw await u.disconnect(),e}}setupRoomEventListeners(){var e=this;this.room.on(t.Connected,async function(){e.isConnected=!0,console.info("WebRTC room connected")}),this.room.on(t.Disconnected,e=>{this.isConnected=!1,this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:null==e?void 0:e.toString()})})}),this.room.on(t.ConnectionStateChanged,e=>{e===n.Disconnected&&(this.isConnected=!1,this.disconnect({reason:"error",message:`LiveKit connection state changed to ${e}`,context:new Event("connection_state_changed")}))}),this.room.on(t.DataReceived,(e,t)=>{try{const t=JSON.parse((new TextDecoder).decode(e));if("audio"===t.type)return;u(t)?this.handleMessage(t):console.warn("Invalid socket event received:",t)}catch(t){console.warn("Failed to parse incoming data message:",t),console.warn("Raw payload:",(new TextDecoder).decode(e))}}),this.room.on(t.TrackSubscribed,async function(t,n,s){if(t.kind===o.Kind.Audio&&s.identity.includes("agent")){const n=t,o=n.attach();o.autoplay=!0,o.controls=!1,o.style.display="none",document.body.appendChild(o),await e.setupAudioCapture(n)}}),this.room.on(t.ActiveSpeakersChanged,async function(t){t.length>0?t[0].identity.includes("agent")&&e.updateMode("speaking"):e.updateMode("listening")})}close(){if(this.isConnected){try{this.room.localParticipant.audioTrackPublications.forEach(e=>{e.track&&e.track.stop()})}catch(e){console.warn("Error stopping local tracks:",e)}this.audioCaptureContext&&(this.audioCaptureContext.close().catch(e=>{console.warn("Error closing audio capture context:",e)}),this.audioCaptureContext=null),this.room.disconnect()}}async sendMessage(e){if(this.isConnected&&this.room.localParticipant){if(!("user_audio_chunk"in e))try{const t=(new TextEncoder).encode(JSON.stringify(e));await this.room.localParticipant.publishData(t,{reliable:!0})}catch(t){this.debug({type:"send_message_error",message:{message:e,error:t}}),console.error("Failed to send message via WebRTC:",t)}}else console.warn("Cannot send message: room not connected or no local participant")}getRoom(){return this.room}async setMicMuted(e){if(!this.isConnected||!this.room.localParticipant)return void console.warn("Cannot set microphone muted: room not connected or no local participant");const t=this.room.localParticipant.getTrackPublication(o.Source.Microphone);if(null!=t&&t.track)try{e?await t.track.mute():await t.track.unmute()}catch(t){await this.room.localParticipant.setMicrophoneEnabled(!e)}else await this.room.localParticipant.setMicrophoneEnabled(!e)}async setupAudioCapture(e){try{const t=new AudioContext;this.audioCaptureContext=t;const n=new MediaStream([e.mediaStreamTrack]),o=t.createMediaStreamSource(n);await w(t.audioWorklet);const s=new AudioWorkletNode(t,"raw-audio-processor");s.port.postMessage({type:"setFormat",format:this.outputFormat.format,sampleRate:this.outputFormat.sampleRate}),s.port.onmessage=e=>{const[t,n]=e.data;if(n>.01){const e=m(t.buffer),n=this.audioEventId++;this.handleMessage({type:"audio",audio_event:{audio_base_64:e,event_id:n}})}},o.connect(s)}catch(e){console.warn("Failed to set up audio capture:",e)}}}async function _(e){const t=function(e){return e.connectionType?e.connectionType:"conversationToken"in e&&e.conversationToken?"webrtc":"websocket"}(e);switch(t){case"websocket":return p.create(e);case"webrtc":return y.create(e);default:throw new Error(`Unknown connection type: ${t}`)}}function b(){return["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document}async function k(e={default:0,android:3e3}){let t=e.default;var n;if(/android/i.test(navigator.userAgent))t=null!=(n=e.android)?n:t;else if(b()){var o;t=null!=(o=e.ios)?o:t}t>0&&await new Promise(e=>setTimeout(e,t))}class C extends i{static async startSession(e){const t=i.getFullOptions(e);t.onStatusChange({status:"connecting"}),t.onCanSendFeedbackChange({canSendFeedback:!1});let n=null;try{return await k(t.connectionDelay),n=await _(e),new C(t,n)}catch(e){var o;throw t.onStatusChange({status:"disconnected"}),null==(o=n)||o.close(),e}}}class M{static async create({sampleRate:e,format:t,preferHeadphonesForIosDevices:n}){let o=null,a=null;try{const i={sampleRate:{ideal:e},echoCancellation:!0,noiseSuppression:!0};if(b()&&n){const e=(await window.navigator.mediaDevices.enumerateDevices()).find(e=>"audioinput"===e.kind&&["airpod","headphone","earphone"].find(t=>e.label.toLowerCase().includes(t)));e&&(i.deviceId={ideal:e.deviceId})}const r=navigator.mediaDevices.getSupportedConstraints().sampleRate;o=new window.AudioContext(r?{sampleRate:e}:{});const c=o.createAnalyser();r||await o.audioWorklet.addModule("https://cdn.jsdelivr.net/npm/@alexanderolsen/libsamplerate-js@2.1.2/dist/libsamplerate.worklet.js"),await w(o.audioWorklet);const l=s({voiceIsolation:!0},i);a=await navigator.mediaDevices.getUserMedia({audio:l});const u=o.createMediaStreamSource(a),d=new AudioWorkletNode(o,"raw-audio-processor");return d.port.postMessage({type:"setFormat",format:t,sampleRate:e}),u.connect(c),c.connect(d),await o.resume(),new M(o,c,d,a)}catch(e){var i,r;throw null==(i=a)||i.getTracks().forEach(e=>e.stop()),null==(r=o)||r.close(),e}}constructor(e,t,n,o){this.context=void 0,this.analyser=void 0,this.worklet=void 0,this.inputStream=void 0,this.context=e,this.analyser=t,this.worklet=n,this.inputStream=o}async close(){this.inputStream.getTracks().forEach(e=>e.stop()),await this.context.close()}setMuted(e){this.worklet.port.postMessage({type:"setMuted",isMuted:e})}}const S=f("audio-concat-processor",'\nconst decodeTable = [0,132,396,924,1980,4092,8316,16764];\n\nexport function decodeSample(muLawSample) {\n let sign;\n let exponent;\n let mantissa;\n let sample;\n muLawSample = ~muLawSample;\n sign = (muLawSample & 0x80);\n exponent = (muLawSample >> 4) & 0x07;\n mantissa = muLawSample & 0x0F;\n sample = decodeTable[exponent] + (mantissa << (exponent+3));\n if (sign !== 0) sample = -sample;\n\n return sample;\n}\n\nclass AudioConcatProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffers = []; // Initialize an empty buffer\n this.cursor = 0;\n this.currentBuffer = null;\n this.wasInterrupted = false;\n this.finished = false;\n \n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.format = data.format;\n break;\n case "buffer":\n this.wasInterrupted = false;\n this.buffers.push(\n this.format === "ulaw"\n ? new Uint8Array(data.buffer)\n : new Int16Array(data.buffer)\n );\n break;\n case "interrupt":\n this.wasInterrupted = true;\n break;\n case "clearInterrupted":\n if (this.wasInterrupted) {\n this.wasInterrupted = false;\n this.buffers = [];\n this.currentBuffer = null;\n }\n }\n };\n }\n process(_, outputs) {\n let finished = false;\n const output = outputs[0][0];\n for (let i = 0; i < output.length; i++) {\n if (!this.currentBuffer) {\n if (this.buffers.length === 0) {\n finished = true;\n break;\n }\n this.currentBuffer = this.buffers.shift();\n this.cursor = 0;\n }\n\n let value = this.currentBuffer[this.cursor];\n if (this.format === "ulaw") {\n value = decodeSample(value);\n }\n output[i] = value / 32768;\n this.cursor++;\n\n if (this.cursor >= this.currentBuffer.length) {\n this.currentBuffer = null;\n }\n }\n\n if (this.finished !== finished) {\n this.finished = finished;\n this.port.postMessage({ type: "process", finished });\n }\n\n return true; // Continue processing\n }\n}\n\nregisterProcessor("audio-concat-processor", AudioConcatProcessor);\n');class F{static async create({sampleRate:e,format:t}){let n=null;try{n=new AudioContext({sampleRate:e});const o=n.createAnalyser(),s=n.createGain();s.connect(o),o.connect(n.destination),await S(n.audioWorklet);const a=new AudioWorkletNode(n,"audio-concat-processor");return a.port.postMessage({type:"setFormat",format:t}),a.connect(s),await n.resume(),new F(n,o,s,a)}catch(e){var o;throw null==(o=n)||o.close(),e}}constructor(e,t,n,o){this.context=void 0,this.analyser=void 0,this.gain=void 0,this.worklet=void 0,this.context=e,this.analyser=t,this.gain=n,this.worklet=o}async close(){await this.context.close()}}class I extends i{static async startSession(e){var t;const n=i.getFullOptions(e);n.onStatusChange({status:"connecting"}),n.onCanSendFeedbackChange({canSendFeedback:!1});let o=null,a=null,r=null,c=null,l=null;if(null==(t=e.useWakeLock)||t)try{l=await navigator.wakeLock.request("screen")}catch(e){}try{var u;return c=await navigator.mediaDevices.getUserMedia({audio:!0}),await k(n.connectionDelay),a=await _(e),[o,r]=await Promise.all([M.create(s({},a.inputFormat,{preferHeadphonesForIosDevices:e.preferHeadphonesForIosDevices})),F.create(a.outputFormat)]),null==(u=c)||u.getTracks().forEach(e=>e.stop()),c=null,new I(n,a,o,r,l)}catch(e){var d,h,p,m;n.onStatusChange({status:"disconnected"}),null==(d=c)||d.getTracks().forEach(e=>e.stop()),null==(h=a)||h.close(),await(null==(p=o)?void 0:p.close()),await(null==(m=r)?void 0:m.close());try{var v;await(null==(v=l)?void 0:v.release()),l=null}catch(e){}throw e}}constructor(e,t,n,o,s){super(e,t),this.input=void 0,this.output=void 0,this.wakeLock=void 0,this.inputFrequencyData=void 0,this.outputFrequencyData=void 0,this.onInputWorkletMessage=e=>{"connected"===this.status&&this.connection.sendMessage({user_audio_chunk:m(e.data[0].buffer)})},this.onOutputWorkletMessage=({data:e})=>{"process"===e.type&&this.updateMode(e.finished?"listening":"speaking")},this.addAudioBase64Chunk=e=>{this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"}),this.output.worklet.port.postMessage({type:"buffer",buffer:v(e)})},this.fadeOutAudio=()=>{this.updateMode("listening"),this.output.worklet.port.postMessage({type:"interrupt"}),this.output.gain.gain.exponentialRampToValueAtTime(1e-4,this.output.context.currentTime+2),setTimeout(()=>{this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"})},2e3)},this.calculateVolume=e=>{if(0===e.length)return 0;let t=0;for(let n=0;n<e.length;n++)t+=e[n]/255;return t/=e.length,t<0?0:t>1?1:t},this.input=n,this.output=o,this.wakeLock=s,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.output.worklet.port.onmessage=this.onOutputWorkletMessage}async handleEndSession(){await super.handleEndSession();try{var e;await(null==(e=this.wakeLock)?void 0:e.release()),this.wakeLock=null}catch(e){}await this.input.close(),await this.output.close()}handleInterruption(e){super.handleInterruption(e),this.fadeOutAudio()}handleAudio(e){var t,n;this.lastInterruptTimestamp<=e.audio_event.event_id&&(null==(t=(n=this.options).onAudio)||t.call(n,e.audio_event.audio_base_64),this.connection instanceof y||this.addAudioBase64Chunk(e.audio_event.audio_base_64),this.currentEventId=e.audio_event.event_id,this.updateCanSendFeedback(),this.updateMode("speaking"))}setMicMuted(e){this.connection instanceof y?this.connection.setMicMuted(e):this.input.setMuted(e)}getInputByteFrequencyData(){return null!=this.inputFrequencyData||(this.inputFrequencyData=new Uint8Array(this.input.analyser.frequencyBinCount)),this.input.analyser.getByteFrequencyData(this.inputFrequencyData),this.inputFrequencyData}getOutputByteFrequencyData(){return null!=this.outputFrequencyData||(this.outputFrequencyData=new Uint8Array(this.output.analyser.frequencyBinCount)),this.output.analyser.getByteFrequencyData(this.outputFrequencyData),this.outputFrequencyData}getInputVolume(){return this.calculateVolume(this.getInputByteFrequencyData())}getOutputVolume(){return this.calculateVolume(this.getOutputByteFrequencyData())}}function x(e,t,n="https://api.elevenlabs.io"){return fetch(`${n}/v1/convai/conversations/${e}/feedback`,{method:"POST",body:JSON.stringify({feedback:t?"like":"dislike"}),headers:{"Content-Type":"application/json"}})}class D extends i{static startSession(e){return e.textOnly?C.startSession(e):I.startSession(e)}}export{D as Conversation,y as WebRTCConnection,p as WebSocketConnection,_ as createConnection,x as postOverallFeedback};
1
+ import{Room as e,RoomEvent as t,ConnectionState as n,Track as o}from"livekit-client";function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s.apply(null,arguments)}const a=new Uint8Array(0);class i{static getFullOptions(e){return s({clientTools:{},onConnect:()=>{},onDebug:()=>{},onDisconnect:()=>{},onError:()=>{},onMessage:()=>{},onAudio:()=>{},onModeChange:()=>{},onStatusChange:()=>{},onCanSendFeedbackChange:()=>{}},e)}constructor(e,t){var n=this;this.options=void 0,this.connection=void 0,this.lastInterruptTimestamp=0,this.mode="listening",this.status="connecting",this.volume=1,this.currentEventId=1,this.lastFeedbackEventId=0,this.canSendFeedback=!1,this.endSessionWithDetails=async function(e){"connected"!==n.status&&"connecting"!==n.status||(n.updateStatus("disconnecting"),await n.handleEndSession(),n.updateStatus("disconnected"),n.options.onDisconnect&&n.options.onDisconnect(e))},this.onMessage=async function(e){switch(e.type){case"interruption":return void n.handleInterruption(e);case"agent_response":return void n.handleAgentResponse(e);case"user_transcript":return void n.handleUserTranscript(e);case"internal_tentative_agent_response":return void n.handleTentativeAgentResponse(e);case"client_tool_call":return void await n.handleClientToolCall(e);case"audio":return void n.handleAudio(e);case"vad_score":return void n.handleVadScore(e);case"ping":return void n.connection.sendMessage({type:"pong",event_id:e.ping_event.event_id});default:return void(n.options.onDebug&&n.options.onDebug(e))}},this.setVolume=({volume:e})=>{this.volume=e},this.options=e,this.connection=t,this.options.onConnect&&this.options.onConnect({conversationId:t.conversationId}),this.connection.onMessage(this.onMessage),this.connection.onDisconnect(this.endSessionWithDetails),this.connection.onModeChange(e=>this.updateMode(e)),this.updateStatus("connected")}endSession(){return this.endSessionWithDetails({reason:"user"})}async handleEndSession(){this.connection.close()}updateMode(e){e!==this.mode&&(this.mode=e,this.options.onModeChange&&this.options.onModeChange({mode:e}))}updateStatus(e){e!==this.status&&(this.status=e,this.options.onStatusChange&&this.options.onStatusChange({status:e}))}updateCanSendFeedback(){const e=this.currentEventId!==this.lastFeedbackEventId;this.canSendFeedback!==e&&(this.canSendFeedback=e,this.options.onCanSendFeedbackChange&&this.options.onCanSendFeedbackChange({canSendFeedback:e}))}handleInterruption(e){e.interruption_event&&(this.lastInterruptTimestamp=e.interruption_event.event_id)}handleAgentResponse(e){this.options.onMessage&&this.options.onMessage({source:"ai",message:e.agent_response_event.agent_response})}handleUserTranscript(e){this.options.onMessage&&this.options.onMessage({source:"user",message:e.user_transcription_event.user_transcript})}handleTentativeAgentResponse(e){this.options.onDebug&&this.options.onDebug({type:"tentative_agent_response",response:e.tentative_agent_response_internal_event.tentative_agent_response})}handleVadScore(e){this.options.onVadScore&&this.options.onVadScore({vadScore:e.vad_score_event.vad_score})}async handleClientToolCall(e){if(Object.prototype.hasOwnProperty.call(this.options.clientTools,e.client_tool_call.tool_name))try{var t;const n=null!=(t=await this.options.clientTools[e.client_tool_call.tool_name](e.client_tool_call.parameters))?t:"Client tool execution successful.",o="object"==typeof n?JSON.stringify(n):String(n);this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:o,is_error:!1})}catch(t){this.onError(`Client tool execution failed with following error: ${null==t?void 0:t.message}`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool execution failed: ${null==t?void 0:t.message}`,is_error:!0})}else{if(this.options.onUnhandledClientToolCall)return void this.options.onUnhandledClientToolCall(e.client_tool_call);this.onError(`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,is_error:!0})}}handleAudio(e){}onError(e,t){console.error(e,t),this.options.onError&&this.options.onError(e,t)}getId(){return this.connection.conversationId}isOpen(){return"connected"===this.status}setMicMuted(e){this.connection.setMicMuted(e)}getInputByteFrequencyData(){return a}getOutputByteFrequencyData(){return a}getInputVolume(){return 0}getOutputVolume(){return 0}sendFeedback(e){this.canSendFeedback?(this.connection.sendMessage({type:"feedback",score:e?"like":"dislike",event_id:this.currentEventId}),this.lastFeedbackEventId=this.currentEventId,this.updateCanSendFeedback()):console.warn(0===this.lastFeedbackEventId?"Cannot send feedback: the conversation has not started yet.":"Cannot send feedback: feedback has already been sent for the current response.")}sendContextualUpdate(e){this.connection.sendMessage({type:"contextual_update",text:e})}sendUserMessage(e){this.connection.sendMessage({type:"user_message",text:e})}sendUserActivity(){this.connection.sendMessage({type:"user_activity"})}sendMCPToolApprovalResult(e,t){this.connection.sendMessage({type:"mcp_tool_approval_result",tool_call_id:e,is_approved:t})}}class r{constructor(e={}){this.queue=[],this.disconnectionDetails=null,this.onDisconnectCallback=null,this.onMessageCallback=null,this.onModeChangeCallback=null,this.onDebug=void 0,this.onDebug=e.onDebug}debug(e){this.onDebug&&this.onDebug(e)}onMessage(e){this.onMessageCallback=e;const t=this.queue;this.queue=[],t.length>0&&queueMicrotask(()=>{t.forEach(e)})}onDisconnect(e){this.onDisconnectCallback=e;const t=this.disconnectionDetails;t&&queueMicrotask(()=>{e(t)})}onModeChange(e){this.onModeChangeCallback=e}updateMode(e){var t;null==(t=this.onModeChangeCallback)||t.call(this,e)}disconnect(e){var t;this.disconnectionDetails||(this.disconnectionDetails=e,null==(t=this.onDisconnectCallback)||t.call(this,e))}handleMessage(e){this.onMessageCallback?this.onMessageCallback(e):this.queue.push(e)}}function c(e){const[t,n]=e.split("_");if(!["pcm","ulaw"].includes(t))throw new Error(`Invalid format: ${e}`);const o=Number.parseInt(n);if(Number.isNaN(o))throw new Error(`Invalid sample rate: ${n}`);return{format:t,sampleRate:o}}const l="0.5.2";function u(e){return!!e.type}const d="conversation_initiation_client_data";function h(e){var t;const n={type:d};var o,s,a,i,r;return e.overrides&&(n.conversation_config_override={agent:{prompt:null==(o=e.overrides.agent)?void 0:o.prompt,first_message:null==(s=e.overrides.agent)?void 0:s.firstMessage,language:null==(a=e.overrides.agent)?void 0:a.language},tts:{voice_id:null==(i=e.overrides.tts)?void 0:i.voiceId},conversation:{text_only:null==(r=e.overrides.conversation)?void 0:r.textOnly}}),e.customLlmExtraBody&&(n.custom_llm_extra_body=e.customLlmExtraBody),e.dynamicVariables&&(n.dynamic_variables=e.dynamicVariables),e.userId&&(n.user_id=e.userId),null!=(t=e.overrides)&&t.client&&(n.source_info={source:e.overrides.client.source,version:e.overrides.client.version}),n}class p extends r{constructor(e,t,n,o){super(),this.socket=void 0,this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.socket=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.socket.addEventListener("error",e=>{setTimeout(()=>this.disconnect({reason:"error",message:"The connection was closed due to a socket error.",context:e}),0)}),this.socket.addEventListener("close",e=>{this.disconnect(1e3===e.code?{reason:"agent",context:e}:{reason:"error",message:e.reason||"The connection was closed by the server.",context:e})}),this.socket.addEventListener("message",e=>{try{const t=JSON.parse(e.data);if(!u(t))return;this.handleMessage(t)}catch(e){}})}static async create(e){let t=null;try{var n,o,s;const a=null!=(n=e.origin)?n:"wss://api.elevenlabs.io";let i;const r=(null==(o=e.overrides)||null==(o=o.client)?void 0:o.version)||l,d=(null==(s=e.overrides)||null==(s=s.client)?void 0:s.source)||"js_sdk";if(e.signedUrl){const t=e.signedUrl.includes("?")?"&":"?";i=`${e.signedUrl}${t}source=${d}&version=${r}`}else i=`${a}/v1/convai/conversation?agent_id=${e.agentId}&source=${d}&version=${r}`;const m=["convai"];e.authorization&&m.push(`bearer.${e.authorization}`),t=new WebSocket(i,m);const g=await new Promise((n,o)=>{t.addEventListener("open",()=>{var n;const o=h(e);null==(n=t)||n.send(JSON.stringify(o))},{once:!0}),t.addEventListener("error",e=>{setTimeout(()=>o(e),0)}),t.addEventListener("close",o),t.addEventListener("message",e=>{const t=JSON.parse(e.data);u(t)&&("conversation_initiation_metadata"===t.type?n(t.conversation_initiation_metadata_event):console.warn("First received message is not conversation metadata."))},{once:!0})}),{conversation_id:v,agent_output_audio_format:f,user_input_audio_format:w}=g,y=c(null!=w?w:"pcm_16000"),b=c(f);return new p(t,v,y,b)}catch(e){var a;throw null==(a=t)||a.close(),e}}close(){this.socket.close()}sendMessage(e){this.socket.send(JSON.stringify(e))}async setMicMuted(e){console.warn(`WebSocket connection setMicMuted called with ${e}, but this is handled by VoiceConversation`)}}function m(e){const t=new Uint8Array(e);return window.btoa(String.fromCharCode(...t))}function g(e){const t=window.atob(e),n=t.length,o=new Uint8Array(n);for(let e=0;e<n;e++)o[e]=t.charCodeAt(e);return o.buffer}const v=new Map;function f(e,t){return async n=>{const o=v.get(e);if(o)return n.addModule(o);const s=new Blob([t],{type:"application/javascript"}),a=URL.createObjectURL(s);try{return await n.addModule(a),void v.set(e,a)}catch(e){URL.revokeObjectURL(a)}try{const o=`data:application/javascript;base64,${btoa(t)}`;await n.addModule(o),v.set(e,o)}catch(t){throw new Error(`Failed to load the ${e} worklet module. Make sure the browser supports AudioWorklets.`)}}}const w=f("raw-audio-processor",'\nconst BIAS = 0x84;\nconst CLIP = 32635;\nconst encodeTable = [\n 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,\n 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7\n];\n\nfunction encodeSample(sample) {\n let sign;\n let exponent;\n let mantissa;\n let muLawSample;\n sign = (sample >> 8) & 0x80;\n if (sign !== 0) sample = -sample;\n sample = sample + BIAS;\n if (sample > CLIP) sample = CLIP;\n exponent = encodeTable[(sample>>7) & 0xFF];\n mantissa = (sample >> (exponent+3)) & 0x0F;\n muLawSample = ~(sign | (exponent << 4) | mantissa);\n \n return muLawSample;\n}\n\nclass RawAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n \n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.isMuted = false;\n this.buffer = []; // Initialize an empty buffer\n this.bufferSize = data.sampleRate / 4;\n this.format = data.format;\n\n if (globalThis.LibSampleRate && sampleRate !== data.sampleRate) {\n globalThis.LibSampleRate.create(1, sampleRate, data.sampleRate).then(resampler => {\n this.resampler = resampler;\n });\n }\n break;\n case "setMuted":\n this.isMuted = data.isMuted;\n break;\n }\n };\n }\n process(inputs) {\n if (!this.buffer) {\n return true;\n }\n \n const input = inputs[0]; // Get the first input node\n if (input.length > 0) {\n let channelData = input[0]; // Get the first channel\'s data\n\n // Resample the audio if necessary\n if (this.resampler) {\n channelData = this.resampler.full(channelData);\n }\n\n // Add channel data to the buffer\n this.buffer.push(...channelData);\n // Get max volume \n let sum = 0.0;\n for (let i = 0; i < channelData.length; i++) {\n sum += channelData[i] * channelData[i];\n }\n const maxVolume = Math.sqrt(sum / channelData.length);\n // Check if buffer size has reached or exceeded the threshold\n if (this.buffer.length >= this.bufferSize) {\n const float32Array = this.isMuted \n ? new Float32Array(this.buffer.length)\n : new Float32Array(this.buffer);\n\n let encodedArray = this.format === "ulaw"\n ? new Uint8Array(float32Array.length)\n : new Int16Array(float32Array.length);\n\n // Iterate through the Float32Array and convert each sample to PCM16\n for (let i = 0; i < float32Array.length; i++) {\n // Clamp the value to the range [-1, 1]\n let sample = Math.max(-1, Math.min(1, float32Array[i]));\n\n // Scale the sample to the range [-32768, 32767]\n let value = sample < 0 ? sample * 32768 : sample * 32767;\n if (this.format === "ulaw") {\n value = encodeSample(Math.round(value));\n }\n\n encodedArray[i] = value;\n }\n\n // Send the buffered data to the main script\n this.port.postMessage([encodedArray, maxVolume]);\n\n // Clear the buffer after sending\n this.buffer = [];\n }\n }\n return true; // Continue processing\n }\n}\nregisterProcessor("raw-audio-processor", RawAudioProcessor);\n');class y extends r{constructor(e,t,n,o,s={}){super(s),this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.room=void 0,this.isConnected=!1,this.audioEventId=1,this.audioCaptureContext=null,this.room=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.setupRoomEventListeners()}static async create(n){let o;if("conversationToken"in n&&n.conversationToken)o=n.conversationToken;else{if(!("agentId"in n)||!n.agentId)throw new Error("Either conversationToken or agentId is required for WebRTC connection");try{var s,a,i;const e=(null==(s=n.overrides)||null==(s=s.client)?void 0:s.version)||l,t=(null==(a=n.overrides)||null==(a=a.client)?void 0:a.source)||"js_sdk",c=`${r=null!=(i=n.origin)?i:"https://api.elevenlabs.io",r.replace(/^wss:\/\//,"https://")}/v1/convai/conversation/token?agent_id=${n.agentId}&source=${t}&version=${e}`,u=await fetch(c);if(!u.ok)throw new Error(`ElevenLabs API returned ${u.status} ${u.statusText}`);if(o=(await u.json()).token,!o)throw new Error("No conversation token received from API")}catch(e){let t=e instanceof Error?e.message:String(e);throw e instanceof Error&&e.message.includes("401")&&(t="Your agent has authentication enabled, but no signed URL or conversation token was provided."),new Error(`Failed to fetch conversation token for agent ${n.agentId}: ${t}`)}}var r;const u=new e;try{const e=`room_${Date.now()}`,s=c("pcm_48000"),a=c("pcm_48000"),i=new y(u,e,s,a,n),r=n.livekitUrl||"wss://livekit.rtc.elevenlabs.io";var p;await u.connect(r,o),await new Promise(e=>{if(i.isConnected)e();else{const n=()=>{u.off(t.Connected,n),e()};u.on(t.Connected,n)}}),u.name&&(i.conversationId=(null==(p=u.name.match(/(conv_[a-zA-Z0-9]+)/))?void 0:p[0])||u.name),await u.localParticipant.setMicrophoneEnabled(!0);const l=h(n);return i.debug({type:d,message:l}),await i.sendMessage(l),i}catch(e){throw await u.disconnect(),e}}setupRoomEventListeners(){var e=this;this.room.on(t.Connected,async function(){e.isConnected=!0,console.info("WebRTC room connected")}),this.room.on(t.Disconnected,e=>{this.isConnected=!1,this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:null==e?void 0:e.toString()})})}),this.room.on(t.ConnectionStateChanged,e=>{e===n.Disconnected&&(this.isConnected=!1,this.disconnect({reason:"error",message:`LiveKit connection state changed to ${e}`,context:new Event("connection_state_changed")}))}),this.room.on(t.DataReceived,(e,t)=>{try{const t=JSON.parse((new TextDecoder).decode(e));if("audio"===t.type)return;u(t)?this.handleMessage(t):console.warn("Invalid socket event received:",t)}catch(t){console.warn("Failed to parse incoming data message:",t),console.warn("Raw payload:",(new TextDecoder).decode(e))}}),this.room.on(t.TrackSubscribed,async function(t,n,s){if(t.kind===o.Kind.Audio&&s.identity.includes("agent")){const n=t,o=n.attach();o.autoplay=!0,o.controls=!1,o.style.display="none",document.body.appendChild(o),await e.setupAudioCapture(n)}}),this.room.on(t.ActiveSpeakersChanged,async function(t){e.updateMode(t.length>0&&t[0].identity.startsWith("agent")?"speaking":"listening")})}close(){if(this.isConnected){try{this.room.localParticipant.audioTrackPublications.forEach(e=>{e.track&&e.track.stop()})}catch(e){console.warn("Error stopping local tracks:",e)}this.audioCaptureContext&&(this.audioCaptureContext.close().catch(e=>{console.warn("Error closing audio capture context:",e)}),this.audioCaptureContext=null),this.room.disconnect()}}async sendMessage(e){if(this.isConnected&&this.room.localParticipant){if(!("user_audio_chunk"in e))try{const t=(new TextEncoder).encode(JSON.stringify(e));await this.room.localParticipant.publishData(t,{reliable:!0})}catch(t){this.debug({type:"send_message_error",message:{message:e,error:t}}),console.error("Failed to send message via WebRTC:",t)}}else console.warn("Cannot send message: room not connected or no local participant")}getRoom(){return this.room}async setMicMuted(e){if(!this.isConnected||!this.room.localParticipant)return void console.warn("Cannot set microphone muted: room not connected or no local participant");const t=this.room.localParticipant.getTrackPublication(o.Source.Microphone);if(null!=t&&t.track)try{e?await t.track.mute():await t.track.unmute()}catch(t){await this.room.localParticipant.setMicrophoneEnabled(!e)}else await this.room.localParticipant.setMicrophoneEnabled(!e)}async setupAudioCapture(e){try{const t=new AudioContext;this.audioCaptureContext=t;const n=new MediaStream([e.mediaStreamTrack]),o=t.createMediaStreamSource(n);await w(t.audioWorklet);const s=new AudioWorkletNode(t,"raw-audio-processor");s.port.postMessage({type:"setFormat",format:this.outputFormat.format,sampleRate:this.outputFormat.sampleRate}),s.port.onmessage=e=>{const[t,n]=e.data;if(n>.01){const e=m(t.buffer),n=this.audioEventId++;this.handleMessage({type:"audio",audio_event:{audio_base_64:e,event_id:n}})}},o.connect(s)}catch(e){console.warn("Failed to set up audio capture:",e)}}}async function b(e){const t=function(e){return e.connectionType?e.connectionType:"conversationToken"in e&&e.conversationToken?"webrtc":"websocket"}(e);switch(t){case"websocket":return p.create(e);case"webrtc":return y.create(e);default:throw new Error(`Unknown connection type: ${t}`)}}function k(){return["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document}async function _(e={default:0,android:3e3}){let t=e.default;var n;if(/android/i.test(navigator.userAgent))t=null!=(n=e.android)?n:t;else if(k()){var o;t=null!=(o=e.ios)?o:t}t>0&&await new Promise(e=>setTimeout(e,t))}class C extends i{static async startSession(e){const t=i.getFullOptions(e);t.onStatusChange&&t.onStatusChange({status:"connecting"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:!1}),t.onModeChange&&t.onModeChange({mode:"listening"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:!1});let n=null;try{return await _(t.connectionDelay),n=await b(e),new C(t,n)}catch(e){var o;throw t.onStatusChange&&t.onStatusChange({status:"disconnected"}),null==(o=n)||o.close(),e}}}class M{static async create({sampleRate:e,format:t,preferHeadphonesForIosDevices:n}){let o=null,a=null;try{const i={sampleRate:{ideal:e},echoCancellation:!0,noiseSuppression:!0};if(k()&&n){const e=(await window.navigator.mediaDevices.enumerateDevices()).find(e=>"audioinput"===e.kind&&["airpod","headphone","earphone"].find(t=>e.label.toLowerCase().includes(t)));e&&(i.deviceId={ideal:e.deviceId})}const r=navigator.mediaDevices.getSupportedConstraints().sampleRate;o=new window.AudioContext(r?{sampleRate:e}:{});const c=o.createAnalyser();r||await o.audioWorklet.addModule("https://cdn.jsdelivr.net/npm/@alexanderolsen/libsamplerate-js@2.1.2/dist/libsamplerate.worklet.js"),await w(o.audioWorklet);const l=s({voiceIsolation:!0},i);a=await navigator.mediaDevices.getUserMedia({audio:l});const u=o.createMediaStreamSource(a),d=new AudioWorkletNode(o,"raw-audio-processor");return d.port.postMessage({type:"setFormat",format:t,sampleRate:e}),u.connect(c),c.connect(d),await o.resume(),new M(o,c,d,a)}catch(e){var i,r;throw null==(i=a)||i.getTracks().forEach(e=>e.stop()),null==(r=o)||r.close(),e}}constructor(e,t,n,o){this.context=void 0,this.analyser=void 0,this.worklet=void 0,this.inputStream=void 0,this.context=e,this.analyser=t,this.worklet=n,this.inputStream=o}async close(){this.inputStream.getTracks().forEach(e=>e.stop()),await this.context.close()}setMuted(e){this.worklet.port.postMessage({type:"setMuted",isMuted:e})}}const S=f("audio-concat-processor",'\nconst decodeTable = [0,132,396,924,1980,4092,8316,16764];\n\nexport function decodeSample(muLawSample) {\n let sign;\n let exponent;\n let mantissa;\n let sample;\n muLawSample = ~muLawSample;\n sign = (muLawSample & 0x80);\n exponent = (muLawSample >> 4) & 0x07;\n mantissa = muLawSample & 0x0F;\n sample = decodeTable[exponent] + (mantissa << (exponent+3));\n if (sign !== 0) sample = -sample;\n\n return sample;\n}\n\nclass AudioConcatProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffers = []; // Initialize an empty buffer\n this.cursor = 0;\n this.currentBuffer = null;\n this.wasInterrupted = false;\n this.finished = false;\n \n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.format = data.format;\n break;\n case "buffer":\n this.wasInterrupted = false;\n this.buffers.push(\n this.format === "ulaw"\n ? new Uint8Array(data.buffer)\n : new Int16Array(data.buffer)\n );\n break;\n case "interrupt":\n this.wasInterrupted = true;\n break;\n case "clearInterrupted":\n if (this.wasInterrupted) {\n this.wasInterrupted = false;\n this.buffers = [];\n this.currentBuffer = null;\n }\n }\n };\n }\n process(_, outputs) {\n let finished = false;\n const output = outputs[0][0];\n for (let i = 0; i < output.length; i++) {\n if (!this.currentBuffer) {\n if (this.buffers.length === 0) {\n finished = true;\n break;\n }\n this.currentBuffer = this.buffers.shift();\n this.cursor = 0;\n }\n\n let value = this.currentBuffer[this.cursor];\n if (this.format === "ulaw") {\n value = decodeSample(value);\n }\n output[i] = value / 32768;\n this.cursor++;\n\n if (this.cursor >= this.currentBuffer.length) {\n this.currentBuffer = null;\n }\n }\n\n if (this.finished !== finished) {\n this.finished = finished;\n this.port.postMessage({ type: "process", finished });\n }\n\n return true; // Continue processing\n }\n}\n\nregisterProcessor("audio-concat-processor", AudioConcatProcessor);\n');class F{static async create({sampleRate:e,format:t}){let n=null;try{n=new AudioContext({sampleRate:e});const o=n.createAnalyser(),s=n.createGain();s.connect(o),o.connect(n.destination),await S(n.audioWorklet);const a=new AudioWorkletNode(n,"audio-concat-processor");return a.port.postMessage({type:"setFormat",format:t}),a.connect(s),await n.resume(),new F(n,o,s,a)}catch(e){var o;throw null==(o=n)||o.close(),e}}constructor(e,t,n,o){this.context=void 0,this.analyser=void 0,this.gain=void 0,this.worklet=void 0,this.context=e,this.analyser=t,this.gain=n,this.worklet=o}async close(){await this.context.close()}}class I extends i{static async startSession(e){var t;const n=i.getFullOptions(e);n.onStatusChange&&n.onStatusChange({status:"connecting"}),n.onCanSendFeedbackChange&&n.onCanSendFeedbackChange({canSendFeedback:!1});let o=null,a=null,r=null,c=null,l=null;if(null==(t=e.useWakeLock)||t)try{l=await navigator.wakeLock.request("screen")}catch(e){}try{var u;return c=await navigator.mediaDevices.getUserMedia({audio:!0}),await _(n.connectionDelay),a=await b(e),[o,r]=await Promise.all([M.create(s({},a.inputFormat,{preferHeadphonesForIosDevices:e.preferHeadphonesForIosDevices})),F.create(a.outputFormat)]),null==(u=c)||u.getTracks().forEach(e=>e.stop()),c=null,new I(n,a,o,r,l)}catch(e){var d,h,p,m;n.onStatusChange&&n.onStatusChange({status:"disconnected"}),null==(d=c)||d.getTracks().forEach(e=>e.stop()),null==(h=a)||h.close(),await(null==(p=o)?void 0:p.close()),await(null==(m=r)?void 0:m.close());try{var g;await(null==(g=l)?void 0:g.release()),l=null}catch(e){}throw e}}constructor(e,t,n,o,s){super(e,t),this.input=void 0,this.output=void 0,this.wakeLock=void 0,this.inputFrequencyData=void 0,this.outputFrequencyData=void 0,this.onInputWorkletMessage=e=>{"connected"===this.status&&this.connection.sendMessage({user_audio_chunk:m(e.data[0].buffer)})},this.onOutputWorkletMessage=({data:e})=>{"process"===e.type&&this.updateMode(e.finished?"listening":"speaking")},this.addAudioBase64Chunk=e=>{this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"}),this.output.worklet.port.postMessage({type:"buffer",buffer:g(e)})},this.fadeOutAudio=()=>{this.updateMode("listening"),this.output.worklet.port.postMessage({type:"interrupt"}),this.output.gain.gain.exponentialRampToValueAtTime(1e-4,this.output.context.currentTime+2),setTimeout(()=>{this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"})},2e3)},this.calculateVolume=e=>{if(0===e.length)return 0;let t=0;for(let n=0;n<e.length;n++)t+=e[n]/255;return t/=e.length,t<0?0:t>1?1:t},this.input=n,this.output=o,this.wakeLock=s,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.output.worklet.port.onmessage=this.onOutputWorkletMessage}async handleEndSession(){await super.handleEndSession();try{var e;await(null==(e=this.wakeLock)?void 0:e.release()),this.wakeLock=null}catch(e){}await this.input.close(),await this.output.close()}handleInterruption(e){super.handleInterruption(e),this.fadeOutAudio()}handleAudio(e){var t,n;this.lastInterruptTimestamp<=e.audio_event.event_id&&(null==(t=(n=this.options).onAudio)||t.call(n,e.audio_event.audio_base_64),this.connection instanceof y||this.addAudioBase64Chunk(e.audio_event.audio_base_64),this.currentEventId=e.audio_event.event_id,this.updateCanSendFeedback(),this.updateMode("speaking"))}setMicMuted(e){this.connection instanceof y?this.connection.setMicMuted(e):this.input.setMuted(e)}getInputByteFrequencyData(){return null!=this.inputFrequencyData||(this.inputFrequencyData=new Uint8Array(this.input.analyser.frequencyBinCount)),this.input.analyser.getByteFrequencyData(this.inputFrequencyData),this.inputFrequencyData}getOutputByteFrequencyData(){return null!=this.outputFrequencyData||(this.outputFrequencyData=new Uint8Array(this.output.analyser.frequencyBinCount)),this.output.analyser.getByteFrequencyData(this.outputFrequencyData),this.outputFrequencyData}getInputVolume(){return this.calculateVolume(this.getInputByteFrequencyData())}getOutputVolume(){return this.calculateVolume(this.getOutputByteFrequencyData())}}function x(e,t,n="https://api.elevenlabs.io"){return fetch(`${n}/v1/convai/conversations/${e}/feedback`,{method:"POST",body:JSON.stringify({feedback:t?"like":"dislike"}),headers:{"Content-Type":"application/json"}})}class D extends i{static startSession(e){return e.textOnly?C.startSession(e):I.startSession(e)}}export{D as Conversation,y as WebRTCConnection,p as WebSocketConnection,b as createConnection,x as postOverallFeedback};
2
2
  //# sourceMappingURL=lib.modern.js.map