perso-interactive-sdk-web 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -0
- package/dist/client/index.cjs +1 -0
- package/dist/client/index.d.ts +520 -0
- package/dist/client/index.iife.js +1 -0
- package/dist/client/index.js +1 -0
- package/dist/server/index.cjs +1 -0
- package/dist/server/index.d.ts +222 -0
- package/dist/server/index.js +1 -0
- package/package.json +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# perso-interactive-sdk-web
|
|
2
|
+
|
|
3
|
+
WebRTC-based real-time interactive AI avatar SDK for web applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# npm
|
|
9
|
+
npm install perso-interactive-sdk-web
|
|
10
|
+
|
|
11
|
+
# yarn
|
|
12
|
+
yarn add perso-interactive-sdk-web
|
|
13
|
+
|
|
14
|
+
# pnpm
|
|
15
|
+
pnpm add perso-interactive-sdk-web
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
The SDK provides two entry points:
|
|
21
|
+
|
|
22
|
+
### Server-side (`perso-interactive-sdk-web/server`)
|
|
23
|
+
|
|
24
|
+
Use this module in Node.js/SvelteKit/Next.js server environments to create sessions securely without exposing your API key.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { createSessionId, getIntroMessage } from "perso-interactive-sdk-web/server";
|
|
28
|
+
|
|
29
|
+
// Create a session on the server
|
|
30
|
+
const sessionId = await createSessionId(apiServer, apiKey, {
|
|
31
|
+
using_stf_webrtc: true,
|
|
32
|
+
model_style: "<model_style_name>",
|
|
33
|
+
prompt: "<prompt_id>",
|
|
34
|
+
llm_type: "<llm_name>",
|
|
35
|
+
tts_type: "<tts_name>",
|
|
36
|
+
stt_type: "<stt_name>",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Get intro message for a prompt
|
|
40
|
+
const introMessage = await getIntroMessage(apiServer, apiKey, promptId);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> ⚠️ **Security Warning**: Never use `createSessionId` on the client-side. Exposing your API key in browser code can lead to unauthorized access and quota abuse. Always create sessions on the server and pass only the `sessionId` to the client.
|
|
44
|
+
|
|
45
|
+
### Client-side (`perso-interactive-sdk-web/client`)
|
|
46
|
+
|
|
47
|
+
Use this module in browser environments to create and manage interactive sessions.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import {
|
|
51
|
+
createSession,
|
|
52
|
+
ChatTool,
|
|
53
|
+
ChatState,
|
|
54
|
+
getAllSettings,
|
|
55
|
+
} from "perso-interactive-sdk-web/client";
|
|
56
|
+
|
|
57
|
+
// Create a session (sessionId should come from server)
|
|
58
|
+
const session = await createSession(
|
|
59
|
+
apiServer,
|
|
60
|
+
sessionId,
|
|
61
|
+
width,
|
|
62
|
+
height,
|
|
63
|
+
enableVoiceChat,
|
|
64
|
+
clientTools,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Bind to video element
|
|
68
|
+
session.setSrc(videoElement);
|
|
69
|
+
|
|
70
|
+
// Subscribe to chat states
|
|
71
|
+
session.subscribeChatStates((states) => {
|
|
72
|
+
console.log("Chat states:", states);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Subscribe to chat log
|
|
76
|
+
session.subscribeChatLog((chatLog) => {
|
|
77
|
+
console.log("Chat log:", chatLog);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Send a message
|
|
81
|
+
session.processChat("Hello!");
|
|
82
|
+
|
|
83
|
+
// Start voice chat
|
|
84
|
+
session.startVoiceChat();
|
|
85
|
+
|
|
86
|
+
// Stop session
|
|
87
|
+
session.stopSession();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Client Tool Calling
|
|
91
|
+
|
|
92
|
+
Define custom tools that the LLM can invoke:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { ChatTool } from "perso-interactive-sdk-web/client";
|
|
96
|
+
|
|
97
|
+
const weatherTool = new ChatTool(
|
|
98
|
+
"get_weather",
|
|
99
|
+
"Get current weather for a location",
|
|
100
|
+
{
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: {
|
|
103
|
+
location: { type: "string", description: "City name" },
|
|
104
|
+
},
|
|
105
|
+
required: ["location"],
|
|
106
|
+
},
|
|
107
|
+
async (args) => {
|
|
108
|
+
// Your implementation
|
|
109
|
+
return { temperature: 22, condition: "Sunny" };
|
|
110
|
+
},
|
|
111
|
+
false, // executeOnly: if true, no follow-up LLM response
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const session = await createSession(
|
|
115
|
+
apiServer,
|
|
116
|
+
sessionId,
|
|
117
|
+
width,
|
|
118
|
+
height,
|
|
119
|
+
enableVoiceChat,
|
|
120
|
+
[weatherTool],
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Browser (IIFE)
|
|
125
|
+
|
|
126
|
+
For direct browser usage via script tag:
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<script src="https://cdn.jsdelivr.net/npm/perso-interactive-sdk-web@2/dist/client/index.iife.js"></script>
|
|
130
|
+
<script>
|
|
131
|
+
const session = await PersoInteractive.createSession(
|
|
132
|
+
apiServer,
|
|
133
|
+
sessionId,
|
|
134
|
+
width,
|
|
135
|
+
height,
|
|
136
|
+
enableVoiceChat,
|
|
137
|
+
[]
|
|
138
|
+
);
|
|
139
|
+
</script>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## API Reference
|
|
143
|
+
|
|
144
|
+
### Server Exports
|
|
145
|
+
|
|
146
|
+
| Export | Description |
|
|
147
|
+
| ---------------------------------------------- | ------------------------------ |
|
|
148
|
+
| `createSessionId(apiServer, apiKey, params)` | Create a new session ID |
|
|
149
|
+
| `getIntroMessage(apiServer, apiKey, promptId)` | Get intro message for a prompt |
|
|
150
|
+
| `PersoUtilServer` | Low-level API utilities |
|
|
151
|
+
| `ApiError` | Error class for API errors |
|
|
152
|
+
|
|
153
|
+
### Client Exports
|
|
154
|
+
|
|
155
|
+
| Export | Description |
|
|
156
|
+
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
157
|
+
| `createSession(apiServer, sessionId, width, height, enableVoiceChat, clientTools)` | Create a session |
|
|
158
|
+
| `Session` | Session class |
|
|
159
|
+
| `ChatTool` | Client tool class |
|
|
160
|
+
| `ChatState` | Enum for chat states (RECORDING, LLM, ANALYZING, SPEAKING) |
|
|
161
|
+
| `getLLMs(apiServer, apiKey)` | Get available LLM providers |
|
|
162
|
+
| `getTTSs(apiServer, apiKey)` | Get available TTS providers |
|
|
163
|
+
| `getSTTs(apiServer, apiKey)` | Get available STT providers |
|
|
164
|
+
| `getModelStyles(apiServer, apiKey)` | Get available avatar styles |
|
|
165
|
+
| `getBackgroundImages(apiServer, apiKey)` | Get available backgrounds |
|
|
166
|
+
| `getPrompts(apiServer, apiKey)` | Get available prompts |
|
|
167
|
+
| `getDocuments(apiServer, apiKey)` | Get available documents |
|
|
168
|
+
| `getMcpServers(apiServer, apiKey)` | Get available MCP servers |
|
|
169
|
+
| `getAllSettings(apiServer, apiKey)` | Get all settings at once |
|
|
170
|
+
| `getSessionInfo(apiServer, sessionId)` | Get session metadata |
|
|
171
|
+
| `ApiError` | Error class for API errors |
|
|
172
|
+
| `LLMError` | Error class for LLM errors |
|
|
173
|
+
| `LLMStreamingResponseError` | Error class for streaming errors |
|
|
174
|
+
|
|
175
|
+
### Session Methods
|
|
176
|
+
|
|
177
|
+
| Method | Description |
|
|
178
|
+
| ------------------------------- | ------------------------------ |
|
|
179
|
+
| `setSrc(videoElement)` | Bind session to video element |
|
|
180
|
+
| `processChat(message)` | Send a message to the LLM |
|
|
181
|
+
| `processTTSTF(message)` | Speak a message without LLM |
|
|
182
|
+
| `startVoiceChat()` | Start recording voice |
|
|
183
|
+
| `stopVoiceChat()` | Stop recording and send to STT |
|
|
184
|
+
| `clearBuffer()` | Cancel ongoing operations |
|
|
185
|
+
| `changeSize(width, height)` | Resize the avatar canvas |
|
|
186
|
+
| `stopSession()` | Close the session |
|
|
187
|
+
| `subscribeChatStates(callback)` | Subscribe to state changes |
|
|
188
|
+
| `subscribeChatLog(callback)` | Subscribe to chat log updates |
|
|
189
|
+
| `setErrorHandler(callback)` | Subscribe to errors |
|
|
190
|
+
| `onClose(callback)` | Subscribe to session close |
|
|
191
|
+
|
|
192
|
+
## API Reference
|
|
193
|
+
|
|
194
|
+
For detailed API documentation, see [api-docs.md](../../core/api-docs.md).
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
Apache-2.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var t,e=require("emoji-regex");class s extends Error{constructor(){super("WebRTC connection timeout")}}class a 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 r extends Error{underlyingError;constructor(t){super(),this.underlyingError=t}}class n extends Error{description;constructor(t){super(),this.description=t}}class i{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 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=fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await a}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 a(t.status,s.code,s.detail,s.attr)}}}class o 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,r){const n=await fetch(`${t}/api/v1/session/${e}/ice-servers/`,{method:"GET"}),c=(await i.parseJson(n)).ice_servers;let l=await o.createPeerConnection(c),h=l.createDataChannel("message",{protocol:"message"}),d=new o(l,h);s.getTracks().forEach(function(t){l.addTrack(t,s)});const p=l.addTransceiver("video",{direction:"recvonly"}),u=RTCRtpReceiver.getCapabilities("video");null!=u&&p.setCodecPreferences(u.codecs.filter(t=>"video/VP8"===t.mimeType));const S=await l.createOffer();await l.setLocalDescription(S);const g=await fetch(`${t}/api/v1/session/${e}/exchange/`,{body:JSON.stringify({client_sdp:S}),headers:{"Content-Type":"application/json"},method:"POST"}),m=await i.parseJson(g);return await l.setRemoteDescription(m.server_sdp),await o.waitFor(()=>d.isReady(),100,50),d.changeSize(a,r),d}static async createPeerConnection(t){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:t})}static async waitFor(t,e,a){let r=0;if(await new Promise(s=>{const n=setInterval(()=>{r+=1,r>=a&&(clearInterval(n),s("bad")),t()&&(clearInterval(n),s("good"))},e)}),r>=a)throw new s}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})}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)}}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"})}}exports.ChatState=void 0,(t=exports.ChatState||(exports.ChatState={})).RECORDING="RECORDING",t.LLM="LLM",t.ANALYZING="ANALYZING",t.SPEAKING="SPEAKING";class c{apiServer;sessionId;stream;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];chatStateMap=new Map([[exports.ChatState.RECORDING,0],[exports.ChatState.LLM,0],[exports.ChatState.ANALYZING,0],[exports.ChatState.SPEAKING,0]]);emojiRegex=e();constructor(t,e,s,a,r){this.apiServer=t,this.sessionId=e,this.stream=s,this.perso=a,this.clientTools=r,this.resetChatState(),a.setMessageCallback("stf",t=>{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)}),a.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)}}),a.setMessageCallback("stt-error",t=>{this.setChatState(null,exports.ChatState.ANALYZING)})}llmJob=null;async processChat(t){0!==t.trim().length&&(this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}startVoiceChat(){return this.setChatState(exports.ChatState.RECORDING),this.perso.recordStart()}stopVoiceChat(){this.setChatState(exports.ChatState.ANALYZING,exports.ChatState.RECORDING),this.perso.recordEndStt()}changeSize(t,e){this.perso.changeSize(t,e)}async clearBuffer(){await this.clearLLMJob(),this.perso.clearBuffer(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()}getLocalStream(){return this.stream}getRemoteStream(){return this.perso.getStream()}stopSession(){this.close()}onClose(t){return 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 i=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(!i.ok){const t=await i.json(),e=new r(new a(i.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 o=i.body?.getReader(),c=new TextDecoder("utf-8");let l="",h=null,d="";for(;;){const{done:t,value:e}=await o.read();if(t)break;let a;for(d+=c.decode(e,{stream:!0});-1!==(a=d.indexOf("\n"));){if(this.llmCancel)return l.length>0&&this.addMessageToChatLog(l,!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 r(new n("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 r(new n(e.reason));return this.setError(t),void this.setChatState(null,exports.ChatState.LLM)}l.length>0&&"message"!=e.type&&(s.push({role:"assistant",type:"message",content:l}),this.addMessageToChatLog(l,!1),l=""),"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):(l+=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;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.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),i=this.exchangeChatStateMapToSet(s);this.chatStateMap=s,this.isEqualChatStateMap(n,i)||this.dispatchChatState(i)}resetChatState(){this.chatStateMap=new Map([[exports.ChatState.RECORDING,0],[exports.ChatState.LLM,0],[exports.ChatState.ANALYZING,0],[exports.ChatState.SPEAKING,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.perso.closeSelf()}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function l(t,e){return await i.getLLMs(t,e)}async function h(t,e){return await i.getTTSs(t,e)}async function d(t,e){return await i.getSTTs(t,e)}async function p(t,e){return await i.getModelStyles(t,e)}async function u(t,e){return await i.getBackgroundImages(t,e)}async function S(t,e){return await i.getPrompts(t,e)}async function g(t,e){return await i.getDocuments(t,e)}async function m(t,e){return await i.getMcpServers(t,e)}exports.ApiError=a,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=r,exports.LLMStreamingResponseError=n,exports.Session=c,exports.createSession=async function(t,e,s,a,r,n){return await(async(t,e,s,a,r,n)=>{let i=null,l=null;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)}}const h=await o.create(t,e,i,s,a),d=new c(t,e,i,h,n);return d.onClose(t=>{l()}),d})(t,e,s,a,r,n)},exports.createSessionId=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("STF_WEBRTC"),s?.llm_type&&(a.capability.push("LLM"),a.llm_type=s.llm_type),s?.tts_type&&(a.capability.push("TTS"),a.tts_type=s.tts_type),s?.stt_type&&(a.capability.push("STT"),a.stt_type=s.stt_type);const r=await fetch(`${t}/api/v1/session/`,{body:JSON.stringify(a),headers:{"PersoLive-APIKey":e,"Content-Type":"application/json"},method:"POST"});return(await i.parseJson(r)).session_id},exports.getAllSettings=async function(t,e){const s=await l(t,e),a=await p(t,e),r=await u(t,e);return{llms:s,ttsTypes:await h(t,e),sttTypes:await d(t,e),modelStyles:a,backgroundImages:r,prompts:await S(t,e),documents:await g(t,e),mcpServers:await m(t,e)}},exports.getBackgroundImages=u,exports.getDocuments=g,exports.getLLMs=l,exports.getMcpServers=m,exports.getModelStyles=p,exports.getPrompts=S,exports.getSTTs=d,exports.getSessionInfo=async function(t,e){return await i.getSessionInfo(t,e)},exports.getTTSs=h;
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level controller around a WebRTC PeerConnection that proxies Perso's
|
|
3
|
+
* real-time APIs through convenience helpers.
|
|
4
|
+
*/
|
|
5
|
+
declare class Perso extends EventTarget {
|
|
6
|
+
#private;
|
|
7
|
+
pc: RTCPeerConnection;
|
|
8
|
+
dc: RTCDataChannel;
|
|
9
|
+
streams: Array<MediaStream>;
|
|
10
|
+
pingTime: number;
|
|
11
|
+
pingIntervalId: number | null;
|
|
12
|
+
/**
|
|
13
|
+
* Hooks a peer/data channel pair to status/ping listeners so consumers can
|
|
14
|
+
* interact with the remote Perso session through a single object.
|
|
15
|
+
* @param pc WebRTC peer connection that handles the media tracks.
|
|
16
|
+
* @param dc Data channel dedicated to control-plane messages.
|
|
17
|
+
*/
|
|
18
|
+
constructor(pc: RTCPeerConnection, dc: RTCDataChannel);
|
|
19
|
+
/**
|
|
20
|
+
* Attaches a local `MediaStream` to the Perso session, negotiates WebRTC
|
|
21
|
+
* connectivity, and waits until the first remote stream is ready.
|
|
22
|
+
* @param apiServer Perso API server URL.
|
|
23
|
+
* @param sessionId Session identifier created via `createSessionId`.
|
|
24
|
+
* @param stream Local camera/mic stream shared with the agent.
|
|
25
|
+
* @param width Desired avatar canvas width.
|
|
26
|
+
* @param height Desired avatar canvas height.
|
|
27
|
+
* @returns Ready-to-use `Perso` instance.
|
|
28
|
+
* @throws Timeout When remote streams fail to arrive in time.
|
|
29
|
+
*/
|
|
30
|
+
static create(apiServer: string, sessionId: string, stream: MediaStream, width: number, height: number): Promise<Perso>;
|
|
31
|
+
/**
|
|
32
|
+
* Configures a browser `RTCPeerConnection` with the ICE servers provided by
|
|
33
|
+
* the Perso API.
|
|
34
|
+
* @param iceServers ICE server configuration list.
|
|
35
|
+
* @returns Initialized RTCPeerConnection.
|
|
36
|
+
*/
|
|
37
|
+
private static createPeerConnection;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves once `condition()` passes or throws a `Timeout` when the maximum
|
|
40
|
+
* number of checks is exceeded.
|
|
41
|
+
* @param condition Predicate that signals readiness.
|
|
42
|
+
* @param interval Interval between checks in milliseconds.
|
|
43
|
+
* @param times Maximum number of attempts before timing out.
|
|
44
|
+
* @throws Timeout When the predicate never returns true.
|
|
45
|
+
*/
|
|
46
|
+
private static waitFor;
|
|
47
|
+
/**
|
|
48
|
+
* Returns true when the first remote track has been attached and the data
|
|
49
|
+
* channel is open.
|
|
50
|
+
* @returns Whether the instance is ready for interaction.
|
|
51
|
+
*/
|
|
52
|
+
isReady(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Subscribes to status updates and returns an unsubscribe helper to mirror
|
|
55
|
+
* the EventTarget subscription pattern.
|
|
56
|
+
* @param callback Listener invoked for each status event.
|
|
57
|
+
* @returns Unsubscribe function that removes the listener.
|
|
58
|
+
*/
|
|
59
|
+
subscribeStatus(callback: (event: CustomEvent) => void): () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Returns the first incoming remote stream if available.
|
|
62
|
+
* @returns Primary remote `MediaStream`.
|
|
63
|
+
*/
|
|
64
|
+
getStream(): MediaStream;
|
|
65
|
+
/**
|
|
66
|
+
* Sends a typed JSON payload through the control data channel. All higher
|
|
67
|
+
* level helpers eventually defer to this method.
|
|
68
|
+
* @param type Message type identifier.
|
|
69
|
+
* @param data Arbitrary JSON-serializable payload.
|
|
70
|
+
*/
|
|
71
|
+
sendMessage(type: string, data: object): void;
|
|
72
|
+
/**
|
|
73
|
+
* Requests a TTS-to-face (TTSTF) playback with the provided text.
|
|
74
|
+
* @param message Text to synthesize and animate.
|
|
75
|
+
*/
|
|
76
|
+
ttstf(message: string): void;
|
|
77
|
+
/**
|
|
78
|
+
* Signals the remote agent to start buffering microphone audio.
|
|
79
|
+
*/
|
|
80
|
+
recordStart(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Stops recording and asks the server to run speech-to-text optionally using
|
|
83
|
+
* a specific language.
|
|
84
|
+
* @param language Optional language code for STT.
|
|
85
|
+
*/
|
|
86
|
+
recordEndStt(language?: string): void;
|
|
87
|
+
/**
|
|
88
|
+
* Stops recording and translates the captured speech from `src_lang` to
|
|
89
|
+
* `dst_lang`.
|
|
90
|
+
* @param src_lang Source language code.
|
|
91
|
+
* @param dst_lang Destination language code.
|
|
92
|
+
*/
|
|
93
|
+
recordEndTranslate(src_lang: string, dst_lang: string): void;
|
|
94
|
+
/**
|
|
95
|
+
* Resizes the render canvas of the avatar/video surface on the remote side.
|
|
96
|
+
* @param width Target width in CSS pixels.
|
|
97
|
+
* @param height Target height in CSS pixels.
|
|
98
|
+
*/
|
|
99
|
+
changeSize(width: number, height: number): void;
|
|
100
|
+
/**
|
|
101
|
+
* Switches the avatar template (model + dress) at runtime.
|
|
102
|
+
* @param model Optional avatar model ID.
|
|
103
|
+
* @param dress Optional outfit ID.
|
|
104
|
+
*/
|
|
105
|
+
setTemplate(model?: string, dress?: string): void;
|
|
106
|
+
/**
|
|
107
|
+
* Drops any buffered speech or text that has not been processed yet.
|
|
108
|
+
*/
|
|
109
|
+
clearBuffer(): void;
|
|
110
|
+
/**
|
|
111
|
+
* Sends a heartbeat over the data channel to keep the connection alive.
|
|
112
|
+
*/
|
|
113
|
+
ping(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Registers a data-channel handler for a specific message `type` and returns
|
|
116
|
+
* a remover so callers can dispose of the listener cleanly.
|
|
117
|
+
* @param type Message type to watch for.
|
|
118
|
+
* @param callback Handler invoked with the parsed payload.
|
|
119
|
+
* @returns Function that removes the listener.
|
|
120
|
+
*/
|
|
121
|
+
setMessageCallback(type: string, callback: (data: any) => void): () => void;
|
|
122
|
+
/**
|
|
123
|
+
* Tears down the PeerConnection due to remote/network failure and emits a
|
|
124
|
+
* timeout status so the UI can inform users.
|
|
125
|
+
*/
|
|
126
|
+
private close;
|
|
127
|
+
/**
|
|
128
|
+
* Allows callers to gracefully terminate a session themselves and emit a
|
|
129
|
+
* successful status code for analytics.
|
|
130
|
+
*/
|
|
131
|
+
closeSelf(): void;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Represents a single entry shown in the chat log UI.
|
|
136
|
+
*/
|
|
137
|
+
interface Chat {
|
|
138
|
+
text: string;
|
|
139
|
+
isUser: boolean;
|
|
140
|
+
timestamp: Date;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Discrete states that describe where the conversation currently is
|
|
144
|
+
* (recording, running the LLM, analyzing text, speaking back, etc.).
|
|
145
|
+
*/
|
|
146
|
+
declare enum ChatState {
|
|
147
|
+
RECORDING = "RECORDING",
|
|
148
|
+
LLM = "LLM",
|
|
149
|
+
ANALYZING = "ANALYZING",
|
|
150
|
+
SPEAKING = "SPEAKING"
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Container describing a callable tool (local client helper or remote MCP)
|
|
154
|
+
* that the LLM runtime can invoke during conversations.
|
|
155
|
+
*/
|
|
156
|
+
declare class ChatTool {
|
|
157
|
+
name: string;
|
|
158
|
+
description: string;
|
|
159
|
+
parameters: object;
|
|
160
|
+
call: (arg: any) => object | Promise<object>;
|
|
161
|
+
executeOnly: boolean;
|
|
162
|
+
constructor(name: string, description: string, parameters: object, call: (arg: any) => object | Promise<object>, executeOnly?: boolean);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Manages a full Perso chat session including UI state, LLM orchestration,
|
|
166
|
+
* microphone handling, and speech synthesis triggers.
|
|
167
|
+
*/
|
|
168
|
+
declare class Session {
|
|
169
|
+
apiServer: string;
|
|
170
|
+
sessionId: string;
|
|
171
|
+
stream: MediaStream;
|
|
172
|
+
perso: Perso;
|
|
173
|
+
clientTools: Array<ChatTool>;
|
|
174
|
+
private chatStatesHandler;
|
|
175
|
+
private chatLogHandler;
|
|
176
|
+
private sttEventHandler;
|
|
177
|
+
private errorHandler;
|
|
178
|
+
private lastStfTimeoutHandle;
|
|
179
|
+
private stfTotalDuration;
|
|
180
|
+
private stfTimeoutStartTime;
|
|
181
|
+
private messageHistory;
|
|
182
|
+
private chatLog;
|
|
183
|
+
private chatStateMap;
|
|
184
|
+
private emojiRegex;
|
|
185
|
+
/**
|
|
186
|
+
* Sets up message listeners and chat-state trackers for a Perso session.
|
|
187
|
+
* @param apiServer Perso API server URL.
|
|
188
|
+
* @param sessionId Id of the session negotiated with the backend.
|
|
189
|
+
* @param stream Local audio stream shared with the session.
|
|
190
|
+
* @param perso Underlying Perso WebRTC controller.
|
|
191
|
+
* @param clientTools Tools exposed to the LLM for function calling.
|
|
192
|
+
*/
|
|
193
|
+
constructor(apiServer: string, sessionId: string, stream: MediaStream, perso: Perso, clientTools: Array<ChatTool>);
|
|
194
|
+
private llmJob;
|
|
195
|
+
/**
|
|
196
|
+
* Sends a user utterance through Perso's internal LLM and speaks the result
|
|
197
|
+
* while automatically updating history, chat logs, and chat states.
|
|
198
|
+
* @param message User message to forward to the LLM.
|
|
199
|
+
* @remarks
|
|
200
|
+
* - Emits entries via `subscribeChatLog`.
|
|
201
|
+
* - Updates all chat states published through `subscribeChatStates`.
|
|
202
|
+
* - Maintains `messageHistory` for subsequent LLM calls.
|
|
203
|
+
*/
|
|
204
|
+
processChat(message: string): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Plays back a response produced by a custom/external LLM without calling
|
|
207
|
+
* the built-in Perso LLM pipeline.
|
|
208
|
+
* @param message Assistant response generated externally.
|
|
209
|
+
* @remarks
|
|
210
|
+
* - Does not mutate `messageHistory`.
|
|
211
|
+
* - Does not emit chat-log updates.
|
|
212
|
+
* - Does not toggle the `LLM` chat state.
|
|
213
|
+
*/
|
|
214
|
+
processCustomChat(message: string): void;
|
|
215
|
+
/**
|
|
216
|
+
* Sends an assistant message to the LLM history and triggers TTSTF playback.
|
|
217
|
+
* @param message Assistant output that should be spoken immediately.
|
|
218
|
+
*/
|
|
219
|
+
processTTSTF(message: string): void;
|
|
220
|
+
/**
|
|
221
|
+
* Triggers the recording state and instructs Perso to buffer microphone
|
|
222
|
+
* audio for speech-to-text.
|
|
223
|
+
* @returns Result of `perso.recordStart()`.
|
|
224
|
+
*/
|
|
225
|
+
startVoiceChat(): void;
|
|
226
|
+
/**
|
|
227
|
+
* Stops the microphone capture, transitions the UI to analyzing, and sends
|
|
228
|
+
* the buffered audio to STT.
|
|
229
|
+
*/
|
|
230
|
+
stopVoiceChat(): void;
|
|
231
|
+
/**
|
|
232
|
+
* Resizes the avatar video canvas on the remote renderer.
|
|
233
|
+
* @param width Target width in CSS pixels.
|
|
234
|
+
* @param height Target height in CSS pixels.
|
|
235
|
+
*/
|
|
236
|
+
changeSize(width: number, height: number): void;
|
|
237
|
+
/**
|
|
238
|
+
* Cancels any ongoing LLM/TTS jobs, clears remote buffers, and resets all
|
|
239
|
+
* chat-state timers.
|
|
240
|
+
*/
|
|
241
|
+
clearBuffer(): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* Assigns the remote video stream to a DOM video tag.
|
|
244
|
+
* @param element Target video element.
|
|
245
|
+
*/
|
|
246
|
+
setSrc(element: HTMLVideoElement): void;
|
|
247
|
+
/**
|
|
248
|
+
* Returns the local microphone stream associated with the session.
|
|
249
|
+
* @returns Local `MediaStream`.
|
|
250
|
+
*/
|
|
251
|
+
getLocalStream(): MediaStream;
|
|
252
|
+
/**
|
|
253
|
+
* Returns the first remote stream exposed by the Perso renderer.
|
|
254
|
+
* @returns Remote `MediaStream`.
|
|
255
|
+
*/
|
|
256
|
+
getRemoteStream(): MediaStream;
|
|
257
|
+
/**
|
|
258
|
+
* Gracefully closes the session and remote connection.
|
|
259
|
+
*/
|
|
260
|
+
stopSession(): void;
|
|
261
|
+
/**
|
|
262
|
+
* Subscribes to Perso status events and notifies the caller when the session
|
|
263
|
+
* closes (distinguishing manual/automatic closure).
|
|
264
|
+
* @param callback Invoked with `true` when closed manually.
|
|
265
|
+
* @returns Function to unsubscribe the listener.
|
|
266
|
+
*/
|
|
267
|
+
onClose(callback: (manualClosed: boolean) => void): () => void;
|
|
268
|
+
/**
|
|
269
|
+
* Subscribes to chat-state updates.
|
|
270
|
+
* @param callback Handler receiving the active state set.
|
|
271
|
+
* @returns Function to unsubscribe.
|
|
272
|
+
*/
|
|
273
|
+
subscribeChatStates(callback: (chatStates: Set<ChatState>) => void): () => void;
|
|
274
|
+
/**
|
|
275
|
+
* Subscribes to chat-log updates (most recent message first).
|
|
276
|
+
* @param callback Handler receiving the full chat log snapshot.
|
|
277
|
+
* @returns Function to unsubscribe.
|
|
278
|
+
*/
|
|
279
|
+
subscribeChatLog(callback: (chatLog: Array<Chat>) => void): () => void;
|
|
280
|
+
/**
|
|
281
|
+
* Streams raw STT text results to the provided callback instead of routing
|
|
282
|
+
* them back into the LLM pipeline automatically.
|
|
283
|
+
* @param callback Handler for STT transcripts.
|
|
284
|
+
* @returns Function to unsubscribe/reset STT event handling.
|
|
285
|
+
*/
|
|
286
|
+
setSttResultCallback(callback: (text: string) => void): () => void;
|
|
287
|
+
/**
|
|
288
|
+
* Allows UI code to react to LLM/streaming errors.
|
|
289
|
+
* @param callback Handler receiving the raised error.
|
|
290
|
+
* @returns Function to unsubscribe.
|
|
291
|
+
*/
|
|
292
|
+
setErrorHandler(callback: (error: Error) => void): () => void;
|
|
293
|
+
/**
|
|
294
|
+
* @returns Session identifier assigned by the backend.
|
|
295
|
+
*/
|
|
296
|
+
getSessionId(): string;
|
|
297
|
+
/**
|
|
298
|
+
* Streams responses from the Perso LLM endpoint, handles tool calls, and
|
|
299
|
+
* updates chat history/state accordingly.
|
|
300
|
+
* @param message Optional user message array or string injected ahead of the
|
|
301
|
+
* pending history (null when recursively continuing after tool calls).
|
|
302
|
+
* @remarks
|
|
303
|
+
* - Accumulates `type: "message"` chunks until a non-message event arrives.
|
|
304
|
+
* - When tool calls are returned, executes client tools (and recursively calls
|
|
305
|
+
* itself if follow-up LLM output is required).
|
|
306
|
+
* - Adds every spoken assistant message to the chat log and messageHistory.
|
|
307
|
+
*/
|
|
308
|
+
private processChatInternal;
|
|
309
|
+
/**
|
|
310
|
+
* Looks up a tool definition by the function name provided in a tool_call.
|
|
311
|
+
* @param clientTools Registered tools.
|
|
312
|
+
* @param funcName Name requested by the LLM.
|
|
313
|
+
* @returns Matching `ChatTool` or null.
|
|
314
|
+
*/
|
|
315
|
+
private getChatTool;
|
|
316
|
+
private llmCancel;
|
|
317
|
+
/**
|
|
318
|
+
* Cancels any in-flight LLM stream by flipping the cancellation flag and
|
|
319
|
+
* awaiting the pending promise if necessary.
|
|
320
|
+
*/
|
|
321
|
+
private clearLLMJob;
|
|
322
|
+
/**
|
|
323
|
+
* Filters/sanitizes text and sends it to Perso's TTSTF endpoint while toggling
|
|
324
|
+
* the ANALYZING chat state.
|
|
325
|
+
* @param message Assistant message to speak aloud.
|
|
326
|
+
*/
|
|
327
|
+
private processTTSTFInternal;
|
|
328
|
+
/**
|
|
329
|
+
* Adds an entry at the top of the chat log and notifies subscribers.
|
|
330
|
+
* @param message Text to store.
|
|
331
|
+
* @param isUser Whether the entry was produced by the user.
|
|
332
|
+
*/
|
|
333
|
+
private addMessageToChatLog;
|
|
334
|
+
/**
|
|
335
|
+
* Adjusts the internal reference-counted chat-state map and emits changes as
|
|
336
|
+
* needed.
|
|
337
|
+
* @param add State(s) to activate/increment.
|
|
338
|
+
* @param remove State(s) to deactivate/decrement.
|
|
339
|
+
*/
|
|
340
|
+
private setChatState;
|
|
341
|
+
/**
|
|
342
|
+
* Resets all chat states to an idle baseline and emits the update.
|
|
343
|
+
*/
|
|
344
|
+
private resetChatState;
|
|
345
|
+
/**
|
|
346
|
+
* Converts the ref-counted map into a set of active chat states.
|
|
347
|
+
* @param state Current state map.
|
|
348
|
+
* @returns Set of states whose count is > 0.
|
|
349
|
+
*/
|
|
350
|
+
private exchangeChatStateMapToSet;
|
|
351
|
+
/**
|
|
352
|
+
* Broadcasts chat-state updates via the internal EventTarget.
|
|
353
|
+
* @param newChatStateSet Active state set.
|
|
354
|
+
*/
|
|
355
|
+
private dispatchChatState;
|
|
356
|
+
/**
|
|
357
|
+
* Compares two chat-state sets for equality.
|
|
358
|
+
*/
|
|
359
|
+
private isEqualChatStateMap;
|
|
360
|
+
/**
|
|
361
|
+
* Emits an error event for UI subscribers.
|
|
362
|
+
*/
|
|
363
|
+
private setError;
|
|
364
|
+
/**
|
|
365
|
+
* Gracefully closes the underlying Perso connection on behalf of the session.
|
|
366
|
+
*/
|
|
367
|
+
private close;
|
|
368
|
+
/**
|
|
369
|
+
* Strips emoji characters that TTSTF may not render correctly.
|
|
370
|
+
* @param str Text to sanitize.
|
|
371
|
+
* @returns Filtered string.
|
|
372
|
+
*/
|
|
373
|
+
private removeEmoji;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Retrieves the list of available LLM providers from the API.
|
|
378
|
+
* @param apiServer Perso API server URL.
|
|
379
|
+
* @param apiKey API key used for authentication.
|
|
380
|
+
* @returns Promise resolving with LLM metadata.
|
|
381
|
+
*/
|
|
382
|
+
declare function getLLMs(apiServer: string, apiKey: string): Promise<any>;
|
|
383
|
+
/**
|
|
384
|
+
* Retrieves available TTS providers.
|
|
385
|
+
* @param apiServer Perso API server URL.
|
|
386
|
+
* @param apiKey API key used for authentication.
|
|
387
|
+
*/
|
|
388
|
+
declare function getTTSs(apiServer: string, apiKey: string): Promise<any>;
|
|
389
|
+
/**
|
|
390
|
+
* Retrieves available STT providers.
|
|
391
|
+
* @param apiServer Perso API server URL.
|
|
392
|
+
* @param apiKey API key used for authentication.
|
|
393
|
+
*/
|
|
394
|
+
declare function getSTTs(apiServer: string, apiKey: string): Promise<any>;
|
|
395
|
+
/**
|
|
396
|
+
* Fetches avatar model styles.
|
|
397
|
+
* @param apiServer Perso API server URL.
|
|
398
|
+
* @param apiKey API key used for authentication.
|
|
399
|
+
*/
|
|
400
|
+
declare function getModelStyles(apiServer: string, apiKey: string): Promise<any>;
|
|
401
|
+
/**
|
|
402
|
+
* Fetches preset background images.
|
|
403
|
+
* @param apiServer Perso API server URL.
|
|
404
|
+
* @param apiKey API key used for authentication.
|
|
405
|
+
*/
|
|
406
|
+
declare function getBackgroundImages(apiServer: string, apiKey: string): Promise<any>;
|
|
407
|
+
/**
|
|
408
|
+
* Returns predefined prompt templates.
|
|
409
|
+
* @param apiServer Perso API server URL.
|
|
410
|
+
* @param apiKey API key used for authentication.
|
|
411
|
+
*/
|
|
412
|
+
declare function getPrompts(apiServer: string, apiKey: string): Promise<any>;
|
|
413
|
+
/**
|
|
414
|
+
* Returns supporting document metadata usable by the session.
|
|
415
|
+
* @param apiServer Perso API server URL.
|
|
416
|
+
* @param apiKey API key used for authentication.
|
|
417
|
+
*/
|
|
418
|
+
declare function getDocuments(apiServer: string, apiKey: string): Promise<any>;
|
|
419
|
+
/**
|
|
420
|
+
* Lists MCP server identifiers configured for the tenant.
|
|
421
|
+
* @param apiServer Perso API server URL.
|
|
422
|
+
* @param apiKey API key used for authentication.
|
|
423
|
+
*/
|
|
424
|
+
declare function getMcpServers(apiServer: string, apiKey: string): Promise<any>;
|
|
425
|
+
/**
|
|
426
|
+
* Convenience helper that fetches every dropdown-friendly resource needed to
|
|
427
|
+
* build a Perso session configuration screen in one call chain.
|
|
428
|
+
* @param apiServer Perso API server URL.
|
|
429
|
+
* @param apiKey API key used for authentication.
|
|
430
|
+
* @returns Object containing arrays for LLMs, TTS/STT types, model styles, etc.
|
|
431
|
+
*/
|
|
432
|
+
declare function getAllSettings(apiServer: string, apiKey: string): Promise<{
|
|
433
|
+
llms: any;
|
|
434
|
+
ttsTypes: any;
|
|
435
|
+
sttTypes: any;
|
|
436
|
+
modelStyles: any;
|
|
437
|
+
backgroundImages: any;
|
|
438
|
+
prompts: any;
|
|
439
|
+
documents: any;
|
|
440
|
+
mcpServers: any;
|
|
441
|
+
}>;
|
|
442
|
+
/**
|
|
443
|
+
* Wraps the lower-level `session.createSession` helper so callers can import
|
|
444
|
+
* from this module.
|
|
445
|
+
* @param apiServer Perso API server URL.
|
|
446
|
+
* @param sessionId Session id to attach to.
|
|
447
|
+
* @param width Avatar canvas width.
|
|
448
|
+
* @param height Avatar canvas height.
|
|
449
|
+
* @param enableVoiceChat Whether microphone capture should be enabled.
|
|
450
|
+
* @param clientTools Client-side tools available for function calling.
|
|
451
|
+
* @returns Initialized Session.
|
|
452
|
+
*/
|
|
453
|
+
declare function createSession(apiServer: string, sessionId: string, width: number, height: number, enableVoiceChat: boolean, clientTools: Array<ChatTool>): Promise<Session>;
|
|
454
|
+
/**
|
|
455
|
+
* Retrieves metadata for an existing session.
|
|
456
|
+
* @param apiServer Perso API server URL.
|
|
457
|
+
* @param sessionId Session id to inspect.
|
|
458
|
+
*/
|
|
459
|
+
declare function getSessionInfo(apiServer: string, sessionId: string): Promise<any>;
|
|
460
|
+
|
|
461
|
+
type CreateSessionIdBody = {
|
|
462
|
+
using_stf_webrtc: boolean;
|
|
463
|
+
model_style: string;
|
|
464
|
+
prompt: string;
|
|
465
|
+
document?: string;
|
|
466
|
+
background_image?: string;
|
|
467
|
+
mcp_servers?: Array<string>;
|
|
468
|
+
padding_left?: number;
|
|
469
|
+
padding_top?: number;
|
|
470
|
+
padding_height?: number;
|
|
471
|
+
llm_type?: string;
|
|
472
|
+
tts_type?: string;
|
|
473
|
+
stt_type?: string;
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* Requests a new session creation ID by POSTing the desired runtime options to
|
|
477
|
+
* the Perso backend (`/api/v1/session/`).
|
|
478
|
+
*
|
|
479
|
+
* WARNING: This function requires an API key. When using this function in
|
|
480
|
+
* client-side code, the API key will be exposed to the browser. For production
|
|
481
|
+
* use, prefer creating session IDs server-side using `perso-interactive-sdk-web/server`.
|
|
482
|
+
*
|
|
483
|
+
* @param apiServer Perso API server URL.
|
|
484
|
+
* @param apiKey API key used for authentication.
|
|
485
|
+
* @param params {
|
|
486
|
+
* using_stf_webrtc: boolean;
|
|
487
|
+
* llm_type?: string;
|
|
488
|
+
* tts_type?: string;
|
|
489
|
+
* stt_type?: string;
|
|
490
|
+
* model_style: string;
|
|
491
|
+
* prompt: string;
|
|
492
|
+
* document?: string;
|
|
493
|
+
* background_image?: string;
|
|
494
|
+
* mcp_servers?: Array<string>;
|
|
495
|
+
* padding_left?: number;
|
|
496
|
+
* padding_top?: number;
|
|
497
|
+
* padding_height?: number;
|
|
498
|
+
* }
|
|
499
|
+
* @returns Session ID returned by the server.
|
|
500
|
+
*/
|
|
501
|
+
declare const createSessionId: (apiServer: string, apiKey: string, params: CreateSessionIdBody) => Promise<string>;
|
|
502
|
+
|
|
503
|
+
declare class ApiError extends Error {
|
|
504
|
+
errorCode: number;
|
|
505
|
+
code: string;
|
|
506
|
+
detail: string;
|
|
507
|
+
attr?: string | undefined;
|
|
508
|
+
constructor(errorCode: number, code: string, detail: string, attr?: string | undefined);
|
|
509
|
+
}
|
|
510
|
+
declare class LLMError extends Error {
|
|
511
|
+
underlyingError: ApiError | LLMStreamingResponseError;
|
|
512
|
+
constructor(underlyingError: ApiError | LLMStreamingResponseError);
|
|
513
|
+
}
|
|
514
|
+
declare class LLMStreamingResponseError extends Error {
|
|
515
|
+
description: string;
|
|
516
|
+
constructor(description: string);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export { ApiError, ChatState, ChatTool, LLMError, LLMStreamingResponseError, Session, createSession, createSessionId, getAllSettings, getBackgroundImages, getDocuments, getLLMs, getMcpServers, getModelStyles, getPrompts, getSTTs, getSessionInfo, getTTSs };
|
|
520
|
+
export type { Chat };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var PersoInteractive=function(D){"use strict";class u extends Error{constructor(){super("WebRTC connection timeout")}}class t extends Error{errorCode;code;detail;attr;constructor(D,u,t,e){let F;F=null!=e?`${D}:${e}_${t}`:`${D}:${t}`,super(F),this.errorCode=D,this.code=u,this.detail=t,this.attr=e}}class e extends Error{underlyingError;constructor(D){super(),this.underlyingError=D}}class F extends Error{description;constructor(D){super(),this.description=D}}class s{static async getLLMs(D,u){const t=fetch(`${D}/api/v1/settings/llm_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getModelStyles(D,u){const t=fetch(`${D}/api/v1/settings/modelstyle/?platform_type=webrtc`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getBackgroundImages(D,u){const t=fetch(`${D}/api/v1/background_image/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getTTSs(D,u){const t=fetch(`${D}/api/v1/settings/tts_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getSTTs(D,u){const t=fetch(`${D}/api/v1/settings/stt_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getPrompts(D,u){const t=fetch(`${D}/api/v1/prompt/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getDocuments(D,u){const t=fetch(`${D}/api/v1/document/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getMcpServers(D,u){const t=fetch(`${D}/api/v1/settings/mcp_type/`,{headers:{"PersoLive-APIKey":u},method:"GET"}),e=await t;return await this.parseJson(e)}static async getSessionInfo(D,u){const t=fetch(`${D}/api/v1/session/${u}/`,{method:"GET"}),e=await t;return await this.parseJson(e)}static async sessionEvent(D,u,t){const e=fetch(`${D}/api/v1/session/${u}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:t})});await e}static async parseJson(D){const u=await D.json();if(D.ok)return u;{const e=u.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${D.status} with no error details`,attr:null};throw new t(D.status,e.code,e.detail,e.attr)}}}var a;class C extends EventTarget{pc;dc;streams=[];pingTime;pingIntervalId=null;constructor(D,u){super(),this.pc=D,this.dc=u,this.pingTime=Date.now()+3e3,this.pc.addEventListener("track",D=>{this.streams=this.streams.concat(D.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.#D({live:!0,code:200,reason:"OK"}),this.setMessageCallback("ping",()=>{this.pingTime=Date.now()})}static async create(D,u,t,e,F){const a=await fetch(`${D}/api/v1/session/${u}/ice-servers/`,{method:"GET"}),n=(await s.parseJson(a)).ice_servers;let i=await C.createPeerConnection(n),r=i.createDataChannel("message",{protocol:"message"}),E=new C(i,r);t.getTracks().forEach(function(D){i.addTrack(D,t)});const o=i.addTransceiver("video",{direction:"recvonly"}),c=RTCRtpReceiver.getCapabilities("video");null!=c&&o.setCodecPreferences(c.codecs.filter(D=>"video/VP8"===D.mimeType));const l=await i.createOffer();await i.setLocalDescription(l);const h=await fetch(`${D}/api/v1/session/${u}/exchange/`,{body:JSON.stringify({client_sdp:l}),headers:{"Content-Type":"application/json"},method:"POST"}),B=await s.parseJson(h);return await i.setRemoteDescription(B.server_sdp),await C.waitFor(()=>E.isReady(),100,50),E.changeSize(e,F),E}static async createPeerConnection(D){return new RTCPeerConnection({sdpSemantics:"unified-plan",iceServers:D})}static async waitFor(D,t,e){let F=0;if(await new Promise(u=>{const s=setInterval(()=>{F+=1,F>=e&&(clearInterval(s),u("bad")),D()&&(clearInterval(s),u("good"))},t)}),F>=e)throw new u}isReady(){return this.streams.length>0&&"open"===this.dc.readyState}#D(D){this.dispatchEvent(new CustomEvent("status",{detail:D}))}subscribeStatus(D){return this.addEventListener("status",D),()=>{this.removeEventListener("status",D)}}getStream(){return this.streams[0]}sendMessage(D,u){this.dc.send(JSON.stringify({type:D,data:u}))}ttstf(D){this.sendMessage("ttstf",{message:D})}recordStart(){this.sendMessage("record-start",{})}recordEndStt(D){this.sendMessage("record-end-stt",{language:D})}recordEndTranslate(D,u){this.sendMessage("record-end-translate",{src_lang:D,dst_lang:u})}changeSize(D,u){this.sendMessage("change-size",{width:D,height:u})}setTemplate(D,u){this.sendMessage("set-template",{model:D,dress:u})}clearBuffer(){this.sendMessage("clear-buffer",{})}ping(){this.sendMessage("ping",{})}setMessageCallback(D,u){const t=t=>{const e=JSON.parse(t.data);e.type===D&&u(e.data)};return this.dc.addEventListener("message",t),()=>{this.dc.removeEventListener("message",t)}}close(){this.dc.close(),this.pc.close(),this.#D({live:!1,code:408,reason:"Request Timeout"})}closeSelf(){this.dc.close(),this.pc.close(),this.#D({live:!1,code:200,reason:"OK"})}}D.ChatState=void 0,(a=D.ChatState||(D.ChatState={})).RECORDING="RECORDING",a.LLM="LLM",a.ANALYZING="ANALYZING",a.SPEAKING="SPEAKING";class n{apiServer;sessionId;stream;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];chatStateMap=new Map([[D.ChatState.RECORDING,0],[D.ChatState.LLM,0],[D.ChatState.ANALYZING,0],[D.ChatState.SPEAKING,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;constructor(u,t,e,F,s){this.apiServer=u,this.sessionId=t,this.stream=e,this.perso=F,this.clientTools=s,this.resetChatState(),F.setMessageCallback("stf",u=>{if(this.setChatState(D.ChatState.SPEAKING,D.ChatState.ANALYZING),null!==this.lastStfTimeoutHandle){clearTimeout(this.lastStfTimeoutHandle);let t=Date.now();this.stfTotalDuration+=u.duration+1e3-(t-this.stfTimeoutStartTime),this.stfTimeoutStartTime=t,this.lastStfTimeoutHandle=setTimeout(()=>{this.lastStfTimeoutHandle=null,this.stfTimeoutStartTime=0,this.stfTotalDuration=0,this.setChatState(null,D.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,D.ChatState.SPEAKING)},this.stfTotalDuration)}),F.setMessageCallback("stt",u=>{if(this.setChatState(null,D.ChatState.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:u.text}));else{if(""===u.text)return;this.processChat(u.text)}}),F.setMessageCallback("stt-error",u=>{this.setChatState(null,D.ChatState.ANALYZING)})}llmJob=null;async processChat(D){0!==D.trim().length&&(this.addMessageToChatLog(D,!0),this.llmJob=this.processChatInternal(D))}processCustomChat(D){0!==D.trim().length&&this.processTTSTFInternal(D)}processTTSTF(D){0!==D.trim().length&&(this.messageHistory.push({role:"assistant",type:"message",content:D}),this.addMessageToChatLog(D,!1),this.processTTSTFInternal(D))}startVoiceChat(){return this.setChatState(D.ChatState.RECORDING),this.perso.recordStart()}stopVoiceChat(){this.setChatState(D.ChatState.ANALYZING,D.ChatState.RECORDING),this.perso.recordEndStt()}changeSize(D,u){this.perso.changeSize(D,u)}async clearBuffer(){await this.clearLLMJob(),this.perso.clearBuffer(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.resetChatState()}setSrc(D){D.srcObject=this.getRemoteStream()}getLocalStream(){return this.stream}getRemoteStream(){return this.perso.getStream()}stopSession(){this.close()}onClose(D){return this.perso.subscribeStatus(u=>{null!=u.detail&&!1===u.detail.live&&D(200===u.detail.code)})}subscribeChatStates(D){const u=u=>{D(u.detail.status)};return this.chatStatesHandler.addEventListener("status",u),()=>{this.chatStatesHandler.removeEventListener("status",u)}}subscribeChatLog(D){const u=u=>{D(u.detail.chatLog)};return this.chatLogHandler.addEventListener("chatLog",u),()=>{this.chatLogHandler.removeEventListener("chatLog",u)}}setSttResultCallback(D){const u=u=>{D(u.detail)};return this.sttEventHandler=new EventTarget,this.sttEventHandler.addEventListener("stt",u),()=>{this.sttEventHandler?.removeEventListener("stt",u),this.sttEventHandler=null}}setErrorHandler(D){const u=u=>{D(u.detail.error)};return this.errorHandler.addEventListener("error",u),()=>{this.errorHandler.removeEventListener("error",u)}}getSessionId(){return this.sessionId}async processChatInternal(u){this.setChatState(D.ChatState.LLM);const s=this.clientTools.map(D=>({type:"function",function:{description:D.description,name:D.name,parameters:D.parameters}})),a=new Array;null===u||(u instanceof Array?a.push(...u):"string"==typeof u&&a.push({role:"user",content:u}));const C=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...a],tools:s}),headers:{"Content-Type":"application/json"},method:"POST"});if(!C.ok){const u=await C.json(),F=new e(new t(C.status,u.errors[0].code,u.errors[0].detail,u.errors[0].attr));return this.setError(F),void this.setChatState(null,D.ChatState.LLM)}const n=C.body?.getReader(),i=new TextDecoder("utf-8");let r="",E=null,o="";for(;;){const{done:u,value:t}=await n.read();if(u)break;let s;for(o+=i.decode(t,{stream:!0});-1!==(s=o.indexOf("\n"));){if(this.llmCancel)return r.length>0&&this.addMessageToChatLog(r,!1),void this.setChatState(null,D.ChatState.LLM);const u=o.slice(0,s).trim();if(o=o.slice(s+1),!u.startsWith("data: {")){const u=new e(new F("Failed to parse SSE response"));return this.setError(u),void this.setChatState(null,D.ChatState.LLM)}const t=JSON.parse(u.slice(6).trim());if("success"!==t.status){const u=new e(new F(t.reason));return this.setError(u),void this.setChatState(null,D.ChatState.LLM)}r.length>0&&"message"!=t.type&&(a.push({role:"assistant",type:"message",content:r}),this.addMessageToChatLog(r,!1),r=""),"message"!==t.type?"tool_call"!==t.type||null==t.tool_calls?"tool"!==t.role||"tool_call"===t.type&&a.push({role:t.role,type:t.type,content:t.content,tool_call_id:t.tool_call_id}):(a.push({role:"assistant",type:t.type,content:t.content,tool_calls:t.tool_calls}),E=t):(r+=t.content,this.processTTSTFInternal(t.content))}}if(this.llmCancel)this.setChatState(null,D.ChatState.LLM);else{if(null!=E){const D=[];for(const u of E.tool_calls){const t=this.getChatTool(this.clientTools,u.function.name);null!=t&&D.push(new Promise(async D=>{try{const e=await t.call(JSON.parse(u.function.arguments));D({toolCallId:u.id,chatTool:t,chatToolResult:e})}catch(e){D({toolCallId:u.id,chatTool:t,chatToolResult:{result:"error!"}})}}))}const u=await Promise.all(D);for(const D of u)a.push({role:"tool",content:JSON.stringify(D.chatToolResult),tool_call_id:D.toolCallId});const t=u.length>0&&E.tool_calls.length!==u.length,e=u.some(D=>!D.chatTool.executeOnly);t||e?await this.processChatInternal(a):this.messageHistory.push(...a)}else this.messageHistory.push(...a);this.setChatState(null,D.ChatState.LLM)}}getChatTool(D,u){for(const t of D)if(t.name===u)return t;return null}llmCancel=!1;async clearLLMJob(){null!=this.llmJob&&(this.llmCancel=!0,await this.llmJob,this.llmCancel=!1)}processTTSTFInternal(u){const t=this.removeEmoji(u).trim();0!==t.length&&(this.setChatState(D.ChatState.ANALYZING),this.perso.ttstf(t))}addMessageToChatLog(D,u){this.chatLog=[{text:D,isUser:u,timestamp:new Date},...this.chatLog],this.chatLogHandler.dispatchEvent(new CustomEvent("chatLog",{detail:{chatLog:this.chatLog}}))}setChatState(u=null,t=null){const e=new Map(this.chatStateMap);function F(u){u===D.ChatState.ANALYZING?e.set(u,(e.get(u)||0)+1):e.set(u,1)}function s(u){u===D.ChatState.ANALYZING?e.set(u,Math.max((e.get(u)||0)-1,0)):e.set(u,0)}if(null!=u)if(u instanceof Array)for(let D of u)F(D);else F(u);if(null!=t)if(t instanceof Array)for(let D of t)s(D);else s(t);const a=this.exchangeChatStateMapToSet(this.chatStateMap),C=this.exchangeChatStateMapToSet(e);this.chatStateMap=e,this.isEqualChatStateMap(a,C)||this.dispatchChatState(C)}resetChatState(){this.chatStateMap=new Map([[D.ChatState.RECORDING,0],[D.ChatState.LLM,0],[D.ChatState.ANALYZING,0],[D.ChatState.SPEAKING,0]]),this.dispatchChatState(this.exchangeChatStateMapToSet(this.chatStateMap))}exchangeChatStateMapToSet(D){const u=new Set;for(const t of D)t[1]>0&&u.add(t[0]);return u}dispatchChatState(D){this.chatStatesHandler.dispatchEvent(new CustomEvent("status",{detail:{status:D}}))}isEqualChatStateMap(D,u){if(D.size!==u.size)return!1;for(const t of D)if(D.has(t)!==u.has(t))return!1;return!0}setError(D){this.errorHandler.dispatchEvent(new CustomEvent("error",{detail:{error:D}}))}close(){this.perso.closeSelf()}removeEmoji(D){return D.replace(this.emojiRegex,"")}}async function i(D,u){return await s.getLLMs(D,u)}async function r(D,u){return await s.getTTSs(D,u)}async function E(D,u){return await s.getSTTs(D,u)}async function o(D,u){return await s.getModelStyles(D,u)}async function c(D,u){return await s.getBackgroundImages(D,u)}async function l(D,u){return await s.getPrompts(D,u)}async function h(D,u){return await s.getDocuments(D,u)}async function B(D,u){return await s.getMcpServers(D,u)}return D.ApiError=t,D.ChatTool=class{name;description;parameters;call;executeOnly;constructor(D,u,t,e,F=!1){this.name=D,this.description=u,this.parameters=t,this.call=e,this.executeOnly=F}},D.LLMError=e,D.LLMStreamingResponseError=F,D.Session=n,D.createSession=async function(D,u,t,e,F,s){return await(async(D,u,t,e,F,s)=>{let a=null,i=null;if(F)a=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),i=()=>{};else{const D=new AudioContext,u=D.createOscillator();u.frequency.value=0;const t=D.createMediaStreamDestination();u.connect(t),u.start(),a=t.stream,i=()=>{u.stop(),u.disconnect(t)}}const r=await C.create(D,u,a,t,e),E=new n(D,u,a,r,s);return E.onClose(D=>{i()}),E})(D,u,t,e,F,s)},D.createSessionId=async(D,u,t)=>{"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 e={capability:[],...t};t.using_stf_webrtc&&e.capability.push("STF_WEBRTC"),t?.llm_type&&(e.capability.push("LLM"),e.llm_type=t.llm_type),t?.tts_type&&(e.capability.push("TTS"),e.tts_type=t.tts_type),t?.stt_type&&(e.capability.push("STT"),e.stt_type=t.stt_type);const F=await fetch(`${D}/api/v1/session/`,{body:JSON.stringify(e),headers:{"PersoLive-APIKey":u,"Content-Type":"application/json"},method:"POST"});return(await s.parseJson(F)).session_id},D.getAllSettings=async function(D,u){const t=await i(D,u),e=await o(D,u),F=await c(D,u);return{llms:t,ttsTypes:await r(D,u),sttTypes:await E(D,u),modelStyles:e,backgroundImages:F,prompts:await l(D,u),documents:await h(D,u),mcpServers:await B(D,u)}},D.getBackgroundImages=c,D.getDocuments=h,D.getLLMs=i,D.getMcpServers=B,D.getModelStyles=o,D.getPrompts=l,D.getSTTs=E,D.getSessionInfo=async function(D,u){return await s.getSessionInfo(D,u)},D.getTTSs=r,D}({});
|
|
@@ -0,0 +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 i{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 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=fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await a}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 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(t,e,s,a,n){const o=await fetch(`${t}/api/v1/session/${e}/ice-servers/`,{method:"GET"}),c=(await i.parseJson(o)).ice_servers;let l=await r.createPeerConnection(c),h=l.createDataChannel("message",{protocol:"message"}),d=new r(l,h);s.getTracks().forEach(function(t){l.addTrack(t,s)});const u=l.addTransceiver("video",{direction:"recvonly"}),p=RTCRtpReceiver.getCapabilities("video");null!=p&&u.setCodecPreferences(p.codecs.filter(t=>"video/VP8"===t.mimeType));const m=await l.createOffer();await l.setLocalDescription(m);const g=await fetch(`${t}/api/v1/session/${e}/exchange/`,{body:JSON.stringify({client_sdp:m}),headers:{"Content-Type":"application/json"},method:"POST"}),S=await i.parseJson(g);return await l.setRemoteDescription(S.server_sdp),await r.waitFor(()=>d.isReady(),100,50),d.changeSize(a,n),d}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 i=setInterval(()=>{n+=1,n>=a&&(clearInterval(i),e("bad")),t()&&(clearInterval(i),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})}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)}}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"})}}var o;!function(t){t.RECORDING="RECORDING",t.LLM="LLM",t.ANALYZING="ANALYZING",t.SPEAKING="SPEAKING"}(o||(o={}));class c{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 l{apiServer;sessionId;stream;perso;clientTools;chatStatesHandler=new EventTarget;chatLogHandler=new EventTarget;sttEventHandler=null;errorHandler=new EventTarget;lastStfTimeoutHandle=null;stfTotalDuration=0;stfTimeoutStartTime=0;messageHistory=[];chatLog=[];chatStateMap=new Map([[o.RECORDING,0],[o.LLM,0],[o.ANALYZING,0],[o.SPEAKING,0]]);emojiRegex=t();constructor(t,e,s,a,n){this.apiServer=t,this.sessionId=e,this.stream=s,this.perso=a,this.clientTools=n,this.resetChatState(),a.setMessageCallback("stf",t=>{if(this.setChatState(o.SPEAKING,o.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,o.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,o.SPEAKING)},this.stfTotalDuration)}),a.setMessageCallback("stt",t=>{if(this.setChatState(null,o.ANALYZING),null!=this.sttEventHandler)this.sttEventHandler.dispatchEvent(new CustomEvent("stt",{detail:t.text}));else{if(""===t.text)return;this.processChat(t.text)}}),a.setMessageCallback("stt-error",t=>{this.setChatState(null,o.ANALYZING)})}llmJob=null;async processChat(t){0!==t.trim().length&&(this.addMessageToChatLog(t,!0),this.llmJob=this.processChatInternal(t))}processCustomChat(t){0!==t.trim().length&&this.processTTSTFInternal(t)}processTTSTF(t){0!==t.trim().length&&(this.messageHistory.push({role:"assistant",type:"message",content:t}),this.addMessageToChatLog(t,!1),this.processTTSTFInternal(t))}startVoiceChat(){return this.setChatState(o.RECORDING),this.perso.recordStart()}stopVoiceChat(){this.setChatState(o.ANALYZING,o.RECORDING),this.perso.recordEndStt()}changeSize(t,e){this.perso.changeSize(t,e)}async clearBuffer(){await this.clearLLMJob(),this.perso.clearBuffer(),null!==this.lastStfTimeoutHandle&&(clearTimeout(this.lastStfTimeoutHandle),this.lastStfTimeoutHandle=null),this.resetChatState()}setSrc(t){t.srcObject=this.getRemoteStream()}getLocalStream(){return this.stream}getRemoteStream(){return this.perso.getStream()}stopSession(){this.close()}onClose(t){return 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(o.LLM);const e=this.clientTools.map(t=>({type:"function",function:{description:t.description,name:t.name,parameters:t.parameters}})),i=new Array;null===t||(t instanceof Array?i.push(...t):"string"==typeof t&&i.push({role:"user",content:t}));const r=await fetch(`${this.apiServer}/api/v1/session/${this.sessionId}/llm/v2/`,{body:JSON.stringify({messages:[...this.messageHistory,...i],tools:e}),headers:{"Content-Type":"application/json"},method:"POST"});if(!r.ok){const t=await r.json(),e=new a(new s(r.status,t.errors[0].code,t.errors[0].detail,t.errors[0].attr));return this.setError(e),void this.setChatState(null,o.LLM)}const c=r.body?.getReader(),l=new TextDecoder("utf-8");let h="",d=null,u="";for(;;){const{done:t,value:e}=await c.read();if(t)break;let s;for(u+=l.decode(e,{stream:!0});-1!==(s=u.indexOf("\n"));){if(this.llmCancel)return h.length>0&&this.addMessageToChatLog(h,!1),void this.setChatState(null,o.LLM);const t=u.slice(0,s).trim();if(u=u.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,o.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,o.LLM)}h.length>0&&"message"!=e.type&&(i.push({role:"assistant",type:"message",content:h}),this.addMessageToChatLog(h,!1),h=""),"message"!==e.type?"tool_call"!==e.type||null==e.tool_calls?"tool"!==e.role||"tool_call"===e.type&&i.push({role:e.role,type:e.type,content:e.content,tool_call_id:e.tool_call_id}):(i.push({role:"assistant",type:e.type,content:e.content,tool_calls:e.tool_calls}),d=e):(h+=e.content,this.processTTSTFInternal(e.content))}}if(this.llmCancel)this.setChatState(null,o.LLM);else{if(null!=d){const t=[];for(const e of d.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)i.push({role:"tool",content:JSON.stringify(t.chatToolResult),tool_call_id:t.toolCallId});const s=e.length>0&&d.tool_calls.length!==e.length,a=e.some(t=>!t.chatTool.executeOnly);s||a?await this.processChatInternal(i):this.messageHistory.push(...i)}else this.messageHistory.push(...i);this.setChatState(null,o.LLM)}}getChatTool(t,e){for(const s of t)if(s.name===e)return s;return null}llmCancel=!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.setChatState(o.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===o.ANALYZING?s.set(t,(s.get(t)||0)+1):s.set(t,1)}function n(t){t===o.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 i=this.exchangeChatStateMapToSet(this.chatStateMap),r=this.exchangeChatStateMapToSet(s);this.chatStateMap=s,this.isEqualChatStateMap(i,r)||this.dispatchChatState(r)}resetChatState(){this.chatStateMap=new Map([[o.RECORDING,0],[o.LLM,0],[o.ANALYZING,0],[o.SPEAKING,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.perso.closeSelf()}removeEmoji(t){return t.replace(this.emojiRegex,"")}}async function h(t,e){return await i.getLLMs(t,e)}async function d(t,e){return await i.getTTSs(t,e)}async function u(t,e){return await i.getSTTs(t,e)}async function p(t,e){return await i.getModelStyles(t,e)}async function m(t,e){return await i.getBackgroundImages(t,e)}async function g(t,e){return await i.getPrompts(t,e)}async function S(t,e){return await i.getDocuments(t,e)}async function f(t,e){return await i.getMcpServers(t,e)}async function T(t,e){const s=await h(t,e),a=await p(t,e),n=await m(t,e);return{llms:s,ttsTypes:await d(t,e),sttTypes:await u(t,e),modelStyles:a,backgroundImages:n,prompts:await g(t,e),documents:await S(t,e),mcpServers:await f(t,e)}}async function y(t,e,s,a,n,i){return await(async(t,e,s,a,n,i)=>{let o=null,c=null;if(n)o=await navigator.mediaDevices.getUserMedia({audio:!0,video:!1}),c=()=>{};else{const t=new AudioContext,e=t.createOscillator();e.frequency.value=0;const s=t.createMediaStreamDestination();e.connect(s),e.start(),o=s.stream,c=()=>{e.stop(),e.disconnect(s)}}const h=await r.create(t,e,o,s,a),d=new l(t,e,o,h,i);return d.onClose(t=>{c()}),d})(t,e,s,a,n,i)}async function v(t,e){return await i.getSessionInfo(t,e)}const w=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("STF_WEBRTC"),s?.llm_type&&(a.capability.push("LLM"),a.llm_type=s.llm_type),s?.tts_type&&(a.capability.push("TTS"),a.tts_type=s.tts_type),s?.stt_type&&(a.capability.push("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 i.parseJson(n)).session_id};export{s as ApiError,o as ChatState,c as ChatTool,a as LLMError,n as LLMStreamingResponseError,l as Session,y as createSession,w as createSessionId,T as getAllSettings,m as getBackgroundImages,S as getDocuments,h as getLLMs,f as getMcpServers,p as getModelStyles,g as getPrompts,u as getSTTs,v as getSessionInfo,d as getTTSs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class t 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 e{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 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=fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await a}static async parseJson(e){const s=await e.json();if(e.ok)return s;{const a=s.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${e.status} with no error details`,attr:null};throw new t(e.status,a.code,a.detail,a.attr)}}}exports.ApiError=t,exports.PersoUtilServer=e,exports.createSessionId=async(t,s,a)=>{const r={capability:[],...a};a.using_stf_webrtc&&r.capability.push("STF_WEBRTC"),a?.llm_type&&(r.capability.push("LLM"),r.llm_type=a.llm_type),a?.tts_type&&(r.capability.push("TTS"),r.tts_type=a.tts_type),a?.stt_type&&(r.capability.push("STT"),r.stt_type=a.stt_type);const i=await fetch(`${t}/api/v1/session/`,{body:JSON.stringify(r),headers:{"PersoLive-APIKey":s,"Content-Type":"application/json"},method:"POST"});return(await e.parseJson(i)).session_id},exports.getIntroMessage=async(s,a,r)=>{try{const t=(await e.getPrompts(s,a)).find(t=>t.prompt_id===r);if(!t)throw new Error(`Prompt (${r}) not found`,{cause:404});return t.intro_message}catch(e){if(e instanceof t)throw new Error(e.detail,{cause:e.errorCode??500});throw e}};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
type CreateSessionIdBody = {
|
|
2
|
+
using_stf_webrtc: boolean;
|
|
3
|
+
model_style: string;
|
|
4
|
+
prompt: string;
|
|
5
|
+
document?: string;
|
|
6
|
+
background_image?: string;
|
|
7
|
+
mcp_servers?: Array<string>;
|
|
8
|
+
padding_left?: number;
|
|
9
|
+
padding_top?: number;
|
|
10
|
+
padding_height?: number;
|
|
11
|
+
llm_type?: string;
|
|
12
|
+
tts_type?: string;
|
|
13
|
+
stt_type?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Requests a new session creation ID by POSTing the desired runtime options to
|
|
17
|
+
* the Perso backend (`/api/v1/session/`).
|
|
18
|
+
* @param apiServer Perso API server URL.
|
|
19
|
+
* @param apiKey API key used for authentication.
|
|
20
|
+
* @param params {
|
|
21
|
+
* using_stf_webrtc: boolean;
|
|
22
|
+
* llm_type?: string;
|
|
23
|
+
* tts_type?: string;
|
|
24
|
+
* stt_type?: string;
|
|
25
|
+
* model_style: string;
|
|
26
|
+
* prompt: string;
|
|
27
|
+
* document?: string;
|
|
28
|
+
* background_image?: string;
|
|
29
|
+
* mcp_servers?: Array<string>;
|
|
30
|
+
* padding_left?: number;
|
|
31
|
+
* padding_top?: number;
|
|
32
|
+
* padding_height?: number;
|
|
33
|
+
* }
|
|
34
|
+
* @returns Session ID returned by the server.
|
|
35
|
+
*/
|
|
36
|
+
declare const createSessionId: (apiServer: string, apiKey: string, params: CreateSessionIdBody) => Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Retrieves the intro message for a specific prompt.
|
|
39
|
+
* @param apiServer Perso API server URL.
|
|
40
|
+
* @param apiKey API key used for authentication.
|
|
41
|
+
* @param promptId The prompt ID to fetch intro message for.
|
|
42
|
+
* @returns The intro message string.
|
|
43
|
+
*/
|
|
44
|
+
declare const getIntroMessage: (apiServer: string, apiKey: string, promptId: string) => Promise<string>;
|
|
45
|
+
|
|
46
|
+
declare class ApiError extends Error {
|
|
47
|
+
errorCode: number;
|
|
48
|
+
code: string;
|
|
49
|
+
detail: string;
|
|
50
|
+
attr?: string | undefined;
|
|
51
|
+
constructor(errorCode: number, code: string, detail: string, attr?: string | undefined);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare class PersoUtil {
|
|
55
|
+
/**
|
|
56
|
+
* @param apiServer Perso Interactive API Server
|
|
57
|
+
* @param apiKey Perso Interactive API Key
|
|
58
|
+
* @returns JSON
|
|
59
|
+
* [
|
|
60
|
+
* {
|
|
61
|
+
* "name": string
|
|
62
|
+
* }
|
|
63
|
+
* ]
|
|
64
|
+
*/
|
|
65
|
+
static getLLMs(apiServer: string, apiKey: string): Promise<any>;
|
|
66
|
+
/**
|
|
67
|
+
* @param apiServer Perso Interactive API Server
|
|
68
|
+
* @param apiKey Perso Interactive API Key
|
|
69
|
+
* @returns JSON
|
|
70
|
+
* [
|
|
71
|
+
* {
|
|
72
|
+
* "name": string,
|
|
73
|
+
* "model": string,
|
|
74
|
+
* "style": string
|
|
75
|
+
* }
|
|
76
|
+
* ]
|
|
77
|
+
*/
|
|
78
|
+
static getModelStyles(apiServer: string, apiKey: string): Promise<any>;
|
|
79
|
+
/**
|
|
80
|
+
* @param apiServer Perso Interactive API Server
|
|
81
|
+
* @param apiKey Perso Interactive API Key
|
|
82
|
+
* @returns JSON
|
|
83
|
+
* [
|
|
84
|
+
* {
|
|
85
|
+
* "backgroundimage_id": string,
|
|
86
|
+
* "title": string,
|
|
87
|
+
* "image": string
|
|
88
|
+
* "created_at": string // ex) "2024-05-02T09:05:55.395Z"
|
|
89
|
+
* }
|
|
90
|
+
* ]
|
|
91
|
+
*/
|
|
92
|
+
static getBackgroundImages(apiServer: string, apiKey: string): Promise<any>;
|
|
93
|
+
/**
|
|
94
|
+
* @param apiServer Perso Interactive API Server
|
|
95
|
+
* @param apiKey Perso Interactive API Key
|
|
96
|
+
* @returns JSON
|
|
97
|
+
* [
|
|
98
|
+
* {
|
|
99
|
+
* "name": string,
|
|
100
|
+
* "service": string,
|
|
101
|
+
* "speaker": string
|
|
102
|
+
* }
|
|
103
|
+
* ]
|
|
104
|
+
*/
|
|
105
|
+
static getTTSs(apiServer: string, apiKey: string): Promise<any>;
|
|
106
|
+
/**
|
|
107
|
+
* @param apiServer Perso Interactive API Server
|
|
108
|
+
* @param apiKey Perso Interactive API Key
|
|
109
|
+
* @returns JSON
|
|
110
|
+
* [
|
|
111
|
+
* {
|
|
112
|
+
* "name": string,
|
|
113
|
+
* "service": string
|
|
114
|
+
* }
|
|
115
|
+
* ]
|
|
116
|
+
*/
|
|
117
|
+
static getSTTs(apiServer: string, apiKey: string): Promise<any>;
|
|
118
|
+
/**
|
|
119
|
+
* @param apiServer Perso Interactive API Server
|
|
120
|
+
* @param apiKey Perso Interactive API Key
|
|
121
|
+
* @returns JSON
|
|
122
|
+
* [
|
|
123
|
+
* {
|
|
124
|
+
* "name": string,
|
|
125
|
+
* "description": string,
|
|
126
|
+
* "prompt_id": string,
|
|
127
|
+
* "system_prompt": string,
|
|
128
|
+
* "require_document": boolean,
|
|
129
|
+
* "intro_message": string
|
|
130
|
+
* }
|
|
131
|
+
* ]
|
|
132
|
+
*/
|
|
133
|
+
static getPrompts(apiServer: string, apiKey: string): Promise<any>;
|
|
134
|
+
/**
|
|
135
|
+
* @param apiServer Perso Interactive API Server
|
|
136
|
+
* @param apiKey Perso Interactive API Key
|
|
137
|
+
* @returns JSON
|
|
138
|
+
* [
|
|
139
|
+
* {
|
|
140
|
+
* "document_id": string,
|
|
141
|
+
* "title": string,
|
|
142
|
+
* "description": string,
|
|
143
|
+
* "search_count": number,
|
|
144
|
+
* "processed": boolean,
|
|
145
|
+
* "created_at": string, // ex) "2024-05-02T09:05:55.395Z",
|
|
146
|
+
* "updated_at": string // ex) "2024-05-02T09:05:55.395Z"
|
|
147
|
+
* }
|
|
148
|
+
* ]
|
|
149
|
+
*/
|
|
150
|
+
static getDocuments(apiServer: string, apiKey: string): Promise<any>;
|
|
151
|
+
/**
|
|
152
|
+
* @param apiServer Perso Interactive API Server
|
|
153
|
+
* @param apiKey Perso Interactive API Key
|
|
154
|
+
* @returns JSON
|
|
155
|
+
* [
|
|
156
|
+
* {
|
|
157
|
+
* "mcpserver_id": string,
|
|
158
|
+
* "name": string,
|
|
159
|
+
* "url": string,
|
|
160
|
+
* "description": string
|
|
161
|
+
* }
|
|
162
|
+
* ]
|
|
163
|
+
*/
|
|
164
|
+
static getMcpServers(apiServer: string, apiKey: string): Promise<any>;
|
|
165
|
+
/**
|
|
166
|
+
* @param apiServer Perso Interactive API Server
|
|
167
|
+
* @param apiKey Perso Interactive API Key
|
|
168
|
+
* @returns JSON
|
|
169
|
+
* {
|
|
170
|
+
* "session_id": string,
|
|
171
|
+
* "client_sdp": string,
|
|
172
|
+
* "server_sdp": string,
|
|
173
|
+
* "prompt": {
|
|
174
|
+
* "name": string,
|
|
175
|
+
* "description": string,
|
|
176
|
+
* "prompt_id": string,
|
|
177
|
+
* "system_prompt": string,
|
|
178
|
+
* "require_document": boolean,
|
|
179
|
+
* "intro_message": string
|
|
180
|
+
* },
|
|
181
|
+
* "document": string,
|
|
182
|
+
* "llm_type": {
|
|
183
|
+
* "name": string
|
|
184
|
+
* },
|
|
185
|
+
* "model_style": {
|
|
186
|
+
* "name": string,
|
|
187
|
+
* "model": string,
|
|
188
|
+
* "model_file": string,
|
|
189
|
+
* "style": string,
|
|
190
|
+
* "file": string
|
|
191
|
+
* },
|
|
192
|
+
* "tts_type": {
|
|
193
|
+
* "name": string,
|
|
194
|
+
* "service": string,
|
|
195
|
+
* "model": string,
|
|
196
|
+
* "voice": string,
|
|
197
|
+
* "style": string,
|
|
198
|
+
* "voice_extra_data": string
|
|
199
|
+
* },
|
|
200
|
+
* "ice_servers": Array<RTCIceServer>,
|
|
201
|
+
* "status": string, // "CREATED", "EXCHANGED", "IN_PROGRESS", "TERMINATED"
|
|
202
|
+
* "termination_reason": string, // "GRACEFUL_TERMINATION", "SESSION_EXPIRED_BEFORE_CONNECTION", "SESSION_LOST_AFTER_CONNECTION", "SESSION_MISC_ERROR", "MAX_ACTIVE_SESSION_QUOTA_EXCEEDED", "MAX_MIN_PER_SESSION_QUOTA_EXCEEDED", "TOTAL_MIN_PER_MONTH_QUOTA_EXCEEDED"
|
|
203
|
+
* "duration_sec": number,
|
|
204
|
+
* "created_at": string, // ex) "2024-05-02T09:05:55.395Z"
|
|
205
|
+
* "padding_left": number,
|
|
206
|
+
* "padding_top": number,
|
|
207
|
+
* "padding_height": number,
|
|
208
|
+
* "background_image": {
|
|
209
|
+
* "backgroundimage_id": string,
|
|
210
|
+
* "title": string,
|
|
211
|
+
* "image": string,
|
|
212
|
+
* "created_at": string // ex) "2024-05-02T09:05:55.395Z"
|
|
213
|
+
* },
|
|
214
|
+
* "extra_data": string
|
|
215
|
+
* }
|
|
216
|
+
*/
|
|
217
|
+
static getSessionInfo(apiServer: string, sessionId: string): Promise<any>;
|
|
218
|
+
static sessionEvent(apiServer: string, sessionId: string, sessionEvent: string): Promise<void>;
|
|
219
|
+
static parseJson(response: Response): Promise<any>;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export { ApiError, PersoUtil as PersoUtilServer, createSessionId, getIntroMessage };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class t extends Error{errorCode;code;detail;attr;constructor(t,e,s,a){let i;i=null!=a?`${t}:${a}_${s}`:`${t}:${s}`,super(i),this.errorCode=t,this.code=e,this.detail=s,this.attr=a}}class e{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 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=fetch(`${t}/api/v1/session/${e}/event/create/`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({detail:"",event:s})});await a}static async parseJson(e){const s=await e.json();if(e.ok)return s;{const a=s.errors?.[0]??{code:"UNKNOWN_ERROR",detail:`Server returned status ${e.status} with no error details`,attr:null};throw new t(e.status,a.code,a.detail,a.attr)}}}const s=async(t,s,a)=>{const i={capability:[],...a};a.using_stf_webrtc&&i.capability.push("STF_WEBRTC"),a?.llm_type&&(i.capability.push("LLM"),i.llm_type=a.llm_type),a?.tts_type&&(i.capability.push("TTS"),i.tts_type=a.tts_type),a?.stt_type&&(i.capability.push("STT"),i.stt_type=a.stt_type);const r=await fetch(`${t}/api/v1/session/`,{body:JSON.stringify(i),headers:{"PersoLive-APIKey":s,"Content-Type":"application/json"},method:"POST"});return(await e.parseJson(r)).session_id},a=async(s,a,i)=>{try{const t=(await e.getPrompts(s,a)).find(t=>t.prompt_id===i);if(!t)throw new Error(`Prompt (${i}) not found`,{cause:404});return t.intro_message}catch(e){if(e instanceof t)throw new Error(e.detail,{cause:e.errorCode??500});throw e}};export{t as ApiError,e as PersoUtilServer,s as createSessionId,a as getIntroMessage};
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "perso-interactive-sdk-web",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Perso Interactive Web SDK - WebRTC-based real-time interactive AI avatar sessions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"keywords": [
|
|
8
|
+
"perso",
|
|
9
|
+
"interactive",
|
|
10
|
+
"ai",
|
|
11
|
+
"avatar",
|
|
12
|
+
"webrtc",
|
|
13
|
+
"llm",
|
|
14
|
+
"tts",
|
|
15
|
+
"stt",
|
|
16
|
+
"stf",
|
|
17
|
+
"real-time"
|
|
18
|
+
],
|
|
19
|
+
"author": "Perso AI",
|
|
20
|
+
"license": "Apache-2.0",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/perso-ai/perso-interactive-sdk-web.git"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/perso-ai/perso-interactive-sdk-web#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/perso-ai/perso-interactive-sdk-web/issues"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
"./client": {
|
|
31
|
+
"types": "./dist/client/index.d.ts",
|
|
32
|
+
"import": "./dist/client/index.js",
|
|
33
|
+
"require": "./dist/client/index.cjs",
|
|
34
|
+
"browser": "./dist/client/index.iife.js"
|
|
35
|
+
},
|
|
36
|
+
"./server": {
|
|
37
|
+
"types": "./dist/server/index.d.ts",
|
|
38
|
+
"import": "./dist/server/index.js",
|
|
39
|
+
"require": "./dist/server/index.cjs"
|
|
40
|
+
},
|
|
41
|
+
".": {
|
|
42
|
+
"types": "./dist/client/index.d.ts",
|
|
43
|
+
"import": "./dist/client/index.js",
|
|
44
|
+
"require": "./dist/client/index.cjs"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"typesVersions": {
|
|
48
|
+
"*": {
|
|
49
|
+
"client": [
|
|
50
|
+
"./dist/client/index.d.ts"
|
|
51
|
+
],
|
|
52
|
+
"server": [
|
|
53
|
+
"./dist/server/index.d.ts"
|
|
54
|
+
],
|
|
55
|
+
"*": [
|
|
56
|
+
"./dist/client/index.d.ts"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"files": [
|
|
61
|
+
"dist",
|
|
62
|
+
"README.md"
|
|
63
|
+
],
|
|
64
|
+
"sideEffects": false,
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "rollup -c",
|
|
67
|
+
"dev": "rollup -c -w",
|
|
68
|
+
"start": "sirv public --no-clear",
|
|
69
|
+
"tsc": "tsc --noEmit --project \"./tsconfig.json\"",
|
|
70
|
+
"test": "jest",
|
|
71
|
+
"test:watch": "jest --watch",
|
|
72
|
+
"prepublishOnly": "npm run build"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@perso-interactive-sdk-web/eslint-config": "workspace:*",
|
|
76
|
+
"@perso-interactive-sdk-web/prettier-config": "workspace:*",
|
|
77
|
+
"@rollup/plugin-commonjs": "^24.0.0",
|
|
78
|
+
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
79
|
+
"@rollup/plugin-terser": "^0.4.0",
|
|
80
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
81
|
+
"@types/jest": "^29.5.14",
|
|
82
|
+
"@types/node": "^24.10.1",
|
|
83
|
+
"jest": "^29.7.0",
|
|
84
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
85
|
+
"rollup": "^3.15.0",
|
|
86
|
+
"rollup-plugin-dts": "^6.0.0",
|
|
87
|
+
"rollup-plugin-livereload": "^2.0.0",
|
|
88
|
+
"ts-jest": "^29.2.5",
|
|
89
|
+
"typescript": "^5.9.3"
|
|
90
|
+
},
|
|
91
|
+
"dependencies": {
|
|
92
|
+
"emoji-regex": "^10.6.0",
|
|
93
|
+
"tslib": "^2.5.0"
|
|
94
|
+
}
|
|
95
|
+
}
|