ttp-agent-sdk 2.0.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/GETTING_STARTED.md +429 -0
- package/README.md +303 -0
- package/dist/agent-widget.js +3 -0
- package/dist/agent-widget.js.LICENSE.txt +21 -0
- package/dist/agent-widget.js.map +1 -0
- package/dist/examples/react-example.html +455 -0
- package/dist/examples/react-example.jsx +307 -0
- package/dist/examples/test.html +235 -0
- package/dist/examples/vanilla-example.html +464 -0
- package/dist/index.html +224 -0
- package/examples/react-example.html +455 -0
- package/examples/react-example.jsx +307 -0
- package/examples/test.html +235 -0
- package/examples/vanilla-example.html +464 -0
- package/package.json +63 -0
- package/src/core/AudioPlayer.js +185 -0
- package/src/core/AudioRecorder.js +128 -0
- package/src/core/ConnectionManager.js +86 -0
- package/src/core/EventEmitter.js +53 -0
- package/src/core/VoiceSDK.js +390 -0
- package/src/core/WebSocketManager.js +218 -0
- package/src/core/WebSocketManagerV2.js +211 -0
- package/src/core/WebSocketSingleton.js +171 -0
- package/src/index.js +64 -0
- package/src/legacy/AgentSDK.js +462 -0
- package/src/react/VoiceButton.jsx +163 -0
- package/src/vanilla/VoiceButton.js +190 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>React VoiceSDK Example</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
10
|
+
max-width: 1000px;
|
|
11
|
+
margin: 0 auto;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
background: #F9FAFB;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.container {
|
|
17
|
+
background: white;
|
|
18
|
+
padding: 30px;
|
|
19
|
+
border-radius: 12px;
|
|
20
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
21
|
+
margin-bottom: 20px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
h1 {
|
|
25
|
+
color: #111827;
|
|
26
|
+
margin-top: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.info {
|
|
30
|
+
background: #EFF6FF;
|
|
31
|
+
border-left: 4px solid #3B82F6;
|
|
32
|
+
padding: 16px;
|
|
33
|
+
margin: 20px 0;
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.code-block {
|
|
38
|
+
background: #1F2937;
|
|
39
|
+
color: #F9FAFB;
|
|
40
|
+
padding: 20px;
|
|
41
|
+
border-radius: 8px;
|
|
42
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
43
|
+
font-size: 14px;
|
|
44
|
+
overflow-x: auto;
|
|
45
|
+
margin: 20px 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.note {
|
|
49
|
+
background: #FEF3C7;
|
|
50
|
+
border-left: 4px solid #F59E0B;
|
|
51
|
+
padding: 16px;
|
|
52
|
+
margin: 20px 0;
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.back-link {
|
|
57
|
+
display: inline-block;
|
|
58
|
+
background: #4F46E5;
|
|
59
|
+
color: white;
|
|
60
|
+
padding: 12px 24px;
|
|
61
|
+
text-decoration: none;
|
|
62
|
+
border-radius: 8px;
|
|
63
|
+
font-weight: 600;
|
|
64
|
+
margin-bottom: 20px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.back-link:hover {
|
|
68
|
+
background: #4338CA;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<a href="../index.html" class="back-link">â Back to SDK Home</a>
|
|
74
|
+
|
|
75
|
+
<div class="container">
|
|
76
|
+
<h1>âī¸ React VoiceSDK Example</h1>
|
|
77
|
+
|
|
78
|
+
<div class="info">
|
|
79
|
+
<strong>Note:</strong> This is a code example showing how to integrate the VoiceSDK with React.
|
|
80
|
+
To run this example, you'll need a React development environment.
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<h2>Installation</h2>
|
|
84
|
+
<div class="code-block">
|
|
85
|
+
npm install ttp-agent-sdk react react-dom
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<h2>Basic React Component</h2>
|
|
89
|
+
<div class="code-block">
|
|
90
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
91
|
+
import { VoiceSDK } from 'ttp-agent-sdk';
|
|
92
|
+
|
|
93
|
+
function VoiceChat() {
|
|
94
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
95
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
96
|
+
const [messages, setMessages] = useState([]);
|
|
97
|
+
const voiceSDKRef = useRef(null);
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
const voiceSDK = new VoiceSDK({
|
|
101
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv',
|
|
102
|
+
agentId: 'your_agent_id',
|
|
103
|
+
appId: 'your_app_id'
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
voiceSDK.on('connected', () => setIsConnected(true));
|
|
107
|
+
voiceSDK.on('disconnected', () => setIsConnected(false));
|
|
108
|
+
voiceSDK.on('recordingStarted', () => setIsRecording(true));
|
|
109
|
+
voiceSDK.on('recordingStopped', () => setIsRecording(false));
|
|
110
|
+
voiceSDK.on('message', (message) => {
|
|
111
|
+
if (message.type === 'agent_response') {
|
|
112
|
+
setMessages(prev => [...prev, { type: 'agent', text: message.agent_response }]);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
voiceSDKRef.current = voiceSDK;
|
|
117
|
+
|
|
118
|
+
return () => voiceSDK.destroy();
|
|
119
|
+
}, []);
|
|
120
|
+
|
|
121
|
+
const handleConnect = async () => {
|
|
122
|
+
await voiceSDKRef.current.connect();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const handleToggleRecording = async () => {
|
|
126
|
+
if (isRecording) {
|
|
127
|
+
await voiceSDKRef.current.stopRecording();
|
|
128
|
+
} else {
|
|
129
|
+
await voiceSDKRef.current.startRecording();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div>
|
|
135
|
+
<button onClick={handleConnect} disabled={isConnected}>
|
|
136
|
+
Connect
|
|
137
|
+
</button>
|
|
138
|
+
<button onClick={handleToggleRecording} disabled={!isConnected}>
|
|
139
|
+
{isRecording ? 'Stop Recording' : 'Start Recording'}
|
|
140
|
+
</button>
|
|
141
|
+
<div>
|
|
142
|
+
{messages.map((msg, i) => (
|
|
143
|
+
<div key={i}>{msg.type}: {msg.text}</div>
|
|
144
|
+
))}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export default VoiceChat;
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<h2>Using the VoiceButton Component</h2>
|
|
154
|
+
<div class="code-block">
|
|
155
|
+
import React from 'react';
|
|
156
|
+
import { VoiceButton } from 'ttp-agent-sdk';
|
|
157
|
+
|
|
158
|
+
function App() {
|
|
159
|
+
return (
|
|
160
|
+
<VoiceButton
|
|
161
|
+
websocketUrl="wss://speech.bidme.co.il/ws/conv"
|
|
162
|
+
agentId="your_agent_id"
|
|
163
|
+
appId="your_app_id"
|
|
164
|
+
onConnected={() => console.log('Connected!')}
|
|
165
|
+
onRecordingStarted={() => console.log('Recording...')}
|
|
166
|
+
onPlaybackStarted={() => console.log('Playing audio...')}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default App;
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<h2>Advanced React Integration</h2>
|
|
175
|
+
<div class="code-block">
|
|
176
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
177
|
+
import { VoiceSDK } from 'ttp-agent-sdk';
|
|
178
|
+
|
|
179
|
+
function AdvancedVoiceChat() {
|
|
180
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
181
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
182
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
183
|
+
const [messages, setMessages] = useState([]);
|
|
184
|
+
const [connectionStatus, setConnectionStatus] = useState('Disconnected');
|
|
185
|
+
|
|
186
|
+
const voiceSDKRef = useRef(null);
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const voiceSDK = new VoiceSDK({
|
|
190
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv',
|
|
191
|
+
agentId: 'your_agent_id',
|
|
192
|
+
appId: 'your_app_id',
|
|
193
|
+
voice: 'default',
|
|
194
|
+
language: 'en',
|
|
195
|
+
autoReconnect: true
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Set up comprehensive event handlers
|
|
199
|
+
voiceSDK.on('connected', () => {
|
|
200
|
+
setIsConnected(true);
|
|
201
|
+
setConnectionStatus('Connected');
|
|
202
|
+
addMessage('system', 'Connected to voice agent');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
voiceSDK.on('disconnected', () => {
|
|
206
|
+
setIsConnected(false);
|
|
207
|
+
setIsRecording(false);
|
|
208
|
+
setIsPlaying(false);
|
|
209
|
+
setConnectionStatus('Disconnected');
|
|
210
|
+
addMessage('system', 'Disconnected from voice agent');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
voiceSDK.on('recordingStarted', () => {
|
|
214
|
+
setIsRecording(true);
|
|
215
|
+
addMessage('user', 'đ¤ Recording...');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
voiceSDK.on('recordingStopped', () => {
|
|
219
|
+
setIsRecording(false);
|
|
220
|
+
addMessage('user', 'âšī¸ Recording stopped');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
voiceSDK.on('playbackStarted', () => {
|
|
224
|
+
setIsPlaying(true);
|
|
225
|
+
addMessage('agent', 'đ Agent is speaking...');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
voiceSDK.on('playbackStopped', () => {
|
|
229
|
+
setIsPlaying(false);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
voiceSDK.on('message', (message) => {
|
|
233
|
+
if (message.type === 'agent_response') {
|
|
234
|
+
addMessage('agent', message.agent_response);
|
|
235
|
+
} else if (message.type === 'user_transcript') {
|
|
236
|
+
addMessage('user', message.user_transcription);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
voiceSDK.on('error', (error) => {
|
|
241
|
+
console.error('VoiceSDK Error:', error);
|
|
242
|
+
addMessage('error', `Error: ${error.message}`);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
voiceSDKRef.current = voiceSDK;
|
|
246
|
+
|
|
247
|
+
return () => {
|
|
248
|
+
if (voiceSDKRef.current) {
|
|
249
|
+
voiceSDKRef.current.destroy();
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}, []);
|
|
253
|
+
|
|
254
|
+
const addMessage = (type, text) => {
|
|
255
|
+
setMessages(prev => [...prev, { type, text, timestamp: new Date() }]);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const handleConnect = async () => {
|
|
259
|
+
if (voiceSDKRef.current) {
|
|
260
|
+
try {
|
|
261
|
+
setConnectionStatus('Connecting...');
|
|
262
|
+
await voiceSDKRef.current.connect();
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error('Connection failed:', error);
|
|
265
|
+
setConnectionStatus('Connection failed');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const handleDisconnect = () => {
|
|
271
|
+
if (voiceSDKRef.current) {
|
|
272
|
+
voiceSDKRef.current.disconnect();
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const handleToggleRecording = async () => {
|
|
277
|
+
if (voiceSDKRef.current) {
|
|
278
|
+
try {
|
|
279
|
+
if (isRecording) {
|
|
280
|
+
await voiceSDKRef.current.stopRecording();
|
|
281
|
+
} else {
|
|
282
|
+
await voiceSDKRef.current.startRecording();
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error('Recording toggle failed:', error);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
|
|
292
|
+
<h1>đ¤ Voice Chat App</h1>
|
|
293
|
+
|
|
294
|
+
<div style={{
|
|
295
|
+
padding: '12px',
|
|
296
|
+
borderRadius: '8px',
|
|
297
|
+
marginBottom: '20px',
|
|
298
|
+
backgroundColor: isConnected ? '#D1FAE5' : '#FEE2E2',
|
|
299
|
+
color: isConnected ? '#065F46' : '#991B1B',
|
|
300
|
+
border: `1px solid ${isConnected ? '#10B981' : '#EF4444'}`
|
|
301
|
+
}}>
|
|
302
|
+
<strong>Status:</strong> {connectionStatus}
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div style={{ display: 'flex', gap: '12px', marginBottom: '20px' }}>
|
|
306
|
+
<button
|
|
307
|
+
onClick={handleConnect}
|
|
308
|
+
disabled={isConnected}
|
|
309
|
+
style={{
|
|
310
|
+
padding: '12px 24px',
|
|
311
|
+
backgroundColor: isConnected ? '#9CA3AF' : '#4F46E5',
|
|
312
|
+
color: 'white',
|
|
313
|
+
border: 'none',
|
|
314
|
+
borderRadius: '6px',
|
|
315
|
+
cursor: isConnected ? 'not-allowed' : 'pointer'
|
|
316
|
+
}}
|
|
317
|
+
>
|
|
318
|
+
Connect
|
|
319
|
+
</button>
|
|
320
|
+
|
|
321
|
+
<button
|
|
322
|
+
onClick={handleDisconnect}
|
|
323
|
+
disabled={!isConnected}
|
|
324
|
+
style={{
|
|
325
|
+
padding: '12px 24px',
|
|
326
|
+
backgroundColor: !isConnected ? '#9CA3AF' : '#EF4444',
|
|
327
|
+
color: 'white',
|
|
328
|
+
border: 'none',
|
|
329
|
+
borderRadius: '6px',
|
|
330
|
+
cursor: !isConnected ? 'not-allowed' : 'pointer'
|
|
331
|
+
}}
|
|
332
|
+
>
|
|
333
|
+
Disconnect
|
|
334
|
+
</button>
|
|
335
|
+
|
|
336
|
+
<button
|
|
337
|
+
onClick={handleToggleRecording}
|
|
338
|
+
disabled={!isConnected}
|
|
339
|
+
style={{
|
|
340
|
+
padding: '12px 24px',
|
|
341
|
+
backgroundColor: !isConnected ? '#9CA3AF' : (isRecording ? '#EF4444' : '#10B981'),
|
|
342
|
+
color: 'white',
|
|
343
|
+
border: 'none',
|
|
344
|
+
borderRadius: '6px',
|
|
345
|
+
cursor: !isConnected ? 'not-allowed' : 'pointer'
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
{isRecording ? 'Stop Recording' : 'Start Recording'}
|
|
349
|
+
</button>
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<div style={{
|
|
353
|
+
border: '1px solid #E5E7EB',
|
|
354
|
+
borderRadius: '8px',
|
|
355
|
+
height: '400px',
|
|
356
|
+
overflowY: 'auto',
|
|
357
|
+
padding: '16px',
|
|
358
|
+
backgroundColor: '#F9FAFB'
|
|
359
|
+
}}>
|
|
360
|
+
<h3>Messages:</h3>
|
|
361
|
+
{messages.length === 0 ? (
|
|
362
|
+
<p style={{ color: '#6B7280', fontStyle: 'italic' }}>
|
|
363
|
+
No messages yet. Connect and start recording to begin the conversation.
|
|
364
|
+
</p>
|
|
365
|
+
) : (
|
|
366
|
+
messages.map((message, index) => (
|
|
367
|
+
<div
|
|
368
|
+
key={index}
|
|
369
|
+
style={{
|
|
370
|
+
marginBottom: '12px',
|
|
371
|
+
padding: '8px 12px',
|
|
372
|
+
borderRadius: '6px',
|
|
373
|
+
backgroundColor: message.type === 'user' ? '#E5E7EB' :
|
|
374
|
+
message.type === 'agent' ? '#F3F4F6' :
|
|
375
|
+
message.type === 'error' ? '#FEE2E2' : '#EFF6FF',
|
|
376
|
+
color: message.type === 'error' ? '#991B1B' : '#111827',
|
|
377
|
+
alignSelf: message.type === 'user' ? 'flex-end' : 'flex-start',
|
|
378
|
+
maxWidth: '80%',
|
|
379
|
+
marginLeft: message.type === 'user' ? 'auto' : '0',
|
|
380
|
+
marginRight: message.type === 'user' ? '0' : 'auto'
|
|
381
|
+
}}
|
|
382
|
+
>
|
|
383
|
+
<div style={{ fontWeight: 'bold', marginBottom: '4px' }}>
|
|
384
|
+
{message.type === 'user' ? 'đ¤ You' :
|
|
385
|
+
message.type === 'agent' ? 'đ¤ Agent' :
|
|
386
|
+
message.type === 'error' ? 'â Error' : 'âšī¸ System'}
|
|
387
|
+
</div>
|
|
388
|
+
<div>{message.text}</div>
|
|
389
|
+
<div style={{
|
|
390
|
+
fontSize: '12px',
|
|
391
|
+
color: '#6B7280',
|
|
392
|
+
marginTop: '4px'
|
|
393
|
+
}}>
|
|
394
|
+
{message.timestamp.toLocaleTimeString()}
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
))
|
|
398
|
+
)}
|
|
399
|
+
</div>
|
|
400
|
+
|
|
401
|
+
<div style={{
|
|
402
|
+
display: 'flex',
|
|
403
|
+
gap: '20px',
|
|
404
|
+
marginTop: '20px',
|
|
405
|
+
fontSize: '14px',
|
|
406
|
+
color: '#6B7280'
|
|
407
|
+
}}>
|
|
408
|
+
<div>
|
|
409
|
+
<span style={{
|
|
410
|
+
display: 'inline-block',
|
|
411
|
+
width: '8px',
|
|
412
|
+
height: '8px',
|
|
413
|
+
borderRadius: '50%',
|
|
414
|
+
backgroundColor: isConnected ? '#10B981' : '#EF4444',
|
|
415
|
+
marginRight: '8px'
|
|
416
|
+
}}></span>
|
|
417
|
+
Connection
|
|
418
|
+
</div>
|
|
419
|
+
<div>
|
|
420
|
+
<span style={{
|
|
421
|
+
display: 'inline-block',
|
|
422
|
+
width: '8px',
|
|
423
|
+
height: '8px',
|
|
424
|
+
borderRadius: '50%',
|
|
425
|
+
backgroundColor: isRecording ? '#EF4444' : '#9CA3AF',
|
|
426
|
+
marginRight: '8px'
|
|
427
|
+
}}></span>
|
|
428
|
+
Recording
|
|
429
|
+
</div>
|
|
430
|
+
<div>
|
|
431
|
+
<span style={{
|
|
432
|
+
display: 'inline-block',
|
|
433
|
+
width: '8px',
|
|
434
|
+
height: '8px',
|
|
435
|
+
borderRadius: '50%',
|
|
436
|
+
backgroundColor: isPlaying ? '#10B981' : '#9CA3AF',
|
|
437
|
+
marginRight: '8px'
|
|
438
|
+
}}></span>
|
|
439
|
+
Playing
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export default AdvancedVoiceChat;
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
<div class="note">
|
|
450
|
+
<strong>Note:</strong> This is a comprehensive React example showing how to integrate the VoiceSDK.
|
|
451
|
+
Copy this code into your React application and customize the agentId and appId values.
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
</body>
|
|
455
|
+
</html>
|