@guava-ai/guava-sdk 0.24.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/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/scheduling-outbound.js +3 -2
- package/dist/examples/scheduling-outbound.js.map +1 -1
- package/dist/src/agent.d.ts +13 -5
- package/dist/src/agent.js +9 -14
- package/dist/src/agent.js.map +1 -1
- package/dist/src/call.d.ts +3 -0
- package/dist/src/call.js +63 -23
- 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/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/helpers/llm.d.ts +10 -0
- package/dist/src/helpers/llm.js +54 -27
- package/dist/src/helpers/llm.js.map +1 -1
- 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/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/multiple-agents.ts +18 -0
- package/examples/scheduling-outbound.ts +3 -2
- package/package.json +5 -1
- package/src/agent.ts +13 -21
- package/src/call.ts +78 -23
- package/src/client.ts +7 -0
- package/src/example-utils.ts +87 -0
- package/src/helpers/llm.ts +23 -31
- package/src/index.ts +3 -1
- package/src/runner.ts +39 -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/src/call.ts
CHANGED
|
@@ -33,6 +33,29 @@ export type ReachPersonOutcome = {
|
|
|
33
33
|
nextActionPreview?: string;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
export const DEFAULT_REACH_PERSON_OUTCOMES: ReachPersonOutcome[] = [
|
|
37
|
+
{ key: "available", description: "The intended contact is confirmed on the line." },
|
|
38
|
+
{
|
|
39
|
+
key: "unavailable",
|
|
40
|
+
description:
|
|
41
|
+
"The contact could not be reached. A third party, gatekeeper, or IVR was unable to transfer the call to the contact.",
|
|
42
|
+
},
|
|
43
|
+
{ key: "voicemail", description: "An answering machine or voicemail system was reached." },
|
|
44
|
+
{ key: "wrong_number", description: "The number does not reach the intended contact." },
|
|
45
|
+
{
|
|
46
|
+
key: "do_not_contact",
|
|
47
|
+
description: "The person on the line has indicated this number should not be called.",
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
function voicemailHangupInstruction(): string {
|
|
52
|
+
return "DO NOT leave a message. REMAIN SILENT AND HANG UP WITHOUT RESPONDING.";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function voicemailMessageInstruction(message: string): string {
|
|
56
|
+
return `Say this message VERBATIM: "${message}" Then hang up.`;
|
|
57
|
+
}
|
|
58
|
+
|
|
36
59
|
@telemetryClient.trackClass()
|
|
37
60
|
export class Call {
|
|
38
61
|
private _callId: string;
|
|
@@ -242,19 +265,38 @@ export class Call {
|
|
|
242
265
|
|
|
243
266
|
async reachPerson(
|
|
244
267
|
contactFullName: string,
|
|
245
|
-
options: {
|
|
268
|
+
options: {
|
|
269
|
+
outcomes?: ReachPersonOutcome[];
|
|
270
|
+
greeting?: string;
|
|
271
|
+
voicemailMessage?: string;
|
|
272
|
+
voicemailHangup?: boolean;
|
|
273
|
+
} = {},
|
|
246
274
|
) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
275
|
+
if ((await this.getVariable("_voicemail_handler")) === "set_voicemail_action") {
|
|
276
|
+
throw new Error(
|
|
277
|
+
"Cannot call reachPerson() after setVoicemailAction(). " +
|
|
278
|
+
"Use the voicemailMessage or voicemailHangup parameters on reachPerson() instead.",
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
await this.setVariable("_voicemail_handler", "reach_person");
|
|
282
|
+
|
|
283
|
+
if (options.voicemailMessage && options.voicemailHangup) {
|
|
284
|
+
throw new Error("Cannot specify both 'voicemailMessage' and 'voicemailHangup'.");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const outcomes = options.outcomes ?? DEFAULT_REACH_PERSON_OUTCOMES;
|
|
288
|
+
|
|
289
|
+
let voicemailRule: string;
|
|
290
|
+
if (options.voicemailHangup) {
|
|
291
|
+
voicemailRule = voicemailHangupInstruction();
|
|
292
|
+
} else if (options.voicemailMessage) {
|
|
293
|
+
voicemailRule = voicemailMessageInstruction(options.voicemailMessage);
|
|
294
|
+
} else {
|
|
295
|
+
voicemailRule = "Leave an appropriate voicemail message.";
|
|
296
|
+
}
|
|
255
297
|
|
|
256
298
|
const availabilityDescription =
|
|
257
|
-
`The availability of ${contactFullName}
|
|
299
|
+
`The availability of ${contactFullName}.` +
|
|
258
300
|
(outcomes.some((o) => o.description)
|
|
259
301
|
? "\nDetailed descriptions of each choice:\n" +
|
|
260
302
|
outcomes
|
|
@@ -266,7 +308,7 @@ export class Call {
|
|
|
266
308
|
const checklist: (FieldItem | SayItem | string)[] = [
|
|
267
309
|
options.greeting !== undefined
|
|
268
310
|
? Say(options.greeting)
|
|
269
|
-
: `Greet the person who answered the phone. Notify them who you are calling on behalf of and the purpose of the call. Ask to speak with ${contactFullName}
|
|
311
|
+
: `Greet the person, IVR, or system who answered the phone. Notify them who you are calling on behalf of and the purpose of the call. Ask to speak with ${contactFullName}. Do not greet if you detect an answering machine or voicemail system.`,
|
|
270
312
|
{
|
|
271
313
|
item_type: "field",
|
|
272
314
|
key: "contact_availability",
|
|
@@ -281,38 +323,51 @@ export class Call {
|
|
|
281
323
|
.map((o) => `- ${o.key} → ${o.nextActionPreview}`);
|
|
282
324
|
if (nextActionLines.length > 0) {
|
|
283
325
|
checklist.push(
|
|
284
|
-
"If a next action is defined below for the value of `contact_availability`, briefly
|
|
326
|
+
"If a next action is defined below for the recorded value of `contact_availability`, briefly let the contact know while you perform it.\n" +
|
|
285
327
|
nextActionLines.join("\n"),
|
|
286
328
|
);
|
|
287
329
|
}
|
|
288
330
|
|
|
289
331
|
const objective = `\
|
|
290
332
|
OBJECTIVE:
|
|
291
|
-
Your goal is to reach ${contactFullName} and
|
|
333
|
+
Your goal is to reach ${contactFullName} and confirm they are on the line.
|
|
292
334
|
|
|
293
335
|
RULES:
|
|
294
|
-
1. If
|
|
295
|
-
- Politely ask to speak with ${contactFullName}
|
|
296
|
-
- Wait to be transferred or for ${contactFullName} to come to the phone
|
|
297
|
-
|
|
336
|
+
1. If someone other than ${contactFullName} answers - including a person or IVR:
|
|
337
|
+
- Politely ask to speak with ${contactFullName}, or navigate menus and prompts to reach them.
|
|
338
|
+
- Wait to be transferred or for ${contactFullName} to come to the phone.
|
|
339
|
+
- If ${contactFullName} cannot be reached, record \`contact_availability\` appropriately.
|
|
340
|
+
2. Once ${contactFullName} is confirmed on the line:
|
|
298
341
|
- Briefly restate who you are and the purpose of your call
|
|
299
|
-
-
|
|
300
|
-
3.
|
|
342
|
+
- Record their availability as available, or equivalent, in \`contact_availability\`.
|
|
343
|
+
3. If it is clearly a wrong number or you have been asked not to call, politely end the call and hang up.
|
|
344
|
+
4. If you reach an answering machine or voicemail: ${voicemailRule}
|
|
345
|
+
`;
|
|
301
346
|
|
|
347
|
+
const completionCriteria = `\
|
|
302
348
|
TASK COMPLETION REQUIREMENTS:
|
|
303
|
-
- The availability of ${contactFullName} must be recorded in \`contact_availability
|
|
349
|
+
- The availability of ${contactFullName} must be recorded in \`contact_availability\`.
|
|
350
|
+
`;
|
|
304
351
|
|
|
305
|
-
await this.setTask({ taskId: "reach_person", objective, checklist });
|
|
352
|
+
await this.setTask({ taskId: "reach_person", objective, checklist, completionCriteria });
|
|
306
353
|
}
|
|
307
354
|
|
|
308
355
|
async setVoicemailAction(action: { hangup: true } | { message: string }) {
|
|
356
|
+
if ((await this.getVariable("_voicemail_handler")) === "reach_person") {
|
|
357
|
+
throw new Error(
|
|
358
|
+
"Cannot call setVoicemailAction() after reachPerson(). " +
|
|
359
|
+
"Use the voicemailMessage or voicemailHangup parameters on reachPerson() instead.",
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
await this.setVariable("_voicemail_handler", "set_voicemail_action");
|
|
363
|
+
|
|
309
364
|
if ("hangup" in action) {
|
|
310
365
|
await this.sendInstruction(
|
|
311
|
-
|
|
366
|
+
`If you encounter an answering machine, ${voicemailHangupInstruction()} You should only do this when it's clear you are unable to reach the person.`,
|
|
312
367
|
);
|
|
313
368
|
} else {
|
|
314
369
|
await this.sendInstruction(
|
|
315
|
-
`If you encounter an answering machine,
|
|
370
|
+
`If you encounter an answering machine, ${voicemailMessageInstruction(action.message)} You should only do this when it's clear you are unable to reach the person.`,
|
|
316
371
|
);
|
|
317
372
|
}
|
|
318
373
|
}
|
package/src/client.ts
CHANGED
|
@@ -172,6 +172,13 @@ export class Client {
|
|
|
172
172
|
return body.webrtc_code;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
async listNumbers(): Promise<{ phoneNumber: string }[]> {
|
|
176
|
+
const url = new URL("v1/phone-numbers", this.getHttpBase());
|
|
177
|
+
const response = await fetchOrThrow(url, { headers: await this.headers() });
|
|
178
|
+
const items = (await response.json()) as { phone_number: string }[];
|
|
179
|
+
return items.map((item) => ({ phoneNumber: item.phone_number }));
|
|
180
|
+
}
|
|
181
|
+
|
|
175
182
|
/**
|
|
176
183
|
* Sends an SMS message from one of your Guava numbers.
|
|
177
184
|
* @param fromNumber - One of your Guava numbers (E.164). Must have SMS configured.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as process from "node:process";
|
|
2
|
+
import { Client } from "./client.ts";
|
|
3
|
+
|
|
4
|
+
export async function getAgentNumber(): Promise<string> {
|
|
5
|
+
if (process.env.GUAVA_AGENT_NUMBER) return process.env.GUAVA_AGENT_NUMBER;
|
|
6
|
+
|
|
7
|
+
const client = new Client();
|
|
8
|
+
const numbers = await client.listNumbers();
|
|
9
|
+
|
|
10
|
+
if (numbers.length === 0) {
|
|
11
|
+
console.error("No agent phone numbers found. Please purchase a number first.");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
if (numbers.length === 1) return numbers[0].phoneNumber;
|
|
15
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
16
|
+
return terminalPicker(
|
|
17
|
+
numbers.map((n) => n.phoneNumber),
|
|
18
|
+
"Multiple agent phone numbers found. Select one",
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
console.error(
|
|
22
|
+
`Multiple agent phone numbers found: ${numbers.map((n) => n.phoneNumber).join(", ")}. Please set GUAVA_AGENT_NUMBER.`,
|
|
23
|
+
);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function terminalPicker(
|
|
28
|
+
options: string[],
|
|
29
|
+
prompt = "Select an option",
|
|
30
|
+
): Promise<string> {
|
|
31
|
+
if (!options.length) throw new Error("options must be non-empty");
|
|
32
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
33
|
+
throw new Error("terminalPicker requires a TTY stdin and stdout");
|
|
34
|
+
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
let selected = 0;
|
|
37
|
+
let renderedLines = 0;
|
|
38
|
+
|
|
39
|
+
function render() {
|
|
40
|
+
const lines = [`${prompt}:`];
|
|
41
|
+
for (let i = 0; i < options.length; i++) {
|
|
42
|
+
lines.push(`${i === selected ? "❯" : " "} ${options[i]}`);
|
|
43
|
+
}
|
|
44
|
+
if (renderedLines > 0) process.stdout.write(`\x1b[${renderedLines}F`);
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
process.stdout.write("\r\x1b[2K");
|
|
47
|
+
process.stdout.write(line);
|
|
48
|
+
process.stdout.write("\n");
|
|
49
|
+
}
|
|
50
|
+
for (let i = 0; i < renderedLines - lines.length; i++) {
|
|
51
|
+
process.stdout.write("\r\x1b[2K\n");
|
|
52
|
+
}
|
|
53
|
+
renderedLines = lines.length;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function cleanup() {
|
|
57
|
+
process.stdin.removeListener("data", onData);
|
|
58
|
+
process.stdin.setRawMode(false);
|
|
59
|
+
process.stdin.pause();
|
|
60
|
+
process.stdout.write("\x1b[?25h");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function onData(data: Buffer) {
|
|
64
|
+
const ch = data.toString();
|
|
65
|
+
if (ch === "\r" || ch === "\n") {
|
|
66
|
+
cleanup();
|
|
67
|
+
process.stdout.write(`\r\x1b[2KSelected: ${options[selected]}\n`);
|
|
68
|
+
resolve(options[selected]);
|
|
69
|
+
} else if (ch === "\x03") {
|
|
70
|
+
cleanup();
|
|
71
|
+
reject(new Error("Interrupted"));
|
|
72
|
+
} else if (ch === "\x1b[A") {
|
|
73
|
+
selected = (selected - 1 + options.length) % options.length;
|
|
74
|
+
render();
|
|
75
|
+
} else if (ch === "\x1b[B") {
|
|
76
|
+
selected = (selected + 1) % options.length;
|
|
77
|
+
render();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
process.stdin.setRawMode(true);
|
|
82
|
+
process.stdin.resume();
|
|
83
|
+
process.stdout.write("\x1b[?25l");
|
|
84
|
+
render();
|
|
85
|
+
process.stdin.on("data", onData);
|
|
86
|
+
});
|
|
87
|
+
}
|
package/src/helpers/llm.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
1
2
|
import type { SuggestedAction } from "../agent.ts";
|
|
2
3
|
import { Client } from "../client.ts";
|
|
3
4
|
import { telemetryClient } from "../telemetry.ts";
|
|
@@ -81,15 +82,10 @@ export class DocumentQA {
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
const _filterSchema = {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
other_appointments: { type: "array", items: { type: "string" } },
|
|
89
|
-
},
|
|
90
|
-
required: ["matching_appointments", "other_appointments"],
|
|
91
|
-
additionalProperties: false,
|
|
92
|
-
};
|
|
85
|
+
export const _filterSchema = z.object({
|
|
86
|
+
matching_appointments: z.array(z.string()),
|
|
87
|
+
other_appointments: z.array(z.string()),
|
|
88
|
+
});
|
|
93
89
|
|
|
94
90
|
@telemetryClient.trackClass()
|
|
95
91
|
export class DatetimeFilter {
|
|
@@ -122,11 +118,8 @@ ${this._slotsStr}
|
|
|
122
118
|
|
|
123
119
|
Return at most ${maxResults} items per list.`;
|
|
124
120
|
|
|
125
|
-
const text = await _generate(this._client, prompt, _filterSchema);
|
|
126
|
-
const result = JSON.parse(text)
|
|
127
|
-
matching_appointments: string[];
|
|
128
|
-
other_appointments: string[];
|
|
129
|
-
};
|
|
121
|
+
const text = await _generate(this._client, prompt, z.toJSONSchema(_filterSchema));
|
|
122
|
+
const result = _filterSchema.parse(JSON.parse(text));
|
|
130
123
|
return [
|
|
131
124
|
result.matching_appointments.slice(0, maxResults),
|
|
132
125
|
result.other_appointments.slice(0, maxResults),
|
|
@@ -134,30 +127,29 @@ Return at most ${maxResults} items per list.`;
|
|
|
134
127
|
}
|
|
135
128
|
}
|
|
136
129
|
|
|
130
|
+
export function _makeIntentSchema(choiceList: [string, ...string[]]) {
|
|
131
|
+
return z
|
|
132
|
+
.object({
|
|
133
|
+
possible_matches: z
|
|
134
|
+
.array(z.enum(choiceList))
|
|
135
|
+
.describe(
|
|
136
|
+
"Choices that could match the caller's intent, ordered by likelihood. Include all plausible matches.",
|
|
137
|
+
),
|
|
138
|
+
})
|
|
139
|
+
.meta({ title: "ChoiceModel" });
|
|
140
|
+
}
|
|
141
|
+
|
|
137
142
|
@telemetryClient.trackClass()
|
|
138
143
|
export class IntentRecognizer {
|
|
139
144
|
private readonly _client: Client;
|
|
140
145
|
private readonly _intentChoices: string[] | Record<string, string>;
|
|
141
|
-
private readonly _schema:
|
|
146
|
+
private readonly _schema: ReturnType<typeof _makeIntentSchema>;
|
|
142
147
|
|
|
143
148
|
constructor(intentChoices: string[] | Record<string, string>) {
|
|
144
149
|
this._client = new Client();
|
|
145
150
|
this._intentChoices = intentChoices;
|
|
146
151
|
const choiceList = Array.isArray(intentChoices) ? intentChoices : Object.keys(intentChoices);
|
|
147
|
-
this._schema =
|
|
148
|
-
type: "object",
|
|
149
|
-
title: "ChoiceModel",
|
|
150
|
-
properties: {
|
|
151
|
-
possible_matches: {
|
|
152
|
-
type: "array",
|
|
153
|
-
items: { type: "string", enum: choiceList },
|
|
154
|
-
description:
|
|
155
|
-
"Choices that could match the caller's intent, ordered by likelihood. Include all plausible matches.",
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
required: ["possible_matches"],
|
|
159
|
-
additionalProperties: false,
|
|
160
|
-
};
|
|
152
|
+
this._schema = _makeIntentSchema(choiceList as [string, ...string[]]);
|
|
161
153
|
}
|
|
162
154
|
|
|
163
155
|
async classify(intent: string): Promise<SuggestedAction[] | null> {
|
|
@@ -183,8 +175,8 @@ Rules:
|
|
|
183
175
|
prompt += `\n\nDetailed descriptions of each choice:\n ${descriptionString}`;
|
|
184
176
|
}
|
|
185
177
|
|
|
186
|
-
const text = await _generate(this._client, prompt, this._schema);
|
|
187
|
-
const keys = (JSON.parse(text)
|
|
178
|
+
const text = await _generate(this._client, prompt, z.toJSONSchema(this._schema));
|
|
179
|
+
const { possible_matches: keys } = this._schema.parse(JSON.parse(text));
|
|
188
180
|
|
|
189
181
|
if (!keys.length) return null;
|
|
190
182
|
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export { Client, type InboundConnection } from "./client.ts";
|
|
2
|
+
export { Runner } from "./runner.ts";
|
|
2
3
|
export type { SmsMessage } from "./sms.ts";
|
|
3
4
|
export { CallController, type TaskObjective } from "./call-controller.ts";
|
|
4
5
|
export { Say, Field } from "./action-item.ts";
|
|
5
6
|
export { Logger, getConsoleLogger, getDefaultLogger } from "./logging.ts";
|
|
6
7
|
export { Agent, CallInfo } from "./agent.ts";
|
|
7
|
-
export { Call } from "./call.ts";
|
|
8
|
+
export { Call, DEFAULT_REACH_PERSON_OUTCOMES } from "./call.ts";
|
|
9
|
+
export type { ReachPersonOutcome } from "./call.ts";
|
|
8
10
|
export type { BotSessionEnded, TerminationReason, DTMFPressedEvent, DTMFDigit } from "./events.ts";
|
|
9
11
|
export { TestSession } from "./testing/session.ts";
|
|
10
12
|
export { MockCall } from "./testing/mocks.ts";
|
package/src/runner.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Agent } from "./agent.ts";
|
|
2
|
+
import { MultiHealthContext, getHealthServer } from "./health.ts";
|
|
3
|
+
|
|
4
|
+
export class Runner {
|
|
5
|
+
private _healthCtx = new MultiHealthContext();
|
|
6
|
+
private _tasks: Array<() => Promise<void>> = [];
|
|
7
|
+
|
|
8
|
+
listenPhone(agent: Agent, agentNumber: string): this {
|
|
9
|
+
const ctx = this._healthCtx.createCtx();
|
|
10
|
+
this._tasks.push(() => agent._listenInbound(ctx, { agent_number: agentNumber }));
|
|
11
|
+
return this;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
listenWebrtc(agent: Agent, webrtcCode?: string): this {
|
|
15
|
+
const ctx = this._healthCtx.createCtx();
|
|
16
|
+
this._tasks.push(async () => {
|
|
17
|
+
const code = webrtcCode ?? (await agent._client.createWebrtcAgent(3600));
|
|
18
|
+
return agent._listenInbound(ctx, { webrtc_code: code });
|
|
19
|
+
});
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
listenSip(agent: Agent, sipCode: string): this {
|
|
24
|
+
const ctx = this._healthCtx.createCtx();
|
|
25
|
+
this._tasks.push(() => agent._listenInbound(ctx, { sip_code: sipCode }));
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
attachCampaign(agent: Agent, campaignCode: string): this {
|
|
30
|
+
const ctx = this._healthCtx.createCtx();
|
|
31
|
+
this._tasks.push(() => agent._serveCampaign(ctx, campaignCode));
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async run(): Promise<void> {
|
|
36
|
+
await using _server = await getHealthServer(this._healthCtx);
|
|
37
|
+
await Promise.all(this._tasks.map((task) => task()));
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/testing/session.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { _generate } from "../helpers/llm.ts";
|
|
|
7
7
|
|
|
8
8
|
type SessionEvent = InjectASR | BotTTS;
|
|
9
9
|
|
|
10
|
-
const _evalResponseSchema = z.object({
|
|
10
|
+
export const _evalResponseSchema = z.object({
|
|
11
11
|
results: z.array(
|
|
12
12
|
z.object({
|
|
13
13
|
passed: z.boolean(),
|
|
@@ -166,28 +166,8 @@ ${transcript || "(empty — no conversation occurred)"}
|
|
|
166
166
|
Criteria:
|
|
167
167
|
${criteriaList}`;
|
|
168
168
|
|
|
169
|
-
const evalSchema = {
|
|
170
|
-
type: "object",
|
|
171
|
-
properties: {
|
|
172
|
-
results: {
|
|
173
|
-
type: "array",
|
|
174
|
-
items: {
|
|
175
|
-
type: "object",
|
|
176
|
-
properties: {
|
|
177
|
-
passed: { type: "boolean" },
|
|
178
|
-
reasoning: { type: "string" },
|
|
179
|
-
},
|
|
180
|
-
required: ["passed"],
|
|
181
|
-
additionalProperties: false,
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
required: ["results"],
|
|
186
|
-
additionalProperties: false,
|
|
187
|
-
};
|
|
188
|
-
|
|
189
169
|
const evalResponse = _evalResponseSchema.parse(
|
|
190
|
-
JSON.parse(await _generate(this._client, prompt,
|
|
170
|
+
JSON.parse(await _generate(this._client, prompt, z.toJSONSchema(_evalResponseSchema))),
|
|
191
171
|
);
|
|
192
172
|
|
|
193
173
|
if (evalResponse.results.length !== allCriteria.length) {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.
|
|
1
|
+
export const SDK_VERSION = "0.25.0";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"example.test.js","sourceRoot":"","sources":["../../examples/example.test.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAGH,mDAA+C;AAC/C,sEAA+D;AAE/D,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,UAAU,GAAG,MAAM,iBAAK,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,oBAAQ,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAC/F,MAAM,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,OAAO,GAAG,MAAM,iBAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACjD,iFAAiF;YACjF,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;YAEvD,6CAA6C;YAC7C,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,mEAAmE;QAEnE,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAChD,mFAAmF;QACnF,MAAM,OAAO,GAAG,MAAM,iBAAK,CAAC,YAAY,CAAC,6CAA6C,CAAC,CAAC;QAExF,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACzD,4EAA4E;QAC5E,MAAM,OAAO,GAAG,iBAAK,CAAC,KAAK,EAAE,CAAC;QAE9B,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,IAAU,EAAE,EAAE;YAC7C,IAAI,CAAC,MAAM,CACT,8GAA8G,CAC/G,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,6CAA6C,CAAC,CAAC;QAE1F,8FAA8F;QAC9F,MAAM,OAAO,CAAC,QAAQ,CAAC;YACrB,YAAY,EAAE,CAAC,sEAAsE,CAAC;YACtF,YAAY,EAAE,CAAC,2DAA2D,CAAC;SAC5E,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
File without changes
|