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.
@@ -1,774 +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>React VoiceSDK Example - TTP Agent SDK</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
- .code-container {
87
- position: relative;
88
- background: #1e293b;
89
- border-radius: 12px;
90
- overflow: hidden;
91
- margin: 20px 0;
92
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
93
- }
94
-
95
- .code-header {
96
- background: #334155;
97
- padding: 12px 20px;
98
- display: flex;
99
- justify-content: space-between;
100
- align-items: center;
101
- border-bottom: 1px solid #475569;
102
- }
103
-
104
- .code-title {
105
- color: #e2e8f0;
106
- font-weight: 600;
107
- font-size: 14px;
108
- }
109
-
110
- .copy-button {
111
- background: #4f46e5;
112
- color: white;
113
- border: none;
114
- padding: 8px 16px;
115
- border-radius: 6px;
116
- font-size: 12px;
117
- font-weight: 600;
118
- cursor: pointer;
119
- transition: all 0.2s;
120
- display: flex;
121
- align-items: center;
122
- gap: 6px;
123
- }
124
-
125
- .copy-button:hover {
126
- background: #4338ca;
127
- transform: translateY(-1px);
128
- }
129
-
130
- .copy-button.copied {
131
- background: #10b981;
132
- }
133
-
134
- .code-block {
135
- background: #1e293b;
136
- color: #e2e8f0;
137
- padding: 30px;
138
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
139
- font-size: 14px;
140
- line-height: 1.6;
141
- overflow-x: auto;
142
- white-space: pre;
143
- }
144
-
145
- .note {
146
- background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
147
- border-left: 4px solid #f59e0b;
148
- padding: 20px;
149
- margin: 30px 0;
150
- border-radius: 8px;
151
- }
152
-
153
- .note strong {
154
- color: #92400e;
155
- }
156
-
157
- .feature-grid {
158
- display: grid;
159
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
160
- gap: 20px;
161
- margin: 30px 0;
162
- }
163
-
164
- .feature-card {
165
- background: #f8fafc;
166
- padding: 24px;
167
- border-radius: 12px;
168
- border: 1px solid #e2e8f0;
169
- transition: all 0.2s;
170
- }
171
-
172
- .feature-card:hover {
173
- transform: translateY(-2px);
174
- box-shadow: 0 8px 25px rgba(0,0,0,0.1);
175
- }
176
-
177
- .feature-card h3 {
178
- color: #4f46e5;
179
- margin-top: 0;
180
- font-size: 1.2rem;
181
- }
182
-
183
- .step {
184
- background: #f1f5f9;
185
- padding: 20px;
186
- border-radius: 8px;
187
- margin: 20px 0;
188
- border-left: 4px solid #4f46e5;
189
- }
190
-
191
- .step-number {
192
- background: #4f46e5;
193
- color: white;
194
- width: 24px;
195
- height: 24px;
196
- border-radius: 50%;
197
- display: inline-flex;
198
- align-items: center;
199
- justify-content: center;
200
- font-weight: bold;
201
- margin-right: 12px;
202
- }
203
- </style>
204
- </head>
205
- <body>
206
- <a href="../index.html" class="back-link">
207
- ← Back to SDK Home
208
- </a>
209
-
210
- <div class="container">
211
- <h1>⚛️ React VoiceSDK Integration</h1>
212
- <p class="subtitle">Clean, readable examples for integrating TTP Agent SDK with React</p>
213
-
214
- <div class="info">
215
- <strong>📋 Prerequisites:</strong> This guide assumes you have a React development environment set up.
216
- Copy the code examples below and customize them for your application.
217
- </div>
218
-
219
- <div class="section">
220
- <h2>🚀 Quick Start</h2>
221
-
222
- <div class="step">
223
- <span class="step-number">1</span>
224
- <strong>Install the SDK:</strong>
225
- <div class="code-container">
226
- <div class="code-header">
227
- <span class="code-title">Terminal</span>
228
- <button class="copy-button" onclick="copyToClipboard('install-command')">
229
- 📋 Copy
230
- </button>
231
- </div>
232
- <div class="code-block" id="install-command">npm install ttp-agent-sdk react react-dom</div>
233
- </div>
234
- </div>
235
-
236
- <div class="step">
237
- <span class="step-number">2</span>
238
- <strong>Choose your integration method:</strong>
239
- </div>
240
- </div>
241
-
242
- <div class="section">
243
- <h2>🎯 Method 1: Basic VoiceSDK Integration</h2>
244
-
245
- <div class="code-container">
246
- <div class="code-header">
247
- <span class="code-title">VoiceChat.jsx</span>
248
- <button class="copy-button" onclick="copyToClipboard('basic-example')">
249
- 📋 Copy to LLM
250
- </button>
251
- </div>
252
- <div class="code-block" id="basic-example">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.talktopc.com/ws/conv',
264
- agentId: 'your_agent_id',
265
- appId: 'your_app_id'
266
- });
267
-
268
- // Event handlers
269
- voiceSDK.on('connected', () => setIsConnected(true));
270
- voiceSDK.on('disconnected', () => setIsConnected(false));
271
- voiceSDK.on('recordingStarted', () => setIsRecording(true));
272
- voiceSDK.on('recordingStopped', () => setIsRecording(false));
273
-
274
- voiceSDK.on('message', (message) => {
275
- if (message.type === 'agent_response') {
276
- setMessages(prev => [...prev, {
277
- type: 'agent',
278
- text: message.agent_response
279
- }]);
280
- }
281
- });
282
-
283
- voiceSDKRef.current = voiceSDK;
284
-
285
- return () => voiceSDK.destroy();
286
- }, []);
287
-
288
- const handleConnect = async () => {
289
- await voiceSDKRef.current.connect();
290
- };
291
-
292
- const handleToggleRecording = async () => {
293
- if (isRecording) {
294
- await voiceSDKRef.current.stopRecording();
295
- } else {
296
- await voiceSDKRef.current.startRecording();
297
- }
298
- };
299
-
300
- return (
301
- <div>
302
- <button onClick={handleConnect} disabled={isConnected}>
303
- {isConnected ? 'Connected' : 'Connect'}
304
- </button>
305
-
306
- <button onClick={handleToggleRecording} disabled={!isConnected}>
307
- {isRecording ? 'Stop Recording' : 'Start Recording'}
308
- </button>
309
-
310
- <div>
311
- {messages.map((msg, i) => (
312
- <div key={i}>
313
- <strong>{msg.type}:</strong> {msg.text}
314
- </div>
315
- ))}
316
- </div>
317
- </div>
318
- );
319
- }
320
-
321
- export default VoiceChat;</div>
322
- </div>
323
- </div>
324
-
325
- <div class="section">
326
- <h2>🎨 Method 2: Using VoiceButton Component</h2>
327
-
328
- <div class="code-container">
329
- <div class="code-header">
330
- <span class="code-title">App.jsx</span>
331
- <button class="copy-button" onclick="copyToClipboard('voicebutton-example')">
332
- 📋 Copy to LLM
333
- </button>
334
- </div>
335
- <div class="code-block" id="voicebutton-example">import React from 'react';
336
- import { VoiceButton } from 'ttp-agent-sdk';
337
-
338
- function App() {
339
- return (
340
- <div>
341
- <h1>My Voice App</h1>
342
-
343
- <VoiceButton
344
- websocketUrl="wss://speech.talktopc.com/ws/conv"
345
- agentId="your_agent_id"
346
- appId="your_app_id"
347
- onConnected={() => console.log('Connected!')}
348
- onRecordingStarted={() => console.log('Recording...')}
349
- onPlaybackStarted={() => console.log('Playing audio...')}
350
- />
351
- </div>
352
- );
353
- }
354
-
355
- export default App;</div>
356
- </div>
357
- </div>
358
-
359
- <div class="section">
360
- <h2>🔧 Method 3: Enhanced AgentWidget (Recommended)</h2>
361
-
362
- <div class="code-container">
363
- <div class="code-header">
364
- <span class="code-title">VoiceWidget.jsx</span>
365
- <button class="copy-button" onclick="copyToClipboard('enhanced-example')">
366
- 📋 Copy to LLM
367
- </button>
368
- </div>
369
- <div class="code-block" id="enhanced-example">import React, { useEffect, useRef } from 'react';
370
- import { EnhancedAgentWidget } from 'ttp-agent-sdk';
371
-
372
- function VoiceWidget() {
373
- const widgetRef = useRef(null);
374
-
375
- useEffect(() => {
376
- if (widgetRef.current) return;
377
-
378
- widgetRef.current = new EnhancedAgentWidget({
379
- agentId: 'your_agent_id',
380
-
381
- // Get signed URL from your backend
382
- getSessionUrl: async ({ agentId, variables }) => {
383
- const response = await fetch('/api/get-voice-session', {
384
- method: 'POST',
385
- headers: {
386
- 'Content-Type': 'application/json',
387
- 'Authorization': `Bearer ${localStorage.getItem('token')}`
388
- },
389
- body: JSON.stringify({ agentId, variables })
390
- });
391
-
392
- const data = await response.json();
393
- return data.signedUrl;
394
- },
395
-
396
- // Customize the widget
397
- icon: {
398
- type: 'custom',
399
- customImage: '/your-logo.png',
400
- size: 'large'
401
- },
402
-
403
- button: {
404
- primaryColor: '#4f46e5',
405
- hoverColor: '#4338ca',
406
- size: 'large'
407
- },
408
-
409
- position: {
410
- vertical: 'bottom',
411
- horizontal: 'right',
412
- offset: { x: 30, y: 30 }
413
- },
414
-
415
- variables: {
416
- userId: localStorage.getItem('userId'),
417
- page: 'homepage'
418
- }
419
- });
420
-
421
- return () => {
422
- if (widgetRef.current) {
423
- widgetRef.current.destroy();
424
- widgetRef.current = null;
425
- }
426
- };
427
- }, []);
428
-
429
- return null; // Widget renders itself
430
- }
431
-
432
- export default VoiceWidget;</div>
433
- </div>
434
- </div>
435
-
436
- <div class="section">
437
- <h2>🏗️ Complete App Example</h2>
438
-
439
- <div class="code-container">
440
- <div class="code-header">
441
- <span class="code-title">CompleteVoiceApp.jsx</span>
442
- <button class="copy-button" onclick="copyToClipboard('complete-example')">
443
- 📋 Copy to LLM
444
- </button>
445
- </div>
446
- <div class="code-block" id="complete-example">import React, { useState, useEffect, useRef } from 'react';
447
- import { VoiceSDK } from 'ttp-agent-sdk';
448
-
449
- function CompleteVoiceApp() {
450
- const [isConnected, setIsConnected] = useState(false);
451
- const [isRecording, setIsRecording] = useState(false);
452
- const [isPlaying, setIsPlaying] = useState(false);
453
- const [messages, setMessages] = useState([]);
454
- const [connectionStatus, setConnectionStatus] = useState('Disconnected');
455
-
456
- const voiceSDKRef = useRef(null);
457
-
458
- useEffect(() => {
459
- const voiceSDK = new VoiceSDK({
460
- websocketUrl: 'wss://speech.talktopc.com/ws/conv',
461
- agentId: 'your_agent_id',
462
- appId: 'your_app_id',
463
- voice: 'default',
464
- language: 'en',
465
- autoReconnect: true
466
- });
467
-
468
- // Comprehensive event handlers
469
- voiceSDK.on('connected', () => {
470
- setIsConnected(true);
471
- setConnectionStatus('Connected');
472
- addMessage('system', 'Connected to voice agent');
473
- });
474
-
475
- voiceSDK.on('disconnected', () => {
476
- setIsConnected(false);
477
- setIsRecording(false);
478
- setIsPlaying(false);
479
- setConnectionStatus('Disconnected');
480
- addMessage('system', 'Disconnected from voice agent');
481
- });
482
-
483
- voiceSDK.on('recordingStarted', () => {
484
- setIsRecording(true);
485
- addMessage('user', '🎤 Recording...');
486
- });
487
-
488
- voiceSDK.on('recordingStopped', () => {
489
- setIsRecording(false);
490
- addMessage('user', '⏹️ Recording stopped');
491
- });
492
-
493
- voiceSDK.on('playbackStarted', () => {
494
- setIsPlaying(true);
495
- addMessage('agent', '🔊 Agent is speaking...');
496
- });
497
-
498
- voiceSDK.on('playbackStopped', () => {
499
- setIsPlaying(false);
500
- });
501
-
502
- voiceSDK.on('message', (message) => {
503
- if (message.type === 'agent_response') {
504
- addMessage('agent', message.agent_response);
505
- } else if (message.type === 'user_transcript') {
506
- addMessage('user', message.user_transcription);
507
- }
508
- });
509
-
510
- voiceSDK.on('error', (error) => {
511
- console.error('VoiceSDK Error:', error);
512
- addMessage('error', `Error: ${error.message}`);
513
- });
514
-
515
- voiceSDKRef.current = voiceSDK;
516
-
517
- return () => {
518
- if (voiceSDKRef.current) {
519
- voiceSDKRef.current.destroy();
520
- }
521
- };
522
- }, []);
523
-
524
- const addMessage = (type, text) => {
525
- setMessages(prev => [...prev, {
526
- type,
527
- text,
528
- timestamp: new Date()
529
- }]);
530
- };
531
-
532
- const handleConnect = async () => {
533
- if (voiceSDKRef.current) {
534
- try {
535
- setConnectionStatus('Connecting...');
536
- await voiceSDKRef.current.connect();
537
- } catch (error) {
538
- console.error('Connection failed:', error);
539
- setConnectionStatus('Connection failed');
540
- }
541
- }
542
- };
543
-
544
- const handleDisconnect = () => {
545
- if (voiceSDKRef.current) {
546
- voiceSDKRef.current.disconnect();
547
- }
548
- };
549
-
550
- const handleToggleRecording = async () => {
551
- if (voiceSDKRef.current) {
552
- try {
553
- if (isRecording) {
554
- await voiceSDKRef.current.stopRecording();
555
- } else {
556
- await voiceSDKRef.current.startRecording();
557
- }
558
- } catch (error) {
559
- console.error('Recording toggle failed:', error);
560
- }
561
- }
562
- };
563
-
564
- return (
565
- <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
566
- <h1>🎤 Voice Chat App</h1>
567
-
568
- {/* Status Indicator */}
569
- <div style={{
570
- padding: '12px',
571
- borderRadius: '8px',
572
- marginBottom: '20px',
573
- backgroundColor: isConnected ? '#d1fae5' : '#fee2e2',
574
- color: isConnected ? '#065f46' : '#991b1b',
575
- border: `1px solid ${isConnected ? '#10b981' : '#ef4444'}`
576
- }}>
577
- <strong>Status:</strong> {connectionStatus}
578
- </div>
579
-
580
- {/* Control Buttons */}
581
- <div style={{ display: 'flex', gap: '12px', marginBottom: '20px' }}>
582
- <button
583
- onClick={handleConnect}
584
- disabled={isConnected}
585
- style={{
586
- padding: '12px 24px',
587
- backgroundColor: isConnected ? '#9ca3af' : '#4f46e5',
588
- color: 'white',
589
- border: 'none',
590
- borderRadius: '6px',
591
- cursor: isConnected ? 'not-allowed' : 'pointer'
592
- }}
593
- >
594
- Connect
595
- </button>
596
-
597
- <button
598
- onClick={handleDisconnect}
599
- disabled={!isConnected}
600
- style={{
601
- padding: '12px 24px',
602
- backgroundColor: !isConnected ? '#9ca3af' : '#ef4444',
603
- color: 'white',
604
- border: 'none',
605
- borderRadius: '6px',
606
- cursor: !isConnected ? 'not-allowed' : 'pointer'
607
- }}
608
- >
609
- Disconnect
610
- </button>
611
-
612
- <button
613
- onClick={handleToggleRecording}
614
- disabled={!isConnected}
615
- style={{
616
- padding: '12px 24px',
617
- backgroundColor: !isConnected ? '#9ca3af' : (isRecording ? '#ef4444' : '#10b981'),
618
- color: 'white',
619
- border: 'none',
620
- borderRadius: '6px',
621
- cursor: !isConnected ? 'not-allowed' : 'pointer'
622
- }}
623
- >
624
- {isRecording ? 'Stop Recording' : 'Start Recording'}
625
- </button>
626
- </div>
627
-
628
- {/* Messages Area */}
629
- <div style={{
630
- border: '1px solid #e5e7eb',
631
- borderRadius: '8px',
632
- height: '400px',
633
- overflowY: 'auto',
634
- padding: '16px',
635
- backgroundColor: '#f9fafb'
636
- }}>
637
- <h3>Messages:</h3>
638
- {messages.length === 0 ? (
639
- <p style={{ color: '#6b7280', fontStyle: 'italic' }}>
640
- No messages yet. Connect and start recording to begin the conversation.
641
- </p>
642
- ) : (
643
- messages.map((message, index) => (
644
- <div
645
- key={index}
646
- style={{
647
- marginBottom: '12px',
648
- padding: '8px 12px',
649
- borderRadius: '6px',
650
- backgroundColor: message.type === 'user' ? '#e5e7eb' :
651
- message.type === 'agent' ? '#f3f4f6' :
652
- message.type === 'error' ? '#fee2e2' : '#eff6ff',
653
- color: message.type === 'error' ? '#991b1b' : '#111827',
654
- alignSelf: message.type === 'user' ? 'flex-end' : 'flex-start',
655
- maxWidth: '80%',
656
- marginLeft: message.type === 'user' ? 'auto' : '0',
657
- marginRight: message.type === 'user' ? '0' : 'auto'
658
- }}
659
- >
660
- <div style={{ fontWeight: 'bold', marginBottom: '4px' }}>
661
- {message.type === 'user' ? '👤 You' :
662
- message.type === 'agent' ? '🤖 Agent' :
663
- message.type === 'error' ? '❌ Error' : 'ℹ️ System'}
664
- </div>
665
- <div>{message.text}</div>
666
- <div style={{
667
- fontSize: '12px',
668
- color: '#6b7280',
669
- marginTop: '4px'
670
- }}>
671
- {message.timestamp.toLocaleTimeString()}
672
- </div>
673
- </div>
674
- ))
675
- )}
676
- </div>
677
-
678
- {/* Status Indicators */}
679
- <div style={{
680
- display: 'flex',
681
- gap: '20px',
682
- marginTop: '20px',
683
- fontSize: '14px',
684
- color: '#6b7280'
685
- }}>
686
- <div>
687
- <span style={{
688
- display: 'inline-block',
689
- width: '8px',
690
- height: '8px',
691
- borderRadius: '50%',
692
- backgroundColor: isConnected ? '#10b981' : '#ef4444',
693
- marginRight: '8px'
694
- }}></span>
695
- Connection
696
- </div>
697
- <div>
698
- <span style={{
699
- display: 'inline-block',
700
- width: '8px',
701
- height: '8px',
702
- borderRadius: '50%',
703
- backgroundColor: isRecording ? '#ef4444' : '#9ca3af',
704
- marginRight: '8px'
705
- }}></span>
706
- Recording
707
- </div>
708
- <div>
709
- <span style={{
710
- display: 'inline-block',
711
- width: '8px',
712
- height: '8px',
713
- borderRadius: '50%',
714
- backgroundColor: isPlaying ? '#10b981' : '#9ca3af',
715
- marginRight: '8px'
716
- }}></span>
717
- Playing
718
- </div>
719
- </div>
720
- </div>
721
- );
722
- }
723
-
724
- export default CompleteVoiceApp;</div>
725
- </div>
726
- </div>
727
-
728
- <div class="feature-grid">
729
- <div class="feature-card">
730
- <h3>🎯 Method 1: Basic VoiceSDK</h3>
731
- <p>Direct integration with VoiceSDK class. Full control over events and state management.</p>
732
- </div>
733
-
734
- <div class="feature-card">
735
- <h3>🎨 Method 2: VoiceButton</h3>
736
- <p>Pre-built React component. Quick integration with minimal code.</p>
737
- </div>
738
-
739
- <div class="feature-card">
740
- <h3>🔧 Method 3: Enhanced Widget</h3>
741
- <p>Advanced customizable widget with signed link authentication. Production-ready.</p>
742
- </div>
743
- </div>
744
-
745
- <div class="note">
746
- <strong>💡 Pro Tip:</strong> For production applications, use Method 3 (Enhanced AgentWidget) with signed link authentication.
747
- It provides the best security, customization options, and user experience.
748
- </div>
749
- </div>
750
-
751
- <script>
752
- function copyToClipboard(elementId) {
753
- const element = document.getElementById(elementId);
754
- const text = element.textContent;
755
-
756
- navigator.clipboard.writeText(text).then(() => {
757
- const button = element.parentElement.querySelector('.copy-button');
758
- const originalText = button.textContent;
759
-
760
- button.textContent = '✅ Copied!';
761
- button.classList.add('copied');
762
-
763
- setTimeout(() => {
764
- button.textContent = originalText;
765
- button.classList.remove('copied');
766
- }, 2000);
767
- }).catch(err => {
768
- console.error('Failed to copy: ', err);
769
- alert('Failed to copy to clipboard');
770
- });
771
- }
772
- </script>
773
- </body>
774
- </html>