@inetafrica/open-claudia 3.5.47 → 3.5.59
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/CHANGELOG.md +70 -10
- package/bin/cli.js +10 -3
- package/bot.js +11 -0
- package/channels/kazee/adapter.js +244 -23
- package/channels/kazee/call/camera.js +233 -0
- package/channels/kazee/call/job-steps.js +81 -0
- package/channels/kazee/call/manager.js +1056 -27
- package/channels/kazee/call/miss-capture.js +82 -0
- package/channels/kazee/call/quick-voice.js +610 -0
- package/channels/kazee/call/session.js +787 -36
- package/channels/kazee/call/thread-context.js +100 -0
- package/channels/kazee/catch-up.js +275 -0
- package/core/actions.js +13 -4
- package/core/enforcer.js +3 -0
- package/core/handlers.js +14 -16
- package/core/identity.js +15 -1
- package/core/loopback.js +4 -1
- package/core/media.js +39 -1
- package/core/relationship.js +41 -18
- package/core/run-context.js +27 -3
- package/core/runner.js +70 -1
- package/core/state.js +6 -2
- package/core/switchboard/brain-link.js +161 -0
- package/core/switchboard/call-host.js +298 -0
- package/core/switchboard/handoff.js +80 -0
- package/core/switchboard/ipc.js +130 -0
- package/core/switchboard/supervisor.js +225 -0
- package/core/system-prompt.js +64 -12
- package/core/transcripts.js +39 -0
- package/core/tts-voice.js +62 -4
- package/core/turn-journal.js +23 -1
- package/core/utility-agent.js +5 -2
- package/package.json +2 -2
- package/test-call-speech.js +7 -0
- package/test-external-rule-scopes.js +24 -0
- package/test-fixtures/switchboard-fake-brain.js +20 -0
- package/test-kazee-call-manager.js +22 -4
- package/test-kazee-call-session.js +345 -8
- package/test-provider-compaction.js +8 -0
- package/test-provider-language.js +19 -9
- package/test-provider-prompt-parity.js +1 -0
- package/test-provider-runner.js +2 -0
- package/test-run-lock.js +24 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Segments the recogniser returned no words for. On 12 Oct a caller's question
|
|
4
|
+
// came back as nine empty finals in a row (1.2-3.7 s of audio each) and the
|
|
5
|
+
// call went silent; nothing showed whether that audio was quiet, garbled or
|
|
6
|
+
// clean speech. Every final now logs how loud its audio was, and with
|
|
7
|
+
// KAZEE_CALL_KEEP_MISSES=on the empty ones are kept as WAV for a day.
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const { resolveConfigDir } = require("../../../core/config-dir");
|
|
12
|
+
const { STT_RATE, rmsDbfs } = require("./audio");
|
|
13
|
+
|
|
14
|
+
const FRAME_BYTES = (STT_RATE / 50) * 2; // 20 ms of 16-bit mono
|
|
15
|
+
const MAX_SEGMENT_BYTES = STT_RATE * 2 * 35; // the turn cap is 30 s
|
|
16
|
+
const MIN_KEEP_BYTES = STT_RATE * 2; // under 1 s is a cough, not a question
|
|
17
|
+
const KEEP_FILES = 40;
|
|
18
|
+
const KEEP_MS = 24 * 60 * 60_000;
|
|
19
|
+
|
|
20
|
+
// The bot's settings: ~/.open-claudia/.env is loaded into core/config's
|
|
21
|
+
// config, not into process.env (3.5.50 read process.env, so the switch in
|
|
22
|
+
// .env never took effect).
|
|
23
|
+
function settings() {
|
|
24
|
+
try { return require("../../../core/config").config || process.env; } catch (_) { return process.env; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function missDir(env = settings()) {
|
|
28
|
+
return /^(on|1|true|yes)$/i.test(String(env.KAZEE_CALL_KEEP_MISSES || "").trim())
|
|
29
|
+
? path.join(resolveConfigDir(env), "call-misses")
|
|
30
|
+
: null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// rms: the whole segment; loud: its 90th-percentile 20 ms frame, i.e. how
|
|
34
|
+
// loud the voice in it was. Both dBFS, rounded.
|
|
35
|
+
function segmentLevel(pcm16) {
|
|
36
|
+
if (!pcm16?.length) return { rms: -Infinity, loud: -Infinity };
|
|
37
|
+
const frames = [];
|
|
38
|
+
for (let at = 0; at + FRAME_BYTES <= pcm16.length; at += FRAME_BYTES) {
|
|
39
|
+
frames.push(rmsDbfs(pcm16.subarray(at, at + FRAME_BYTES)));
|
|
40
|
+
}
|
|
41
|
+
frames.sort((a, b) => a - b);
|
|
42
|
+
const loud = frames.length ? frames[Math.min(frames.length - 1, Math.floor(frames.length * 0.9))] : -Infinity;
|
|
43
|
+
return { rms: Math.round(rmsDbfs(pcm16)), loud: Math.round(loud) };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function wav(pcm16, rate = STT_RATE) {
|
|
47
|
+
const header = Buffer.alloc(44);
|
|
48
|
+
header.write("RIFF", 0);
|
|
49
|
+
header.writeUInt32LE(36 + pcm16.length, 4);
|
|
50
|
+
header.write("WAVEfmt ", 8);
|
|
51
|
+
header.writeUInt32LE(16, 16);
|
|
52
|
+
header.writeUInt16LE(1, 20);
|
|
53
|
+
header.writeUInt16LE(1, 22);
|
|
54
|
+
header.writeUInt32LE(rate, 24);
|
|
55
|
+
header.writeUInt32LE(rate * 2, 28);
|
|
56
|
+
header.writeUInt16LE(2, 32);
|
|
57
|
+
header.writeUInt16LE(16, 34);
|
|
58
|
+
header.write("data", 36);
|
|
59
|
+
header.writeUInt32LE(pcm16.length, 40);
|
|
60
|
+
return Buffer.concat([header, pcm16]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function prune(dir, now = Date.now()) {
|
|
64
|
+
const files = fs.readdirSync(dir).filter(name => name.endsWith(".wav"))
|
|
65
|
+
.map(name => ({ file: path.join(dir, name), mtime: fs.statSync(path.join(dir, name)).mtimeMs }))
|
|
66
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
67
|
+
files.forEach(({ file, mtime }, index) => {
|
|
68
|
+
if (index >= KEEP_FILES || now - mtime > KEEP_MS) fs.rmSync(file, { force: true });
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Returns the kept file, or null when the segment is too short to matter.
|
|
73
|
+
function keepMiss(dir, name, pcm16) {
|
|
74
|
+
if (!dir || !pcm16 || pcm16.length < MIN_KEEP_BYTES) return null;
|
|
75
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
76
|
+
const file = path.join(dir, `${String(name).replace(/[^\w.-]+/g, "_")}.wav`);
|
|
77
|
+
fs.writeFileSync(file, wav(pcm16), { mode: 0o600 });
|
|
78
|
+
prune(dir);
|
|
79
|
+
return file;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { MAX_SEGMENT_BYTES, MIN_KEEP_BYTES, KEEP_FILES, missDir, segmentLevel, keepMiss, wav, settings };
|