ttp-agent-sdk 2.5.6 → 2.5.8
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 -1
- package/dist/examples/test-signed-link.html +134 -115
- package/dist/index.html +561 -28
- package/examples/test-signed-link.html +134 -115
- package/package.json +1 -1
package/dist/audio-processor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class AudioProcessor extends AudioWorkletProcessor{constructor(e){super(),this.config=e.processorOptions||{},this.sampleRate=this.config.sampleRate||
|
|
1
|
+
class AudioProcessor extends AudioWorkletProcessor{constructor(e){super(),this.config=e.processorOptions||{},this.sampleRate="undefined"!=typeof sampleRate?sampleRate:this.config.sampleRate||this.config.outputSampleRate||44100,this.bufferSize=128,this.buffer=new Float32Array(this.bufferSize),this.bufferIndex=0,this.silenceThreshold=.01,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,this.port.onmessage=e=>{const{type:t,data:s}=e.data;switch(t){case"start":this.isProcessing=!0,this.isCurrentlyStreaming=!0;break;case"stop":this.isProcessing=!1,this.isCurrentlyStreaming=!1,this.isVoiceActive=!1,this.forceContinuous=!1,this.flushBuffer();break;case"setForceContinuous":this.forceContinuous=s.enabled,this.isProcessing=!0,this.isCurrentlyStreaming=!0;break;case"flush":this.flushBuffer();break;case"config":Object.assign(this.config,s)}}}process(e,t,s){const i=e[0],r=t[0];return i.length>0&&r.length>0&&r[0].set(i[0]),i.length>0&&i[0].length>0&&this.processAudioData(i[0]),!0}processAudioData(e){this.frameCount++;for(let t=0;t<e.length;t+=this.bufferSize){const s=Math.min(this.bufferSize,e.length-t);for(let i=0;i<s;i++)this.buffer[i]=e[t+i];for(let e=s;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 r=Math.sqrt(i/this.bufferSize);let h=0,n=0;for(let e=1;e<this.bufferSize;e++){const t=Math.abs(this.buffer[e]-this.buffer[e-1]);h+=t,t>.1&&n++}h/=this.bufferSize,this.bufferSize;const o=Date.now();let f=r>this.silenceThreshold;const a=o-this.lastVoiceTime;if(f)this.consecutiveSilenceFrames=0,this.isVoiceActive||(this.isVoiceActive=!0,this.voiceStartTime=o,this.isCurrentlyStreaming=!0),this.lastVoiceTime=o;else{this.consecutiveSilenceFrames++;const e=this.forceContinuous?1500:200;!f&&this.isVoiceActive&&a>=e&&(this.isVoiceActive=!1,this.isCurrentlyStreaming=!1,this.voiceStartTime=0,this.lastVoiceTime=0,this.consecutiveSilenceFrames=0)}this.isCurrentlyStreaming&&this.isProcessing&&this.sendPCMAudioData(this.buffer)}}sendPCMAudioData(e){const t=new Int16Array(e.length);for(let s=0;s<e.length;s++){const i=Math.max(-1,Math.min(1,e[s]));t[s]=Math.round(32767*i)}for(this.sendBuffer||(this.sendBuffer=[],this.sendBufferBytes=0),this.sendBuffer.push(t),this.sendBufferBytes+=t.byteLength;this.sendBufferBytes>=4096;){let e=0,t=0;for(let s=0;s<this.sendBuffer.length;s++){const i=this.sendBuffer[s].byteLength;if(!(t+i<=4096))break;e++,t+=i}const s=this.sendBuffer.slice(0,e),i=s.reduce((e,t)=>e+t.length,0),r=new Int16Array(i);let h=0;for(const e of s)r.set(e,h),h+=e.length;this.port.postMessage({type:"pcm_audio_data",data:r,sampleRate:this.sampleRate,channelCount:1,frameCount:this.frameCount,batchSize:e,totalBytes:r.byteLength}),this.sendBuffer=this.sendBuffer.slice(e),this.sendBufferBytes-=t}}flushBuffer(){if(this.sendBuffer&&this.sendBuffer.length>0){const e=this.sendBuffer.reduce((e,t)=>e+t.length,0),t=new Int16Array(e);let s=0;for(const e of this.sendBuffer)t.set(e,s),s+=e.length;this.port.postMessage({type:"pcm_audio_data",data:t,sampleRate:this.sampleRate,channelCount:1,frameCount:this.frameCount,batchSize:this.sendBuffer.length,totalBytes:t.byteLength,isFlush:!0}),this.sendBuffer=[],this.sendBufferBytes=0}}}registerProcessor("audio-processor",AudioProcessor);
|
|
@@ -141,41 +141,54 @@
|
|
|
141
141
|
|
|
142
142
|
<h2>Implementation Code:</h2>
|
|
143
143
|
<div class="code-block">
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
// Step 1: Get signed URL from backend
|
|
145
|
+
async function initializeWidget() {
|
|
146
|
+
const response = await fetch('https://backend.talktopc.com/api/public/agents/signed-url', {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: {
|
|
149
|
+
'Content-Type': 'application/json',
|
|
150
|
+
},
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
agentId: 'agent_87c4a55a1',
|
|
153
|
+
appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
|
|
154
|
+
variables: {
|
|
155
|
+
testMode: true,
|
|
156
|
+
userName: 'Test User',
|
|
157
|
+
page: 'test-signed-link.html'
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
});
|
|
147
161
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
throw new Error(`Backend API error: ${response.status}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const data = await response.json();
|
|
167
|
+
if (!data.signedUrl) {
|
|
168
|
+
throw new Error('Backend response must contain "signedUrl" field');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Step 2: Initialize widget with signedUrl
|
|
172
|
+
const widget = new TTPAgentSDK.AgentWidget({
|
|
173
|
+
agentId: 'agent_87c4a55a1',
|
|
174
|
+
appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',
|
|
175
|
+
signedUrl: data.signedUrl, // Use signed URL from backend
|
|
161
176
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
177
|
+
variables: {
|
|
178
|
+
testMode: true,
|
|
179
|
+
userName: 'Test User',
|
|
180
|
+
page: 'test-signed-link.html'
|
|
181
|
+
},
|
|
165
182
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
variables: {
|
|
171
|
-
testMode: true,
|
|
172
|
-
userName: 'Test User',
|
|
173
|
-
page: 'test-signed-link.html'
|
|
174
|
-
},
|
|
183
|
+
primaryColor: '#F59E0B',
|
|
184
|
+
position: 'bottom-right'
|
|
185
|
+
});
|
|
175
186
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
return widget;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Initialize when ready
|
|
191
|
+
const widget = await initializeWidget();
|
|
179
192
|
</div>
|
|
180
193
|
|
|
181
194
|
<h2>Manual Tests:</h2>
|
|
@@ -266,89 +279,95 @@ const widget = new TTPAgentSDK.AgentWidget({
|
|
|
266
279
|
throw new Error('TTPAgentSDK.AgentWidget is not defined. Check the SDK build.');
|
|
267
280
|
}
|
|
268
281
|
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
282
|
+
// Step 1: Get signed URL from backend
|
|
283
|
+
console.log('Requesting signed URL from backend API...');
|
|
284
|
+
updateStatus('Requesting signed URL from backend...', 'info');
|
|
285
|
+
|
|
286
|
+
const agentId = 'agent_87c4a55a1';
|
|
287
|
+
const appId = 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC';
|
|
288
|
+
const variables = {
|
|
289
|
+
testMode: true,
|
|
290
|
+
userName: 'Test User',
|
|
291
|
+
page: 'test-signed-link.html'
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
console.log('Agent ID:', agentId);
|
|
295
|
+
console.log('App ID:', appId);
|
|
296
|
+
console.log('Variables:', variables);
|
|
273
297
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
298
|
+
try {
|
|
299
|
+
const response = await fetch('https://backend.talktopc.com/api/public/agents/signed-url', {
|
|
300
|
+
method: 'POST',
|
|
301
|
+
headers: {
|
|
302
|
+
'Content-Type': 'application/json',
|
|
303
|
+
},
|
|
304
|
+
body: JSON.stringify({
|
|
305
|
+
agentId: agentId,
|
|
306
|
+
appId: appId,
|
|
307
|
+
variables: variables
|
|
308
|
+
})
|
|
309
|
+
});
|
|
280
310
|
|
|
281
|
-
|
|
311
|
+
console.log('Backend API response status:', response.status);
|
|
282
312
|
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// Handle different possible response formats
|
|
307
|
-
const signedUrl = data.signedUrl || data.url || data.websocketUrl || data.sessionUrl;
|
|
308
|
-
|
|
309
|
-
if (!signedUrl) {
|
|
310
|
-
throw new Error('No signed URL found in API response');
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
console.log('Generated signed URL:', signedUrl);
|
|
314
|
-
updateStatus('Signed URL received from backend ✓', 'success');
|
|
315
|
-
|
|
316
|
-
return signedUrl;
|
|
317
|
-
} catch (error) {
|
|
318
|
-
console.error('Failed to get signed URL from backend:', error);
|
|
319
|
-
updateStatus('Failed to get signed URL from backend ✗', 'error');
|
|
313
|
+
if (!response.ok) {
|
|
314
|
+
const errorText = await response.text();
|
|
315
|
+
throw new Error(`Backend API error: ${response.status} - ${errorText}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const data = await response.json();
|
|
319
|
+
console.log('Backend API response data:', data);
|
|
320
|
+
|
|
321
|
+
// Backend must return signedUrl field
|
|
322
|
+
if (!data.signedUrl) {
|
|
323
|
+
throw new Error('Backend response must contain "signedUrl" field');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const signedUrl = data.signedUrl;
|
|
327
|
+
console.log('Generated signed URL:', signedUrl);
|
|
328
|
+
updateStatus('Signed URL received from backend ✓', 'success');
|
|
329
|
+
|
|
330
|
+
// Step 2: Create widget with signedUrl
|
|
331
|
+
const widget = new window.TTPAgentSDK.AgentWidget({
|
|
332
|
+
agentId: agentId,
|
|
333
|
+
appId: appId,
|
|
334
|
+
signedUrl: signedUrl, // Use signed URL from backend
|
|
320
335
|
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
const fallbackUrl = `wss://speech.talktopc.com/ws/conv?agentId=${agentId}&appId=${appId}&demo=true`;
|
|
324
|
-
console.log('Using fallback URL:', fallbackUrl);
|
|
325
|
-
updateStatus('Using fallback direct connection', 'info');
|
|
336
|
+
// Pass some test variables
|
|
337
|
+
variables: variables,
|
|
326
338
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
339
|
+
// Customize appearance
|
|
340
|
+
primaryColor: '#F59E0B',
|
|
341
|
+
position: 'bottom-right'
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Store widget reference for testing
|
|
345
|
+
window.testWidget = widget;
|
|
346
|
+
|
|
347
|
+
console.log('Widget initialized successfully with signed link!');
|
|
348
|
+
console.log('Widget instance:', widget);
|
|
349
|
+
console.log('Test methods available at window.testWidget');
|
|
350
|
+
updateStatus('Widget loaded and ready ✓', 'success');
|
|
351
|
+
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error('Failed to get signed URL or initialize widget:', error);
|
|
354
|
+
updateStatus('Failed: ' + error.message, 'error');
|
|
355
|
+
|
|
356
|
+
// Fallback to direct connection if backend fails
|
|
357
|
+
console.log('Falling back to direct connection...');
|
|
358
|
+
updateStatus('Using fallback direct connection', 'info');
|
|
359
|
+
|
|
360
|
+
const widget = new window.TTPAgentSDK.AgentWidget({
|
|
361
|
+
agentId: agentId,
|
|
362
|
+
appId: appId,
|
|
363
|
+
variables: variables,
|
|
364
|
+
primaryColor: '#F59E0B',
|
|
365
|
+
position: 'bottom-right'
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
window.testWidget = widget;
|
|
369
|
+
console.log('Widget initialized with direct connection (fallback)');
|
|
370
|
+
}
|
|
352
371
|
} catch (error) {
|
|
353
372
|
console.error('Failed to initialize widget:', error);
|
|
354
373
|
updateStatus('Failed to initialize widget: ' + error.message, 'error');
|
|
@@ -450,14 +469,14 @@ const widget = new TTPAgentSDK.AgentWidget({
|
|
|
450
469
|
|
|
451
470
|
if (response.ok) {
|
|
452
471
|
const data = await response.json();
|
|
453
|
-
const signedUrl = data.signedUrl || data.url || data.websocketUrl || data.sessionUrl;
|
|
454
472
|
|
|
455
|
-
if (signedUrl) {
|
|
456
|
-
|
|
457
|
-
alert(`Signed URL generated successfully!\n\nURL: ${signedUrl.substring(0, 100)}...`);
|
|
458
|
-
} else {
|
|
459
|
-
throw new Error('No signed URL in response');
|
|
473
|
+
if (!data.signedUrl) {
|
|
474
|
+
throw new Error('Backend response must contain "signedUrl" field');
|
|
460
475
|
}
|
|
476
|
+
|
|
477
|
+
const signedUrl = data.signedUrl;
|
|
478
|
+
console.log('Generated signed URL:', signedUrl);
|
|
479
|
+
alert(`Signed URL generated successfully!\n\nURL: ${signedUrl.substring(0, 100)}...\n\nYou can now use this signedUrl when initializing the widget:\n\nconst widget = new TTPAgentSDK.AgentWidget({\n agentId: 'agent_87c4a55a1',\n appId: 'app_Bc01EqMQt2Euehl4qqZSi6l3FJP42Q9vJ0pC',\n signedUrl: '${signedUrl.substring(0, 50)}...'\n});`);
|
|
461
480
|
} else {
|
|
462
481
|
throw new Error(`API error: ${response.status}`);
|
|
463
482
|
}
|