@guava-ai/guava-sdk 0.3.0 → 0.4.1
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.internal.md +13 -0
- package/README.md +50 -8
- package/bin/example-runner.js +16 -7
- package/dist/examples/credit-card-activation.js +94 -112
- package/dist/examples/credit-card-activation.js.map +1 -1
- package/dist/examples/property-insurance.js +12 -26
- package/dist/examples/property-insurance.js.map +1 -1
- package/dist/examples/scheduling-outbound.d.ts +1 -0
- package/dist/examples/scheduling-outbound.js +77 -0
- package/dist/examples/scheduling-outbound.js.map +1 -0
- package/dist/examples/thai-palace.js +25 -43
- package/dist/examples/thai-palace.js.map +1 -1
- package/dist/package.json +9 -5
- package/dist/src/action_item.d.ts +34 -12
- package/dist/src/action_item.js +34 -7
- package/dist/src/action_item.js.map +1 -1
- package/dist/src/call-controller.d.ts +137 -0
- package/dist/src/call-controller.js +433 -0
- package/dist/src/call-controller.js.map +1 -0
- package/dist/src/commands.d.ts +67 -27
- package/dist/src/commands.js +41 -27
- package/dist/src/commands.js.map +1 -1
- package/dist/src/events.d.ts +47 -30
- package/dist/src/events.js +42 -36
- package/dist/src/events.js.map +1 -1
- package/dist/src/example_data.d.ts +1 -0
- package/dist/src/example_data.js +33 -0
- package/dist/src/example_data.js.map +1 -1
- package/dist/src/helpers/openai.d.ts +12 -1
- package/dist/src/helpers/openai.js +168 -68
- package/dist/src/helpers/openai.js.map +1 -1
- package/dist/src/index.d.ts +6 -121
- package/dist/src/index.js +249 -483
- package/dist/src/index.js.map +1 -1
- package/dist/src/logging.d.ts +2 -1
- package/dist/src/logging.js +32 -7
- package/dist/src/logging.js.map +1 -1
- package/dist/src/telemetry.d.ts +23 -0
- package/dist/src/telemetry.js +98 -0
- package/dist/src/telemetry.js.map +1 -0
- package/dist/src/utils.d.ts +3 -0
- package/dist/src/utils.js +28 -0
- package/dist/src/utils.js.map +1 -0
- package/examples/biome.json +5 -0
- package/examples/credit-card-activation.ts +20 -26
- package/examples/property-insurance.ts +6 -16
- package/examples/scheduling-outbound.ts +97 -0
- package/examples/thai-palace.ts +10 -13
- package/package.json +9 -5
- package/src/action_item.ts +53 -13
- package/src/call-controller.ts +451 -0
- package/src/commands.ts +58 -42
- package/src/events.ts +66 -51
- package/src/example_data.ts +42 -0
- package/src/helpers/openai.ts +73 -18
- package/src/index.ts +81 -403
- package/src/logging.ts +39 -7
- package/src/telemetry.ts +125 -0
- package/src/utils.ts +32 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# (Guava Internal) Typescript SDK Instructions
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
Run `npm install` to install dependencies.
|
|
6
|
+
|
|
7
|
+
## Running Examples
|
|
8
|
+
|
|
9
|
+
If you're modifying an example and want to run it, use the following.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx tsx ./examples/scheduling-outbound.ts +1...
|
|
13
|
+
```
|
package/README.md
CHANGED
|
@@ -1,19 +1,61 @@
|
|
|
1
1
|
# Guava Typescript SDK
|
|
2
|
+

|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
This library allows you to build Guava voice agents using TypeScript or Javascript. Currently only NodeJS is officially supported as a runtime.
|
|
4
5
|
|
|
6
|
+
## Documentation
|
|
7
|
+
|
|
8
|
+
Full documentation for the TypeScript SDK can be found at [https://docs.goguava.ai/typescript-sdk/](https://docs.goguava.ai/typescript-sdk/)
|
|
9
|
+
|
|
10
|
+
## Try an Example
|
|
11
|
+
|
|
12
|
+
Export two environment variables. You should have received these in your beta invite email.
|
|
5
13
|
```bash
|
|
6
|
-
export GUAVA_API_KEY="..."
|
|
7
|
-
export GUAVA_AGENT_NUMBER="..."
|
|
14
|
+
$ export GUAVA_API_KEY="..." # Your API key for authentication.
|
|
15
|
+
$ export GUAVA_AGENT_NUMBER="..." # A phone number for your agent to use.
|
|
8
16
|
```
|
|
9
17
|
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
Run an outbound phone call example using `npx`. Replace the phone number with your own and your agent will call you.
|
|
12
19
|
```bash
|
|
13
|
-
npx
|
|
20
|
+
$ npx @guava-ai/guava-sdk scheduling-outbound +15556667777 # Your agent will call this number.
|
|
14
21
|
```
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Install the SDK using your preferred package manager.
|
|
26
|
+
|
|
17
27
|
```bash
|
|
18
|
-
|
|
28
|
+
$ npm install @guava-ai/guava-sdk
|
|
29
|
+
$ yarn add @guava-ai/guava-sdk
|
|
30
|
+
$ pnpm add @guava-ai/guava-sdk
|
|
19
31
|
```
|
|
32
|
+
|
|
33
|
+
The SDK can be used with Javascript, but TypeScript is highly recommended.
|
|
34
|
+
|
|
35
|
+
## Basic Usage
|
|
36
|
+
|
|
37
|
+
The Guava SDK is primarily used by subclassing `guava.CallController`. `CallController` subclasses implement callbacks that steer the call in real-time.
|
|
38
|
+
|
|
39
|
+
You can make outbound calls using a `CallController` instance.
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import * as guava from "@guava-ai/guava-sdk";
|
|
43
|
+
|
|
44
|
+
// Make an outbound call with a call controller.
|
|
45
|
+
new guava.Client().createOutbound(
|
|
46
|
+
agentNumber, toNumber,
|
|
47
|
+
new MyCallController(),
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You can also accept inbound calls using Guava. You don't need to setup a server — inbound calls can be received on your dev machine, including behind NATs and most firewalls.
|
|
52
|
+
Instead of passing a controller, you will pass a factory function that constructs a controller upon receipt of a call.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// Attach a listener to the phone number.
|
|
56
|
+
// Spawn new call controllers as calls come in.
|
|
57
|
+
new guava.Client().listenInbound(
|
|
58
|
+
{ agent_number: agentNumber },
|
|
59
|
+
() => new MyCallController(),
|
|
60
|
+
);
|
|
61
|
+
```
|
package/bin/example-runner.js
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { readdirSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const examplesURL = new URL("../dist/examples/", import.meta.url);
|
|
7
|
+
const examplesDir = fileURLToPath(examplesURL);
|
|
8
|
+
const availableExamples = readdirSync(examplesDir).filter(f => f.endsWith(".js")).map(f => f.replace(/\.js$/, ""));
|
|
9
|
+
|
|
3
10
|
const exampleName = process.argv[2];
|
|
4
11
|
if (!exampleName) {
|
|
5
12
|
console.error("Usage: guava-example <example-name> <example-args>");
|
|
13
|
+
console.error("Available examples:", availableExamples.join(", "))
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!availableExamples.includes(exampleName)) {
|
|
18
|
+
console.error(`Unknown example "${exampleName}". Available examples: ${availableExamples.join(", ")}`);
|
|
6
19
|
process.exit(1);
|
|
7
20
|
}
|
|
8
21
|
|
|
9
22
|
const run = async () => {
|
|
10
23
|
try {
|
|
11
24
|
// ESM dynamic import works in both ESM packages and CJS CLI files in modern Node
|
|
12
|
-
const exampleModule = await import(new URL(
|
|
13
|
-
exampleModule.run(process.argv.slice(3))
|
|
25
|
+
const exampleModule = await import(new URL(`${exampleName}.js`, examplesURL));
|
|
26
|
+
await exampleModule.run(process.argv.slice(3));
|
|
14
27
|
} catch (err) {
|
|
15
|
-
|
|
16
|
-
console.error(`Couldn't find example: ${exampleName}`)
|
|
17
|
-
} else {
|
|
18
|
-
console.error(err);
|
|
19
|
-
}
|
|
28
|
+
console.error(err);
|
|
20
29
|
process.exit(1);
|
|
21
30
|
}
|
|
22
31
|
};
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { IntentRecognizer } from "../src/helpers/openai.js";
|
|
11
2
|
import * as guava from "../src/index.js";
|
|
12
3
|
const CUSTOMER_DB = [
|
|
@@ -19,13 +10,14 @@ const CUSTOMER_DB = [
|
|
|
19
10
|
},
|
|
20
11
|
];
|
|
21
12
|
function findCustomerBySSN(ssn) {
|
|
22
|
-
return CUSTOMER_DB.find((c) => c.ssn
|
|
13
|
+
return CUSTOMER_DB.find((c) => c.ssn === ssn);
|
|
23
14
|
}
|
|
24
15
|
const ORGANIZATION_NAME = "Harper Valley Bank";
|
|
25
16
|
class CreditCardActivationController extends guava.CallController {
|
|
17
|
+
choices = ["activate credit card", "anything else"];
|
|
18
|
+
intentRecognizer;
|
|
26
19
|
constructor(logger) {
|
|
27
20
|
super(logger);
|
|
28
|
-
this.choices = ["activate credit card", "anything else"];
|
|
29
21
|
this.intentRecognizer = new IntentRecognizer(this.choices, logger);
|
|
30
22
|
this.setPersona({
|
|
31
23
|
organizationName: ORGANIZATION_NAME,
|
|
@@ -34,125 +26,115 @@ class CreditCardActivationController extends guava.CallController {
|
|
|
34
26
|
this.readScript(`Hello, thank you for calling the credit card activation line for ${ORGANIZATION_NAME}. My name is Grace. Are you here to activate your credit card?`);
|
|
35
27
|
this.acceptCall();
|
|
36
28
|
}
|
|
37
|
-
onIntent(intent) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
async onIntent(intent) {
|
|
30
|
+
const choice = await this.intentRecognizer.classify(intent);
|
|
31
|
+
this.logger.info(`Chosen intent: ${choice}`);
|
|
32
|
+
if (choice === "activate credit card") {
|
|
33
|
+
await this.activateCreditCard();
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return "Unfortunately I'm not able to help with that.";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async findCustomer() {
|
|
41
|
+
let customer;
|
|
42
|
+
let cardNumber;
|
|
43
|
+
while (true) {
|
|
44
|
+
await this.awaitTask({
|
|
45
|
+
checklist: [
|
|
46
|
+
guava.Field({
|
|
47
|
+
description: "Could you give me your social security number?",
|
|
48
|
+
key: "social_security_number",
|
|
49
|
+
fieldType: "integer",
|
|
50
|
+
required: true,
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
const ssn_data = this.getField("social_security_number");
|
|
55
|
+
let ssn;
|
|
56
|
+
if (typeof ssn_data === "string") {
|
|
57
|
+
ssn = ssn_data;
|
|
44
58
|
}
|
|
45
59
|
else {
|
|
46
|
-
|
|
60
|
+
// Should we assume all payloads are strings? or leave room by returning unknown
|
|
61
|
+
ssn = JSON.stringify(ssn_data);
|
|
47
62
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
let customer;
|
|
53
|
-
let cardNumber;
|
|
54
|
-
while (true) {
|
|
55
|
-
yield this.awaitTask({
|
|
56
|
-
checklist: [
|
|
57
|
-
{
|
|
58
|
-
item_type: "field",
|
|
59
|
-
description: "Could you give me your social security number?",
|
|
60
|
-
key: "social_security_number",
|
|
61
|
-
field_type: "integer",
|
|
62
|
-
required: true,
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
});
|
|
66
|
-
const ssn_data = this.getField("social_security_number");
|
|
67
|
-
let ssn;
|
|
68
|
-
if (typeof ssn_data == "string") {
|
|
69
|
-
ssn = ssn_data;
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
// Should we assume all payloads are strings? or leave room by returning unknown
|
|
73
|
-
ssn = JSON.stringify(ssn_data);
|
|
74
|
-
}
|
|
75
|
-
customer = findCustomerBySSN(ssn);
|
|
76
|
-
if (!customer) {
|
|
77
|
-
this.sendInstruction("We were unable to identify the customer using the SSN they provided. Let the caller know this, and ask if they have the correct social security number.");
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
yield this.awaitTask({
|
|
81
|
-
objective: "We were able to identify the customer using the Social Security Number they have provided. We're going to confirm the client's name.",
|
|
82
|
-
checklist: [
|
|
83
|
-
{
|
|
84
|
-
item_type: "field",
|
|
85
|
-
description: `We're going to confirm the client's name. Am I speaking with ${customer.name}?`,
|
|
86
|
-
key: "is_client",
|
|
87
|
-
field_type: "multiple_choice",
|
|
88
|
-
choices: ["yes", "no"],
|
|
89
|
-
required: true,
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
});
|
|
93
|
-
if (this.getField("is_client") == "no") {
|
|
94
|
-
this.sendInstruction("We were unable to identify the client's name in our files. Let the caller know this, and re-ask their social security number.");
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
63
|
+
customer = findCustomerBySSN(ssn);
|
|
64
|
+
if (!customer) {
|
|
65
|
+
this.sendInstruction("We were unable to identify the customer using the SSN they provided. Let the caller know this, and ask if they have the correct social security number.");
|
|
100
66
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
67
|
+
else {
|
|
68
|
+
await this.awaitTask({
|
|
69
|
+
objective: "We were able to identify the customer using the Social Security Number they have provided. We're going to confirm the client's name.",
|
|
104
70
|
checklist: [
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
71
|
+
guava.Field({
|
|
72
|
+
description: `We're going to confirm the client's name. Am I speaking with ${customer.name}?`,
|
|
73
|
+
key: "is_client",
|
|
74
|
+
fieldType: "multiple_choice",
|
|
75
|
+
choices: ["yes", "no"],
|
|
110
76
|
required: true,
|
|
111
|
-
},
|
|
77
|
+
}),
|
|
112
78
|
],
|
|
113
79
|
});
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this.sendInstruction("We were unable to find the matching card number in our system. Let the caller know this, and re-ask for the credit card number.");
|
|
80
|
+
if (this.getField("is_client") === "no") {
|
|
81
|
+
this.sendInstruction("We were unable to identify the client's name in our files. Let the caller know this, and re-ask their social security number.");
|
|
117
82
|
}
|
|
118
83
|
else {
|
|
119
|
-
this.sendInstruction("We were able to find the matching card number in our system. Let the caller know this, and ask for security code on their card.");
|
|
120
84
|
break;
|
|
121
85
|
}
|
|
122
86
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
87
|
+
}
|
|
88
|
+
this.sendInstruction("We were able to find the client's name in our files. Proceed to ask for their card number.");
|
|
89
|
+
while (true) {
|
|
90
|
+
await this.awaitTask({
|
|
91
|
+
checklist: [
|
|
92
|
+
guava.Field({
|
|
93
|
+
fieldType: "integer",
|
|
94
|
+
description: "Could you read me the digits on the front of your credit card?",
|
|
95
|
+
key: "credit_card_number",
|
|
96
|
+
required: true,
|
|
97
|
+
}),
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
cardNumber = this.getField("credit_card_number");
|
|
101
|
+
if (!(cardNumber in customer.unactivated_cards)) {
|
|
102
|
+
this.sendInstruction("We were unable to find the matching card number in our system. Let the caller know this, and re-ask for the credit card number.");
|
|
143
103
|
}
|
|
144
|
-
|
|
145
|
-
|
|
104
|
+
else {
|
|
105
|
+
this.sendInstruction("We were able to find the matching card number in our system. Let the caller know this, and ask for security code on their card.");
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const correctCvv = customer.unactivated_cards[cardNumber];
|
|
110
|
+
while (true) {
|
|
111
|
+
await this.awaitTask({
|
|
112
|
+
checklist: [
|
|
113
|
+
guava.Field({
|
|
114
|
+
fieldType: "integer",
|
|
115
|
+
key: "security_code",
|
|
116
|
+
description: "To wrap up, could I get the security code on your card?",
|
|
117
|
+
required: true,
|
|
118
|
+
}),
|
|
119
|
+
],
|
|
120
|
+
});
|
|
121
|
+
const security_code = this.getField("security_code");
|
|
122
|
+
if (security_code !== correctCvv.toString()) {
|
|
123
|
+
this.sendInstruction("We were unable to match the security code to the credit card. Let the caller know this and re-ask for the security code.");
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this.hangup("Explain to the caller that their credit card has now been activated. Thank them for using the bank's services, and hang up.");
|
|
146
130
|
}
|
|
147
|
-
activateCreditCard() {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
yield this.findCustomer();
|
|
151
|
-
});
|
|
131
|
+
async activateCreditCard() {
|
|
132
|
+
this.sendInstruction("We are starting the credit card activation process, which starts with asking the caller for their social security number.");
|
|
133
|
+
await this.findCustomer();
|
|
152
134
|
}
|
|
153
135
|
}
|
|
154
136
|
export function run(_args) {
|
|
155
|
-
|
|
137
|
+
new guava.Client().listenInbound({
|
|
156
138
|
agent_number: process.env.GUAVA_AGENT_NUMBER,
|
|
157
139
|
}, (logger) => new CreditCardActivationController(logger));
|
|
158
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credit-card-activation.js","sourceRoot":"","sources":["../../examples/credit-card-activation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credit-card-activation.js","sourceRoot":"","sources":["../../examples/credit-card-activation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,35 +1,21 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import * as guava from "../src/index.js";
|
|
11
2
|
import { DocumentQA } from "../src/helpers/openai.js";
|
|
12
3
|
import { PROPERTY_INSURANCE_POLICY } from "../src/example_data.js";
|
|
13
4
|
class InsuranceCallController extends guava.CallController {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
yield this.setPersona({
|
|
21
|
-
organizationName: "Harper Valley Property Insurance",
|
|
22
|
-
});
|
|
23
|
-
this.setTask({
|
|
24
|
-
objective: "You are making an outbound call to a potential customer. Your task is to answer questions regarding property insurance policy until there are no more questions.",
|
|
25
|
-
});
|
|
5
|
+
documentQA;
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.documentQA = new DocumentQA("harper-valley-property-insurance", PROPERTY_INSURANCE_POLICY);
|
|
9
|
+
this.setPersona({
|
|
10
|
+
organizationName: "Harper Valley Property Insurance",
|
|
26
11
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
-
return yield this.documentQA.ask(question);
|
|
12
|
+
this.setTask({
|
|
13
|
+
objective: "You are making an outbound call to a potential customer. Your task is to answer questions regarding property insurance policy until there are no more questions.",
|
|
31
14
|
});
|
|
32
15
|
}
|
|
16
|
+
async onQuestion(question) {
|
|
17
|
+
return await this.documentQA.ask(question);
|
|
18
|
+
}
|
|
33
19
|
}
|
|
34
20
|
export function run(args) {
|
|
35
21
|
const [phone] = args;
|
|
@@ -37,7 +23,7 @@ export function run(args) {
|
|
|
37
23
|
console.error("Usage: guava-example property-insurance <phone>");
|
|
38
24
|
process.exit(1);
|
|
39
25
|
}
|
|
40
|
-
new guava.Client().createOutbound(process.env.GUAVA_AGENT_NUMBER, phone,
|
|
26
|
+
new guava.Client().createOutbound(process.env.GUAVA_AGENT_NUMBER, phone, new InsuranceCallController());
|
|
41
27
|
}
|
|
42
28
|
if (import.meta.main) {
|
|
43
29
|
run(process.argv.slice(2));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"property-insurance.js","sourceRoot":"","sources":["../../examples/property-insurance.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"property-insurance.js","sourceRoot":"","sources":["../../examples/property-insurance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAEnE,MAAM,uBAAwB,SAAQ,KAAK,CAAC,cAAc;IAChD,UAAU,CAAa;IAC/B;QACE,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,CAAC,kCAAkC,EAAE,yBAAyB,CAAC,CAAC;QAEhG,IAAI,CAAC,UAAU,CAAC;YACd,gBAAgB,EAAE,kCAAkC;SACrD,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC;YACX,SAAS,EACP,kKAAkK;SACrK,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK,CAAC,UAAU,CAAC,QAAgB;QACxC,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;CACF;AAED,MAAM,UAAU,GAAG,CAAC,IAAc;IAChC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAErB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,cAAc,CAC/B,OAAO,CAAC,GAAG,CAAC,kBAAmB,EAC/B,KAAK,EACL,IAAI,uBAAuB,EAAE,CAC9B,CAAC;AACJ,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACrB,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(args: string[]): void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as guava from "../src/index.js";
|
|
2
|
+
import { DatetimeFilter } from "../src/helpers/openai.js";
|
|
3
|
+
import { mockAppointmentsForFuture } from "../src/example_data.js";
|
|
4
|
+
class SchedulingController extends guava.CallController {
|
|
5
|
+
patientName;
|
|
6
|
+
datetimeFilter;
|
|
7
|
+
constructor(patientName) {
|
|
8
|
+
super();
|
|
9
|
+
this.patientName = patientName;
|
|
10
|
+
// This is a basic appointment time selector that pulls from a mock list of appointments.
|
|
11
|
+
// In production, you would likely replace this with your own agentic scheduling backend.
|
|
12
|
+
this.datetimeFilter = new DatetimeFilter({
|
|
13
|
+
sourceList: mockAppointmentsForFuture(),
|
|
14
|
+
});
|
|
15
|
+
// Use setPersona to set basic information about the agent, as well as it's high-level purpose.
|
|
16
|
+
this.setPersona({
|
|
17
|
+
organizationName: "Bright Smile Dental",
|
|
18
|
+
agentName: "Grace",
|
|
19
|
+
agentPurpose: `You are calling ${patientName} to help them schedule a dental appointment`,
|
|
20
|
+
});
|
|
21
|
+
// reachPerson is a convenience function to confirm that we are talking to the intended recipient.
|
|
22
|
+
this.reachPerson(this.patientName, {
|
|
23
|
+
onSuccess: () => this.scheduleRecipient(),
|
|
24
|
+
onFailure: () => this.recipientUnavailable(),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
scheduleRecipient() {
|
|
28
|
+
// In the branch where we reached the intended participant,
|
|
29
|
+
// set a task to collect the desired appointment time.
|
|
30
|
+
this.setTask({
|
|
31
|
+
// The check list is an ordered list of items for the agent to go through.
|
|
32
|
+
checklist: [
|
|
33
|
+
// guava.Say instructions are verbatim.
|
|
34
|
+
guava.Say("Let me look to see what appointment times we have available."),
|
|
35
|
+
// We have one field to collect, which is the appointment_time.
|
|
36
|
+
guava.Field({
|
|
37
|
+
key: "appointment_time",
|
|
38
|
+
fieldType: "calendar_slot",
|
|
39
|
+
description: "Find a time that works for the caller",
|
|
40
|
+
// This field has a choice generator attached. The agent will invoke this function
|
|
41
|
+
// with a natural language query, allowing the caller to select between the different options.
|
|
42
|
+
// The agent can potentially invoke the callback multiple times if the caller rejects the options
|
|
43
|
+
// or provides a new query.
|
|
44
|
+
choiceGenerator: async (query) => {
|
|
45
|
+
// Use the simple DatetimeFilter class we instantiated earlier.
|
|
46
|
+
const result = await this.datetimeFilter.filter(query, { maxResults: 3 });
|
|
47
|
+
this.logger.info("Choice generator results: %s", JSON.stringify(result));
|
|
48
|
+
return result;
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
guava.Say("Your appointment has been confirmed! Have a nice day."),
|
|
52
|
+
],
|
|
53
|
+
}, () => this.hangup("Thank them for their time and hang up the call."));
|
|
54
|
+
}
|
|
55
|
+
recipientUnavailable() {
|
|
56
|
+
this.hangup("Apologize for your mistake and hang up the call.");
|
|
57
|
+
}
|
|
58
|
+
onSessionDone() {
|
|
59
|
+
// This callback is invoked at the end of the bot session.
|
|
60
|
+
const selectedTime = this.getField("appointment_time");
|
|
61
|
+
if (selectedTime) {
|
|
62
|
+
this.logger.info(`Appointment confirmed for: ${selectedTime}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function run(args) {
|
|
67
|
+
const [toNumber, patientName = "Benjamin Buttons"] = args;
|
|
68
|
+
if (!toNumber) {
|
|
69
|
+
console.error("Usage: guava-example scheduling-outbound <phone> [name]");
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
new guava.Client().createOutbound(process.env.GUAVA_AGENT_NUMBER, toNumber, new SchedulingController(patientName));
|
|
73
|
+
}
|
|
74
|
+
if (import.meta.main) {
|
|
75
|
+
run(process.argv.slice(2));
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=scheduling-outbound.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduling-outbound.js","sourceRoot":"","sources":["../../examples/scheduling-outbound.ts"],"names":[],"mappings":"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"}
|