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,429 @@
|
|
|
1
|
+
# Getting Started with TTP Agent SDK
|
|
2
|
+
|
|
3
|
+
This guide will help you quickly integrate voice AI agents into your web application using the TTP Agent SDK.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### 1. Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install ttp-agent-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### 2. Basic Usage
|
|
14
|
+
|
|
15
|
+
#### Option A: Pre-built Widget (Easiest)
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<!DOCTYPE html>
|
|
19
|
+
<html>
|
|
20
|
+
<head>
|
|
21
|
+
<title>Voice Agent Demo</title>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<!-- Your app content -->
|
|
25
|
+
|
|
26
|
+
<!-- Load the SDK -->
|
|
27
|
+
<script src="https://unpkg.com/ttp-agent-sdk/dist/agent-widget.js"></script>
|
|
28
|
+
|
|
29
|
+
<script>
|
|
30
|
+
// Initialize the voice widget
|
|
31
|
+
TTPAgentSDK.AgentWidget.init({
|
|
32
|
+
agentId: 'your_agent_id',
|
|
33
|
+
getSessionUrl: async ({ agentId, variables }) => {
|
|
34
|
+
const response = await fetch('/api/get-session', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: { 'Content-Type': 'application/json' },
|
|
37
|
+
body: JSON.stringify({ agentId, variables })
|
|
38
|
+
});
|
|
39
|
+
const data = await response.json();
|
|
40
|
+
return data.signedUrl;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
</body>
|
|
45
|
+
</html>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
#### Option B: React Component
|
|
49
|
+
|
|
50
|
+
```jsx
|
|
51
|
+
import React from 'react';
|
|
52
|
+
import { VoiceButton } from 'ttp-agent-sdk';
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
return (
|
|
56
|
+
<VoiceButton
|
|
57
|
+
websocketUrl="wss://speech.bidme.co.il/ws/conv"
|
|
58
|
+
agentId="your_agent_id"
|
|
59
|
+
appId="your_app_id"
|
|
60
|
+
onConnected={() => console.log('Connected!')}
|
|
61
|
+
onRecordingStarted={() => console.log('Recording...')}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Option C: Vanilla JavaScript
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { VoiceSDK } from 'ttp-agent-sdk';
|
|
71
|
+
|
|
72
|
+
const voiceSDK = new VoiceSDK({
|
|
73
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv',
|
|
74
|
+
agentId: 'your_agent_id',
|
|
75
|
+
appId: 'your_app_id'
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Connect and start recording
|
|
79
|
+
await voiceSDK.connect();
|
|
80
|
+
await voiceSDK.startRecording();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Authentication Methods
|
|
84
|
+
|
|
85
|
+
### Method 1: Direct Agent ID (Development)
|
|
86
|
+
|
|
87
|
+
For development and testing, you can use direct agent ID authentication:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
const voiceSDK = new VoiceSDK({
|
|
91
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv',
|
|
92
|
+
agentId: 'your_agent_id', // Direct agent ID
|
|
93
|
+
appId: 'your_app_id' // User's app ID
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Note**: This method is unsecured as the agent ID is visible in network traffic.
|
|
98
|
+
|
|
99
|
+
### Method 2: Signed Link (Production)
|
|
100
|
+
|
|
101
|
+
For production applications, use signed link authentication:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
const voiceSDK = new VoiceSDK({
|
|
105
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv?signed_token=eyJ...'
|
|
106
|
+
// No agentId needed - server validates signed token
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
To get a signed URL from your backend:
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
async function getSignedUrl(agentId, variables = {}) {
|
|
114
|
+
const response = await fetch('/api/get-session', {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: { 'Content-Type': 'application/json' },
|
|
117
|
+
body: JSON.stringify({ agentId, variables })
|
|
118
|
+
});
|
|
119
|
+
const data = await response.json();
|
|
120
|
+
return data.signedUrl;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Configuration Options
|
|
125
|
+
|
|
126
|
+
### VoiceSDK Configuration
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
const voiceSDK = new VoiceSDK({
|
|
130
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv', // Required
|
|
131
|
+
agentId: 'agent_12345', // Optional - for direct access
|
|
132
|
+
appId: 'app_67890', // Optional - user's app ID
|
|
133
|
+
ttpId: 'ttp_abc123', // Optional - custom TTP ID
|
|
134
|
+
voice: 'default', // Optional - voice selection
|
|
135
|
+
language: 'en', // Optional - language code
|
|
136
|
+
sampleRate: 16000, // Optional - audio sample rate
|
|
137
|
+
autoReconnect: true // Optional - auto-reconnect
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Widget Configuration
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
TTPAgentSDK.AgentWidget.init({
|
|
145
|
+
agentId: 'your_agent_id', // Required
|
|
146
|
+
getSessionUrl: 'https://...', // Required - URL or function
|
|
147
|
+
variables: { // Optional - dynamic variables
|
|
148
|
+
userName: 'John Doe',
|
|
149
|
+
page: 'homepage'
|
|
150
|
+
},
|
|
151
|
+
position: 'bottom-right', // Optional - widget position
|
|
152
|
+
primaryColor: '#4F46E5' // Optional - theme color
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Event Handling
|
|
157
|
+
|
|
158
|
+
The SDK provides comprehensive event handling:
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
voiceSDK.on('connected', () => {
|
|
162
|
+
console.log('Connected to voice agent');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
voiceSDK.on('disconnected', () => {
|
|
166
|
+
console.log('Disconnected from voice agent');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
voiceSDK.on('recordingStarted', () => {
|
|
170
|
+
console.log('Recording started');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
voiceSDK.on('recordingStopped', () => {
|
|
174
|
+
console.log('Recording stopped');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
voiceSDK.on('playbackStarted', () => {
|
|
178
|
+
console.log('Agent is speaking');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
voiceSDK.on('playbackStopped', () => {
|
|
182
|
+
console.log('Agent finished speaking');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
voiceSDK.on('message', (message) => {
|
|
186
|
+
if (message.type === 'agent_response') {
|
|
187
|
+
console.log('Agent response:', message.agent_response);
|
|
188
|
+
} else if (message.type === 'user_transcript') {
|
|
189
|
+
console.log('User said:', message.user_transcription);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
voiceSDK.on('error', (error) => {
|
|
194
|
+
console.error('VoiceSDK Error:', error);
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Backend Integration
|
|
199
|
+
|
|
200
|
+
### Required Backend Endpoint
|
|
201
|
+
|
|
202
|
+
Your backend needs to provide a session endpoint that returns a signed WebSocket URL:
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
// Example Express.js endpoint
|
|
206
|
+
app.post('/api/get-session', async (req, res) => {
|
|
207
|
+
const { agentId, variables } = req.body;
|
|
208
|
+
|
|
209
|
+
// Validate user authentication
|
|
210
|
+
const user = await authenticateUser(req);
|
|
211
|
+
if (!user) {
|
|
212
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Generate signed URL
|
|
216
|
+
const signedUrl = await generateSignedUrl({
|
|
217
|
+
agentId,
|
|
218
|
+
userId: user.id,
|
|
219
|
+
appId: user.appId,
|
|
220
|
+
variables
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
res.json({ signedUrl });
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Signed URL Generation
|
|
228
|
+
|
|
229
|
+
The signed URL should contain a JWT token with the following claims:
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
const jwt = require('jsonwebtoken');
|
|
233
|
+
|
|
234
|
+
function generateSignedUrl({ agentId, userId, appId, variables }) {
|
|
235
|
+
const token = jwt.sign({
|
|
236
|
+
agentId,
|
|
237
|
+
userId,
|
|
238
|
+
appId,
|
|
239
|
+
variables,
|
|
240
|
+
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 hour expiry
|
|
241
|
+
}, process.env.CONVERSATION_SECRET_KEY);
|
|
242
|
+
|
|
243
|
+
return `wss://speech.bidme.co.il/ws/conv?signed_token=${token}`;
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Examples
|
|
248
|
+
|
|
249
|
+
### Complete React Example
|
|
250
|
+
|
|
251
|
+
```jsx
|
|
252
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
253
|
+
import { VoiceSDK } from 'ttp-agent-sdk';
|
|
254
|
+
|
|
255
|
+
function VoiceChat() {
|
|
256
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
257
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
258
|
+
const [messages, setMessages] = useState([]);
|
|
259
|
+
const voiceSDKRef = useRef(null);
|
|
260
|
+
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
const voiceSDK = new VoiceSDK({
|
|
263
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv',
|
|
264
|
+
agentId: 'your_agent_id',
|
|
265
|
+
appId: 'your_app_id'
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
voiceSDK.on('connected', () => setIsConnected(true));
|
|
269
|
+
voiceSDK.on('disconnected', () => setIsConnected(false));
|
|
270
|
+
voiceSDK.on('recordingStarted', () => setIsRecording(true));
|
|
271
|
+
voiceSDK.on('recordingStopped', () => setIsRecording(false));
|
|
272
|
+
voiceSDK.on('message', (message) => {
|
|
273
|
+
if (message.type === 'agent_response') {
|
|
274
|
+
setMessages(prev => [...prev, { type: 'agent', text: message.agent_response }]);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
voiceSDKRef.current = voiceSDK;
|
|
279
|
+
|
|
280
|
+
return () => voiceSDK.destroy();
|
|
281
|
+
}, []);
|
|
282
|
+
|
|
283
|
+
const handleConnect = async () => {
|
|
284
|
+
await voiceSDKRef.current.connect();
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const handleToggleRecording = async () => {
|
|
288
|
+
if (isRecording) {
|
|
289
|
+
await voiceSDKRef.current.stopRecording();
|
|
290
|
+
} else {
|
|
291
|
+
await voiceSDKRef.current.startRecording();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
return (
|
|
296
|
+
<div>
|
|
297
|
+
<button onClick={handleConnect} disabled={isConnected}>
|
|
298
|
+
Connect
|
|
299
|
+
</button>
|
|
300
|
+
<button onClick={handleToggleRecording} disabled={!isConnected}>
|
|
301
|
+
{isRecording ? 'Stop Recording' : 'Start Recording'}
|
|
302
|
+
</button>
|
|
303
|
+
<div>
|
|
304
|
+
{messages.map((msg, i) => (
|
|
305
|
+
<div key={i}>{msg.type}: {msg.text}</div>
|
|
306
|
+
))}
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Complete Vanilla JavaScript Example
|
|
314
|
+
|
|
315
|
+
```html
|
|
316
|
+
<!DOCTYPE html>
|
|
317
|
+
<html>
|
|
318
|
+
<head>
|
|
319
|
+
<title>Voice Chat</title>
|
|
320
|
+
</head>
|
|
321
|
+
<body>
|
|
322
|
+
<button id="connectBtn">Connect</button>
|
|
323
|
+
<button id="recordBtn" disabled>Start Recording</button>
|
|
324
|
+
<div id="messages"></div>
|
|
325
|
+
|
|
326
|
+
<script type="module">
|
|
327
|
+
import { VoiceSDK } from 'https://unpkg.com/ttp-agent-sdk/dist/agent-widget.js';
|
|
328
|
+
|
|
329
|
+
const voiceSDK = new VoiceSDK({
|
|
330
|
+
websocketUrl: 'wss://speech.bidme.co.il/ws/conv',
|
|
331
|
+
agentId: 'your_agent_id',
|
|
332
|
+
appId: 'your_app_id'
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
let isRecording = false;
|
|
336
|
+
|
|
337
|
+
voiceSDK.on('connected', () => {
|
|
338
|
+
document.getElementById('connectBtn').disabled = true;
|
|
339
|
+
document.getElementById('recordBtn').disabled = false;
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
voiceSDK.on('recordingStarted', () => {
|
|
343
|
+
isRecording = true;
|
|
344
|
+
document.getElementById('recordBtn').textContent = 'Stop Recording';
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
voiceSDK.on('recordingStopped', () => {
|
|
348
|
+
isRecording = false;
|
|
349
|
+
document.getElementById('recordBtn').textContent = 'Start Recording';
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
voiceSDK.on('message', (message) => {
|
|
353
|
+
if (message.type === 'agent_response') {
|
|
354
|
+
addMessage('Agent', message.agent_response);
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
document.getElementById('connectBtn').onclick = () => voiceSDK.connect();
|
|
359
|
+
document.getElementById('recordBtn').onclick = () => {
|
|
360
|
+
if (isRecording) {
|
|
361
|
+
voiceSDK.stopRecording();
|
|
362
|
+
} else {
|
|
363
|
+
voiceSDK.startRecording();
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
function addMessage(sender, text) {
|
|
368
|
+
const div = document.createElement('div');
|
|
369
|
+
div.textContent = `${sender}: ${text}`;
|
|
370
|
+
document.getElementById('messages').appendChild(div);
|
|
371
|
+
}
|
|
372
|
+
</script>
|
|
373
|
+
</body>
|
|
374
|
+
</html>
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Troubleshooting
|
|
378
|
+
|
|
379
|
+
### Common Issues
|
|
380
|
+
|
|
381
|
+
1. **Connection Failed**
|
|
382
|
+
- Check if the WebSocket URL is correct
|
|
383
|
+
- Verify agent ID and app ID are valid
|
|
384
|
+
- Ensure backend is running and accessible
|
|
385
|
+
|
|
386
|
+
2. **Microphone Access Denied**
|
|
387
|
+
- Request microphone permission in browser
|
|
388
|
+
- Use HTTPS in production (required for microphone access)
|
|
389
|
+
|
|
390
|
+
3. **Audio Not Playing**
|
|
391
|
+
- Check browser audio settings
|
|
392
|
+
- Ensure Web Audio API is supported
|
|
393
|
+
- Verify audio format compatibility
|
|
394
|
+
|
|
395
|
+
4. **Authentication Errors**
|
|
396
|
+
- Verify signed token is valid and not expired
|
|
397
|
+
- Check secret key configuration
|
|
398
|
+
- Ensure agent ID exists in backend
|
|
399
|
+
|
|
400
|
+
### Debug Mode
|
|
401
|
+
|
|
402
|
+
Enable debug logging:
|
|
403
|
+
|
|
404
|
+
```javascript
|
|
405
|
+
const voiceSDK = new VoiceSDK({
|
|
406
|
+
// ... config
|
|
407
|
+
debug: true // Enable debug logging
|
|
408
|
+
});
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Browser Support
|
|
412
|
+
|
|
413
|
+
- Chrome 66+
|
|
414
|
+
- Firefox 60+
|
|
415
|
+
- Safari 11.1+
|
|
416
|
+
- Edge 79+
|
|
417
|
+
|
|
418
|
+
## Next Steps
|
|
419
|
+
|
|
420
|
+
- Check out the [examples](./examples/) directory for more complete examples
|
|
421
|
+
- Read the [API Reference](./README.md#api-reference) for detailed documentation
|
|
422
|
+
- Join our community for support and updates
|
|
423
|
+
|
|
424
|
+
## Support
|
|
425
|
+
|
|
426
|
+
For support and questions:
|
|
427
|
+
- Open an issue on GitHub
|
|
428
|
+
- Contact our support team
|
|
429
|
+
- Check the documentation
|