omnius 1.0.426 → 1.0.428
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/dist/index.js +161 -53
- package/dist/scripts/live-whisper.py +14 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -603384,6 +603384,15 @@ async function findLiveWhisperScript() {
|
|
|
603384
603384
|
}
|
|
603385
603385
|
return null;
|
|
603386
603386
|
}
|
|
603387
|
+
function defaultConsensusModel(primaryModel) {
|
|
603388
|
+
const model = primaryModel.trim().toLowerCase();
|
|
603389
|
+
if (model === "tiny") return "base";
|
|
603390
|
+
if (model === "base") return "small";
|
|
603391
|
+
if (model === "small") return "base";
|
|
603392
|
+
if (model === "medium") return "base";
|
|
603393
|
+
if (model === "large-v3" || model === "large") return "small";
|
|
603394
|
+
return "base";
|
|
603395
|
+
}
|
|
603387
603396
|
async function ensureVenvForTranscribeCli() {
|
|
603388
603397
|
const bin = process.platform === "win32" ? "Scripts" : "bin";
|
|
603389
603398
|
const exe = process.platform === "win32" ? "python.exe" : "python3";
|
|
@@ -603573,7 +603582,9 @@ var init_listen = __esm({
|
|
|
603573
603582
|
lastStatus: `loading whisper ${this.model} model...`
|
|
603574
603583
|
});
|
|
603575
603584
|
const consensusEnv = String(process.env["OMNIUS_ASR_CONSENSUS"] ?? "").trim().toLowerCase();
|
|
603576
|
-
const consensusModel = consensusEnv === "0" || consensusEnv === "off" || consensusEnv === "false" ? "off" : consensusEnv || (this.model
|
|
603585
|
+
const consensusModel = consensusEnv === "0" || consensusEnv === "off" || consensusEnv === "false" ? "off" : consensusEnv || defaultConsensusModel(this.model);
|
|
603586
|
+
const consensusThreshold = String(process.env["OMNIUS_ASR_CONSENSUS_THRESHOLD"] ?? "0.45");
|
|
603587
|
+
const silenceMs = String(process.env["OMNIUS_ASR_SILENCE_MS"] ?? "800");
|
|
603577
603588
|
this.process = spawn29(
|
|
603578
603589
|
pyPath,
|
|
603579
603590
|
[
|
|
@@ -603585,7 +603596,11 @@ var init_listen = __esm({
|
|
|
603585
603596
|
"--window-seconds",
|
|
603586
603597
|
"10",
|
|
603587
603598
|
"--consensus-model",
|
|
603588
|
-
consensusModel
|
|
603599
|
+
consensusModel,
|
|
603600
|
+
"--consensus-threshold",
|
|
603601
|
+
consensusThreshold,
|
|
603602
|
+
"--silence-ms",
|
|
603603
|
+
silenceMs
|
|
603589
603604
|
],
|
|
603590
603605
|
{
|
|
603591
603606
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -603619,13 +603634,19 @@ var init_listen = __esm({
|
|
|
603619
603634
|
case "transcript":
|
|
603620
603635
|
this.emit("transcript", {
|
|
603621
603636
|
text: evt.text,
|
|
603622
|
-
isFinal: evt.isFinal ?? false
|
|
603637
|
+
isFinal: evt.isFinal ?? false,
|
|
603638
|
+
consensus: typeof evt.consensus === "boolean" ? evt.consensus : void 0,
|
|
603639
|
+
doa: typeof evt.doa === "number" ? evt.doa : void 0
|
|
603623
603640
|
});
|
|
603624
603641
|
break;
|
|
603625
603642
|
case "consensus_rejected":
|
|
603626
|
-
|
|
603627
|
-
|
|
603628
|
-
|
|
603643
|
+
{
|
|
603644
|
+
const message2 = `consensus rejected (${evt.reason ?? "mismatch"}): ${String(evt.primary ?? "").slice(0, 80)}`;
|
|
603645
|
+
updateListenLiveState({
|
|
603646
|
+
lastStatus: message2
|
|
603647
|
+
});
|
|
603648
|
+
this.emit("status", message2);
|
|
603649
|
+
}
|
|
603629
603650
|
break;
|
|
603630
603651
|
case "vad": {
|
|
603631
603652
|
lastHardwareVadAt = Date.now();
|
|
@@ -604016,7 +604037,7 @@ var init_listen = __esm({
|
|
|
604016
604037
|
this.lastTranscriptTime = Date.now();
|
|
604017
604038
|
this.pendingText = evt.text.trim();
|
|
604018
604039
|
updateListenLiveState({ lastTranscript: this.pendingText, lastTranscriptAt: this.lastTranscriptTime });
|
|
604019
|
-
this.emit("transcript", this.pendingText, evt.isFinal);
|
|
604040
|
+
this.emit("transcript", this.pendingText, evt.isFinal, { consensus: evt.consensus, doa: evt.doa });
|
|
604020
604041
|
if (this.config.mode === "auto") this.resetSilenceTimer();
|
|
604021
604042
|
});
|
|
604022
604043
|
fallback.on("error", (err) => {
|
|
@@ -604031,8 +604052,9 @@ transcribe-cli error: ${transcribeCliError}` : "";
|
|
|
604031
604052
|
return `Failed to start live transcription: ${msg}${tcHint}`;
|
|
604032
604053
|
}
|
|
604033
604054
|
}
|
|
604034
|
-
this.spawnMicProcess(micCmd);
|
|
604035
604055
|
this.active = true;
|
|
604056
|
+
this.paused = false;
|
|
604057
|
+
this.spawnMicProcess(micCmd);
|
|
604036
604058
|
this.blinkState = true;
|
|
604037
604059
|
this.blinkTimer = setInterval(() => {
|
|
604038
604060
|
this.blinkState = !this.blinkState;
|
|
@@ -617229,6 +617251,25 @@ function rms(arr) {
|
|
|
617229
617251
|
return Math.sqrt(sum / arr.length);
|
|
617230
617252
|
}
|
|
617231
617253
|
|
|
617254
|
+
// Linear resampler: browsers frequently IGNORE the requested AudioContext
|
|
617255
|
+
// sample rate and run at the hardware rate (typically 48kHz). Sending those
|
|
617256
|
+
// frames as-if-16kHz made the audio unpack 3x slow/pitch-dropped server-side,
|
|
617257
|
+
// wrecking ASR. Always resample the actual context rate down to 16kHz.
|
|
617258
|
+
function resampleTo16k(input, srcRate) {
|
|
617259
|
+
if (srcRate === 16000) return input;
|
|
617260
|
+
const ratio = srcRate / 16000;
|
|
617261
|
+
const outLen = Math.floor(input.length / ratio);
|
|
617262
|
+
const out = new Float32Array(outLen);
|
|
617263
|
+
for (let i = 0; i < outLen; i++) {
|
|
617264
|
+
const pos = i * ratio;
|
|
617265
|
+
const i0 = Math.floor(pos);
|
|
617266
|
+
const i1 = Math.min(input.length - 1, i0 + 1);
|
|
617267
|
+
const frac = pos - i0;
|
|
617268
|
+
out[i] = input[i0] * (1 - frac) + input[i1] * frac;
|
|
617269
|
+
}
|
|
617270
|
+
return out;
|
|
617271
|
+
}
|
|
617272
|
+
|
|
617232
617273
|
async function toggleMic() {
|
|
617233
617274
|
if (micActive) { stopMic(); return; }
|
|
617234
617275
|
try {
|
|
@@ -617237,17 +617278,19 @@ async function toggleMic() {
|
|
|
617237
617278
|
audio: { sampleRate: 16000, channelCount: 1, echoCancellation: true, noiseSuppression: true }
|
|
617238
617279
|
});
|
|
617239
617280
|
const source = micCtx.createMediaStreamSource(micStream);
|
|
617281
|
+
const actualRate = micCtx.sampleRate; // may be 48000 regardless of the request
|
|
617240
617282
|
scriptProcessor = micCtx.createScriptProcessor(4096, 1, 1);
|
|
617241
617283
|
scriptProcessor.onaudioprocess = (e) => {
|
|
617242
617284
|
if (!micActive || !ws || ws.readyState !== 1) return;
|
|
617243
|
-
const
|
|
617285
|
+
const raw = e.inputBuffer.getChannelData(0);
|
|
617286
|
+
const input = resampleTo16k(raw, actualRate);
|
|
617244
617287
|
const int16 = new Int16Array(input.length);
|
|
617245
617288
|
for (let i = 0; i < input.length; i++) {
|
|
617246
617289
|
const s = Math.max(-1, Math.min(1, input[i]));
|
|
617247
617290
|
int16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
|
617248
617291
|
}
|
|
617249
617292
|
ws.send(int16.buffer);
|
|
617250
|
-
micLevel = Math.min(1, rms(
|
|
617293
|
+
micLevel = Math.min(1, rms(raw) * 5);
|
|
617251
617294
|
};
|
|
617252
617295
|
source.connect(scriptProcessor);
|
|
617253
617296
|
scriptProcessor.connect(micCtx.destination);
|
|
@@ -617256,7 +617299,8 @@ async function toggleMic() {
|
|
|
617256
617299
|
micBtn.textContent = 'stop';
|
|
617257
617300
|
micBtn.classList.add('active');
|
|
617258
617301
|
hintEl.textContent = 'listening...';
|
|
617259
|
-
|
|
617302
|
+
// Declare the post-resample rate (and the raw context rate for debugging)
|
|
617303
|
+
ws.send(JSON.stringify({ type: 'mic_start', username: 'web-user', sampleRate: 16000, contextRate: actualRate }));
|
|
617260
617304
|
} catch (err) {
|
|
617261
617305
|
hintEl.textContent = 'mic error: ' + err.message;
|
|
617262
617306
|
}
|
|
@@ -617629,6 +617673,23 @@ connectPP();
|
|
|
617629
617673
|
</body>
|
|
617630
617674
|
</html>`;
|
|
617631
617675
|
}
|
|
617676
|
+
function resamplePcm16Mono(pcm, srcRate, dstRate) {
|
|
617677
|
+
if (srcRate === dstRate || srcRate <= 0 || dstRate <= 0) return pcm;
|
|
617678
|
+
const srcSamples = Math.floor(pcm.length / 2);
|
|
617679
|
+
if (srcSamples === 0) return pcm;
|
|
617680
|
+
const ratio = srcRate / dstRate;
|
|
617681
|
+
const outSamples = Math.max(1, Math.floor(srcSamples / ratio));
|
|
617682
|
+
const out = Buffer.allocUnsafe(outSamples * 2);
|
|
617683
|
+
for (let i2 = 0; i2 < outSamples; i2++) {
|
|
617684
|
+
const pos = i2 * ratio;
|
|
617685
|
+
const i0 = Math.min(srcSamples - 1, Math.floor(pos));
|
|
617686
|
+
const i1 = Math.min(srcSamples - 1, i0 + 1);
|
|
617687
|
+
const frac = pos - i0;
|
|
617688
|
+
const sample = pcm.readInt16LE(i0 * 2) * (1 - frac) + pcm.readInt16LE(i1 * 2) * frac;
|
|
617689
|
+
out.writeInt16LE(Math.max(-32768, Math.min(32767, Math.round(sample))), i2 * 2);
|
|
617690
|
+
}
|
|
617691
|
+
return out;
|
|
617692
|
+
}
|
|
617632
617693
|
function renderVoiceSessionStart(tunnelUrl) {
|
|
617633
617694
|
renderBoxedBlock({
|
|
617634
617695
|
title: "☁ Live Voice Session",
|
|
@@ -617936,17 +617997,26 @@ var init_voice_session = __esm({
|
|
|
617936
617997
|
clearInterval(keepaliveInterval);
|
|
617937
617998
|
}
|
|
617938
617999
|
}, 1e4);
|
|
618000
|
+
let clientPcmRate = 16e3;
|
|
617939
618001
|
ws.on("message", (data, isBinary) => {
|
|
617940
618002
|
if (isBinary) {
|
|
617941
618003
|
if (!this.ttsSpeaking) {
|
|
617942
|
-
|
|
618004
|
+
let pcm = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
618005
|
+
if (clientPcmRate !== 16e3 && clientPcmRate > 0) {
|
|
618006
|
+
pcm = resamplePcm16Mono(pcm, clientPcmRate, 16e3);
|
|
618007
|
+
}
|
|
618008
|
+
this.onUserAudio?.(pcm, clientId);
|
|
617943
618009
|
}
|
|
617944
618010
|
} else {
|
|
617945
618011
|
try {
|
|
617946
618012
|
const msg = JSON.parse(data.toString());
|
|
617947
|
-
if (msg.type === "mic_start"
|
|
617948
|
-
|
|
617949
|
-
|
|
618013
|
+
if (msg.type === "mic_start") {
|
|
618014
|
+
if (msg.username) {
|
|
618015
|
+
const entry = this.state.connectedUsers.get(clientId);
|
|
618016
|
+
if (entry) entry.username = msg.username;
|
|
618017
|
+
}
|
|
618018
|
+
const declared = Number(msg.sampleRate);
|
|
618019
|
+
clientPcmRate = Number.isFinite(declared) && declared >= 8e3 && declared <= 192e3 ? declared : 16e3;
|
|
617950
618020
|
}
|
|
617951
618021
|
} catch {
|
|
617952
618022
|
}
|
|
@@ -697514,6 +697584,9 @@ Rules:
|
|
|
697514
697584
|
// VAD segment capture
|
|
697515
697585
|
captureBuffer = "";
|
|
697516
697586
|
captureStartTime = 0;
|
|
697587
|
+
consensusMetadataSeen = false;
|
|
697588
|
+
consensusText = null;
|
|
697589
|
+
consensusSignalScore = null;
|
|
697517
697590
|
silenceTimer = null;
|
|
697518
697591
|
maxSegmentTimer = null;
|
|
697519
697592
|
lastSignalScore = null;
|
|
@@ -697604,18 +697677,25 @@ Rules:
|
|
|
697604
697677
|
let isFinal;
|
|
697605
697678
|
let snr;
|
|
697606
697679
|
let confidence2;
|
|
697680
|
+
let consensus;
|
|
697607
697681
|
if (typeof args[0] === "object" && args[0] !== null) {
|
|
697608
697682
|
const evt = args[0];
|
|
697609
697683
|
text2 = evt.text ?? "";
|
|
697610
697684
|
isFinal = evt.isFinal ?? false;
|
|
697611
697685
|
snr = evt.snr;
|
|
697612
697686
|
confidence2 = evt.confidence;
|
|
697687
|
+
consensus = typeof evt.consensus === "boolean" ? evt.consensus : void 0;
|
|
697613
697688
|
} else {
|
|
697614
697689
|
text2 = String(args[0] ?? "");
|
|
697615
697690
|
isFinal = Boolean(args[1]);
|
|
697691
|
+
const meta = args[2];
|
|
697692
|
+
if (meta && typeof meta === "object") {
|
|
697693
|
+
const m2 = meta;
|
|
697694
|
+
if (typeof m2.consensus === "boolean") consensus = m2.consensus;
|
|
697695
|
+
}
|
|
697616
697696
|
}
|
|
697617
697697
|
if (!text2.trim()) return;
|
|
697618
|
-
this.handleTranscript(text2.trim(), isFinal, snr, confidence2);
|
|
697698
|
+
this.handleTranscript(text2.trim(), isFinal, snr, confidence2, consensus);
|
|
697619
697699
|
};
|
|
697620
697700
|
this._onError = (err) => {
|
|
697621
697701
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -697682,7 +697762,7 @@ Rules:
|
|
|
697682
697762
|
// ---------------------------------------------------------------------------
|
|
697683
697763
|
// Transcript handling — VAD-style segment capture (Voryn pattern)
|
|
697684
697764
|
// ---------------------------------------------------------------------------
|
|
697685
|
-
handleTranscript(text2, isFinal, snr, confidence2) {
|
|
697765
|
+
handleTranscript(text2, isFinal, snr, confidence2, consensus) {
|
|
697686
697766
|
if (!this.active) return;
|
|
697687
697767
|
if (this._state !== "LISTENING" && this._state !== "CAPTURING") {
|
|
697688
697768
|
return;
|
|
@@ -697695,7 +697775,7 @@ Rules:
|
|
|
697695
697775
|
}
|
|
697696
697776
|
if (this._state === "LISTENING") {
|
|
697697
697777
|
this.setState("CAPTURING");
|
|
697698
|
-
this.
|
|
697778
|
+
this.resetCaptureState();
|
|
697699
697779
|
this.captureStartTime = Date.now();
|
|
697700
697780
|
this.maxSegmentTimer = setTimeout(() => {
|
|
697701
697781
|
if (this._state === "CAPTURING") {
|
|
@@ -697703,23 +697783,44 @@ Rules:
|
|
|
697703
697783
|
}
|
|
697704
697784
|
}, MAX_SEGMENT_MS);
|
|
697705
697785
|
}
|
|
697706
|
-
|
|
697707
|
-
this.lastSignalScore =
|
|
697786
|
+
const signalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp0116(snr) : computeSignalFromText(text2, confidence2);
|
|
697787
|
+
this.lastSignalScore = signalScore;
|
|
697788
|
+
if (consensus !== void 0) {
|
|
697789
|
+
this.consensusMetadataSeen = true;
|
|
697790
|
+
if (consensus === true) {
|
|
697791
|
+
this.consensusText = text2;
|
|
697792
|
+
this.consensusSignalScore = signalScore;
|
|
697793
|
+
this.captureBuffer = text2;
|
|
697794
|
+
}
|
|
697795
|
+
} else {
|
|
697796
|
+
this.captureBuffer = text2;
|
|
697797
|
+
}
|
|
697708
697798
|
this.emit("snr", { score: this.lastSignalScore });
|
|
697709
697799
|
this.onPartialTranscript(text2);
|
|
697710
697800
|
if (this.silenceTimer) clearTimeout(this.silenceTimer);
|
|
697711
|
-
const waitMs = this._vadSilenceMs ?? VAD_SILENCE_MS;
|
|
697801
|
+
const waitMs = isFinal ? 0 : this._vadSilenceMs ?? VAD_SILENCE_MS;
|
|
697712
697802
|
this.silenceTimer = setTimeout(() => {
|
|
697713
697803
|
if (this._state === "CAPTURING") {
|
|
697714
697804
|
this.finalizeSegment();
|
|
697715
697805
|
}
|
|
697716
697806
|
}, waitMs);
|
|
697717
697807
|
}
|
|
697808
|
+
resetCaptureState() {
|
|
697809
|
+
this.captureBuffer = "";
|
|
697810
|
+
this.captureStartTime = 0;
|
|
697811
|
+
this.lastSignalScore = null;
|
|
697812
|
+
this.consensusMetadataSeen = false;
|
|
697813
|
+
this.consensusText = null;
|
|
697814
|
+
this.consensusSignalScore = null;
|
|
697815
|
+
}
|
|
697718
697816
|
// ---------------------------------------------------------------------------
|
|
697719
697817
|
// Segment finalization → Transcribing → Thinking → Speaking
|
|
697720
697818
|
// ---------------------------------------------------------------------------
|
|
697721
697819
|
finalizeSegment() {
|
|
697722
|
-
const
|
|
697820
|
+
const rawText = this.captureBuffer.trim();
|
|
697821
|
+
const requiresConsensus = this.consensusMetadataSeen;
|
|
697822
|
+
const text2 = requiresConsensus ? (this.consensusText ?? "").trim() : rawText;
|
|
697823
|
+
const signalScore = requiresConsensus ? this.consensusSignalScore ?? this.lastSignalScore : this.lastSignalScore;
|
|
697723
697824
|
if (this.silenceTimer) {
|
|
697724
697825
|
clearTimeout(this.silenceTimer);
|
|
697725
697826
|
this.silenceTimer = null;
|
|
@@ -697728,18 +697829,20 @@ Rules:
|
|
|
697728
697829
|
clearTimeout(this.maxSegmentTimer);
|
|
697729
697830
|
this.maxSegmentTimer = null;
|
|
697730
697831
|
}
|
|
697731
|
-
this.
|
|
697832
|
+
this.resetCaptureState();
|
|
697732
697833
|
if (!text2) {
|
|
697834
|
+
if (requiresConsensus && rawText) {
|
|
697835
|
+
if (this.debugSnr) this.onStatus(`Ignoring utterance without dual-ASR consensus: ${truncateForLog(rawText, 48)}`);
|
|
697836
|
+
this.emit("consensusFiltered", { text: rawText });
|
|
697837
|
+
}
|
|
697733
697838
|
this.setState("LISTENING");
|
|
697734
697839
|
return;
|
|
697735
697840
|
}
|
|
697736
|
-
const score = Math.min(
|
|
697841
|
+
const score = Math.min(signalScore ?? 1, computeSignalFromText(text2));
|
|
697737
697842
|
if (score < MIN_SIGNAL_SCORE || NOISE_ONLY_RE.test(text2)) {
|
|
697738
697843
|
if (this.debugSnr) this.onStatus(`Ignoring low-signal utterance (SNR:${score.toFixed(2)}): ${truncateForLog(text2, 48)}`);
|
|
697739
697844
|
this.emit("snrFiltered", { score, text: text2 });
|
|
697740
697845
|
this.setState("LISTENING");
|
|
697741
|
-
this.captureBuffer = "";
|
|
697742
|
-
this.lastSignalScore = null;
|
|
697743
697846
|
return;
|
|
697744
697847
|
}
|
|
697745
697848
|
this.setState("TRANSCRIBING");
|
|
@@ -697862,35 +697965,35 @@ ${toolOutput}` });
|
|
|
697862
697965
|
const reply = extractVoiceModelReply(stripToolJsonLines(response.trim()));
|
|
697863
697966
|
const finalSpoken = reply.text;
|
|
697864
697967
|
if (!finalSpoken || reply.action === "silent") {
|
|
697865
|
-
|
|
697866
|
-
|
|
697867
|
-
|
|
697868
|
-
|
|
697869
|
-
|
|
697870
|
-
|
|
697968
|
+
} else {
|
|
697969
|
+
if (typeof this.voice.waitUntilIdle === "function") {
|
|
697970
|
+
try {
|
|
697971
|
+
await this.voice.waitUntilIdle();
|
|
697972
|
+
} catch {
|
|
697973
|
+
}
|
|
697871
697974
|
}
|
|
697872
|
-
|
|
697873
|
-
|
|
697874
|
-
|
|
697875
|
-
|
|
697876
|
-
|
|
697877
|
-
|
|
697878
|
-
|
|
697879
|
-
|
|
697880
|
-
|
|
697881
|
-
|
|
697882
|
-
|
|
697883
|
-
|
|
697884
|
-
|
|
697885
|
-
|
|
697886
|
-
|
|
697975
|
+
this.context.push({ role: "assistant", content: finalSpoken });
|
|
697976
|
+
this.trimContext();
|
|
697977
|
+
this.setState("SPEAKING");
|
|
697978
|
+
this.onAgentSpeech(finalSpoken);
|
|
697979
|
+
this.lastAgentSpeech = { text: finalSpoken, startedAt: Date.now() };
|
|
697980
|
+
this.voice.speak(finalSpoken);
|
|
697981
|
+
this.voiceTranscript.push({ role: "assistant", content: finalSpoken, ts: Date.now() });
|
|
697982
|
+
if (this.runner && this.turnCount % SUMMARY_INJECTION_INTERVAL === 0) {
|
|
697983
|
+
this.injectSummary();
|
|
697984
|
+
}
|
|
697985
|
+
if (typeof this.voice.waitUntilIdle === "function") {
|
|
697986
|
+
try {
|
|
697987
|
+
await this.voice.waitUntilIdle();
|
|
697988
|
+
} catch {
|
|
697989
|
+
}
|
|
697990
|
+
} else {
|
|
697991
|
+
const estimatedMs = Math.max(1500, finalSpoken.length / 5 * (6e4 / 150));
|
|
697992
|
+
await new Promise((r2) => setTimeout(r2, estimatedMs));
|
|
697993
|
+
}
|
|
697994
|
+
if (reply.action === "hangup") {
|
|
697995
|
+
this.active = false;
|
|
697887
697996
|
}
|
|
697888
|
-
} else {
|
|
697889
|
-
const estimatedMs = Math.max(1500, finalSpoken.length / 5 * (6e4 / 150));
|
|
697890
|
-
await new Promise((r2) => setTimeout(r2, estimatedMs));
|
|
697891
|
-
}
|
|
697892
|
-
if (reply.action === "hangup") {
|
|
697893
|
-
this.active = false;
|
|
697894
697997
|
}
|
|
697895
697998
|
}
|
|
697896
697999
|
} catch (err) {
|
|
@@ -737057,6 +737160,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
737057
737160
|
listenEng.on("level", (evt) => {
|
|
737058
737161
|
statusBar.setMicActivity(evt.speechActive, evt.levelDb);
|
|
737059
737162
|
});
|
|
737163
|
+
listenEng.on("info", (msg) => {
|
|
737164
|
+
if (/consensus rejected|loading whisper|whisper model loaded/i.test(msg)) {
|
|
737165
|
+
writeContent(() => renderInfo(`[voicechat/asr] ${msg}`));
|
|
737166
|
+
}
|
|
737167
|
+
});
|
|
737060
737168
|
const summaryRunner = {
|
|
737061
737169
|
injectUserMessage(content) {
|
|
737062
737170
|
if (activeTask?.runner) {
|
|
@@ -76,10 +76,12 @@ def emit_error(msg: str):
|
|
|
76
76
|
emit({"type": "error", "message": msg})
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def emit_transcript(text: str, is_final: bool = False, doa=None):
|
|
79
|
+
def emit_transcript(text: str, is_final: bool = False, doa=None, consensus=None):
|
|
80
80
|
event = {"type": "transcript", "text": text, "isFinal": is_final}
|
|
81
81
|
if doa is not None:
|
|
82
82
|
event["doa"] = doa
|
|
83
|
+
if consensus is not None:
|
|
84
|
+
event["consensus"] = bool(consensus)
|
|
83
85
|
emit(event)
|
|
84
86
|
|
|
85
87
|
# ---------------------------------------------------------------------------
|
|
@@ -441,7 +443,7 @@ def main():
|
|
|
441
443
|
result = run_transcribe(model, amplify(samples))
|
|
442
444
|
text = _segment_filtered_text(result)
|
|
443
445
|
if text:
|
|
444
|
-
emit_transcript(text, is_final=False)
|
|
446
|
+
emit_transcript(text, is_final=False, consensus=False)
|
|
445
447
|
except Exception as e:
|
|
446
448
|
emit_error(f"Transcription error: {e}")
|
|
447
449
|
|
|
@@ -481,7 +483,16 @@ def main():
|
|
|
481
483
|
if text == last_final_text:
|
|
482
484
|
return
|
|
483
485
|
last_final_text = text
|
|
484
|
-
|
|
486
|
+
# consensus=True only when BOTH models ran and converged. When the
|
|
487
|
+
# second model is off/unavailable, omit consensus metadata entirely
|
|
488
|
+
# so legacy single-model consumers do not interpret consensus=false
|
|
489
|
+
# as an explicit dual-ASR rejection.
|
|
490
|
+
emit_transcript(
|
|
491
|
+
text,
|
|
492
|
+
is_final=True,
|
|
493
|
+
doa=tuning.doa() if tuning else None,
|
|
494
|
+
consensus=True if consensus_model is not None else None,
|
|
495
|
+
)
|
|
485
496
|
except Exception as e:
|
|
486
497
|
emit_error(f"Transcription error: {e}")
|
|
487
498
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.428",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "omnius",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.428",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED