ttp-agent-sdk 2.1.7 → 2.1.9

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 @@
1
+ class AudioProcessor extends AudioWorkletProcessor{constructor(e){super(),this.config=e.processorOptions||{},this.sampleRate=this.config.sampleRate||16e3,this.bufferSize=128,this.buffer=new Float32Array(this.bufferSize),this.bufferIndex=0,this.silenceThreshold=.005,this.minVoiceDuration=100,this.pauseThreshold=3e3,this.isVoiceActive=!1,this.voiceStartTime=0,this.lastVoiceTime=0,this.consecutiveSilenceFrames=0,this.silenceFramesThreshold=5,this.frameCount=0,this.lastLogTime=0,this.continuousMode=!0,this.forceContinuous=!0,this.isCurrentlyStreaming=!1,this.sendBuffer=null,this.sendBufferBytes=0,console.log("🎤 AudioProcessor: Initialized with sample rate:",this.sampleRate),console.log("🎤 AudioProcessor: Buffer size:",this.bufferSize),console.log("🎤 AudioProcessor: Force continuous mode:",this.forceContinuous),this.port.onmessage=e=>{const{type:s,data:t}=e.data;switch(console.log("🎤 AudioProcessor: Received message:",s,t),s){case"start":this.isProcessing=!0,this.isCurrentlyStreaming=!0,console.log("🎤 AudioProcessor: Started processing");break;case"stop":this.isProcessing=!1,this.isCurrentlyStreaming=!1,this.isVoiceActive=!1,this.forceContinuous=!1,this.flushBuffer(),console.log("🎤 AudioProcessor: Stopped processing and flushed buffer");break;case"setForceContinuous":this.forceContinuous=t.enabled,this.isProcessing=!0,this.isCurrentlyStreaming=!0,console.log("🎤 AudioProcessor: Enabled continuous mode");break;case"flush":this.flushBuffer(),console.log("🎤 AudioProcessor: Flushing buffer");break;case"config":Object.assign(this.config,t),console.log("🎤 AudioProcessor: Updated config:",this.config)}}}process(e,s,t){const i=e[0],o=s[0];return i.length>0&&o.length>0&&o[0].set(i[0]),i.length>0&&i[0].length>0&&this.processAudioData(i[0]),!0}processAudioData(e){this.frameCount++;for(let s=0;s<e.length;s+=this.bufferSize){const t=Math.min(this.bufferSize,e.length-s);for(let i=0;i<t;i++)this.buffer[i]=e[s+i];for(let e=t;e<this.bufferSize;e++)this.buffer[e]=0;let i=0;for(let e=0;e<this.bufferSize;e++)i+=this.buffer[e]*this.buffer[e];const o=Math.sqrt(i/this.bufferSize);let r=0,n=0;for(let e=1;e<this.bufferSize;e++){const s=Math.abs(this.buffer[e]-this.buffer[e-1]);r+=s,s>.1&&n++}r/=this.bufferSize,this.bufferSize;const h=Date.now();let c=o>this.silenceThreshold;if(h-this.lastLogTime>2e3){const e=c?"VOICE":"SILENCE";console.log(`🎤 AudioProcessor: RMS: ${o.toFixed(4)}, Threshold: ${this.silenceThreshold}, Status: ${e}, Streaming: ${this.isCurrentlyStreaming}, ForceContinuous: ${this.forceContinuous}, VoiceActive: ${this.isVoiceActive}`),this.lastLogTime=h}const f=h-this.lastVoiceTime;this.forceContinuous||c?(this.consecutiveSilenceFrames=0,this.isVoiceActive||(this.isVoiceActive=!0,this.voiceStartTime=h,this.isCurrentlyStreaming=!0,console.log(`🗣️ Voice detected (RMS: ${o.toFixed(4)})`)),this.lastVoiceTime=h):this.consecutiveSilenceFrames++,!this.forceContinuous&&!c&&this.isVoiceActive&&f>=200&&(this.isVoiceActive=!1,this.isCurrentlyStreaming=!1,console.log(`⏸️ AudioProcessor: Silence detected, stopping streaming (${f.toFixed(0)}ms silence)`),this.voiceStartTime=0,this.lastVoiceTime=0,this.consecutiveSilenceFrames=0),this.isCurrentlyStreaming&&this.isProcessing&&this.sendPCMAudioData(this.buffer)}}sendPCMAudioData(e){const s=new Int16Array(e.length);for(let t=0;t<e.length;t++){const i=Math.max(-1,Math.min(1,e[t]));s[t]=Math.round(32767*i)}for(this.sendBuffer||(this.sendBuffer=[],this.sendBufferBytes=0),this.sendBuffer.push(s),this.sendBufferBytes+=s.byteLength;this.sendBufferBytes>=4096;){let e=0,s=0;for(let t=0;t<this.sendBuffer.length;t++){const i=this.sendBuffer[t].byteLength;if(!(s+i<=4096))break;e++,s+=i}const t=this.sendBuffer.slice(0,e),i=t.reduce((e,s)=>e+s.length,0),o=new Int16Array(i);let r=0;for(const e of t)o.set(e,r),r+=e.length;Math.random()<.01&&console.log(`🎤 AudioProcessor: Sending PCM batch - ${o.byteLength} bytes (${e} chunks)`),this.port.postMessage({type:"pcm_audio_data",data:o,sampleRate:this.sampleRate,channelCount:1,frameCount:this.frameCount,batchSize:e,totalBytes:o.byteLength}),this.sendBuffer=this.sendBuffer.slice(e),this.sendBufferBytes-=s}}flushBuffer(){if(this.sendBuffer&&this.sendBuffer.length>0){const e=this.sendBuffer.reduce((e,s)=>e+s.length,0),s=new Int16Array(e);let t=0;for(const e of this.sendBuffer)s.set(e,t),t+=e.length;this.port.postMessage({type:"pcm_audio_data",data:s,sampleRate:this.sampleRate,channelCount:1,frameCount:this.frameCount,batchSize:this.sendBuffer.length,totalBytes:s.byteLength,isFlush:!0}),this.sendBuffer=[],this.sendBufferBytes=0}}}registerProcessor("audio-processor",AudioProcessor);
@@ -1,15 +1,9 @@
1
- <!--
2
- ============================================
3
- FILE: examples/test.html
4
- Test page for the widget
5
- ============================================
6
- -->
7
1
  <!DOCTYPE html>
