ttp-agent-sdk 2.0.7 → 2.1.1
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.
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Enhanced AgentWidget - Complete Customization Guide
|
|
2
|
+
|
|
3
|
+
The `EnhancedAgentWidget` provides extensive customization options while maintaining sensible defaults. Users can customize icons, positioning, colors, animations, and much more.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { EnhancedAgentWidget } from 'ttp-agent-sdk';
|
|
9
|
+
|
|
10
|
+
// Minimal configuration (uses all defaults)
|
|
11
|
+
new EnhancedAgentWidget({
|
|
12
|
+
agentId: 'your_agent_id',
|
|
13
|
+
getSessionUrl: async ({ agentId, variables }) => {
|
|
14
|
+
const response = await fetch('/api/get-session', {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
body: JSON.stringify({ agentId, variables })
|
|
18
|
+
});
|
|
19
|
+
const data = await response.json();
|
|
20
|
+
return data.signedUrl;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Complete Configuration Options
|
|
26
|
+
|
|
27
|
+
### Required Configuration
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
{
|
|
31
|
+
agentId: 'your_agent_id', // Required: Your agent ID
|
|
32
|
+
getSessionUrl: async ({ agentId, variables }) => { ... } // Required: Function to get signed URL
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Icon/Image Configuration
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
{
|
|
40
|
+
icon: {
|
|
41
|
+
type: 'microphone', // 'microphone', 'custom', 'emoji', 'text'
|
|
42
|
+
customImage: 'https://example.com/icon.png', // URL for custom image
|
|
43
|
+
emoji: '🎤', // Emoji to display
|
|
44
|
+
text: 'AI', // Text to display
|
|
45
|
+
size: 'medium' // 'small', 'medium', 'large', 'xl'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Icon Examples:**
|
|
51
|
+
```javascript
|
|
52
|
+
// Custom image
|
|
53
|
+
icon: { type: 'custom', customImage: 'https://example.com/my-logo.png' }
|
|
54
|
+
|
|
55
|
+
// Emoji
|
|
56
|
+
icon: { type: 'emoji', emoji: '🤖', size: 'large' }
|
|
57
|
+
|
|
58
|
+
// Text
|
|
59
|
+
icon: { type: 'text', text: 'HELP', size: 'small' }
|
|
60
|
+
|
|
61
|
+
// Default microphone
|
|
62
|
+
icon: { type: 'microphone' }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Positioning Configuration
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
{
|
|
69
|
+
position: {
|
|
70
|
+
vertical: 'bottom', // 'top', 'bottom', 'center'
|
|
71
|
+
horizontal: 'right', // 'left', 'right', 'center'
|
|
72
|
+
offset: { x: 20, y: 20 } // Custom offset in pixels
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Position Examples:**
|
|
78
|
+
```javascript
|
|
79
|
+
// Top-left corner
|
|
80
|
+
position: { vertical: 'top', horizontal: 'left' }
|
|
81
|
+
|
|
82
|
+
// Center of screen
|
|
83
|
+
position: { vertical: 'center', horizontal: 'center' }
|
|
84
|
+
|
|
85
|
+
// Custom offset from bottom-right
|
|
86
|
+
position: {
|
|
87
|
+
vertical: 'bottom',
|
|
88
|
+
horizontal: 'right',
|
|
89
|
+
offset: { x: 50, y: 100 }
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Button Configuration
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
{
|
|
97
|
+
button: {
|
|
98
|
+
size: 'medium', // 'small', 'medium', 'large', 'xl'
|
|
99
|
+
shape: 'circle', // 'circle', 'square', 'rounded'
|
|
100
|
+
primaryColor: '#4F46E5', // Main button color
|
|
101
|
+
hoverColor: '#7C3AED', // Hover state color
|
|
102
|
+
activeColor: '#EF4444', // Active/recording color
|
|
103
|
+
shadow: true, // Enable shadow
|
|
104
|
+
shadowColor: 'rgba(0,0,0,0.15)' // Shadow color
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Panel Configuration
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
{
|
|
113
|
+
panel: {
|
|
114
|
+
width: 350, // Panel width in pixels
|
|
115
|
+
height: 500, // Panel height in pixels
|
|
116
|
+
borderRadius: 12, // Border radius in pixels
|
|
117
|
+
backgroundColor: 'rgba(255,255,255,0.95)', // Panel background
|
|
118
|
+
backdropFilter: 'blur(10px)', // Backdrop filter effect
|
|
119
|
+
border: '1px solid rgba(0,0,0,0.1)' // Panel border
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Header Configuration
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
{
|
|
128
|
+
header: {
|
|
129
|
+
title: 'Voice Assistant', // Header title
|
|
130
|
+
showTitle: true, // Show/hide title
|
|
131
|
+
backgroundColor: null, // null = uses button primaryColor
|
|
132
|
+
textColor: '#FFFFFF', // Header text color
|
|
133
|
+
showCloseButton: true // Show/hide close button
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Messages Configuration
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
{
|
|
142
|
+
messages: {
|
|
143
|
+
userBackgroundColor: '#E5E7EB', // User message background
|
|
144
|
+
agentBackgroundColor: '#F3F4F6', // Agent message background
|
|
145
|
+
systemBackgroundColor: '#DCFCE7', // System message background
|
|
146
|
+
errorBackgroundColor: '#FEE2E2', // Error message background
|
|
147
|
+
textColor: '#1F2937', // Text color
|
|
148
|
+
fontSize: '14px', // Font size
|
|
149
|
+
borderRadius: 8 // Message border radius
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Animation Configuration
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
{
|
|
158
|
+
animation: {
|
|
159
|
+
enableHover: true, // Enable hover animations
|
|
160
|
+
enablePulse: true, // Enable pulse animation when recording
|
|
161
|
+
enableSlide: true, // Enable slide animations
|
|
162
|
+
duration: 0.3 // Animation duration in seconds
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Behavior Configuration
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
{
|
|
171
|
+
behavior: {
|
|
172
|
+
autoOpen: false, // Auto-open panel on load
|
|
173
|
+
autoConnect: false, // Auto-connect on load
|
|
174
|
+
showWelcomeMessage: true, // Show welcome message
|
|
175
|
+
welcomeMessage: 'Hello! How can I help you today?' // Welcome message text
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Accessibility Configuration
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
{
|
|
184
|
+
accessibility: {
|
|
185
|
+
ariaLabel: 'Voice Assistant', // ARIA label for button
|
|
186
|
+
ariaDescription: 'Click to open voice assistant', // ARIA description
|
|
187
|
+
keyboardNavigation: true // Enable keyboard navigation (ESC to close)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Custom CSS
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
{
|
|
196
|
+
customStyles: `
|
|
197
|
+
#enhanced-agent-widget {
|
|
198
|
+
/* Your custom CSS here */
|
|
199
|
+
}
|
|
200
|
+
`
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Complete Example
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
import { EnhancedAgentWidget } from 'ttp-agent-sdk';
|
|
208
|
+
|
|
209
|
+
const widget = new EnhancedAgentWidget({
|
|
210
|
+
// Required
|
|
211
|
+
agentId: 'my_agent_123',
|
|
212
|
+
getSessionUrl: async ({ agentId, variables }) => {
|
|
213
|
+
const response = await fetch('/api/get-session', {
|
|
214
|
+
method: 'POST',
|
|
215
|
+
headers: { 'Content-Type': 'application/json' },
|
|
216
|
+
body: JSON.stringify({ agentId, variables })
|
|
217
|
+
});
|
|
218
|
+
const data = await response.json();
|
|
219
|
+
return data.signedUrl;
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
// Custom icon (company logo)
|
|
223
|
+
icon: {
|
|
224
|
+
type: 'custom',
|
|
225
|
+
customImage: 'https://mycompany.com/logo.png',
|
|
226
|
+
size: 'large'
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
// Position in top-left
|
|
230
|
+
position: {
|
|
231
|
+
vertical: 'top',
|
|
232
|
+
horizontal: 'left',
|
|
233
|
+
offset: { x: 30, y: 30 }
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// Brand colors
|
|
237
|
+
button: {
|
|
238
|
+
size: 'large',
|
|
239
|
+
shape: 'rounded',
|
|
240
|
+
primaryColor: '#FF6B35',
|
|
241
|
+
hoverColor: '#FF8C42',
|
|
242
|
+
activeColor: '#FF4444'
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
// Custom panel styling
|
|
246
|
+
panel: {
|
|
247
|
+
width: 400,
|
|
248
|
+
height: 600,
|
|
249
|
+
backgroundColor: 'rgba(255, 255, 255, 0.98)',
|
|
250
|
+
borderRadius: 20,
|
|
251
|
+
backdropFilter: 'blur(15px)'
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
// Custom header
|
|
255
|
+
header: {
|
|
256
|
+
title: 'My Company Assistant',
|
|
257
|
+
backgroundColor: '#FF6B35',
|
|
258
|
+
textColor: '#FFFFFF'
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// Custom messages
|
|
262
|
+
messages: {
|
|
263
|
+
userBackgroundColor: '#FF6B35',
|
|
264
|
+
agentBackgroundColor: '#F0F0F0',
|
|
265
|
+
textColor: '#333333',
|
|
266
|
+
fontSize: '16px'
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
// Smooth animations
|
|
270
|
+
animation: {
|
|
271
|
+
enableHover: true,
|
|
272
|
+
enablePulse: true,
|
|
273
|
+
enableSlide: true,
|
|
274
|
+
duration: 0.4
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
// Auto-open for demo
|
|
278
|
+
behavior: {
|
|
279
|
+
autoOpen: true,
|
|
280
|
+
welcomeMessage: 'Welcome to My Company! How can I assist you?'
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
// Custom variables
|
|
284
|
+
variables: {
|
|
285
|
+
company: 'My Company',
|
|
286
|
+
page: 'homepage',
|
|
287
|
+
userType: 'visitor'
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Public API Methods
|
|
293
|
+
|
|
294
|
+
```javascript
|
|
295
|
+
// Update configuration dynamically
|
|
296
|
+
widget.updateConfig({
|
|
297
|
+
button: { primaryColor: '#00FF00' },
|
|
298
|
+
position: { vertical: 'top' }
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Destroy the widget
|
|
302
|
+
widget.destroy();
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Migration from Original AgentWidget
|
|
306
|
+
|
|
307
|
+
The enhanced widget is fully backward compatible. Simply replace:
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
// Old
|
|
311
|
+
import { AgentWidget } from 'ttp-agent-sdk';
|
|
312
|
+
new AgentWidget({ ... });
|
|
313
|
+
|
|
314
|
+
// New
|
|
315
|
+
import { EnhancedAgentWidget } from 'ttp-agent-sdk';
|
|
316
|
+
new EnhancedAgentWidget({ ... });
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
All existing configurations will work with sensible defaults applied.
|
|
320
|
+
|
|
321
|
+
## Browser Support
|
|
322
|
+
|
|
323
|
+
- Chrome 66+
|
|
324
|
+
- Firefox 60+
|
|
325
|
+
- Safari 11.1+
|
|
326
|
+
- Edge 79+
|
|
327
|
+
|
|
328
|
+
## License
|
|
329
|
+
|
|
330
|
+
MIT
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# TTP Agent SDK - Signed Link Authentication Guide
|
|
2
|
+
|
|
3
|
+
## 🔐 Understanding Signed Links
|
|
4
|
+
|
|
5
|
+
Signed links provide **secure, production-ready authentication** for your voice agents. Your **UI backend** generates secure, time-limited URLs that the widget uses to connect to the **conversation backend**.
|
|
6
|
+
|
|
7
|
+
## 🏗️ Architecture Overview
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
User's Frontend → User's Backend → TTP UI Backend → TTP Conversation Backend
|
|
11
|
+
↓ ↓ ↓ ↓
|
|
12
|
+
Requests Requests Generates Handles
|
|
13
|
+
signed URL signed URL signed URL conversation
|
|
14
|
+
↑ ↑ ↑ ↑
|
|
15
|
+
Receives Receives Returns Validates
|
|
16
|
+
signed URL signed URL signed URL signed token
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**The complete flow:**
|
|
20
|
+
1. **User's Frontend** → requests signed URL from **User's Backend**
|
|
21
|
+
2. **User's Backend** → requests signed URL from **TTP UI Backend** (`/api/public/agents`)
|
|
22
|
+
3. **TTP UI Backend** → generates signed URL and returns to **User's Backend**
|
|
23
|
+
4. **User's Backend** → returns signed URL to **User's Frontend**
|
|
24
|
+
5. **User's Frontend** → connects directly to **TTP Conversation Backend** using signed URL
|
|
25
|
+
|
|
26
|
+
## 🎯 Why Use Signed Links?
|
|
27
|
+
|
|
28
|
+
### ❌ **Direct Agent ID (Insecure)**
|
|
29
|
+
```javascript
|
|
30
|
+
// DON'T DO THIS IN PRODUCTION
|
|
31
|
+
new EnhancedAgentWidget({
|
|
32
|
+
agentId: 'agent_12345', // ❌ Visible in network traffic
|
|
33
|
+
websocketUrl: 'wss://speech.talktopc.com/ws/conv'
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Problems:**
|
|
38
|
+
- Agent ID exposed in browser network tab
|
|
39
|
+
- No cost control
|
|
40
|
+
- No user-specific permissions
|
|
41
|
+
- Security risk
|
|
42
|
+
|
|
43
|
+
### ✅ **Signed Link (Secure)**
|
|
44
|
+
```javascript
|
|
45
|
+
// PRODUCTION-READY APPROACH
|
|
46
|
+
new EnhancedAgentWidget({
|
|
47
|
+
agentId: 'agent_12345',
|
|
48
|
+
getSessionUrl: async ({ agentId, variables }) => {
|
|
49
|
+
// User's Frontend calls User's Backend
|
|
50
|
+
const response = await fetch('/api/get-voice-session', {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
body: JSON.stringify({ agentId, variables })
|
|
54
|
+
});
|
|
55
|
+
const data = await response.json();
|
|
56
|
+
return data.signedUrl; // ✅ Secure, time-limited URL from TTP
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Benefits:**
|
|
62
|
+
- ✅ Secure authentication
|
|
63
|
+
- ✅ Cost control per user
|
|
64
|
+
- ✅ User-specific permissions
|
|
65
|
+
- ✅ Time-limited access
|
|
66
|
+
- ✅ Production-ready
|
|
67
|
+
|
|
68
|
+
## 🏗️ Implementation Guide
|
|
69
|
+
|
|
70
|
+
### Step 1: User's Backend Implementation
|
|
71
|
+
|
|
72
|
+
Your user's backend should have an endpoint that requests signed URLs from TTP:
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
// User's Backend (Node.js example)
|
|
76
|
+
app.post('/api/get-voice-session', async (req, res) => {
|
|
77
|
+
try {
|
|
78
|
+
const { agentId, variables } = req.body;
|
|
79
|
+
|
|
80
|
+
// Validate user authentication
|
|
81
|
+
const user = await validateUser(req.headers.authorization);
|
|
82
|
+
if (!user) {
|
|
83
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Request signed URL from TTP UI Backend
|
|
87
|
+
const ttpResponse = await fetch('https://backend.talktopc.com/api/public/agents', {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'application/json',
|
|
91
|
+
'Authorization': `Bearer ${process.env.TTP_API_KEY}` // Your TTP API key
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({
|
|
94
|
+
agentId,
|
|
95
|
+
variables: {
|
|
96
|
+
...variables,
|
|
97
|
+
userId: user.id,
|
|
98
|
+
userEmail: user.email
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!ttpResponse.ok) {
|
|
104
|
+
throw new Error(`TTP API error: ${ttpResponse.status}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const ttpData = await ttpResponse.json();
|
|
108
|
+
|
|
109
|
+
// Return signed URL to user's frontend
|
|
110
|
+
res.json({
|
|
111
|
+
signedUrl: ttpData.signedUrl
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('Failed to get signed URL from TTP:', error);
|
|
116
|
+
res.status(500).json({ error: 'Failed to get voice session' });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Step 2: User's Frontend Integration
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
import { EnhancedAgentWidget } from 'ttp-agent-sdk';
|
|
125
|
+
|
|
126
|
+
new EnhancedAgentWidget({
|
|
127
|
+
agentId: 'your_agent_id',
|
|
128
|
+
|
|
129
|
+
// User's Frontend calls User's Backend
|
|
130
|
+
getSessionUrl: async ({ agentId, variables }) => {
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetch('/api/get-voice-session', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: {
|
|
135
|
+
'Content-Type': 'application/json',
|
|
136
|
+
'Authorization': `Bearer ${getAuthToken()}` // User's auth token
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify({
|
|
139
|
+
agentId,
|
|
140
|
+
variables: {
|
|
141
|
+
...variables,
|
|
142
|
+
page: 'homepage',
|
|
143
|
+
userType: 'customer'
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
throw new Error(`Your backend responded with ${response.status}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const data = await response.json();
|
|
153
|
+
return data.signedUrl; // This connects directly to TTP Conversation Backend
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('Failed to get signed URL from your backend:', error);
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
variables: {
|
|
162
|
+
page: 'homepage',
|
|
163
|
+
userType: 'customer',
|
|
164
|
+
language: 'en'
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## 🔧 Your Current Implementation
|
|
170
|
+
|
|
171
|
+
Based on your setup, you're already using this pattern:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// Your current LandingPageWidget.jsx
|
|
175
|
+
getSessionUrl: async ({ agentId, variables }) => {
|
|
176
|
+
const response = await fetch(buildApiUrl('/api/landing/signed-url'), {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
headers: { 'Content-Type': 'application/json' },
|
|
179
|
+
body: JSON.stringify({
|
|
180
|
+
agentId: agentId,
|
|
181
|
+
variables: variables,
|
|
182
|
+
page: 'landing',
|
|
183
|
+
userType: 'visitor'
|
|
184
|
+
})
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const data = await response.json();
|
|
188
|
+
return data.signedUrl; // This connects to conversation backend
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 🎯 Key Points
|
|
193
|
+
|
|
194
|
+
1. **UI Backend** (`/api/landing/signed-url`):
|
|
195
|
+
- Handles authentication
|
|
196
|
+
- Generates signed URLs
|
|
197
|
+
- Manages user permissions
|
|
198
|
+
- Controls costs
|
|
199
|
+
|
|
200
|
+
2. **Conversation Backend** (`wss://speech.talktopc.com/ws/conv`):
|
|
201
|
+
- Validates signed tokens
|
|
202
|
+
- Handles voice conversations
|
|
203
|
+
- Processes audio streams
|
|
204
|
+
|
|
205
|
+
3. **Frontend Widget**:
|
|
206
|
+
- Requests signed URL from UI backend
|
|
207
|
+
- Connects to conversation backend using signed URL
|
|
208
|
+
- Never exposes agent IDs directly
|
|
209
|
+
|
|
210
|
+
## 🚀 Production Benefits
|
|
211
|
+
|
|
212
|
+
- **Security**: Agent IDs never exposed in frontend
|
|
213
|
+
- **Separation**: UI logic separate from conversation logic
|
|
214
|
+
- **Scalability**: Each backend can scale independently
|
|
215
|
+
- **Control**: UI backend controls access and permissions
|
|
216
|
+
- **Monitoring**: Track usage and costs at UI backend level
|
|
217
|
+
|
|
218
|
+
This architecture ensures your voice agents are secure, scalable, and production-ready! 🎤✨
|