@guava-ai/guava-sdk 0.23.0 → 0.25.0
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/README.md +13 -3
- package/bin/example-runner.ts +138 -11
- package/dist/bin/example-runner.d.ts +23 -6
- package/dist/bin/example-runner.js +112 -9
- package/dist/bin/example-runner.js.map +1 -1
- package/dist/examples/{example.test.js → agent-testing.test.js} +2 -2
- package/dist/examples/agent-testing.test.js.map +1 -0
- package/dist/examples/help-desk.js +22 -8
- package/dist/examples/help-desk.js.map +1 -1
- package/dist/examples/multiple-agents.d.ts +1 -0
- package/dist/examples/multiple-agents.js +52 -0
- package/dist/examples/multiple-agents.js.map +1 -0
- package/dist/examples/property-insurance.js +14 -3
- package/dist/examples/property-insurance.js.map +1 -1
- package/dist/examples/restaurant-waitlist.js +9 -1
- package/dist/examples/restaurant-waitlist.js.map +1 -1
- package/dist/examples/scheduling-outbound.js +5 -6
- package/dist/examples/scheduling-outbound.js.map +1 -1
- package/dist/src/action-item.d.ts +4 -4
- package/dist/src/action-item.js +1 -1
- package/dist/src/action-item.js.map +1 -1
- package/dist/src/agent.d.ts +31 -11
- package/dist/src/agent.js +278 -135
- package/dist/src/agent.js.map +1 -1
- package/dist/src/call.d.ts +12 -1
- package/dist/src/call.js +87 -24
- package/dist/src/call.js.map +1 -1
- package/dist/src/client.d.ts +3 -0
- package/dist/src/client.js +6 -0
- package/dist/src/client.js.map +1 -1
- package/dist/src/commands.d.ts +37 -9
- package/dist/src/commands.js +13 -3
- package/dist/src/commands.js.map +1 -1
- package/dist/src/events.d.ts +28 -1
- package/dist/src/events.js +10 -2
- package/dist/src/events.js.map +1 -1
- package/dist/src/example-utils.d.ts +2 -0
- package/dist/src/example-utils.js +115 -0
- package/dist/src/example-utils.js.map +1 -0
- package/dist/src/health.d.ts +35 -0
- package/dist/src/health.js +99 -0
- package/dist/src/health.js.map +1 -0
- package/dist/src/helpers/index.d.ts +1 -0
- package/dist/src/helpers/index.js +9 -0
- package/dist/src/helpers/index.js.map +1 -0
- package/dist/src/helpers/llm.d.ts +43 -1
- package/dist/src/helpers/llm.js +247 -0
- package/dist/src/helpers/llm.js.map +1 -1
- package/dist/src/helpers/server-rag.d.ts +25 -0
- package/dist/src/helpers/server-rag.js +119 -0
- package/dist/src/helpers/server-rag.js.map +1 -0
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +4 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/runner.d.ts +10 -0
- package/dist/src/runner.js +101 -0
- package/dist/src/runner.js.map +1 -0
- package/dist/src/socket/protocol.d.ts +4 -4
- package/dist/src/testing/mocks.d.ts +6 -0
- package/dist/src/testing/mocks.js +12 -0
- package/dist/src/testing/mocks.js.map +1 -1
- package/dist/src/testing/session.d.ts +7 -0
- package/dist/src/testing/session.js +3 -22
- package/dist/src/testing/session.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/examples/{example.test.ts → agent-testing.test.ts} +1 -1
- package/examples/help-desk.ts +21 -12
- package/examples/multiple-agents.ts +18 -0
- package/examples/property-insurance.ts +15 -3
- package/examples/restaurant-waitlist.ts +10 -1
- package/examples/scheduling-outbound.ts +5 -6
- package/package.json +9 -1
- package/src/action-item.ts +1 -1
- package/src/agent.ts +273 -147
- package/src/call.ts +115 -24
- package/src/client.ts +7 -0
- package/src/commands.ts +16 -2
- package/src/events.ts +11 -1
- package/src/example-utils.ts +87 -0
- package/src/health.ts +120 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/llm.ts +170 -1
- package/src/helpers/server-rag.ts +132 -0
- package/src/index.ts +3 -1
- package/src/runner.ts +39 -0
- package/src/testing/mocks.ts +17 -0
- package/src/testing/session.ts +2 -22
- package/src/version.ts +1 -1
- package/dist/examples/example.test.js.map +0 -1
- /package/dist/examples/{example.test.d.ts → agent-testing.test.d.ts} +0 -0
package/dist/src/agent.js
CHANGED
|
@@ -122,22 +122,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
122
122
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
123
123
|
};
|
|
124
124
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
125
|
-
exports.Agent = void 0;
|
|
125
|
+
exports.Agent = exports._roleplayActionSchema = void 0;
|
|
126
126
|
const ws_1 = __importDefault(require("ws"));
|
|
127
|
-
const index_ts_1 = require("./index.js");
|
|
128
|
-
const logging_ts_1 = require("./logging.js");
|
|
129
|
-
const utils_ts_1 = require("./utils.js");
|
|
130
|
-
const webrtc_helper_ts_1 = require("./webrtc-helper.js");
|
|
131
127
|
const commands_ts_1 = require("./commands.js");
|
|
132
128
|
const events_ts_1 = require("./events.js");
|
|
133
|
-
const telemetry_ts_1 = require("./telemetry.js");
|
|
134
|
-
const client_ts_1 = require("./socket/client.js");
|
|
135
129
|
const guavadialer_events_ts_1 = require("./guavadialer-events.js");
|
|
130
|
+
const health_ts_1 = require("./health.js");
|
|
131
|
+
const llm_ts_1 = require("./helpers/llm.js");
|
|
132
|
+
const index_ts_1 = require("./index.js");
|
|
133
|
+
const logging_ts_1 = require("./logging.js");
|
|
134
|
+
const client_ts_1 = require("./socket/client.js");
|
|
136
135
|
const ListenInbound = __importStar(require("./socket/listen-inbound.js"));
|
|
137
|
-
const
|
|
138
|
-
const protocol_ts_1 = require("./testing/protocol.js");
|
|
136
|
+
const telemetry_ts_1 = require("./telemetry.js");
|
|
139
137
|
const chat_ts_1 = require("./testing/chat.js");
|
|
140
|
-
const
|
|
138
|
+
const protocol_ts_1 = require("./testing/protocol.js");
|
|
139
|
+
const session_ts_1 = require("./testing/session.js");
|
|
140
|
+
const z = __importStar(require("zod"));
|
|
141
|
+
const utils_ts_1 = require("./utils.js");
|
|
142
|
+
const webrtc_helper_ts_1 = require("./webrtc-helper.js");
|
|
143
|
+
exports._roleplayActionSchema = z.object({
|
|
144
|
+
action: z.enum(["speak", "hangup"]),
|
|
145
|
+
utterance: z.string().optional(),
|
|
146
|
+
});
|
|
141
147
|
let Agent = (() => {
|
|
142
148
|
let _classDecorators = [telemetry_ts_1.telemetryClient.trackClass()];
|
|
143
149
|
let _classDescriptor;
|
|
@@ -155,6 +161,8 @@ let Agent = (() => {
|
|
|
155
161
|
_name;
|
|
156
162
|
_organization;
|
|
157
163
|
_purpose;
|
|
164
|
+
_voice;
|
|
165
|
+
_acceptDtmfForNumbers;
|
|
158
166
|
_logger;
|
|
159
167
|
_client = new index_ts_1.Client();
|
|
160
168
|
_onCallReceived = async () => ({
|
|
@@ -170,12 +178,16 @@ let Agent = (() => {
|
|
|
170
178
|
_onActionRequested;
|
|
171
179
|
_onActionGeneric;
|
|
172
180
|
_onActionHandlers = {};
|
|
181
|
+
_onValidateHandlers = {};
|
|
173
182
|
_onSessionEnd;
|
|
183
|
+
_onEscalate;
|
|
174
184
|
_onDtmf;
|
|
175
185
|
constructor(args) {
|
|
176
186
|
this._name = args?.name;
|
|
177
187
|
this._organization = args?.organization;
|
|
178
188
|
this._purpose = args?.purpose;
|
|
189
|
+
this._voice = args?.voice;
|
|
190
|
+
this._acceptDtmfForNumbers = args?.acceptDtmf ?? true;
|
|
179
191
|
this._logger = (0, logging_ts_1.getDefaultLogger)();
|
|
180
192
|
}
|
|
181
193
|
onCallReceived(callback) {
|
|
@@ -206,6 +218,9 @@ let Agent = (() => {
|
|
|
206
218
|
this._onTaskCompleteGeneric = callbackOrTaskName;
|
|
207
219
|
}
|
|
208
220
|
}
|
|
221
|
+
onValidate(fieldKey, callback) {
|
|
222
|
+
this._onValidateHandlers[fieldKey] = callback;
|
|
223
|
+
}
|
|
209
224
|
onSearchQuery(fieldKey, callback) {
|
|
210
225
|
this._searchQueryHandlers[fieldKey] = callback;
|
|
211
226
|
}
|
|
@@ -228,6 +243,9 @@ let Agent = (() => {
|
|
|
228
243
|
onSessionEnd(callback) {
|
|
229
244
|
this._onSessionEnd = callback;
|
|
230
245
|
}
|
|
246
|
+
onEscalate(callback) {
|
|
247
|
+
this._onEscalate = callback;
|
|
248
|
+
}
|
|
231
249
|
onDtmf(callback) {
|
|
232
250
|
this._onDtmf = callback;
|
|
233
251
|
}
|
|
@@ -292,18 +310,64 @@ let Agent = (() => {
|
|
|
292
310
|
});
|
|
293
311
|
}
|
|
294
312
|
async listenPhone(phoneNumber) {
|
|
295
|
-
|
|
313
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
314
|
+
try {
|
|
315
|
+
const healthCtx = new health_ts_1.HealthContext();
|
|
316
|
+
const _server = __addDisposableResource(env_1, await (0, health_ts_1.getHealthServer)(healthCtx), true);
|
|
317
|
+
return this._listenInbound(healthCtx, { agent_number: phoneNumber });
|
|
318
|
+
}
|
|
319
|
+
catch (e_1) {
|
|
320
|
+
env_1.error = e_1;
|
|
321
|
+
env_1.hasError = true;
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
const result_1 = __disposeResources(env_1);
|
|
325
|
+
if (result_1)
|
|
326
|
+
await result_1;
|
|
327
|
+
}
|
|
296
328
|
}
|
|
297
329
|
async listenWebrtc(webrtcCode) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
330
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
331
|
+
try {
|
|
332
|
+
if (!webrtcCode) {
|
|
333
|
+
this._logger.info("No WebRTC code provided. Creating a temporary one.");
|
|
334
|
+
webrtcCode = await this._client.createWebrtcAgent(3600);
|
|
335
|
+
}
|
|
336
|
+
const healthCtx = new health_ts_1.HealthContext();
|
|
337
|
+
const _server = __addDisposableResource(env_2, await (0, health_ts_1.getHealthServer)(healthCtx), true);
|
|
338
|
+
return this._listenInbound(healthCtx, { webrtc_code: webrtcCode });
|
|
339
|
+
}
|
|
340
|
+
catch (e_2) {
|
|
341
|
+
env_2.error = e_2;
|
|
342
|
+
env_2.hasError = true;
|
|
343
|
+
}
|
|
344
|
+
finally {
|
|
345
|
+
const result_2 = __disposeResources(env_2);
|
|
346
|
+
if (result_2)
|
|
347
|
+
await result_2;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async listenSip(sipCode) {
|
|
351
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
352
|
+
try {
|
|
353
|
+
const healthCtx = new health_ts_1.HealthContext();
|
|
354
|
+
const _server = __addDisposableResource(env_3, await (0, health_ts_1.getHealthServer)(healthCtx), true);
|
|
355
|
+
return this._listenInbound(healthCtx, { sip_code: sipCode });
|
|
356
|
+
}
|
|
357
|
+
catch (e_3) {
|
|
358
|
+
env_3.error = e_3;
|
|
359
|
+
env_3.hasError = true;
|
|
360
|
+
}
|
|
361
|
+
finally {
|
|
362
|
+
const result_3 = __disposeResources(env_3);
|
|
363
|
+
if (result_3)
|
|
364
|
+
await result_3;
|
|
301
365
|
}
|
|
302
|
-
return this._listenInbound({ webrtc_code: webrtcCode });
|
|
303
366
|
}
|
|
304
367
|
async callLocal() {
|
|
305
368
|
const webrtcCode = await this._client.createWebrtcAgent(300);
|
|
306
|
-
|
|
369
|
+
// No health-server for call_local.
|
|
370
|
+
this._listenInbound(new health_ts_1.HealthContext(), { webrtc_code: webrtcCode }).catch((err) => {
|
|
307
371
|
this._logger.error("Listen loop error: %s", err);
|
|
308
372
|
});
|
|
309
373
|
await (0, webrtc_helper_ts_1.runWebrtcHelper)(webrtcCode, (0, utils_ts_1.getBaseUrl)());
|
|
@@ -320,15 +384,31 @@ let Agent = (() => {
|
|
|
320
384
|
}
|
|
321
385
|
}
|
|
322
386
|
else if (event.event_type === "task-done") {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
387
|
+
const errors = [];
|
|
388
|
+
for (const fieldKey of call._fieldKeysByTaskId[event.task_id] ?? []) {
|
|
389
|
+
const handler = this._onValidateHandlers[fieldKey];
|
|
390
|
+
if (handler) {
|
|
391
|
+
const result = await handler(call, await call.getField(fieldKey));
|
|
392
|
+
if (result !== true) {
|
|
393
|
+
const [, error] = result;
|
|
394
|
+
errors.push(error);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
326
397
|
}
|
|
327
|
-
|
|
328
|
-
await
|
|
398
|
+
if (errors.length > 0) {
|
|
399
|
+
await call.retryTask(errors.join(" "));
|
|
329
400
|
}
|
|
330
401
|
else {
|
|
331
|
-
this._logger.
|
|
402
|
+
this._logger.info(`Task ${event.task_id} completed.`);
|
|
403
|
+
if (this._onTaskCompleteGeneric !== undefined) {
|
|
404
|
+
await this._onTaskCompleteGeneric(call, event.task_id);
|
|
405
|
+
}
|
|
406
|
+
else if (event.task_id in this._onTaskCompleteHandlers) {
|
|
407
|
+
await this._onTaskCompleteHandlers[event.task_id](call);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
this._logger.warn(`No handler registered for completion of task '${event.task_id}'`);
|
|
411
|
+
}
|
|
332
412
|
}
|
|
333
413
|
}
|
|
334
414
|
else if (event.event_type === "agent-question") {
|
|
@@ -379,15 +459,20 @@ let Agent = (() => {
|
|
|
379
459
|
}
|
|
380
460
|
else if (event.event_type === "action-request") {
|
|
381
461
|
this._logger.info(`Received action request ${event.intent_id}: ${event.intent_summary}`);
|
|
382
|
-
let
|
|
462
|
+
let suggestions = [];
|
|
383
463
|
if (this._onActionRequested !== undefined) {
|
|
384
|
-
|
|
464
|
+
const result = await this._onActionRequested(call, event.intent_summary);
|
|
465
|
+
if (Array.isArray(result)) {
|
|
466
|
+
suggestions = result;
|
|
467
|
+
}
|
|
468
|
+
else if (result !== null && result !== undefined) {
|
|
469
|
+
suggestions = [result];
|
|
470
|
+
}
|
|
385
471
|
}
|
|
386
472
|
await call.sendCommand(commands_ts_1.ActionSuggestionCommand, {
|
|
387
473
|
command_type: "action-suggestion",
|
|
388
474
|
intent_id: event.intent_id,
|
|
389
|
-
|
|
390
|
-
action_description: suggestion?.description ?? "",
|
|
475
|
+
actions: suggestions.map((s) => ({ key: s.key, description: s.description ?? "" })),
|
|
391
476
|
});
|
|
392
477
|
}
|
|
393
478
|
else if (event.event_type === "execute-action") {
|
|
@@ -421,6 +506,17 @@ let Agent = (() => {
|
|
|
421
506
|
await this._onDtmf(call, event);
|
|
422
507
|
}
|
|
423
508
|
}
|
|
509
|
+
else if (event.event_type === "escalate") {
|
|
510
|
+
if (this._onEscalate !== undefined) {
|
|
511
|
+
await this._onEscalate(call, event);
|
|
512
|
+
}
|
|
513
|
+
else if (event.requested_by === "agent") {
|
|
514
|
+
await call.sendInstruction("No escalation target set. Apologize for not being able to help, ask them to try calling another time, and hang up the call immediately.");
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
await call.sendInstruction("Let them know there are no representatives available to take their call. Ask them if they would prefer to continue or to call another time.");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
424
520
|
else if (event.event_type === "error") {
|
|
425
521
|
this._logger.error(`The Guava agent reported an error: ${event.content}`);
|
|
426
522
|
}
|
|
@@ -428,30 +524,33 @@ let Agent = (() => {
|
|
|
428
524
|
this._logger.warn(`The Guava agent reported a warning: ${event.content}`);
|
|
429
525
|
}
|
|
430
526
|
}
|
|
431
|
-
async _initCall(variables = {}) {
|
|
432
|
-
const call = new index_ts_1.Call(variables);
|
|
527
|
+
async _initCall(callId, callInfo, variables = {}) {
|
|
528
|
+
const call = new index_ts_1.Call(callId, callInfo, variables);
|
|
433
529
|
await call.setPersona({
|
|
434
530
|
agentName: this._name,
|
|
435
531
|
agentPurpose: this._purpose,
|
|
436
532
|
organizationName: this._organization,
|
|
533
|
+
voice: this._voice,
|
|
437
534
|
});
|
|
438
535
|
await call.sendCommand(commands_ts_1.RegisteredHooksCommand, {
|
|
439
536
|
command_type: "registered-hooks",
|
|
440
537
|
has_on_question: this._onQuestion !== undefined,
|
|
441
538
|
has_on_intent: false,
|
|
442
539
|
has_on_action_requested: this._onActionRequested !== undefined,
|
|
540
|
+
has_on_escalate: this._onEscalate !== undefined,
|
|
541
|
+
accept_dtmf_for_numbers: this._acceptDtmfForNumbers,
|
|
443
542
|
});
|
|
444
543
|
if (this._onCallStart !== undefined) {
|
|
445
544
|
await this._onCallStart(call);
|
|
446
545
|
}
|
|
447
546
|
return call;
|
|
448
547
|
}
|
|
449
|
-
async _attachToCall(callId, initialVariables = {}, testSession, route = "v2/connect-call") {
|
|
450
|
-
const
|
|
548
|
+
async _attachToCall(callId, callInfo, initialVariables = {}, testSession, route = "v2/connect-call") {
|
|
549
|
+
const env_4 = { stack: [], error: void 0, hasError: false };
|
|
451
550
|
try {
|
|
452
|
-
const call = await this._initCall(initialVariables);
|
|
551
|
+
const call = await this._initCall(callId, callInfo, initialVariables);
|
|
453
552
|
const url = new URL(`${route}/${callId}`, this._client.getWebsocketBase());
|
|
454
|
-
const socket = __addDisposableResource(
|
|
553
|
+
const socket = __addDisposableResource(env_4, await new client_ts_1.GuavaSocket(`call-connection-${callId}`, url.toString(), this._client, (cmd) => cmd, (payload) => (0, events_ts_1.decodeEventDict)(payload), 18000).connect(), true);
|
|
455
554
|
await call.setDrain(async (commands) => {
|
|
456
555
|
for (const command of commands.splice(0)) {
|
|
457
556
|
socket.send(command);
|
|
@@ -473,67 +572,79 @@ let Agent = (() => {
|
|
|
473
572
|
throw e;
|
|
474
573
|
}
|
|
475
574
|
}
|
|
476
|
-
catch (
|
|
477
|
-
|
|
478
|
-
|
|
575
|
+
catch (e_4) {
|
|
576
|
+
env_4.error = e_4;
|
|
577
|
+
env_4.hasError = true;
|
|
479
578
|
}
|
|
480
579
|
finally {
|
|
481
|
-
const
|
|
482
|
-
if (
|
|
483
|
-
await
|
|
580
|
+
const result_4 = __disposeResources(env_4);
|
|
581
|
+
if (result_4)
|
|
582
|
+
await result_4;
|
|
484
583
|
}
|
|
485
584
|
}
|
|
486
|
-
async _listenInbound(conn) {
|
|
487
|
-
const
|
|
585
|
+
async _listenInbound(healthCtx, conn) {
|
|
586
|
+
const url = new URL("v2/listen-inbound", this._client.getWebsocketBase());
|
|
587
|
+
if ("agent_number" in conn) {
|
|
588
|
+
url.searchParams.set("phone_number", conn.agent_number);
|
|
589
|
+
}
|
|
590
|
+
else if ("webrtc_code" in conn) {
|
|
591
|
+
url.searchParams.set("webrtc_code", conn.webrtc_code);
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
url.searchParams.set("sip_code", conn.sip_code);
|
|
595
|
+
}
|
|
488
596
|
try {
|
|
489
|
-
const
|
|
490
|
-
if ("agent_number" in conn) {
|
|
491
|
-
url.searchParams.set("phone_number", conn.agent_number);
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
url.searchParams.set("webrtc_code", conn.webrtc_code);
|
|
495
|
-
}
|
|
496
|
-
const socket = __addDisposableResource(env_2, await new client_ts_1.GuavaSocket("listen-inbound", url.toString(), this._client, (msg) => msg, ListenInbound.decodeServerMessage).connect(), true);
|
|
597
|
+
const env_5 = { stack: [], error: void 0, hasError: false };
|
|
497
598
|
try {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
599
|
+
const socket = __addDisposableResource(env_5, await new client_ts_1.GuavaSocket("listen-inbound", url.toString(), this._client, (msg) => msg, ListenInbound.decodeServerMessage).connect(), true);
|
|
600
|
+
try {
|
|
601
|
+
for await (const msg of socket) {
|
|
602
|
+
switch (msg.message_type) {
|
|
603
|
+
case "listen-started":
|
|
604
|
+
healthCtx.ready();
|
|
605
|
+
if ("agent_number" in conn) {
|
|
606
|
+
this._logger.info("Listening on %s (%d other listeners).", conn.agent_number, msg.other_listeners);
|
|
607
|
+
}
|
|
608
|
+
else if ("webrtc_code" in conn) {
|
|
609
|
+
this._logger.info("Listening on WebRTC code %s (%d other listeners).", conn.webrtc_code, msg.other_listeners);
|
|
610
|
+
const debugUrl = new URL(`debug-webrtc?webrtc_code=${conn.webrtc_code}`, this._client.getHttpBase());
|
|
611
|
+
this._logger.info("Call your agent at: %s", debugUrl);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
this._logger.info("Listening on SIP code %s (%d other listeners).", conn.sip_code, msg.other_listeners);
|
|
615
|
+
}
|
|
616
|
+
break;
|
|
617
|
+
case "incoming-call":
|
|
618
|
+
socket.send({ message_type: "claim-call", call_id: msg.call_id });
|
|
619
|
+
break;
|
|
620
|
+
case "assign-call": {
|
|
621
|
+
const { call_id, call_info } = msg;
|
|
622
|
+
this._logger.info("Received call (session ID: %s)", call_id);
|
|
623
|
+
this._handleAssignedCall(call_id, call_info, socket).catch((err) => {
|
|
624
|
+
this._logger.error("Error handling assigned call %s: %s", call_id, err);
|
|
625
|
+
});
|
|
626
|
+
break;
|
|
508
627
|
}
|
|
509
|
-
break;
|
|
510
|
-
case "incoming-call":
|
|
511
|
-
socket.send({ message_type: "claim-call", call_id: msg.call_id });
|
|
512
|
-
break;
|
|
513
|
-
case "assign-call": {
|
|
514
|
-
const { call_id, call_info } = msg;
|
|
515
|
-
this._logger.info("Received call (session ID: %s)", call_id);
|
|
516
|
-
this._handleAssignedCall(call_id, call_info, socket).catch((err) => {
|
|
517
|
-
this._logger.error("Error handling assigned call %s: %s", call_id, err);
|
|
518
|
-
});
|
|
519
|
-
break;
|
|
520
628
|
}
|
|
521
629
|
}
|
|
522
630
|
}
|
|
631
|
+
catch (e) {
|
|
632
|
+
if (!(e instanceof client_ts_1.GuavaSocketClosedError))
|
|
633
|
+
throw e;
|
|
634
|
+
}
|
|
523
635
|
}
|
|
524
|
-
catch (
|
|
525
|
-
|
|
526
|
-
|
|
636
|
+
catch (e_5) {
|
|
637
|
+
env_5.error = e_5;
|
|
638
|
+
env_5.hasError = true;
|
|
639
|
+
}
|
|
640
|
+
finally {
|
|
641
|
+
const result_5 = __disposeResources(env_5);
|
|
642
|
+
if (result_5)
|
|
643
|
+
await result_5;
|
|
527
644
|
}
|
|
528
|
-
}
|
|
529
|
-
catch (e_2) {
|
|
530
|
-
env_2.error = e_2;
|
|
531
|
-
env_2.hasError = true;
|
|
532
645
|
}
|
|
533
646
|
finally {
|
|
534
|
-
|
|
535
|
-
if (result_2)
|
|
536
|
-
await result_2;
|
|
647
|
+
healthCtx.stopped();
|
|
537
648
|
}
|
|
538
649
|
}
|
|
539
650
|
async _handleAssignedCall(callId, callInfo, socket, initialVariables = {}) {
|
|
@@ -545,7 +656,7 @@ let Agent = (() => {
|
|
|
545
656
|
else {
|
|
546
657
|
this._logger.info("Accepting call %s.", callId);
|
|
547
658
|
socket.send({ message_type: "answer-call", call_id: callId });
|
|
548
|
-
await this._attachToCall(callId, initialVariables);
|
|
659
|
+
await this._attachToCall(callId, callInfo, initialVariables);
|
|
549
660
|
}
|
|
550
661
|
}
|
|
551
662
|
/**
|
|
@@ -553,8 +664,7 @@ let Agent = (() => {
|
|
|
553
664
|
*/
|
|
554
665
|
async callPhone(fromNumber, toNumber, variables = {}) {
|
|
555
666
|
const url = new URL("v2/create-outbound", this._client.getHttpBase());
|
|
556
|
-
|
|
557
|
-
url.searchParams.set("from_number", fromNumber);
|
|
667
|
+
url.searchParams.set("from_number", fromNumber);
|
|
558
668
|
url.searchParams.set("to_number", toNumber);
|
|
559
669
|
const response = await (0, utils_ts_1.fetchOrThrow)(url, {
|
|
560
670
|
method: "POST",
|
|
@@ -562,7 +672,13 @@ let Agent = (() => {
|
|
|
562
672
|
});
|
|
563
673
|
const { call_id } = (await response.json());
|
|
564
674
|
this._logger.info("Outbound call created with session ID: %s", call_id);
|
|
565
|
-
|
|
675
|
+
const callInfo = {
|
|
676
|
+
call_type: "pstn",
|
|
677
|
+
from_number: fromNumber ?? null,
|
|
678
|
+
to_number: toNumber,
|
|
679
|
+
caller_id: null,
|
|
680
|
+
};
|
|
681
|
+
await this._attachToCall(call_id, callInfo, variables);
|
|
566
682
|
}
|
|
567
683
|
/**
|
|
568
684
|
* Run the agent against a live test session.
|
|
@@ -594,7 +710,13 @@ let Agent = (() => {
|
|
|
594
710
|
});
|
|
595
711
|
const sessionStarted = protocol_ts_1.SessionStarted.parse(JSON.parse(rawFirst));
|
|
596
712
|
const testSession = new session_ts_1.TestSession(ws, this._client);
|
|
597
|
-
const
|
|
713
|
+
const testCallInfo = {
|
|
714
|
+
call_type: "pstn",
|
|
715
|
+
from_number: null,
|
|
716
|
+
to_number: "+15555555555",
|
|
717
|
+
caller_id: null,
|
|
718
|
+
};
|
|
719
|
+
const attachPromise = this._attachToCall(sessionStarted.session_id, testCallInfo, variables, testSession).catch((err) => {
|
|
598
720
|
this._logger.error("Error in _attachToCall during test: %s", err);
|
|
599
721
|
});
|
|
600
722
|
try {
|
|
@@ -626,15 +748,6 @@ let Agent = (() => {
|
|
|
626
748
|
* assert(session.executedActions.includes("sales"));
|
|
627
749
|
*/
|
|
628
750
|
async roleplay(roleplayPrompt, variables = {}) {
|
|
629
|
-
const roleplaySchema = {
|
|
630
|
-
type: "object",
|
|
631
|
-
properties: {
|
|
632
|
-
action: { type: "string", enum: ["speak", "hangup"] },
|
|
633
|
-
utterance: { type: "string" },
|
|
634
|
-
},
|
|
635
|
-
required: ["action"],
|
|
636
|
-
additionalProperties: false,
|
|
637
|
-
};
|
|
638
751
|
return this.test(async (session) => {
|
|
639
752
|
let snapshotLen = 0;
|
|
640
753
|
try {
|
|
@@ -655,8 +768,8 @@ Conversation:
|
|
|
655
768
|
${transcript || "(The agent has not spoken yet)"}
|
|
656
769
|
|
|
657
770
|
Choose "speak" and provide your next utterance, or choose "hangup" if the conversation has naturally concluded.`;
|
|
658
|
-
const raw = await (0, llm_ts_1._generate)(this._client, prompt,
|
|
659
|
-
const action = JSON.parse(raw);
|
|
771
|
+
const raw = await (0, llm_ts_1._generate)(this._client, prompt, z.toJSONSchema(exports._roleplayActionSchema));
|
|
772
|
+
const action = exports._roleplayActionSchema.parse(JSON.parse(raw));
|
|
660
773
|
if (action.action === "hangup") {
|
|
661
774
|
this._logger.info("(Roleplay Session) [caller hangs up]");
|
|
662
775
|
break;
|
|
@@ -716,6 +829,8 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
|
|
|
716
829
|
name: this._name,
|
|
717
830
|
organization: this._organization,
|
|
718
831
|
purpose: this._purpose,
|
|
832
|
+
voice: this._voice,
|
|
833
|
+
acceptDtmf: this._acceptDtmfForNumbers,
|
|
719
834
|
});
|
|
720
835
|
cloned._onCallReceived = this._onCallReceived;
|
|
721
836
|
cloned._onCallStart = this._onCallStart;
|
|
@@ -725,61 +840,75 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
|
|
|
725
840
|
cloned._onTaskCompleteGeneric = this._onTaskCompleteGeneric;
|
|
726
841
|
cloned._onTaskCompleteHandlers = { ...this._onTaskCompleteHandlers };
|
|
727
842
|
cloned._searchQueryHandlers = { ...this._searchQueryHandlers };
|
|
843
|
+
cloned._onValidateHandlers = { ...this._onValidateHandlers };
|
|
728
844
|
cloned._onActionRequested = this._onActionRequested;
|
|
729
845
|
cloned._onActionGeneric = this._onActionGeneric;
|
|
730
846
|
cloned._onActionHandlers = { ...this._onActionHandlers };
|
|
731
847
|
cloned._onSessionEnd = this._onSessionEnd;
|
|
848
|
+
cloned._onEscalate = this._onEscalate;
|
|
732
849
|
cloned._onDtmf = this._onDtmf;
|
|
733
850
|
return cloned;
|
|
734
851
|
}
|
|
735
|
-
async _serveCampaign(campaignCode) {
|
|
736
|
-
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
852
|
+
async _serveCampaign(healthCtx, campaignCode) {
|
|
737
853
|
try {
|
|
738
|
-
const
|
|
739
|
-
const campaignResponse = await (0, utils_ts_1.fetchOrThrow)(campaignUrl, {
|
|
740
|
-
headers: await this._client.headers(),
|
|
741
|
-
});
|
|
742
|
-
const campaign = (await campaignResponse.json());
|
|
743
|
-
const wsUrl = new URL(`v1/serve-campaign/${campaign.id}`, this._client.getWebsocketBase());
|
|
744
|
-
this._logger.info("Connecting to campaign '%s' (id: %s).", campaign.name, campaign.id);
|
|
745
|
-
const socket = __addDisposableResource(env_3, await new client_ts_1.GuavaSocket("serve-campaign", wsUrl.toString(), this._client, (msg) => msg, guavadialer_events_ts_1.decodeServerMessage).connect(), true);
|
|
746
|
-
const activeCalls = [];
|
|
854
|
+
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
747
855
|
try {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
856
|
+
const campaignUrl = new URL(`v1/campaigns/${campaignCode}`, this._client.getHttpBase());
|
|
857
|
+
const campaignResponse = await (0, utils_ts_1.fetchOrThrow)(campaignUrl, {
|
|
858
|
+
headers: await this._client.headers(),
|
|
859
|
+
});
|
|
860
|
+
const campaign = (await campaignResponse.json());
|
|
861
|
+
const wsUrl = new URL(`v1/serve-campaign/${campaign.id}`, this._client.getWebsocketBase());
|
|
862
|
+
this._logger.info("Connecting to campaign '%s' (id: %s).", campaign.name, campaign.id);
|
|
863
|
+
const socket = __addDisposableResource(env_6, await new client_ts_1.GuavaSocket("serve-campaign", wsUrl.toString(), this._client, (msg) => msg, guavadialer_events_ts_1.decodeServerMessage).connect(), true);
|
|
864
|
+
const activeCalls = [];
|
|
865
|
+
try {
|
|
866
|
+
for await (const msg of socket) {
|
|
867
|
+
switch (msg.message_type) {
|
|
868
|
+
case "listen-started":
|
|
869
|
+
this._logger.info("Listening for calls on campaign '%s'. Ready.", campaign.name);
|
|
870
|
+
healthCtx.ready();
|
|
871
|
+
break;
|
|
872
|
+
case "initiate-and-assign-call": {
|
|
873
|
+
const { call_id, contact_data } = msg;
|
|
874
|
+
const data = contact_data;
|
|
875
|
+
const logPhone = data?.phone_number ?? "?";
|
|
876
|
+
this._logger.info("Ready to make call, id %s — initiating call setup and dispatch for contact %s.", call_id, logPhone);
|
|
877
|
+
activeCalls.push((async () => {
|
|
878
|
+
socket.send({ message_type: "controller-ready", call_id });
|
|
879
|
+
const variables = data?.data ?? {};
|
|
880
|
+
const campaignCallInfo = {
|
|
881
|
+
call_type: "pstn",
|
|
882
|
+
from_number: null,
|
|
883
|
+
to_number: data?.phone_number ?? "",
|
|
884
|
+
caller_id: null,
|
|
885
|
+
};
|
|
886
|
+
await this._attachToCall(call_id, campaignCallInfo, variables, undefined, "v2/connect-campaign-call");
|
|
887
|
+
})());
|
|
888
|
+
break;
|
|
889
|
+
}
|
|
764
890
|
}
|
|
765
891
|
}
|
|
766
892
|
}
|
|
893
|
+
catch (e) {
|
|
894
|
+
if (!(e instanceof client_ts_1.GuavaSocketClosedError))
|
|
895
|
+
throw e;
|
|
896
|
+
this._logger.info("Campaign '%s' disconnected.", campaign.name);
|
|
897
|
+
}
|
|
898
|
+
await Promise.all(activeCalls);
|
|
767
899
|
}
|
|
768
|
-
catch (
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
900
|
+
catch (e_6) {
|
|
901
|
+
env_6.error = e_6;
|
|
902
|
+
env_6.hasError = true;
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
const result_6 = __disposeResources(env_6);
|
|
906
|
+
if (result_6)
|
|
907
|
+
await result_6;
|
|
772
908
|
}
|
|
773
|
-
await Promise.all(activeCalls);
|
|
774
|
-
}
|
|
775
|
-
catch (e_3) {
|
|
776
|
-
env_3.error = e_3;
|
|
777
|
-
env_3.hasError = true;
|
|
778
909
|
}
|
|
779
910
|
finally {
|
|
780
|
-
|
|
781
|
-
if (result_3)
|
|
782
|
-
await result_3;
|
|
911
|
+
healthCtx.stopped();
|
|
783
912
|
}
|
|
784
913
|
}
|
|
785
914
|
/**
|
|
@@ -794,7 +923,21 @@ Choose "speak" and provide your next utterance, or choose "hangup" if the conver
|
|
|
794
923
|
* await agent.attachCampaign("gcmp-abc123");
|
|
795
924
|
*/
|
|
796
925
|
async attachCampaign(campaignCode) {
|
|
797
|
-
|
|
926
|
+
const env_7 = { stack: [], error: void 0, hasError: false };
|
|
927
|
+
try {
|
|
928
|
+
const healthCtx = new health_ts_1.HealthContext();
|
|
929
|
+
const _server = __addDisposableResource(env_7, await (0, health_ts_1.getHealthServer)(healthCtx), true);
|
|
930
|
+
return this._serveCampaign(healthCtx, campaignCode);
|
|
931
|
+
}
|
|
932
|
+
catch (e_7) {
|
|
933
|
+
env_7.error = e_7;
|
|
934
|
+
env_7.hasError = true;
|
|
935
|
+
}
|
|
936
|
+
finally {
|
|
937
|
+
const result_7 = __disposeResources(env_7);
|
|
938
|
+
if (result_7)
|
|
939
|
+
await result_7;
|
|
940
|
+
}
|
|
798
941
|
}
|
|
799
942
|
/* ===== Aliases to be removed at some point. ===== */
|
|
800
943
|
/** @deprecated Use {@link listenPhone} instead. */
|