perso-interactive-sdk-web 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,7 @@ pnpm add perso-interactive-sdk-web
17
17
 
18
18
  ## Usage
19
19
 
20
- > 📖 **Looking for step-by-step examples?** See the [Example Guide](./example-guide/en/README.md) for annotated code snippets covering LLM, TTS, STT, STF, and full pipeline patterns.
20
+ > 📖 **Looking for step-by-step examples?** See the [Example Guide](https://github.com/perso-ai/perso-interactive-sdk-web/blob/master/packages/perso-interactive-sdk/example-guide/en/README.md) for annotated code snippets covering LLM, TTS, STT, STF, and full pipeline patterns.
21
21
 
22
22
  The SDK provides two entry points:
23
23
 
@@ -95,7 +95,11 @@ const sessionId = await createSessionId(apiServer, apiKey, {
95
95
  });
96
96
 
97
97
  const session = await createSession(apiServer, sessionId, 1920, 1080, []);
98
- session.setSrc(document.getElementById("video"));
98
+
99
+ const videoEl = document.getElementById("video");
100
+ if (videoEl instanceof HTMLVideoElement) {
101
+ session.setSrc(videoEl);
102
+ }
99
103
  ```
100
104
 
101
105
  ### Client-side (`perso-interactive-sdk-web/client`)
@@ -120,7 +124,10 @@ const sessionId = await fetch("/api/session", { method: "POST" })
120
124
  const session = await createSession(apiServer, sessionId, 1920, 1080, []);
121
125
 
122
126
  // Bind to video element
123
- session.setSrc(videoElement);
127
+ const videoEl = document.getElementById("video");
128
+ if (videoEl instanceof HTMLVideoElement) {
129
+ session.setSrc(videoEl);
130
+ }
124
131
 
125
132
  // Subscribe to chat states
126
133
  session.subscribeChatStates((states) => {
@@ -131,15 +138,56 @@ session.subscribeChatStates((states) => {
131
138
  session.subscribeChatLog((chatLog) => {
132
139
  console.log("Chat log:", chatLog);
133
140
  });
141
+ ```
134
142
 
135
- // Send a message
136
- session.processChat("Hello!");
143
+ #### Chat (Recommended) — processLLM → processTTS → processSTF
144
+
145
+ Full pipeline with individual step control. Use this when you need to handle each stage (LLM response, TTS audio, avatar animation) separately.
146
+
147
+ ```typescript
148
+ // 1. Get LLM response
149
+ const llmGenerator = session.processLLM({ message: "Hello!" });
150
+ let llmResponse = "";
151
+ for await (const chunk of llmGenerator) {
152
+ if (chunk.type === "message" && chunk.finish) {
153
+ llmResponse = chunk.message;
154
+ }
155
+ }
137
156
 
138
- // Voice chat using STT
157
+ // 2. Convert text to speech
158
+ const audioBlob = await session.processTTS(llmResponse);
159
+
160
+ // 3. Animate avatar with audio
161
+ if (audioBlob) {
162
+ await session.processSTF(audioBlob, "wav", llmResponse);
163
+ }
164
+ ```
165
+
166
+ With voice input (STT → LLM → TTS → STF):
167
+
168
+ ```typescript
139
169
  await session.startProcessSTT();
140
170
  const text = await session.stopProcessSTT();
141
- session.processChat(text);
171
+ // Pass `text` to the processLLM pipeline above
172
+ ```
142
173
 
174
+ #### Chat (Simple) — processChat
175
+
176
+ All-in-one call that runs LLM → TTS → STF internally. Use this when you don't need control over individual steps.
177
+
178
+ ```typescript
179
+ session.processChat("Hello!");
180
+ ```
181
+
182
+ #### Direct Speech — processTTSTF
183
+
184
+ Avatar speaks text directly without LLM. Useful for scripted greetings, announcements, or guided messages.
185
+
186
+ ```typescript
187
+ session.processTTSTF("Welcome! How can I help you today?");
188
+ ```
189
+
190
+ ```typescript
143
191
  // Stop session
144
192
  session.stopSession();
145
193
  ```
@@ -200,7 +248,10 @@ For direct browser usage via `<script>` tag without a bundler. The SDK exposes a
200
248
  []
201
249
  );
202
250
 
203
- session.setSrc(document.getElementById("video"));
251
+ const videoEl = document.getElementById("video");
252
+ if (videoEl instanceof HTMLVideoElement) {
253
+ session.setSrc(videoEl);
254
+ }
204
255
  }
205
256
 
206
257
  start();
@@ -211,7 +262,7 @@ For direct browser usage via `<script>` tag without a bundler. The SDK exposes a
211
262
 
212
263
  ### Example Guide
213
264
 
