@hivegpt/hiveai-angular 0.0.425 → 0.0.428
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 +506 -164
- 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/hivegpt-hiveai-angular.js +6 -4
- package/esm2015/lib/components/chat-drawer/chat-drawer.component.js +22 -7
- package/esm2015/lib/components/voice-agent/services/audio-analyzer.service.js +5 -1
- package/esm2015/lib/components/voice-agent/services/daily-voice-client.service.js +181 -0
- package/esm2015/lib/components/voice-agent/services/voice-agent.service.js +134 -140
- package/esm2015/lib/components/voice-agent/services/websocket-voice-client.service.js +95 -0
- package/esm2015/lib/components/voice-agent/voice-agent.module.js +10 -2
- package/fesm2015/hivegpt-hiveai-angular.js +432 -149
- package/fesm2015/hivegpt-hiveai-angular.js.map +1 -1
- package/hivegpt-hiveai-angular.d.ts +5 -3
- package/hivegpt-hiveai-angular.d.ts.map +1 -1
- package/hivegpt-hiveai-angular.metadata.json +1 -1
- package/lib/components/chat-drawer/chat-drawer.component.d.ts +7 -0
- package/lib/components/chat-drawer/chat-drawer.component.d.ts.map +1 -1
- package/lib/components/voice-agent/services/audio-analyzer.service.d.ts +4 -0
- package/lib/components/voice-agent/services/audio-analyzer.service.d.ts.map +1 -1
- package/lib/components/voice-agent/services/daily-voice-client.service.d.ts +48 -0
- package/lib/components/voice-agent/services/daily-voice-client.service.d.ts.map +1 -0
- package/lib/components/voice-agent/services/voice-agent.service.d.ts +24 -8
- 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 +49 -0
- package/lib/components/voice-agent/services/websocket-voice-client.service.d.ts.map +1 -0
- package/lib/components/voice-agent/voice-agent.module.d.ts +4 -0
- package/lib/components/voice-agent/voice-agent.module.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/cdk/overlay'), require('@angular/cdk/portal'), require('@angular/common/http'), require('@angular/core'), require('@angular/platform-browser'), require('rxjs'), require('rxjs/operators'), require('ngx-socket-io'), require('@angular/forms'), require('microsoft-cognitiveservices-speech-sdk'), require('marked'), require('@
|
|
3
|
-
typeof define === 'function' && define.amd ? define('@hivegpt/hiveai-angular', ['exports', '@angular/cdk/overlay', '@angular/cdk/portal', '@angular/common/http', '@angular/core', '@angular/platform-browser', 'rxjs', 'rxjs/operators', 'ngx-socket-io', '@angular/forms', 'microsoft-cognitiveservices-speech-sdk', 'marked', '@
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.hivegpt = global.hivegpt || {}, global.hivegpt["hiveai-angular"] = {}), global.ng.cdk.overlay, global.ng.cdk.portal, global.ng.common.http, global.ng.core, global.ng.platformBrowser, global.rxjs, global.rxjs.operators, global.ngxSocketIo, global.ng.forms, global.SpeechSDK, global.marked, global.
|
|
5
|
-
})(this, (function (exports, overlay, portal, i1, i0, platformBrowser, rxjs, operators, ngxSocketIo, forms, SpeechSDK, marked,
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/cdk/overlay'), require('@angular/cdk/portal'), require('@angular/common/http'), require('@angular/core'), require('@angular/platform-browser'), require('rxjs'), require('rxjs/operators'), require('ngx-socket-io'), require('@angular/forms'), require('microsoft-cognitiveservices-speech-sdk'), require('marked'), require('@daily-co/daily-js'), require('@angular/common'), require('@angular/material/icon'), require('@angular/material/sidenav'), require('ngx-quill')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define('@hivegpt/hiveai-angular', ['exports', '@angular/cdk/overlay', '@angular/cdk/portal', '@angular/common/http', '@angular/core', '@angular/platform-browser', 'rxjs', 'rxjs/operators', 'ngx-socket-io', '@angular/forms', 'microsoft-cognitiveservices-speech-sdk', 'marked', '@daily-co/daily-js', '@angular/common', '@angular/material/icon', '@angular/material/sidenav', 'ngx-quill'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.hivegpt = global.hivegpt || {}, global.hivegpt["hiveai-angular"] = {}), global.ng.cdk.overlay, global.ng.cdk.portal, global.ng.common.http, global.ng.core, global.ng.platformBrowser, global.rxjs, global.rxjs.operators, global.ngxSocketIo, global.ng.forms, global.SpeechSDK, global.marked, global.Daily, global.ng.common, global.ng.material.icon, global.ng.material.sidenav, global.ngxQuill));
|
|
5
|
+
})(this, (function (exports, overlay, portal, i1, i0, platformBrowser, rxjs, operators, ngxSocketIo, forms, SpeechSDK, marked, Daily, common, icon, sidenav, ngxQuill) { 'use strict';
|
|
6
|
+
|
|
7
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
6
8
|
|
|
7
9
|
function _interopNamespace(e) {
|
|
8
10
|
if (e && e.__esModule) return e;
|
|
@@ -26,6 +28,7 @@
|
|
|
26
28
|
var i0__namespace = /*#__PURE__*/_interopNamespace(i0);
|
|
27
29
|
var SpeechSDK__namespace = /*#__PURE__*/_interopNamespace(SpeechSDK);
|
|
28
30
|
var marked__namespace = /*#__PURE__*/_interopNamespace(marked);
|
|
31
|
+
var Daily__default = /*#__PURE__*/_interopDefaultLegacy(Daily);
|
|
29
32
|
|
|
30
33
|
/******************************************************************************
|
|
31
34
|
Copyright (c) Microsoft Corporation.
|
|
@@ -1028,6 +1031,10 @@
|
|
|
1028
1031
|
{ type: i1.HttpClient }
|
|
1029
1032
|
]; };
|
|
1030
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* Audio analyzer for waveform visualization only.
|
|
1036
|
+
* Do NOT use isUserSpeaking$ for call state; speaking state must come from Daily.js.
|
|
1037
|
+
*/
|
|
1031
1038
|
var AudioAnalyzerService = /** @class */ (function () {
|
|
1032
1039
|
function AudioAnalyzerService() {
|
|
1033
1040
|
this.audioContext = null;
|
|
@@ -1146,14 +1153,323 @@
|
|
|
1146
1153
|
},] }
|
|
1147
1154
|
];
|
|
1148
1155
|
|
|
1156
|
+
/**
|
|
1157
|
+
* WebSocket-only client for voice agent signaling.
|
|
1158
|
+
* CRITICAL: Uses native WebSocket only. NO Socket.IO, NO ngx-socket-io.
|
|
1159
|
+
*
|
|
1160
|
+
* Responsibilities:
|
|
1161
|
+
* - Connect to ws_url (from POST /ai/ask-voice response)
|
|
1162
|
+
* - Parse JSON messages (room_created, user_transcript, bot_transcript)
|
|
1163
|
+
* - Emit roomCreated$, userTranscript$, botTranscript$
|
|
1164
|
+
* - NO audio logic, NO mic logic. Audio is handled by Daily.js (WebRTC).
|
|
1165
|
+
*/
|
|
1166
|
+
var WebSocketVoiceClientService = /** @class */ (function () {
|
|
1167
|
+
function WebSocketVoiceClientService() {
|
|
1168
|
+
this.ws = null;
|
|
1169
|
+
this.roomCreatedSubject = new rxjs.Subject();
|
|
1170
|
+
this.userTranscriptSubject = new rxjs.Subject();
|
|
1171
|
+
this.botTranscriptSubject = new rxjs.Subject();
|
|
1172
|
+
/** Emits room_url when backend sends room_created. */
|
|
1173
|
+
this.roomCreated$ = this.roomCreatedSubject.asObservable();
|
|
1174
|
+
/** Emits user transcript updates. */
|
|
1175
|
+
this.userTranscript$ = this.userTranscriptSubject.asObservable();
|
|
1176
|
+
/** Emits bot transcript updates. */
|
|
1177
|
+
this.botTranscript$ = this.botTranscriptSubject.asObservable();
|
|
1178
|
+
}
|
|
1179
|
+
/** Connect to signaling WebSocket. No audio over this connection. */
|
|
1180
|
+
WebSocketVoiceClientService.prototype.connect = function (wsUrl) {
|
|
1181
|
+
var _this = this;
|
|
1182
|
+
var _a;
|
|
1183
|
+
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
if (this.ws) {
|
|
1187
|
+
this.ws.close();
|
|
1188
|
+
this.ws = null;
|
|
1189
|
+
}
|
|
1190
|
+
try {
|
|
1191
|
+
this.ws = new WebSocket(wsUrl);
|
|
1192
|
+
this.ws.onmessage = function (event) {
|
|
1193
|
+
var _a;
|
|
1194
|
+
try {
|
|
1195
|
+
var msg = JSON.parse(event.data);
|
|
1196
|
+
if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'room_created') {
|
|
1197
|
+
var roomUrl = ((_a = msg.room_url) !== null && _a !== void 0 ? _a : msg.roomUrl);
|
|
1198
|
+
if (typeof roomUrl === 'string') {
|
|
1199
|
+
_this.roomCreatedSubject.next(roomUrl);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
else if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'user_transcript' && typeof msg.text === 'string') {
|
|
1203
|
+
_this.userTranscriptSubject.next({
|
|
1204
|
+
text: msg.text,
|
|
1205
|
+
final: msg.final === true,
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
else if ((msg === null || msg === void 0 ? void 0 : msg.type) === 'bot_transcript' && typeof msg.text === 'string') {
|
|
1209
|
+
_this.botTranscriptSubject.next(msg.text);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
catch (_b) {
|
|
1213
|
+
// Ignore non-JSON or unknown messages
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
this.ws.onerror = function () {
|
|
1217
|
+
_this.disconnect();
|
|
1218
|
+
};
|
|
1219
|
+
this.ws.onclose = function () {
|
|
1220
|
+
_this.ws = null;
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
catch (err) {
|
|
1224
|
+
console.error('WebSocketVoiceClient: connect failed', err);
|
|
1225
|
+
this.ws = null;
|
|
1226
|
+
throw err;
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
/** Disconnect and cleanup. */
|
|
1230
|
+
WebSocketVoiceClientService.prototype.disconnect = function () {
|
|
1231
|
+
if (this.ws) {
|
|
1232
|
+
this.ws.close();
|
|
1233
|
+
this.ws = null;
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
Object.defineProperty(WebSocketVoiceClientService.prototype, "isConnected", {
|
|
1237
|
+
/** Whether the WebSocket is open. */
|
|
1238
|
+
get: function () {
|
|
1239
|
+
var _a;
|
|
1240
|
+
return ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN;
|
|
1241
|
+
},
|
|
1242
|
+
enumerable: false,
|
|
1243
|
+
configurable: true
|
|
1244
|
+
});
|
|
1245
|
+
return WebSocketVoiceClientService;
|
|
1246
|
+
}());
|
|
1247
|
+
WebSocketVoiceClientService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function WebSocketVoiceClientService_Factory() { return new WebSocketVoiceClientService(); }, token: WebSocketVoiceClientService, providedIn: "root" });
|
|
1248
|
+
WebSocketVoiceClientService.decorators = [
|
|
1249
|
+
{ type: i0.Injectable, args: [{
|
|
1250
|
+
providedIn: 'root',
|
|
1251
|
+
},] }
|
|
1252
|
+
];
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* Daily.js WebRTC client for voice agent audio.
|
|
1256
|
+
* Responsibilities:
|
|
1257
|
+
* - Create and manage Daily CallObject
|
|
1258
|
+
* - Join Daily room using room_url
|
|
1259
|
+
* - Handle mic capture + speaker playback
|
|
1260
|
+
* - Provide real-time speaking detection via active-speaker-change (primary)
|
|
1261
|
+
* and track-started/track-stopped (fallback for immediate feedback)
|
|
1262
|
+
* - Expose speaking$ (bot speaking), userSpeaking$ (user speaking), micMuted$
|
|
1263
|
+
* - Expose localStream$ for waveform visualization (AudioAnalyzerService)
|
|
1264
|
+
*
|
|
1265
|
+
* Speaking state flips immediately when agent audio starts playing.
|
|
1266
|
+
* If user speaks while bot is talking, state switches to listening.
|
|
1267
|
+
*/
|
|
1268
|
+
var DailyVoiceClientService = /** @class */ (function () {
|
|
1269
|
+
function DailyVoiceClientService(ngZone) {
|
|
1270
|
+
this.ngZone = ngZone;
|
|
1271
|
+
this.callObject = null;
|
|
1272
|
+
this.localStream = null;
|
|
1273
|
+
this.localSessionId = null;
|
|
1274
|
+
this.speakingSubject = new rxjs.BehaviorSubject(false);
|
|
1275
|
+
this.userSpeakingSubject = new rxjs.BehaviorSubject(false);
|
|
1276
|
+
this.micMutedSubject = new rxjs.BehaviorSubject(false);
|
|
1277
|
+
this.localStreamSubject = new rxjs.BehaviorSubject(null);
|
|
1278
|
+
/** True when bot (remote participant) is the active speaker. */
|
|
1279
|
+
this.speaking$ = this.speakingSubject.asObservable();
|
|
1280
|
+
/** True when user (local participant) is the active speaker. */
|
|
1281
|
+
this.userSpeaking$ = this.userSpeakingSubject.asObservable();
|
|
1282
|
+
/** True when mic is muted. */
|
|
1283
|
+
this.micMuted$ = this.micMutedSubject.asObservable();
|
|
1284
|
+
/** Emits local mic stream for waveform visualization. */
|
|
1285
|
+
this.localStream$ = this.localStreamSubject.asObservable();
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Connect to Daily room. Acquires mic first for waveform, then joins with audio.
|
|
1289
|
+
* @param roomUrl Daily room URL (from room_created)
|
|
1290
|
+
* @param token Optional meeting token
|
|
1291
|
+
*/
|
|
1292
|
+
DailyVoiceClientService.prototype.connect = function (roomUrl, token) {
|
|
1293
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1294
|
+
var stream, audioTrack, callObject, participants, err_1;
|
|
1295
|
+
return __generator(this, function (_c) {
|
|
1296
|
+
switch (_c.label) {
|
|
1297
|
+
case 0:
|
|
1298
|
+
if (!this.callObject) return [3 /*break*/, 2];
|
|
1299
|
+
return [4 /*yield*/, this.disconnect()];
|
|
1300
|
+
case 1:
|
|
1301
|
+
_c.sent();
|
|
1302
|
+
_c.label = 2;
|
|
1303
|
+
case 2:
|
|
1304
|
+
_c.trys.push([2, 5, , 6]);
|
|
1305
|
+
return [4 /*yield*/, navigator.mediaDevices.getUserMedia({ audio: true })];
|
|
1306
|
+
case 3:
|
|
1307
|
+
stream = _c.sent();
|
|
1308
|
+
audioTrack = stream.getAudioTracks()[0];
|
|
1309
|
+
if (!audioTrack) {
|
|
1310
|
+
stream.getTracks().forEach(function (t) { return t.stop(); });
|
|
1311
|
+
throw new Error('No audio track');
|
|
1312
|
+
}
|
|
1313
|
+
this.localStream = stream;
|
|
1314
|
+
this.localStreamSubject.next(stream);
|
|
1315
|
+
callObject = Daily__default["default"].createCallObject({
|
|
1316
|
+
videoSource: false,
|
|
1317
|
+
audioSource: audioTrack,
|
|
1318
|
+
});
|
|
1319
|
+
this.callObject = callObject;
|
|
1320
|
+
this.setupEventHandlers(callObject);
|
|
1321
|
+
// Join room; Daily handles playback of remote (bot) audio automatically
|
|
1322
|
+
return [4 /*yield*/, callObject.join({ url: roomUrl, token: token })];
|
|
1323
|
+
case 4:
|
|
1324
|
+
// Join room; Daily handles playback of remote (bot) audio automatically
|
|
1325
|
+
_c.sent();
|
|
1326
|
+
participants = callObject.participants();
|
|
1327
|
+
if (participants === null || participants === void 0 ? void 0 : participants.local) {
|
|
1328
|
+
this.localSessionId = participants.local.session_id;
|
|
1329
|
+
}
|
|
1330
|
+
// Initial mute state: Daily starts with audio on
|
|
1331
|
+
this.micMutedSubject.next(!callObject.localAudio());
|
|
1332
|
+
return [3 /*break*/, 6];
|
|
1333
|
+
case 5:
|
|
1334
|
+
err_1 = _c.sent();
|
|
1335
|
+
this.cleanup();
|
|
1336
|
+
throw err_1;
|
|
1337
|
+
case 6: return [2 /*return*/];
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
});
|
|
1341
|
+
};
|
|
1342
|
+
DailyVoiceClientService.prototype.setupEventHandlers = function (call) {
|
|
1343
|
+
var _this = this;
|
|
1344
|
+
// active-speaker-change: primary source for real-time speaking detection.
|
|
1345
|
+
// Emits when the loudest participant changes; bot speaking = remote is active.
|
|
1346
|
+
call.on('active-speaker-change', function (event) {
|
|
1347
|
+
_this.ngZone.run(function () {
|
|
1348
|
+
var _a;
|
|
1349
|
+
var peerId = (_a = event === null || event === void 0 ? void 0 : event.activeSpeaker) === null || _a === void 0 ? void 0 : _a.peerId;
|
|
1350
|
+
if (!peerId || !_this.localSessionId) {
|
|
1351
|
+
_this.speakingSubject.next(false);
|
|
1352
|
+
_this.userSpeakingSubject.next(false);
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
var isLocal = peerId === _this.localSessionId;
|
|
1356
|
+
_this.userSpeakingSubject.next(isLocal);
|
|
1357
|
+
_this.speakingSubject.next(!isLocal);
|
|
1358
|
+
});
|
|
1359
|
+
});
|
|
1360
|
+
// track-started / track-stopped: fallback for immediate feedback when
|
|
1361
|
+
// remote (bot) audio track starts or stops. Ensures talking indicator
|
|
1362
|
+
// flips as soon as agent audio begins, without waiting for active-speaker-change.
|
|
1363
|
+
call.on('track-started', function (event) {
|
|
1364
|
+
_this.ngZone.run(function () {
|
|
1365
|
+
var _a, _b;
|
|
1366
|
+
var p = event === null || event === void 0 ? void 0 : event.participant;
|
|
1367
|
+
var type = (_a = event === null || event === void 0 ? void 0 : event.type) !== null && _a !== void 0 ? _a : (_b = event === null || event === void 0 ? void 0 : event.track) === null || _b === void 0 ? void 0 : _b.kind;
|
|
1368
|
+
if (p && !p.local && type === 'audio') {
|
|
1369
|
+
_this.speakingSubject.next(true);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
});
|
|
1373
|
+
call.on('track-stopped', function (event) {
|
|
1374
|
+
_this.ngZone.run(function () {
|
|
1375
|
+
var _a, _b;
|
|
1376
|
+
var p = event === null || event === void 0 ? void 0 : event.participant;
|
|
1377
|
+
var type = (_a = event === null || event === void 0 ? void 0 : event.type) !== null && _a !== void 0 ? _a : (_b = event === null || event === void 0 ? void 0 : event.track) === null || _b === void 0 ? void 0 : _b.kind;
|
|
1378
|
+
if (p && !p.local && type === 'audio') {
|
|
1379
|
+
_this.speakingSubject.next(false);
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
});
|
|
1383
|
+
call.on('left-meeting', function () {
|
|
1384
|
+
_this.ngZone.run(function () { return _this.cleanup(); });
|
|
1385
|
+
});
|
|
1386
|
+
call.on('error', function (event) {
|
|
1387
|
+
_this.ngZone.run(function () {
|
|
1388
|
+
var _a;
|
|
1389
|
+
console.error('DailyVoiceClient: Daily error', (_a = event === null || event === void 0 ? void 0 : event.errorMsg) !== null && _a !== void 0 ? _a : event);
|
|
1390
|
+
_this.cleanup();
|
|
1391
|
+
});
|
|
1392
|
+
});
|
|
1393
|
+
};
|
|
1394
|
+
/** Set mic muted state. */
|
|
1395
|
+
DailyVoiceClientService.prototype.setMuted = function (muted) {
|
|
1396
|
+
if (!this.callObject)
|
|
1397
|
+
return;
|
|
1398
|
+
this.callObject.setLocalAudio(!muted);
|
|
1399
|
+
this.micMutedSubject.next(muted);
|
|
1400
|
+
};
|
|
1401
|
+
/** Disconnect and cleanup. */
|
|
1402
|
+
DailyVoiceClientService.prototype.disconnect = function () {
|
|
1403
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1404
|
+
var e_1;
|
|
1405
|
+
return __generator(this, function (_c) {
|
|
1406
|
+
switch (_c.label) {
|
|
1407
|
+
case 0:
|
|
1408
|
+
if (!this.callObject) {
|
|
1409
|
+
this.cleanup();
|
|
1410
|
+
return [2 /*return*/];
|
|
1411
|
+
}
|
|
1412
|
+
_c.label = 1;
|
|
1413
|
+
case 1:
|
|
1414
|
+
_c.trys.push([1, 3, , 4]);
|
|
1415
|
+
return [4 /*yield*/, this.callObject.leave()];
|
|
1416
|
+
case 2:
|
|
1417
|
+
_c.sent();
|
|
1418
|
+
return [3 /*break*/, 4];
|
|
1419
|
+
case 3:
|
|
1420
|
+
e_1 = _c.sent();
|
|
1421
|
+
return [3 /*break*/, 4];
|
|
1422
|
+
case 4:
|
|
1423
|
+
this.cleanup();
|
|
1424
|
+
return [2 /*return*/];
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
});
|
|
1428
|
+
};
|
|
1429
|
+
DailyVoiceClientService.prototype.cleanup = function () {
|
|
1430
|
+
if (this.callObject) {
|
|
1431
|
+
this.callObject.destroy().catch(function () { });
|
|
1432
|
+
this.callObject = null;
|
|
1433
|
+
}
|
|
1434
|
+
if (this.localStream) {
|
|
1435
|
+
this.localStream.getTracks().forEach(function (t) { return t.stop(); });
|
|
1436
|
+
this.localStream = null;
|
|
1437
|
+
}
|
|
1438
|
+
this.localSessionId = null;
|
|
1439
|
+
this.speakingSubject.next(false);
|
|
1440
|
+
this.userSpeakingSubject.next(false);
|
|
1441
|
+
this.localStreamSubject.next(null);
|
|
1442
|
+
// Keep last micMuted state; will reset on next connect
|
|
1443
|
+
};
|
|
1444
|
+
return DailyVoiceClientService;
|
|
1445
|
+
}());
|
|
1446
|
+
DailyVoiceClientService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function DailyVoiceClientService_Factory() { return new DailyVoiceClientService(i0__namespace.ɵɵinject(i0__namespace.NgZone)); }, token: DailyVoiceClientService, providedIn: "root" });
|
|
1447
|
+
DailyVoiceClientService.decorators = [
|
|
1448
|
+
{ type: i0.Injectable, args: [{
|
|
1449
|
+
providedIn: 'root',
|
|
1450
|
+
},] }
|
|
1451
|
+
];
|
|
1452
|
+
DailyVoiceClientService.ctorParameters = function () { return [
|
|
1453
|
+
{ type: i0.NgZone }
|
|
1454
|
+
]; };
|
|
1455
|
+
|
|
1456
|
+
/**
|
|
1457
|
+
* Voice agent orchestrator. Coordinates WebSocket (signaling) and Daily.js (WebRTC audio).
|
|
1458
|
+
*
|
|
1459
|
+
* CRITICAL: This service must NEVER use Socket.IO or ngx-socket-io. Voice flow uses only:
|
|
1460
|
+
* - Native WebSocket (WebSocketVoiceClientService) for signaling (room_created, transcripts)
|
|
1461
|
+
* - Daily.js (DailyVoiceClientService) for WebRTC audio. Audio does NOT flow over WebSocket.
|
|
1462
|
+
*
|
|
1463
|
+
* - Maintains callState, statusText, duration, isMicMuted, isUserSpeaking, audioLevels
|
|
1464
|
+
* - Uses WebSocket for room_created and transcripts only (no audio)
|
|
1465
|
+
* - Uses Daily.js for all audio, mic, and real-time speaking detection
|
|
1466
|
+
*/
|
|
1149
1467
|
var VoiceAgentService = /** @class */ (function () {
|
|
1150
|
-
function VoiceAgentService(audioAnalyzer) {
|
|
1468
|
+
function VoiceAgentService(audioAnalyzer, wsClient, dailyClient) {
|
|
1151
1469
|
var _this = this;
|
|
1152
1470
|
this.audioAnalyzer = audioAnalyzer;
|
|
1153
|
-
this.
|
|
1154
|
-
this.
|
|
1155
|
-
this.userMediaStream = null;
|
|
1156
|
-
this.botAudioElement = null;
|
|
1471
|
+
this.wsClient = wsClient;
|
|
1472
|
+
this.dailyClient = dailyClient;
|
|
1157
1473
|
this.callStateSubject = new rxjs.BehaviorSubject('idle');
|
|
1158
1474
|
this.statusTextSubject = new rxjs.BehaviorSubject('');
|
|
1159
1475
|
this.durationSubject = new rxjs.BehaviorSubject('00:00');
|
|
@@ -1164,6 +1480,8 @@
|
|
|
1164
1480
|
this.botTranscriptSubject = new rxjs.Subject();
|
|
1165
1481
|
this.callStartTime = 0;
|
|
1166
1482
|
this.durationInterval = null;
|
|
1483
|
+
this.subscriptions = new rxjs.Subscription();
|
|
1484
|
+
this.destroy$ = new rxjs.Subject();
|
|
1167
1485
|
this.callState$ = this.callStateSubject.asObservable();
|
|
1168
1486
|
this.statusText$ = this.statusTextSubject.asObservable();
|
|
1169
1487
|
this.duration$ = this.durationSubject.asObservable();
|
|
@@ -1172,39 +1490,23 @@
|
|
|
1172
1490
|
this.audioLevels$ = this.audioLevelsSubject.asObservable();
|
|
1173
1491
|
this.userTranscript$ = this.userTranscriptSubject.asObservable();
|
|
1174
1492
|
this.botTranscript$ = this.botTranscriptSubject.asObservable();
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
});
|
|
1178
|
-
this.audioAnalyzer.isUserSpeaking$.subscribe(function (isSpeaking) {
|
|
1179
|
-
_this.isUserSpeakingSubject.next(isSpeaking);
|
|
1180
|
-
if (isSpeaking && _this.callStateSubject.value === 'connected') {
|
|
1181
|
-
_this.callStateSubject.next('listening');
|
|
1182
|
-
}
|
|
1183
|
-
else if (!isSpeaking && _this.callStateSubject.value === 'listening') {
|
|
1184
|
-
_this.callStateSubject.next('connected');
|
|
1185
|
-
}
|
|
1186
|
-
});
|
|
1493
|
+
// Waveform visualization only - do NOT use for speaking state
|
|
1494
|
+
this.subscriptions.add(this.audioAnalyzer.audioLevels$.subscribe(function (levels) { return _this.audioLevelsSubject.next(levels); }));
|
|
1187
1495
|
}
|
|
1496
|
+
VoiceAgentService.prototype.ngOnDestroy = function () {
|
|
1497
|
+
this.destroy$.next();
|
|
1498
|
+
this.subscriptions.unsubscribe();
|
|
1499
|
+
this.disconnect();
|
|
1500
|
+
};
|
|
1188
1501
|
/** Reset to idle state (e.g. when modal opens so user can click Start Call). */
|
|
1189
1502
|
VoiceAgentService.prototype.resetToIdle = function () {
|
|
1190
1503
|
if (this.callStateSubject.value === 'idle')
|
|
1191
1504
|
return;
|
|
1192
|
-
|
|
1193
|
-
clearInterval(this.durationInterval);
|
|
1194
|
-
this.durationInterval = null;
|
|
1195
|
-
}
|
|
1505
|
+
this.stopDurationTimer();
|
|
1196
1506
|
this.audioAnalyzer.stop();
|
|
1197
|
-
this.
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
this.userMediaStream.getTracks().forEach(function (track) { return track.stop(); });
|
|
1201
|
-
this.userMediaStream = null;
|
|
1202
|
-
}
|
|
1203
|
-
if (this.botAudioElement) {
|
|
1204
|
-
this.botAudioElement.pause();
|
|
1205
|
-
this.botAudioElement.srcObject = null;
|
|
1206
|
-
this.botAudioElement = null;
|
|
1207
|
-
}
|
|
1507
|
+
this.wsClient.disconnect();
|
|
1508
|
+
// Fire-and-forget: Daily disconnect is async; connect() will await if needed
|
|
1509
|
+
void this.dailyClient.disconnect();
|
|
1208
1510
|
this.callStateSubject.next('idle');
|
|
1209
1511
|
this.statusTextSubject.next('');
|
|
1210
1512
|
this.durationSubject.next('0:00');
|
|
@@ -1212,7 +1514,8 @@
|
|
|
1212
1514
|
VoiceAgentService.prototype.connect = function (apiUrl, token, botId, conversationId, apiKey, eventToken, eventUrl, domainAuthority) {
|
|
1213
1515
|
var _a;
|
|
1214
1516
|
return __awaiter(this, void 0, void 0, function () {
|
|
1215
|
-
var baseUrl,
|
|
1517
|
+
var baseUrl, postUrl, headers, res, json, wsUrl, error_1;
|
|
1518
|
+
var _this = this;
|
|
1216
1519
|
return __generator(this, function (_b) {
|
|
1217
1520
|
switch (_b.label) {
|
|
1218
1521
|
case 0:
|
|
@@ -1222,152 +1525,160 @@
|
|
|
1222
1525
|
}
|
|
1223
1526
|
_b.label = 1;
|
|
1224
1527
|
case 1:
|
|
1225
|
-
_b.trys.push([1,
|
|
1528
|
+
_b.trys.push([1, 4, , 6]);
|
|
1226
1529
|
this.callStateSubject.next('connecting');
|
|
1227
1530
|
this.statusTextSubject.next('Connecting...');
|
|
1228
1531
|
baseUrl = apiUrl.replace(/\/$/, '');
|
|
1229
|
-
|
|
1230
|
-
headers =
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
startBotParams = {
|
|
1239
|
-
endpoint: connectUrl.ws_url,
|
|
1240
|
-
headers: headers,
|
|
1241
|
-
requestData: {
|
|
1242
|
-
bot_id: botId,
|
|
1243
|
-
conversation_id: conversationId,
|
|
1244
|
-
voice: 'alloy',
|
|
1245
|
-
},
|
|
1532
|
+
postUrl = baseUrl + "/ai/ask-voice";
|
|
1533
|
+
headers = {
|
|
1534
|
+
'Content-Type': 'application/json',
|
|
1535
|
+
Authorization: "Bearer " + token,
|
|
1536
|
+
'domain-authority': domainAuthority,
|
|
1537
|
+
'eventtoken': eventToken,
|
|
1538
|
+
'eventurl': eventUrl,
|
|
1539
|
+
'hive-bot-id': botId,
|
|
1540
|
+
'x-api-key': apiKey,
|
|
1246
1541
|
};
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
return [4 /*yield*/, this.client.startBotAndConnect(startBotParams)];
|
|
1542
|
+
return [4 /*yield*/, fetch(postUrl, {
|
|
1543
|
+
method: 'POST',
|
|
1544
|
+
headers: headers,
|
|
1545
|
+
body: JSON.stringify({
|
|
1546
|
+
bot_id: botId,
|
|
1547
|
+
conversation_id: conversationId,
|
|
1548
|
+
voice: 'alloy',
|
|
1549
|
+
}),
|
|
1550
|
+
})];
|
|
1257
1551
|
case 2:
|
|
1258
|
-
_b.sent();
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
this.userMediaStream = new MediaStream([tracks.local.audio]);
|
|
1262
|
-
this.audioAnalyzer.start(this.userMediaStream);
|
|
1552
|
+
res = _b.sent();
|
|
1553
|
+
if (!res.ok) {
|
|
1554
|
+
throw new Error("HTTP " + res.status);
|
|
1263
1555
|
}
|
|
1264
|
-
|
|
1265
|
-
this.callStateSubject.next('connected');
|
|
1266
|
-
this.statusTextSubject.next('Connected');
|
|
1267
|
-
this.callStartTime = Date.now();
|
|
1268
|
-
this.startDurationTimer();
|
|
1269
|
-
return [3 /*break*/, 5];
|
|
1556
|
+
return [4 /*yield*/, res.json()];
|
|
1270
1557
|
case 3:
|
|
1558
|
+
json = _b.sent();
|
|
1559
|
+
wsUrl = (_a = json === null || json === void 0 ? void 0 : json.ws_url) !== null && _a !== void 0 ? _a : json === null || json === void 0 ? void 0 : json.rn_ws_url;
|
|
1560
|
+
if (!wsUrl || typeof wsUrl !== 'string') {
|
|
1561
|
+
throw new Error('No ws_url in response');
|
|
1562
|
+
}
|
|
1563
|
+
// Subscribe to room_created BEFORE connecting to avoid race
|
|
1564
|
+
this.wsClient.roomCreated$
|
|
1565
|
+
.pipe(operators.take(1), operators.takeUntil(this.destroy$))
|
|
1566
|
+
.subscribe(function (roomUrl) { return __awaiter(_this, void 0, void 0, function () {
|
|
1567
|
+
var err_1;
|
|
1568
|
+
return __generator(this, function (_b) {
|
|
1569
|
+
switch (_b.label) {
|
|
1570
|
+
case 0:
|
|
1571
|
+
_b.trys.push([0, 2, , 4]);
|
|
1572
|
+
return [4 /*yield*/, this.onRoomCreated(roomUrl)];
|
|
1573
|
+
case 1:
|
|
1574
|
+
_b.sent();
|
|
1575
|
+
return [3 /*break*/, 4];
|
|
1576
|
+
case 2:
|
|
1577
|
+
err_1 = _b.sent();
|
|
1578
|
+
console.error('Daily join failed:', err_1);
|
|
1579
|
+
this.callStateSubject.next('ended');
|
|
1580
|
+
this.statusTextSubject.next('Connection failed');
|
|
1581
|
+
return [4 /*yield*/, this.disconnect()];
|
|
1582
|
+
case 3:
|
|
1583
|
+
_b.sent();
|
|
1584
|
+
throw err_1;
|
|
1585
|
+
case 4: return [2 /*return*/];
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
}); });
|
|
1589
|
+
// Forward transcripts from WebSocket
|
|
1590
|
+
this.subscriptions.add(this.wsClient.userTranscript$
|
|
1591
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
1592
|
+
.subscribe(function (t) { return _this.userTranscriptSubject.next(t); }));
|
|
1593
|
+
this.subscriptions.add(this.wsClient.botTranscript$
|
|
1594
|
+
.pipe(operators.takeUntil(this.destroy$))
|
|
1595
|
+
.subscribe(function (t) { return _this.botTranscriptSubject.next(t); }));
|
|
1596
|
+
// Connect signaling WebSocket (no audio over WS)
|
|
1597
|
+
this.wsClient.connect(wsUrl);
|
|
1598
|
+
return [3 /*break*/, 6];
|
|
1599
|
+
case 4:
|
|
1271
1600
|
error_1 = _b.sent();
|
|
1272
1601
|
console.error('Error connecting voice agent:', error_1);
|
|
1273
1602
|
this.callStateSubject.next('ended');
|
|
1274
1603
|
return [4 /*yield*/, this.disconnect()];
|
|
1275
|
-
case
|
|
1604
|
+
case 5:
|
|
1276
1605
|
_b.sent();
|
|
1277
1606
|
this.statusTextSubject.next('Connection failed');
|
|
1278
1607
|
throw error_1;
|
|
1279
|
-
case
|
|
1608
|
+
case 6: return [2 /*return*/];
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
});
|
|
1612
|
+
};
|
|
1613
|
+
VoiceAgentService.prototype.onRoomCreated = function (roomUrl) {
|
|
1614
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1615
|
+
var _this = this;
|
|
1616
|
+
return __generator(this, function (_b) {
|
|
1617
|
+
switch (_b.label) {
|
|
1618
|
+
case 0:
|
|
1619
|
+
// Connect Daily.js for WebRTC audio
|
|
1620
|
+
return [4 /*yield*/, this.dailyClient.connect(roomUrl)];
|
|
1621
|
+
case 1:
|
|
1622
|
+
// Connect Daily.js for WebRTC audio
|
|
1623
|
+
_b.sent();
|
|
1624
|
+
// Waveform: use local mic stream from Daily client
|
|
1625
|
+
this.dailyClient.localStream$
|
|
1626
|
+
.pipe(operators.filter(function (s) { return s != null; }), operators.take(1))
|
|
1627
|
+
.subscribe(function (stream) {
|
|
1628
|
+
_this.audioAnalyzer.start(stream);
|
|
1629
|
+
});
|
|
1630
|
+
// Speaking state from Daily only (NOT from AudioAnalyzer).
|
|
1631
|
+
// User speaking overrides bot talking: if user speaks while bot talks, state = listening.
|
|
1632
|
+
this.subscriptions.add(this.dailyClient.userSpeaking$.subscribe(function (s) { return _this.isUserSpeakingSubject.next(s); }));
|
|
1633
|
+
this.subscriptions.add(rxjs.combineLatest([
|
|
1634
|
+
this.dailyClient.speaking$,
|
|
1635
|
+
this.dailyClient.userSpeaking$,
|
|
1636
|
+
]).subscribe(function (_b) {
|
|
1637
|
+
var _c = __read(_b, 2), bot = _c[0], user = _c[1];
|
|
1638
|
+
if (user) {
|
|
1639
|
+
_this.callStateSubject.next('listening');
|
|
1640
|
+
}
|
|
1641
|
+
else if (bot) {
|
|
1642
|
+
_this.callStateSubject.next('talking');
|
|
1643
|
+
}
|
|
1644
|
+
else if (_this.callStateSubject.value === 'talking' ||
|
|
1645
|
+
_this.callStateSubject.value === 'listening') {
|
|
1646
|
+
_this.callStateSubject.next('connected');
|
|
1647
|
+
}
|
|
1648
|
+
}));
|
|
1649
|
+
this.subscriptions.add(this.dailyClient.micMuted$.subscribe(function (muted) { return _this.isMicMutedSubject.next(muted); }));
|
|
1650
|
+
this.callStateSubject.next('connected');
|
|
1651
|
+
this.statusTextSubject.next('Connected');
|
|
1652
|
+
this.callStartTime = Date.now();
|
|
1653
|
+
this.startDurationTimer();
|
|
1654
|
+
return [2 /*return*/];
|
|
1280
1655
|
}
|
|
1281
1656
|
});
|
|
1282
1657
|
});
|
|
1283
1658
|
};
|
|
1284
1659
|
VoiceAgentService.prototype.disconnect = function () {
|
|
1285
1660
|
return __awaiter(this, void 0, void 0, function () {
|
|
1286
|
-
var error_2;
|
|
1287
1661
|
return __generator(this, function (_b) {
|
|
1288
1662
|
switch (_b.label) {
|
|
1289
1663
|
case 0:
|
|
1290
|
-
|
|
1291
|
-
if (this.durationInterval) {
|
|
1292
|
-
clearInterval(this.durationInterval);
|
|
1293
|
-
this.durationInterval = null;
|
|
1294
|
-
}
|
|
1664
|
+
this.stopDurationTimer();
|
|
1295
1665
|
this.audioAnalyzer.stop();
|
|
1296
|
-
|
|
1297
|
-
return [4 /*yield*/, this.
|
|
1666
|
+
// Daily first, then WebSocket
|
|
1667
|
+
return [4 /*yield*/, this.dailyClient.disconnect()];
|
|
1298
1668
|
case 1:
|
|
1669
|
+
// Daily first, then WebSocket
|
|
1299
1670
|
_b.sent();
|
|
1300
|
-
this.
|
|
1301
|
-
_b.label = 2;
|
|
1302
|
-
case 2:
|
|
1303
|
-
this.transport = null;
|
|
1304
|
-
if (this.userMediaStream) {
|
|
1305
|
-
this.userMediaStream.getTracks().forEach(function (track) { return track.stop(); });
|
|
1306
|
-
this.userMediaStream = null;
|
|
1307
|
-
}
|
|
1308
|
-
if (this.botAudioElement) {
|
|
1309
|
-
this.botAudioElement.pause();
|
|
1310
|
-
this.botAudioElement.srcObject = null;
|
|
1311
|
-
this.botAudioElement = null;
|
|
1312
|
-
}
|
|
1671
|
+
this.wsClient.disconnect();
|
|
1313
1672
|
this.callStateSubject.next('ended');
|
|
1314
1673
|
this.statusTextSubject.next('Call Ended');
|
|
1315
|
-
return [
|
|
1316
|
-
case 3:
|
|
1317
|
-
error_2 = _b.sent();
|
|
1318
|
-
console.error('Error disconnecting voice agent:', error_2);
|
|
1319
|
-
return [3 /*break*/, 4];
|
|
1320
|
-
case 4: return [2 /*return*/];
|
|
1674
|
+
return [2 /*return*/];
|
|
1321
1675
|
}
|
|
1322
1676
|
});
|
|
1323
1677
|
});
|
|
1324
1678
|
};
|
|
1325
1679
|
VoiceAgentService.prototype.toggleMic = function () {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
var newState = !this.client.isMicEnabled;
|
|
1329
|
-
this.client.enableMic(newState);
|
|
1330
|
-
this.isMicMutedSubject.next(!newState);
|
|
1331
|
-
};
|
|
1332
|
-
VoiceAgentService.prototype.setupEventHandlers = function () {
|
|
1333
|
-
var _this = this;
|
|
1334
|
-
if (!this.client)
|
|
1335
|
-
return;
|
|
1336
|
-
this.client.on(clientJs.RTVIEvent.BotStartedSpeaking, function () {
|
|
1337
|
-
_this.callStateSubject.next('talking');
|
|
1338
|
-
});
|
|
1339
|
-
this.client.on(clientJs.RTVIEvent.BotStoppedSpeaking, function () {
|
|
1340
|
-
if (_this.callStateSubject.value === 'talking') {
|
|
1341
|
-
_this.callStateSubject.next('connected');
|
|
1342
|
-
}
|
|
1343
|
-
});
|
|
1344
|
-
this.client.on(clientJs.RTVIEvent.TrackStarted, function (track, participant) {
|
|
1345
|
-
if (_this.botAudioElement && participant && !participant.local && track.kind === 'audio') {
|
|
1346
|
-
var stream = new MediaStream([track]);
|
|
1347
|
-
_this.botAudioElement.srcObject = stream;
|
|
1348
|
-
_this.botAudioElement.play().catch(console.error);
|
|
1349
|
-
}
|
|
1350
|
-
});
|
|
1351
|
-
this.client.on(clientJs.RTVIEvent.UserTranscript, function (data) {
|
|
1352
|
-
var _a;
|
|
1353
|
-
_this.userTranscriptSubject.next({
|
|
1354
|
-
text: data.text,
|
|
1355
|
-
final: (_a = data.final) !== null && _a !== void 0 ? _a : false,
|
|
1356
|
-
});
|
|
1357
|
-
});
|
|
1358
|
-
this.client.on(clientJs.RTVIEvent.BotTranscript, function (data) {
|
|
1359
|
-
if (data === null || data === void 0 ? void 0 : data.text) {
|
|
1360
|
-
_this.botTranscriptSubject.next(data.text);
|
|
1361
|
-
}
|
|
1362
|
-
});
|
|
1363
|
-
this.client.on(clientJs.RTVIEvent.Error, function () {
|
|
1364
|
-
_this.statusTextSubject.next('Error occurred');
|
|
1365
|
-
});
|
|
1366
|
-
this.client.on(clientJs.RTVIEvent.Disconnected, function () {
|
|
1367
|
-
_this.callStateSubject.next('ended');
|
|
1368
|
-
_this.statusTextSubject.next('Disconnected');
|
|
1369
|
-
_this.disconnect();
|
|
1370
|
-
});
|
|
1680
|
+
var current = this.isMicMutedSubject.value;
|
|
1681
|
+
this.dailyClient.setMuted(!current);
|
|
1371
1682
|
};
|
|
1372
1683
|
VoiceAgentService.prototype.startDurationTimer = function () {
|
|
1373
1684
|
var _this = this;
|
|
@@ -1379,19 +1690,27 @@
|
|
|
1379
1690
|
_this.durationSubject.next(minutes + ":" + String(seconds).padStart(2, '0'));
|
|
1380
1691
|
}
|
|
1381
1692
|
};
|
|
1382
|
-
updateDuration();
|
|
1693
|
+
updateDuration();
|
|
1383
1694
|
this.durationInterval = setInterval(updateDuration, 1000);
|
|
1384
1695
|
};
|
|
1696
|
+
VoiceAgentService.prototype.stopDurationTimer = function () {
|
|
1697
|
+
if (this.durationInterval) {
|
|
1698
|
+
clearInterval(this.durationInterval);
|
|
1699
|
+
this.durationInterval = null;
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1385
1702
|
return VoiceAgentService;
|
|
1386
1703
|
}());
|
|
1387
|
-
VoiceAgentService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function VoiceAgentService_Factory() { return new VoiceAgentService(i0__namespace.ɵɵinject(AudioAnalyzerService)); }, token: VoiceAgentService, providedIn: "root" });
|
|
1704
|
+
VoiceAgentService.ɵprov = i0__namespace.ɵɵdefineInjectable({ factory: function VoiceAgentService_Factory() { return new VoiceAgentService(i0__namespace.ɵɵinject(AudioAnalyzerService), i0__namespace.ɵɵinject(WebSocketVoiceClientService), i0__namespace.ɵɵinject(DailyVoiceClientService)); }, token: VoiceAgentService, providedIn: "root" });
|
|
1388
1705
|
VoiceAgentService.decorators = [
|
|
1389
1706
|
{ type: i0.Injectable, args: [{
|
|
1390
|
-
providedIn: 'root'
|
|
1707
|
+
providedIn: 'root',
|
|
1391
1708
|
},] }
|
|
1392
1709
|
];
|
|
1393
1710
|
VoiceAgentService.ctorParameters = function () { return [
|
|
1394
|
-
{ type: AudioAnalyzerService }
|
|
1711
|
+
{ type: AudioAnalyzerService },
|
|
1712
|
+
{ type: WebSocketVoiceClientService },
|
|
1713
|
+
{ type: DailyVoiceClientService }
|
|
1395
1714
|
]; };
|
|
1396
1715
|
|
|
1397
1716
|
var VOICE_MODAL_CONFIG = new i0.InjectionToken('VOICE_MODAL_CONFIG');
|
|
@@ -1652,6 +1971,8 @@
|
|
|
1652
1971
|
this.voiceModalOverlayRef = null;
|
|
1653
1972
|
this.voiceTranscriptSubscriptions = [];
|
|
1654
1973
|
this.domainAuthorityValue = 'prod-lite';
|
|
1974
|
+
/** True after Socket has been initialized for chat (avoids connecting on voice-only usage). */
|
|
1975
|
+
this.chatSocketInitialized = false;
|
|
1655
1976
|
this.isChatingWithAi = false;
|
|
1656
1977
|
this.readAllChunks = function (stream) {
|
|
1657
1978
|
var reader = stream.getReader();
|
|
@@ -1742,11 +2063,11 @@
|
|
|
1742
2063
|
}
|
|
1743
2064
|
}
|
|
1744
2065
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
2066
|
+
// Do NOT call initializeSocket() here. Socket.IO is for chat only; we defer
|
|
2067
|
+
// until the user actually uses chat (send message or start new conversation)
|
|
2068
|
+
// so that voice-only usage never triggers Socket.IO (avoids v2/v3 mismatch error).
|
|
2069
|
+
if (changes.orgId && changes.orgId.currentValue != changes.orgId.previousValue) {
|
|
2070
|
+
this.chatSocketInitialized = false;
|
|
1750
2071
|
}
|
|
1751
2072
|
};
|
|
1752
2073
|
ChatDrawerComponent.prototype.ngOnInit = function () {
|
|
@@ -1793,6 +2114,16 @@
|
|
|
1793
2114
|
_this.initializeSpeechRecognizer(token);
|
|
1794
2115
|
});
|
|
1795
2116
|
};
|
|
2117
|
+
/**
|
|
2118
|
+
* Call before chat actions so Socket.IO is used only for chat, not for voice.
|
|
2119
|
+
* Voice agent uses native WebSocket + Daily.js only; Socket.IO must not run during voice flow.
|
|
2120
|
+
*/
|
|
2121
|
+
ChatDrawerComponent.prototype.ensureChatSocket = function () {
|
|
2122
|
+
if (this.chatSocketInitialized || !this.orgId)
|
|
2123
|
+
return;
|
|
2124
|
+
this.chatSocketInitialized = true;
|
|
2125
|
+
this.initializeSocket();
|
|
2126
|
+
};
|
|
1796
2127
|
ChatDrawerComponent.prototype.initializeSocket = function () {
|
|
1797
2128
|
var _this = this;
|
|
1798
2129
|
try {
|
|
@@ -2171,6 +2502,7 @@
|
|
|
2171
2502
|
if (!this.input || this.loading) {
|
|
2172
2503
|
return;
|
|
2173
2504
|
}
|
|
2505
|
+
this.ensureChatSocket();
|
|
2174
2506
|
this.chatLog.push({
|
|
2175
2507
|
type: 'user',
|
|
2176
2508
|
message: this.processMessageForDisplay(this.input),
|
|
@@ -2194,6 +2526,7 @@
|
|
|
2194
2526
|
if (!inputMsg || this.loading) {
|
|
2195
2527
|
return;
|
|
2196
2528
|
}
|
|
2529
|
+
this.ensureChatSocket();
|
|
2197
2530
|
try {
|
|
2198
2531
|
chat.relatedListItems = [];
|
|
2199
2532
|
this.cdr.detectChanges();
|
|
@@ -3253,8 +3586,9 @@
|
|
|
3253
3586
|
this.conversationKey = this.conversationService.getKey(this.botId, true);
|
|
3254
3587
|
this.chatLog = [this.chatLog[0]];
|
|
3255
3588
|
this.isChatingWithAi = false;
|
|
3589
|
+
this.chatSocketInitialized = false;
|
|
3256
3590
|
setTimeout(function () {
|
|
3257
|
-
_this.
|
|
3591
|
+
_this.ensureChatSocket();
|
|
3258
3592
|
}, 200);
|
|
3259
3593
|
this.scrollToBottom();
|
|
3260
3594
|
this.cdr.detectChanges();
|
|
@@ -3557,6 +3891,10 @@
|
|
|
3557
3891
|
isDev: [{ type: i0.Input }]
|
|
3558
3892
|
};
|
|
3559
3893
|
|
|
3894
|
+
/**
|
|
3895
|
+
* Voice agent module. Uses native WebSocket + Daily.js only.
|
|
3896
|
+
* Does NOT use Socket.IO or ngx-socket-io.
|
|
3897
|
+
*/
|
|
3560
3898
|
var VoiceAgentModule = /** @class */ (function () {
|
|
3561
3899
|
function VoiceAgentModule() {
|
|
3562
3900
|
}
|
|
@@ -3572,7 +3910,9 @@
|
|
|
3572
3910
|
],
|
|
3573
3911
|
providers: [
|
|
3574
3912
|
VoiceAgentService,
|
|
3575
|
-
AudioAnalyzerService
|
|
3913
|
+
AudioAnalyzerService,
|
|
3914
|
+
WebSocketVoiceClientService,
|
|
3915
|
+
DailyVoiceClientService
|
|
3576
3916
|
],
|
|
3577
3917
|
exports: [
|
|
3578
3918
|
VoiceAgentModalComponent
|
|
@@ -3870,9 +4210,11 @@
|
|
|
3870
4210
|
exports["ɵc"] = ConversationService;
|
|
3871
4211
|
exports["ɵd"] = NotificationSocket;
|
|
3872
4212
|
exports["ɵe"] = TranslationService;
|
|
3873
|
-
exports["ɵf"] =
|
|
3874
|
-
exports["ɵg"] =
|
|
3875
|
-
exports["ɵh"] =
|
|
4213
|
+
exports["ɵf"] = WebSocketVoiceClientService;
|
|
4214
|
+
exports["ɵg"] = DailyVoiceClientService;
|
|
4215
|
+
exports["ɵh"] = VideoPlayerComponent;
|
|
4216
|
+
exports["ɵi"] = SafeHtmlPipe;
|
|
4217
|
+
exports["ɵj"] = BotHtmlEditorComponent;
|
|
3876
4218
|
|
|
3877
4219
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3878
4220
|
|