@skillhq/concierge 1.5.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 +91 -0
- package/dist/cli/program.d.ts +3 -0
- package/dist/cli/program.d.ts.map +1 -0
- package/dist/cli/program.js +46 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/shared.d.ts +18 -0
- package/dist/cli/shared.d.ts.map +1 -0
- package/dist/cli/shared.js +2 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/call.d.ts +7 -0
- package/dist/commands/call.d.ts.map +1 -0
- package/dist/commands/call.js +409 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +120 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/find-contact.d.ts +4 -0
- package/dist/commands/find-contact.d.ts.map +1 -0
- package/dist/commands/find-contact.js +57 -0
- package/dist/commands/find-contact.js.map +1 -0
- package/dist/commands/server.d.ts +7 -0
- package/dist/commands/server.d.ts.map +1 -0
- package/dist/commands/server.js +212 -0
- package/dist/commands/server.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/call/audio/mulaw.d.ts +35 -0
- package/dist/lib/call/audio/mulaw.d.ts.map +1 -0
- package/dist/lib/call/audio/mulaw.js +109 -0
- package/dist/lib/call/audio/mulaw.js.map +1 -0
- package/dist/lib/call/audio/pcm-utils.d.ts +62 -0
- package/dist/lib/call/audio/pcm-utils.d.ts.map +1 -0
- package/dist/lib/call/audio/pcm-utils.js +149 -0
- package/dist/lib/call/audio/pcm-utils.js.map +1 -0
- package/dist/lib/call/audio/resample.d.ts +34 -0
- package/dist/lib/call/audio/resample.d.ts.map +1 -0
- package/dist/lib/call/audio/resample.js +97 -0
- package/dist/lib/call/audio/resample.js.map +1 -0
- package/dist/lib/call/audio/streaming-decoder.d.ts +45 -0
- package/dist/lib/call/audio/streaming-decoder.d.ts.map +1 -0
- package/dist/lib/call/audio/streaming-decoder.js +110 -0
- package/dist/lib/call/audio/streaming-decoder.js.map +1 -0
- package/dist/lib/call/call-server.d.ts +110 -0
- package/dist/lib/call/call-server.d.ts.map +1 -0
- package/dist/lib/call/call-server.js +681 -0
- package/dist/lib/call/call-server.js.map +1 -0
- package/dist/lib/call/call-session.d.ts +133 -0
- package/dist/lib/call/call-session.d.ts.map +1 -0
- package/dist/lib/call/call-session.js +890 -0
- package/dist/lib/call/call-session.js.map +1 -0
- package/dist/lib/call/call-types.d.ts +133 -0
- package/dist/lib/call/call-types.d.ts.map +1 -0
- package/dist/lib/call/call-types.js +16 -0
- package/dist/lib/call/call-types.js.map +1 -0
- package/dist/lib/call/conversation-ai.d.ts +56 -0
- package/dist/lib/call/conversation-ai.d.ts.map +1 -0
- package/dist/lib/call/conversation-ai.js +276 -0
- package/dist/lib/call/conversation-ai.js.map +1 -0
- package/dist/lib/call/eval/codec-test.d.ts +45 -0
- package/dist/lib/call/eval/codec-test.d.ts.map +1 -0
- package/dist/lib/call/eval/codec-test.js +169 -0
- package/dist/lib/call/eval/codec-test.js.map +1 -0
- package/dist/lib/call/eval/conversation-scripts.d.ts +55 -0
- package/dist/lib/call/eval/conversation-scripts.d.ts.map +1 -0
- package/dist/lib/call/eval/conversation-scripts.js +359 -0
- package/dist/lib/call/eval/conversation-scripts.js.map +1 -0
- package/dist/lib/call/eval/eval-runner.d.ts +64 -0
- package/dist/lib/call/eval/eval-runner.d.ts.map +1 -0
- package/dist/lib/call/eval/eval-runner.js +369 -0
- package/dist/lib/call/eval/eval-runner.js.map +1 -0
- package/dist/lib/call/eval/index.d.ts +9 -0
- package/dist/lib/call/eval/index.d.ts.map +1 -0
- package/dist/lib/call/eval/index.js +9 -0
- package/dist/lib/call/eval/index.js.map +1 -0
- package/dist/lib/call/eval/integration-test-suite.d.ts +71 -0
- package/dist/lib/call/eval/integration-test-suite.d.ts.map +1 -0
- package/dist/lib/call/eval/integration-test-suite.js +519 -0
- package/dist/lib/call/eval/integration-test-suite.js.map +1 -0
- package/dist/lib/call/eval/turn-taking-test.d.ts +84 -0
- package/dist/lib/call/eval/turn-taking-test.d.ts.map +1 -0
- package/dist/lib/call/eval/turn-taking-test.js +260 -0
- package/dist/lib/call/eval/turn-taking-test.js.map +1 -0
- package/dist/lib/call/index.d.ts +12 -0
- package/dist/lib/call/index.d.ts.map +1 -0
- package/dist/lib/call/index.js +17 -0
- package/dist/lib/call/index.js.map +1 -0
- package/dist/lib/call/providers/deepgram.d.ts +81 -0
- package/dist/lib/call/providers/deepgram.d.ts.map +1 -0
- package/dist/lib/call/providers/deepgram.js +279 -0
- package/dist/lib/call/providers/deepgram.js.map +1 -0
- package/dist/lib/call/providers/elevenlabs.d.ts +78 -0
- package/dist/lib/call/providers/elevenlabs.d.ts.map +1 -0
- package/dist/lib/call/providers/elevenlabs.js +272 -0
- package/dist/lib/call/providers/elevenlabs.js.map +1 -0
- package/dist/lib/call/providers/local-deps.d.ts +18 -0
- package/dist/lib/call/providers/local-deps.d.ts.map +1 -0
- package/dist/lib/call/providers/local-deps.js +114 -0
- package/dist/lib/call/providers/local-deps.js.map +1 -0
- package/dist/lib/call/providers/twilio.d.ts +53 -0
- package/dist/lib/call/providers/twilio.d.ts.map +1 -0
- package/dist/lib/call/providers/twilio.js +173 -0
- package/dist/lib/call/providers/twilio.js.map +1 -0
- package/dist/lib/concierge-client-types.d.ts +68 -0
- package/dist/lib/concierge-client-types.d.ts.map +1 -0
- package/dist/lib/concierge-client-types.js +2 -0
- package/dist/lib/concierge-client-types.js.map +1 -0
- package/dist/lib/concierge-client.d.ts +29 -0
- package/dist/lib/concierge-client.d.ts.map +1 -0
- package/dist/lib/concierge-client.js +534 -0
- package/dist/lib/concierge-client.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +66 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/output.d.ts +7 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +114 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/utils/contact-extractor.d.ts +12 -0
- package/dist/lib/utils/contact-extractor.d.ts.map +1 -0
- package/dist/lib/utils/contact-extractor.js +159 -0
- package/dist/lib/utils/contact-extractor.js.map +1 -0
- package/dist/lib/utils/formatters.d.ts +15 -0
- package/dist/lib/utils/formatters.d.ts.map +1 -0
- package/dist/lib/utils/formatters.js +107 -0
- package/dist/lib/utils/formatters.js.map +1 -0
- package/dist/lib/utils/url-parser.d.ts +11 -0
- package/dist/lib/utils/url-parser.d.ts.map +1 -0
- package/dist/lib/utils/url-parser.js +103 -0
- package/dist/lib/utils/url-parser.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive Integration Test Suite
|
|
3
|
+
*
|
|
4
|
+
* Combines all tests into a single suite:
|
|
5
|
+
* 1. Codec tests - verify audio pipeline
|
|
6
|
+
* 2. Turn-taking tests - verify debounce logic
|
|
7
|
+
* 3. Conversation flow tests - verify AI behavior
|
|
8
|
+
* 4. End-to-end pipeline tests - full flow without phone
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { ConversationAI } from '../conversation-ai.js';
|
|
13
|
+
import { testTTSPipeline } from './codec-test.js';
|
|
14
|
+
import { runAllTurnTakingTests } from './turn-taking-test.js';
|
|
15
|
+
function countWords(text) {
|
|
16
|
+
return text.trim().split(/\s+/).filter(Boolean).length;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Test 1: Codec Pipeline
|
|
20
|
+
* Verifies ElevenLabs → ffmpeg → µ-law conversion
|
|
21
|
+
*/
|
|
22
|
+
async function runCodecTests(config) {
|
|
23
|
+
const testTexts = [
|
|
24
|
+
'Hello.',
|
|
25
|
+
'This is a longer sentence to test the audio conversion pipeline.',
|
|
26
|
+
'Testing numbers: one, two, three, four, five.',
|
|
27
|
+
];
|
|
28
|
+
const failures = [];
|
|
29
|
+
for (const text of testTexts) {
|
|
30
|
+
try {
|
|
31
|
+
const result = await testTTSPipeline(text, config.elevenLabsApiKey, config.elevenLabsVoiceId);
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
failures.push(`"${text.substring(0, 30)}...": ${result.errors.join(', ')}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
failures.push(`"${text.substring(0, 30)}...": ${error}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
passed: failures.length === 0,
|
|
42
|
+
tests: testTexts.length,
|
|
43
|
+
failures,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Test 2: Turn-Taking Debounce
|
|
48
|
+
* Verifies transcript accumulation and timing
|
|
49
|
+
*/
|
|
50
|
+
async function runTurnTakingTests() {
|
|
51
|
+
const { passed, failed, results } = await runAllTurnTakingTests();
|
|
52
|
+
const failures = results.filter((r) => !r.passed);
|
|
53
|
+
return {
|
|
54
|
+
passed: failed === 0,
|
|
55
|
+
total: passed + failed,
|
|
56
|
+
passedCount: passed,
|
|
57
|
+
failures,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Test 3: Conversation Flow
|
|
62
|
+
* Tests AI behavior in various scenarios
|
|
63
|
+
*/
|
|
64
|
+
async function runConversationFlowTests(config) {
|
|
65
|
+
const failures = [];
|
|
66
|
+
// Test 1: Greeting doesn't end conversation
|
|
67
|
+
try {
|
|
68
|
+
const ai1 = new ConversationAI({
|
|
69
|
+
apiKey: config.anthropicApiKey,
|
|
70
|
+
goal: 'Book a hotel room',
|
|
71
|
+
context: 'Hotel: Test Hotel, Dates: March 1-3',
|
|
72
|
+
});
|
|
73
|
+
const greeting = await ai1.getGreeting();
|
|
74
|
+
if (ai1.complete) {
|
|
75
|
+
failures.push('Greeting marked conversation complete prematurely');
|
|
76
|
+
}
|
|
77
|
+
if (!greeting || greeting.length < 10) {
|
|
78
|
+
failures.push(`Greeting too short: "${greeting}"`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
failures.push(`Greeting test failed: ${error}`);
|
|
83
|
+
}
|
|
84
|
+
// Test 2: Normal conversation doesn't end prematurely
|
|
85
|
+
try {
|
|
86
|
+
const ai2 = new ConversationAI({
|
|
87
|
+
apiKey: config.anthropicApiKey,
|
|
88
|
+
goal: 'Get business hours',
|
|
89
|
+
context: 'Business: Test Store',
|
|
90
|
+
});
|
|
91
|
+
await ai2.getGreeting();
|
|
92
|
+
// Store employee responds - AI should ask about hours, not end call
|
|
93
|
+
await ai2.respond('Hello, how can I help you?');
|
|
94
|
+
// The AI should continue the conversation to get the hours
|
|
95
|
+
// It shouldn't mark complete just because someone said hello
|
|
96
|
+
if (ai2.complete) {
|
|
97
|
+
failures.push('Conversation ended prematurely - AI should ask for hours, not end call');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
failures.push(`Conversation flow test failed: ${error}`);
|
|
102
|
+
}
|
|
103
|
+
// Test 3: Conversation can complete properly
|
|
104
|
+
try {
|
|
105
|
+
const ai3 = new ConversationAI({
|
|
106
|
+
apiKey: config.anthropicApiKey,
|
|
107
|
+
goal: 'Confirm a reservation exists',
|
|
108
|
+
context: 'Confirmation number: ABC123',
|
|
109
|
+
});
|
|
110
|
+
await ai3.getGreeting();
|
|
111
|
+
await ai3.respond('Yes, I can confirm that reservation ABC123 is valid.');
|
|
112
|
+
// The AI might mark complete after confirmation
|
|
113
|
+
// This is expected behavior
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
failures.push(`Conversation completion test failed: ${error}`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
passed: failures.length === 0,
|
|
120
|
+
tests: 3,
|
|
121
|
+
failures,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Test 4: AI Disclosure and Role
|
|
126
|
+
* Verifies AI discloses it's an AI AND doesn't say "how can I help you"
|
|
127
|
+
*/
|
|
128
|
+
async function runAIDisclosureTest(config) {
|
|
129
|
+
const ai = new ConversationAI({
|
|
130
|
+
apiKey: config.anthropicApiKey,
|
|
131
|
+
goal: 'Book a hotel room',
|
|
132
|
+
context: 'Customer: John Smith, Hotel: Grand Plaza',
|
|
133
|
+
});
|
|
134
|
+
const greeting = await ai.getGreeting();
|
|
135
|
+
const greetingLower = greeting.toLowerCase();
|
|
136
|
+
const issues = [];
|
|
137
|
+
// Check for AI disclosure keywords
|
|
138
|
+
const disclosureKeywords = ['ai', 'artificial', 'assistant', 'automated', 'behalf'];
|
|
139
|
+
const containsDisclosure = disclosureKeywords.some((keyword) => greetingLower.includes(keyword));
|
|
140
|
+
if (!containsDisclosure) {
|
|
141
|
+
issues.push('Missing AI disclosure in greeting');
|
|
142
|
+
}
|
|
143
|
+
// Check for wrong role (AI shouldn't ask how it can help - it's the caller!)
|
|
144
|
+
const wrongRolePhrases = [
|
|
145
|
+
'how can i assist',
|
|
146
|
+
'how can i help',
|
|
147
|
+
'how may i help',
|
|
148
|
+
'how may i assist',
|
|
149
|
+
'what can i do for you',
|
|
150
|
+
];
|
|
151
|
+
const hasWrongRole = wrongRolePhrases.some((phrase) => greetingLower.includes(phrase));
|
|
152
|
+
if (hasWrongRole) {
|
|
153
|
+
issues.push('AI incorrectly asked "how can I help" - AI is the caller seeking help!');
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
passed: containsDisclosure && !hasWrongRole,
|
|
157
|
+
greeting,
|
|
158
|
+
containsDisclosure,
|
|
159
|
+
hasWrongRole,
|
|
160
|
+
issues,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Test 5: Role Consistency (No Role Reversal)
|
|
165
|
+
* Verifies AI stays in customer role throughout, doesn't switch to hotel employee phrases
|
|
166
|
+
*/
|
|
167
|
+
async function runRoleConsistencyTest(config) {
|
|
168
|
+
const ai = new ConversationAI({
|
|
169
|
+
apiKey: config.anthropicApiKey,
|
|
170
|
+
goal: 'Book a hotel room and get a confirmation number',
|
|
171
|
+
context: 'Customer: Derek Rein, Hotel: Grand Plaza, Dates: March 12-14, Email: derek@example.com',
|
|
172
|
+
});
|
|
173
|
+
const issues = [];
|
|
174
|
+
const responses = [];
|
|
175
|
+
const roleReversalPhrases = [];
|
|
176
|
+
// Phrases that indicate the AI switched to acting like the hotel employee
|
|
177
|
+
const badPhrases = [
|
|
178
|
+
'does this look correct',
|
|
179
|
+
'does this all look correct',
|
|
180
|
+
'does that look correct',
|
|
181
|
+
'let me know if you need anything',
|
|
182
|
+
'please let me know if you need',
|
|
183
|
+
"i've noted",
|
|
184
|
+
'i have noted',
|
|
185
|
+
"i'll note that",
|
|
186
|
+
'anything else i can help you with',
|
|
187
|
+
'is there anything else i can do',
|
|
188
|
+
"i've got that booked",
|
|
189
|
+
'i have that booked',
|
|
190
|
+
'your reservation is confirmed', // AI shouldn't confirm - hotel does
|
|
191
|
+
"you're all set", // Hotel says this, not caller
|
|
192
|
+
];
|
|
193
|
+
// Simulate a quick agreement conversation
|
|
194
|
+
const greeting = await ai.getGreeting();
|
|
195
|
+
responses.push(greeting);
|
|
196
|
+
const r1 = await ai.respond('Mhmm. Sure, we can do that.');
|
|
197
|
+
responses.push(r1 || '');
|
|
198
|
+
const r2 = await ai.respond('Yes, that works. Go ahead.');
|
|
199
|
+
responses.push(r2 || '');
|
|
200
|
+
const r3 = await ai.respond('Okay, done. Anything else?');
|
|
201
|
+
responses.push(r3 || '');
|
|
202
|
+
// Check all responses for role reversal phrases
|
|
203
|
+
for (const text of responses) {
|
|
204
|
+
const textLower = text.toLowerCase();
|
|
205
|
+
for (const phrase of badPhrases) {
|
|
206
|
+
if (textLower.includes(phrase)) {
|
|
207
|
+
roleReversalPhrases.push(`"${phrase}" found in: "${text.substring(0, 80)}..."`);
|
|
208
|
+
issues.push(`Role reversal detected: AI used hotel-employee phrase "${phrase}"`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
passed: issues.length === 0,
|
|
214
|
+
issues,
|
|
215
|
+
responses,
|
|
216
|
+
roleReversalPhrases,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Test 6: Voice-Friendly Formatting
|
|
221
|
+
* Verifies dates and numbers are spelled out for voice
|
|
222
|
+
*/
|
|
223
|
+
async function runVoiceFormattingTest(config) {
|
|
224
|
+
const ai = new ConversationAI({
|
|
225
|
+
apiKey: config.anthropicApiKey,
|
|
226
|
+
goal: 'Book a hotel room for March 12-14',
|
|
227
|
+
context: 'Customer: John Smith, Hotel: Grand Plaza, Dates: March 12-14, Price: $393',
|
|
228
|
+
});
|
|
229
|
+
const issues = [];
|
|
230
|
+
const responses = [];
|
|
231
|
+
// Get greeting and first response
|
|
232
|
+
const greeting = await ai.getGreeting();
|
|
233
|
+
responses.push(greeting);
|
|
234
|
+
const response = await ai.respond('Sure, what dates are you looking at?');
|
|
235
|
+
responses.push(response || '');
|
|
236
|
+
// Check all responses for formatting issues
|
|
237
|
+
for (const text of responses) {
|
|
238
|
+
// Check for numeric date ranges (should be spelled out)
|
|
239
|
+
if (/\d+-\d+/.test(text)) {
|
|
240
|
+
issues.push(`Contains numeric date range: "${text.match(/\d+-\d+/)?.[0]}"`);
|
|
241
|
+
}
|
|
242
|
+
// Check for $ symbol (should say "dollars")
|
|
243
|
+
if (/\$\d+/.test(text)) {
|
|
244
|
+
issues.push(`Contains $ symbol: "${text.match(/\$\d+/)?.[0]}"`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
passed: issues.length === 0,
|
|
249
|
+
issues,
|
|
250
|
+
responses,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Test 6: No Repetition
|
|
255
|
+
* Verifies AI doesn't keep repeating the same information
|
|
256
|
+
*/
|
|
257
|
+
async function runNoRepetitionTest(config) {
|
|
258
|
+
const ai = new ConversationAI({
|
|
259
|
+
apiKey: config.anthropicApiKey,
|
|
260
|
+
goal: 'Book a hotel room',
|
|
261
|
+
context: 'Hotel: Test Hotel, Room: Deluxe Suite, Dates: March 15-17, Price: $500',
|
|
262
|
+
});
|
|
263
|
+
const issues = [];
|
|
264
|
+
const responses = [];
|
|
265
|
+
// Simulate a conversation
|
|
266
|
+
const greeting = await ai.getGreeting();
|
|
267
|
+
responses.push(greeting);
|
|
268
|
+
const r1 = await ai.respond('Yes, we have that room available.');
|
|
269
|
+
responses.push(r1 || '');
|
|
270
|
+
const r2 = await ai.respond('The rate is four fifty per night.');
|
|
271
|
+
responses.push(r2 || '');
|
|
272
|
+
// Count how many times key phrases appear across all responses
|
|
273
|
+
const allText = responses.join(' ').toLowerCase();
|
|
274
|
+
// Check for excessive repetition of specific details
|
|
275
|
+
const hotelMentions = (allText.match(/test hotel/g) || []).length;
|
|
276
|
+
const dateMentions = (allText.match(/march (15|fifteen)/g) || []).length;
|
|
277
|
+
if (hotelMentions > 2) {
|
|
278
|
+
issues.push(`Hotel name repeated ${hotelMentions} times (max 2)`);
|
|
279
|
+
}
|
|
280
|
+
if (dateMentions > 2) {
|
|
281
|
+
issues.push(`Dates repeated ${dateMentions} times (max 2)`);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
passed: issues.length === 0,
|
|
285
|
+
issues,
|
|
286
|
+
responses,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Test 7: Conciseness and Redundant Follow-ups
|
|
291
|
+
* Uses a real booking flow where the AI often becomes too wordy.
|
|
292
|
+
*/
|
|
293
|
+
async function runConcisenessTest(config) {
|
|
294
|
+
const ai = new ConversationAI({
|
|
295
|
+
apiKey: config.anthropicApiKey,
|
|
296
|
+
goal: 'Book a room directly, share email for payment link, and get confirmation details',
|
|
297
|
+
context: 'Hotel: Haus im Tal, Dates: March 12-14, Online rate: $393, Guest: Derek Rein, Email: alexanderderekrein@gmail.com',
|
|
298
|
+
});
|
|
299
|
+
const issues = [];
|
|
300
|
+
const responses = [];
|
|
301
|
+
const greeting = await ai.getGreeting();
|
|
302
|
+
responses.push(greeting);
|
|
303
|
+
const scriptedTurns = [
|
|
304
|
+
'Sure.',
|
|
305
|
+
'Yes.',
|
|
306
|
+
'Yes.',
|
|
307
|
+
'I need to email you a premium link. Does that work?',
|
|
308
|
+
'Can you spell out the email again?',
|
|
309
|
+
'Okay. Perfect.',
|
|
310
|
+
'Yes.',
|
|
311
|
+
];
|
|
312
|
+
let emailSpellingResponse = '';
|
|
313
|
+
for (const human of scriptedTurns) {
|
|
314
|
+
const response = await ai.respond(human);
|
|
315
|
+
if (!response)
|
|
316
|
+
break;
|
|
317
|
+
responses.push(response);
|
|
318
|
+
if (human.toLowerCase().includes('spell out the email')) {
|
|
319
|
+
emailSpellingResponse = response;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const wordCounts = responses.map(countWords);
|
|
323
|
+
const avgWordsPerResponse = wordCounts.length > 0 ? wordCounts.reduce((sum, count) => sum + count, 0) / wordCounts.length : 0;
|
|
324
|
+
const maxWordsPerResponse = wordCounts.length > 0 ? Math.max(...wordCounts) : 0;
|
|
325
|
+
if (maxWordsPerResponse > 45) {
|
|
326
|
+
issues.push(`Response too long (${maxWordsPerResponse} words). Target is <= 45 for phone pacing.`);
|
|
327
|
+
}
|
|
328
|
+
if (avgWordsPerResponse > 28) {
|
|
329
|
+
issues.push(`Average response too long (${avgWordsPerResponse.toFixed(1)} words). Target is <= 28.`);
|
|
330
|
+
}
|
|
331
|
+
const allTextLower = responses.join(' ').toLowerCase();
|
|
332
|
+
const enthusiasmMatches = allTextLower.match(/\b(wonderful|excellent|perfect|fantastic|amazing)\b/g) || [];
|
|
333
|
+
const enthusiasmCount = enthusiasmMatches.length;
|
|
334
|
+
if (enthusiasmCount > 2) {
|
|
335
|
+
issues.push(`Too much enthusiasm filler (${enthusiasmCount} matches). Keep acknowledgements brief.`);
|
|
336
|
+
}
|
|
337
|
+
const redundantReconfirmPhrases = allTextLower.match(/just to confirm|final price|for march|confirmation number/g) || [];
|
|
338
|
+
if (redundantReconfirmPhrases.length > 4) {
|
|
339
|
+
issues.push(`Likely over-reconfirmation (${redundantReconfirmPhrases.length} repeated confirmation phrases).`);
|
|
340
|
+
}
|
|
341
|
+
if (emailSpellingResponse) {
|
|
342
|
+
if (!/gmail dot com|at gmail dot com/i.test(emailSpellingResponse)) {
|
|
343
|
+
issues.push('When asked to spell the email again, response did not clearly repeat the email.');
|
|
344
|
+
}
|
|
345
|
+
if (/that works|works perfectly|final price|just to confirm/i.test(emailSpellingResponse)) {
|
|
346
|
+
issues.push('Email spelling response included stale/redundant confirmation language.');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
issues.push('Did not capture an answer to "Can you spell out the email again?"');
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
passed: issues.length === 0,
|
|
354
|
+
issues,
|
|
355
|
+
responses,
|
|
356
|
+
avgWordsPerResponse,
|
|
357
|
+
maxWordsPerResponse,
|
|
358
|
+
enthusiasmCount,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Run the complete integration test suite
|
|
363
|
+
*/
|
|
364
|
+
export async function runIntegrationTestSuite(config) {
|
|
365
|
+
const startTime = Date.now();
|
|
366
|
+
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
367
|
+
console.log('║ COMPREHENSIVE INTEGRATION TEST SUITE ║');
|
|
368
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
369
|
+
// Run all test categories
|
|
370
|
+
console.log('🔊 Running Codec Tests...');
|
|
371
|
+
const codecResult = await runCodecTests(config);
|
|
372
|
+
console.log(` ${codecResult.passed ? '✅' : '❌'} ${codecResult.tests - codecResult.failures.length}/${codecResult.tests} passed\n`);
|
|
373
|
+
console.log('🔄 Running Turn-Taking Tests...');
|
|
374
|
+
const turnTakingResult = await runTurnTakingTests();
|
|
375
|
+
console.log(` ${turnTakingResult.passed ? '✅' : '❌'} ${turnTakingResult.passedCount}/${turnTakingResult.total} passed\n`);
|
|
376
|
+
console.log('💬 Running Conversation Flow Tests...');
|
|
377
|
+
const conversationResult = await runConversationFlowTests(config);
|
|
378
|
+
console.log(` ${conversationResult.passed ? '✅' : '❌'} ${conversationResult.tests - conversationResult.failures.length}/${conversationResult.tests} passed\n`);
|
|
379
|
+
console.log('🤖 Running AI Disclosure & Role Test...');
|
|
380
|
+
const disclosureResult = await runAIDisclosureTest(config);
|
|
381
|
+
console.log(` ${disclosureResult.passed ? '✅' : '❌'} Disclosure: ${disclosureResult.containsDisclosure ? 'yes' : 'no'}, Wrong role: ${disclosureResult.hasWrongRole ? 'YES (bad)' : 'no (good)'}\n`);
|
|
382
|
+
console.log('🔄 Running Role Consistency Test...');
|
|
383
|
+
const roleResult = await runRoleConsistencyTest(config);
|
|
384
|
+
console.log(` ${roleResult.passed ? '✅' : '❌'} ${roleResult.issues.length === 0 ? 'AI stayed in customer role' : `Role reversals: ${roleResult.roleReversalPhrases.length}`}\n`);
|
|
385
|
+
console.log('🗣️ Running Voice Formatting Test...');
|
|
386
|
+
const voiceResult = await runVoiceFormattingTest(config);
|
|
387
|
+
console.log(` ${voiceResult.passed ? '✅' : '❌'} ${voiceResult.issues.length === 0 ? 'Dates/numbers properly spelled out' : voiceResult.issues.join(', ')}\n`);
|
|
388
|
+
console.log('🔁 Running No Repetition Test...');
|
|
389
|
+
const repetitionResult = await runNoRepetitionTest(config);
|
|
390
|
+
console.log(` ${repetitionResult.passed ? '✅' : '❌'} ${repetitionResult.issues.length === 0 ? 'No excessive repetition' : repetitionResult.issues.join(', ')}\n`);
|
|
391
|
+
console.log('✂️ Running Conciseness Test...');
|
|
392
|
+
const concisenessResult = await runConcisenessTest(config);
|
|
393
|
+
console.log(` ${concisenessResult.passed ? '✅' : '❌'} avg words: ${concisenessResult.avgWordsPerResponse.toFixed(1)}, max: ${concisenessResult.maxWordsPerResponse}, filler words: ${concisenessResult.enthusiasmCount}\n`);
|
|
394
|
+
const duration = Date.now() - startTime;
|
|
395
|
+
const overallPassed = codecResult.passed &&
|
|
396
|
+
turnTakingResult.passed &&
|
|
397
|
+
conversationResult.passed &&
|
|
398
|
+
disclosureResult.passed &&
|
|
399
|
+
roleResult.passed &&
|
|
400
|
+
voiceResult.passed &&
|
|
401
|
+
repetitionResult.passed &&
|
|
402
|
+
concisenessResult.passed;
|
|
403
|
+
const result = {
|
|
404
|
+
timestamp: new Date().toISOString(),
|
|
405
|
+
duration,
|
|
406
|
+
overallPassed,
|
|
407
|
+
codec: codecResult,
|
|
408
|
+
turnTaking: turnTakingResult,
|
|
409
|
+
conversationFlow: conversationResult,
|
|
410
|
+
aiDisclosure: disclosureResult,
|
|
411
|
+
roleConsistency: roleResult,
|
|
412
|
+
voiceFormatting: voiceResult,
|
|
413
|
+
noRepetition: repetitionResult,
|
|
414
|
+
conciseness: concisenessResult,
|
|
415
|
+
};
|
|
416
|
+
// Print summary
|
|
417
|
+
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
418
|
+
if (overallPassed) {
|
|
419
|
+
console.log('║ ✅ ALL INTEGRATION TESTS PASSED ║');
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
console.log('║ ❌ SOME INTEGRATION TESTS FAILED ║');
|
|
423
|
+
}
|
|
424
|
+
console.log('╠════════════════════════════════════════════════════════════╣');
|
|
425
|
+
console.log(`║ Codec: ${codecResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
426
|
+
console.log(`║ Turn-Taking: ${turnTakingResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
427
|
+
console.log(`║ Conversation: ${conversationResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
428
|
+
console.log(`║ AI Disclosure: ${disclosureResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
429
|
+
console.log(`║ Role Consistency:${roleResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
430
|
+
console.log(`║ Voice Format: ${voiceResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
431
|
+
console.log(`║ No Repetition: ${repetitionResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
432
|
+
console.log(`║ Conciseness: ${concisenessResult.passed ? '✅ PASS' : '❌ FAIL'} ║`);
|
|
433
|
+
console.log(`║ Duration: ${(duration / 1000).toFixed(1)}s ║`);
|
|
434
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
435
|
+
// Print failures if any
|
|
436
|
+
if (!overallPassed) {
|
|
437
|
+
console.log('FAILURES:\n');
|
|
438
|
+
if (!codecResult.passed) {
|
|
439
|
+
console.log('Codec failures:');
|
|
440
|
+
for (const f of codecResult.failures) {
|
|
441
|
+
console.log(` - ${f}`);
|
|
442
|
+
}
|
|
443
|
+
console.log('');
|
|
444
|
+
}
|
|
445
|
+
if (!turnTakingResult.passed) {
|
|
446
|
+
console.log('Turn-taking failures:');
|
|
447
|
+
for (const f of turnTakingResult.failures) {
|
|
448
|
+
console.log(` - ${f.testName}: transcript="${f.transcript}" (expected "${f.expectedTranscript}")`);
|
|
449
|
+
}
|
|
450
|
+
console.log('');
|
|
451
|
+
}
|
|
452
|
+
if (!conversationResult.passed) {
|
|
453
|
+
console.log('Conversation flow failures:');
|
|
454
|
+
for (const f of conversationResult.failures) {
|
|
455
|
+
console.log(` - ${f}`);
|
|
456
|
+
}
|
|
457
|
+
console.log('');
|
|
458
|
+
}
|
|
459
|
+
if (!disclosureResult.passed) {
|
|
460
|
+
console.log('AI disclosure/role failures:');
|
|
461
|
+
for (const f of disclosureResult.issues) {
|
|
462
|
+
console.log(` - ${f}`);
|
|
463
|
+
}
|
|
464
|
+
console.log(` Greeting was: "${disclosureResult.greeting}"`);
|
|
465
|
+
console.log('');
|
|
466
|
+
}
|
|
467
|
+
if (!roleResult.passed) {
|
|
468
|
+
console.log('Role consistency failures (AI switched to hotel employee role):');
|
|
469
|
+
for (const f of roleResult.roleReversalPhrases) {
|
|
470
|
+
console.log(` - ${f}`);
|
|
471
|
+
}
|
|
472
|
+
console.log(' Responses:');
|
|
473
|
+
for (let i = 0; i < roleResult.responses.length; i++) {
|
|
474
|
+
console.log(` ${i + 1}. "${roleResult.responses[i].substring(0, 100)}..."`);
|
|
475
|
+
}
|
|
476
|
+
console.log('');
|
|
477
|
+
}
|
|
478
|
+
if (!voiceResult.passed) {
|
|
479
|
+
console.log('Voice formatting failures:');
|
|
480
|
+
for (const f of voiceResult.issues) {
|
|
481
|
+
console.log(` - ${f}`);
|
|
482
|
+
}
|
|
483
|
+
console.log(' Responses:');
|
|
484
|
+
for (let i = 0; i < voiceResult.responses.length; i++) {
|
|
485
|
+
console.log(` ${i + 1}. "${voiceResult.responses[i].substring(0, 80)}..."`);
|
|
486
|
+
}
|
|
487
|
+
console.log('');
|
|
488
|
+
}
|
|
489
|
+
if (!repetitionResult.passed) {
|
|
490
|
+
console.log('Repetition failures:');
|
|
491
|
+
for (const f of repetitionResult.issues) {
|
|
492
|
+
console.log(` - ${f}`);
|
|
493
|
+
}
|
|
494
|
+
console.log('');
|
|
495
|
+
}
|
|
496
|
+
if (!concisenessResult.passed) {
|
|
497
|
+
console.log('Conciseness failures:');
|
|
498
|
+
for (const f of concisenessResult.issues) {
|
|
499
|
+
console.log(` - ${f}`);
|
|
500
|
+
}
|
|
501
|
+
console.log(' Responses:');
|
|
502
|
+
for (let i = 0; i < concisenessResult.responses.length; i++) {
|
|
503
|
+
console.log(` ${i + 1}. "${concisenessResult.responses[i].substring(0, 100)}..."`);
|
|
504
|
+
}
|
|
505
|
+
console.log('');
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// Save results if output dir specified
|
|
509
|
+
if (config.outputDir) {
|
|
510
|
+
if (!existsSync(config.outputDir)) {
|
|
511
|
+
mkdirSync(config.outputDir, { recursive: true });
|
|
512
|
+
}
|
|
513
|
+
const resultFile = join(config.outputDir, `integration_${Date.now()}.json`);
|
|
514
|
+
writeFileSync(resultFile, JSON.stringify(result, null, 2));
|
|
515
|
+
console.log(`Results saved to: ${resultFile}`);
|
|
516
|
+
}
|
|
517
|
+
return result;
|
|
518
|
+
}
|
|
519
|
+
//# sourceMappingURL=integration-test-suite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration-test-suite.js","sourceRoot":"","sources":["../../../../src/lib/call/eval/integration-test-suite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn-Taking Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the debounce logic and transcript accumulation to ensure:
|
|
5
|
+
* 1. AI doesn't interrupt mid-sentence pauses
|
|
6
|
+
* 2. Multiple transcript segments are properly combined
|
|
7
|
+
* 3. Response timing is appropriate
|
|
8
|
+
* 4. Edge cases are handled (rapid speech, long pauses, etc.)
|
|
9
|
+
*/
|
|
10
|
+
import { EventEmitter } from 'node:events';
|
|
11
|
+
export interface TranscriptEvent {
|
|
12
|
+
text: string;
|
|
13
|
+
isFinal: boolean;
|
|
14
|
+
delayMs: number;
|
|
15
|
+
}
|
|
16
|
+
export interface TurnTakingTestCase {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
/** Sequence of transcript events to simulate */
|
|
21
|
+
events: TranscriptEvent[];
|
|
22
|
+
/** Expected combined transcript when AI responds */
|
|
23
|
+
expectedTranscript: string;
|
|
24
|
+
/** Expected minimum delay before AI response (ms) */
|
|
25
|
+
expectedMinDelayMs: number;
|
|
26
|
+
/** Expected maximum delay before AI response (ms) */
|
|
27
|
+
expectedMaxDelayMs: number;
|
|
28
|
+
/** Whether AI should respond at all */
|
|
29
|
+
shouldRespond: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Simulates the turn-taking logic from CallSession
|
|
33
|
+
*/
|
|
34
|
+
export declare class TurnTakingSimulator extends EventEmitter {
|
|
35
|
+
private responseDebounceTimer;
|
|
36
|
+
private pendingTranscript;
|
|
37
|
+
private isProcessingResponse;
|
|
38
|
+
private responseTriggeredAt;
|
|
39
|
+
private startTime;
|
|
40
|
+
/**
|
|
41
|
+
* Simulate receiving a transcript event
|
|
42
|
+
*/
|
|
43
|
+
handleTranscript(text: string, isFinal: boolean): void;
|
|
44
|
+
/**
|
|
45
|
+
* Run a sequence of transcript events
|
|
46
|
+
*/
|
|
47
|
+
runSequence(events: TranscriptEvent[]): Promise<{
|
|
48
|
+
responded: boolean;
|
|
49
|
+
transcript: string;
|
|
50
|
+
delayMs: number;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Test cases for turn-taking
|
|
55
|
+
*/
|
|
56
|
+
export declare const TURN_TAKING_TEST_CASES: TurnTakingTestCase[];
|
|
57
|
+
export interface TurnTakingTestResult {
|
|
58
|
+
testId: string;
|
|
59
|
+
testName: string;
|
|
60
|
+
passed: boolean;
|
|
61
|
+
responded: boolean;
|
|
62
|
+
expectedRespond: boolean;
|
|
63
|
+
transcript: string;
|
|
64
|
+
expectedTranscript: string;
|
|
65
|
+
transcriptMatch: boolean;
|
|
66
|
+
delayMs: number;
|
|
67
|
+
expectedMinDelayMs: number;
|
|
68
|
+
expectedMaxDelayMs: number;
|
|
69
|
+
delayInRange: boolean;
|
|
70
|
+
error?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run a single turn-taking test
|
|
74
|
+
*/
|
|
75
|
+
export declare function runTurnTakingTest(testCase: TurnTakingTestCase): Promise<TurnTakingTestResult>;
|
|
76
|
+
/**
|
|
77
|
+
* Run all turn-taking tests
|
|
78
|
+
*/
|
|
79
|
+
export declare function runAllTurnTakingTests(): Promise<{
|
|
80
|
+
passed: number;
|
|
81
|
+
failed: number;
|
|
82
|
+
results: TurnTakingTestResult[];
|
|
83
|
+
}>;
|
|
84
|
+
//# sourceMappingURL=turn-taking-test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-taking-test.d.ts","sourceRoot":"","sources":["../../../../src/lib/call/eval/turn-taking-test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,oDAAoD;IACpD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,SAAS,CAAa;IAE9B;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAuCtD;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;QACpD,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CA0CH;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,EAkHtD,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAsBnG;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC,CAAC,CAgBD"}
|