ttp-agent-sdk 2.0.1 → 2.0.2

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/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "ttp-agent-sdk",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Comprehensive Voice Agent SDK for web integration with real-time audio, WebSocket communication, and React components",
5
5
  "main": "dist/agent-widget.js",
6
- "module": "src/index.js",
6
+ "module": "dist/agent-widget.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "dist/",
10
- "src/",
11
10
  "examples/",
12
11
  "README.md",
13
12
  "GETTING_STARTED.md"
@@ -1,185 +0,0 @@
1
- /**
2
- * AudioPlayer - Handles audio playback with queue system
3
- */
4
- import EventEmitter from './EventEmitter.js';
5
-
6
- export default class AudioPlayer extends EventEmitter {
7
- constructor(config) {
8
- super();
9
- this.config = config;
10
- this.audioContext = null;
11
- this.audioQueue = [];
12
- this.isPlaying = false;
13
- this.isProcessingQueue = false;
14
- this.currentSource = null;
15
- }
16
-
17
- /**
18
- * Add audio data to playback queue
19
- */
20
- playAudio(audioData) {
21
- try {
22
- const audioBlob = this.createAudioBlob(audioData);
23
- this.audioQueue.push(audioBlob);
24
-
25
- // Process queue if not already playing or processing
26
- if (!this.isPlaying && !this.isProcessingQueue) {
27
- setTimeout(() => this.processQueue(), 50);
28
- }
29
- } catch (error) {
30
- this.emit('playbackError', error);
31
- }
32
- }
33
-
34
- /**
35
- * Create audio blob from ArrayBuffer
36
- */
37
- createAudioBlob(arrayBuffer) {
38
- const uint8Array = new Uint8Array(arrayBuffer);
39
-
40
- // Detect audio format
41
- if (uint8Array.length >= 4) {
42
- // WAV header (RIFF)
43
- if (uint8Array[0] === 0x52 && uint8Array[1] === 0x49 &&
44
- uint8Array[2] === 0x46 && uint8Array[3] === 0x46) {
45
- return new Blob([arrayBuffer], { type: 'audio/wav' });
46
- }
47
-
48
- // MP3 header
49
- if (uint8Array[0] === 0xFF && (uint8Array[1] & 0xE0) === 0xE0) {
50
- return new Blob([arrayBuffer], { type: 'audio/mpeg' });
51
- }
52
-
53
- // OGG header
54
- if (uint8Array[0] === 0x4F && uint8Array[1] === 0x67 &&
55
- uint8Array[2] === 0x67 && uint8Array[3] === 0x53) {
56
- return new Blob([arrayBuffer], { type: 'audio/ogg' });
57
- }
58
- }
59
-
60
- // Default to WAV format
61
- return new Blob([arrayBuffer], { type: 'audio/wav' });
62
- }
63
-
64
- /**
65
- * Process audio queue
66
- */
67
- async processQueue() {
68
- // Prevent multiple simultaneous queue processing
69
- if (this.isProcessingQueue || this.isPlaying || this.audioQueue.length === 0) {
70
- return;
71
- }
72
-
73
- this.isProcessingQueue = true;
74
-
75
- const audioBlob = this.audioQueue.shift();
76
- if (!audioBlob) {
77
- this.isProcessingQueue = false;
78
- return;
79
- }
80
-
81
- try {
82
- this.isPlaying = true;
83
- this.emit('playbackStarted');
84
-
85
- // Create AudioContext if not exists
86
- if (!this.audioContext) {
87
- this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
88
- }
89
-
90
- const audioContext = this.audioContext;
91
-
92
- // Resume AudioContext if suspended
93
- if (audioContext.state === 'suspended') {
94
- await audioContext.resume();
95
- }
96
-
97
- // Create audio source from blob
98
- const arrayBuffer = await audioBlob.arrayBuffer();
99
- const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
100
-
101
- const source = audioContext.createBufferSource();
102
- source.buffer = audioBuffer;
103
- source.connect(audioContext.destination);
104
-
105
- this.currentSource = source;
106
-
107
- // Handle audio end
108
- source.onended = () => {
109
- this.isPlaying = false;
110
- this.isProcessingQueue = false;
111
- this.currentSource = null;
112
- this.emit('playbackStopped');
113
-
114
- // Process next audio in queue if there are more items
115
- if (this.audioQueue.length > 0) {
116
- setTimeout(() => this.processQueue(), 100);
117
- }
118
- };
119
-
120
- // Start playback
121
- source.start();
122
-
123
- } catch (error) {
124
- this.isPlaying = false;
125
- this.isProcessingQueue = false;
126
- this.currentSource = null;
127
- this.emit('playbackError', error);
128
-
129
- // Try to process next audio in queue if there are more items
130
- if (this.audioQueue.length > 0) {
131
- setTimeout(() => this.processQueue(), 100);
132
- }
133
- }
134
- }
135
-
136
- /**
137
- * Stop current playback and clear queue
138
- */
139
- stop() {
140
- this.stopImmediate();
141
- }
142
-
143
- /**
144
- * Stop current playback immediately and clear queue
145
- */
146
- stopImmediate() {
147
- if (this.currentSource) {
148
- try {
149
- this.currentSource.stop();
150
- } catch (error) {
151
- // Ignore errors when stopping
152
- }
153
- this.currentSource = null;
154
- }
155
-
156
- this.isPlaying = false;
157
- this.isProcessingQueue = false;
158
- this.audioQueue = [];
159
- this.emit('playbackStopped');
160
- }
161
-
162
- /**
163
- * Get playback status
164
- */
165
- getStatus() {
166
- return {
167
- isPlaying: this.isPlaying,
168
- isProcessingQueue: this.isProcessingQueue,
169
- queueLength: this.audioQueue.length,
170
- audioContextState: this.audioContext ? this.audioContext.state : 'closed'
171
- };
172
- }
173
-
174
- /**
175
- * Cleanup resources
176
- */
177
- destroy() {
178
- this.stop();
179
-
180
- if (this.audioContext && this.audioContext.state !== 'closed') {
181
- this.audioContext.close();
182
- this.audioContext = null;
183
- }
184
- }
185
- }
@@ -1,128 +0,0 @@
1
- /**
2
- * AudioRecorder - Handles audio recording using AudioWorklet
3
- */
4
- import EventEmitter from './EventEmitter.js';
5
-
6
- export default class AudioRecorder extends EventEmitter {
7
- constructor(config) {
8
- super();
9
- this.config = config;
10
- this.audioContext = null;
11
- this.audioWorkletNode = null;
12
- this.mediaStream = null;
13
- this.isRecording = false;
14
- }
15
-
16
- /**
17
- * Start audio recording
18
- */
19
- async start() {
20
- try {
21
- // Get user media
22
- this.mediaStream = await navigator.mediaDevices.getUserMedia({
23
- audio: {
24
- sampleRate: this.config.sampleRate,
25
- channelCount: 1,
26
- echoCancellation: true,
27
- noiseSuppression: true,
28
- autoGainControl: true
29
- }
30
- });
31
-
32
- // Create AudioContext
33
- this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
34
- sampleRate: this.config.sampleRate
35
- });
36
-
37
- // Resume AudioContext if suspended
38
- if (this.audioContext.state === 'suspended') {
39
- await this.audioContext.resume();
40
- }
41
-
42
- // Load AudioWorklet module
43
- await this.audioContext.audioWorklet.addModule('/audio-processor.js');
44
-
45
- // Create AudioWorklet node
46
- this.audioWorkletNode = new AudioWorkletNode(this.audioContext, 'audio-processor');
47
-
48
- // Create media stream source
49
- const source = this.audioContext.createMediaStreamSource(this.mediaStream);
50
- source.connect(this.audioWorkletNode);
51
-
52
- // Handle messages from AudioWorklet
53
- this.audioWorkletNode.port.onmessage = (event) => {
54
- const { type, data } = event.data;
55
-
56
- if (type === 'pcm_audio_data') {
57
- this.emit('audioData', data);
58
- }
59
- };
60
-
61
- // Enable continuous mode
62
- this.audioWorkletNode.port.postMessage({
63
- type: 'setForceContinuous',
64
- data: { enabled: true }
65
- });
66
-
67
- this.isRecording = true;
68
- this.emit('recordingStarted');
69
-
70
- } catch (error) {
71
- this.emit('error', error);
72
- throw error;
73
- }
74
- }
75
-
76
- /**
77
- * Stop audio recording
78
- */
79
- async stop() {
80
- if (!this.isRecording) {
81
- return;
82
- }
83
-
84
- try {
85
- // Flush any remaining audio data
86
- if (this.audioWorkletNode) {
87
- this.audioWorkletNode.port.postMessage({ type: 'flush' });
88
- await new Promise(resolve => setTimeout(resolve, 100));
89
- }
90
-
91
- // Disconnect and cleanup
92
- if (this.mediaStream) {
93
- this.mediaStream.getTracks().forEach(track => track.stop());
94
- this.mediaStream = null;
95
- }
96
-
97
- if (this.audioContext && this.audioContext.state !== 'closed') {
98
- await this.audioContext.close();
99
- this.audioContext = null;
100
- }
101
-
102
- this.audioWorkletNode = null;
103
- this.isRecording = false;
104
- this.emit('recordingStopped');
105
-
106
- } catch (error) {
107
- this.emit('error', error);
108
- throw error;
109
- }
110
- }
111
-
112
- /**
113
- * Get recording status
114
- */
115
- getStatus() {
116
- return {
117
- isRecording: this.isRecording,
118
- audioContextState: this.audioContext ? this.audioContext.state : 'closed'
119
- };
120
- }
121
-
122
- /**
123
- * Cleanup resources
124
- */
125
- destroy() {
126
- this.stop();
127
- }
128
- }
@@ -1,86 +0,0 @@
1
- /**
2
- * ConnectionManager - Global connection manager to prevent multiple connections to the same URL
3
- */
4
- class ConnectionManager {
5
- constructor() {
6
- this.connections = new Map(); // Map of URL -> connection info
7
- }
8
-
9
- /**
10
- * Register a connection attempt
11
- */
12
- registerConnection(url, connectionId) {
13
- if (!this.connections.has(url)) {
14
- this.connections.set(url, {
15
- connectionId,
16
- timestamp: Date.now(),
17
- count: 1
18
- });
19
- console.log(`🔌 ConnectionManager: Registered connection ${connectionId} for ${url}`);
20
- return true;
21
- }
22
-
23
- const existing = this.connections.get(url);
24
- const timeSinceLastConnection = Date.now() - existing.timestamp;
25
-
26
- // If it's been more than 30 seconds since the last connection, allow it
27
- if (timeSinceLastConnection > 30000) {
28
- this.connections.set(url, {
29
- connectionId,
30
- timestamp: Date.now(),
31
- count: 1
32
- });
33
- console.log(`🔌 ConnectionManager: Allowed new connection ${connectionId} for ${url} (old connection was ${timeSinceLastConnection}ms ago)`);
34
- return true;
35
- }
36
-
37
- // Otherwise, prevent the connection
38
- existing.count++;
39
- console.log(`🔌 ConnectionManager: Blocked connection ${connectionId} for ${url} (${existing.count} attempts in ${timeSinceLastConnection}ms)`);
40
- return false;
41
- }
42
-
43
- /**
44
- * Unregister a connection
45
- */
46
- unregisterConnection(url, connectionId) {
47
- const existing = this.connections.get(url);
48
- if (existing && existing.connectionId === connectionId) {
49
- this.connections.delete(url);
50
- console.log(`🔌 ConnectionManager: Unregistered connection ${connectionId} for ${url}`);
51
- }
52
- }
53
-
54
- /**
55
- * Check if a connection is allowed
56
- */
57
- isConnectionAllowed(url) {
58
- const existing = this.connections.get(url);
59
- if (!existing) {
60
- return true;
61
- }
62
-
63
- const timeSinceLastConnection = Date.now() - existing.timestamp;
64
- return timeSinceLastConnection > 30000; // Allow if more than 30 seconds ago
65
- }
66
-
67
- /**
68
- * Get connection info
69
- */
70
- getConnectionInfo(url) {
71
- return this.connections.get(url);
72
- }
73
-
74
- /**
75
- * Clear all connections (useful for testing)
76
- */
77
- clearAll() {
78
- this.connections.clear();
79
- console.log('🔌 ConnectionManager: Cleared all connections');
80
- }
81
- }
82
-
83
- // Global instance
84
- const connectionManager = new ConnectionManager();
85
-
86
- export default connectionManager;
@@ -1,53 +0,0 @@
1
- /**
2
- * EventEmitter - Simple event system for the VoiceSDK
3
- */
4
- export default class EventEmitter {
5
- constructor() {
6
- this.events = {};
7
- }
8
-
9
- /**
10
- * Add event listener
11
- */
12
- on(event, callback) {
13
- if (!this.events[event]) {
14
- this.events[event] = [];
15
- }
16
- this.events[event].push(callback);
17
- }
18
-
19
- /**
20
- * Remove event listener
21
- */
22
- off(event, callback) {
23
- if (!this.events[event]) return;
24
-
25
- this.events[event] = this.events[event].filter(cb => cb !== callback);
26
- }
27
-
28
- /**
29
- * Emit event
30
- */
31
- emit(event, ...args) {
32
- if (!this.events[event]) return;
33
-
34
- this.events[event].forEach(callback => {
35
- try {
36
- callback(...args);
37
- } catch (error) {
38
- console.error(`Error in event listener for ${event}:`, error);
39
- }
40
- });
41
- }
42
-
43
- /**
44
- * Remove all listeners for an event
45
- */
46
- removeAllListeners(event) {
47
- if (event) {
48
- delete this.events[event];
49
- } else {
50
- this.events = {};
51
- }
52
- }
53
- }