8
2
  <html lang="en">
9
3
  <head>
10
4
  <meta charset="UTF-8">
11
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
- <title>Voice Agent Widget - Test Page</title>
6
+ <title>Voice Agent Widget - Agent ID + App ID Test</title>
13
7
  <style>
14
8
  body {
15
9
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
@@ -39,6 +33,14 @@ Test page for the widget
39
33
  border-radius: 4px;
40
34
  }
41
35
 
36
+ .config-info {
37
+ background: #F0FDF4;
38
+ border-left: 4px solid #10B981;
39
+ padding: 16px;
40
+ margin: 20px 0;
41
+ border-radius: 4px;
42
+ }
43
+
42
44
  .status {
43
45
  margin: 20px 0;
44
46
  padding: 12px;
@@ -72,20 +74,39 @@ Test page for the widget
72
74
  button:hover {
73
75
  background: #4338CA;
74
76
  }
77
+
78
+ .code-block {
79
+ background: #1F2937;
80
+ color: #F9FAFB;
81
+ padding: 16px;
82
+ border-radius: 6px;
83
+ font-family: monospace;
84
+ font-size: 12px;
85
+ overflow-x: auto;
86
+ margin: 20px 0;
87
+ }
75
88
  </style>
76
89
  </head>
77
90
  <body>
78
91
  <div class="container">
79
- <h1>🎤 Voice Agent Widget - Test Page</h1>
92
+ <h1>🎤 Voice Agent Widget - Agent ID + App ID Test</h1>
93
+
94
+ <div class="config-info">
95
+ <strong>📝 Configuration:</strong>
96
+ <ul style="margin: 10px 0 0 20px; line-height: 1.8;">
97
+ <li><strong>Agent ID:</strong> agent_87c4a55a1</li>
98
+ <li><strong>App ID:</strong> app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC</li>
99
+ <li><strong>Method:</strong> Direct connection (no signed link)</li>
100
+ </ul>
101
+ </div>
80
102
 
81
103
  <div class="info">
82
104
  <strong>📝 Testing Instructions:</strong>
83
105
  <ol style="margin: 10px 0 0 20px; line-height: 1.8;">
84
- <li><strong>No backend required!</strong> This demo uses a mock session URL</li>
85
106
  <li>Look for the floating voice button in the bottom-right corner</li>
86
107
  <li>Click the button to open the widget</li>
87
108
  <li>Click the microphone to start (will request mic permission)</li>
88
- <li>Widget will connect to production backend at speech.talktopc.com</li>
109
+ <li>Widget will connect directly using agent ID + app ID</li>
89
110
  <li>Check the status updates below</li>
90
111
  </ol>
91
112
  </div>
@@ -100,13 +121,39 @@ Test page for the widget
100
121
  Console messages will appear here...
101
122
  </div>
102
123
 
124
+ <h2>Implementation Code:</h2>
125
+ <div class="code-block">
126
+ const widget = new TTPAgentSDK.AgentWidget({
127
+ agentId: 'agent_87c4a55a1',
128
+ appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
129
+
130
+ // Direct connection without signed link
131
+ getSessionUrl: async ({ agentId, appId, variables }) => {
132
+ // Direct WebSocket URL construction with demo flag
133
+ const wsUrl = `wss://speech.talktopc.com/ws/conv?agentId=${agentId}&appId=${appId}&demo=true`;
134
+ return wsUrl;
135
+ },
136
+
137
+ variables: {
138
+ testMode: true,
139
+ userName: 'Test User',
140
+ page: 'test-agent-app.html'
141
+ },
142
+
143
+ primaryColor: '#10B981',
144
+ position: 'bottom-right'
145
+ });
146
+ </div>
147
+
103
148
  <h2>Manual Tests:</h2>
104
- <button onclick="testConnection()">Test Backend Connection</button>
149
+ <button onclick="testMicButton()">Test Mic Button Click</button>
150
+ <button onclick="testToggleVoice()">Test ToggleVoice Method</button>
151
+ <button onclick="checkWidgetState()">Check Widget State</button>
105
152
  <button onclick="testMicrophone()">Test Microphone Access</button>
106
153
  </div>
107
154
 
108
155
  <!-- Load the widget -->
109
- <script src="../agent-widget.js" onload="console.log('SDK script loaded successfully')" onerror="console.error('Failed to load SDK script')"></script>
156
+ <script src="../agent-widget.js?v=24" onload="console.log('SDK script loaded successfully')" onerror="console.error('Failed to load SDK script')"></script>
110
157
 
111
158
  <script>
112
159
  // Override console.log to show in page
@@ -140,7 +187,7 @@ Test page for the widget
140
187
 
141
188
  // Initialize the widget
142
189
  try {
143
- console.log('Initializing widget...');
190
+ console.log('Initializing widget with agent ID + app ID...');
144
191
  console.log('TTPAgentSDK available:', typeof TTPAgentSDK);
145
192
  console.log('TTPAgentSDK.AgentWidget available:', typeof TTPAgentSDK?.AgentWidget);
146
193
 
@@ -152,31 +199,30 @@ Test page for the widget
152
199
  throw new Error('TTPAgentSDK.AgentWidget is not defined. Check the SDK build.');
153
200
  }
154
201
 
155
- // Create a new AgentWidget instance
202
+ // Create a new AgentWidget instance with agent ID + app ID
156
203
  const widget = new TTPAgentSDK.AgentWidget({
157
- agentId: 'agent_test_123',
204
+ agentId: 'agent_87c4a55a1',
205
+ appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
158
206
 
159
- // Mock backend for local testing (no real backend needed!)
160
- getSessionUrl: async ({ agentId, variables }) => {
161
- console.log('Requesting session URL for agent:', agentId);
207
+ // Direct connection without signed link
208
+ getSessionUrl: async ({ agentId, appId, variables }) => {
209
+ console.log('Creating direct session URL for agent:', agentId);
210
+ console.log('App ID:', appId);
162
211
  console.log('Variables:', variables);
163
212
 
164
- updateStatus('Creating mock session URL...', 'info');
213
+ updateStatus('Creating direct session URL...', 'info');
165
214
 
166
215
  try {
167
- // Simulate a small delay like a real backend
168
- await new Promise(resolve => setTimeout(resolve, 500));
216
+ // Direct WebSocket URL construction with demo flag
217
+ const wsUrl = `wss://speech.talktopc.com/ws/conv?agentId=${agentId}&appId=${appId}&demo=true`;
169
218
 
170
- // Create a mock WebSocket URL for testing
171
- const mockSignedUrl = `wss://speech.talktopc.com/ws/conv?agentId=${agentId}&appId=test_app_123`;
219
+ console.log('Generated direct session URL:', wsUrl);
220
+ updateStatus('Direct session URL created ✓', 'success');
172
221
 
173
- console.log('Generated mock session URL:', mockSignedUrl);
174
- updateStatus('Mock session URL created ✓', 'success');
175
-
176
- return mockSignedUrl;
222
+ return wsUrl;
177
223
  } catch (error) {
178
- console.error('Failed to create mock session URL:', error);
179
- updateStatus('Failed to create mock session URL ✗', 'error');
224
+ console.error('Failed to create direct session URL:', error);
225
+ updateStatus('Failed to create direct session URL ✗', 'error');
180
226
  throw error;
181
227
  }
182
228
  },
@@ -185,7 +231,8 @@ Test page for the widget
185
231
  variables: {
186
232
  testMode: true,
187
233
  userName: 'Test User',
188
- page: 'test.html'
234
+ page: 'test-agent-app.html',
235
+ connectionType: 'direct'
189
236
  },
190
237
 
191
238
  // Customize appearance
@@ -193,7 +240,12 @@ Test page for the widget
193
240
  position: 'bottom-right'
194
241
  });
195
242
 
196
- console.log('Widget initialized successfully!');
243
+ // Store widget reference for testing (like the working implementation)
244
+ window.testWidget = widget;
245
+
246
+ console.log('Widget initialized successfully with agent ID + app ID!');
247
+ console.log('Widget instance:', widget);
248
+ console.log('Test methods available at window.testWidget');
197
249
  updateStatus('Widget loaded and ready ✓', 'success');
198
250
 
199
251
  } catch (error) {
@@ -201,23 +253,35 @@ Test page for the widget
201
253
  updateStatus('Widget failed to load ✗', 'error');
202
254
  }
203
255
 
204
- // Manual test functions
205
- async function testConnection() {
206
- console.log('Testing backend connection...');
207
- try {
208
- const response = await fetch('http://localhost:3000/health');
209
- if (response.ok) {
210
- console.log('Backend is reachable ✓');
211
- alert('Backend connection OK!');
212
- } else {
213
- throw new Error('Backend returned error');
214
- }
215
- } catch (error) {
216
- console.error('Cannot reach backend:', error);
217
- alert('Widget initialization FAILED! Check console for details.');
256
+ // Test functions (matching the working implementation)
257
+ function testMicButton() {
258
+ console.log('🎤 Testing mic button manually...');
259
+ const micButton = document.getElementById('agent-mic-button');
260
+ if (micButton) {
261
+ console.log('🎤 Found mic button, clicking...');
262
+ micButton.click();
263
+ } else {
264
+ console.log('🎤 Mic button not found');
265
+ }
266
+ }
267
+
268
+ function testToggleVoice() {
269
+ console.log('🎤 Testing toggleVoice method...');
270
+ if (window.testWidget && window.testWidget.toggleVoice) {
271
+ window.testWidget.toggleVoice();
272
+ } else {
273
+ console.log('🎤 toggleVoice method not available');
218
274
  }
219
275
  }
220
276
 
277
+ function checkWidgetState() {
278
+ console.log('🎤 Widget state:', {
279
+ isActive: window.testWidget?.isActive,
280
+ isOpen: window.testWidget?.isOpen,
281
+ sdk: window.testWidget?.sdk
282
+ });
283
+ }
284
+
221
285
  async function testMicrophone() {
222
286
  console.log('Testing microphone access...');
223
287
  try {