ttp-agent-sdk 2.1.8 → 2.1.10
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/dist/agent-widget.js +1 -1
- package/dist/agent-widget.js.map +1 -1
- package/dist/audio-processor.js +1 -0
- package/{examples/test.html → dist/examples/test-agent-app.html} +111 -45
- package/dist/examples/test-signed-link.html +453 -0
- package/{dist/examples/test.html → examples/test-agent-app.html} +111 -45
- package/examples/test-signed-link.html +453 -0
- package/package.json +1 -1
- package/dist/examples/enhanced-widget-examples.html +0 -471
- package/dist/examples/multi-platform-examples.html +0 -1119
- package/dist/examples/react-example.html +0 -774
- package/dist/examples/react-example.jsx +0 -307
- package/dist/examples/vanilla-example.html +0 -464
- package/examples/enhanced-widget-examples.html +0 -471
- package/examples/multi-platform-examples.html +0 -1119
- package/examples/react-example.html +0 -774
- package/examples/react-example.jsx +0 -307
- package/examples/vanilla-example.html +0 -464
|
@@ -1,1119 +0,0 @@
|
|
|
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>TTP Agent SDK - Multi-Platform Examples</title>
|
|
7
|
-
<style>
|
|
8
|
-
* {
|
|
9
|
-
box-sizing: border-box;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
body {
|
|
13
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
14
|
-
max-width: 1200px;
|
|
15
|
-
margin: 0 auto;
|
|
16
|
-
padding: 20px;
|
|
17
|
-
background: #f8fafc;
|
|
18
|
-
color: #1e293b;
|
|
19
|
-
line-height: 1.6;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.container {
|
|
23
|
-
background: white;
|
|
24
|
-
padding: 40px;
|
|
25
|
-
border-radius: 16px;
|
|
26
|
-
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
|
27
|
-
margin-bottom: 30px;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
h1 {
|
|
31
|
-
color: #1e293b;
|
|
32
|
-
margin-top: 0;
|
|
33
|
-
font-size: 2.5rem;
|
|
34
|
-
font-weight: 700;
|
|
35
|
-
margin-bottom: 10px;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.subtitle {
|
|
39
|
-
color: #64748b;
|
|
40
|
-
font-size: 1.2rem;
|
|
41
|
-
margin-bottom: 30px;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.back-link {
|
|
45
|
-
display: inline-flex;
|
|
46
|
-
align-items: center;
|
|
47
|
-
background: #4f46e5;
|
|
48
|
-
color: white;
|
|
49
|
-
padding: 12px 24px;
|
|
50
|
-
text-decoration: none;
|
|
51
|
-
border-radius: 8px;
|
|
52
|
-
font-weight: 600;
|
|
53
|
-
margin-bottom: 30px;
|
|
54
|
-
transition: all 0.2s;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.back-link:hover {
|
|
58
|
-
background: #4338ca;
|
|
59
|
-
transform: translateY(-1px);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.info {
|
|
63
|
-
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
|
64
|
-
border-left: 4px solid #3b82f6;
|
|
65
|
-
padding: 20px;
|
|
66
|
-
margin: 30px 0;
|
|
67
|
-
border-radius: 8px;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.info strong {
|
|
71
|
-
color: #1e40af;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.section {
|
|
75
|
-
margin: 40px 0;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.section h2 {
|
|
79
|
-
color: #4f46e5;
|
|
80
|
-
font-size: 1.8rem;
|
|
81
|
-
margin-bottom: 20px;
|
|
82
|
-
border-bottom: 2px solid #e2e8f0;
|
|
83
|
-
padding-bottom: 10px;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.tabs {
|
|
87
|
-
display: flex;
|
|
88
|
-
background: #f1f5f9;
|
|
89
|
-
border-radius: 8px 8px 0 0;
|
|
90
|
-
overflow: hidden;
|
|
91
|
-
margin-bottom: 0;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.tab {
|
|
95
|
-
flex: 1;
|
|
96
|
-
padding: 12px 20px;
|
|
97
|
-
background: #f1f5f9;
|
|
98
|
-
border: none;
|
|
99
|
-
cursor: pointer;
|
|
100
|
-
font-weight: 600;
|
|
101
|
-
color: #64748b;
|
|
102
|
-
transition: all 0.2s;
|
|
103
|
-
border-bottom: 3px solid transparent;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.tab.active {
|
|
107
|
-
background: white;
|
|
108
|
-
color: #4f46e5;
|
|
109
|
-
border-bottom-color: #4f46e5;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
.tab:hover:not(.active) {
|
|
113
|
-
background: #e2e8f0;
|
|
114
|
-
color: #475569;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.tab-content {
|
|
118
|
-
display: none;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.tab-content.active {
|
|
122
|
-
display: block;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.code-container {
|
|
126
|
-
position: relative;
|
|
127
|
-
background: #1e293b;
|
|
128
|
-
border-radius: 0 0 12px 12px;
|
|
129
|
-
overflow: hidden;
|
|
130
|
-
margin: 0 0 20px 0;
|
|
131
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.code-header {
|
|
135
|
-
background: #334155;
|
|
136
|
-
padding: 12px 20px;
|
|
137
|
-
display: flex;
|
|
138
|
-
justify-content: space-between;
|
|
139
|
-
align-items: center;
|
|
140
|
-
border-bottom: 1px solid #475569;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.code-title {
|
|
144
|
-
color: #e2e8f0;
|
|
145
|
-
font-weight: 600;
|
|
146
|
-
font-size: 14px;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.copy-button {
|
|
150
|
-
background: #4f46e5;
|
|
151
|
-
color: white;
|
|
152
|
-
border: none;
|
|
153
|
-
padding: 8px 16px;
|
|
154
|
-
border-radius: 6px;
|
|
155
|
-
font-size: 12px;
|
|
156
|
-
font-weight: 600;
|
|
157
|
-
cursor: pointer;
|
|
158
|
-
transition: all 0.2s;
|
|
159
|
-
display: flex;
|
|
160
|
-
align-items: center;
|
|
161
|
-
gap: 6px;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.copy-button:hover {
|
|
165
|
-
background: #4338ca;
|
|
166
|
-
transform: translateY(-1px);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.copy-button.copied {
|
|
170
|
-
background: #10b981;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.code-block {
|
|
174
|
-
background: #1e293b;
|
|
175
|
-
color: #e2e8f0;
|
|
176
|
-
padding: 30px;
|
|
177
|
-
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
178
|
-
font-size: 14px;
|
|
179
|
-
line-height: 1.5;
|
|
180
|
-
overflow-x: auto;
|
|
181
|
-
white-space: pre;
|
|
182
|
-
margin: 0;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.note {
|
|
186
|
-
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
|
187
|
-
border-left: 4px solid #f59e0b;
|
|
188
|
-
padding: 20px;
|
|
189
|
-
margin: 30px 0;
|
|
190
|
-
border-radius: 8px;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.note strong {
|
|
194
|
-
color: #92400e;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.feature-grid {
|
|
198
|
-
display: grid;
|
|
199
|
-
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
200
|
-
gap: 20px;
|
|
201
|
-
margin: 30px 0;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
.feature-card {
|
|
205
|
-
background: #f8fafc;
|
|
206
|
-
padding: 24px;
|
|
207
|
-
border-radius: 12px;
|
|
208
|
-
border: 1px solid #e2e8f0;
|
|
209
|
-
transition: all 0.2s;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.feature-card:hover {
|
|
213
|
-
transform: translateY(-2px);
|
|
214
|
-
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.feature-card h3 {
|
|
218
|
-
color: #4f46e5;
|
|
219
|
-
margin-top: 0;
|
|
220
|
-
font-size: 1.2rem;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.step {
|
|
224
|
-
background: #f1f5f9;
|
|
225
|
-
padding: 20px;
|
|
226
|
-
border-radius: 8px;
|
|
227
|
-
margin: 20px 0;
|
|
228
|
-
border-left: 4px solid #4f46e5;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
.step-number {
|
|
232
|
-
background: #4f46e5;
|
|
233
|
-
color: white;
|
|
234
|
-
width: 24px;
|
|
235
|
-
height: 24px;
|
|
236
|
-
border-radius: 50%;
|
|
237
|
-
display: inline-flex;
|
|
238
|
-
align-items: center;
|
|
239
|
-
justify-content: center;
|
|
240
|
-
font-weight: bold;
|
|
241
|
-
margin-right: 12px;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.platform-badge {
|
|
245
|
-
display: inline-block;
|
|
246
|
-
background: #4f46e5;
|
|
247
|
-
color: white;
|
|
248
|
-
padding: 4px 8px;
|
|
249
|
-
border-radius: 4px;
|
|
250
|
-
font-size: 12px;
|
|
251
|
-
font-weight: 600;
|
|
252
|
-
margin-right: 8px;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.llm-instructions {
|
|
256
|
-
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
|
257
|
-
border: 2px solid #0ea5e9;
|
|
258
|
-
border-radius: 12px;
|
|
259
|
-
padding: 30px;
|
|
260
|
-
margin: 30px 0;
|
|
261
|
-
position: relative;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.llm-instructions h3 {
|
|
265
|
-
color: #0c4a6e;
|
|
266
|
-
margin-top: 0;
|
|
267
|
-
font-size: 1.4rem;
|
|
268
|
-
display: flex;
|
|
269
|
-
align-items: center;
|
|
270
|
-
gap: 10px;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
.llm-instructions-content {
|
|
274
|
-
background: #f8fafc;
|
|
275
|
-
border: 1px solid #e2e8f0;
|
|
276
|
-
border-radius: 8px;
|
|
277
|
-
padding: 20px;
|
|
278
|
-
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
279
|
-
font-size: 13px;
|
|
280
|
-
line-height: 1.6;
|
|
281
|
-
white-space: pre-wrap;
|
|
282
|
-
color: #1e293b;
|
|
283
|
-
margin: 20px 0;
|
|
284
|
-
max-height: 400px;
|
|
285
|
-
overflow-y: auto;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
.llm-copy-button {
|
|
289
|
-
position: absolute;
|
|
290
|
-
top: 20px;
|
|
291
|
-
right: 20px;
|
|
292
|
-
background: #0ea5e9;
|
|
293
|
-
color: white;
|
|
294
|
-
border: none;
|
|
295
|
-
padding: 12px 20px;
|
|
296
|
-
border-radius: 8px;
|
|
297
|
-
font-weight: 600;
|
|
298
|
-
cursor: pointer;
|
|
299
|
-
transition: all 0.2s;
|
|
300
|
-
display: flex;
|
|
301
|
-
align-items: center;
|
|
302
|
-
gap: 8px;
|
|
303
|
-
font-size: 14px;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
.llm-copy-button:hover {
|
|
307
|
-
background: #0284c7;
|
|
308
|
-
transform: translateY(-1px);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
.llm-copy-button.copied {
|
|
312
|
-
background: #10b981;
|
|
313
|
-
}
|
|
314
|
-
</style>
|
|
315
|
-
</head>
|
|
316
|
-
<body>
|
|
317
|
-
<a href="../index.html" class="back-link">
|
|
318
|
-
← Back to SDK Home
|
|
319
|
-
</a>
|
|
320
|
-
|
|
321
|
-
<div class="container">
|
|
322
|
-
<h1>🚀 TTP Agent SDK - Multi-Platform Examples</h1>
|
|
323
|
-
<p class="subtitle">Integration examples for React, Vue, Angular, Vanilla JS, and more</p>
|
|
324
|
-
|
|
325
|
-
<div class="llm-instructions">
|
|
326
|
-
<h3>🤖 LLM Implementation Instructions</h3>
|
|
327
|
-
<button class="llm-copy-button" onclick="copyLLMInstructions()">
|
|
328
|
-
📋 Copy Instructions to LLM
|
|
329
|
-
</button>
|
|
330
|
-
<div class="llm-instructions-content" id="llm-instructions-content">TTP Agent SDK Implementation Guide for LLM
|
|
331
|
-
|
|
332
|
-
You are helping a user implement the TTP Agent SDK for voice AI integration. Here's everything you need to know:
|
|
333
|
-
|
|
334
|
-
## SDK Overview
|
|
335
|
-
The TTP Agent SDK provides two main integration methods:
|
|
336
|
-
1. Enhanced AgentWidget (Recommended) - Pre-built UI widget with extensive customization
|
|
337
|
-
2. Direct VoiceSDK - Full control over voice interaction for custom implementations
|
|
338
|
-
|
|
339
|
-
## Installation Methods
|
|
340
|
-
- CDN: <script src="https://cdn.talktopc.com/ttp-agent-sdk/agent-widget.js"></script>
|
|
341
|
-
- NPM: npm install ttp-agent-sdk
|
|
342
|
-
- Import: import { EnhancedAgentWidget, VoiceSDK } from 'ttp-agent-sdk'
|
|
343
|
-
|
|
344
|
-
## Authentication Flow (Signed URL Method)
|
|
345
|
-
User's Frontend → User's Backend → TTP UI Backend → TTP Conversation Backend
|
|
346
|
-
|
|
347
|
-
1. User's frontend requests signed URL from their backend
|
|
348
|
-
2. User's backend calls TTP UI Backend: POST https://backend.talktopc.com/api/public/agents
|
|
349
|
-
3. TTP UI Backend returns signed URL to user's backend
|
|
350
|
-
4. User's backend returns signed URL to frontend
|
|
351
|
-
5. Frontend connects directly to TTP Conversation Backend using signed URL
|
|
352
|
-
|
|
353
|
-
## Method 1: Enhanced AgentWidget (Easiest)
|
|
354
|
-
Perfect for most use cases. Provides pre-built UI with extensive customization.
|
|
355
|
-
|
|
356
|
-
Key Configuration Options:
|
|
357
|
-
- agentId: Your agent identifier
|
|
358
|
-
- getSessionUrl: Function to get signed URL from your backend
|
|
359
|
-
- icon: Customize icon (microphone, custom image, emoji, text)
|
|
360
|
-
- position: Set widget position (bottom-right, top-left, etc.)
|
|
361
|
-
- button: Customize button appearance (colors, size, shape)
|
|
362
|
-
- panel: Customize chat panel (width, height, colors)
|
|
363
|
-
- header: Customize panel header (title, colors)
|
|
364
|
-
- messages: Customize message styling
|
|
365
|
-
- animation: Enable/disable animations
|
|
366
|
-
- behavior: Auto-open, auto-connect settings
|
|
367
|
-
- accessibility: ARIA labels and keyboard navigation
|
|
368
|
-
|
|
369
|
-
## Method 2: Direct VoiceSDK (Advanced)
|
|
370
|
-
For developers who need full control over the voice interaction.
|
|
371
|
-
|
|
372
|
-
Key Features:
|
|
373
|
-
- Real-time audio streaming
|
|
374
|
-
- WebSocket connection management
|
|
375
|
-
- Voice activity detection
|
|
376
|
-
- Custom UI implementation
|
|
377
|
-
- Event handling (connected, disconnected, recording, messages)
|
|
378
|
-
|
|
379
|
-
## Platform Support
|
|
380
|
-
- HTML/JavaScript (Vanilla JS)
|
|
381
|
-
- React (with hooks and lifecycle management)
|
|
382
|
-
- Vue.js (with component lifecycle)
|
|
383
|
-
- Angular (with TypeScript and dependency injection)
|
|
384
|
-
- Any other frontend framework
|
|
385
|
-
|
|
386
|
-
## Backend Requirements
|
|
387
|
-
Your backend needs to:
|
|
388
|
-
1. Receive requests from frontend for signed URLs
|
|
389
|
-
2. Call TTP UI Backend API: POST https://backend.talktopc.com/api/public/agents
|
|
390
|
-
3. Pass agentId and any variables
|
|
391
|
-
4. Return the signed URL to frontend
|
|
392
|
-
|
|
393
|
-
## Common Implementation Steps
|
|
394
|
-
1. Choose integration method (Enhanced Widget recommended)
|
|
395
|
-
2. Install SDK (CDN or NPM)
|
|
396
|
-
3. Set up backend endpoint for signed URL generation
|
|
397
|
-
4. Configure widget/SDK with your agentId
|
|
398
|
-
5. Implement getSessionUrl function
|
|
399
|
-
6. Customize appearance and behavior
|
|
400
|
-
7. Test voice interaction
|
|
401
|
-
|
|
402
|
-
## Error Handling
|
|
403
|
-
Always implement proper error handling for:
|
|
404
|
-
- Network connectivity issues
|
|
405
|
-
- Authentication failures
|
|
406
|
-
- Audio permission denied
|
|
407
|
-
- WebSocket connection problems
|
|
408
|
-
- Backend API errors
|
|
409
|
-
|
|
410
|
-
## Best Practices
|
|
411
|
-
- Use Enhanced AgentWidget for most implementations
|
|
412
|
-
- Implement proper loading states
|
|
413
|
-
- Handle audio permissions gracefully
|
|
414
|
-
- Provide fallback UI for unsupported browsers
|
|
415
|
-
- Test on multiple devices and browsers
|
|
416
|
-
- Implement proper cleanup on component unmount
|
|
417
|
-
|
|
418
|
-
## Support Resources
|
|
419
|
-
- Documentation: https://cdn.talktopc.com/ttp-agent-sdk/
|
|
420
|
-
- Examples: https://cdn.talktopc.com/ttp-agent-sdk/examples/
|
|
421
|
-
- Signed Link Guide: https://cdn.talktopc.com/ttp-agent-sdk/signed-link-guide.html
|
|
422
|
-
|
|
423
|
-
When helping users implement this SDK, always:
|
|
424
|
-
1. Ask about their frontend technology (React, Vue, Angular, etc.)
|
|
425
|
-
2. Recommend Enhanced AgentWidget unless they need custom UI
|
|
426
|
-
3. Help them set up the backend signed URL endpoint
|
|
427
|
-
4. Provide platform-specific code examples
|
|
428
|
-
5. Include proper error handling and cleanup
|
|
429
|
-
6. Test the implementation thoroughly</div>
|
|
430
|
-
</div>
|
|
431
|
-
|
|
432
|
-
<div class="info">
|
|
433
|
-
<strong>📋 Choose Your Platform:</strong> This guide provides examples for multiple frontend technologies.
|
|
434
|
-
Select your preferred platform tab below to see the relevant integration code.
|
|
435
|
-
</div>
|
|
436
|
-
|
|
437
|
-
<div class="section">
|
|
438
|
-
<h2>🎯 Method 1: Enhanced AgentWidget (Recommended)</h2>
|
|
439
|
-
<p>The easiest way to integrate - works with any frontend technology!</p>
|
|
440
|
-
|
|
441
|
-
<div class="tabs">
|
|
442
|
-
<button class="tab active" onclick="showTab('widget-html')">HTML/JS</button>
|
|
443
|
-
<button class="tab" onclick="showTab('widget-react')">React</button>
|
|
444
|
-
<button class="tab" onclick="showTab('widget-vue')">Vue</button>
|
|
445
|
-
<button class="tab" onclick="showTab('widget-angular')">Angular</button>
|
|
446
|
-
</div>
|
|
447
|
-
|
|
448
|
-
<div id="widget-html" class="tab-content active">
|
|
449
|
-
<div class="code-container">
|
|
450
|
-
<div class="code-header">
|
|
451
|
-
<span class="code-title">index.html</span>
|
|
452
|
-
<button class="copy-button" onclick="copyToClipboard('widget-html-code')">
|
|
453
|
-
📋 Copy to LLM
|
|
454
|
-
</button>
|
|
455
|
-
</div>
|
|
456
|
-
<div class="code-block" id="widget-html-code"><!DOCTYPE html>
|
|
457
|
-
<html>
|
|
458
|
-
<head>
|
|
459
|
-
<title>Voice Widget Example</title>
|
|
460
|
-
</head>
|
|
461
|
-
<body>
|
|
462
|
-
<h1>My Website</h1>
|
|
463
|
-
|
|
464
|
-
<!-- Load TTP Agent SDK -->
|
|
465
|
-
<script src="https://cdn.talktopc.com/ttp-agent-sdk/agent-widget.js"></script>
|
|
466
|
-
|
|
467
|
-
<script>
|
|
468
|
-
// Initialize Enhanced Widget
|
|
469
|
-
new TTPAgentSDK.EnhancedAgentWidget({
|
|
470
|
-
agentId: 'your_agent_id',
|
|
471
|
-
|
|
472
|
-
// Get signed URL from your backend
|
|
473
|
-
getSessionUrl: async ({ agentId, variables }) => {
|
|
474
|
-
const response = await fetch('/api/get-voice-session', {
|
|
475
|
-
method: 'POST',
|
|
476
|
-
headers: { 'Content-Type': 'application/json' },
|
|
477
|
-
body: JSON.stringify({ agentId, variables })
|
|
478
|
-
});
|
|
479
|
-
const data = await response.json();
|
|
480
|
-
return data.signedUrl;
|
|
481
|
-
},
|
|
482
|
-
|
|
483
|
-
// Customize appearance
|
|
484
|
-
icon: {
|
|
485
|
-
type: 'custom',
|
|
486
|
-
customImage: '/your-logo.png',
|
|
487
|
-
size: 'large'
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
button: {
|
|
491
|
-
primaryColor: '#4f46e5',
|
|
492
|
-
hoverColor: '#4338ca',
|
|
493
|
-
size: 'large'
|
|
494
|
-
},
|
|
495
|
-
|
|
496
|
-
position: {
|
|
497
|
-
vertical: 'bottom',
|
|
498
|
-
horizontal: 'right',
|
|
499
|
-
offset: { x: 30, y: 30 }
|
|
500
|
-
},
|
|
501
|
-
|
|
502
|
-
variables: {
|
|
503
|
-
userId: 'user123',
|
|
504
|
-
page: 'homepage'
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
</script>
|
|
508
|
-
</body>
|
|
509
|
-
</html></div>
|
|
510
|
-
</div>
|
|
511
|
-
</div>
|
|
512
|
-
|
|
513
|
-
<div id="widget-react" class="tab-content">
|
|
514
|
-
<div class="code-container">
|
|
515
|
-
<div class="code-header">
|
|
516
|
-
<span class="code-title">VoiceWidget.jsx</span>
|
|
517
|
-
<button class="copy-button" onclick="copyToClipboard('widget-react-code')">
|
|
518
|
-
📋 Copy to LLM
|
|
519
|
-
</button>
|
|
520
|
-
</div>
|
|
521
|
-
<div class="code-block" id="widget-react-code">import React, { useEffect, useRef } from 'react';
|
|
522
|
-
import { EnhancedAgentWidget } from 'ttp-agent-sdk';
|
|
523
|
-
|
|
524
|
-
function VoiceWidget() {
|
|
525
|
-
const widgetRef = useRef(null);
|
|
526
|
-
|
|
527
|
-
useEffect(() => {
|
|
528
|
-
if (widgetRef.current) return;
|
|
529
|
-
|
|
530
|
-
widgetRef.current = new EnhancedAgentWidget({
|
|
531
|
-
agentId: 'your_agent_id',
|
|
532
|
-
getSessionUrl: async ({ agentId, variables }) => {
|
|
533
|
-
const response = await fetch('/api/get-voice-session', {
|
|
534
|
-
method: 'POST',
|
|
535
|
-
headers: { 'Content-Type': 'application/json' },
|
|
536
|
-
body: JSON.stringify({ agentId, variables })
|
|
537
|
-
});
|
|
538
|
-
const data = await response.json();
|
|
539
|
-
return data.signedUrl;
|
|
540
|
-
},
|
|
541
|
-
icon: { type: 'custom', customImage: '/logo.png', size: 'large' },
|
|
542
|
-
button: { primaryColor: '#4f46e5', hoverColor: '#4338ca' },
|
|
543
|
-
position: { vertical: 'bottom', horizontal: 'right' },
|
|
544
|
-
variables: { userId: 'user123', page: 'homepage' }
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
return () => {
|
|
548
|
-
if (widgetRef.current) {
|
|
549
|
-
widgetRef.current.destroy();
|
|
550
|
-
widgetRef.current = null;
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
}, []);
|
|
554
|
-
|
|
555
|
-
return null; // Widget renders itself
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
export default VoiceWidget;</div>
|
|
559
|
-
</div>
|
|
560
|
-
</div>
|
|
561
|
-
|
|
562
|
-
<div id="widget-vue" class="tab-content">
|
|
563
|
-
<div class="code-container">
|
|
564
|
-
<div class="code-header">
|
|
565
|
-
<span class="code-title">VoiceWidget.vue</span>
|
|
566
|
-
<button class="copy-button" onclick="copyToClipboard('widget-vue-code')">
|
|
567
|
-
📋 Copy to LLM
|
|
568
|
-
</button>
|
|
569
|
-
</div>
|
|
570
|
-
<div class="code-block" id="widget-vue-code"><template>
|
|
571
|
-
<div><!-- Widget renders itself --></div>
|
|
572
|
-
</template>
|
|
573
|
-
|
|
574
|
-
<script>
|
|
575
|
-
import { EnhancedAgentWidget } from 'ttp-agent-sdk';
|
|
576
|
-
|
|
577
|
-
export default {
|
|
578
|
-
name: 'VoiceWidget',
|
|
579
|
-
mounted() {
|
|
580
|
-
this.widget = new EnhancedAgentWidget({
|
|
581
|
-
agentId: 'your_agent_id',
|
|
582
|
-
getSessionUrl: async ({ agentId, variables }) => {
|
|
583
|
-
const response = await fetch('/api/get-voice-session', {
|
|
584
|
-
method: 'POST',
|
|
585
|
-
headers: { 'Content-Type': 'application/json' },
|
|
586
|
-
body: JSON.stringify({ agentId, variables })
|
|
587
|
-
});
|
|
588
|
-
const data = await response.json();
|
|
589
|
-
return data.signedUrl;
|
|
590
|
-
},
|
|
591
|
-
icon: { type: 'custom', customImage: '/logo.png', size: 'large' },
|
|
592
|
-
button: { primaryColor: '#4f46e5', hoverColor: '#4338ca' },
|
|
593
|
-
position: { vertical: 'bottom', horizontal: 'right' },
|
|
594
|
-
variables: { userId: 'user123', page: 'homepage' }
|
|
595
|
-
});
|
|
596
|
-
},
|
|
597
|
-
beforeUnmount() {
|
|
598
|
-
if (this.widget) {
|
|
599
|
-
this.widget.destroy();
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
};
|
|
603
|
-
</script></div>
|
|
604
|
-
</div>
|
|
605
|
-
</div>
|
|
606
|
-
|
|
607
|
-
<div id="widget-angular" class="tab-content">
|
|
608
|
-
<div class="code-container">
|
|
609
|
-
<div class="code-header">
|
|
610
|
-
<span class="code-title">voice-widget.component.ts</span>
|
|
611
|
-
<button class="copy-button" onclick="copyToClipboard('widget-angular-code')">
|
|
612
|
-
📋 Copy to LLM
|
|
613
|
-
</button>
|
|
614
|
-
</div>
|
|
615
|
-
<div class="code-block" id="widget-angular-code">import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
616
|
-
import { EnhancedAgentWidget } from 'ttp-agent-sdk';
|
|
617
|
-
|
|
618
|
-
@Component({
|
|
619
|
-
selector: 'app-voice-widget',
|
|
620
|
-
template: '<div><!-- Widget renders itself --></div>'
|
|
621
|
-
})
|
|
622
|
-
export class VoiceWidgetComponent implements OnInit, OnDestroy {
|
|
623
|
-
private widget: any;
|
|
624
|
-
|
|
625
|
-
ngOnInit() {
|
|
626
|
-
this.widget = new EnhancedAgentWidget({
|
|
627
|
-
agentId: 'your_agent_id',
|
|
628
|
-
getSessionUrl: async ({ agentId, variables }) => {
|
|
629
|
-
const response = await fetch('/api/get-voice-session', {
|
|
630
|
-
method: 'POST',
|
|
631
|
-
headers: { 'Content-Type': 'application/json' },
|
|
632
|
-
body: JSON.stringify({ agentId, variables })
|
|
633
|
-
});
|
|
634
|
-
const data = await response.json();
|
|
635
|
-
return data.signedUrl;
|
|
636
|
-
},
|
|
637
|
-
icon: { type: 'custom', customImage: '/logo.png', size: 'large' },
|
|
638
|
-
button: { primaryColor: '#4f46e5', hoverColor: '#4338ca' },
|
|
639
|
-
position: { vertical: 'bottom', horizontal: 'right' },
|
|
640
|
-
variables: { userId: 'user123', page: 'homepage' }
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
ngOnDestroy() {
|
|
645
|
-
if (this.widget) {
|
|
646
|
-
this.widget.destroy();
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
}</div>
|
|
650
|
-
</div>
|
|
651
|
-
</div>
|
|
652
|
-
</div>
|
|
653
|
-
|
|
654
|
-
<div class="section">
|
|
655
|
-
<h2>🔧 Method 2: Direct VoiceSDK Integration</h2>
|
|
656
|
-
<p>For developers who want full control over the voice interaction.</p>
|
|
657
|
-
|
|
658
|
-
<div class="tabs">
|
|
659
|
-
<button class="tab active" onclick="showTab('sdk-js')">Vanilla JS</button>
|
|
660
|
-
<button class="tab" onclick="showTab('sdk-react')">React</button>
|
|
661
|
-
<button class="tab" onclick="showTab('sdk-vue')">Vue</button>
|
|
662
|
-
<button class="tab" onclick="showTab('sdk-angular')">Angular</button>
|
|
663
|
-
</div>
|
|
664
|
-
|
|
665
|
-
<div id="sdk-js" class="tab-content active">
|
|
666
|
-
<div class="code-container">
|
|
667
|
-
<div class="code-header">
|
|
668
|
-
<span class="code-title">voice-app.js</span>
|
|
669
|
-
<button class="copy-button" onclick="copyToClipboard('sdk-js-code')">
|
|
670
|
-
📋 Copy to LLM
|
|
671
|
-
</button>
|
|
672
|
-
</div>
|
|
673
|
-
<div class="code-block" id="sdk-js-code"><!DOCTYPE html>
|
|
674
|
-
<html>
|
|
675
|
-
<head>
|
|
676
|
-
<title>Voice App</title>
|
|
677
|
-
</head>
|
|
678
|
-
<body>
|
|
679
|
-
<div id="app">
|
|
680
|
-
<h1>Voice Chat</h1>
|
|
681
|
-
<button id="connectBtn">Connect</button>
|
|
682
|
-
<button id="recordBtn" disabled>Start Recording</button>
|
|
683
|
-
<div id="messages"></div>
|
|
684
|
-
</div>
|
|
685
|
-
|
|
686
|
-
<script src="https://cdn.talktopc.com/ttp-agent-sdk/agent-widget.js"></script>
|
|
687
|
-
<script>
|
|
688
|
-
class VoiceApp {
|
|
689
|
-
constructor() {
|
|
690
|
-
this.isConnected = false;
|
|
691
|
-
this.isRecording = false;
|
|
692
|
-
this.messages = [];
|
|
693
|
-
this.voiceSDK = null;
|
|
694
|
-
|
|
695
|
-
this.init();
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
init() {
|
|
699
|
-
this.voiceSDK = new TTPAgentSDK.VoiceSDK({
|
|
700
|
-
websocketUrl: 'wss://speech.talktopc.com/ws/conv',
|
|
701
|
-
agentId: 'your_agent_id',
|
|
702
|
-
appId: 'your_app_id'
|
|
703
|
-
});
|
|
704
|
-
|
|
705
|
-
this.setupEventHandlers();
|
|
706
|
-
this.setupUI();
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
setupEventHandlers() {
|
|
710
|
-
this.voiceSDK.on('connected', () => {
|
|
711
|
-
this.isConnected = true;
|
|
712
|
-
this.updateUI();
|
|
713
|
-
this.addMessage('system', 'Connected to voice agent');
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
this.voiceSDK.on('disconnected', () => {
|
|
717
|
-
this.isConnected = false;
|
|
718
|
-
this.isRecording = false;
|
|
719
|
-
this.updateUI();
|
|
720
|
-
this.addMessage('system', 'Disconnected');
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
this.voiceSDK.on('recordingStarted', () => {
|
|
724
|
-
this.isRecording = true;
|
|
725
|
-
this.updateUI();
|
|
726
|
-
this.addMessage('user', '🎤 Recording...');
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
this.voiceSDK.on('recordingStopped', () => {
|
|
730
|
-
this.isRecording = false;
|
|
731
|
-
this.updateUI();
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
this.voiceSDK.on('message', (message) => {
|
|
735
|
-
if (message.type === 'agent_response') {
|
|
736
|
-
this.addMessage('agent', message.agent_response);
|
|
737
|
-
}
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
setupUI() {
|
|
742
|
-
document.getElementById('connectBtn').onclick = () => this.connect();
|
|
743
|
-
document.getElementById('recordBtn').onclick = () => this.toggleRecording();
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
async connect() {
|
|
747
|
-
try {
|
|
748
|
-
await this.voiceSDK.connect();
|
|
749
|
-
} catch (error) {
|
|
750
|
-
this.addMessage('error', `Connection failed: ${error.message}`);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
async toggleRecording() {
|
|
755
|
-
try {
|
|
756
|
-
if (this.isRecording) {
|
|
757
|
-
await this.voiceSDK.stopRecording();
|
|
758
|
-
} else {
|
|
759
|
-
await this.voiceSDK.startRecording();
|
|
760
|
-
}
|
|
761
|
-
} catch (error) {
|
|
762
|
-
this.addMessage('error', `Recording failed: ${error.message}`);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
addMessage(type, text) {
|
|
767
|
-
this.messages.push({ type, text, timestamp: new Date() });
|
|
768
|
-
this.renderMessages();
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
renderMessages() {
|
|
772
|
-
const container = document.getElementById('messages');
|
|
773
|
-
container.innerHTML = this.messages.map(msg => `
|
|
774
|
-
<div class="message ${msg.type}">
|
|
775
|
-
<strong>${msg.type}:</strong> ${msg.text}
|
|
776
|
-
<small>${msg.timestamp.toLocaleTimeString()}</small>
|
|
777
|
-
</div>
|
|
778
|
-
`).join('');
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
updateUI() {
|
|
782
|
-
const connectBtn = document.getElementById('connectBtn');
|
|
783
|
-
const recordBtn = document.getElementById('recordBtn');
|
|
784
|
-
|
|
785
|
-
connectBtn.disabled = this.isConnected;
|
|
786
|
-
connectBtn.textContent = this.isConnected ? 'Connected' : 'Connect';
|
|
787
|
-
|
|
788
|
-
recordBtn.disabled = !this.isConnected;
|
|
789
|
-
recordBtn.textContent = this.isRecording ? 'Stop Recording' : 'Start Recording';
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Initialize app
|
|
794
|
-
new VoiceApp();
|
|
795
|
-
</script>
|
|
796
|
-
</body>
|
|
797
|
-
</html></div>
|
|
798
|
-
</div>
|
|
799
|
-
</div>
|
|
800
|
-
|
|
801
|
-
<div id="sdk-react" class="tab-content">
|
|
802
|
-
<div class="code-container">
|
|
803
|
-
<div class="code-header">
|
|
804
|
-
<span class="code-title">VoiceChat.jsx</span>
|
|
805
|
-
<button class="copy-button" onclick="copyToClipboard('sdk-react-code')">
|
|
806
|
-
📋 Copy to LLM
|
|
807
|
-
</button>
|
|
808
|
-
</div>
|
|
809
|
-
<div class="code-block" id="sdk-react-code">import React, { useState, useEffect, useRef } from 'react';
|
|
810
|
-
import { VoiceSDK } from 'ttp-agent-sdk';
|
|
811
|
-
|
|
812
|
-
function VoiceChat() {
|
|
813
|
-
const [isConnected, setIsConnected] = useState(false);
|
|
814
|
-
const [isRecording, setIsRecording] = useState(false);
|
|
815
|
-
const [messages, setMessages] = useState([]);
|
|
816
|
-
const voiceSDKRef = useRef(null);
|
|
817
|
-
|
|
818
|
-
useEffect(() => {
|
|
819
|
-
const voiceSDK = new VoiceSDK({
|
|
820
|
-
websocketUrl: 'wss://speech.talktopc.com/ws/conv',
|
|
821
|
-
agentId: 'your_agent_id',
|
|
822
|
-
appId: 'your_app_id'
|
|
823
|
-
});
|
|
824
|
-
|
|
825
|
-
voiceSDK.on('connected', () => setIsConnected(true));
|
|
826
|
-
voiceSDK.on('disconnected', () => setIsConnected(false));
|
|
827
|
-
voiceSDK.on('recordingStarted', () => setIsRecording(true));
|
|
828
|
-
voiceSDK.on('recordingStopped', () => setIsRecording(false));
|
|
829
|
-
|
|
830
|
-
voiceSDK.on('message', (message) => {
|
|
831
|
-
if (message.type === 'agent_response') {
|
|
832
|
-
setMessages(prev => [...prev, { type: 'agent', text: message.agent_response }]);
|
|
833
|
-
}
|
|
834
|
-
});
|
|
835
|
-
|
|
836
|
-
voiceSDKRef.current = voiceSDK;
|
|
837
|
-
return () => voiceSDK.destroy();
|
|
838
|
-
}, []);
|
|
839
|
-
|
|
840
|
-
const handleConnect = async () => {
|
|
841
|
-
await voiceSDKRef.current.connect();
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
const handleToggleRecording = async () => {
|
|
845
|
-
if (isRecording) {
|
|
846
|
-
await voiceSDKRef.current.stopRecording();
|
|
847
|
-
} else {
|
|
848
|
-
await voiceSDKRef.current.startRecording();
|
|
849
|
-
}
|
|
850
|
-
};
|
|
851
|
-
|
|
852
|
-
return (
|
|
853
|
-
<div>
|
|
854
|
-
<button onClick={handleConnect} disabled={isConnected}>
|
|
855
|
-
{isConnected ? 'Connected' : 'Connect'}
|
|
856
|
-
</button>
|
|
857
|
-
<button onClick={handleToggleRecording} disabled={!isConnected}>
|
|
858
|
-
{isRecording ? 'Stop Recording' : 'Start Recording'}
|
|
859
|
-
</button>
|
|
860
|
-
<div>
|
|
861
|
-
{messages.map((msg, i) => (
|
|
862
|
-
<div key={i}><strong>{msg.type}:</strong> {msg.text}</div>
|
|
863
|
-
))}
|
|
864
|
-
</div>
|
|
865
|
-
</div>
|
|
866
|
-
);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
export default VoiceChat;</div>
|
|
870
|
-
</div>
|
|
871
|
-
</div>
|
|
872
|
-
|
|
873
|
-
<div id="sdk-vue" class="tab-content">
|
|
874
|
-
<div class="code-container">
|
|
875
|
-
<div class="code-header">
|
|
876
|
-
<span class="code-title">VoiceChat.vue</span>
|
|
877
|
-
<button class="copy-button" onclick="copyToClipboard('sdk-vue-code')">
|
|
878
|
-
📋 Copy to LLM
|
|
879
|
-
</button>
|
|
880
|
-
</div>
|
|
881
|
-
<div class="code-block" id="sdk-vue-code"><template>
|
|
882
|
-
<div>
|
|
883
|
-
<h1>Voice Chat</h1>
|
|
884
|
-
<button @click="connect" :disabled="isConnected">
|
|
885
|
-
{{ isConnected ? 'Connected' : 'Connect' }}
|
|
886
|
-
</button>
|
|
887
|
-
<button @click="toggleRecording" :disabled="!isConnected">
|
|
888
|
-
{{ isRecording ? 'Stop Recording' : 'Start Recording' }}
|
|
889
|
-
</button>
|
|
890
|
-
<div>
|
|
891
|
-
<div v-for="(msg, i) in messages" :key="i">
|
|
892
|
-
<strong>{{ msg.type }}:</strong> {{ msg.text }}
|
|
893
|
-
</div>
|
|
894
|
-
</div>
|
|
895
|
-
</div>
|
|
896
|
-
</template>
|
|
897
|
-
|
|
898
|
-
<script>
|
|
899
|
-
import { VoiceSDK } from 'ttp-agent-sdk';
|
|
900
|
-
|
|
901
|
-
export default {
|
|
902
|
-
name: 'VoiceChat',
|
|
903
|
-
data() {
|
|
904
|
-
return {
|
|
905
|
-
isConnected: false,
|
|
906
|
-
isRecording: false,
|
|
907
|
-
messages: [],
|
|
908
|
-
voiceSDK: null
|
|
909
|
-
};
|
|
910
|
-
},
|
|
911
|
-
mounted() {
|
|
912
|
-
this.voiceSDK = new VoiceSDK({
|
|
913
|
-
websocketUrl: 'wss://speech.talktopc.com/ws/conv',
|
|
914
|
-
agentId: 'your_agent_id',
|
|
915
|
-
appId: 'your_app_id'
|
|
916
|
-
});
|
|
917
|
-
|
|
918
|
-
this.voiceSDK.on('connected', () => this.isConnected = true);
|
|
919
|
-
this.voiceSDK.on('disconnected', () => this.isConnected = false);
|
|
920
|
-
this.voiceSDK.on('recordingStarted', () => this.isRecording = true);
|
|
921
|
-
this.voiceSDK.on('recordingStopped', () => this.isRecording = false);
|
|
922
|
-
|
|
923
|
-
this.voiceSDK.on('message', (message) => {
|
|
924
|
-
if (message.type === 'agent_response') {
|
|
925
|
-
this.messages.push({ type: 'agent', text: message.agent_response });
|
|
926
|
-
}
|
|
927
|
-
});
|
|
928
|
-
},
|
|
929
|
-
beforeUnmount() {
|
|
930
|
-
if (this.voiceSDK) {
|
|
931
|
-
this.voiceSDK.destroy();
|
|
932
|
-
}
|
|
933
|
-
},
|
|
934
|
-
methods: {
|
|
935
|
-
async connect() {
|
|
936
|
-
await this.voiceSDK.connect();
|
|
937
|
-
},
|
|
938
|
-
async toggleRecording() {
|
|
939
|
-
if (this.isRecording) {
|
|
940
|
-
await this.voiceSDK.stopRecording();
|
|
941
|
-
} else {
|
|
942
|
-
await this.voiceSDK.startRecording();
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
</script></div>
|
|
948
|
-
</div>
|
|
949
|
-
</div>
|
|
950
|
-
|
|
951
|
-
<div id="sdk-angular" class="tab-content">
|
|
952
|
-
<div class="code-container">
|
|
953
|
-
<div class="code-header">
|
|
954
|
-
<span class="code-title">voice-chat.component.ts</span>
|
|
955
|
-
<button class="copy-button" onclick="copyToClipboard('sdk-angular-code')">
|
|
956
|
-
📋 Copy to LLM
|
|
957
|
-
</button>
|
|
958
|
-
</div>
|
|
959
|
-
<div class="code-block" id="sdk-angular-code">import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
960
|
-
import { VoiceSDK } from 'ttp-agent-sdk';
|
|
961
|
-
|
|
962
|
-
@Component({
|
|
963
|
-
selector: 'app-voice-chat',
|
|
964
|
-
template: `
|
|
965
|
-
<div>
|
|
966
|
-
<h1>Voice Chat</h1>
|
|
967
|
-
<button (click)="connect()" [disabled]="isConnected">
|
|
968
|
-
{{ isConnected ? 'Connected' : 'Connect' }}
|
|
969
|
-
</button>
|
|
970
|
-
<button (click)="toggleRecording()" [disabled]="!isConnected">
|
|
971
|
-
{{ isRecording ? 'Stop Recording' : 'Start Recording' }}
|
|
972
|
-
</button>
|
|
973
|
-
<div>
|
|
974
|
-
<div *ngFor="let msg of messages; let i = index">
|
|
975
|
-
<strong>{{ msg.type }}:</strong> {{ msg.text }}
|
|
976
|
-
</div>
|
|
977
|
-
</div>
|
|
978
|
-
</div>
|
|
979
|
-
`
|
|
980
|
-
})
|
|
981
|
-
export class VoiceChatComponent implements OnInit, OnDestroy {
|
|
982
|
-
isConnected = false;
|
|
983
|
-
isRecording = false;
|
|
984
|
-
messages: any[] = [];
|
|
985
|
-
private voiceSDK: any;
|
|
986
|
-
|
|
987
|
-
ngOnInit() {
|
|
988
|
-
this.voiceSDK = new VoiceSDK({
|
|
989
|
-
websocketUrl: 'wss://speech.talktopc.com/ws/conv',
|
|
990
|
-
agentId: 'your_agent_id',
|
|
991
|
-
appId: 'your_app_id'
|
|
992
|
-
});
|
|
993
|
-
|
|
994
|
-
this.voiceSDK.on('connected', () => this.isConnected = true);
|
|
995
|
-
this.voiceSDK.on('disconnected', () => this.isConnected = false);
|
|
996
|
-
this.voiceSDK.on('recordingStarted', () => this.isRecording = true);
|
|
997
|
-
this.voiceSDK.on('recordingStopped', () => this.isRecording = false);
|
|
998
|
-
|
|
999
|
-
this.voiceSDK.on('message', (message: any) => {
|
|
1000
|
-
if (message.type === 'agent_response') {
|
|
1001
|
-
this.messages.push({ type: 'agent', text: message.agent_response });
|
|
1002
|
-
}
|
|
1003
|
-
});
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
ngOnDestroy() {
|
|
1007
|
-
if (this.voiceSDK) {
|
|
1008
|
-
this.voiceSDK.destroy();
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
async connect() {
|
|
1013
|
-
await this.voiceSDK.connect();
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
async toggleRecording() {
|
|
1017
|
-
if (this.isRecording) {
|
|
1018
|
-
await this.voiceSDK.stopRecording();
|
|
1019
|
-
} else {
|
|
1020
|
-
await this.voiceSDK.startRecording();
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
}</div>
|
|
1024
|
-
</div>
|
|
1025
|
-
</div>
|
|
1026
|
-
</div>
|
|
1027
|
-
|
|
1028
|
-
<div class="feature-grid">
|
|
1029
|
-
<div class="feature-card">
|
|
1030
|
-
<h3><span class="platform-badge">HTML/JS</span>Vanilla JavaScript</h3>
|
|
1031
|
-
<p>Works with any HTML page. No build tools required. Perfect for simple websites.</p>
|
|
1032
|
-
</div>
|
|
1033
|
-
|
|
1034
|
-
<div class="feature-card">
|
|
1035
|
-
<h3><span class="platform-badge">React</span>React Integration</h3>
|
|
1036
|
-
<p>Full React component integration with hooks and lifecycle management.</p>
|
|
1037
|
-
</div>
|
|
1038
|
-
|
|
1039
|
-
<div class="feature-card">
|
|
1040
|
-
<h3><span class="platform-badge">Vue</span>Vue.js Integration</h3>
|
|
1041
|
-
<p>Vue component examples with proper lifecycle hooks and reactive data.</p>
|
|
1042
|
-
</div>
|
|
1043
|
-
|
|
1044
|
-
<div class="feature-card">
|
|
1045
|
-
<h3><span class="platform-badge">Angular</span>Angular Integration</h3>
|
|
1046
|
-
<p>Angular component examples with TypeScript and proper dependency injection.</p>
|
|
1047
|
-
</div>
|
|
1048
|
-
</div>
|
|
1049
|
-
|
|
1050
|
-
<div class="note">
|
|
1051
|
-
<strong>💡 Recommendation:</strong> For most use cases, use Method 1 (Enhanced AgentWidget) as it provides
|
|
1052
|
-
the best user experience with minimal code. Use Method 2 (Direct VoiceSDK) only when you need
|
|
1053
|
-
custom UI or advanced control over the voice interaction.
|
|
1054
|
-
</div>
|
|
1055
|
-
</div>
|
|
1056
|
-
|
|
1057
|
-
<script>
|
|
1058
|
-
function showTab(tabId) {
|
|
1059
|
-
// Hide all tab contents
|
|
1060
|
-
document.querySelectorAll('.tab-content').forEach(content => {
|
|
1061
|
-
content.classList.remove('active');
|
|
1062
|
-
});
|
|
1063
|
-
|
|
1064
|
-
// Remove active class from all tabs
|
|
1065
|
-
document.querySelectorAll('.tab').forEach(tab => {
|
|
1066
|
-
tab.classList.remove('active');
|
|
1067
|
-
});
|
|
1068
|
-
|
|
1069
|
-
// Show selected tab content
|
|
1070
|
-
document.getElementById(tabId).classList.add('active');
|
|
1071
|
-
|
|
1072
|
-
// Add active class to clicked tab
|
|
1073
|
-
event.target.classList.add('active');
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
function copyToClipboard(elementId) {
|
|
1077
|
-
const element = document.getElementById(elementId);
|
|
1078
|
-
const text = element.textContent;
|
|
1079
|
-
|
|
1080
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
1081
|
-
const button = element.parentElement.querySelector('.copy-button');
|
|
1082
|
-
const originalText = button.textContent;
|
|
1083
|
-
|
|
1084
|
-
button.textContent = '✅ Copied!';
|
|
1085
|
-
button.classList.add('copied');
|
|
1086
|
-
|
|
1087
|
-
setTimeout(() => {
|
|
1088
|
-
button.textContent = originalText;
|
|
1089
|
-
button.classList.remove('copied');
|
|
1090
|
-
}, 2000);
|
|
1091
|
-
}).catch(err => {
|
|
1092
|
-
console.error('Failed to copy: ', err);
|
|
1093
|
-
alert('Failed to copy to clipboard');
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
function copyLLMInstructions() {
|
|
1098
|
-
const element = document.getElementById('llm-instructions-content');
|
|
1099
|
-
const text = element.textContent;
|
|
1100
|
-
|
|
1101
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
1102
|
-
const button = document.querySelector('.llm-copy-button');
|
|
1103
|
-
const originalText = button.textContent;
|
|
1104
|
-
|
|
1105
|
-
button.textContent = '✅ Copied to LLM!';
|
|
1106
|
-
button.classList.add('copied');
|
|
1107
|
-
|
|
1108
|
-
setTimeout(() => {
|
|
1109
|
-
button.textContent = originalText;
|
|
1110
|
-
button.classList.remove('copied');
|
|
1111
|
-
}, 3000);
|
|
1112
|
-
}).catch(err => {
|
|
1113
|
-
console.error('Failed to copy: ', err);
|
|
1114
|
-
alert('Failed to copy instructions to clipboard');
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
</script>
|
|
1118
|
-
</body>
|
|
1119
|
-
</html>
|