214
- > 📖 **Example Guide**: [English](./example-guide/en/README.md)
265
+ > 📖 **Example Guide**: [English](https://github.com/perso-ai/perso-interactive-sdk-web/blob/master/packages/perso-interactive-sdk/example-guide/en/README.md)
215
266
 
216
267
  ## API Reference
217
268
 
@@ -288,7 +339,7 @@ For direct browser usage via `<script>` tag without a bundler. The SDK exposes a
288
339
  | ------------------------ | -------------- | ---------------------------------------------- |
289
340
  | `lastRecordedAudioFile` | `File \| null` | Last recorded WAV audio file from STT |
290
341
 
291
- For detailed API documentation, see [api-docs.md](../../core/api-docs.md).
342
+ For detailed API documentation, see [api-docs.md](https://github.com/perso-ai/perso-interactive-sdk-web/blob/master/core/api-docs.md).
292
343
 
293
344
  ## License
294
345
 
@@ -1 +1 @@
1
- "use strict";var t,e,s=require("emoji-regex");class a extends Error{constructor(){super("WebRTC connection timeout")}}class r extends Error{errorCode;code;detail;attr;constructor(t,e,s,a){let r;r=null!=a?`${t}:${a}_${s}`:`${t}:${s}`,super(r),this.errorCode=t,this.code=e,this.detail=s,this.attr=a}}class n extends Error{underlyingError;constructor(t){super(),this.underlyingError=t}}class o extends Error{description;constructor(t){super(),this.description=t}}class i extends Error{underlyingError;constructor(t){super(`STT Error: ${t.detail}`),this.underlyingError=t}}class l extends Error{underlyingError;constructor(t){super(t.message),this.underlyingError=t}}class c extends Error{description;constructor(t){super(`TTS decode error: ${t}`),this.description=t}}!function(t){t.LLM="LLM",t.TTS="TTS",t.STT="STT",t.STF_ONPREMISE="STF_ONPREMISE",t.STF_WEBRTC="STF_WEBRTC"}(t||(t={})),function(t){t.SESSION_START="SESSION_START",t.SESSION_DURING="SESSION_DURING",t.SESSION_LOG="SESSION_LOG",t.SESSION_END="SESSION_END",t.SESSION_ERROR="SESSION_ERROR",t.SESSION_TTS="SESSION_TTS",t.SESSION_STT="SESSION_STT",t.SESSION_LLM="SESSION_LLM"}(e||(e={}));class h{static async getLLMs(t,e){const s=fetch(`${t}/api/v1/settings/llm_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getModelStyles(t,e){const s=fetch(`${t}/api/v1/settings/modelstyle/?platform_type=webrtc`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getBackgroundImages(t,e){const s=fetch(`${t}/api/v1/background_image/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getTTSs(t,e){const s=fetch(`${t}/api/v1/settings/tts_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSTTs(t,e){const s=fetch(`${t}/api/v1/settings/stt_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async makeTTS(t,{sessionId:e,text:s}){const a=await fetch(`${t}/api/v1/session/${e}/tts/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:s})});return await this.parseJson(a)}static async getPrompts(t,e){const s=fetch(`${t}/api/v1/prompt/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getDocuments(t,e){const s=fetch(`${t}/api/v1/document/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getMcpServers(t,e){const s=fetch(`${t}/api/v1/settings/mcp_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSessionInfo(t,e){const s=fetch(`${t}/api/v1/session/${e}/`,{method:"GET"}),a=await s;return await this.parseJson(a)}static async sessionEvent(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await this.parseJson(a)}static async makeSTT(t,e,s,a){const r=new FormData;r.append("audio",s),a&&r.append("language",a);const n=await fetch(`${t}/api/v1/session/${e}/stt/`,{method:"POST",body:r});return await this.parseJson(n)}static async makeLLM(t,e,s,a){const n=await fetch(`${t}/api/v1/session/${e}/llm/v2/`,{body:JSON.stringify(s),headers:{"Content-Type":"application/json"},method:"POST",signal:a});if(!n.ok){const t=await n.json(),e=t.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${n.status} with no error details`,attr:null};throw new r(n.status,e.code,e.detail,e.attr)}return n.body.getReader()}static async getIceServers(t,e){const s=await fetch(`${t}/api/v1/session/${e}/ice-servers/`,{method:"GET"});return(await this.parseJson(s)).ice_servers}static async exchangeSDP(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/exchange/`,{body:JSON.stringify({client_sdp:s}),headers:{"Content-Type":"application/json"},method:"POST"});return(await this.parseJson(a)).server_sdp}static async parseJson(t){const e=await t.json();if(t.ok)return e;{const s=e.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${t.status} with no error details`,attr:null};throw new r(t.status,s.code,s.detail,s.attr)}}}class d extends Error{constructor(t){super(`WAV parse error: ${t}`),this.name="WavParseError"}}function u(t,e,s=1){const a=2*t.length,r=new ArrayBuffer(44+a),n=new DataView(r);g(n,0,"RIFF"),n.setUint32(4,36+a,!0),g(n,8,"WAVE"),g(n,12,"fmt "),n.setUint32(16,16,!0),n.setUint16(20,1,!0),n.setUint16(22,s,!0),n.setUint32(24,e,!0),n.setUint32(28,e*s*2,!0),n.setUint16(32,2*s,!0),n.setUint16(34,16,!0),g(n,36,"data"),n.setUint32(40,a,!0);let o=44;for(let e=0;e<t.length;e++){const s=Math.max(-1,Math.min(1,t[e]));n.setInt16(o,s<0?32768*s:32767*s,!0),o+=2}return r}function p(t,e,s){let a="";for(let r=0;r<s;r++)a+=String.fromCharCode(t.getUint8(e+r));return a}function g(t,e,s){for(let a=0;a<s.length;a++)t.setUint8(e+a,s.charCodeAt(a))}function S(t,e,s){switch(s){case 8:return(t.getUint8(e)-128)/128;case 16:return t.getInt16(e,!0)/32768;case 24:{const s=t.getUint8(e),a=t.getUint8(e+1),r=t.getUint8(e+2)<<16|a<<8|s;return(r>8388607?r-16777216:r)/8388608}case 32:return t.getInt32(e,!0)/2147483648;default:return 0}}class m extends Error{constructor(t){super(`Audio resample error: ${t}`),this.name="AudioResampleError"}}async function f(t,e,s,a=1){if(0===t.length)throw new m("Cannot resample empty audio data");if(e<=0||s<=0)throw new m(`Invalid sample rate: original=${e}, target=${s}`);if(e===s)return t;try{const r=t.length/e,n=Math.ceil(r*s),o=new OfflineAudioContext(a,t.length,e).createBuffer(a,t.length,e);o.getChannelData(0).set(t);const i=new OfflineAudioContext(a,n,s),l=i.createBufferSource();l.buffer=o,l.connect(i.destination),l.start(0);return(await i.startRendering()).getChannelData(0)}catch(t){const a=t instanceof Error?t.message:String(t);throw new m(`Failed to resample audio from ${e}Hz to ${s}Hz: ${a}`)}}const w=16e3;async function y(t,e=!0){let s;try{const e=atob(t),a=new Array(e.length);for(let t=0;t<e.length;t++)a[t]=e.charCodeAt(t);s=new Uint8Array(a).buffer}catch{throw new c("Invalid Base64 audio data")}const a=function(t){const e=new Uint8Array(t);if(e.length>=4){const t=String.fromCharCode(e[0],e[1],e[2],e[3]);if("RIFF"===t)return"audio/wav";if(t.startsWith("ID3"))return"audio/mpeg";if(255===e[0]&&!(224&~e[1]))return"audio/mpeg"}return"audio/wav"}(s);if(!e)return new Blob([s],{type:a});try{const t=await async function(t,e){if("audio/wav"===e){const e=function(t){const e=new DataView(t);if(t.byteLength<44)throw new d("File too small to be a valid WAV");if("RIFF"!==p(e,0,4))throw new d("Missing RIFF header");if("WAVE"!==p(e,8,4))throw new d("Missing WAVE format identifier");let s=12,a=!1,r=0,n=0,o=0,i=0;for(;s<t.byteLength-8;){const l=p(e,s,4),c=e.getUint32(s+4,!0);if("fmt "===l){if(s+24>t.byteLength)throw new d("fmt chunk extends beyond file");r=e.getUint16(s+8,!0),n=e.getUint16(s+10,!0),o=e.getUint32(s+12,!0),i=e.getUint16(s+22,!0),a=!0,s+=8+c;break}const h=s+8+c;if(h<=s)break;s=h}if(!a)throw new d("Missing fmt chunk");if(1!==r)throw new d(`Unsupported audio format: ${r} (only PCM format 1 is supported)`);if(0===n||n>8)throw new d(`Invalid channel count: ${n}`);if(0===o||o>192e3)throw new d(`Invalid sample rate: ${o}`);if(![8,16,24,32].includes(i))throw new d(`Unsupported bits per sample: ${i}`);let l=-1,c=0;for(;s<t.byteLength-8;){const t=p(e,s,4),a=e.getUint32(s+4,!0);if("data"===t){l=s+8,c=a;break}const r=s+8+a;if(r<=s)break;s=r}if(-1===l)throw new d("Missing data chunk");const h=t.byteLength-l,u=Math.min(c,h),g=i/8,m=Math.floor(u/(g*n)),f=new Float32Array(m*n);let w=0;for(let s=0;s<m*n;s++){const a=l+s*g;if(a+g>t.byteLength)break;f[w++]=Math.max(-1,Math.min(1,S(e,a,i)))}if(2===n){const t=new Float32Array(m);for(let e=0;e<m;e++)t[e]=(f[2*e]+f[2*e+1])/2;return{sampleRate:o,channels:1,bitsPerSample:i,samples:t}}return{sampleRate:o,channels:n,bitsPerSample:i,samples:f.slice(0,w)}}(t);if(e.sampleRate===w)return{samples:e.samples,sampleRate:e.sampleRate};return{samples:await f(e.samples,e.sampleRate,w,e.channels),sampleRate:w}}const s=new AudioContext({sampleRate:w});try{const e=await s.decodeAudioData(t.slice(0));if(e.sampleRate===w)return{samples:e.getChannelData(0),sampleRate:w};return{samples:await f(e.getChannelData(0),e.sampleRate,w,1),sampleRate:w}}finally{await s.close()}}(s,a),e=u(t.samples,w,1);return new Blob([e],{type:"audio/wav"})}catch{return new Blob([s],{type:a})}}var T;exports.ChatState=void 0,(T=exports.ChatState||(exports.ChatState={})).RECORDING="RECORDING",T.LLM="LLM",T.ANALYZING="ANALYZING",T.SPEAKING="SPEAKING",T.TTS="TTS";class C{config;messageHistory=[];constructor(t){this.config=t}async*processLLM(t){if(0===t.message.length)throw new Error("Message cannot be empty");const e=t.tools??this.config.clientTools,s=e.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),a={newMessageHistory:[{role:"user",content:t.message}],allChunks:[],message:"",lastYieldedChunkCount:0,pendingToolCallsMessage:null,aborted:!1,streamingError:null};let i=0,l=[...this.messageHistory,...a.newMessageHistory];this.config.callbacks.onChatStateChange(exports.ChatState.LLM,null);try{for(;;){if(t.signal?.aborted)return void(a.allChunks.length>0&&(yield{type:"message",chunks:[...a.allChunks],message:a.message,finish:!0}));let c;try{c=await h.makeLLM(this.config.apiServer,this.config.sessionId,{messages:l,tools:s},t.signal)}catch(t){if(t instanceof r)return void(yield{type:"error",error:new n(t)});throw t}if(a.streamingError=null,yield*this.parseSSEStream(c,a,t),a.streamingError)return;if(a.aborted)return void(a.allChunks.length>0&&(yield{type:"message",chunks:[...a.allChunks],message:a.message,finish:!0}));if(null!=a.pendingToolCallsMessage){yield*this.executeToolCalls(a,e);const t=a.lastToolCallResults,s=t.length>0&&a.pendingToolCallsMessage.tool_calls.length!==t.length,r=t.some(t=>!t.chatTool.executeOnly);if(s||r){if(i++,i>=10)return void(yield{type:"error",error:new n(new o("Tool follow-up loop exceeded maximum rounds (10)"))});l=[...this.messageHistory,...a.newMessageHistory],a.pendingToolCallsMessage=null;continue}}return this.messageHistory.push(...a.newMessageHistory),void(yield{type:"message",chunks:[...a.allChunks],message:a.message,finish:!0})}}finally{this.config.callbacks.onChatStateChange(null,exports.ChatState.LLM)}}async*parseSSEStream(t,e,s){const a=new TextDecoder("utf-8");let r="",i="";e.pendingToolCallsMessage=null;const l=()=>e.allChunks.length>e.lastYieldedChunkCount?(e.lastYieldedChunkCount=e.allChunks.length,{type:"message",chunks:[...e.allChunks],message:e.message,finish:!1}):null;for(;;){const{done:c,value:h}=await t.read();if(c)break;let d;for(r+=a.decode(h,{stream:!0});-1!==(d=r.indexOf("\n"));){if(s.signal?.aborted)return void(e.aborted=!0);const t=r.slice(0,d).trim();if(r=r.slice(d+1),!t.startsWith("data: {"))return e.streamingError=new n(new o("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let a;try{a=JSON.parse(t.slice(6).trim())}catch{return e.streamingError=new n(new o("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==a.status)return e.streamingError=new n(new o(a.reason)),void(yield{type:"error",error:e.streamingError});if(i.length>0&&"message"!=a.type){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}"message"!==a.type?"tool_call"!==a.type||null==a.tool_calls?"tool"!==a.role||"tool_call"===a.type&&e.newMessageHistory.push({role:a.role,type:a.type,content:a.content,tool_call_id:a.tool_call_id}):(e.newMessageHistory.push({role:"assistant",type:a.type,content:a.content,tool_calls:a.tool_calls}),e.pendingToolCallsMessage=a,yield{type:"tool_call",tool_calls:a.tool_calls}):(i+=a.content,e.message+=a.content,e.allChunks.push(a.content))}const u=l();u&&(yield u)}const c=r.trim();if(c.length>0){if(!c.startsWith("data: {"))return e.streamingError=new n(new o("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let t;try{t=JSON.parse(c.slice(6).trim())}catch{return e.streamingError=new n(new o("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==t.status)return e.streamingError=new n(new o(t.reason)),void(yield{type:"error",error:e.streamingError});if("message"===t.type)i+=t.content,e.message+=t.content,e.allChunks.push(t.content);else if("tool_call"===t.type&&null!=t.tool_calls){if(i.length>0){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}e.newMessageHistory.push({role:"assistant",type:t.type,content:t.content,tool_calls:t.tool_calls}),e.pendingToolCallsMessage=t,yield{type:"tool_call",tool_calls:t.tool_calls}}}i.length>0&&e.newMessageHistory.push({role:"assistant",type:"message",content:i})}async*executeToolCalls(t,e){const s=t=>{for(const s of e)if(s.name===t)return s;return null},a=[];for(const e of t.pendingToolCallsMessage.tool_calls){const t=s(e.function.name);null!=t&&a.push((async()=>{try{const s=await t.call(JSON.parse(e.function.arguments));return{toolCallId:e.id,chatTool:t,chatToolResult:s}}catch(s){return{toolCallId:e.id,chatTool:t,chatToolResult:{error:s.message}}}})())}const r=await Promise.all(a);t.lastToolCallResults=r;for(const e of r)t.newMessageHistory.push({role:"tool",content:JSON.stringify(e.chatToolResult),tool_call_id:e.toolCallId}),yield{type:"tool_result",tool_call_id:e.toolCallId,result:e.chatToolResult}}addToHistory(t){this.messageHistory.push(t)}getHistory(){return this.messageHistory}}const E=`data:application/javascript,${encodeURIComponent("\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.isRecording = true;\n \n // Listen for stop message from main thread\n this.port.onmessage = (event) => {\n if (event.data.type === 'stop') {\n this.isRecording = false;\n // Send confirmation back to main thread\n this.port.postMessage({ type: 'stopped' });\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (input && input.length > 0 && this.isRecording) {\n // Clone the audio data and send to main thread\n const channelData = new Float32Array(input[0]);\n this.port.postMessage({ type: 'audio', data: channelData });\n }\n // Return true to keep the processor alive until stopped\n return this.isRecording;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n")}`;class v{audioContext=null;mediaStream=null;workletNode=null;sourceNode=null;audioChunks=[];isRecordingState=!1;channels;targetSampleRate;constructor(t={}){this.channels=t.channels||1,this.targetSampleRate=t.targetSampleRate}async start(){if(this.isRecordingState)throw new Error("WavRecorder is already recording");if(this.mediaStream=await navigator.mediaDevices.getUserMedia({audio:!0}),this.audioContext=new AudioContext,"running"!==this.audioContext.state)try{await this.audioContext.resume()}catch(t){console.warn("WavRecorder: Failed to resume AudioContext:",t)}await this.audioContext.audioWorklet.addModule(E),this.sourceNode=this.audioContext.createMediaStreamSource(this.mediaStream),this.workletNode=new AudioWorkletNode(this.audioContext,"recorder-processor"),this.audioChunks=[],this.workletNode.port.onmessage=t=>{"audio"===t.data.type&&this.audioChunks.push(t.data.data)},this.sourceNode.connect(this.workletNode),this.workletNode.connect(this.audioContext.destination),this.isRecordingState=!0}async stop(){if(!this.isRecordingState)throw new Error("WavRecorder is not recording");this.isRecordingState=!1,await new Promise(t=>{this.workletNode.port.onmessage=e=>{"stopped"===e.data.type?t():"audio"===e.data.type&&this.audioChunks.push(e.data.data)},this.workletNode.port.postMessage({type:"stop"})}),this.workletNode?.disconnect(),this.sourceNode?.disconnect(),this.mediaStream?.getTracks().forEach(t=>t.stop());const t=this.audioChunks,e=this.audioContext.sampleRate;try{const s=t.reduce((t,e)=>t+e.length,0),a=new Float32Array(s);let r,n,o=0;for(const e of t)a.set(e,o),o+=e.length;this.targetSampleRate&&this.targetSampleRate!==e?(r=await f(a,e,this.targetSampleRate,this.channels),n=this.targetSampleRate):(r=a,n=e);const i=u(r,n,this.channels),l=new Blob([i],{type:"audio/wav"});return new File([l],"recording.wav",{type:"audio/wav"})}finally{await(this.audioContext?.close()),this.audioContext=null,this.mediaStream=null,this.workletNode=null,this.sourceNode=null,this.audioChunks=[]}}isRecording(){return this.isRecordingState}}class R extends EventTarget{pc;dc;streams=[];pingTime;pingIntervalId=null;constructor(t,e){super(),this.pc=t,this.dc=e,this.pingTime=Date.now()+3e3,this.pc.addEventListener("track",t=>{this.streams=this.streams.concat(t.streams)}),this.pc.addEventListener("connectionstatechange",()=>{"disconnected"!==this.pc.connectionState&&"failed"!==this.pc.connectionState||this.close()}),this.dc.onopen=()=>{this.pingIntervalId=setInterval(()=>{this.ping(),Date.now()-this.pingTime>5e3&&this.close()},1e3)},this.dc.onclose=()=>{null!=this.pingIntervalId&&clearInterval(this.pingIntervalId)},this.#t({live:!0,code:200,reason:"OK"}),this.setMessageCallback("ping",()=>{this.pingTime=Date.now()})}static async create(s,a,r,n,o){const i=await h.getSessionInfo(s,a);if(!(Array.isArray(i.capability)&&i.capability.some(e=>e.name===t.STF_ONPREMISE||e.name===t.STF_WEBRTC)))return await h.sessionEvent(s,a,e.SESSION_START),null;const l=await h.getIceServers(s,a);let c=await R.createPeerConnection(l),d=c.createDataChannel("message",{protocol:"message"}),u=new R(c,d);o?o.getTracks().forEach(function(t){c.addTrack(t,o)}):c.addTransceiver("audio",{direction:"recvonly"});const p=c.addTransceiver("video",{direction:"recvonly"}),g=RTCRtpReceiver.getCapabilities("video");null!=g&&p.setCodecPreferences(g.codecs);const S=await c.createOffer();await c.setLocalDescription(S);const m=await h.exchangeSDP(s,a,S);return await c.setRemoteDescription(m),await R.waitFor(()=>u.isReady(),100,50),u.changeSize(r,n),u}static async createPeerConnection(t){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:t})}static async waitFor(t,e,s){let r=0;if(await new Promise(a=>{const n=setInterval(()=>{r+=1,r>=s&&(clearInterval(n),a("bad")),t()&&(clearInterval(n),a("good"))},e)}),r>=s)throw new a}isReady(){return this.streams.length>0&&"open"===this.dc.readyState}#t(t){this.dispatchEvent(new CustomEvent("status",{detail:t}))}subscribeStatus(t){return this.addEventListener("status",t),()=>{this.removeEventListener("status",t)}}getStream(){return this.streams[0]}sendMessage(t,e){this.dc.send(JSON.stringify({type:t,data:e}))}ttstf(t){this.sendMessage("ttstf",{message:t})}static BACKPRESSURE_THRESHOLD=524288;sendFile(t,e=65536){return new Promise((s,a)=>{const r=this.pc.createDataChannel("file",{protocol:"file"});r.onerror=t=>{r.close(),a(new Error(`File channel error: ${t}`))},r.addEventListener("message",async n=>{try{if(0===n.data.length){const s=new Uint8Array(await t.arrayBuffer());let n=0;const o=()=>{for(;n<s.length;){if(r.bufferedAmount>R.BACKPRESSURE_THRESHOLD)return r.bufferedAmountLowThreshold=R.BACKPRESSURE_THRESHOLD/2,r.onbufferedamountlow=()=>{r.onbufferedamountlow=null,r.onclose=null,o()},void(r.onclose=()=>{r.onbufferedamountlow=null,a(new Error("File channel closed during transfer"))});r.send(s.slice(n,n+e)),n+=e}r.send(new Uint8Array(0))};o()}else r.close(),s(n.data)}catch(t){r.close(),a(t)}})})}async stf(t,e,s){const a=await this.sendFile(t);return this.sendMessage("stf",{message:s,file_ref:a,format:e}),a}recordStart(){this.sendMessage("record-start",{})}recordEndStt(t){this.sendMessage("record-end-stt",{language:t})}recordEndTranslate(t,e){this.sendMessage("record-end-translate",{src_lang:t,dst_lang:e})}changeSize(t,e){this.sendMessage("change-size",{width:t,height:e})}setTemplate(t,e){this.sendMessage("set-template",{model:t,dress:e})}clearBuffer(){this.sendMessage("clear-buffer",{})}ping(){this.sendMessage("ping",{})}setMessageCallback(t,e){const s=s=>{const a=JSON.parse(s.data);a.type===t&&e(a.data)};return this.dc.addEventListener("message",s),()=>{this.dc.removeEventListener("message",s)}}async tts(t,e=!0){return y(t,e)}close(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:408,reason:"Request Timeout"})}closeSelf(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:200,reason:"OK"})}}class I{apiServer;sessionId;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];llmProcessor;chatStateMap=new Map([[exports.ChatState.RECORDING,0],[exports.ChatState.LLM,0],[exports.ChatState.ANALYZING,0],[exports.ChatState.SPEAKING,0],[exports.ChatState.TTS,0]]);emojiRegex=s();sttRecorder=null;sttTimeoutHandle=null;sttTimeoutAudioFile=null;heartbeatIntervalId=null;legacyVoiceChatMode;stream;constructor(t,e,s,a,r){this.apiServer=t,this.sessionId=e,this.perso=s,this.clientTools=a,this.legacyVoiceChatMode=r?.legacyVoiceChatMode??!1,this.stream=r?.stream??null,this.resetChatState(),this.llmProcessor=new C({apiServer:t,sessionId:e,clientTools:a,callbacks:{onChatStateChange:(t,e)=>this.setChatState(t,e),onError:t=>this.setError(t),onChatLog:(t,e)=>this.addMessageToChatLog(t,e),onTTSTF:t=>this.processTTSTFInternal(t)}}),this.startHeartbeat(),s&&(s.subscribeStatus(t=>{!1===t.detail?.live&&this.stopHeartbeat()}),s.setMessageCallback("stf",t=>{if(this.chatStateMap.get(exports.ChatState.ANALYZING)||this.chatStateMap.get(exports.ChatState.SPEAKING))if(this.setChatState(exports.ChatState.SPEAKING,exports.ChatState.ANALYZING),null!==this.lastStfTimeoutHandle){clearTimeout(this.lastStfTimeoutHandle);let e=Date.now();this.stfTotalDuration+=t.duration+1e3-(e-this.stfTimeoutStartTime),this.stfTimeoutStartTime=e,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,exports.ChatState.SPEAKING)},this.stfTotalDuration)}else this.stfTimeoutStartTime=Date.now(),this.stfTotalDuration=t.duration+2e3,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,exports.ChatState.SPEAKING)},this.stfTotalDuration)}),s.setMessageCallback("stt",t=>{if(this.setChatState(null,exports.ChatState.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:t.text}));else{if(""===t.text)return;this.processChat(t.text)}}),s.setMessageCallback("stt-error",t=>{this.setChatState(null,exports.ChatState.ANALYZING)}))}llmJob=null;async processChat(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processLLM(t){return this.pipelineSuppressed=!1,this.llmProcessor.processLLM(t)}getMessageHistory(){return this.llmProcessor.getHistory()}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}async transcribeAudio(t,e){const s=t instanceof File?t:new File([t],"audio.wav",{type:t.type});try{return(await h.makeSTT(this.apiServer,this.sessionId,s,e)).text}catch(t){if(t instanceof r)throw new i(t);throw t}}async processSTF(t,e,s){if(!this.perso)throw new Error("processSTF requires WebRTC (STF mode)");return this.pipelineSuppressed?"":(this.setChatState(exports.ChatState.ANALYZING),await this.perso.stf(t,e,s))}async processTTS(t,e={}){const{resample:s=!0}=e,a=this.removeEmoji(t).trim();if(0===a.length)return;if(this.pipelineSuppressed)return;const n=/[.?!]$/.test(a)?a:a+".";this.setChatState(exports.ChatState.TTS,null);try{const{audio:t}=await h.makeTTS(this.apiServer,{sessionId:this.sessionId,text:n});return await y(t,s)}catch(t){t instanceof r||t instanceof c?this.setError(new l(t)):this.setError(t instanceof Error?t:new Error(String(t)))}finally{this.setChatState(null,exports.ChatState.TTS)}}startVoiceChat(){if(!this.perso)throw new Error("startVoiceChat requires WebRTC (STF mode)");return this.pipelineSuppressed=!1,this.setChatState(exports.ChatState.RECORDING),this.perso.recordStart()}stopVoiceChat(){if(!this.perso)throw new Error("stopVoiceChat requires WebRTC (STF mode)");this.setChatState(exports.ChatState.ANALYZING,exports.ChatState.RECORDING),this.perso.recordEndStt()}async startProcessSTT(t){if(this.sttRecorder?.isRecording())throw new Error("STT recording is already in progress");this.pipelineSuppressed=!1,this.setChatState(exports.ChatState.RECORDING);try{this.sttRecorder=new v({targetSampleRate:16e3}),await this.sttRecorder.start(),t&&t>0&&(this.sttTimeoutHandle=setTimeout(async()=>{if(this.sttTimeoutHandle=null,this.sttRecorder?.isRecording()){try{this.sttTimeoutAudioFile=await this.sttRecorder.stop()}catch{this.sttTimeoutAudioFile=null,this.setChatState(null,exports.ChatState.RECORDING)}this.sttRecorder=null}},t))}catch(t){throw this.setChatState(null,exports.ChatState.RECORDING),this.sttRecorder=null,t}}lastRecordedAudioFile=null;async stopProcessSTT(t){let e;if(this.sttTimeoutHandle&&(clearTimeout(this.sttTimeoutHandle),this.sttTimeoutHandle=null),this.setChatState(null,exports.ChatState.RECORDING),this.sttTimeoutAudioFile)e=this.sttTimeoutAudioFile,this.sttTimeoutAudioFile=null;else{if(!this.sttRecorder?.isRecording())throw this.sttRecorder?(this.sttRecorder=null,new Error("STT recording is not in progress")):new Error("STT recording has not been started");e=await this.sttRecorder.stop(),this.sttRecorder=null}this.lastRecordedAudioFile=e;try{return(await h.makeSTT(this.apiServer,this.sessionId,e,t)).text}catch(t){if(t instanceof r)throw new i(t);throw t}}isSTTRecording(){return(this.sttRecorder?.isRecording()??!1)||null!==this.sttTimeoutAudioFile}changeSize(t,e){this.perso?.changeSize(t,e)}async clearBuffer(){this.perso?.clearBuffer(),await this.clearLLMJob(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.pipelineSuppressed=!0,this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()??null}getRemoteStream(){return this.perso?.getStream()}getLocalStream(){return this.stream}stopSession(){this.close()}onClose(t){return this.perso?this.perso.subscribeStatus(e=>{null!=e.detail&&!1===e.detail.live&&t(200===e.detail.code)}):()=>{}}subscribeChatStates(t){const e=e=>{t(e.detail.status)};return this.chatStatesHandler.addEventListener("status",e),()=>{this.chatStatesHandler.removeEventListener("status",e)}}subscribeChatLog(t){const e=e=>{t(e.detail.chatLog)};return this.chatLogHandler.addEventListener("chatLog",e),()=>{this.chatLogHandler.removeEventListener("chatLog",e)}}setSttResultCallback(t){const e=e=>{t(e.detail)};return this.sttEventHandler=new EventTarget,this.sttEventHandler.addEventListener("stt",e),()=>{this.sttEventHandler?.removeEventListener("stt",e),this.sttEventHandler=null}}setErrorHandler(t){const e=e=>{t(e.detail.error)};return this.errorHandler.addEventListener("error",e),()=>{this.errorHandler.removeEventListener("error",e)}}getSessionId(){return this.sessionId}async processChatInternal(t){this.setChatState(exports.ChatState.LLM);const e=this.clientTools.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),s=new Array;null===t||(t instanceof Array?s.push(...t):"string"==typeof t&&s.push({role:"user",content:t}));const a=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...s],tools:e}),headers:{"Content-Type":"application/json"},method:"POST"});if(!a.ok){const t=await a.json(),e=new n(new r(a.status,t.errors[0].code,t.errors[0].detail,t.errors[0].attr));return this.setError(e),void this.setChatState(null,exports.ChatState.LLM)}const i=a.body?.getReader(),l=new TextDecoder("utf-8");let c="",h=null,d="";for(;;){const{done:t,value:e}=await i.read();if(t)break;let a;for(d+=l.decode(e,{stream:!0});-1!==(a=d.indexOf("\n"));){if(this.llmCancel)return c.length>0&&this.addMessageToChatLog(c,!1),void this.setChatState(null,exports.ChatState.LLM);const t=d.slice(0,a).trim();if(d=d.slice(a+1),!t.startsWith("data: {")){const t=new n(new o("Failed to parse SSE response"));return this.setError(t),void this.setChatState(null,exports.ChatState.LLM)}const e=JSON.parse(t.slice(6).trim());if("success"!==e.status){const t=new n(new o(e.reason));return this.setError(t),void this.setChatState(null,exports.ChatState.LLM)}c.length>0&&"message"!=e.type&&(s.push({role:"assistant",type:"message",content:c}),this.addMessageToChatLog(c,!1),c=""),"message"!==e.type?"tool_call"!==e.type||null==e.tool_calls?"tool"!==e.role||"tool_call"===e.type&&s.push({role:e.role,type:e.type,content:e.content,tool_call_id:e.tool_call_id}):(s.push({role:"assistant",type:e.type,content:e.content,tool_calls:e.tool_calls}),h=e):(c+=e.content,this.processTTSTFInternal(e.content))}}if(this.llmCancel)this.setChatState(null,exports.ChatState.LLM);else{if(null!=h){const t=[];for(const e of h.tool_calls){const s=this.getChatTool(this.clientTools,e.function.name);null!=s&&t.push(new Promise(async t=>{try{const a=await s.call(JSON.parse(e.function.arguments));t({toolCallId:e.id,chatTool:s,chatToolResult:a})}catch(a){t({toolCallId:e.id,chatTool:s,chatToolResult:{result:"error!"}})}}))}const e=await Promise.all(t);for(const t of e)s.push({role:"tool",content:JSON.stringify(t.chatToolResult),tool_call_id:t.toolCallId});const a=e.length>0&&h.tool_calls.length!==e.length,r=e.some(t=>!t.chatTool.executeOnly);a||r?await this.processChatInternal(s):this.messageHistory.push(...s)}else this.messageHistory.push(...s);this.setChatState(null,exports.ChatState.LLM)}}getChatTool(t,e){for(const s of t)if(s.name===e)return s;return null}llmCancel=!1;pipelineSuppressed=!1;async clearLLMJob(){null!=this.llmJob&&(this.llmCancel=!0,await this.llmJob,this.llmCancel=!1)}processTTSTFInternal(t){const e=this.removeEmoji(t).trim();0!==e.length&&this.perso&&(this.setChatState(exports.ChatState.ANALYZING),this.perso.ttstf(e))}addMessageToChatLog(t,e){this.chatLog=[{text:t,isUser:e,timestamp:new Date},...this.chatLog],this.chatLogHandler.dispatchEvent(new CustomEvent("chatLog",{detail:{chatLog:this.chatLog}}))}setChatState(t=null,e=null){const s=new Map(this.chatStateMap);function a(t){t===exports.ChatState.ANALYZING?s.set(t,(s.get(t)||0)+1):s.set(t,1)}function r(t){t===exports.ChatState.ANALYZING?s.set(t,Math.max((s.get(t)||0)-1,0)):s.set(t,0)}if(null!=t)if(t instanceof Array)for(let e of t)a(e);else a(t);if(null!=e)if(e instanceof Array)for(let t of e)r(t);else r(e);const n=this.exchangeChatStateMapToSet(this.chatStateMap),o=this.exchangeChatStateMapToSet(s);this.chatStateMap=s,this.isEqualChatStateMap(n,o)||this.dispatchChatState(o)}resetChatState(){this.chatStateMap=new Map([[exports.ChatState.RECORDING,0],[exports.ChatState.LLM,0],[exports.ChatState.ANALYZING,0],[exports.ChatState.SPEAKING,0],[exports.ChatState.TTS,0]]),this.dispatchChatState(this.exchangeChatStateMapToSet(this.chatStateMap))}exchangeChatStateMapToSet(t){const e=new Set;for(const s of t)s[1]>0&&e.add(s[0]);return e}dispatchChatState(t){this.chatStatesHandler.dispatchEvent(new CustomEvent("status",{detail:{status:t}}))}isEqualChatStateMap(t,e){if(t.size!==e.size)return!1;for(const s of t)if(t.has(s)!==e.has(s))return!1;return!0}setError(t){this.errorHandler.dispatchEvent(new CustomEvent("error",{detail:{error:t}}))}close(){this.stopHeartbeat(),this.perso?.closeSelf()}startHeartbeat(){const t=async()=>{try{await h.sessionEvent(this.apiServer,this.sessionId,e.SESSION_DURING),null!==this.heartbeatIntervalId&&(this.heartbeatIntervalId=setTimeout(t,1e4))}catch(t){t instanceof r?this.setError(t):this.setError(t instanceof Error?t:new Error(String(t))),this.close()}};this.heartbeatIntervalId=setTimeout(t,1e4)}stopHeartbeat(){null!==this.heartbeatIntervalId&&(clearTimeout(this.heartbeatIntervalId),this.heartbeatIntervalId=null)}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function L(t,e,s,a,r,n){if("boolean"!=typeof r){const n=await R.create(t,e,s,a);return new I(t,e,n,r)}const o=n??[];let i,l;if(r)i=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),l=()=>{};else{const t=new AudioContext,e=t.createOscillator();e.frequency.value=0;const s=t.createMediaStreamDestination();e.connect(s),e.start(),i=s.stream,l=()=>{e.stop(),e.disconnect(s),t.close()}}const c=await R.create(t,e,s,a,i);if(!c)return l(),new I(t,e,null,o);const h=new I(t,e,c,o,{stream:i,legacyVoiceChatMode:!0});return h.onClose(()=>{l()}),h}async function x(t,e){return await h.getLLMs(t,e)}async function M(t,e){return await h.getTTSs(t,e)}async function b(t,e){return await h.getSTTs(t,e)}async function N(t,e){return await h.getModelStyles(t,e)}async function A(t,e){return await h.getBackgroundImages(t,e)}async function _(t,e){return await h.getPrompts(t,e)}async function k(t,e){return await h.getDocuments(t,e)}async function O(t,e){return await h.getMcpServers(t,e)}exports.ApiError=r,exports.ChatTool=class{name;description;parameters;call;executeOnly;constructor(t,e,s,a,r=!1){this.name=t,this.description=e,this.parameters=s,this.call=a,this.executeOnly=r}},exports.LLMError=n,exports.LLMStreamingResponseError=o,exports.LlmProcessor=C,exports.STTError=i,exports.Session=I,exports.TTSDecodeError=c,exports.TTSError=l,exports.TTS_TARGET_SAMPLE_RATE=w,exports.WavRecorder=v,exports.createSession=async function(t,e,s,a,r,n){return"boolean"==typeof r?await L(t,e,s,a,r,n??[]):await L(t,e,s,a,r)},exports.createSessionId=async(e,s,a)=>{"undefined"!=typeof window&&console.warn("[perso-interactive-sdk-web] WARNING: createSessionId is being called from the browser. This exposes your API key and is not recommended for production. Use server-side session creation with 'perso-interactive-sdk-web/server' instead. See: https://github.com/perso-ai/perso-interactive-sdk-web#server-side");const r={capability:[],...a};a.using_stf_webrtc&&r.capability.push(t.STF_WEBRTC),a?.llm_type&&(r.capability.push(t.LLM),r.llm_type=a.llm_type),a?.tts_type&&(r.capability.push(t.TTS),r.tts_type=a.tts_type),a?.stt_type&&(r.capability.push(t.STT),r.stt_type=a.stt_type);const n=await fetch(`${e}/api/v1/session/`,{body:JSON.stringify(r),headers:{"PersoLive-APIKey":s,"Content-Type":"application/json"},method:"POST"});return(await h.parseJson(n)).session_id},exports.createWavRecorder=function(t){return new v(t)},exports.getAllSettings=async function(t,e){const s=await x(t,e),a=await N(t,e),r=await A(t,e);return{llms:s,ttsTypes:await M(t,e),sttTypes:await b(t,e),modelStyles:a,backgroundImages:r,prompts:await _(t,e),documents:await k(t,e),mcpServers:await O(t,e)}},exports.getBackgroundImages=A,exports.getDocuments=k,exports.getLLMs=x,exports.getMcpServers=O,exports.getModelStyles=N,exports.getPrompts=_,exports.getSTTs=b,exports.getSessionInfo=async function(t,e){return await h.getSessionInfo(t,e)},exports.getTTSs=M,exports.getWavSampleRate=function(t){const e=new DataView(t);if(t.byteLength<28)throw new d("File too small to be a valid WAV");if("RIFF"!==p(e,0,4))throw new d("Missing RIFF header");let s=12;for(;s<t.byteLength-8;){const a=p(e,s,4),r=e.getUint32(s+4,!0);if("fmt "===a){if(s+16>t.byteLength)throw new d("fmt chunk extends beyond file");return e.getUint32(s+12,!0)}const n=s+8+r;if(n<=s)break;s=n}throw new d("Missing fmt chunk")};
1
+ "use strict";var t,e,s=require("emoji-regex");class a extends Error{constructor(){super("WebRTC connection timeout")}}class r extends Error{errorCode;code;detail;attr;constructor(t,e,s,a){let r;r=null!=a?`${t}:${a}_${s}`:`${t}:${s}`,super(r),this.errorCode=t,this.code=e,this.detail=s,this.attr=a}}class n extends Error{underlyingError;constructor(t){super(),this.underlyingError=t}}class o extends Error{description;constructor(t){super(),this.description=t}}class i extends Error{underlyingError;constructor(t){super(`STT Error: ${t.detail}`),this.underlyingError=t}}class l extends Error{underlyingError;constructor(t){super(t.message),this.underlyingError=t}}class c extends Error{description;constructor(t){super(`TTS decode error: ${t}`),this.description=t}}!function(t){t.LLM="LLM",t.TTS="TTS",t.STT="STT",t.STF_ONPREMISE="STF_ONPREMISE",t.STF_WEBRTC="STF_WEBRTC"}(t||(t={})),function(t){t.SESSION_START="SESSION_START",t.SESSION_DURING="SESSION_DURING",t.SESSION_LOG="SESSION_LOG",t.SESSION_END="SESSION_END",t.SESSION_ERROR="SESSION_ERROR",t.SESSION_TTS="SESSION_TTS",t.SESSION_STT="SESSION_STT",t.SESSION_LLM="SESSION_LLM"}(e||(e={}));class h{static async getLLMs(t,e){const s=fetch(`${t}/api/v1/settings/llm_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getModelStyles(t,e){const s=fetch(`${t}/api/v1/settings/modelstyle/?platform_type=webrtc`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getBackgroundImages(t,e){const s=fetch(`${t}/api/v1/background_image/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getTTSs(t,e){const s=fetch(`${t}/api/v1/settings/tts_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSTTs(t,e){const s=fetch(`${t}/api/v1/settings/stt_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async makeTTS(t,{sessionId:e,text:s}){const a=await fetch(`${t}/api/v1/session/${e}/tts/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:s})});return await this.parseJson(a)}static async getPrompts(t,e){const s=fetch(`${t}/api/v1/prompt/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getDocuments(t,e){const s=fetch(`${t}/api/v1/document/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getMcpServers(t,e){const s=fetch(`${t}/api/v1/settings/mcp_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSessionInfo(t,e){const s=fetch(`${t}/api/v1/session/${e}/`,{method:"GET"}),a=await s;return await this.parseJson(a)}static async sessionEvent(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await this.parseJson(a)}static async makeSTT(t,e,s,a){const r=new FormData;r.append("audio",s),a&&r.append("language",a);const n=await fetch(`${t}/api/v1/session/${e}/stt/`,{method:"POST",body:r});return await this.parseJson(n)}static async makeLLM(t,e,s,a){const n=await fetch(`${t}/api/v1/session/${e}/llm/v2/`,{body:JSON.stringify(s),headers:{"Content-Type":"application/json"},method:"POST",signal:a});if(!n.ok){const t=await n.json(),e=t.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${n.status} with no error details`,attr:null};throw new r(n.status,e.code,e.detail,e.attr)}return n.body.getReader()}static async getIceServers(t,e){const s=await fetch(`${t}/api/v1/session/${e}/ice-servers/`,{method:"GET"});return(await this.parseJson(s)).ice_servers}static async exchangeSDP(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/exchange/`,{body:JSON.stringify({client_sdp:s}),headers:{"Content-Type":"application/json"},method:"POST"});return(await this.parseJson(a)).server_sdp}static async parseJson(t){const e=await t.json();if(t.ok)return e;{const s=e.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${t.status} with no error details`,attr:null};throw new r(t.status,s.code,s.detail,s.attr)}}}class d extends Error{constructor(t){super(`WAV parse error: ${t}`),this.name="WavParseError"}}function u(t,e,s=1){const a=2*t.length,r=new ArrayBuffer(44+a),n=new DataView(r);g(n,0,"RIFF"),n.setUint32(4,36+a,!0),g(n,8,"WAVE"),g(n,12,"fmt "),n.setUint32(16,16,!0),n.setUint16(20,1,!0),n.setUint16(22,s,!0),n.setUint32(24,e,!0),n.setUint32(28,e*s*2,!0),n.setUint16(32,2*s,!0),n.setUint16(34,16,!0),g(n,36,"data"),n.setUint32(40,a,!0);let o=44;for(let e=0;e<t.length;e++){const s=Math.max(-1,Math.min(1,t[e]));n.setInt16(o,s<0?32768*s:32767*s,!0),o+=2}return r}function p(t,e,s){let a="";for(let r=0;r<s;r++)a+=String.fromCharCode(t.getUint8(e+r));return a}function g(t,e,s){for(let a=0;a<s.length;a++)t.setUint8(e+a,s.charCodeAt(a))}function S(t,e,s){switch(s){case 8:return(t.getUint8(e)-128)/128;case 16:return t.getInt16(e,!0)/32768;case 24:{const s=t.getUint8(e),a=t.getUint8(e+1),r=t.getUint8(e+2)<<16|a<<8|s;return(r>8388607?r-16777216:r)/8388608}case 32:return t.getInt32(e,!0)/2147483648;default:return 0}}class m extends Error{constructor(t){super(`Audio resample error: ${t}`),this.name="AudioResampleError"}}async function f(t,e,s,a=1){if(0===t.length)throw new m("Cannot resample empty audio data");if(e<=0||s<=0)throw new m(`Invalid sample rate: original=${e}, target=${s}`);if(e===s)return t;try{const r=t.length/e,n=Math.ceil(r*s),o=new OfflineAudioContext(a,t.length,e).createBuffer(a,t.length,e);o.getChannelData(0).set(t);const i=new OfflineAudioContext(a,n,s),l=i.createBufferSource();l.buffer=o,l.connect(i.destination),l.start(0);return(await i.startRendering()).getChannelData(0)}catch(t){const a=t instanceof Error?t.message:String(t);throw new m(`Failed to resample audio from ${e}Hz to ${s}Hz: ${a}`)}}const w=16e3;async function y(t,e=!0){let s;try{const e=atob(t),a=new Array(e.length);for(let t=0;t<e.length;t++)a[t]=e.charCodeAt(t);s=new Uint8Array(a).buffer}catch{throw new c("Invalid Base64 audio data")}const a=function(t){const e=new Uint8Array(t);if(e.length>=4){const t=String.fromCharCode(e[0],e[1],e[2],e[3]);if("RIFF"===t)return"audio/wav";if(t.startsWith("ID3"))return"audio/mpeg";if(255===e[0]&&!(224&~e[1]))return"audio/mpeg"}return"audio/wav"}(s);if(!e)return new Blob([s],{type:a});try{const t=await async function(t,e){if("audio/wav"===e){const e=function(t){const e=new DataView(t);if(t.byteLength<44)throw new d("File too small to be a valid WAV");if("RIFF"!==p(e,0,4))throw new d("Missing RIFF header");if("WAVE"!==p(e,8,4))throw new d("Missing WAVE format identifier");let s=12,a=!1,r=0,n=0,o=0,i=0;for(;s<t.byteLength-8;){const l=p(e,s,4),c=e.getUint32(s+4,!0);if("fmt "===l){if(s+24>t.byteLength)throw new d("fmt chunk extends beyond file");r=e.getUint16(s+8,!0),n=e.getUint16(s+10,!0),o=e.getUint32(s+12,!0),i=e.getUint16(s+22,!0),a=!0,s+=8+c;break}const h=s+8+c;if(h<=s)break;s=h}if(!a)throw new d("Missing fmt chunk");if(1!==r)throw new d(`Unsupported audio format: ${r} (only PCM format 1 is supported)`);if(0===n||n>8)throw new d(`Invalid channel count: ${n}`);if(0===o||o>192e3)throw new d(`Invalid sample rate: ${o}`);if(![8,16,24,32].includes(i))throw new d(`Unsupported bits per sample: ${i}`);let l=-1,c=0;for(;s<t.byteLength-8;){const t=p(e,s,4),a=e.getUint32(s+4,!0);if("data"===t){l=s+8,c=a;break}const r=s+8+a;if(r<=s)break;s=r}if(-1===l)throw new d("Missing data chunk");const h=t.byteLength-l,u=Math.min(c,h),g=i/8,m=Math.floor(u/(g*n)),f=new Float32Array(m*n);let w=0;for(let s=0;s<m*n;s++){const a=l+s*g;if(a+g>t.byteLength)break;f[w++]=Math.max(-1,Math.min(1,S(e,a,i)))}if(2===n){const t=new Float32Array(m);for(let e=0;e<m;e++)t[e]=(f[2*e]+f[2*e+1])/2;return{sampleRate:o,channels:1,bitsPerSample:i,samples:t}}return{sampleRate:o,channels:n,bitsPerSample:i,samples:f.slice(0,w)}}(t);if(e.sampleRate===w)return{samples:e.samples,sampleRate:e.sampleRate};return{samples:await f(e.samples,e.sampleRate,w,e.channels),sampleRate:w}}const s=new AudioContext({sampleRate:w});try{const e=await s.decodeAudioData(t.slice(0));if(e.sampleRate===w)return{samples:e.getChannelData(0),sampleRate:w};return{samples:await f(e.getChannelData(0),e.sampleRate,w,1),sampleRate:w}}finally{await s.close()}}(s,a),e=u(t.samples,w,1);return new Blob([e],{type:"audio/wav"})}catch{return new Blob([s],{type:a})}}var T;exports.ChatState=void 0,(T=exports.ChatState||(exports.ChatState={})).RECORDING="RECORDING",T.LLM="LLM",T.ANALYZING="ANALYZING",T.SPEAKING="SPEAKING",T.TTS="TTS";class C{config;messageHistory=[];constructor(t){this.config=t}async*processLLM(t){if(0===t.message.length)throw new Error("Message cannot be empty");const e=t.tools??this.config.clientTools,s=e.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),a={newMessageHistory:[{role:"user",content:t.message}],allChunks:[],message:"",lastYieldedChunkCount:0,pendingToolCallsMessage:null,aborted:!1,streamingError:null};let i=0,l=[...this.messageHistory,...a.newMessageHistory];this.config.callbacks.onChatStateChange(exports.ChatState.LLM,null);try{for(;;){if(t.signal?.aborted)return void(a.allChunks.length>0&&(yield{type:"message",chunks:[...a.allChunks],message:a.message,finish:!0}));let c;try{c=await h.makeLLM(this.config.apiServer,this.config.sessionId,{messages:l,tools:s},t.signal)}catch(t){if(t instanceof r)return void(yield{type:"error",error:new n(t)});throw t}if(a.streamingError=null,yield*this.parseSSEStream(c,a,t),a.streamingError)return;if(a.aborted)return void(a.allChunks.length>0&&(yield{type:"message",chunks:[...a.allChunks],message:a.message,finish:!0}));if(null!=a.pendingToolCallsMessage){yield*this.executeToolCalls(a,e);const t=a.lastToolCallResults,s=t.length>0&&a.pendingToolCallsMessage.tool_calls.length!==t.length,r=t.some(t=>!t.chatTool.executeOnly);if(s||r){if(i++,i>=10)return void(yield{type:"error",error:new n(new o("Tool follow-up loop exceeded maximum rounds (10)"))});l=[...this.messageHistory,...a.newMessageHistory],a.pendingToolCallsMessage=null;continue}}return this.messageHistory.push(...a.newMessageHistory),void(yield{type:"message",chunks:[...a.allChunks],message:a.message,finish:!0})}}finally{this.config.callbacks.onChatStateChange(null,exports.ChatState.LLM)}}async*parseSSEStream(t,e,s){const a=new TextDecoder("utf-8");let r="",i="";e.pendingToolCallsMessage=null;const l=()=>e.allChunks.length>e.lastYieldedChunkCount?(e.lastYieldedChunkCount=e.allChunks.length,{type:"message",chunks:[...e.allChunks],message:e.message,finish:!1}):null;for(;;){const{done:c,value:h}=await t.read();if(c)break;let d;for(r+=a.decode(h,{stream:!0});-1!==(d=r.indexOf("\n"));){if(s.signal?.aborted)return void(e.aborted=!0);const t=r.slice(0,d).trim();if(r=r.slice(d+1),!t.startsWith("data: {"))return e.streamingError=new n(new o("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let a;try{a=JSON.parse(t.slice(6).trim())}catch{return e.streamingError=new n(new o("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==a.status)return e.streamingError=new n(new o(a.reason)),void(yield{type:"error",error:e.streamingError});if(i.length>0&&"message"!=a.type){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}"message"!==a.type?"tool_call"!==a.type||null==a.tool_calls?"tool"!==a.role||"tool_call"===a.type&&e.newMessageHistory.push({role:a.role,type:a.type,content:a.content,tool_call_id:a.tool_call_id}):(e.newMessageHistory.push({role:"assistant",type:a.type,content:a.content,tool_calls:a.tool_calls}),e.pendingToolCallsMessage=a,yield{type:"tool_call",tool_calls:a.tool_calls}):(i+=a.content,e.message+=a.content,e.allChunks.push(a.content))}const u=l();u&&(yield u)}const c=r.trim();if(c.length>0){if(!c.startsWith("data: {"))return e.streamingError=new n(new o("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let t;try{t=JSON.parse(c.slice(6).trim())}catch{return e.streamingError=new n(new o("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==t.status)return e.streamingError=new n(new o(t.reason)),void(yield{type:"error",error:e.streamingError});if("message"===t.type)i+=t.content,e.message+=t.content,e.allChunks.push(t.content);else if("tool_call"===t.type&&null!=t.tool_calls){if(i.length>0){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}e.newMessageHistory.push({role:"assistant",type:t.type,content:t.content,tool_calls:t.tool_calls}),e.pendingToolCallsMessage=t,yield{type:"tool_call",tool_calls:t.tool_calls}}}i.length>0&&e.newMessageHistory.push({role:"assistant",type:"message",content:i})}async*executeToolCalls(t,e){const s=t=>{for(const s of e)if(s.name===t)return s;return null},a=[];for(const e of t.pendingToolCallsMessage.tool_calls){const t=s(e.function.name);null!=t&&a.push((async()=>{try{const s=await t.call(JSON.parse(e.function.arguments));return{toolCallId:e.id,chatTool:t,chatToolResult:s}}catch(s){return{toolCallId:e.id,chatTool:t,chatToolResult:{error:s.message}}}})())}const r=await Promise.all(a);t.lastToolCallResults=r;for(const e of r)t.newMessageHistory.push({role:"tool",content:JSON.stringify(e.chatToolResult),tool_call_id:e.toolCallId}),yield{type:"tool_result",tool_call_id:e.toolCallId,result:e.chatToolResult}}addToHistory(t){this.messageHistory.push(t)}getHistory(){return this.messageHistory}}const E=`data:application/javascript,${encodeURIComponent("\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.isRecording = true;\n \n // Listen for stop message from main thread\n this.port.onmessage = (event) => {\n if (event.data.type === 'stop') {\n this.isRecording = false;\n // Send confirmation back to main thread\n this.port.postMessage({ type: 'stopped' });\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (input && input.length > 0 && this.isRecording) {\n // Clone the audio data and send to main thread\n const channelData = new Float32Array(input[0]);\n this.port.postMessage({ type: 'audio', data: channelData });\n }\n // Return true to keep the processor alive until stopped\n return this.isRecording;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n")}`;class v{audioContext=null;mediaStream=null;workletNode=null;sourceNode=null;audioChunks=[];isRecordingState=!1;channels;targetSampleRate;constructor(t={}){this.channels=t.channels||1,this.targetSampleRate=t.targetSampleRate}async start(){if(this.isRecordingState)throw new Error("WavRecorder is already recording");if(this.mediaStream=await navigator.mediaDevices.getUserMedia({audio:!0}),this.audioContext=new AudioContext,"running"!==this.audioContext.state)try{await this.audioContext.resume()}catch(t){console.warn("WavRecorder: Failed to resume AudioContext:",t)}await this.audioContext.audioWorklet.addModule(E),this.sourceNode=this.audioContext.createMediaStreamSource(this.mediaStream),this.workletNode=new AudioWorkletNode(this.audioContext,"recorder-processor"),this.audioChunks=[],this.workletNode.port.onmessage=t=>{"audio"===t.data.type&&this.audioChunks.push(t.data.data)},this.sourceNode.connect(this.workletNode),this.workletNode.connect(this.audioContext.destination),this.isRecordingState=!0}async stop(){if(!this.isRecordingState)throw new Error("WavRecorder is not recording");this.isRecordingState=!1,await new Promise(t=>{this.workletNode.port.onmessage=e=>{"stopped"===e.data.type?t():"audio"===e.data.type&&this.audioChunks.push(e.data.data)},this.workletNode.port.postMessage({type:"stop"})}),this.workletNode?.disconnect(),this.sourceNode?.disconnect(),this.mediaStream?.getTracks().forEach(t=>t.stop());const t=this.audioChunks,e=this.audioContext.sampleRate;try{const s=t.reduce((t,e)=>t+e.length,0),a=new Float32Array(s);let r,n,o=0;for(const e of t)a.set(e,o),o+=e.length;this.targetSampleRate&&this.targetSampleRate!==e?(r=await f(a,e,this.targetSampleRate,this.channels),n=this.targetSampleRate):(r=a,n=e);const i=u(r,n,this.channels),l=new Blob([i],{type:"audio/wav"});return new File([l],"recording.wav",{type:"audio/wav"})}finally{await(this.audioContext?.close()),this.audioContext=null,this.mediaStream=null,this.workletNode=null,this.sourceNode=null,this.audioChunks=[]}}isRecording(){return this.isRecordingState}}class R extends EventTarget{pc;dc;streams=[];pingTime;pingIntervalId=null;constructor(t,e){super(),this.pc=t,this.dc=e,this.pingTime=Date.now()+3e3,this.pc.addEventListener("track",t=>{this.streams=this.streams.concat(t.streams)}),this.pc.addEventListener("connectionstatechange",()=>{"disconnected"!==this.pc.connectionState&&"failed"!==this.pc.connectionState||this.close()}),this.dc.onopen=()=>{this.pingIntervalId=setInterval(()=>{this.ping(),Date.now()-this.pingTime>5e3&&this.close()},1e3)},this.dc.onclose=()=>{null!=this.pingIntervalId&&clearInterval(this.pingIntervalId)},this.#t({live:!0,code:200,reason:"OK"}),this.setMessageCallback("ping",()=>{this.pingTime=Date.now()})}static async create(s,a,r,n,o){const i=await h.getSessionInfo(s,a);if(!(Array.isArray(i.capability)&&i.capability.some(e=>e.name===t.STF_ONPREMISE||e.name===t.STF_WEBRTC)))return await h.sessionEvent(s,a,e.SESSION_START),null;const l=await h.getIceServers(s,a);let c=await R.createPeerConnection(l),d=c.createDataChannel("message",{protocol:"message"}),u=new R(c,d);o?o.getTracks().forEach(function(t){c.addTrack(t,o)}):c.addTransceiver("audio",{direction:"recvonly"});const p=c.addTransceiver("video",{direction:"recvonly"}),g=RTCRtpReceiver.getCapabilities("video");null!=g&&p.setCodecPreferences(g.codecs);const S=await c.createOffer();await c.setLocalDescription(S);const m=await h.exchangeSDP(s,a,S);return await c.setRemoteDescription(m),await R.waitFor(()=>u.isReady(),100,50),u.changeSize(r,n),u}static async createPeerConnection(t){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:t})}static async waitFor(t,e,s){let r=0;if(await new Promise(a=>{const n=setInterval(()=>{r+=1,r>=s&&(clearInterval(n),a("bad")),t()&&(clearInterval(n),a("good"))},e)}),r>=s)throw new a}isReady(){return this.streams.length>0&&"open"===this.dc.readyState}#t(t){this.dispatchEvent(new CustomEvent("status",{detail:t}))}subscribeStatus(t){return this.addEventListener("status",t),()=>{this.removeEventListener("status",t)}}getStream(){return this.streams[0]}sendMessage(t,e){this.dc.send(JSON.stringify({type:t,data:e}))}ttstf(t){this.sendMessage("ttstf",{message:t})}static BACKPRESSURE_THRESHOLD=524288;sendFile(t,e=65536){return new Promise((s,a)=>{const r=this.pc.createDataChannel("file",{protocol:"file"});r.onerror=t=>{r.close(),a(new Error(`File channel error: ${t}`))},r.addEventListener("message",async n=>{try{if(0===n.data.length){const s=new Uint8Array(await t.arrayBuffer());let n=0;const o=()=>{for(;n<s.length;){if(r.bufferedAmount>R.BACKPRESSURE_THRESHOLD)return r.bufferedAmountLowThreshold=R.BACKPRESSURE_THRESHOLD/2,r.onbufferedamountlow=()=>{r.onbufferedamountlow=null,r.onclose=null,o()},void(r.onclose=()=>{r.onbufferedamountlow=null,a(new Error("File channel closed during transfer"))});r.send(s.slice(n,n+e)),n+=e}r.send(new Uint8Array(0))};o()}else r.close(),s(n.data)}catch(t){r.close(),a(t)}})})}async stf(t,e,s){const a=await this.sendFile(t);return this.sendMessage("stf",{message:s,file_ref:a,format:e}),a}recordStart(){this.sendMessage("record-start",{})}recordEndStt(t){this.sendMessage("record-end-stt",{language:t})}recordEndTranslate(t,e){this.sendMessage("record-end-translate",{src_lang:t,dst_lang:e})}changeSize(t,e){this.sendMessage("change-size",{width:t,height:e})}setTemplate(t,e){this.sendMessage("set-template",{model:t,dress:e})}clearBuffer(){this.sendMessage("clear-buffer",{})}ping(){this.sendMessage("ping",{})}setMessageCallback(t,e){const s=s=>{const a=JSON.parse(s.data);a.type===t&&e(a.data)};return this.dc.addEventListener("message",s),()=>{this.dc.removeEventListener("message",s)}}async tts(t,e=!0){return y(t,e)}close(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:408,reason:"Request Timeout"})}closeSelf(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:200,reason:"OK"})}}class I{apiServer;sessionId;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];llmProcessor;chatStateMap=new Map([[exports.ChatState.RECORDING,0],[exports.ChatState.LLM,0],[exports.ChatState.ANALYZING,0],[exports.ChatState.SPEAKING,0],[exports.ChatState.TTS,0]]);emojiRegex=s();sttRecorder=null;sttTimeoutHandle=null;sttTimeoutAudioFile=null;heartbeatIntervalId=null;legacyVoiceChatMode;stream;constructor(t,e,s,a,r){this.apiServer=t,this.sessionId=e,this.perso=s,this.clientTools=a,this.legacyVoiceChatMode=r?.legacyVoiceChatMode??!1,this.stream=r?.stream??null,this.resetChatState(),this.llmProcessor=new C({apiServer:t,sessionId:e,clientTools:a,callbacks:{onChatStateChange:(t,e)=>this.setChatState(t,e),onError:t=>this.setError(t),onChatLog:(t,e)=>this.addMessageToChatLog(t,e),onTTSTF:t=>this.processTTSTFInternal(t)}}),this.startHeartbeat(),s&&(s.subscribeStatus(t=>{!1===t.detail?.live&&this.stopHeartbeat()}),s.setMessageCallback("stf",t=>{if(this.chatStateMap.get(exports.ChatState.ANALYZING)||this.chatStateMap.get(exports.ChatState.SPEAKING))if(this.setChatState(exports.ChatState.SPEAKING,exports.ChatState.ANALYZING),null!==this.lastStfTimeoutHandle){clearTimeout(this.lastStfTimeoutHandle);let e=Date.now();this.stfTotalDuration+=t.duration+1e3-(e-this.stfTimeoutStartTime),this.stfTimeoutStartTime=e,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,exports.ChatState.SPEAKING)},this.stfTotalDuration)}else this.stfTimeoutStartTime=Date.now(),this.stfTotalDuration=t.duration+2e3,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,exports.ChatState.SPEAKING)},this.stfTotalDuration)}),s.setMessageCallback("stt",t=>{if(this.setChatState(null,exports.ChatState.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:t.text}));else{if(""===t.text)return;this.processChat(t.text)}}),s.setMessageCallback("stt-error",t=>{this.setChatState(null,exports.ChatState.ANALYZING)}))}llmJob=null;async processChat(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processLLM(t){return this.pipelineSuppressed=!1,this.llmProcessor.processLLM(t)}getMessageHistory(){return this.llmProcessor.getHistory()}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}async transcribeAudio(t,e){const s=t instanceof File?t:new File([t],"audio.wav",{type:t.type});try{return(await h.makeSTT(this.apiServer,this.sessionId,s,e)).text}catch(t){if(t instanceof r)throw new i(t);throw t}}async processSTF(t,e,s){if(!this.perso)throw new Error("processSTF requires WebRTC (STF mode)");this.pipelineSuppressed=!1,this.setChatState(exports.ChatState.ANALYZING);try{const a=await this.perso.stf(t,e,s);return this.pipelineSuppressed?(this.setChatState(null,exports.ChatState.ANALYZING),a):a}catch(t){throw this.setChatState(null,exports.ChatState.ANALYZING),t}}async processTTS(t,e={}){const{resample:s=!0}=e,a=this.removeEmoji(t).trim();if(0===a.length)return;this.pipelineSuppressed=!1;const n=/[.?!]$/.test(a)?a:a+".";this.setChatState(exports.ChatState.TTS,null);try{const{audio:t}=await h.makeTTS(this.apiServer,{sessionId:this.sessionId,text:n});if(this.pipelineSuppressed)return;return await y(t,s)}catch(t){t instanceof r||t instanceof c?this.setError(new l(t)):this.setError(t instanceof Error?t:new Error(String(t)))}finally{this.setChatState(null,exports.ChatState.TTS)}}startVoiceChat(){if(!this.perso)throw new Error("startVoiceChat requires WebRTC (STF mode)");return this.pipelineSuppressed=!1,this.setChatState(exports.ChatState.RECORDING),this.perso.recordStart()}stopVoiceChat(){if(!this.perso)throw new Error("stopVoiceChat requires WebRTC (STF mode)");this.setChatState(exports.ChatState.ANALYZING,exports.ChatState.RECORDING),this.perso.recordEndStt()}async startProcessSTT(t){if(this.sttRecorder?.isRecording())throw new Error("STT recording is already in progress");this.pipelineSuppressed=!1,this.setChatState(exports.ChatState.RECORDING);try{this.sttRecorder=new v({targetSampleRate:16e3}),await this.sttRecorder.start(),t&&t>0&&(this.sttTimeoutHandle=setTimeout(async()=>{if(this.sttTimeoutHandle=null,this.sttRecorder?.isRecording()){try{this.sttTimeoutAudioFile=await this.sttRecorder.stop()}catch{this.sttTimeoutAudioFile=null,this.setChatState(null,exports.ChatState.RECORDING)}this.sttRecorder=null}},t))}catch(t){throw this.setChatState(null,exports.ChatState.RECORDING),this.sttRecorder=null,t}}lastRecordedAudioFile=null;async stopProcessSTT(t){let e;if(this.sttTimeoutHandle&&(clearTimeout(this.sttTimeoutHandle),this.sttTimeoutHandle=null),this.setChatState(null,exports.ChatState.RECORDING),this.sttTimeoutAudioFile)e=this.sttTimeoutAudioFile,this.sttTimeoutAudioFile=null;else{if(!this.sttRecorder?.isRecording())throw this.sttRecorder?(this.sttRecorder=null,new Error("STT recording is not in progress")):new Error("STT recording has not been started");e=await this.sttRecorder.stop(),this.sttRecorder=null}this.lastRecordedAudioFile=e;try{return(await h.makeSTT(this.apiServer,this.sessionId,e,t)).text}catch(t){if(t instanceof r)throw new i(t);throw t}}isSTTRecording(){return(this.sttRecorder?.isRecording()??!1)||null!==this.sttTimeoutAudioFile}changeSize(t,e){this.perso?.changeSize(t,e)}async clearBuffer(){this.perso?.clearBuffer(),await this.clearLLMJob(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.pipelineSuppressed=!0,this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()??null}getRemoteStream(){return this.perso?.getStream()}getLocalStream(){return this.stream}stopSession(){this.close()}onClose(t){return this.perso?this.perso.subscribeStatus(e=>{null!=e.detail&&!1===e.detail.live&&t(200===e.detail.code)}):()=>{}}subscribeChatStates(t){const e=e=>{t(e.detail.status)};return this.chatStatesHandler.addEventListener("status",e),()=>{this.chatStatesHandler.removeEventListener("status",e)}}subscribeChatLog(t){const e=e=>{t(e.detail.chatLog)};return this.chatLogHandler.addEventListener("chatLog",e),()=>{this.chatLogHandler.removeEventListener("chatLog",e)}}setSttResultCallback(t){const e=e=>{t(e.detail)};return this.sttEventHandler=new EventTarget,this.sttEventHandler.addEventListener("stt",e),()=>{this.sttEventHandler?.removeEventListener("stt",e),this.sttEventHandler=null}}setErrorHandler(t){const e=e=>{t(e.detail.error)};return this.errorHandler.addEventListener("error",e),()=>{this.errorHandler.removeEventListener("error",e)}}getSessionId(){return this.sessionId}async processChatInternal(t){this.setChatState(exports.ChatState.LLM);const e=this.clientTools.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),s=new Array;null===t||(t instanceof Array?s.push(...t):"string"==typeof t&&s.push({role:"user",content:t}));const a=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...s],tools:e}),headers:{"Content-Type":"application/json"},method:"POST"});if(!a.ok){const t=await a.json(),e=new n(new r(a.status,t.errors[0].code,t.errors[0].detail,t.errors[0].attr));return this.setError(e),void this.setChatState(null,exports.ChatState.LLM)}const i=a.body?.getReader(),l=new TextDecoder("utf-8");let c="",h=null,d="";for(;;){const{done:t,value:e}=await i.read();if(t)break;let a;for(d+=l.decode(e,{stream:!0});-1!==(a=d.indexOf("\n"));){if(this.llmCancel)return c.length>0&&this.addMessageToChatLog(c,!1),void this.setChatState(null,exports.ChatState.LLM);const t=d.slice(0,a).trim();if(d=d.slice(a+1),!t.startsWith("data: {")){const t=new n(new o("Failed to parse SSE response"));return this.setError(t),void this.setChatState(null,exports.ChatState.LLM)}const e=JSON.parse(t.slice(6).trim());if("success"!==e.status){const t=new n(new o(e.reason));return this.setError(t),void this.setChatState(null,exports.ChatState.LLM)}c.length>0&&"message"!=e.type&&(s.push({role:"assistant",type:"message",content:c}),this.addMessageToChatLog(c,!1),c=""),"message"!==e.type?"tool_call"!==e.type||null==e.tool_calls?"tool"!==e.role||"tool_call"===e.type&&s.push({role:e.role,type:e.type,content:e.content,tool_call_id:e.tool_call_id}):(s.push({role:"assistant",type:e.type,content:e.content,tool_calls:e.tool_calls}),h=e):(c+=e.content,this.processTTSTFInternal(e.content))}}if(this.llmCancel)this.setChatState(null,exports.ChatState.LLM);else{if(null!=h){const t=[];for(const e of h.tool_calls){const s=this.getChatTool(this.clientTools,e.function.name);null!=s&&t.push(new Promise(async t=>{try{const a=await s.call(JSON.parse(e.function.arguments));t({toolCallId:e.id,chatTool:s,chatToolResult:a})}catch(a){t({toolCallId:e.id,chatTool:s,chatToolResult:{result:"error!"}})}}))}const e=await Promise.all(t);for(const t of e)s.push({role:"tool",content:JSON.stringify(t.chatToolResult),tool_call_id:t.toolCallId});const a=e.length>0&&h.tool_calls.length!==e.length,r=e.some(t=>!t.chatTool.executeOnly);a||r?await this.processChatInternal(s):this.messageHistory.push(...s)}else this.messageHistory.push(...s);this.setChatState(null,exports.ChatState.LLM)}}getChatTool(t,e){for(const s of t)if(s.name===e)return s;return null}llmCancel=!1;pipelineSuppressed=!1;async clearLLMJob(){null!=this.llmJob&&(this.llmCancel=!0,await this.llmJob,this.llmCancel=!1)}processTTSTFInternal(t){const e=this.removeEmoji(t).trim();0!==e.length&&this.perso&&(this.setChatState(exports.ChatState.ANALYZING),this.perso.ttstf(e))}addMessageToChatLog(t,e){this.chatLog=[{text:t,isUser:e,timestamp:new Date},...this.chatLog],this.chatLogHandler.dispatchEvent(new CustomEvent("chatLog",{detail:{chatLog:this.chatLog}}))}setChatState(t=null,e=null){const s=new Map(this.chatStateMap);function a(t){t===exports.ChatState.ANALYZING?s.set(t,(s.get(t)||0)+1):s.set(t,1)}function r(t){t===exports.ChatState.ANALYZING?s.set(t,Math.max((s.get(t)||0)-1,0)):s.set(t,0)}if(null!=t)if(t instanceof Array)for(let e of t)a(e);else a(t);if(null!=e)if(e instanceof Array)for(let t of e)r(t);else r(e);const n=this.exchangeChatStateMapToSet(this.chatStateMap),o=this.exchangeChatStateMapToSet(s);this.chatStateMap=s,this.isEqualChatStateMap(n,o)||this.dispatchChatState(o)}resetChatState(){this.chatStateMap=new Map([[exports.ChatState.RECORDING,0],[exports.ChatState.LLM,0],[exports.ChatState.ANALYZING,0],[exports.ChatState.SPEAKING,0],[exports.ChatState.TTS,0]]),this.dispatchChatState(this.exchangeChatStateMapToSet(this.chatStateMap))}exchangeChatStateMapToSet(t){const e=new Set;for(const s of t)s[1]>0&&e.add(s[0]);return e}dispatchChatState(t){this.chatStatesHandler.dispatchEvent(new CustomEvent("status",{detail:{status:t}}))}isEqualChatStateMap(t,e){if(t.size!==e.size)return!1;for(const s of t)if(t.has(s)!==e.has(s))return!1;return!0}setError(t){this.errorHandler.dispatchEvent(new CustomEvent("error",{detail:{error:t}}))}close(){this.stopHeartbeat(),this.perso?.closeSelf()}startHeartbeat(){const t=async()=>{try{await h.sessionEvent(this.apiServer,this.sessionId,e.SESSION_DURING),null!==this.heartbeatIntervalId&&(this.heartbeatIntervalId=setTimeout(t,1e4))}catch(t){t instanceof r?this.setError(t):this.setError(t instanceof Error?t:new Error(String(t))),this.close()}};this.heartbeatIntervalId=setTimeout(t,1e4)}stopHeartbeat(){null!==this.heartbeatIntervalId&&(clearTimeout(this.heartbeatIntervalId),this.heartbeatIntervalId=null)}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function L(t,e,s,a,r,n){if("boolean"!=typeof r){const n=await R.create(t,e,s,a);return new I(t,e,n,r)}const o=n??[];let i,l;if(r)i=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),l=()=>{};else{const t=new AudioContext,e=t.createOscillator();e.frequency.value=0;const s=t.createMediaStreamDestination();e.connect(s),e.start(),i=s.stream,l=()=>{e.stop(),e.disconnect(s),t.close()}}const c=await R.create(t,e,s,a,i);if(!c)return l(),new I(t,e,null,o);const h=new I(t,e,c,o,{stream:i,legacyVoiceChatMode:!0});return h.onClose(()=>{l()}),h}async function x(t,e){return await h.getLLMs(t,e)}async function M(t,e){return await h.getTTSs(t,e)}async function b(t,e){return await h.getSTTs(t,e)}async function N(t,e){return await h.getModelStyles(t,e)}async function A(t,e){return await h.getBackgroundImages(t,e)}async function _(t,e){return await h.getPrompts(t,e)}async function k(t,e){return await h.getDocuments(t,e)}async function O(t,e){return await h.getMcpServers(t,e)}exports.ApiError=r,exports.ChatTool=class{name;description;parameters;call;executeOnly;constructor(t,e,s,a,r=!1){this.name=t,this.description=e,this.parameters=s,this.call=a,this.executeOnly=r}},exports.LLMError=n,exports.LLMStreamingResponseError=o,exports.LlmProcessor=C,exports.STTError=i,exports.Session=I,exports.TTSDecodeError=c,exports.TTSError=l,exports.TTS_TARGET_SAMPLE_RATE=w,exports.WavRecorder=v,exports.createSession=async function(t,e,s,a,r,n){return"boolean"==typeof r?await L(t,e,s,a,r,n??[]):await L(t,e,s,a,r)},exports.createSessionId=async(e,s,a)=>{"undefined"!=typeof window&&console.warn("[perso-interactive-sdk-web] WARNING: createSessionId is being called from the browser. This exposes your API key and is not recommended for production. Use server-side session creation with 'perso-interactive-sdk-web/server' instead. See: https://github.com/perso-ai/perso-interactive-sdk-web#server-side");const r={capability:[],...a};a.using_stf_webrtc&&r.capability.push(t.STF_WEBRTC),a?.llm_type&&(r.capability.push(t.LLM),r.llm_type=a.llm_type),a?.tts_type&&(r.capability.push(t.TTS),r.tts_type=a.tts_type),a?.stt_type&&(r.capability.push(t.STT),r.stt_type=a.stt_type);const n=await fetch(`${e}/api/v1/session/`,{body:JSON.stringify(r),headers:{"PersoLive-APIKey":s,"Content-Type":"application/json"},method:"POST"});return(await h.parseJson(n)).session_id},exports.createWavRecorder=function(t){return new v(t)},exports.getAllSettings=async function(t,e){const s=await x(t,e),a=await N(t,e),r=await A(t,e);return{llms:s,ttsTypes:await M(t,e),sttTypes:await b(t,e),modelStyles:a,backgroundImages:r,prompts:await _(t,e),documents:await k(t,e),mcpServers:await O(t,e)}},exports.getBackgroundImages=A,exports.getDocuments=k,exports.getLLMs=x,exports.getMcpServers=O,exports.getModelStyles=N,exports.getPrompts=_,exports.getSTTs=b,exports.getSessionInfo=async function(t,e){return await h.getSessionInfo(t,e)},exports.getTTSs=M,exports.getWavSampleRate=function(t){const e=new DataView(t);if(t.byteLength<28)throw new d("File too small to be a valid WAV");if("RIFF"!==p(e,0,4))throw new d("Missing RIFF header");let s=12;for(;s<t.byteLength-8;){const a=p(e,s,4),r=e.getUint32(s+4,!0);if("fmt "===a){if(s+16>t.byteLength)throw new d("fmt chunk extends beyond file");return e.getUint32(s+12,!0)}const n=s+8+r;if(n<=s)break;s=n}throw new d("Missing fmt chunk")};
@@ -1 +1 @@
1
- var PersoInteractive=function(t){"use strict";class u extends Error{constructor(){super("WebRTC connection timeout")}}class e extends Error{errorCode;code;detail;attr;constructor(t,u,e,D){let s;s=null!=D?`${t}:${D}_${e}`:`${t}:${e}`,super(s),this.errorCode=t,this.code=u,this.detail=e,this.attr=D}}class D extends Error{underlyingError;constructor(t){super(),this.underlyingError=t}}class s extends Error{description;constructor(t){super(),this.description=t}}class a extends Error{underlyingError;constructor(t){super(`STT Error: ${t.detail}`),this.underlyingError=t}}class n extends Error{underlyingError;constructor(t){super(t.message),this.underlyingError=t}}class r extends Error{description;constructor(t){super(`TTS decode error: ${t}`),this.description=t}}var o,i;!function(t){t.LLM="LLM",t.TTS="TTS",t.STT="STT",t.STF_ONPREMISE="STF_ONPREMISE",t.STF_WEBRTC="STF_WEBRTC"}(o||(o={})),function(t){t.SESSION_START="SESSION_START",t.SESSION_DURING="SESSION_DURING",t.SESSION_LOG="SESSION_LOG",t.SESSION_END="SESSION_END",t.SESSION_ERROR="SESSION_ERROR",t.SESSION_TTS="SESSION_TTS",t.SESSION_STT="SESSION_STT",t.SESSION_LLM="SESSION_LLM"}(i||(i={}));class F{static async getLLMs(t,u){const e=fetch(`${t}/api/v1/settings/llm_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getModelStyles(t,u){const e=fetch(`${t}/api/v1/settings/modelstyle/?platform_type=webrtc`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getBackgroundImages(t,u){const e=fetch(`${t}/api/v1/background_image/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getTTSs(t,u){const e=fetch(`${t}/api/v1/settings/tts_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getSTTs(t,u){const e=fetch(`${t}/api/v1/settings/stt_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async makeTTS(t,{sessionId:u,text:e}){const D=await fetch(`${t}/api/v1/session/${u}/tts/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:e})});return await this.parseJson(D)}static async getPrompts(t,u){const e=fetch(`${t}/api/v1/prompt/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getDocuments(t,u){const e=fetch(`${t}/api/v1/document/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getMcpServers(t,u){const e=fetch(`${t}/api/v1/settings/mcp_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getSessionInfo(t,u){const e=fetch(`${t}/api/v1/session/${u}/`,{method:"GET"}),D=await e;return await this.parseJson(D)}static async sessionEvent(t,u,e){const D=await fetch(`${t}/api/v1/session/${u}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:e})});await this.parseJson(D)}static async makeSTT(t,u,e,D){const s=new FormData;s.append("audio",e),D&&s.append("language",D);const a=await fetch(`${t}/api/v1/session/${u}/stt/`,{method:"POST",body:s});return await this.parseJson(a)}static async makeLLM(t,u,D,s){const a=await fetch(`${t}/api/v1/session/${u}/llm/v2/`,{body:JSON.stringify(D),headers:{"Content-Type":"application/json"},method:"POST",signal:s});if(!a.ok){const t=await a.json(),u=t.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${a.status} with no error details`,attr:null};throw new e(a.status,u.code,u.detail,u.attr)}return a.body.getReader()}static async getIceServers(t,u){const e=await fetch(`${t}/api/v1/session/${u}/ice-servers/`,{method:"GET"});return(await this.parseJson(e)).ice_servers}static async exchangeSDP(t,u,e){const D=await fetch(`${t}/api/v1/session/${u}/exchange/`,{body:JSON.stringify({client_sdp:e}),headers:{"Content-Type":"application/json"},method:"POST"});return(await this.parseJson(D)).server_sdp}static async parseJson(t){const u=await t.json();if(t.ok)return u;{const D=u.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${t.status} with no error details`,attr:null};throw new e(t.status,D.code,D.detail,D.attr)}}}class l extends Error{constructor(t){super(`WAV parse error: ${t}`),this.name="WavParseError"}}function c(t,u,e=1){const D=2*t.length,s=new ArrayBuffer(44+D),a=new DataView(s);C(a,0,"RIFF"),a.setUint32(4,36+D,!0),C(a,8,"WAVE"),C(a,12,"fmt "),a.setUint32(16,16,!0),a.setUint16(20,1,!0),a.setUint16(22,e,!0),a.setUint32(24,u,!0),a.setUint32(28,u*e*2,!0),a.setUint16(32,2*e,!0),a.setUint16(34,16,!0),C(a,36,"data"),a.setUint32(40,D,!0);let n=44;for(let u=0;u<t.length;u++){const e=Math.max(-1,Math.min(1,t[u]));a.setInt16(n,e<0?32768*e:32767*e,!0),n+=2}return s}function h(t,u,e){let D="";for(let s=0;s<e;s++)D+=String.fromCharCode(t.getUint8(u+s));return D}function C(t,u,e){for(let D=0;D<e.length;D++)t.setUint8(u+D,e.charCodeAt(D))}function E(t,u,e){switch(e){case 8:return(t.getUint8(u)-128)/128;case 16:return t.getInt16(u,!0)/32768;case 24:{const e=t.getUint8(u),D=t.getUint8(u+1),s=t.getUint8(u+2)<<16|D<<8|e;return(s>8388607?s-16777216:s)/8388608}case 32:return t.getInt32(u,!0)/2147483648;default:return 0}}class d extends Error{constructor(t){super(`Audio resample error: ${t}`),this.name="AudioResampleError"}}async function p(t,u,e,D=1){if(0===t.length)throw new d("Cannot resample empty audio data");if(u<=0||e<=0)throw new d(`Invalid sample rate: original=${u}, target=${e}`);if(u===e)return t;try{const s=t.length/u,a=Math.ceil(s*e),n=new OfflineAudioContext(D,t.length,u).createBuffer(D,t.length,u);n.getChannelData(0).set(t);const r=new OfflineAudioContext(D,a,e),o=r.createBufferSource();o.buffer=n,o.connect(r.destination),o.start(0);return(await r.startRendering()).getChannelData(0)}catch(t){const D=t instanceof Error?t.message:String(t);throw new d(`Failed to resample audio from ${u}Hz to ${e}Hz: ${D}`)}}const g=16e3;async function S(t,u=!0){let e;try{const u=atob(t),D=new Array(u.length);for(let t=0;t<u.length;t++)D[t]=u.charCodeAt(t);e=new Uint8Array(D).buffer}catch{throw new r("Invalid Base64 audio data")}const D=function(t){const u=new Uint8Array(t);if(u.length>=4){const t=String.fromCharCode(u[0],u[1],u[2],u[3]);if("RIFF"===t)return"audio/wav";if(t.startsWith("ID3"))return"audio/mpeg";if(255===u[0]&&!(224&~u[1]))return"audio/mpeg"}return"audio/wav"}(e);if(!u)return new Blob([e],{type:D});try{const t=await async function(t,u){if("audio/wav"===u){const u=function(t){const u=new DataView(t);if(t.byteLength<44)throw new l("File too small to be a valid WAV");if("RIFF"!==h(u,0,4))throw new l("Missing RIFF header");if("WAVE"!==h(u,8,4))throw new l("Missing WAVE format identifier");let e=12,D=!1,s=0,a=0,n=0,r=0;for(;e<t.byteLength-8;){const o=h(u,e,4),i=u.getUint32(e+4,!0);if("fmt "===o){if(e+24>t.byteLength)throw new l("fmt chunk extends beyond file");s=u.getUint16(e+8,!0),a=u.getUint16(e+10,!0),n=u.getUint32(e+12,!0),r=u.getUint16(e+22,!0),D=!0,e+=8+i;break}const F=e+8+i;if(F<=e)break;e=F}if(!D)throw new l("Missing fmt chunk");if(1!==s)throw new l(`Unsupported audio format: ${s} (only PCM format 1 is supported)`);if(0===a||a>8)throw new l(`Invalid channel count: ${a}`);if(0===n||n>192e3)throw new l(`Invalid sample rate: ${n}`);if(![8,16,24,32].includes(r))throw new l(`Unsupported bits per sample: ${r}`);let o=-1,i=0;for(;e<t.byteLength-8;){const t=h(u,e,4),D=u.getUint32(e+4,!0);if("data"===t){o=e+8,i=D;break}const s=e+8+D;if(s<=e)break;e=s}if(-1===o)throw new l("Missing data chunk");const F=t.byteLength-o,c=Math.min(i,F),C=r/8,d=Math.floor(c/(C*a)),p=new Float32Array(d*a);let g=0;for(let e=0;e<d*a;e++){const D=o+e*C;if(D+C>t.byteLength)break;p[g++]=Math.max(-1,Math.min(1,E(u,D,r)))}if(2===a){const t=new Float32Array(d);for(let u=0;u<d;u++)t[u]=(p[2*u]+p[2*u+1])/2;return{sampleRate:n,channels:1,bitsPerSample:r,samples:t}}return{sampleRate:n,channels:a,bitsPerSample:r,samples:p.slice(0,g)}}(t);if(u.sampleRate===g)return{samples:u.samples,sampleRate:u.sampleRate};return{samples:await p(u.samples,u.sampleRate,g,u.channels),sampleRate:g}}const e=new AudioContext({sampleRate:g});try{const u=await e.decodeAudioData(t.slice(0));if(u.sampleRate===g)return{samples:u.getChannelData(0),sampleRate:g};return{samples:await p(u.getChannelData(0),u.sampleRate,g,1),sampleRate:g}}finally{await e.close()}}(e,D),u=c(t.samples,g,1);return new Blob([u],{type:"audio/wav"})}catch{return new Blob([e],{type:D})}}var m;t.ChatState=void 0,(m=t.ChatState||(t.ChatState={})).RECORDING="RECORDING",m.LLM="LLM",m.ANALYZING="ANALYZING",m.SPEAKING="SPEAKING",m.TTS="TTS";class f{config;messageHistory=[];constructor(t){this.config=t}async*processLLM(u){if(0===u.message.length)throw new Error("Message cannot be empty");const a=u.tools??this.config.clientTools,n=a.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),r={newMessageHistory:[{role:"user",content:u.message}],allChunks:[],message:"",lastYieldedChunkCount:0,pendingToolCallsMessage:null,aborted:!1,streamingError:null};let o=0,i=[...this.messageHistory,...r.newMessageHistory];this.config.callbacks.onChatStateChange(t.ChatState.LLM,null);try{for(;;){if(u.signal?.aborted)return void(r.allChunks.length>0&&(yield{type:"message",chunks:[...r.allChunks],message:r.message,finish:!0}));let t;try{t=await F.makeLLM(this.config.apiServer,this.config.sessionId,{messages:i,tools:n},u.signal)}catch(t){if(t instanceof e)return void(yield{type:"error",error:new D(t)});throw t}if(r.streamingError=null,yield*this.parseSSEStream(t,r,u),r.streamingError)return;if(r.aborted)return void(r.allChunks.length>0&&(yield{type:"message",chunks:[...r.allChunks],message:r.message,finish:!0}));if(null!=r.pendingToolCallsMessage){yield*this.executeToolCalls(r,a);const t=r.lastToolCallResults,u=t.length>0&&r.pendingToolCallsMessage.tool_calls.length!==t.length,e=t.some(t=>!t.chatTool.executeOnly);if(u||e){if(o++,o>=10)return void(yield{type:"error",error:new D(new s("Tool follow-up loop exceeded maximum rounds (10)"))});i=[...this.messageHistory,...r.newMessageHistory],r.pendingToolCallsMessage=null;continue}}return this.messageHistory.push(...r.newMessageHistory),void(yield{type:"message",chunks:[...r.allChunks],message:r.message,finish:!0})}}finally{this.config.callbacks.onChatStateChange(null,t.ChatState.LLM)}}async*parseSSEStream(t,u,e){const a=new TextDecoder("utf-8");let n="",r="";u.pendingToolCallsMessage=null;const o=()=>u.allChunks.length>u.lastYieldedChunkCount?(u.lastYieldedChunkCount=u.allChunks.length,{type:"message",chunks:[...u.allChunks],message:u.message,finish:!1}):null;for(;;){const{done:i,value:F}=await t.read();if(i)break;let l;for(n+=a.decode(F,{stream:!0});-1!==(l=n.indexOf("\n"));){if(e.signal?.aborted)return void(u.aborted=!0);const t=n.slice(0,l).trim();if(n=n.slice(l+1),!t.startsWith("data: {"))return u.streamingError=new D(new s("Failed to parse SSE response")),void(yield{type:"error",error:u.streamingError});let a;try{a=JSON.parse(t.slice(6).trim())}catch{return u.streamingError=new D(new s("Failed to parse SSE JSON")),void(yield{type:"error",error:u.streamingError})}if("success"!==a.status)return u.streamingError=new D(new s(a.reason)),void(yield{type:"error",error:u.streamingError});if(r.length>0&&"message"!=a.type){u.newMessageHistory.push({role:"assistant",type:"message",content:r}),r="";const t=o();t&&(yield t)}"message"!==a.type?"tool_call"!==a.type||null==a.tool_calls?"tool"!==a.role||"tool_call"===a.type&&u.newMessageHistory.push({role:a.role,type:a.type,content:a.content,tool_call_id:a.tool_call_id}):(u.newMessageHistory.push({role:"assistant",type:a.type,content:a.content,tool_calls:a.tool_calls}),u.pendingToolCallsMessage=a,yield{type:"tool_call",tool_calls:a.tool_calls}):(r+=a.content,u.message+=a.content,u.allChunks.push(a.content))}const c=o();c&&(yield c)}const i=n.trim();if(i.length>0){if(!i.startsWith("data: {"))return u.streamingError=new D(new s("Failed to parse SSE response")),void(yield{type:"error",error:u.streamingError});let t;try{t=JSON.parse(i.slice(6).trim())}catch{return u.streamingError=new D(new s("Failed to parse SSE JSON")),void(yield{type:"error",error:u.streamingError})}if("success"!==t.status)return u.streamingError=new D(new s(t.reason)),void(yield{type:"error",error:u.streamingError});if("message"===t.type)r+=t.content,u.message+=t.content,u.allChunks.push(t.content);else if("tool_call"===t.type&&null!=t.tool_calls){if(r.length>0){u.newMessageHistory.push({role:"assistant",type:"message",content:r}),r="";const t=o();t&&(yield t)}u.newMessageHistory.push({role:"assistant",type:t.type,content:t.content,tool_calls:t.tool_calls}),u.pendingToolCallsMessage=t,yield{type:"tool_call",tool_calls:t.tool_calls}}}r.length>0&&u.newMessageHistory.push({role:"assistant",type:"message",content:r})}async*executeToolCalls(t,u){const e=t=>{for(const e of u)if(e.name===t)return e;return null},D=[];for(const u of t.pendingToolCallsMessage.tool_calls){const t=e(u.function.name);null!=t&&D.push((async()=>{try{const e=await t.call(JSON.parse(u.function.arguments));return{toolCallId:u.id,chatTool:t,chatToolResult:e}}catch(e){return{toolCallId:u.id,chatTool:t,chatToolResult:{error:e.message}}}})())}const s=await Promise.all(D);t.lastToolCallResults=s;for(const u of s)t.newMessageHistory.push({role:"tool",content:JSON.stringify(u.chatToolResult),tool_call_id:u.toolCallId}),yield{type:"tool_result",tool_call_id:u.toolCallId,result:u.chatToolResult}}addToHistory(t){this.messageHistory.push(t)}getHistory(){return this.messageHistory}}const B=`data:application/javascript,${encodeURIComponent("\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.isRecording = true;\n \n // Listen for stop message from main thread\n this.port.onmessage = (event) => {\n if (event.data.type === 'stop') {\n this.isRecording = false;\n // Send confirmation back to main thread\n this.port.postMessage({ type: 'stopped' });\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (input && input.length > 0 && this.isRecording) {\n // Clone the audio data and send to main thread\n const channelData = new Float32Array(input[0]);\n this.port.postMessage({ type: 'audio', data: channelData });\n }\n // Return true to keep the processor alive until stopped\n return this.isRecording;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n")}`;class w{audioContext=null;mediaStream=null;workletNode=null;sourceNode=null;audioChunks=[];isRecordingState=!1;channels;targetSampleRate;constructor(t={}){this.channels=t.channels||1,this.targetSampleRate=t.targetSampleRate}async start(){if(this.isRecordingState)throw new Error("WavRecorder is already recording");if(this.mediaStream=await navigator.mediaDevices.getUserMedia({audio:!0}),this.audioContext=new AudioContext,"running"!==this.audioContext.state)try{await this.audioContext.resume()}catch(t){console.warn("WavRecorder: Failed to resume AudioContext:",t)}await this.audioContext.audioWorklet.addModule(B),this.sourceNode=this.audioContext.createMediaStreamSource(this.mediaStream),this.workletNode=new AudioWorkletNode(this.audioContext,"recorder-processor"),this.audioChunks=[],this.workletNode.port.onmessage=t=>{"audio"===t.data.type&&this.audioChunks.push(t.data.data)},this.sourceNode.connect(this.workletNode),this.workletNode.connect(this.audioContext.destination),this.isRecordingState=!0}async stop(){if(!this.isRecordingState)throw new Error("WavRecorder is not recording");this.isRecordingState=!1,await new Promise(t=>{this.workletNode.port.onmessage=u=>{"stopped"===u.data.type?t():"audio"===u.data.type&&this.audioChunks.push(u.data.data)},this.workletNode.port.postMessage({type:"stop"})}),this.workletNode?.disconnect(),this.sourceNode?.disconnect(),this.mediaStream?.getTracks().forEach(t=>t.stop());const t=this.audioChunks,u=this.audioContext.sampleRate;try{const e=t.reduce((t,u)=>t+u.length,0),D=new Float32Array(e);let s,a,n=0;for(const u of t)D.set(u,n),n+=u.length;this.targetSampleRate&&this.targetSampleRate!==u?(s=await p(D,u,this.targetSampleRate,this.channels),a=this.targetSampleRate):(s=D,a=u);const r=c(s,a,this.channels),o=new Blob([r],{type:"audio/wav"});return new File([o],"recording.wav",{type:"audio/wav"})}finally{await(this.audioContext?.close()),this.audioContext=null,this.mediaStream=null,this.workletNode=null,this.sourceNode=null,this.audioChunks=[]}}isRecording(){return this.isRecordingState}}class y extends EventTarget{pc;dc;streams=[];pingTime;pingIntervalId=null;constructor(t,u){super(),this.pc=t,this.dc=u,this.pingTime=Date.now()+3e3,this.pc.addEventListener("track",t=>{this.streams=this.streams.concat(t.streams)}),this.pc.addEventListener("connectionstatechange",()=>{"disconnected"!==this.pc.connectionState&&"failed"!==this.pc.connectionState||this.close()}),this.dc.onopen=()=>{this.pingIntervalId=setInterval(()=>{this.ping(),Date.now()-this.pingTime>5e3&&this.close()},1e3)},this.dc.onclose=()=>{null!=this.pingIntervalId&&clearInterval(this.pingIntervalId)},this.#t({live:!0,code:200,reason:"OK"}),this.setMessageCallback("ping",()=>{this.pingTime=Date.now()})}static async create(t,u,e,D,s){const a=await F.getSessionInfo(t,u);if(!(Array.isArray(a.capability)&&a.capability.some(t=>t.name===o.STF_ONPREMISE||t.name===o.STF_WEBRTC)))return await F.sessionEvent(t,u,i.SESSION_START),null;const n=await F.getIceServers(t,u);let r=await y.createPeerConnection(n),l=r.createDataChannel("message",{protocol:"message"}),c=new y(r,l);s?s.getTracks().forEach(function(t){r.addTrack(t,s)}):r.addTransceiver("audio",{direction:"recvonly"});const h=r.addTransceiver("video",{direction:"recvonly"}),C=RTCRtpReceiver.getCapabilities("video");null!=C&&h.setCodecPreferences(C.codecs);const E=await r.createOffer();await r.setLocalDescription(E);const d=await F.exchangeSDP(t,u,E);return await r.setRemoteDescription(d),await y.waitFor(()=>c.isReady(),100,50),c.changeSize(e,D),c}static async createPeerConnection(t){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:t})}static async waitFor(t,e,D){let s=0;if(await new Promise(u=>{const a=setInterval(()=>{s+=1,s>=D&&(clearInterval(a),u("bad")),t()&&(clearInterval(a),u("good"))},e)}),s>=D)throw new u}isReady(){return this.streams.length>0&&"open"===this.dc.readyState}#t(t){this.dispatchEvent(new CustomEvent("status",{detail:t}))}subscribeStatus(t){return this.addEventListener("status",t),()=>{this.removeEventListener("status",t)}}getStream(){return this.streams[0]}sendMessage(t,u){this.dc.send(JSON.stringify({type:t,data:u}))}ttstf(t){this.sendMessage("ttstf",{message:t})}static BACKPRESSURE_THRESHOLD=524288;sendFile(t,u=65536){return new Promise((e,D)=>{const s=this.pc.createDataChannel("file",{protocol:"file"});s.onerror=t=>{s.close(),D(new Error(`File channel error: ${t}`))},s.addEventListener("message",async a=>{try{if(0===a.data.length){const e=new Uint8Array(await t.arrayBuffer());let a=0;const n=()=>{for(;a<e.length;){if(s.bufferedAmount>y.BACKPRESSURE_THRESHOLD)return s.bufferedAmountLowThreshold=y.BACKPRESSURE_THRESHOLD/2,s.onbufferedamountlow=()=>{s.onbufferedamountlow=null,s.onclose=null,n()},void(s.onclose=()=>{s.onbufferedamountlow=null,D(new Error("File channel closed during transfer"))});s.send(e.slice(a,a+u)),a+=u}s.send(new Uint8Array(0))};n()}else s.close(),e(a.data)}catch(t){s.close(),D(t)}})})}async stf(t,u,e){const D=await this.sendFile(t);return this.sendMessage("stf",{message:e,file_ref:D,format:u}),D}recordStart(){this.sendMessage("record-start",{})}recordEndStt(t){this.sendMessage("record-end-stt",{language:t})}recordEndTranslate(t,u){this.sendMessage("record-end-translate",{src_lang:t,dst_lang:u})}changeSize(t,u){this.sendMessage("change-size",{width:t,height:u})}setTemplate(t,u){this.sendMessage("set-template",{model:t,dress:u})}clearBuffer(){this.sendMessage("clear-buffer",{})}ping(){this.sendMessage("ping",{})}setMessageCallback(t,u){const e=e=>{const D=JSON.parse(e.data);D.type===t&&u(D.data)};return this.dc.addEventListener("message",e),()=>{this.dc.removeEventListener("message",e)}}async tts(t,u=!0){return S(t,u)}close(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:408,reason:"Request Timeout"})}closeSelf(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:200,reason:"OK"})}}class T{apiServer;sessionId;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];llmProcessor;chatStateMap=new Map([[t.ChatState.RECORDING,0],[t.ChatState.LLM,0],[t.ChatState.ANALYZING,0],[t.ChatState.SPEAKING,0],[t.ChatState.TTS,0]]);emojiRegex=/[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF43\uDF45-\uDF4A\uDF4C-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDF44(?:\u200D\uD83D\uDFEB)?|\uDF4B(?:\u200D\uD83D\uDFE9)?|\uDFC3(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E-\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4\uDEB5](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE41\uDE43\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED8\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC08(?:\u200D\u2B1B)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC26(?:\u200D(?:\u2B1B|\uD83D\uDD25))?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])))?))?|\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDE42(?:\u200D[\u2194\u2195]\uFE0F?)?|\uDEB6(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3C-\uDD3E\uDDB8\uDDB9\uDDCD\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE8A\uDE8E-\uDEC2\uDEC6\uDEC8\uDECD-\uDEDC\uDEDF-\uDEEA\uDEEF]|\uDDCE(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1|\uDDD1\u200D\uD83E\uDDD2(?:\u200D\uD83E\uDDD2)?|\uDDD2(?:\u200D\uD83E\uDDD2)?))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;sttRecorder=null;sttTimeoutHandle=null;sttTimeoutAudioFile=null;heartbeatIntervalId=null;legacyVoiceChatMode;stream;constructor(u,e,D,s,a){this.apiServer=u,this.sessionId=e,this.perso=D,this.clientTools=s,this.legacyVoiceChatMode=a?.legacyVoiceChatMode??!1,this.stream=a?.stream??null,this.resetChatState(),this.llmProcessor=new f({apiServer:u,sessionId:e,clientTools:s,callbacks:{onChatStateChange:(t,u)=>this.setChatState(t,u),onError:t=>this.setError(t),onChatLog:(t,u)=>this.addMessageToChatLog(t,u),onTTSTF:t=>this.processTTSTFInternal(t)}}),this.startHeartbeat(),D&&(D.subscribeStatus(t=>{!1===t.detail?.live&&this.stopHeartbeat()}),D.setMessageCallback("stf",u=>{if(this.chatStateMap.get(t.ChatState.ANALYZING)||this.chatStateMap.get(t.ChatState.SPEAKING))if(this.setChatState(t.ChatState.SPEAKING,t.ChatState.ANALYZING),null!==this.lastStfTimeoutHandle){clearTimeout(this.lastStfTimeoutHandle);let e=Date.now();this.stfTotalDuration+=u.duration+1e3-(e-this.stfTimeoutStartTime),this.stfTimeoutStartTime=e,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,t.ChatState.SPEAKING)},this.stfTotalDuration)}else this.stfTimeoutStartTime=Date.now(),this.stfTotalDuration=u.duration+2e3,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,t.ChatState.SPEAKING)},this.stfTotalDuration)}),D.setMessageCallback("stt",u=>{if(this.setChatState(null,t.ChatState.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:u.text}));else{if(""===u.text)return;this.processChat(u.text)}}),D.setMessageCallback("stt-error",u=>{this.setChatState(null,t.ChatState.ANALYZING)}))}llmJob=null;async processChat(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processLLM(t){return this.pipelineSuppressed=!1,this.llmProcessor.processLLM(t)}getMessageHistory(){return this.llmProcessor.getHistory()}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}async transcribeAudio(t,u){const D=t instanceof File?t:new File([t],"audio.wav",{type:t.type});try{return(await F.makeSTT(this.apiServer,this.sessionId,D,u)).text}catch(t){if(t instanceof e)throw new a(t);throw t}}async processSTF(u,e,D){if(!this.perso)throw new Error("processSTF requires WebRTC (STF mode)");return this.pipelineSuppressed?"":(this.setChatState(t.ChatState.ANALYZING),await this.perso.stf(u,e,D))}async processTTS(u,D={}){const{resample:s=!0}=D,a=this.removeEmoji(u).trim();if(0===a.length)return;if(this.pipelineSuppressed)return;const o=/[.?!]$/.test(a)?a:a+".";this.setChatState(t.ChatState.TTS,null);try{const{audio:t}=await F.makeTTS(this.apiServer,{sessionId:this.sessionId,text:o});return await S(t,s)}catch(t){t instanceof e||t instanceof r?this.setError(new n(t)):this.setError(t instanceof Error?t:new Error(String(t)))}finally{this.setChatState(null,t.ChatState.TTS)}}startVoiceChat(){if(!this.perso)throw new Error("startVoiceChat requires WebRTC (STF mode)");return this.pipelineSuppressed=!1,this.setChatState(t.ChatState.RECORDING),this.perso.recordStart()}stopVoiceChat(){if(!this.perso)throw new Error("stopVoiceChat requires WebRTC (STF mode)");this.setChatState(t.ChatState.ANALYZING,t.ChatState.RECORDING),this.perso.recordEndStt()}async startProcessSTT(u){if(this.sttRecorder?.isRecording())throw new Error("STT recording is already in progress");this.pipelineSuppressed=!1,this.setChatState(t.ChatState.RECORDING);try{this.sttRecorder=new w({targetSampleRate:16e3}),await this.sttRecorder.start(),u&&u>0&&(this.sttTimeoutHandle=setTimeout(async()=>{if(this.sttTimeoutHandle=null,this.sttRecorder?.isRecording()){try{this.sttTimeoutAudioFile=await this.sttRecorder.stop()}catch{this.sttTimeoutAudioFile=null,this.setChatState(null,t.ChatState.RECORDING)}this.sttRecorder=null}},u))}catch(u){throw this.setChatState(null,t.ChatState.RECORDING),this.sttRecorder=null,u}}lastRecordedAudioFile=null;async stopProcessSTT(u){let D;if(this.sttTimeoutHandle&&(clearTimeout(this.sttTimeoutHandle),this.sttTimeoutHandle=null),this.setChatState(null,t.ChatState.RECORDING),this.sttTimeoutAudioFile)D=this.sttTimeoutAudioFile,this.sttTimeoutAudioFile=null;else{if(!this.sttRecorder?.isRecording())throw this.sttRecorder?(this.sttRecorder=null,new Error("STT recording is not in progress")):new Error("STT recording has not been started");D=await this.sttRecorder.stop(),this.sttRecorder=null}this.lastRecordedAudioFile=D;try{return(await F.makeSTT(this.apiServer,this.sessionId,D,u)).text}catch(t){if(t instanceof e)throw new a(t);throw t}}isSTTRecording(){return(this.sttRecorder?.isRecording()??!1)||null!==this.sttTimeoutAudioFile}changeSize(t,u){this.perso?.changeSize(t,u)}async clearBuffer(){this.perso?.clearBuffer(),await this.clearLLMJob(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.pipelineSuppressed=!0,this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()??null}getRemoteStream(){return this.perso?.getStream()}getLocalStream(){return this.stream}stopSession(){this.close()}onClose(t){return this.perso?this.perso.subscribeStatus(u=>{null!=u.detail&&!1===u.detail.live&&t(200===u.detail.code)}):()=>{}}subscribeChatStates(t){const u=u=>{t(u.detail.status)};return this.chatStatesHandler.addEventListener("status",u),()=>{this.chatStatesHandler.removeEventListener("status",u)}}subscribeChatLog(t){const u=u=>{t(u.detail.chatLog)};return this.chatLogHandler.addEventListener("chatLog",u),()=>{this.chatLogHandler.removeEventListener("chatLog",u)}}setSttResultCallback(t){const u=u=>{t(u.detail)};return this.sttEventHandler=new EventTarget,this.sttEventHandler.addEventListener("stt",u),()=>{this.sttEventHandler?.removeEventListener("stt",u),this.sttEventHandler=null}}setErrorHandler(t){const u=u=>{t(u.detail.error)};return this.errorHandler.addEventListener("error",u),()=>{this.errorHandler.removeEventListener("error",u)}}getSessionId(){return this.sessionId}async processChatInternal(u){this.setChatState(t.ChatState.LLM);const a=this.clientTools.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),n=new Array;null===u||(u instanceof Array?n.push(...u):"string"==typeof u&&n.push({role:"user",content:u}));const r=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...n],tools:a}),headers:{"Content-Type":"application/json"},method:"POST"});if(!r.ok){const u=await r.json(),s=new D(new e(r.status,u.errors[0].code,u.errors[0].detail,u.errors[0].attr));return this.setError(s),void this.setChatState(null,t.ChatState.LLM)}const o=r.body?.getReader(),i=new TextDecoder("utf-8");let F="",l=null,c="";for(;;){const{done:u,value:e}=await o.read();if(u)break;let a;for(c+=i.decode(e,{stream:!0});-1!==(a=c.indexOf("\n"));){if(this.llmCancel)return F.length>0&&this.addMessageToChatLog(F,!1),void this.setChatState(null,t.ChatState.LLM);const u=c.slice(0,a).trim();if(c=c.slice(a+1),!u.startsWith("data: {")){const u=new D(new s("Failed to parse SSE response"));return this.setError(u),void this.setChatState(null,t.ChatState.LLM)}const e=JSON.parse(u.slice(6).trim());if("success"!==e.status){const u=new D(new s(e.reason));return this.setError(u),void this.setChatState(null,t.ChatState.LLM)}F.length>0&&"message"!=e.type&&(n.push({role:"assistant",type:"message",content:F}),this.addMessageToChatLog(F,!1),F=""),"message"!==e.type?"tool_call"!==e.type||null==e.tool_calls?"tool"!==e.role||"tool_call"===e.type&&n.push({role:e.role,type:e.type,content:e.content,tool_call_id:e.tool_call_id}):(n.push({role:"assistant",type:e.type,content:e.content,tool_calls:e.tool_calls}),l=e):(F+=e.content,this.processTTSTFInternal(e.content))}}if(this.llmCancel)this.setChatState(null,t.ChatState.LLM);else{if(null!=l){const t=[];for(const u of l.tool_calls){const e=this.getChatTool(this.clientTools,u.function.name);null!=e&&t.push(new Promise(async t=>{try{const D=await e.call(JSON.parse(u.function.arguments));t({toolCallId:u.id,chatTool:e,chatToolResult:D})}catch(D){t({toolCallId:u.id,chatTool:e,chatToolResult:{result:"error!"}})}}))}const u=await Promise.all(t);for(const t of u)n.push({role:"tool",content:JSON.stringify(t.chatToolResult),tool_call_id:t.toolCallId});const e=u.length>0&&l.tool_calls.length!==u.length,D=u.some(t=>!t.chatTool.executeOnly);e||D?await this.processChatInternal(n):this.messageHistory.push(...n)}else this.messageHistory.push(...n);this.setChatState(null,t.ChatState.LLM)}}getChatTool(t,u){for(const e of t)if(e.name===u)return e;return null}llmCancel=!1;pipelineSuppressed=!1;async clearLLMJob(){null!=this.llmJob&&(this.llmCancel=!0,await this.llmJob,this.llmCancel=!1)}processTTSTFInternal(u){const e=this.removeEmoji(u).trim();0!==e.length&&this.perso&&(this.setChatState(t.ChatState.ANALYZING),this.perso.ttstf(e))}addMessageToChatLog(t,u){this.chatLog=[{text:t,isUser:u,timestamp:new Date},...this.chatLog],this.chatLogHandler.dispatchEvent(new CustomEvent("chatLog",{detail:{chatLog:this.chatLog}}))}setChatState(u=null,e=null){const D=new Map(this.chatStateMap);function s(u){u===t.ChatState.ANALYZING?D.set(u,(D.get(u)||0)+1):D.set(u,1)}function a(u){u===t.ChatState.ANALYZING?D.set(u,Math.max((D.get(u)||0)-1,0)):D.set(u,0)}if(null!=u)if(u instanceof Array)for(let t of u)s(t);else s(u);if(null!=e)if(e instanceof Array)for(let t of e)a(t);else a(e);const n=this.exchangeChatStateMapToSet(this.chatStateMap),r=this.exchangeChatStateMapToSet(D);this.chatStateMap=D,this.isEqualChatStateMap(n,r)||this.dispatchChatState(r)}resetChatState(){this.chatStateMap=new Map([[t.ChatState.RECORDING,0],[t.ChatState.LLM,0],[t.ChatState.ANALYZING,0],[t.ChatState.SPEAKING,0],[t.ChatState.TTS,0]]),this.dispatchChatState(this.exchangeChatStateMapToSet(this.chatStateMap))}exchangeChatStateMapToSet(t){const u=new Set;for(const e of t)e[1]>0&&u.add(e[0]);return u}dispatchChatState(t){this.chatStatesHandler.dispatchEvent(new CustomEvent("status",{detail:{status:t}}))}isEqualChatStateMap(t,u){if(t.size!==u.size)return!1;for(const e of t)if(t.has(e)!==u.has(e))return!1;return!0}setError(t){this.errorHandler.dispatchEvent(new CustomEvent("error",{detail:{error:t}}))}close(){this.stopHeartbeat(),this.perso?.closeSelf()}startHeartbeat(){const t=async()=>{try{await F.sessionEvent(this.apiServer,this.sessionId,i.SESSION_DURING),null!==this.heartbeatIntervalId&&(this.heartbeatIntervalId=setTimeout(t,1e4))}catch(t){t instanceof e?this.setError(t):this.setError(t instanceof Error?t:new Error(String(t))),this.close()}};this.heartbeatIntervalId=setTimeout(t,1e4)}stopHeartbeat(){null!==this.heartbeatIntervalId&&(clearTimeout(this.heartbeatIntervalId),this.heartbeatIntervalId=null)}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function A(t,u,e,D,s,a){if("boolean"!=typeof s){const a=await y.create(t,u,e,D);return new T(t,u,a,s)}const n=a??[];let r,o;if(s)r=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),o=()=>{};else{const t=new AudioContext,u=t.createOscillator();u.frequency.value=0;const e=t.createMediaStreamDestination();u.connect(e),u.start(),r=e.stream,o=()=>{u.stop(),u.disconnect(e),t.close()}}const i=await y.create(t,u,e,D,r);if(!i)return o(),new T(t,u,null,n);const F=new T(t,u,i,n,{stream:r,legacyVoiceChatMode:!0});return F.onClose(()=>{o()}),F}async function v(t,u){return await F.getLLMs(t,u)}async function R(t,u){return await F.getTTSs(t,u)}async function I(t,u){return await F.getSTTs(t,u)}async function L(t,u){return await F.getModelStyles(t,u)}async function M(t,u){return await F.getBackgroundImages(t,u)}async function b(t,u){return await F.getPrompts(t,u)}async function N(t,u){return await F.getDocuments(t,u)}async function _(t,u){return await F.getMcpServers(t,u)}return t.ApiError=e,t.ChatTool=class{name;description;parameters;call;executeOnly;constructor(t,u,e,D,s=!1){this.name=t,this.description=u,this.parameters=e,this.call=D,this.executeOnly=s}},t.LLMError=D,t.LLMStreamingResponseError=s,t.LlmProcessor=f,t.STTError=a,t.Session=T,t.TTSDecodeError=r,t.TTSError=n,t.TTS_TARGET_SAMPLE_RATE=g,t.WavRecorder=w,t.createSession=async function(t,u,e,D,s,a){return"boolean"==typeof s?await A(t,u,e,D,s,a??[]):await A(t,u,e,D,s)},t.createSessionId=async(t,u,e)=>{"undefined"!=typeof window&&console.warn("[perso-interactive-sdk-web] WARNING: createSessionId is being called from the browser. This exposes your API key and is not recommended for production. Use server-side session creation with 'perso-interactive-sdk-web/server' instead. See: https://github.com/perso-ai/perso-interactive-sdk-web#server-side");const D={capability:[],...e};e.using_stf_webrtc&&D.capability.push(o.STF_WEBRTC),e?.llm_type&&(D.capability.push(o.LLM),D.llm_type=e.llm_type),e?.tts_type&&(D.capability.push(o.TTS),D.tts_type=e.tts_type),e?.stt_type&&(D.capability.push(o.STT),D.stt_type=e.stt_type);const s=await fetch(`${t}/api/v1/session/`,{body:JSON.stringify(D),headers:{"PersoLive-APIKey":u,"Content-Type":"application/json"},method:"POST"});return(await F.parseJson(s)).session_id},t.createWavRecorder=function(t){return new w(t)},t.getAllSettings=async function(t,u){const e=await v(t,u),D=await L(t,u),s=await M(t,u);return{llms:e,ttsTypes:await R(t,u),sttTypes:await I(t,u),modelStyles:D,backgroundImages:s,prompts:await b(t,u),documents:await N(t,u),mcpServers:await _(t,u)}},t.getBackgroundImages=M,t.getDocuments=N,t.getLLMs=v,t.getMcpServers=_,t.getModelStyles=L,t.getPrompts=b,t.getSTTs=I,t.getSessionInfo=async function(t,u){return await F.getSessionInfo(t,u)},t.getTTSs=R,t.getWavSampleRate=function(t){const u=new DataView(t);if(t.byteLength<28)throw new l("File too small to be a valid WAV");if("RIFF"!==h(u,0,4))throw new l("Missing RIFF header");let e=12;for(;e<t.byteLength-8;){const D=h(u,e,4),s=u.getUint32(e+4,!0);if("fmt "===D){if(e+16>t.byteLength)throw new l("fmt chunk extends beyond file");return u.getUint32(e+12,!0)}const a=e+8+s;if(a<=e)break;e=a}throw new l("Missing fmt chunk")},t}({});
1
+ var PersoInteractive=function(t){"use strict";class u extends Error{constructor(){super("WebRTC connection timeout")}}class e extends Error{errorCode;code;detail;attr;constructor(t,u,e,D){let s;s=null!=D?`${t}:${D}_${e}`:`${t}:${e}`,super(s),this.errorCode=t,this.code=u,this.detail=e,this.attr=D}}class D extends Error{underlyingError;constructor(t){super(),this.underlyingError=t}}class s extends Error{description;constructor(t){super(),this.description=t}}class a extends Error{underlyingError;constructor(t){super(`STT Error: ${t.detail}`),this.underlyingError=t}}class n extends Error{underlyingError;constructor(t){super(t.message),this.underlyingError=t}}class r extends Error{description;constructor(t){super(`TTS decode error: ${t}`),this.description=t}}var o,i;!function(t){t.LLM="LLM",t.TTS="TTS",t.STT="STT",t.STF_ONPREMISE="STF_ONPREMISE",t.STF_WEBRTC="STF_WEBRTC"}(o||(o={})),function(t){t.SESSION_START="SESSION_START",t.SESSION_DURING="SESSION_DURING",t.SESSION_LOG="SESSION_LOG",t.SESSION_END="SESSION_END",t.SESSION_ERROR="SESSION_ERROR",t.SESSION_TTS="SESSION_TTS",t.SESSION_STT="SESSION_STT",t.SESSION_LLM="SESSION_LLM"}(i||(i={}));class F{static async getLLMs(t,u){const e=fetch(`${t}/api/v1/settings/llm_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getModelStyles(t,u){const e=fetch(`${t}/api/v1/settings/modelstyle/?platform_type=webrtc`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getBackgroundImages(t,u){const e=fetch(`${t}/api/v1/background_image/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getTTSs(t,u){const e=fetch(`${t}/api/v1/settings/tts_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getSTTs(t,u){const e=fetch(`${t}/api/v1/settings/stt_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async makeTTS(t,{sessionId:u,text:e}){const D=await fetch(`${t}/api/v1/session/${u}/tts/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:e})});return await this.parseJson(D)}static async getPrompts(t,u){const e=fetch(`${t}/api/v1/prompt/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getDocuments(t,u){const e=fetch(`${t}/api/v1/document/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getMcpServers(t,u){const e=fetch(`${t}/api/v1/settings/mcp_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),D=await e;return await this.parseJson(D)}static async getSessionInfo(t,u){const e=fetch(`${t}/api/v1/session/${u}/`,{method:"GET"}),D=await e;return await this.parseJson(D)}static async sessionEvent(t,u,e){const D=await fetch(`${t}/api/v1/session/${u}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:e})});await this.parseJson(D)}static async makeSTT(t,u,e,D){const s=new FormData;s.append("audio",e),D&&s.append("language",D);const a=await fetch(`${t}/api/v1/session/${u}/stt/`,{method:"POST",body:s});return await this.parseJson(a)}static async makeLLM(t,u,D,s){const a=await fetch(`${t}/api/v1/session/${u}/llm/v2/`,{body:JSON.stringify(D),headers:{"Content-Type":"application/json"},method:"POST",signal:s});if(!a.ok){const t=await a.json(),u=t.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${a.status} with no error details`,attr:null};throw new e(a.status,u.code,u.detail,u.attr)}return a.body.getReader()}static async getIceServers(t,u){const e=await fetch(`${t}/api/v1/session/${u}/ice-servers/`,{method:"GET"});return(await this.parseJson(e)).ice_servers}static async exchangeSDP(t,u,e){const D=await fetch(`${t}/api/v1/session/${u}/exchange/`,{body:JSON.stringify({client_sdp:e}),headers:{"Content-Type":"application/json"},method:"POST"});return(await this.parseJson(D)).server_sdp}static async parseJson(t){const u=await t.json();if(t.ok)return u;{const D=u.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${t.status} with no error details`,attr:null};throw new e(t.status,D.code,D.detail,D.attr)}}}class l extends Error{constructor(t){super(`WAV parse error: ${t}`),this.name="WavParseError"}}function c(t,u,e=1){const D=2*t.length,s=new ArrayBuffer(44+D),a=new DataView(s);C(a,0,"RIFF"),a.setUint32(4,36+D,!0),C(a,8,"WAVE"),C(a,12,"fmt "),a.setUint32(16,16,!0),a.setUint16(20,1,!0),a.setUint16(22,e,!0),a.setUint32(24,u,!0),a.setUint32(28,u*e*2,!0),a.setUint16(32,2*e,!0),a.setUint16(34,16,!0),C(a,36,"data"),a.setUint32(40,D,!0);let n=44;for(let u=0;u<t.length;u++){const e=Math.max(-1,Math.min(1,t[u]));a.setInt16(n,e<0?32768*e:32767*e,!0),n+=2}return s}function h(t,u,e){let D="";for(let s=0;s<e;s++)D+=String.fromCharCode(t.getUint8(u+s));return D}function C(t,u,e){for(let D=0;D<e.length;D++)t.setUint8(u+D,e.charCodeAt(D))}function E(t,u,e){switch(e){case 8:return(t.getUint8(u)-128)/128;case 16:return t.getInt16(u,!0)/32768;case 24:{const e=t.getUint8(u),D=t.getUint8(u+1),s=t.getUint8(u+2)<<16|D<<8|e;return(s>8388607?s-16777216:s)/8388608}case 32:return t.getInt32(u,!0)/2147483648;default:return 0}}class d extends Error{constructor(t){super(`Audio resample error: ${t}`),this.name="AudioResampleError"}}async function p(t,u,e,D=1){if(0===t.length)throw new d("Cannot resample empty audio data");if(u<=0||e<=0)throw new d(`Invalid sample rate: original=${u}, target=${e}`);if(u===e)return t;try{const s=t.length/u,a=Math.ceil(s*e),n=new OfflineAudioContext(D,t.length,u).createBuffer(D,t.length,u);n.getChannelData(0).set(t);const r=new OfflineAudioContext(D,a,e),o=r.createBufferSource();o.buffer=n,o.connect(r.destination),o.start(0);return(await r.startRendering()).getChannelData(0)}catch(t){const D=t instanceof Error?t.message:String(t);throw new d(`Failed to resample audio from ${u}Hz to ${e}Hz: ${D}`)}}const g=16e3;async function S(t,u=!0){let e;try{const u=atob(t),D=new Array(u.length);for(let t=0;t<u.length;t++)D[t]=u.charCodeAt(t);e=new Uint8Array(D).buffer}catch{throw new r("Invalid Base64 audio data")}const D=function(t){const u=new Uint8Array(t);if(u.length>=4){const t=String.fromCharCode(u[0],u[1],u[2],u[3]);if("RIFF"===t)return"audio/wav";if(t.startsWith("ID3"))return"audio/mpeg";if(255===u[0]&&!(224&~u[1]))return"audio/mpeg"}return"audio/wav"}(e);if(!u)return new Blob([e],{type:D});try{const t=await async function(t,u){if("audio/wav"===u){const u=function(t){const u=new DataView(t);if(t.byteLength<44)throw new l("File too small to be a valid WAV");if("RIFF"!==h(u,0,4))throw new l("Missing RIFF header");if("WAVE"!==h(u,8,4))throw new l("Missing WAVE format identifier");let e=12,D=!1,s=0,a=0,n=0,r=0;for(;e<t.byteLength-8;){const o=h(u,e,4),i=u.getUint32(e+4,!0);if("fmt "===o){if(e+24>t.byteLength)throw new l("fmt chunk extends beyond file");s=u.getUint16(e+8,!0),a=u.getUint16(e+10,!0),n=u.getUint32(e+12,!0),r=u.getUint16(e+22,!0),D=!0,e+=8+i;break}const F=e+8+i;if(F<=e)break;e=F}if(!D)throw new l("Missing fmt chunk");if(1!==s)throw new l(`Unsupported audio format: ${s} (only PCM format 1 is supported)`);if(0===a||a>8)throw new l(`Invalid channel count: ${a}`);if(0===n||n>192e3)throw new l(`Invalid sample rate: ${n}`);if(![8,16,24,32].includes(r))throw new l(`Unsupported bits per sample: ${r}`);let o=-1,i=0;for(;e<t.byteLength-8;){const t=h(u,e,4),D=u.getUint32(e+4,!0);if("data"===t){o=e+8,i=D;break}const s=e+8+D;if(s<=e)break;e=s}if(-1===o)throw new l("Missing data chunk");const F=t.byteLength-o,c=Math.min(i,F),C=r/8,d=Math.floor(c/(C*a)),p=new Float32Array(d*a);let g=0;for(let e=0;e<d*a;e++){const D=o+e*C;if(D+C>t.byteLength)break;p[g++]=Math.max(-1,Math.min(1,E(u,D,r)))}if(2===a){const t=new Float32Array(d);for(let u=0;u<d;u++)t[u]=(p[2*u]+p[2*u+1])/2;return{sampleRate:n,channels:1,bitsPerSample:r,samples:t}}return{sampleRate:n,channels:a,bitsPerSample:r,samples:p.slice(0,g)}}(t);if(u.sampleRate===g)return{samples:u.samples,sampleRate:u.sampleRate};return{samples:await p(u.samples,u.sampleRate,g,u.channels),sampleRate:g}}const e=new AudioContext({sampleRate:g});try{const u=await e.decodeAudioData(t.slice(0));if(u.sampleRate===g)return{samples:u.getChannelData(0),sampleRate:g};return{samples:await p(u.getChannelData(0),u.sampleRate,g,1),sampleRate:g}}finally{await e.close()}}(e,D),u=c(t.samples,g,1);return new Blob([u],{type:"audio/wav"})}catch{return new Blob([e],{type:D})}}var m;t.ChatState=void 0,(m=t.ChatState||(t.ChatState={})).RECORDING="RECORDING",m.LLM="LLM",m.ANALYZING="ANALYZING",m.SPEAKING="SPEAKING",m.TTS="TTS";class f{config;messageHistory=[];constructor(t){this.config=t}async*processLLM(u){if(0===u.message.length)throw new Error("Message cannot be empty");const a=u.tools??this.config.clientTools,n=a.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),r={newMessageHistory:[{role:"user",content:u.message}],allChunks:[],message:"",lastYieldedChunkCount:0,pendingToolCallsMessage:null,aborted:!1,streamingError:null};let o=0,i=[...this.messageHistory,...r.newMessageHistory];this.config.callbacks.onChatStateChange(t.ChatState.LLM,null);try{for(;;){if(u.signal?.aborted)return void(r.allChunks.length>0&&(yield{type:"message",chunks:[...r.allChunks],message:r.message,finish:!0}));let t;try{t=await F.makeLLM(this.config.apiServer,this.config.sessionId,{messages:i,tools:n},u.signal)}catch(t){if(t instanceof e)return void(yield{type:"error",error:new D(t)});throw t}if(r.streamingError=null,yield*this.parseSSEStream(t,r,u),r.streamingError)return;if(r.aborted)return void(r.allChunks.length>0&&(yield{type:"message",chunks:[...r.allChunks],message:r.message,finish:!0}));if(null!=r.pendingToolCallsMessage){yield*this.executeToolCalls(r,a);const t=r.lastToolCallResults,u=t.length>0&&r.pendingToolCallsMessage.tool_calls.length!==t.length,e=t.some(t=>!t.chatTool.executeOnly);if(u||e){if(o++,o>=10)return void(yield{type:"error",error:new D(new s("Tool follow-up loop exceeded maximum rounds (10)"))});i=[...this.messageHistory,...r.newMessageHistory],r.pendingToolCallsMessage=null;continue}}return this.messageHistory.push(...r.newMessageHistory),void(yield{type:"message",chunks:[...r.allChunks],message:r.message,finish:!0})}}finally{this.config.callbacks.onChatStateChange(null,t.ChatState.LLM)}}async*parseSSEStream(t,u,e){const a=new TextDecoder("utf-8");let n="",r="";u.pendingToolCallsMessage=null;const o=()=>u.allChunks.length>u.lastYieldedChunkCount?(u.lastYieldedChunkCount=u.allChunks.length,{type:"message",chunks:[...u.allChunks],message:u.message,finish:!1}):null;for(;;){const{done:i,value:F}=await t.read();if(i)break;let l;for(n+=a.decode(F,{stream:!0});-1!==(l=n.indexOf("\n"));){if(e.signal?.aborted)return void(u.aborted=!0);const t=n.slice(0,l).trim();if(n=n.slice(l+1),!t.startsWith("data: {"))return u.streamingError=new D(new s("Failed to parse SSE response")),void(yield{type:"error",error:u.streamingError});let a;try{a=JSON.parse(t.slice(6).trim())}catch{return u.streamingError=new D(new s("Failed to parse SSE JSON")),void(yield{type:"error",error:u.streamingError})}if("success"!==a.status)return u.streamingError=new D(new s(a.reason)),void(yield{type:"error",error:u.streamingError});if(r.length>0&&"message"!=a.type){u.newMessageHistory.push({role:"assistant",type:"message",content:r}),r="";const t=o();t&&(yield t)}"message"!==a.type?"tool_call"!==a.type||null==a.tool_calls?"tool"!==a.role||"tool_call"===a.type&&u.newMessageHistory.push({role:a.role,type:a.type,content:a.content,tool_call_id:a.tool_call_id}):(u.newMessageHistory.push({role:"assistant",type:a.type,content:a.content,tool_calls:a.tool_calls}),u.pendingToolCallsMessage=a,yield{type:"tool_call",tool_calls:a.tool_calls}):(r+=a.content,u.message+=a.content,u.allChunks.push(a.content))}const c=o();c&&(yield c)}const i=n.trim();if(i.length>0){if(!i.startsWith("data: {"))return u.streamingError=new D(new s("Failed to parse SSE response")),void(yield{type:"error",error:u.streamingError});let t;try{t=JSON.parse(i.slice(6).trim())}catch{return u.streamingError=new D(new s("Failed to parse SSE JSON")),void(yield{type:"error",error:u.streamingError})}if("success"!==t.status)return u.streamingError=new D(new s(t.reason)),void(yield{type:"error",error:u.streamingError});if("message"===t.type)r+=t.content,u.message+=t.content,u.allChunks.push(t.content);else if("tool_call"===t.type&&null!=t.tool_calls){if(r.length>0){u.newMessageHistory.push({role:"assistant",type:"message",content:r}),r="";const t=o();t&&(yield t)}u.newMessageHistory.push({role:"assistant",type:t.type,content:t.content,tool_calls:t.tool_calls}),u.pendingToolCallsMessage=t,yield{type:"tool_call",tool_calls:t.tool_calls}}}r.length>0&&u.newMessageHistory.push({role:"assistant",type:"message",content:r})}async*executeToolCalls(t,u){const e=t=>{for(const e of u)if(e.name===t)return e;return null},D=[];for(const u of t.pendingToolCallsMessage.tool_calls){const t=e(u.function.name);null!=t&&D.push((async()=>{try{const e=await t.call(JSON.parse(u.function.arguments));return{toolCallId:u.id,chatTool:t,chatToolResult:e}}catch(e){return{toolCallId:u.id,chatTool:t,chatToolResult:{error:e.message}}}})())}const s=await Promise.all(D);t.lastToolCallResults=s;for(const u of s)t.newMessageHistory.push({role:"tool",content:JSON.stringify(u.chatToolResult),tool_call_id:u.toolCallId}),yield{type:"tool_result",tool_call_id:u.toolCallId,result:u.chatToolResult}}addToHistory(t){this.messageHistory.push(t)}getHistory(){return this.messageHistory}}const B=`data:application/javascript,${encodeURIComponent("\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.isRecording = true;\n \n // Listen for stop message from main thread\n this.port.onmessage = (event) => {\n if (event.data.type === 'stop') {\n this.isRecording = false;\n // Send confirmation back to main thread\n this.port.postMessage({ type: 'stopped' });\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (input && input.length > 0 && this.isRecording) {\n // Clone the audio data and send to main thread\n const channelData = new Float32Array(input[0]);\n this.port.postMessage({ type: 'audio', data: channelData });\n }\n // Return true to keep the processor alive until stopped\n return this.isRecording;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n")}`;class w{audioContext=null;mediaStream=null;workletNode=null;sourceNode=null;audioChunks=[];isRecordingState=!1;channels;targetSampleRate;constructor(t={}){this.channels=t.channels||1,this.targetSampleRate=t.targetSampleRate}async start(){if(this.isRecordingState)throw new Error("WavRecorder is already recording");if(this.mediaStream=await navigator.mediaDevices.getUserMedia({audio:!0}),this.audioContext=new AudioContext,"running"!==this.audioContext.state)try{await this.audioContext.resume()}catch(t){console.warn("WavRecorder: Failed to resume AudioContext:",t)}await this.audioContext.audioWorklet.addModule(B),this.sourceNode=this.audioContext.createMediaStreamSource(this.mediaStream),this.workletNode=new AudioWorkletNode(this.audioContext,"recorder-processor"),this.audioChunks=[],this.workletNode.port.onmessage=t=>{"audio"===t.data.type&&this.audioChunks.push(t.data.data)},this.sourceNode.connect(this.workletNode),this.workletNode.connect(this.audioContext.destination),this.isRecordingState=!0}async stop(){if(!this.isRecordingState)throw new Error("WavRecorder is not recording");this.isRecordingState=!1,await new Promise(t=>{this.workletNode.port.onmessage=u=>{"stopped"===u.data.type?t():"audio"===u.data.type&&this.audioChunks.push(u.data.data)},this.workletNode.port.postMessage({type:"stop"})}),this.workletNode?.disconnect(),this.sourceNode?.disconnect(),this.mediaStream?.getTracks().forEach(t=>t.stop());const t=this.audioChunks,u=this.audioContext.sampleRate;try{const e=t.reduce((t,u)=>t+u.length,0),D=new Float32Array(e);let s,a,n=0;for(const u of t)D.set(u,n),n+=u.length;this.targetSampleRate&&this.targetSampleRate!==u?(s=await p(D,u,this.targetSampleRate,this.channels),a=this.targetSampleRate):(s=D,a=u);const r=c(s,a,this.channels),o=new Blob([r],{type:"audio/wav"});return new File([o],"recording.wav",{type:"audio/wav"})}finally{await(this.audioContext?.close()),this.audioContext=null,this.mediaStream=null,this.workletNode=null,this.sourceNode=null,this.audioChunks=[]}}isRecording(){return this.isRecordingState}}class y extends EventTarget{pc;dc;streams=[];pingTime;pingIntervalId=null;constructor(t,u){super(),this.pc=t,this.dc=u,this.pingTime=Date.now()+3e3,this.pc.addEventListener("track",t=>{this.streams=this.streams.concat(t.streams)}),this.pc.addEventListener("connectionstatechange",()=>{"disconnected"!==this.pc.connectionState&&"failed"!==this.pc.connectionState||this.close()}),this.dc.onopen=()=>{this.pingIntervalId=setInterval(()=>{this.ping(),Date.now()-this.pingTime>5e3&&this.close()},1e3)},this.dc.onclose=()=>{null!=this.pingIntervalId&&clearInterval(this.pingIntervalId)},this.#t({live:!0,code:200,reason:"OK"}),this.setMessageCallback("ping",()=>{this.pingTime=Date.now()})}static async create(t,u,e,D,s){const a=await F.getSessionInfo(t,u);if(!(Array.isArray(a.capability)&&a.capability.some(t=>t.name===o.STF_ONPREMISE||t.name===o.STF_WEBRTC)))return await F.sessionEvent(t,u,i.SESSION_START),null;const n=await F.getIceServers(t,u);let r=await y.createPeerConnection(n),l=r.createDataChannel("message",{protocol:"message"}),c=new y(r,l);s?s.getTracks().forEach(function(t){r.addTrack(t,s)}):r.addTransceiver("audio",{direction:"recvonly"});const h=r.addTransceiver("video",{direction:"recvonly"}),C=RTCRtpReceiver.getCapabilities("video");null!=C&&h.setCodecPreferences(C.codecs);const E=await r.createOffer();await r.setLocalDescription(E);const d=await F.exchangeSDP(t,u,E);return await r.setRemoteDescription(d),await y.waitFor(()=>c.isReady(),100,50),c.changeSize(e,D),c}static async createPeerConnection(t){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:t})}static async waitFor(t,e,D){let s=0;if(await new Promise(u=>{const a=setInterval(()=>{s+=1,s>=D&&(clearInterval(a),u("bad")),t()&&(clearInterval(a),u("good"))},e)}),s>=D)throw new u}isReady(){return this.streams.length>0&&"open"===this.dc.readyState}#t(t){this.dispatchEvent(new CustomEvent("status",{detail:t}))}subscribeStatus(t){return this.addEventListener("status",t),()=>{this.removeEventListener("status",t)}}getStream(){return this.streams[0]}sendMessage(t,u){this.dc.send(JSON.stringify({type:t,data:u}))}ttstf(t){this.sendMessage("ttstf",{message:t})}static BACKPRESSURE_THRESHOLD=524288;sendFile(t,u=65536){return new Promise((e,D)=>{const s=this.pc.createDataChannel("file",{protocol:"file"});s.onerror=t=>{s.close(),D(new Error(`File channel error: ${t}`))},s.addEventListener("message",async a=>{try{if(0===a.data.length){const e=new Uint8Array(await t.arrayBuffer());let a=0;const n=()=>{for(;a<e.length;){if(s.bufferedAmount>y.BACKPRESSURE_THRESHOLD)return s.bufferedAmountLowThreshold=y.BACKPRESSURE_THRESHOLD/2,s.onbufferedamountlow=()=>{s.onbufferedamountlow=null,s.onclose=null,n()},void(s.onclose=()=>{s.onbufferedamountlow=null,D(new Error("File channel closed during transfer"))});s.send(e.slice(a,a+u)),a+=u}s.send(new Uint8Array(0))};n()}else s.close(),e(a.data)}catch(t){s.close(),D(t)}})})}async stf(t,u,e){const D=await this.sendFile(t);return this.sendMessage("stf",{message:e,file_ref:D,format:u}),D}recordStart(){this.sendMessage("record-start",{})}recordEndStt(t){this.sendMessage("record-end-stt",{language:t})}recordEndTranslate(t,u){this.sendMessage("record-end-translate",{src_lang:t,dst_lang:u})}changeSize(t,u){this.sendMessage("change-size",{width:t,height:u})}setTemplate(t,u){this.sendMessage("set-template",{model:t,dress:u})}clearBuffer(){this.sendMessage("clear-buffer",{})}ping(){this.sendMessage("ping",{})}setMessageCallback(t,u){const e=e=>{const D=JSON.parse(e.data);D.type===t&&u(D.data)};return this.dc.addEventListener("message",e),()=>{this.dc.removeEventListener("message",e)}}async tts(t,u=!0){return S(t,u)}close(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:408,reason:"Request Timeout"})}closeSelf(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:200,reason:"OK"})}}class T{apiServer;sessionId;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];llmProcessor;chatStateMap=new Map([[t.ChatState.RECORDING,0],[t.ChatState.LLM,0],[t.ChatState.ANALYZING,0],[t.ChatState.SPEAKING,0],[t.ChatState.TTS,0]]);emojiRegex=/[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF43\uDF45-\uDF4A\uDF4C-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDF44(?:\u200D\uD83D\uDFEB)?|\uDF4B(?:\u200D\uD83D\uDFE9)?|\uDFC3(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E-\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4\uDEB5](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE41\uDE43\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED8\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC08(?:\u200D\u2B1B)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC26(?:\u200D(?:\u2B1B|\uD83D\uDD25))?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDD1D\uDEEF]\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE]|[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE]|\uDEEF\u200D\uD83D\uDC69\uD83C[\uDFFB-\uDFFE])))?))?|\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDE42(?:\u200D[\u2194\u2195]\uFE0F?)?|\uDEB6(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3C-\uDD3E\uDDB8\uDDB9\uDDCD\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE8A\uDE8E-\uDEC2\uDEC6\uDEC8\uDECD-\uDEDC\uDEDF-\uDEEA\uDEEF]|\uDDCE(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1|\uDDD1\u200D\uD83E\uDDD2(?:\u200D\uD83E\uDDD2)?|\uDDD2(?:\u200D\uD83E\uDDD2)?))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC30\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3\uDE70]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF]|\uDEEF\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;sttRecorder=null;sttTimeoutHandle=null;sttTimeoutAudioFile=null;heartbeatIntervalId=null;legacyVoiceChatMode;stream;constructor(u,e,D,s,a){this.apiServer=u,this.sessionId=e,this.perso=D,this.clientTools=s,this.legacyVoiceChatMode=a?.legacyVoiceChatMode??!1,this.stream=a?.stream??null,this.resetChatState(),this.llmProcessor=new f({apiServer:u,sessionId:e,clientTools:s,callbacks:{onChatStateChange:(t,u)=>this.setChatState(t,u),onError:t=>this.setError(t),onChatLog:(t,u)=>this.addMessageToChatLog(t,u),onTTSTF:t=>this.processTTSTFInternal(t)}}),this.startHeartbeat(),D&&(D.subscribeStatus(t=>{!1===t.detail?.live&&this.stopHeartbeat()}),D.setMessageCallback("stf",u=>{if(this.chatStateMap.get(t.ChatState.ANALYZING)||this.chatStateMap.get(t.ChatState.SPEAKING))if(this.setChatState(t.ChatState.SPEAKING,t.ChatState.ANALYZING),null!==this.lastStfTimeoutHandle){clearTimeout(this.lastStfTimeoutHandle);let e=Date.now();this.stfTotalDuration+=u.duration+1e3-(e-this.stfTimeoutStartTime),this.stfTimeoutStartTime=e,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,t.ChatState.SPEAKING)},this.stfTotalDuration)}else this.stfTimeoutStartTime=Date.now(),this.stfTotalDuration=u.duration+2e3,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,t.ChatState.SPEAKING)},this.stfTotalDuration)}),D.setMessageCallback("stt",u=>{if(this.setChatState(null,t.ChatState.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:u.text}));else{if(""===u.text)return;this.processChat(u.text)}}),D.setMessageCallback("stt-error",u=>{this.setChatState(null,t.ChatState.ANALYZING)}))}llmJob=null;async processChat(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processLLM(t){return this.pipelineSuppressed=!1,this.llmProcessor.processLLM(t)}getMessageHistory(){return this.llmProcessor.getHistory()}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}async transcribeAudio(t,u){const D=t instanceof File?t:new File([t],"audio.wav",{type:t.type});try{return(await F.makeSTT(this.apiServer,this.sessionId,D,u)).text}catch(t){if(t instanceof e)throw new a(t);throw t}}async processSTF(u,e,D){if(!this.perso)throw new Error("processSTF requires WebRTC (STF mode)");this.pipelineSuppressed=!1,this.setChatState(t.ChatState.ANALYZING);try{const s=await this.perso.stf(u,e,D);return this.pipelineSuppressed?(this.setChatState(null,t.ChatState.ANALYZING),s):s}catch(u){throw this.setChatState(null,t.ChatState.ANALYZING),u}}async processTTS(u,D={}){const{resample:s=!0}=D,a=this.removeEmoji(u).trim();if(0===a.length)return;this.pipelineSuppressed=!1;const o=/[.?!]$/.test(a)?a:a+".";this.setChatState(t.ChatState.TTS,null);try{const{audio:t}=await F.makeTTS(this.apiServer,{sessionId:this.sessionId,text:o});if(this.pipelineSuppressed)return;return await S(t,s)}catch(t){t instanceof e||t instanceof r?this.setError(new n(t)):this.setError(t instanceof Error?t:new Error(String(t)))}finally{this.setChatState(null,t.ChatState.TTS)}}startVoiceChat(){if(!this.perso)throw new Error("startVoiceChat requires WebRTC (STF mode)");return this.pipelineSuppressed=!1,this.setChatState(t.ChatState.RECORDING),this.perso.recordStart()}stopVoiceChat(){if(!this.perso)throw new Error("stopVoiceChat requires WebRTC (STF mode)");this.setChatState(t.ChatState.ANALYZING,t.ChatState.RECORDING),this.perso.recordEndStt()}async startProcessSTT(u){if(this.sttRecorder?.isRecording())throw new Error("STT recording is already in progress");this.pipelineSuppressed=!1,this.setChatState(t.ChatState.RECORDING);try{this.sttRecorder=new w({targetSampleRate:16e3}),await this.sttRecorder.start(),u&&u>0&&(this.sttTimeoutHandle=setTimeout(async()=>{if(this.sttTimeoutHandle=null,this.sttRecorder?.isRecording()){try{this.sttTimeoutAudioFile=await this.sttRecorder.stop()}catch{this.sttTimeoutAudioFile=null,this.setChatState(null,t.ChatState.RECORDING)}this.sttRecorder=null}},u))}catch(u){throw this.setChatState(null,t.ChatState.RECORDING),this.sttRecorder=null,u}}lastRecordedAudioFile=null;async stopProcessSTT(u){let D;if(this.sttTimeoutHandle&&(clearTimeout(this.sttTimeoutHandle),this.sttTimeoutHandle=null),this.setChatState(null,t.ChatState.RECORDING),this.sttTimeoutAudioFile)D=this.sttTimeoutAudioFile,this.sttTimeoutAudioFile=null;else{if(!this.sttRecorder?.isRecording())throw this.sttRecorder?(this.sttRecorder=null,new Error("STT recording is not in progress")):new Error("STT recording has not been started");D=await this.sttRecorder.stop(),this.sttRecorder=null}this.lastRecordedAudioFile=D;try{return(await F.makeSTT(this.apiServer,this.sessionId,D,u)).text}catch(t){if(t instanceof e)throw new a(t);throw t}}isSTTRecording(){return(this.sttRecorder?.isRecording()??!1)||null!==this.sttTimeoutAudioFile}changeSize(t,u){this.perso?.changeSize(t,u)}async clearBuffer(){this.perso?.clearBuffer(),await this.clearLLMJob(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.pipelineSuppressed=!0,this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()??null}getRemoteStream(){return this.perso?.getStream()}getLocalStream(){return this.stream}stopSession(){this.close()}onClose(t){return this.perso?this.perso.subscribeStatus(u=>{null!=u.detail&&!1===u.detail.live&&t(200===u.detail.code)}):()=>{}}subscribeChatStates(t){const u=u=>{t(u.detail.status)};return this.chatStatesHandler.addEventListener("status",u),()=>{this.chatStatesHandler.removeEventListener("status",u)}}subscribeChatLog(t){const u=u=>{t(u.detail.chatLog)};return this.chatLogHandler.addEventListener("chatLog",u),()=>{this.chatLogHandler.removeEventListener("chatLog",u)}}setSttResultCallback(t){const u=u=>{t(u.detail)};return this.sttEventHandler=new EventTarget,this.sttEventHandler.addEventListener("stt",u),()=>{this.sttEventHandler?.removeEventListener("stt",u),this.sttEventHandler=null}}setErrorHandler(t){const u=u=>{t(u.detail.error)};return this.errorHandler.addEventListener("error",u),()=>{this.errorHandler.removeEventListener("error",u)}}getSessionId(){return this.sessionId}async processChatInternal(u){this.setChatState(t.ChatState.LLM);const a=this.clientTools.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),n=new Array;null===u||(u instanceof Array?n.push(...u):"string"==typeof u&&n.push({role:"user",content:u}));const r=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...n],tools:a}),headers:{"Content-Type":"application/json"},method:"POST"});if(!r.ok){const u=await r.json(),s=new D(new e(r.status,u.errors[0].code,u.errors[0].detail,u.errors[0].attr));return this.setError(s),void this.setChatState(null,t.ChatState.LLM)}const o=r.body?.getReader(),i=new TextDecoder("utf-8");let F="",l=null,c="";for(;;){const{done:u,value:e}=await o.read();if(u)break;let a;for(c+=i.decode(e,{stream:!0});-1!==(a=c.indexOf("\n"));){if(this.llmCancel)return F.length>0&&this.addMessageToChatLog(F,!1),void this.setChatState(null,t.ChatState.LLM);const u=c.slice(0,a).trim();if(c=c.slice(a+1),!u.startsWith("data: {")){const u=new D(new s("Failed to parse SSE response"));return this.setError(u),void this.setChatState(null,t.ChatState.LLM)}const e=JSON.parse(u.slice(6).trim());if("success"!==e.status){const u=new D(new s(e.reason));return this.setError(u),void this.setChatState(null,t.ChatState.LLM)}F.length>0&&"message"!=e.type&&(n.push({role:"assistant",type:"message",content:F}),this.addMessageToChatLog(F,!1),F=""),"message"!==e.type?"tool_call"!==e.type||null==e.tool_calls?"tool"!==e.role||"tool_call"===e.type&&n.push({role:e.role,type:e.type,content:e.content,tool_call_id:e.tool_call_id}):(n.push({role:"assistant",type:e.type,content:e.content,tool_calls:e.tool_calls}),l=e):(F+=e.content,this.processTTSTFInternal(e.content))}}if(this.llmCancel)this.setChatState(null,t.ChatState.LLM);else{if(null!=l){const t=[];for(const u of l.tool_calls){const e=this.getChatTool(this.clientTools,u.function.name);null!=e&&t.push(new Promise(async t=>{try{const D=await e.call(JSON.parse(u.function.arguments));t({toolCallId:u.id,chatTool:e,chatToolResult:D})}catch(D){t({toolCallId:u.id,chatTool:e,chatToolResult:{result:"error!"}})}}))}const u=await Promise.all(t);for(const t of u)n.push({role:"tool",content:JSON.stringify(t.chatToolResult),tool_call_id:t.toolCallId});const e=u.length>0&&l.tool_calls.length!==u.length,D=u.some(t=>!t.chatTool.executeOnly);e||D?await this.processChatInternal(n):this.messageHistory.push(...n)}else this.messageHistory.push(...n);this.setChatState(null,t.ChatState.LLM)}}getChatTool(t,u){for(const e of t)if(e.name===u)return e;return null}llmCancel=!1;pipelineSuppressed=!1;async clearLLMJob(){null!=this.llmJob&&(this.llmCancel=!0,await this.llmJob,this.llmCancel=!1)}processTTSTFInternal(u){const e=this.removeEmoji(u).trim();0!==e.length&&this.perso&&(this.setChatState(t.ChatState.ANALYZING),this.perso.ttstf(e))}addMessageToChatLog(t,u){this.chatLog=[{text:t,isUser:u,timestamp:new Date},...this.chatLog],this.chatLogHandler.dispatchEvent(new CustomEvent("chatLog",{detail:{chatLog:this.chatLog}}))}setChatState(u=null,e=null){const D=new Map(this.chatStateMap);function s(u){u===t.ChatState.ANALYZING?D.set(u,(D.get(u)||0)+1):D.set(u,1)}function a(u){u===t.ChatState.ANALYZING?D.set(u,Math.max((D.get(u)||0)-1,0)):D.set(u,0)}if(null!=u)if(u instanceof Array)for(let t of u)s(t);else s(u);if(null!=e)if(e instanceof Array)for(let t of e)a(t);else a(e);const n=this.exchangeChatStateMapToSet(this.chatStateMap),r=this.exchangeChatStateMapToSet(D);this.chatStateMap=D,this.isEqualChatStateMap(n,r)||this.dispatchChatState(r)}resetChatState(){this.chatStateMap=new Map([[t.ChatState.RECORDING,0],[t.ChatState.LLM,0],[t.ChatState.ANALYZING,0],[t.ChatState.SPEAKING,0],[t.ChatState.TTS,0]]),this.dispatchChatState(this.exchangeChatStateMapToSet(this.chatStateMap))}exchangeChatStateMapToSet(t){const u=new Set;for(const e of t)e[1]>0&&u.add(e[0]);return u}dispatchChatState(t){this.chatStatesHandler.dispatchEvent(new CustomEvent("status",{detail:{status:t}}))}isEqualChatStateMap(t,u){if(t.size!==u.size)return!1;for(const e of t)if(t.has(e)!==u.has(e))return!1;return!0}setError(t){this.errorHandler.dispatchEvent(new CustomEvent("error",{detail:{error:t}}))}close(){this.stopHeartbeat(),this.perso?.closeSelf()}startHeartbeat(){const t=async()=>{try{await F.sessionEvent(this.apiServer,this.sessionId,i.SESSION_DURING),null!==this.heartbeatIntervalId&&(this.heartbeatIntervalId=setTimeout(t,1e4))}catch(t){t instanceof e?this.setError(t):this.setError(t instanceof Error?t:new Error(String(t))),this.close()}};this.heartbeatIntervalId=setTimeout(t,1e4)}stopHeartbeat(){null!==this.heartbeatIntervalId&&(clearTimeout(this.heartbeatIntervalId),this.heartbeatIntervalId=null)}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function A(t,u,e,D,s,a){if("boolean"!=typeof s){const a=await y.create(t,u,e,D);return new T(t,u,a,s)}const n=a??[];let r,o;if(s)r=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),o=()=>{};else{const t=new AudioContext,u=t.createOscillator();u.frequency.value=0;const e=t.createMediaStreamDestination();u.connect(e),u.start(),r=e.stream,o=()=>{u.stop(),u.disconnect(e),t.close()}}const i=await y.create(t,u,e,D,r);if(!i)return o(),new T(t,u,null,n);const F=new T(t,u,i,n,{stream:r,legacyVoiceChatMode:!0});return F.onClose(()=>{o()}),F}async function v(t,u){return await F.getLLMs(t,u)}async function R(t,u){return await F.getTTSs(t,u)}async function I(t,u){return await F.getSTTs(t,u)}async function L(t,u){return await F.getModelStyles(t,u)}async function M(t,u){return await F.getBackgroundImages(t,u)}async function b(t,u){return await F.getPrompts(t,u)}async function N(t,u){return await F.getDocuments(t,u)}async function _(t,u){return await F.getMcpServers(t,u)}return t.ApiError=e,t.ChatTool=class{name;description;parameters;call;executeOnly;constructor(t,u,e,D,s=!1){this.name=t,this.description=u,this.parameters=e,this.call=D,this.executeOnly=s}},t.LLMError=D,t.LLMStreamingResponseError=s,t.LlmProcessor=f,t.STTError=a,t.Session=T,t.TTSDecodeError=r,t.TTSError=n,t.TTS_TARGET_SAMPLE_RATE=g,t.WavRecorder=w,t.createSession=async function(t,u,e,D,s,a){return"boolean"==typeof s?await A(t,u,e,D,s,a??[]):await A(t,u,e,D,s)},t.createSessionId=async(t,u,e)=>{"undefined"!=typeof window&&console.warn("[perso-interactive-sdk-web] WARNING: createSessionId is being called from the browser. This exposes your API key and is not recommended for production. Use server-side session creation with 'perso-interactive-sdk-web/server' instead. See: https://github.com/perso-ai/perso-interactive-sdk-web#server-side");const D={capability:[],...e};e.using_stf_webrtc&&D.capability.push(o.STF_WEBRTC),e?.llm_type&&(D.capability.push(o.LLM),D.llm_type=e.llm_type),e?.tts_type&&(D.capability.push(o.TTS),D.tts_type=e.tts_type),e?.stt_type&&(D.capability.push(o.STT),D.stt_type=e.stt_type);const s=await fetch(`${t}/api/v1/session/`,{body:JSON.stringify(D),headers:{"PersoLive-APIKey":u,"Content-Type":"application/json"},method:"POST"});return(await F.parseJson(s)).session_id},t.createWavRecorder=function(t){return new w(t)},t.getAllSettings=async function(t,u){const e=await v(t,u),D=await L(t,u),s=await M(t,u);return{llms:e,ttsTypes:await R(t,u),sttTypes:await I(t,u),modelStyles:D,backgroundImages:s,prompts:await b(t,u),documents:await N(t,u),mcpServers:await _(t,u)}},t.getBackgroundImages=M,t.getDocuments=N,t.getLLMs=v,t.getMcpServers=_,t.getModelStyles=L,t.getPrompts=b,t.getSTTs=I,t.getSessionInfo=async function(t,u){return await F.getSessionInfo(t,u)},t.getTTSs=R,t.getWavSampleRate=function(t){const u=new DataView(t);if(t.byteLength<28)throw new l("File too small to be a valid WAV");if("RIFF"!==h(u,0,4))throw new l("Missing RIFF header");let e=12;for(;e<t.byteLength-8;){const D=h(u,e,4),s=u.getUint32(e+4,!0);if("fmt "===D){if(e+16>t.byteLength)throw new l("fmt chunk extends beyond file");return u.getUint32(e+12,!0)}const a=e+8+s;if(a<=e)break;e=a}throw new l("Missing fmt chunk")},t}({});
@@ -1 +1 @@
1
- import t from"emoji-regex";class e extends Error{constructor(){super("WebRTC connection timeout")}}class s extends Error{errorCode;code;detail;attr;constructor(t,e,s,a){let n;n=null!=a?`${t}:${a}_${s}`:`${t}:${s}`,super(n),this.errorCode=t,this.code=e,this.detail=s,this.attr=a}}class a extends Error{underlyingError;constructor(t){super(),this.underlyingError=t}}class n extends Error{description;constructor(t){super(),this.description=t}}class r extends Error{underlyingError;constructor(t){super(`STT Error: ${t.detail}`),this.underlyingError=t}}class o extends Error{underlyingError;constructor(t){super(t.message),this.underlyingError=t}}class i extends Error{description;constructor(t){super(`TTS decode error: ${t}`),this.description=t}}var l,c;!function(t){t.LLM="LLM",t.TTS="TTS",t.STT="STT",t.STF_ONPREMISE="STF_ONPREMISE",t.STF_WEBRTC="STF_WEBRTC"}(l||(l={})),function(t){t.SESSION_START="SESSION_START",t.SESSION_DURING="SESSION_DURING",t.SESSION_LOG="SESSION_LOG",t.SESSION_END="SESSION_END",t.SESSION_ERROR="SESSION_ERROR",t.SESSION_TTS="SESSION_TTS",t.SESSION_STT="SESSION_STT",t.SESSION_LLM="SESSION_LLM"}(c||(c={}));class h{static async getLLMs(t,e){const s=fetch(`${t}/api/v1/settings/llm_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getModelStyles(t,e){const s=fetch(`${t}/api/v1/settings/modelstyle/?platform_type=webrtc`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getBackgroundImages(t,e){const s=fetch(`${t}/api/v1/background_image/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getTTSs(t,e){const s=fetch(`${t}/api/v1/settings/tts_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSTTs(t,e){const s=fetch(`${t}/api/v1/settings/stt_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async makeTTS(t,{sessionId:e,text:s}){const a=await fetch(`${t}/api/v1/session/${e}/tts/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:s})});return await this.parseJson(a)}static async getPrompts(t,e){const s=fetch(`${t}/api/v1/prompt/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getDocuments(t,e){const s=fetch(`${t}/api/v1/document/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getMcpServers(t,e){const s=fetch(`${t}/api/v1/settings/mcp_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSessionInfo(t,e){const s=fetch(`${t}/api/v1/session/${e}/`,{method:"GET"}),a=await s;return await this.parseJson(a)}static async sessionEvent(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await this.parseJson(a)}static async makeSTT(t,e,s,a){const n=new FormData;n.append("audio",s),a&&n.append("language",a);const r=await fetch(`${t}/api/v1/session/${e}/stt/`,{method:"POST",body:n});return await this.parseJson(r)}static async makeLLM(t,e,a,n){const r=await fetch(`${t}/api/v1/session/${e}/llm/v2/`,{body:JSON.stringify(a),headers:{"Content-Type":"application/json"},method:"POST",signal:n});if(!r.ok){const t=await r.json(),e=t.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${r.status} with no error details`,attr:null};throw new s(r.status,e.code,e.detail,e.attr)}return r.body.getReader()}static async getIceServers(t,e){const s=await fetch(`${t}/api/v1/session/${e}/ice-servers/`,{method:"GET"});return(await this.parseJson(s)).ice_servers}static async exchangeSDP(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/exchange/`,{body:JSON.stringify({client_sdp:s}),headers:{"Content-Type":"application/json"},method:"POST"});return(await this.parseJson(a)).server_sdp}static async parseJson(t){const e=await t.json();if(t.ok)return e;{const a=e.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${t.status} with no error details`,attr:null};throw new s(t.status,a.code,a.detail,a.attr)}}}class d extends Error{constructor(t){super(`WAV parse error: ${t}`),this.name="WavParseError"}}function u(t,e,s=1){const a=2*t.length,n=new ArrayBuffer(44+a),r=new DataView(n);m(r,0,"RIFF"),r.setUint32(4,36+a,!0),m(r,8,"WAVE"),m(r,12,"fmt "),r.setUint32(16,16,!0),r.setUint16(20,1,!0),r.setUint16(22,s,!0),r.setUint32(24,e,!0),r.setUint32(28,e*s*2,!0),r.setUint16(32,2*s,!0),r.setUint16(34,16,!0),m(r,36,"data"),r.setUint32(40,a,!0);let o=44;for(let e=0;e<t.length;e++){const s=Math.max(-1,Math.min(1,t[e]));r.setInt16(o,s<0?32768*s:32767*s,!0),o+=2}return n}function p(t){const e=new DataView(t);if(t.byteLength<28)throw new d("File too small to be a valid WAV");if("RIFF"!==g(e,0,4))throw new d("Missing RIFF header");let s=12;for(;s<t.byteLength-8;){const a=g(e,s,4),n=e.getUint32(s+4,!0);if("fmt "===a){if(s+16>t.byteLength)throw new d("fmt chunk extends beyond file");return e.getUint32(s+12,!0)}const r=s+8+n;if(r<=s)break;s=r}throw new d("Missing fmt chunk")}function g(t,e,s){let a="";for(let n=0;n<s;n++)a+=String.fromCharCode(t.getUint8(e+n));return a}function m(t,e,s){for(let a=0;a<s.length;a++)t.setUint8(e+a,s.charCodeAt(a))}function f(t,e,s){switch(s){case 8:return(t.getUint8(e)-128)/128;case 16:return t.getInt16(e,!0)/32768;case 24:{const s=t.getUint8(e),a=t.getUint8(e+1),n=t.getUint8(e+2)<<16|a<<8|s;return(n>8388607?n-16777216:n)/8388608}case 32:return t.getInt32(e,!0)/2147483648;default:return 0}}class S extends Error{constructor(t){super(`Audio resample error: ${t}`),this.name="AudioResampleError"}}async function w(t,e,s,a=1){if(0===t.length)throw new S("Cannot resample empty audio data");if(e<=0||s<=0)throw new S(`Invalid sample rate: original=${e}, target=${s}`);if(e===s)return t;try{const n=t.length/e,r=Math.ceil(n*s),o=new OfflineAudioContext(a,t.length,e).createBuffer(a,t.length,e);o.getChannelData(0).set(t);const i=new OfflineAudioContext(a,r,s),l=i.createBufferSource();l.buffer=o,l.connect(i.destination),l.start(0);return(await i.startRendering()).getChannelData(0)}catch(t){const a=t instanceof Error?t.message:String(t);throw new S(`Failed to resample audio from ${e}Hz to ${s}Hz: ${a}`)}}const y=16e3;async function T(t,e=!0){let s;try{const e=atob(t),a=new Array(e.length);for(let t=0;t<e.length;t++)a[t]=e.charCodeAt(t);s=new Uint8Array(a).buffer}catch{throw new i("Invalid Base64 audio data")}const a=function(t){const e=new Uint8Array(t);if(e.length>=4){const t=String.fromCharCode(e[0],e[1],e[2],e[3]);if("RIFF"===t)return"audio/wav";if(t.startsWith("ID3"))return"audio/mpeg";if(255===e[0]&&!(224&~e[1]))return"audio/mpeg"}return"audio/wav"}(s);if(!e)return new Blob([s],{type:a});try{const t=await async function(t,e){if("audio/wav"===e){const e=function(t){const e=new DataView(t);if(t.byteLength<44)throw new d("File too small to be a valid WAV");if("RIFF"!==g(e,0,4))throw new d("Missing RIFF header");if("WAVE"!==g(e,8,4))throw new d("Missing WAVE format identifier");let s=12,a=!1,n=0,r=0,o=0,i=0;for(;s<t.byteLength-8;){const l=g(e,s,4),c=e.getUint32(s+4,!0);if("fmt "===l){if(s+24>t.byteLength)throw new d("fmt chunk extends beyond file");n=e.getUint16(s+8,!0),r=e.getUint16(s+10,!0),o=e.getUint32(s+12,!0),i=e.getUint16(s+22,!0),a=!0,s+=8+c;break}const h=s+8+c;if(h<=s)break;s=h}if(!a)throw new d("Missing fmt chunk");if(1!==n)throw new d(`Unsupported audio format: ${n} (only PCM format 1 is supported)`);if(0===r||r>8)throw new d(`Invalid channel count: ${r}`);if(0===o||o>192e3)throw new d(`Invalid sample rate: ${o}`);if(![8,16,24,32].includes(i))throw new d(`Unsupported bits per sample: ${i}`);let l=-1,c=0;for(;s<t.byteLength-8;){const t=g(e,s,4),a=e.getUint32(s+4,!0);if("data"===t){l=s+8,c=a;break}const n=s+8+a;if(n<=s)break;s=n}if(-1===l)throw new d("Missing data chunk");const h=t.byteLength-l,u=Math.min(c,h),p=i/8,m=Math.floor(u/(p*r)),S=new Float32Array(m*r);let w=0;for(let s=0;s<m*r;s++){const a=l+s*p;if(a+p>t.byteLength)break;S[w++]=Math.max(-1,Math.min(1,f(e,a,i)))}if(2===r){const t=new Float32Array(m);for(let e=0;e<m;e++)t[e]=(S[2*e]+S[2*e+1])/2;return{sampleRate:o,channels:1,bitsPerSample:i,samples:t}}return{sampleRate:o,channels:r,bitsPerSample:i,samples:S.slice(0,w)}}(t);if(e.sampleRate===y)return{samples:e.samples,sampleRate:e.sampleRate};return{samples:await w(e.samples,e.sampleRate,y,e.channels),sampleRate:y}}const s=new AudioContext({sampleRate:y});try{const e=await s.decodeAudioData(t.slice(0));if(e.sampleRate===y)return{samples:e.getChannelData(0),sampleRate:y};return{samples:await w(e.getChannelData(0),e.sampleRate,y,1),sampleRate:y}}finally{await s.close()}}(s,a),e=u(t.samples,y,1);return new Blob([e],{type:"audio/wav"})}catch{return new Blob([s],{type:a})}}var C;!function(t){t.RECORDING="RECORDING",t.LLM="LLM",t.ANALYZING="ANALYZING",t.SPEAKING="SPEAKING",t.TTS="TTS"}(C||(C={}));class E{name;description;parameters;call;executeOnly;constructor(t,e,s,a,n=!1){this.name=t,this.description=e,this.parameters=s,this.call=a,this.executeOnly=n}}class v{config;messageHistory=[];constructor(t){this.config=t}async*processLLM(t){if(0===t.message.length)throw new Error("Message cannot be empty");const e=t.tools??this.config.clientTools,r=e.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),o={newMessageHistory:[{role:"user",content:t.message}],allChunks:[],message:"",lastYieldedChunkCount:0,pendingToolCallsMessage:null,aborted:!1,streamingError:null};let i=0,l=[...this.messageHistory,...o.newMessageHistory];this.config.callbacks.onChatStateChange(C.LLM,null);try{for(;;){if(t.signal?.aborted)return void(o.allChunks.length>0&&(yield{type:"message",chunks:[...o.allChunks],message:o.message,finish:!0}));let c;try{c=await h.makeLLM(this.config.apiServer,this.config.sessionId,{messages:l,tools:r},t.signal)}catch(t){if(t instanceof s)return void(yield{type:"error",error:new a(t)});throw t}if(o.streamingError=null,yield*this.parseSSEStream(c,o,t),o.streamingError)return;if(o.aborted)return void(o.allChunks.length>0&&(yield{type:"message",chunks:[...o.allChunks],message:o.message,finish:!0}));if(null!=o.pendingToolCallsMessage){yield*this.executeToolCalls(o,e);const t=o.lastToolCallResults,s=t.length>0&&o.pendingToolCallsMessage.tool_calls.length!==t.length,r=t.some(t=>!t.chatTool.executeOnly);if(s||r){if(i++,i>=10)return void(yield{type:"error",error:new a(new n("Tool follow-up loop exceeded maximum rounds (10)"))});l=[...this.messageHistory,...o.newMessageHistory],o.pendingToolCallsMessage=null;continue}}return this.messageHistory.push(...o.newMessageHistory),void(yield{type:"message",chunks:[...o.allChunks],message:o.message,finish:!0})}}finally{this.config.callbacks.onChatStateChange(null,C.LLM)}}async*parseSSEStream(t,e,s){const r=new TextDecoder("utf-8");let o="",i="";e.pendingToolCallsMessage=null;const l=()=>e.allChunks.length>e.lastYieldedChunkCount?(e.lastYieldedChunkCount=e.allChunks.length,{type:"message",chunks:[...e.allChunks],message:e.message,finish:!1}):null;for(;;){const{done:c,value:h}=await t.read();if(c)break;let d;for(o+=r.decode(h,{stream:!0});-1!==(d=o.indexOf("\n"));){if(s.signal?.aborted)return void(e.aborted=!0);const t=o.slice(0,d).trim();if(o=o.slice(d+1),!t.startsWith("data: {"))return e.streamingError=new a(new n("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let r;try{r=JSON.parse(t.slice(6).trim())}catch{return e.streamingError=new a(new n("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==r.status)return e.streamingError=new a(new n(r.reason)),void(yield{type:"error",error:e.streamingError});if(i.length>0&&"message"!=r.type){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}"message"!==r.type?"tool_call"!==r.type||null==r.tool_calls?"tool"!==r.role||"tool_call"===r.type&&e.newMessageHistory.push({role:r.role,type:r.type,content:r.content,tool_call_id:r.tool_call_id}):(e.newMessageHistory.push({role:"assistant",type:r.type,content:r.content,tool_calls:r.tool_calls}),e.pendingToolCallsMessage=r,yield{type:"tool_call",tool_calls:r.tool_calls}):(i+=r.content,e.message+=r.content,e.allChunks.push(r.content))}const u=l();u&&(yield u)}const c=o.trim();if(c.length>0){if(!c.startsWith("data: {"))return e.streamingError=new a(new n("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let t;try{t=JSON.parse(c.slice(6).trim())}catch{return e.streamingError=new a(new n("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==t.status)return e.streamingError=new a(new n(t.reason)),void(yield{type:"error",error:e.streamingError});if("message"===t.type)i+=t.content,e.message+=t.content,e.allChunks.push(t.content);else if("tool_call"===t.type&&null!=t.tool_calls){if(i.length>0){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}e.newMessageHistory.push({role:"assistant",type:t.type,content:t.content,tool_calls:t.tool_calls}),e.pendingToolCallsMessage=t,yield{type:"tool_call",tool_calls:t.tool_calls}}}i.length>0&&e.newMessageHistory.push({role:"assistant",type:"message",content:i})}async*executeToolCalls(t,e){const s=t=>{for(const s of e)if(s.name===t)return s;return null},a=[];for(const e of t.pendingToolCallsMessage.tool_calls){const t=s(e.function.name);null!=t&&a.push((async()=>{try{const s=await t.call(JSON.parse(e.function.arguments));return{toolCallId:e.id,chatTool:t,chatToolResult:s}}catch(s){return{toolCallId:e.id,chatTool:t,chatToolResult:{error:s.message}}}})())}const n=await Promise.all(a);t.lastToolCallResults=n;for(const e of n)t.newMessageHistory.push({role:"tool",content:JSON.stringify(e.chatToolResult),tool_call_id:e.toolCallId}),yield{type:"tool_result",tool_call_id:e.toolCallId,result:e.chatToolResult}}addToHistory(t){this.messageHistory.push(t)}getHistory(){return this.messageHistory}}const R=`data:application/javascript,${encodeURIComponent("\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.isRecording = true;\n \n // Listen for stop message from main thread\n this.port.onmessage = (event) => {\n if (event.data.type === 'stop') {\n this.isRecording = false;\n // Send confirmation back to main thread\n this.port.postMessage({ type: 'stopped' });\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (input && input.length > 0 && this.isRecording) {\n // Clone the audio data and send to main thread\n const channelData = new Float32Array(input[0]);\n this.port.postMessage({ type: 'audio', data: channelData });\n }\n // Return true to keep the processor alive until stopped\n return this.isRecording;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n")}`;class I{audioContext=null;mediaStream=null;workletNode=null;sourceNode=null;audioChunks=[];isRecordingState=!1;channels;targetSampleRate;constructor(t={}){this.channels=t.channels||1,this.targetSampleRate=t.targetSampleRate}async start(){if(this.isRecordingState)throw new Error("WavRecorder is already recording");if(this.mediaStream=await navigator.mediaDevices.getUserMedia({audio:!0}),this.audioContext=new AudioContext,"running"!==this.audioContext.state)try{await this.audioContext.resume()}catch(t){console.warn("WavRecorder: Failed to resume AudioContext:",t)}await this.audioContext.audioWorklet.addModule(R),this.sourceNode=this.audioContext.createMediaStreamSource(this.mediaStream),this.workletNode=new AudioWorkletNode(this.audioContext,"recorder-processor"),this.audioChunks=[],this.workletNode.port.onmessage=t=>{"audio"===t.data.type&&this.audioChunks.push(t.data.data)},this.sourceNode.connect(this.workletNode),this.workletNode.connect(this.audioContext.destination),this.isRecordingState=!0}async stop(){if(!this.isRecordingState)throw new Error("WavRecorder is not recording");this.isRecordingState=!1,await new Promise(t=>{this.workletNode.port.onmessage=e=>{"stopped"===e.data.type?t():"audio"===e.data.type&&this.audioChunks.push(e.data.data)},this.workletNode.port.postMessage({type:"stop"})}),this.workletNode?.disconnect(),this.sourceNode?.disconnect(),this.mediaStream?.getTracks().forEach(t=>t.stop());const t=this.audioChunks,e=this.audioContext.sampleRate;try{const s=t.reduce((t,e)=>t+e.length,0),a=new Float32Array(s);let n,r,o=0;for(const e of t)a.set(e,o),o+=e.length;this.targetSampleRate&&this.targetSampleRate!==e?(n=await w(a,e,this.targetSampleRate,this.channels),r=this.targetSampleRate):(n=a,r=e);const i=u(n,r,this.channels),l=new Blob([i],{type:"audio/wav"});return new File([l],"recording.wav",{type:"audio/wav"})}finally{await(this.audioContext?.close()),this.audioContext=null,this.mediaStream=null,this.workletNode=null,this.sourceNode=null,this.audioChunks=[]}}isRecording(){return this.isRecordingState}}function L(t){return new I(t)}class M extends EventTarget{pc;dc;streams=[];pingTime;pingIntervalId=null;constructor(t,e){super(),this.pc=t,this.dc=e,this.pingTime=Date.now()+3e3,this.pc.addEventListener("track",t=>{this.streams=this.streams.concat(t.streams)}),this.pc.addEventListener("connectionstatechange",()=>{"disconnected"!==this.pc.connectionState&&"failed"!==this.pc.connectionState||this.close()}),this.dc.onopen=()=>{this.pingIntervalId=setInterval(()=>{this.ping(),Date.now()-this.pingTime>5e3&&this.close()},1e3)},this.dc.onclose=()=>{null!=this.pingIntervalId&&clearInterval(this.pingIntervalId)},this.#t({live:!0,code:200,reason:"OK"}),this.setMessageCallback("ping",()=>{this.pingTime=Date.now()})}static async create(t,e,s,a,n){const r=await h.getSessionInfo(t,e);if(!(Array.isArray(r.capability)&&r.capability.some(t=>t.name===l.STF_ONPREMISE||t.name===l.STF_WEBRTC)))return await h.sessionEvent(t,e,c.SESSION_START),null;const o=await h.getIceServers(t,e);let i=await M.createPeerConnection(o),d=i.createDataChannel("message",{protocol:"message"}),u=new M(i,d);n?n.getTracks().forEach(function(t){i.addTrack(t,n)}):i.addTransceiver("audio",{direction:"recvonly"});const p=i.addTransceiver("video",{direction:"recvonly"}),g=RTCRtpReceiver.getCapabilities("video");null!=g&&p.setCodecPreferences(g.codecs);const m=await i.createOffer();await i.setLocalDescription(m);const f=await h.exchangeSDP(t,e,m);return await i.setRemoteDescription(f),await M.waitFor(()=>u.isReady(),100,50),u.changeSize(s,a),u}static async createPeerConnection(t){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:t})}static async waitFor(t,s,a){let n=0;if(await new Promise(e=>{const r=setInterval(()=>{n+=1,n>=a&&(clearInterval(r),e("bad")),t()&&(clearInterval(r),e("good"))},s)}),n>=a)throw new e}isReady(){return this.streams.length>0&&"open"===this.dc.readyState}#t(t){this.dispatchEvent(new CustomEvent("status",{detail:t}))}subscribeStatus(t){return this.addEventListener("status",t),()=>{this.removeEventListener("status",t)}}getStream(){return this.streams[0]}sendMessage(t,e){this.dc.send(JSON.stringify({type:t,data:e}))}ttstf(t){this.sendMessage("ttstf",{message:t})}static BACKPRESSURE_THRESHOLD=524288;sendFile(t,e=65536){return new Promise((s,a)=>{const n=this.pc.createDataChannel("file",{protocol:"file"});n.onerror=t=>{n.close(),a(new Error(`File channel error: ${t}`))},n.addEventListener("message",async r=>{try{if(0===r.data.length){const s=new Uint8Array(await t.arrayBuffer());let r=0;const o=()=>{for(;r<s.length;){if(n.bufferedAmount>M.BACKPRESSURE_THRESHOLD)return n.bufferedAmountLowThreshold=M.BACKPRESSURE_THRESHOLD/2,n.onbufferedamountlow=()=>{n.onbufferedamountlow=null,n.onclose=null,o()},void(n.onclose=()=>{n.onbufferedamountlow=null,a(new Error("File channel closed during transfer"))});n.send(s.slice(r,r+e)),r+=e}n.send(new Uint8Array(0))};o()}else n.close(),s(r.data)}catch(t){n.close(),a(t)}})})}async stf(t,e,s){const a=await this.sendFile(t);return this.sendMessage("stf",{message:s,file_ref:a,format:e}),a}recordStart(){this.sendMessage("record-start",{})}recordEndStt(t){this.sendMessage("record-end-stt",{language:t})}recordEndTranslate(t,e){this.sendMessage("record-end-translate",{src_lang:t,dst_lang:e})}changeSize(t,e){this.sendMessage("change-size",{width:t,height:e})}setTemplate(t,e){this.sendMessage("set-template",{model:t,dress:e})}clearBuffer(){this.sendMessage("clear-buffer",{})}ping(){this.sendMessage("ping",{})}setMessageCallback(t,e){const s=s=>{const a=JSON.parse(s.data);a.type===t&&e(a.data)};return this.dc.addEventListener("message",s),()=>{this.dc.removeEventListener("message",s)}}async tts(t,e=!0){return T(t,e)}close(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:408,reason:"Request Timeout"})}closeSelf(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:200,reason:"OK"})}}class b{apiServer;sessionId;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];llmProcessor;chatStateMap=new Map([[C.RECORDING,0],[C.LLM,0],[C.ANALYZING,0],[C.SPEAKING,0],[C.TTS,0]]);emojiRegex=t();sttRecorder=null;sttTimeoutHandle=null;sttTimeoutAudioFile=null;heartbeatIntervalId=null;legacyVoiceChatMode;stream;constructor(t,e,s,a,n){this.apiServer=t,this.sessionId=e,this.perso=s,this.clientTools=a,this.legacyVoiceChatMode=n?.legacyVoiceChatMode??!1,this.stream=n?.stream??null,this.resetChatState(),this.llmProcessor=new v({apiServer:t,sessionId:e,clientTools:a,callbacks:{onChatStateChange:(t,e)=>this.setChatState(t,e),onError:t=>this.setError(t),onChatLog:(t,e)=>this.addMessageToChatLog(t,e),onTTSTF:t=>this.processTTSTFInternal(t)}}),this.startHeartbeat(),s&&(s.subscribeStatus(t=>{!1===t.detail?.live&&this.stopHeartbeat()}),s.setMessageCallback("stf",t=>{if(this.chatStateMap.get(C.ANALYZING)||this.chatStateMap.get(C.SPEAKING))if(this.setChatState(C.SPEAKING,C.ANALYZING),null!==this.lastStfTimeoutHandle){clearTimeout(this.lastStfTimeoutHandle);let e=Date.now();this.stfTotalDuration+=t.duration+1e3-(e-this.stfTimeoutStartTime),this.stfTimeoutStartTime=e,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,C.SPEAKING)},this.stfTotalDuration)}else this.stfTimeoutStartTime=Date.now(),this.stfTotalDuration=t.duration+2e3,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,C.SPEAKING)},this.stfTotalDuration)}),s.setMessageCallback("stt",t=>{if(this.setChatState(null,C.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:t.text}));else{if(""===t.text)return;this.processChat(t.text)}}),s.setMessageCallback("stt-error",t=>{this.setChatState(null,C.ANALYZING)}))}llmJob=null;async processChat(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processLLM(t){return this.pipelineSuppressed=!1,this.llmProcessor.processLLM(t)}getMessageHistory(){return this.llmProcessor.getHistory()}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}async transcribeAudio(t,e){const a=t instanceof File?t:new File([t],"audio.wav",{type:t.type});try{return(await h.makeSTT(this.apiServer,this.sessionId,a,e)).text}catch(t){if(t instanceof s)throw new r(t);throw t}}async processSTF(t,e,s){if(!this.perso)throw new Error("processSTF requires WebRTC (STF mode)");return this.pipelineSuppressed?"":(this.setChatState(C.ANALYZING),await this.perso.stf(t,e,s))}async processTTS(t,e={}){const{resample:a=!0}=e,n=this.removeEmoji(t).trim();if(0===n.length)return;if(this.pipelineSuppressed)return;const r=/[.?!]$/.test(n)?n:n+".";this.setChatState(C.TTS,null);try{const{audio:t}=await h.makeTTS(this.apiServer,{sessionId:this.sessionId,text:r});return await T(t,a)}catch(t){t instanceof s||t instanceof i?this.setError(new o(t)):this.setError(t instanceof Error?t:new Error(String(t)))}finally{this.setChatState(null,C.TTS)}}startVoiceChat(){if(!this.perso)throw new Error("startVoiceChat requires WebRTC (STF mode)");return this.pipelineSuppressed=!1,this.setChatState(C.RECORDING),this.perso.recordStart()}stopVoiceChat(){if(!this.perso)throw new Error("stopVoiceChat requires WebRTC (STF mode)");this.setChatState(C.ANALYZING,C.RECORDING),this.perso.recordEndStt()}async startProcessSTT(t){if(this.sttRecorder?.isRecording())throw new Error("STT recording is already in progress");this.pipelineSuppressed=!1,this.setChatState(C.RECORDING);try{this.sttRecorder=new I({targetSampleRate:16e3}),await this.sttRecorder.start(),t&&t>0&&(this.sttTimeoutHandle=setTimeout(async()=>{if(this.sttTimeoutHandle=null,this.sttRecorder?.isRecording()){try{this.sttTimeoutAudioFile=await this.sttRecorder.stop()}catch{this.sttTimeoutAudioFile=null,this.setChatState(null,C.RECORDING)}this.sttRecorder=null}},t))}catch(t){throw this.setChatState(null,C.RECORDING),this.sttRecorder=null,t}}lastRecordedAudioFile=null;async stopProcessSTT(t){let e;if(this.sttTimeoutHandle&&(clearTimeout(this.sttTimeoutHandle),this.sttTimeoutHandle=null),this.setChatState(null,C.RECORDING),this.sttTimeoutAudioFile)e=this.sttTimeoutAudioFile,this.sttTimeoutAudioFile=null;else{if(!this.sttRecorder?.isRecording())throw this.sttRecorder?(this.sttRecorder=null,new Error("STT recording is not in progress")):new Error("STT recording has not been started");e=await this.sttRecorder.stop(),this.sttRecorder=null}this.lastRecordedAudioFile=e;try{return(await h.makeSTT(this.apiServer,this.sessionId,e,t)).text}catch(t){if(t instanceof s)throw new r(t);throw t}}isSTTRecording(){return(this.sttRecorder?.isRecording()??!1)||null!==this.sttTimeoutAudioFile}changeSize(t,e){this.perso?.changeSize(t,e)}async clearBuffer(){this.perso?.clearBuffer(),await this.clearLLMJob(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.pipelineSuppressed=!0,this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()??null}getRemoteStream(){return this.perso?.getStream()}getLocalStream(){return this.stream}stopSession(){this.close()}onClose(t){return this.perso?this.perso.subscribeStatus(e=>{null!=e.detail&&!1===e.detail.live&&t(200===e.detail.code)}):()=>{}}subscribeChatStates(t){const e=e=>{t(e.detail.status)};return this.chatStatesHandler.addEventListener("status",e),()=>{this.chatStatesHandler.removeEventListener("status",e)}}subscribeChatLog(t){const e=e=>{t(e.detail.chatLog)};return this.chatLogHandler.addEventListener("chatLog",e),()=>{this.chatLogHandler.removeEventListener("chatLog",e)}}setSttResultCallback(t){const e=e=>{t(e.detail)};return this.sttEventHandler=new EventTarget,this.sttEventHandler.addEventListener("stt",e),()=>{this.sttEventHandler?.removeEventListener("stt",e),this.sttEventHandler=null}}setErrorHandler(t){const e=e=>{t(e.detail.error)};return this.errorHandler.addEventListener("error",e),()=>{this.errorHandler.removeEventListener("error",e)}}getSessionId(){return this.sessionId}async processChatInternal(t){this.setChatState(C.LLM);const e=this.clientTools.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),r=new Array;null===t||(t instanceof Array?r.push(...t):"string"==typeof t&&r.push({role:"user",content:t}));const o=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...r],tools:e}),headers:{"Content-Type":"application/json"},method:"POST"});if(!o.ok){const t=await o.json(),e=new a(new s(o.status,t.errors[0].code,t.errors[0].detail,t.errors[0].attr));return this.setError(e),void this.setChatState(null,C.LLM)}const i=o.body?.getReader(),l=new TextDecoder("utf-8");let c="",h=null,d="";for(;;){const{done:t,value:e}=await i.read();if(t)break;let s;for(d+=l.decode(e,{stream:!0});-1!==(s=d.indexOf("\n"));){if(this.llmCancel)return c.length>0&&this.addMessageToChatLog(c,!1),void this.setChatState(null,C.LLM);const t=d.slice(0,s).trim();if(d=d.slice(s+1),!t.startsWith("data: {")){const t=new a(new n("Failed to parse SSE response"));return this.setError(t),void this.setChatState(null,C.LLM)}const e=JSON.parse(t.slice(6).trim());if("success"!==e.status){const t=new a(new n(e.reason));return this.setError(t),void this.setChatState(null,C.LLM)}c.length>0&&"message"!=e.type&&(r.push({role:"assistant",type:"message",content:c}),this.addMessageToChatLog(c,!1),c=""),"message"!==e.type?"tool_call"!==e.type||null==e.tool_calls?"tool"!==e.role||"tool_call"===e.type&&r.push({role:e.role,type:e.type,content:e.content,tool_call_id:e.tool_call_id}):(r.push({role:"assistant",type:e.type,content:e.content,tool_calls:e.tool_calls}),h=e):(c+=e.content,this.processTTSTFInternal(e.content))}}if(this.llmCancel)this.setChatState(null,C.LLM);else{if(null!=h){const t=[];for(const e of h.tool_calls){const s=this.getChatTool(this.clientTools,e.function.name);null!=s&&t.push(new Promise(async t=>{try{const a=await s.call(JSON.parse(e.function.arguments));t({toolCallId:e.id,chatTool:s,chatToolResult:a})}catch(a){t({toolCallId:e.id,chatTool:s,chatToolResult:{result:"error!"}})}}))}const e=await Promise.all(t);for(const t of e)r.push({role:"tool",content:JSON.stringify(t.chatToolResult),tool_call_id:t.toolCallId});const s=e.length>0&&h.tool_calls.length!==e.length,a=e.some(t=>!t.chatTool.executeOnly);s||a?await this.processChatInternal(r):this.messageHistory.push(...r)}else this.messageHistory.push(...r);this.setChatState(null,C.LLM)}}getChatTool(t,e){for(const s of t)if(s.name===e)return s;return null}llmCancel=!1;pipelineSuppressed=!1;async clearLLMJob(){null!=this.llmJob&&(this.llmCancel=!0,await this.llmJob,this.llmCancel=!1)}processTTSTFInternal(t){const e=this.removeEmoji(t).trim();0!==e.length&&this.perso&&(this.setChatState(C.ANALYZING),this.perso.ttstf(e))}addMessageToChatLog(t,e){this.chatLog=[{text:t,isUser:e,timestamp:new Date},...this.chatLog],this.chatLogHandler.dispatchEvent(new CustomEvent("chatLog",{detail:{chatLog:this.chatLog}}))}setChatState(t=null,e=null){const s=new Map(this.chatStateMap);function a(t){t===C.ANALYZING?s.set(t,(s.get(t)||0)+1):s.set(t,1)}function n(t){t===C.ANALYZING?s.set(t,Math.max((s.get(t)||0)-1,0)):s.set(t,0)}if(null!=t)if(t instanceof Array)for(let e of t)a(e);else a(t);if(null!=e)if(e instanceof Array)for(let t of e)n(t);else n(e);const r=this.exchangeChatStateMapToSet(this.chatStateMap),o=this.exchangeChatStateMapToSet(s);this.chatStateMap=s,this.isEqualChatStateMap(r,o)||this.dispatchChatState(o)}resetChatState(){this.chatStateMap=new Map([[C.RECORDING,0],[C.LLM,0],[C.ANALYZING,0],[C.SPEAKING,0],[C.TTS,0]]),this.dispatchChatState(this.exchangeChatStateMapToSet(this.chatStateMap))}exchangeChatStateMapToSet(t){const e=new Set;for(const s of t)s[1]>0&&e.add(s[0]);return e}dispatchChatState(t){this.chatStatesHandler.dispatchEvent(new CustomEvent("status",{detail:{status:t}}))}isEqualChatStateMap(t,e){if(t.size!==e.size)return!1;for(const s of t)if(t.has(s)!==e.has(s))return!1;return!0}setError(t){this.errorHandler.dispatchEvent(new CustomEvent("error",{detail:{error:t}}))}close(){this.stopHeartbeat(),this.perso?.closeSelf()}startHeartbeat(){const t=async()=>{try{await h.sessionEvent(this.apiServer,this.sessionId,c.SESSION_DURING),null!==this.heartbeatIntervalId&&(this.heartbeatIntervalId=setTimeout(t,1e4))}catch(t){t instanceof s?this.setError(t):this.setError(t instanceof Error?t:new Error(String(t))),this.close()}};this.heartbeatIntervalId=setTimeout(t,1e4)}stopHeartbeat(){null!==this.heartbeatIntervalId&&(clearTimeout(this.heartbeatIntervalId),this.heartbeatIntervalId=null)}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function N(t,e,s,a,n,r){if("boolean"!=typeof n){const r=await M.create(t,e,s,a);return new b(t,e,r,n)}const o=r??[];let i,l;if(n)i=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),l=()=>{};else{const t=new AudioContext,e=t.createOscillator();e.frequency.value=0;const s=t.createMediaStreamDestination();e.connect(s),e.start(),i=s.stream,l=()=>{e.stop(),e.disconnect(s),t.close()}}const c=await M.create(t,e,s,a,i);if(!c)return l(),new b(t,e,null,o);const h=new b(t,e,c,o,{stream:i,legacyVoiceChatMode:!0});return h.onClose(()=>{l()}),h}async function A(t,e){return await h.getLLMs(t,e)}async function _(t,e){return await h.getTTSs(t,e)}async function k(t,e){return await h.getSTTs(t,e)}async function O(t,e){return await h.getModelStyles(t,e)}async function H(t,e){return await h.getBackgroundImages(t,e)}async function P(t,e){return await h.getPrompts(t,e)}async function x(t,e){return await h.getDocuments(t,e)}async function F(t,e){return await h.getMcpServers(t,e)}async function D(t,e){const s=await A(t,e),a=await O(t,e),n=await H(t,e);return{llms:s,ttsTypes:await _(t,e),sttTypes:await k(t,e),modelStyles:a,backgroundImages:n,prompts:await P(t,e),documents:await x(t,e),mcpServers:await F(t,e)}}async function $(t,e,s,a,n,r){return"boolean"==typeof n?await N(t,e,s,a,n,r??[]):await N(t,e,s,a,n)}async function G(t,e){return await h.getSessionInfo(t,e)}const U=async(t,e,s)=>{"undefined"!=typeof window&&console.warn("[perso-interactive-sdk-web] WARNING: createSessionId is being called from the browser. This exposes your API key and is not recommended for production. Use server-side session creation with 'perso-interactive-sdk-web/server' instead. See: https://github.com/perso-ai/perso-interactive-sdk-web#server-side");const a={capability:[],...s};s.using_stf_webrtc&&a.capability.push(l.STF_WEBRTC),s?.llm_type&&(a.capability.push(l.LLM),a.llm_type=s.llm_type),s?.tts_type&&(a.capability.push(l.TTS),a.tts_type=s.tts_type),s?.stt_type&&(a.capability.push(l.STT),a.stt_type=s.stt_type);const n=await fetch(`${t}/api/v1/session/`,{body:JSON.stringify(a),headers:{"PersoLive-APIKey":e,"Content-Type":"application/json"},method:"POST"});return(await h.parseJson(n)).session_id};export{s as ApiError,C as ChatState,E as ChatTool,a as LLMError,n as LLMStreamingResponseError,v as LlmProcessor,r as STTError,b as Session,i as TTSDecodeError,o as TTSError,y as TTS_TARGET_SAMPLE_RATE,I as WavRecorder,$ as createSession,U as createSessionId,L as createWavRecorder,D as getAllSettings,H as getBackgroundImages,x as getDocuments,A as getLLMs,F as getMcpServers,O as getModelStyles,P as getPrompts,k as getSTTs,G as getSessionInfo,_ as getTTSs,p as getWavSampleRate};
1
+ import t from"emoji-regex";class e extends Error{constructor(){super("WebRTC connection timeout")}}class s extends Error{errorCode;code;detail;attr;constructor(t,e,s,a){let n;n=null!=a?`${t}:${a}_${s}`:`${t}:${s}`,super(n),this.errorCode=t,this.code=e,this.detail=s,this.attr=a}}class a extends Error{underlyingError;constructor(t){super(),this.underlyingError=t}}class n extends Error{description;constructor(t){super(),this.description=t}}class r extends Error{underlyingError;constructor(t){super(`STT Error: ${t.detail}`),this.underlyingError=t}}class o extends Error{underlyingError;constructor(t){super(t.message),this.underlyingError=t}}class i extends Error{description;constructor(t){super(`TTS decode error: ${t}`),this.description=t}}var l,c;!function(t){t.LLM="LLM",t.TTS="TTS",t.STT="STT",t.STF_ONPREMISE="STF_ONPREMISE",t.STF_WEBRTC="STF_WEBRTC"}(l||(l={})),function(t){t.SESSION_START="SESSION_START",t.SESSION_DURING="SESSION_DURING",t.SESSION_LOG="SESSION_LOG",t.SESSION_END="SESSION_END",t.SESSION_ERROR="SESSION_ERROR",t.SESSION_TTS="SESSION_TTS",t.SESSION_STT="SESSION_STT",t.SESSION_LLM="SESSION_LLM"}(c||(c={}));class h{static async getLLMs(t,e){const s=fetch(`${t}/api/v1/settings/llm_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getModelStyles(t,e){const s=fetch(`${t}/api/v1/settings/modelstyle/?platform_type=webrtc`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getBackgroundImages(t,e){const s=fetch(`${t}/api/v1/background_image/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getTTSs(t,e){const s=fetch(`${t}/api/v1/settings/tts_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSTTs(t,e){const s=fetch(`${t}/api/v1/settings/stt_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async makeTTS(t,{sessionId:e,text:s}){const a=await fetch(`${t}/api/v1/session/${e}/tts/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:s})});return await this.parseJson(a)}static async getPrompts(t,e){const s=fetch(`${t}/api/v1/prompt/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getDocuments(t,e){const s=fetch(`${t}/api/v1/document/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getMcpServers(t,e){const s=fetch(`${t}/api/v1/settings/mcp_type/`,{headers:{"PersoLive-APIKey":e},method:"GET"}),a=await s;return await this.parseJson(a)}static async getSessionInfo(t,e){const s=fetch(`${t}/api/v1/session/${e}/`,{method:"GET"}),a=await s;return await this.parseJson(a)}static async sessionEvent(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await this.parseJson(a)}static async makeSTT(t,e,s,a){const n=new FormData;n.append("audio",s),a&&n.append("language",a);const r=await fetch(`${t}/api/v1/session/${e}/stt/`,{method:"POST",body:n});return await this.parseJson(r)}static async makeLLM(t,e,a,n){const r=await fetch(`${t}/api/v1/session/${e}/llm/v2/`,{body:JSON.stringify(a),headers:{"Content-Type":"application/json"},method:"POST",signal:n});if(!r.ok){const t=await r.json(),e=t.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${r.status} with no error details`,attr:null};throw new s(r.status,e.code,e.detail,e.attr)}return r.body.getReader()}static async getIceServers(t,e){const s=await fetch(`${t}/api/v1/session/${e}/ice-servers/`,{method:"GET"});return(await this.parseJson(s)).ice_servers}static async exchangeSDP(t,e,s){const a=await fetch(`${t}/api/v1/session/${e}/exchange/`,{body:JSON.stringify({client_sdp:s}),headers:{"Content-Type":"application/json"},method:"POST"});return(await this.parseJson(a)).server_sdp}static async parseJson(t){const e=await t.json();if(t.ok)return e;{const a=e.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${t.status} with no error details`,attr:null};throw new s(t.status,a.code,a.detail,a.attr)}}}class d extends Error{constructor(t){super(`WAV parse error: ${t}`),this.name="WavParseError"}}function u(t,e,s=1){const a=2*t.length,n=new ArrayBuffer(44+a),r=new DataView(n);m(r,0,"RIFF"),r.setUint32(4,36+a,!0),m(r,8,"WAVE"),m(r,12,"fmt "),r.setUint32(16,16,!0),r.setUint16(20,1,!0),r.setUint16(22,s,!0),r.setUint32(24,e,!0),r.setUint32(28,e*s*2,!0),r.setUint16(32,2*s,!0),r.setUint16(34,16,!0),m(r,36,"data"),r.setUint32(40,a,!0);let o=44;for(let e=0;e<t.length;e++){const s=Math.max(-1,Math.min(1,t[e]));r.setInt16(o,s<0?32768*s:32767*s,!0),o+=2}return n}function p(t){const e=new DataView(t);if(t.byteLength<28)throw new d("File too small to be a valid WAV");if("RIFF"!==g(e,0,4))throw new d("Missing RIFF header");let s=12;for(;s<t.byteLength-8;){const a=g(e,s,4),n=e.getUint32(s+4,!0);if("fmt "===a){if(s+16>t.byteLength)throw new d("fmt chunk extends beyond file");return e.getUint32(s+12,!0)}const r=s+8+n;if(r<=s)break;s=r}throw new d("Missing fmt chunk")}function g(t,e,s){let a="";for(let n=0;n<s;n++)a+=String.fromCharCode(t.getUint8(e+n));return a}function m(t,e,s){for(let a=0;a<s.length;a++)t.setUint8(e+a,s.charCodeAt(a))}function S(t,e,s){switch(s){case 8:return(t.getUint8(e)-128)/128;case 16:return t.getInt16(e,!0)/32768;case 24:{const s=t.getUint8(e),a=t.getUint8(e+1),n=t.getUint8(e+2)<<16|a<<8|s;return(n>8388607?n-16777216:n)/8388608}case 32:return t.getInt32(e,!0)/2147483648;default:return 0}}class f extends Error{constructor(t){super(`Audio resample error: ${t}`),this.name="AudioResampleError"}}async function w(t,e,s,a=1){if(0===t.length)throw new f("Cannot resample empty audio data");if(e<=0||s<=0)throw new f(`Invalid sample rate: original=${e}, target=${s}`);if(e===s)return t;try{const n=t.length/e,r=Math.ceil(n*s),o=new OfflineAudioContext(a,t.length,e).createBuffer(a,t.length,e);o.getChannelData(0).set(t);const i=new OfflineAudioContext(a,r,s),l=i.createBufferSource();l.buffer=o,l.connect(i.destination),l.start(0);return(await i.startRendering()).getChannelData(0)}catch(t){const a=t instanceof Error?t.message:String(t);throw new f(`Failed to resample audio from ${e}Hz to ${s}Hz: ${a}`)}}const y=16e3;async function T(t,e=!0){let s;try{const e=atob(t),a=new Array(e.length);for(let t=0;t<e.length;t++)a[t]=e.charCodeAt(t);s=new Uint8Array(a).buffer}catch{throw new i("Invalid Base64 audio data")}const a=function(t){const e=new Uint8Array(t);if(e.length>=4){const t=String.fromCharCode(e[0],e[1],e[2],e[3]);if("RIFF"===t)return"audio/wav";if(t.startsWith("ID3"))return"audio/mpeg";if(255===e[0]&&!(224&~e[1]))return"audio/mpeg"}return"audio/wav"}(s);if(!e)return new Blob([s],{type:a});try{const t=await async function(t,e){if("audio/wav"===e){const e=function(t){const e=new DataView(t);if(t.byteLength<44)throw new d("File too small to be a valid WAV");if("RIFF"!==g(e,0,4))throw new d("Missing RIFF header");if("WAVE"!==g(e,8,4))throw new d("Missing WAVE format identifier");let s=12,a=!1,n=0,r=0,o=0,i=0;for(;s<t.byteLength-8;){const l=g(e,s,4),c=e.getUint32(s+4,!0);if("fmt "===l){if(s+24>t.byteLength)throw new d("fmt chunk extends beyond file");n=e.getUint16(s+8,!0),r=e.getUint16(s+10,!0),o=e.getUint32(s+12,!0),i=e.getUint16(s+22,!0),a=!0,s+=8+c;break}const h=s+8+c;if(h<=s)break;s=h}if(!a)throw new d("Missing fmt chunk");if(1!==n)throw new d(`Unsupported audio format: ${n} (only PCM format 1 is supported)`);if(0===r||r>8)throw new d(`Invalid channel count: ${r}`);if(0===o||o>192e3)throw new d(`Invalid sample rate: ${o}`);if(![8,16,24,32].includes(i))throw new d(`Unsupported bits per sample: ${i}`);let l=-1,c=0;for(;s<t.byteLength-8;){const t=g(e,s,4),a=e.getUint32(s+4,!0);if("data"===t){l=s+8,c=a;break}const n=s+8+a;if(n<=s)break;s=n}if(-1===l)throw new d("Missing data chunk");const h=t.byteLength-l,u=Math.min(c,h),p=i/8,m=Math.floor(u/(p*r)),f=new Float32Array(m*r);let w=0;for(let s=0;s<m*r;s++){const a=l+s*p;if(a+p>t.byteLength)break;f[w++]=Math.max(-1,Math.min(1,S(e,a,i)))}if(2===r){const t=new Float32Array(m);for(let e=0;e<m;e++)t[e]=(f[2*e]+f[2*e+1])/2;return{sampleRate:o,channels:1,bitsPerSample:i,samples:t}}return{sampleRate:o,channels:r,bitsPerSample:i,samples:f.slice(0,w)}}(t);if(e.sampleRate===y)return{samples:e.samples,sampleRate:e.sampleRate};return{samples:await w(e.samples,e.sampleRate,y,e.channels),sampleRate:y}}const s=new AudioContext({sampleRate:y});try{const e=await s.decodeAudioData(t.slice(0));if(e.sampleRate===y)return{samples:e.getChannelData(0),sampleRate:y};return{samples:await w(e.getChannelData(0),e.sampleRate,y,1),sampleRate:y}}finally{await s.close()}}(s,a),e=u(t.samples,y,1);return new Blob([e],{type:"audio/wav"})}catch{return new Blob([s],{type:a})}}var C;!function(t){t.RECORDING="RECORDING",t.LLM="LLM",t.ANALYZING="ANALYZING",t.SPEAKING="SPEAKING",t.TTS="TTS"}(C||(C={}));class E{name;description;parameters;call;executeOnly;constructor(t,e,s,a,n=!1){this.name=t,this.description=e,this.parameters=s,this.call=a,this.executeOnly=n}}class v{config;messageHistory=[];constructor(t){this.config=t}async*processLLM(t){if(0===t.message.length)throw new Error("Message cannot be empty");const e=t.tools??this.config.clientTools,r=e.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),o={newMessageHistory:[{role:"user",content:t.message}],allChunks:[],message:"",lastYieldedChunkCount:0,pendingToolCallsMessage:null,aborted:!1,streamingError:null};let i=0,l=[...this.messageHistory,...o.newMessageHistory];this.config.callbacks.onChatStateChange(C.LLM,null);try{for(;;){if(t.signal?.aborted)return void(o.allChunks.length>0&&(yield{type:"message",chunks:[...o.allChunks],message:o.message,finish:!0}));let c;try{c=await h.makeLLM(this.config.apiServer,this.config.sessionId,{messages:l,tools:r},t.signal)}catch(t){if(t instanceof s)return void(yield{type:"error",error:new a(t)});throw t}if(o.streamingError=null,yield*this.parseSSEStream(c,o,t),o.streamingError)return;if(o.aborted)return void(o.allChunks.length>0&&(yield{type:"message",chunks:[...o.allChunks],message:o.message,finish:!0}));if(null!=o.pendingToolCallsMessage){yield*this.executeToolCalls(o,e);const t=o.lastToolCallResults,s=t.length>0&&o.pendingToolCallsMessage.tool_calls.length!==t.length,r=t.some(t=>!t.chatTool.executeOnly);if(s||r){if(i++,i>=10)return void(yield{type:"error",error:new a(new n("Tool follow-up loop exceeded maximum rounds (10)"))});l=[...this.messageHistory,...o.newMessageHistory],o.pendingToolCallsMessage=null;continue}}return this.messageHistory.push(...o.newMessageHistory),void(yield{type:"message",chunks:[...o.allChunks],message:o.message,finish:!0})}}finally{this.config.callbacks.onChatStateChange(null,C.LLM)}}async*parseSSEStream(t,e,s){const r=new TextDecoder("utf-8");let o="",i="";e.pendingToolCallsMessage=null;const l=()=>e.allChunks.length>e.lastYieldedChunkCount?(e.lastYieldedChunkCount=e.allChunks.length,{type:"message",chunks:[...e.allChunks],message:e.message,finish:!1}):null;for(;;){const{done:c,value:h}=await t.read();if(c)break;let d;for(o+=r.decode(h,{stream:!0});-1!==(d=o.indexOf("\n"));){if(s.signal?.aborted)return void(e.aborted=!0);const t=o.slice(0,d).trim();if(o=o.slice(d+1),!t.startsWith("data: {"))return e.streamingError=new a(new n("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let r;try{r=JSON.parse(t.slice(6).trim())}catch{return e.streamingError=new a(new n("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==r.status)return e.streamingError=new a(new n(r.reason)),void(yield{type:"error",error:e.streamingError});if(i.length>0&&"message"!=r.type){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}"message"!==r.type?"tool_call"!==r.type||null==r.tool_calls?"tool"!==r.role||"tool_call"===r.type&&e.newMessageHistory.push({role:r.role,type:r.type,content:r.content,tool_call_id:r.tool_call_id}):(e.newMessageHistory.push({role:"assistant",type:r.type,content:r.content,tool_calls:r.tool_calls}),e.pendingToolCallsMessage=r,yield{type:"tool_call",tool_calls:r.tool_calls}):(i+=r.content,e.message+=r.content,e.allChunks.push(r.content))}const u=l();u&&(yield u)}const c=o.trim();if(c.length>0){if(!c.startsWith("data: {"))return e.streamingError=new a(new n("Failed to parse SSE response")),void(yield{type:"error",error:e.streamingError});let t;try{t=JSON.parse(c.slice(6).trim())}catch{return e.streamingError=new a(new n("Failed to parse SSE JSON")),void(yield{type:"error",error:e.streamingError})}if("success"!==t.status)return e.streamingError=new a(new n(t.reason)),void(yield{type:"error",error:e.streamingError});if("message"===t.type)i+=t.content,e.message+=t.content,e.allChunks.push(t.content);else if("tool_call"===t.type&&null!=t.tool_calls){if(i.length>0){e.newMessageHistory.push({role:"assistant",type:"message",content:i}),i="";const t=l();t&&(yield t)}e.newMessageHistory.push({role:"assistant",type:t.type,content:t.content,tool_calls:t.tool_calls}),e.pendingToolCallsMessage=t,yield{type:"tool_call",tool_calls:t.tool_calls}}}i.length>0&&e.newMessageHistory.push({role:"assistant",type:"message",content:i})}async*executeToolCalls(t,e){const s=t=>{for(const s of e)if(s.name===t)return s;return null},a=[];for(const e of t.pendingToolCallsMessage.tool_calls){const t=s(e.function.name);null!=t&&a.push((async()=>{try{const s=await t.call(JSON.parse(e.function.arguments));return{toolCallId:e.id,chatTool:t,chatToolResult:s}}catch(s){return{toolCallId:e.id,chatTool:t,chatToolResult:{error:s.message}}}})())}const n=await Promise.all(a);t.lastToolCallResults=n;for(const e of n)t.newMessageHistory.push({role:"tool",content:JSON.stringify(e.chatToolResult),tool_call_id:e.toolCallId}),yield{type:"tool_result",tool_call_id:e.toolCallId,result:e.chatToolResult}}addToHistory(t){this.messageHistory.push(t)}getHistory(){return this.messageHistory}}const R=`data:application/javascript,${encodeURIComponent("\nclass RecorderProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.isRecording = true;\n \n // Listen for stop message from main thread\n this.port.onmessage = (event) => {\n if (event.data.type === 'stop') {\n this.isRecording = false;\n // Send confirmation back to main thread\n this.port.postMessage({ type: 'stopped' });\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n if (input && input.length > 0 && this.isRecording) {\n // Clone the audio data and send to main thread\n const channelData = new Float32Array(input[0]);\n this.port.postMessage({ type: 'audio', data: channelData });\n }\n // Return true to keep the processor alive until stopped\n return this.isRecording;\n }\n}\n\nregisterProcessor('recorder-processor', RecorderProcessor);\n")}`;class I{audioContext=null;mediaStream=null;workletNode=null;sourceNode=null;audioChunks=[];isRecordingState=!1;channels;targetSampleRate;constructor(t={}){this.channels=t.channels||1,this.targetSampleRate=t.targetSampleRate}async start(){if(this.isRecordingState)throw new Error("WavRecorder is already recording");if(this.mediaStream=await navigator.mediaDevices.getUserMedia({audio:!0}),this.audioContext=new AudioContext,"running"!==this.audioContext.state)try{await this.audioContext.resume()}catch(t){console.warn("WavRecorder: Failed to resume AudioContext:",t)}await this.audioContext.audioWorklet.addModule(R),this.sourceNode=this.audioContext.createMediaStreamSource(this.mediaStream),this.workletNode=new AudioWorkletNode(this.audioContext,"recorder-processor"),this.audioChunks=[],this.workletNode.port.onmessage=t=>{"audio"===t.data.type&&this.audioChunks.push(t.data.data)},this.sourceNode.connect(this.workletNode),this.workletNode.connect(this.audioContext.destination),this.isRecordingState=!0}async stop(){if(!this.isRecordingState)throw new Error("WavRecorder is not recording");this.isRecordingState=!1,await new Promise(t=>{this.workletNode.port.onmessage=e=>{"stopped"===e.data.type?t():"audio"===e.data.type&&this.audioChunks.push(e.data.data)},this.workletNode.port.postMessage({type:"stop"})}),this.workletNode?.disconnect(),this.sourceNode?.disconnect(),this.mediaStream?.getTracks().forEach(t=>t.stop());const t=this.audioChunks,e=this.audioContext.sampleRate;try{const s=t.reduce((t,e)=>t+e.length,0),a=new Float32Array(s);let n,r,o=0;for(const e of t)a.set(e,o),o+=e.length;this.targetSampleRate&&this.targetSampleRate!==e?(n=await w(a,e,this.targetSampleRate,this.channels),r=this.targetSampleRate):(n=a,r=e);const i=u(n,r,this.channels),l=new Blob([i],{type:"audio/wav"});return new File([l],"recording.wav",{type:"audio/wav"})}finally{await(this.audioContext?.close()),this.audioContext=null,this.mediaStream=null,this.workletNode=null,this.sourceNode=null,this.audioChunks=[]}}isRecording(){return this.isRecordingState}}function L(t){return new I(t)}class M extends EventTarget{pc;dc;streams=[];pingTime;pingIntervalId=null;constructor(t,e){super(),this.pc=t,this.dc=e,this.pingTime=Date.now()+3e3,this.pc.addEventListener("track",t=>{this.streams=this.streams.concat(t.streams)}),this.pc.addEventListener("connectionstatechange",()=>{"disconnected"!==this.pc.connectionState&&"failed"!==this.pc.connectionState||this.close()}),this.dc.onopen=()=>{this.pingIntervalId=setInterval(()=>{this.ping(),Date.now()-this.pingTime>5e3&&this.close()},1e3)},this.dc.onclose=()=>{null!=this.pingIntervalId&&clearInterval(this.pingIntervalId)},this.#t({live:!0,code:200,reason:"OK"}),this.setMessageCallback("ping",()=>{this.pingTime=Date.now()})}static async create(t,e,s,a,n){const r=await h.getSessionInfo(t,e);if(!(Array.isArray(r.capability)&&r.capability.some(t=>t.name===l.STF_ONPREMISE||t.name===l.STF_WEBRTC)))return await h.sessionEvent(t,e,c.SESSION_START),null;const o=await h.getIceServers(t,e);let i=await M.createPeerConnection(o),d=i.createDataChannel("message",{protocol:"message"}),u=new M(i,d);n?n.getTracks().forEach(function(t){i.addTrack(t,n)}):i.addTransceiver("audio",{direction:"recvonly"});const p=i.addTransceiver("video",{direction:"recvonly"}),g=RTCRtpReceiver.getCapabilities("video");null!=g&&p.setCodecPreferences(g.codecs);const m=await i.createOffer();await i.setLocalDescription(m);const S=await h.exchangeSDP(t,e,m);return await i.setRemoteDescription(S),await M.waitFor(()=>u.isReady(),100,50),u.changeSize(s,a),u}static async createPeerConnection(t){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:t})}static async waitFor(t,s,a){let n=0;if(await new Promise(e=>{const r=setInterval(()=>{n+=1,n>=a&&(clearInterval(r),e("bad")),t()&&(clearInterval(r),e("good"))},s)}),n>=a)throw new e}isReady(){return this.streams.length>0&&"open"===this.dc.readyState}#t(t){this.dispatchEvent(new CustomEvent("status",{detail:t}))}subscribeStatus(t){return this.addEventListener("status",t),()=>{this.removeEventListener("status",t)}}getStream(){return this.streams[0]}sendMessage(t,e){this.dc.send(JSON.stringify({type:t,data:e}))}ttstf(t){this.sendMessage("ttstf",{message:t})}static BACKPRESSURE_THRESHOLD=524288;sendFile(t,e=65536){return new Promise((s,a)=>{const n=this.pc.createDataChannel("file",{protocol:"file"});n.onerror=t=>{n.close(),a(new Error(`File channel error: ${t}`))},n.addEventListener("message",async r=>{try{if(0===r.data.length){const s=new Uint8Array(await t.arrayBuffer());let r=0;const o=()=>{for(;r<s.length;){if(n.bufferedAmount>M.BACKPRESSURE_THRESHOLD)return n.bufferedAmountLowThreshold=M.BACKPRESSURE_THRESHOLD/2,n.onbufferedamountlow=()=>{n.onbufferedamountlow=null,n.onclose=null,o()},void(n.onclose=()=>{n.onbufferedamountlow=null,a(new Error("File channel closed during transfer"))});n.send(s.slice(r,r+e)),r+=e}n.send(new Uint8Array(0))};o()}else n.close(),s(r.data)}catch(t){n.close(),a(t)}})})}async stf(t,e,s){const a=await this.sendFile(t);return this.sendMessage("stf",{message:s,file_ref:a,format:e}),a}recordStart(){this.sendMessage("record-start",{})}recordEndStt(t){this.sendMessage("record-end-stt",{language:t})}recordEndTranslate(t,e){this.sendMessage("record-end-translate",{src_lang:t,dst_lang:e})}changeSize(t,e){this.sendMessage("change-size",{width:t,height:e})}setTemplate(t,e){this.sendMessage("set-template",{model:t,dress:e})}clearBuffer(){this.sendMessage("clear-buffer",{})}ping(){this.sendMessage("ping",{})}setMessageCallback(t,e){const s=s=>{const a=JSON.parse(s.data);a.type===t&&e(a.data)};return this.dc.addEventListener("message",s),()=>{this.dc.removeEventListener("message",s)}}async tts(t,e=!0){return T(t,e)}close(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:408,reason:"Request Timeout"})}closeSelf(){this.dc.close(),this.pc.close(),this.#t({live:!1,code:200,reason:"OK"})}}class b{apiServer;sessionId;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];llmProcessor;chatStateMap=new Map([[C.RECORDING,0],[C.LLM,0],[C.ANALYZING,0],[C.SPEAKING,0],[C.TTS,0]]);emojiRegex=t();sttRecorder=null;sttTimeoutHandle=null;sttTimeoutAudioFile=null;heartbeatIntervalId=null;legacyVoiceChatMode;stream;constructor(t,e,s,a,n){this.apiServer=t,this.sessionId=e,this.perso=s,this.clientTools=a,this.legacyVoiceChatMode=n?.legacyVoiceChatMode??!1,this.stream=n?.stream??null,this.resetChatState(),this.llmProcessor=new v({apiServer:t,sessionId:e,clientTools:a,callbacks:{onChatStateChange:(t,e)=>this.setChatState(t,e),onError:t=>this.setError(t),onChatLog:(t,e)=>this.addMessageToChatLog(t,e),onTTSTF:t=>this.processTTSTFInternal(t)}}),this.startHeartbeat(),s&&(s.subscribeStatus(t=>{!1===t.detail?.live&&this.stopHeartbeat()}),s.setMessageCallback("stf",t=>{if(this.chatStateMap.get(C.ANALYZING)||this.chatStateMap.get(C.SPEAKING))if(this.setChatState(C.SPEAKING,C.ANALYZING),null!==this.lastStfTimeoutHandle){clearTimeout(this.lastStfTimeoutHandle);let e=Date.now();this.stfTotalDuration+=t.duration+1e3-(e-this.stfTimeoutStartTime),this.stfTimeoutStartTime=e,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,C.SPEAKING)},this.stfTotalDuration)}else this.stfTimeoutStartTime=Date.now(),this.stfTotalDuration=t.duration+2e3,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,C.SPEAKING)},this.stfTotalDuration)}),s.setMessageCallback("stt",t=>{if(this.setChatState(null,C.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:t.text}));else{if(""===t.text)return;this.processChat(t.text)}}),s.setMessageCallback("stt-error",t=>{this.setChatState(null,C.ANALYZING)}))}llmJob=null;async processChat(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processLLM(t){return this.pipelineSuppressed=!1,this.llmProcessor.processLLM(t)}getMessageHistory(){return this.llmProcessor.getHistory()}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.pipelineSuppressed=!1,this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}async transcribeAudio(t,e){const a=t instanceof File?t:new File([t],"audio.wav",{type:t.type});try{return(await h.makeSTT(this.apiServer,this.sessionId,a,e)).text}catch(t){if(t instanceof s)throw new r(t);throw t}}async processSTF(t,e,s){if(!this.perso)throw new Error("processSTF requires WebRTC (STF mode)");this.pipelineSuppressed=!1,this.setChatState(C.ANALYZING);try{const a=await this.perso.stf(t,e,s);return this.pipelineSuppressed?(this.setChatState(null,C.ANALYZING),a):a}catch(t){throw this.setChatState(null,C.ANALYZING),t}}async processTTS(t,e={}){const{resample:a=!0}=e,n=this.removeEmoji(t).trim();if(0===n.length)return;this.pipelineSuppressed=!1;const r=/[.?!]$/.test(n)?n:n+".";this.setChatState(C.TTS,null);try{const{audio:t}=await h.makeTTS(this.apiServer,{sessionId:this.sessionId,text:r});if(this.pipelineSuppressed)return;return await T(t,a)}catch(t){t instanceof s||t instanceof i?this.setError(new o(t)):this.setError(t instanceof Error?t:new Error(String(t)))}finally{this.setChatState(null,C.TTS)}}startVoiceChat(){if(!this.perso)throw new Error("startVoiceChat requires WebRTC (STF mode)");return this.pipelineSuppressed=!1,this.setChatState(C.RECORDING),this.perso.recordStart()}stopVoiceChat(){if(!this.perso)throw new Error("stopVoiceChat requires WebRTC (STF mode)");this.setChatState(C.ANALYZING,C.RECORDING),this.perso.recordEndStt()}async startProcessSTT(t){if(this.sttRecorder?.isRecording())throw new Error("STT recording is already in progress");this.pipelineSuppressed=!1,this.setChatState(C.RECORDING);try{this.sttRecorder=new I({targetSampleRate:16e3}),await this.sttRecorder.start(),t&&t>0&&(this.sttTimeoutHandle=setTimeout(async()=>{if(this.sttTimeoutHandle=null,this.sttRecorder?.isRecording()){try{this.sttTimeoutAudioFile=await this.sttRecorder.stop()}catch{this.sttTimeoutAudioFile=null,this.setChatState(null,C.RECORDING)}this.sttRecorder=null}},t))}catch(t){throw this.setChatState(null,C.RECORDING),this.sttRecorder=null,t}}lastRecordedAudioFile=null;async stopProcessSTT(t){let e;if(this.sttTimeoutHandle&&(clearTimeout(this.sttTimeoutHandle),this.sttTimeoutHandle=null),this.setChatState(null,C.RECORDING),this.sttTimeoutAudioFile)e=this.sttTimeoutAudioFile,this.sttTimeoutAudioFile=null;else{if(!this.sttRecorder?.isRecording())throw this.sttRecorder?(this.sttRecorder=null,new Error("STT recording is not in progress")):new Error("STT recording has not been started");e=await this.sttRecorder.stop(),this.sttRecorder=null}this.lastRecordedAudioFile=e;try{return(await h.makeSTT(this.apiServer,this.sessionId,e,t)).text}catch(t){if(t instanceof s)throw new r(t);throw t}}isSTTRecording(){return(this.sttRecorder?.isRecording()??!1)||null!==this.sttTimeoutAudioFile}changeSize(t,e){this.perso?.changeSize(t,e)}async clearBuffer(){this.perso?.clearBuffer(),await this.clearLLMJob(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.pipelineSuppressed=!0,this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()??null}getRemoteStream(){return this.perso?.getStream()}getLocalStream(){return this.stream}stopSession(){this.close()}onClose(t){return this.perso?this.perso.subscribeStatus(e=>{null!=e.detail&&!1===e.detail.live&&t(200===e.detail.code)}):()=>{}}subscribeChatStates(t){const e=e=>{t(e.detail.status)};return this.chatStatesHandler.addEventListener("status",e),()=>{this.chatStatesHandler.removeEventListener("status",e)}}subscribeChatLog(t){const e=e=>{t(e.detail.chatLog)};return this.chatLogHandler.addEventListener("chatLog",e),()=>{this.chatLogHandler.removeEventListener("chatLog",e)}}setSttResultCallback(t){const e=e=>{t(e.detail)};return this.sttEventHandler=new EventTarget,this.sttEventHandler.addEventListener("stt",e),()=>{this.sttEventHandler?.removeEventListener("stt",e),this.sttEventHandler=null}}setErrorHandler(t){const e=e=>{t(e.detail.error)};return this.errorHandler.addEventListener("error",e),()=>{this.errorHandler.removeEventListener("error",e)}}getSessionId(){return this.sessionId}async processChatInternal(t){this.setChatState(C.LLM);const e=this.clientTools.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),r=new Array;null===t||(t instanceof Array?r.push(...t):"string"==typeof t&&r.push({role:"user",content:t}));const o=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...r],tools:e}),headers:{"Content-Type":"application/json"},method:"POST"});if(!o.ok){const t=await o.json(),e=new a(new s(o.status,t.errors[0].code,t.errors[0].detail,t.errors[0].attr));return this.setError(e),void this.setChatState(null,C.LLM)}const i=o.body?.getReader(),l=new TextDecoder("utf-8");let c="",h=null,d="";for(;;){const{done:t,value:e}=await i.read();if(t)break;let s;for(d+=l.decode(e,{stream:!0});-1!==(s=d.indexOf("\n"));){if(this.llmCancel)return c.length>0&&this.addMessageToChatLog(c,!1),void this.setChatState(null,C.LLM);const t=d.slice(0,s).trim();if(d=d.slice(s+1),!t.startsWith("data: {")){const t=new a(new n("Failed to parse SSE response"));return this.setError(t),void this.setChatState(null,C.LLM)}const e=JSON.parse(t.slice(6).trim());if("success"!==e.status){const t=new a(new n(e.reason));return this.setError(t),void this.setChatState(null,C.LLM)}c.length>0&&"message"!=e.type&&(r.push({role:"assistant",type:"message",content:c}),this.addMessageToChatLog(c,!1),c=""),"message"!==e.type?"tool_call"!==e.type||null==e.tool_calls?"tool"!==e.role||"tool_call"===e.type&&r.push({role:e.role,type:e.type,content:e.content,tool_call_id:e.tool_call_id}):(r.push({role:"assistant",type:e.type,content:e.content,tool_calls:e.tool_calls}),h=e):(c+=e.content,this.processTTSTFInternal(e.content))}}if(this.llmCancel)this.setChatState(null,C.LLM);else{if(null!=h){const t=[];for(const e of h.tool_calls){const s=this.getChatTool(this.clientTools,e.function.name);null!=s&&t.push(new Promise(async t=>{try{const a=await s.call(JSON.parse(e.function.arguments));t({toolCallId:e.id,chatTool:s,chatToolResult:a})}catch(a){t({toolCallId:e.id,chatTool:s,chatToolResult:{result:"error!"}})}}))}const e=await Promise.all(t);for(const t of e)r.push({role:"tool",content:JSON.stringify(t.chatToolResult),tool_call_id:t.toolCallId});const s=e.length>0&&h.tool_calls.length!==e.length,a=e.some(t=>!t.chatTool.executeOnly);s||a?await this.processChatInternal(r):this.messageHistory.push(...r)}else this.messageHistory.push(...r);this.setChatState(null,C.LLM)}}getChatTool(t,e){for(const s of t)if(s.name===e)return s;return null}llmCancel=!1;pipelineSuppressed=!1;async clearLLMJob(){null!=this.llmJob&&(this.llmCancel=!0,await this.llmJob,this.llmCancel=!1)}processTTSTFInternal(t){const e=this.removeEmoji(t).trim();0!==e.length&&this.perso&&(this.setChatState(C.ANALYZING),this.perso.ttstf(e))}addMessageToChatLog(t,e){this.chatLog=[{text:t,isUser:e,timestamp:new Date},...this.chatLog],this.chatLogHandler.dispatchEvent(new CustomEvent("chatLog",{detail:{chatLog:this.chatLog}}))}setChatState(t=null,e=null){const s=new Map(this.chatStateMap);function a(t){t===C.ANALYZING?s.set(t,(s.get(t)||0)+1):s.set(t,1)}function n(t){t===C.ANALYZING?s.set(t,Math.max((s.get(t)||0)-1,0)):s.set(t,0)}if(null!=t)if(t instanceof Array)for(let e of t)a(e);else a(t);if(null!=e)if(e instanceof Array)for(let t of e)n(t);else n(e);const r=this.exchangeChatStateMapToSet(this.chatStateMap),o=this.exchangeChatStateMapToSet(s);this.chatStateMap=s,this.isEqualChatStateMap(r,o)||this.dispatchChatState(o)}resetChatState(){this.chatStateMap=new Map([[C.RECORDING,0],[C.LLM,0],[C.ANALYZING,0],[C.SPEAKING,0],[C.TTS,0]]),this.dispatchChatState(this.exchangeChatStateMapToSet(this.chatStateMap))}exchangeChatStateMapToSet(t){const e=new Set;for(const s of t)s[1]>0&&e.add(s[0]);return e}dispatchChatState(t){this.chatStatesHandler.dispatchEvent(new CustomEvent("status",{detail:{status:t}}))}isEqualChatStateMap(t,e){if(t.size!==e.size)return!1;for(const s of t)if(t.has(s)!==e.has(s))return!1;return!0}setError(t){this.errorHandler.dispatchEvent(new CustomEvent("error",{detail:{error:t}}))}close(){this.stopHeartbeat(),this.perso?.closeSelf()}startHeartbeat(){const t=async()=>{try{await h.sessionEvent(this.apiServer,this.sessionId,c.SESSION_DURING),null!==this.heartbeatIntervalId&&(this.heartbeatIntervalId=setTimeout(t,1e4))}catch(t){t instanceof s?this.setError(t):this.setError(t instanceof Error?t:new Error(String(t))),this.close()}};this.heartbeatIntervalId=setTimeout(t,1e4)}stopHeartbeat(){null!==this.heartbeatIntervalId&&(clearTimeout(this.heartbeatIntervalId),this.heartbeatIntervalId=null)}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function N(t,e,s,a,n,r){if("boolean"!=typeof n){const r=await M.create(t,e,s,a);return new b(t,e,r,n)}const o=r??[];let i,l;if(n)i=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),l=()=>{};else{const t=new AudioContext,e=t.createOscillator();e.frequency.value=0;const s=t.createMediaStreamDestination();e.connect(s),e.start(),i=s.stream,l=()=>{e.stop(),e.disconnect(s),t.close()}}const c=await M.create(t,e,s,a,i);if(!c)return l(),new b(t,e,null,o);const h=new b(t,e,c,o,{stream:i,legacyVoiceChatMode:!0});return h.onClose(()=>{l()}),h}async function A(t,e){return await h.getLLMs(t,e)}async function _(t,e){return await h.getTTSs(t,e)}async function k(t,e){return await h.getSTTs(t,e)}async function O(t,e){return await h.getModelStyles(t,e)}async function H(t,e){return await h.getBackgroundImages(t,e)}async function P(t,e){return await h.getPrompts(t,e)}async function x(t,e){return await h.getDocuments(t,e)}async function F(t,e){return await h.getMcpServers(t,e)}async function D(t,e){const s=await A(t,e),a=await O(t,e),n=await H(t,e);return{llms:s,ttsTypes:await _(t,e),sttTypes:await k(t,e),modelStyles:a,backgroundImages:n,prompts:await P(t,e),documents:await x(t,e),mcpServers:await F(t,e)}}async function G(t,e,s,a,n,r){return"boolean"==typeof n?await N(t,e,s,a,n,r??[]):await N(t,e,s,a,n)}async function $(t,e){return await h.getSessionInfo(t,e)}const U=async(t,e,s)=>{"undefined"!=typeof window&&console.warn("[perso-interactive-sdk-web] WARNING: createSessionId is being called from the browser. This exposes your API key and is not recommended for production. Use server-side session creation with 'perso-interactive-sdk-web/server' instead. See: https://github.com/perso-ai/perso-interactive-sdk-web#server-side");const a={capability:[],...s};s.using_stf_webrtc&&a.capability.push(l.STF_WEBRTC),s?.llm_type&&(a.capability.push(l.LLM),a.llm_type=s.llm_type),s?.tts_type&&(a.capability.push(l.TTS),a.tts_type=s.tts_type),s?.stt_type&&(a.capability.push(l.STT),a.stt_type=s.stt_type);const n=await fetch(`${t}/api/v1/session/`,{body:JSON.stringify(a),headers:{"PersoLive-APIKey":e,"Content-Type":"application/json"},method:"POST"});return(await h.parseJson(n)).session_id};export{s as ApiError,C as ChatState,E as ChatTool,a as LLMError,n as LLMStreamingResponseError,v as LlmProcessor,r as STTError,b as Session,i as TTSDecodeError,o as TTSError,y as TTS_TARGET_SAMPLE_RATE,I as WavRecorder,G as createSession,U as createSessionId,L as createWavRecorder,D as getAllSettings,H as getBackgroundImages,x as getDocuments,A as getLLMs,F as getMcpServers,O as getModelStyles,P as getPrompts,k as getSTTs,$ as getSessionInfo,_ as getTTSs,p as getWavSampleRate};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perso-interactive-sdk-web",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Perso Interactive Web SDK - WebRTC-based real-time interactive AI avatar sessions",
5
5
  "type": "module",
6
6
  "private": false,
@@ -20,7 +20,8 @@
20
20
  "license": "Apache-2.0",
21
21
  "repository": {
22
22
  "type": "git",
23
- "url": "https://github.com/perso-ai/perso-interactive-sdk-web.git"
23
+ "url": "https://github.com/perso-ai/perso-interactive-sdk-web.git",
24
+ "directory": "packages/perso-interactive-sdk"
24
25
  },
25
26
  "homepage": "https://github.com/perso-ai/perso-interactive-sdk-web#readme",
26
27
  "bugs": {