@hivegpt/hiveai-angular 0.0.582 → 0.0.583
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/bundles/hivegpt-hiveai-angular.umd.js +158 -10
- package/bundles/hivegpt-hiveai-angular.umd.js.map +1 -1
- package/bundles/hivegpt-hiveai-angular.umd.min.js +1 -1
- package/bundles/hivegpt-hiveai-angular.umd.min.js.map +1 -1
- package/esm2015/lib/components/voice-agent/services/voice-agent.service.js +80 -1
- package/esm2015/lib/components/voice-agent/services/websocket-voice-client.service.js +54 -11
- package/fesm2015/hivegpt-hiveai-angular.js +131 -10
- package/fesm2015/hivegpt-hiveai-angular.js.map +1 -1
- package/hivegpt-hiveai-angular.metadata.json +1 -1
- package/lib/components/voice-agent/services/voice-agent.service.d.ts +11 -0
- package/lib/components/voice-agent/services/voice-agent.service.d.ts.map +1 -1
- package/lib/components/voice-agent/services/websocket-voice-client.service.d.ts +7 -0
- package/lib/components/voice-agent/services/websocket-voice-client.service.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1357,6 +1357,7 @@
|
|
|
1357
1357
|
this.botTranscriptSubject = new rxjs.Subject();
|
|
1358
1358
|
this.assistantSpeakingSubject = new rxjs.Subject();
|
|
1359
1359
|
this.serverUserSpeakingSubject = new rxjs.Subject();
|
|
1360
|
+
this.audioChunkSubject = new rxjs.Subject();
|
|
1360
1361
|
/** Fires once each time the WebSocket reaches OPEN. */
|
|
1361
1362
|
this.opened$ = this.openedSubject.asObservable();
|
|
1362
1363
|
/** Fires when the socket closes without a client-initiated {@link disconnect}. */
|
|
@@ -1367,6 +1368,8 @@
|
|
|
1367
1368
|
this.assistantSpeaking$ = this.assistantSpeakingSubject.asObservable();
|
|
1368
1369
|
/** User speaking from server-side VAD, if provided. */
|
|
1369
1370
|
this.serverUserSpeaking$ = this.serverUserSpeakingSubject.asObservable();
|
|
1371
|
+
/** Binary audio frames from server (when backend streams bot audio over WS). */
|
|
1372
|
+
this.audioChunk$ = this.audioChunkSubject.asObservable();
|
|
1370
1373
|
}
|
|
1371
1374
|
WebSocketVoiceClientService.prototype.connect = function (wsUrl) {
|
|
1372
1375
|
var _this = this;
|
|
@@ -1389,16 +1392,7 @@
|
|
|
1389
1392
|
socket_1.onmessage = function (event) {
|
|
1390
1393
|
if (_this.ws !== socket_1)
|
|
1391
1394
|
return;
|
|
1392
|
-
|
|
1393
|
-
return;
|
|
1394
|
-
}
|
|
1395
|
-
try {
|
|
1396
|
-
var msg_1 = JSON.parse(event.data);
|
|
1397
|
-
_this.ngZone.run(function () { return _this.handleJsonMessage(msg_1); });
|
|
1398
|
-
}
|
|
1399
|
-
catch (_a) {
|
|
1400
|
-
// Ignore non-JSON
|
|
1401
|
-
}
|
|
1395
|
+
void _this.handleIncomingMessage(event.data);
|
|
1402
1396
|
};
|
|
1403
1397
|
socket_1.onerror = function () {
|
|
1404
1398
|
_this.ngZone.run(function () {
|
|
@@ -1424,6 +1418,65 @@
|
|
|
1424
1418
|
throw err;
|
|
1425
1419
|
}
|
|
1426
1420
|
};
|
|
1421
|
+
WebSocketVoiceClientService.prototype.handleIncomingMessage = function (payload) {
|
|
1422
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1423
|
+
var ab;
|
|
1424
|
+
return __generator(this, function (_b) {
|
|
1425
|
+
switch (_b.label) {
|
|
1426
|
+
case 0:
|
|
1427
|
+
if (typeof payload === 'string') {
|
|
1428
|
+
this.handleJsonString(payload);
|
|
1429
|
+
return [2 /*return*/];
|
|
1430
|
+
}
|
|
1431
|
+
if (payload instanceof ArrayBuffer) {
|
|
1432
|
+
this.handleBinaryMessage(payload);
|
|
1433
|
+
return [2 /*return*/];
|
|
1434
|
+
}
|
|
1435
|
+
if (!(payload instanceof Blob)) return [3 /*break*/, 2];
|
|
1436
|
+
return [4 /*yield*/, payload.arrayBuffer()];
|
|
1437
|
+
case 1:
|
|
1438
|
+
ab = _b.sent();
|
|
1439
|
+
this.handleBinaryMessage(ab);
|
|
1440
|
+
_b.label = 2;
|
|
1441
|
+
case 2: return [2 /*return*/];
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
});
|
|
1445
|
+
};
|
|
1446
|
+
WebSocketVoiceClientService.prototype.handleJsonString = function (jsonText) {
|
|
1447
|
+
var _this = this;
|
|
1448
|
+
try {
|
|
1449
|
+
var msg_1 = JSON.parse(jsonText);
|
|
1450
|
+
this.ngZone.run(function () { return _this.handleJsonMessage(msg_1); });
|
|
1451
|
+
}
|
|
1452
|
+
catch (_a) {
|
|
1453
|
+
// Ignore non-JSON
|
|
1454
|
+
}
|
|
1455
|
+
};
|
|
1456
|
+
WebSocketVoiceClientService.prototype.handleBinaryMessage = function (buffer) {
|
|
1457
|
+
var _this = this;
|
|
1458
|
+
// Some backends wrap JSON events inside binary WS frames.
|
|
1459
|
+
var maybeText = this.tryDecodeUtf8(buffer);
|
|
1460
|
+
if (maybeText !== null) {
|
|
1461
|
+
this.handleJsonString(maybeText);
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
// Otherwise treat binary as streamed assistant audio.
|
|
1465
|
+
this.ngZone.run(function () { return _this.audioChunkSubject.next(buffer); });
|
|
1466
|
+
};
|
|
1467
|
+
WebSocketVoiceClientService.prototype.tryDecodeUtf8 = function (buffer) {
|
|
1468
|
+
try {
|
|
1469
|
+
var text = new TextDecoder('utf-8', { fatal: true }).decode(buffer);
|
|
1470
|
+
var trimmed = text.trim();
|
|
1471
|
+
if (!trimmed || (trimmed[0] !== '{' && trimmed[0] !== '[')) {
|
|
1472
|
+
return null;
|
|
1473
|
+
}
|
|
1474
|
+
return trimmed;
|
|
1475
|
+
}
|
|
1476
|
+
catch (_a) {
|
|
1477
|
+
return null;
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1427
1480
|
WebSocketVoiceClientService.prototype.handleJsonMessage = function (msg) {
|
|
1428
1481
|
var type = msg.type;
|
|
1429
1482
|
var typeStr = typeof type === 'string' ? type : '';
|
|
@@ -1529,6 +1582,9 @@
|
|
|
1529
1582
|
this.callStartTime = 0;
|
|
1530
1583
|
this.durationInterval = null;
|
|
1531
1584
|
this.localMicStream = null;
|
|
1585
|
+
this.remoteAudioContext = null;
|
|
1586
|
+
this.pendingRemoteAudio = [];
|
|
1587
|
+
this.remoteAudioPlaying = false;
|
|
1532
1588
|
this.endCall$ = new rxjs.Subject();
|
|
1533
1589
|
this.subscriptions = new rxjs.Subscription();
|
|
1534
1590
|
this.destroy$ = new rxjs.Subject();
|
|
@@ -1544,6 +1600,9 @@
|
|
|
1544
1600
|
this.subscriptions.add(this.wsClient.remoteClose$
|
|
1545
1601
|
.pipe(operators.takeUntil(this.destroy$))
|
|
1546
1602
|
.subscribe(function () { return void _this.handleRemoteClose(); }));
|
|
1603
|
+
this.subscriptions.add(this.wsClient.audioChunk$
|
|
1604
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
1605
|
+
.subscribe(function (chunk) { return _this.enqueueRemoteAudio(chunk); }));
|
|
1547
1606
|
}
|
|
1548
1607
|
VoiceAgentService.prototype.ngOnDestroy = function () {
|
|
1549
1608
|
this.destroy$.next();
|
|
@@ -1559,6 +1618,7 @@
|
|
|
1559
1618
|
this.callStartTime = 0;
|
|
1560
1619
|
this.audioAnalyzer.stop();
|
|
1561
1620
|
this.stopLocalMic();
|
|
1621
|
+
this.resetRemoteAudioPlayback();
|
|
1562
1622
|
this.wsClient.disconnect();
|
|
1563
1623
|
this.callStateSubject.next('idle');
|
|
1564
1624
|
this.statusTextSubject.next('');
|
|
@@ -1762,6 +1822,92 @@
|
|
|
1762
1822
|
this.localMicStream = null;
|
|
1763
1823
|
}
|
|
1764
1824
|
};
|
|
1825
|
+
VoiceAgentService.prototype.enqueueRemoteAudio = function (chunk) {
|
|
1826
|
+
this.pendingRemoteAudio.push(chunk.slice(0));
|
|
1827
|
+
if (!this.remoteAudioPlaying) {
|
|
1828
|
+
void this.playRemoteAudioQueue();
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
VoiceAgentService.prototype.playRemoteAudioQueue = function () {
|
|
1832
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1833
|
+
var context, chunk, decoded, _a_1;
|
|
1834
|
+
return __generator(this, function (_b) {
|
|
1835
|
+
switch (_b.label) {
|
|
1836
|
+
case 0:
|
|
1837
|
+
this.remoteAudioPlaying = true;
|
|
1838
|
+
context = this.getOrCreateRemoteAudioContext();
|
|
1839
|
+
_b.label = 1;
|
|
1840
|
+
case 1:
|
|
1841
|
+
if (!(this.pendingRemoteAudio.length > 0)) return [3 /*break*/, 7];
|
|
1842
|
+
chunk = this.pendingRemoteAudio.shift();
|
|
1843
|
+
if (!chunk)
|
|
1844
|
+
return [3 /*break*/, 1];
|
|
1845
|
+
_b.label = 2;
|
|
1846
|
+
case 2:
|
|
1847
|
+
_b.trys.push([2, 5, , 6]);
|
|
1848
|
+
return [4 /*yield*/, this.decodeAudioChunk(context, chunk)];
|
|
1849
|
+
case 3:
|
|
1850
|
+
decoded = _b.sent();
|
|
1851
|
+
this.assistantAudioStarted();
|
|
1852
|
+
return [4 /*yield*/, this.playDecodedBuffer(context, decoded)];
|
|
1853
|
+
case 4:
|
|
1854
|
+
_b.sent();
|
|
1855
|
+
return [3 /*break*/, 6];
|
|
1856
|
+
case 5:
|
|
1857
|
+
_a_1 = _b.sent();
|
|
1858
|
+
return [3 /*break*/, 6];
|
|
1859
|
+
case 6: return [3 /*break*/, 1];
|
|
1860
|
+
case 7:
|
|
1861
|
+
this.remoteAudioPlaying = false;
|
|
1862
|
+
this.assistantAudioStopped();
|
|
1863
|
+
return [2 /*return*/];
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
});
|
|
1867
|
+
};
|
|
1868
|
+
VoiceAgentService.prototype.getOrCreateRemoteAudioContext = function () {
|
|
1869
|
+
if (!this.remoteAudioContext || this.remoteAudioContext.state === 'closed') {
|
|
1870
|
+
this.remoteAudioContext = new AudioContext();
|
|
1871
|
+
}
|
|
1872
|
+
if (this.remoteAudioContext.state === 'suspended') {
|
|
1873
|
+
void this.remoteAudioContext.resume();
|
|
1874
|
+
}
|
|
1875
|
+
return this.remoteAudioContext;
|
|
1876
|
+
};
|
|
1877
|
+
VoiceAgentService.prototype.decodeAudioChunk = function (context, chunk) {
|
|
1878
|
+
return new Promise(function (resolve, reject) {
|
|
1879
|
+
context.decodeAudioData(chunk.slice(0), resolve, reject);
|
|
1880
|
+
});
|
|
1881
|
+
};
|
|
1882
|
+
VoiceAgentService.prototype.playDecodedBuffer = function (context, buffer) {
|
|
1883
|
+
return new Promise(function (resolve) {
|
|
1884
|
+
var source = context.createBufferSource();
|
|
1885
|
+
source.buffer = buffer;
|
|
1886
|
+
source.connect(context.destination);
|
|
1887
|
+
source.onended = function () { return resolve(); };
|
|
1888
|
+
source.start();
|
|
1889
|
+
});
|
|
1890
|
+
};
|
|
1891
|
+
VoiceAgentService.prototype.assistantAudioStarted = function () {
|
|
1892
|
+
if (this.callStartTime === 0) {
|
|
1893
|
+
this.callStartTime = Date.now();
|
|
1894
|
+
this.startDurationTimer();
|
|
1895
|
+
}
|
|
1896
|
+
this.callStateSubject.next('talking');
|
|
1897
|
+
};
|
|
1898
|
+
VoiceAgentService.prototype.assistantAudioStopped = function () {
|
|
1899
|
+
if (this.callStateSubject.value === 'talking') {
|
|
1900
|
+
this.callStateSubject.next('connected');
|
|
1901
|
+
}
|
|
1902
|
+
};
|
|
1903
|
+
VoiceAgentService.prototype.resetRemoteAudioPlayback = function () {
|
|
1904
|
+
this.pendingRemoteAudio = [];
|
|
1905
|
+
this.remoteAudioPlaying = false;
|
|
1906
|
+
if (this.remoteAudioContext && this.remoteAudioContext.state !== 'closed') {
|
|
1907
|
+
this.remoteAudioContext.close().catch(function () { });
|
|
1908
|
+
}
|
|
1909
|
+
this.remoteAudioContext = null;
|
|
1910
|
+
};
|
|
1765
1911
|
VoiceAgentService.prototype.handleRemoteClose = function () {
|
|
1766
1912
|
return __awaiter(this, void 0, void 0, function () {
|
|
1767
1913
|
var state;
|
|
@@ -1774,6 +1920,7 @@
|
|
|
1774
1920
|
this.callStartTime = 0;
|
|
1775
1921
|
this.audioAnalyzer.stop();
|
|
1776
1922
|
this.stopLocalMic();
|
|
1923
|
+
this.resetRemoteAudioPlayback();
|
|
1777
1924
|
this.callStateSubject.next('ended');
|
|
1778
1925
|
this.statusTextSubject.next('Connection lost');
|
|
1779
1926
|
return [2 /*return*/];
|
|
@@ -1788,6 +1935,7 @@
|
|
|
1788
1935
|
this.callStartTime = 0;
|
|
1789
1936
|
this.audioAnalyzer.stop();
|
|
1790
1937
|
this.stopLocalMic();
|
|
1938
|
+
this.resetRemoteAudioPlayback();
|
|
1791
1939
|
this.wsClient.disconnect();
|
|
1792
1940
|
this.callStateSubject.next('ended');
|
|
1793
1941
|
this.statusTextSubject.next('Call Ended');
|