omnius 1.0.425 → 1.0.427
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 +144 -27
- package/dist/scripts/live-whisper.py +12 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29383,9 +29383,19 @@ function _extractSponsorFromMeshMessage(msg) {
|
|
|
29383
29383
|
if (payload.type === 'sponsor.announce') return payload;
|
|
29384
29384
|
return null;
|
|
29385
29385
|
}
|
|
29386
|
+
var _profilePublishInFlight = false;
|
|
29387
|
+
var _profilePublishLastAt = 0;
|
|
29386
29388
|
async function _publishAgentProfileSnapshot() {
|
|
29387
29389
|
try {
|
|
29388
29390
|
if (!connected || !nexus.network || !nexus.network.dht || !nexus.network.dht.registry) return;
|
|
29391
|
+
// DHT publishes on congested networks time out (observed as repeated
|
|
29392
|
+
// [dht:registry] TimeoutError storms). Promise.race below does NOT cancel
|
|
29393
|
+
// the underlying query, so overlapping calls stack concurrent DHT walks
|
|
29394
|
+
// and burn CPU. Single-flight + a 60s floor keeps one publish at a time.
|
|
29395
|
+
var _ppNow = Date.now();
|
|
29396
|
+
if (_profilePublishInFlight || _ppNow - _profilePublishLastAt < 60000) return;
|
|
29397
|
+
_profilePublishInFlight = true;
|
|
29398
|
+
_profilePublishLastAt = _ppNow;
|
|
29389
29399
|
var node = nexus.network.node;
|
|
29390
29400
|
var caps = typeof nexus.getRegisteredCapabilities === 'function' ? nexus.getRegisteredCapabilities() : [];
|
|
29391
29401
|
var profile = {
|
|
@@ -29407,6 +29417,8 @@ async function _publishAgentProfileSnapshot() {
|
|
|
29407
29417
|
]);
|
|
29408
29418
|
} catch (err) {
|
|
29409
29419
|
dlog('profile publish failed: ' + (err.message || err));
|
|
29420
|
+
} finally {
|
|
29421
|
+
_profilePublishInFlight = false;
|
|
29410
29422
|
}
|
|
29411
29423
|
}
|
|
29412
29424
|
async function _publishCapabilityRecord(name, details) {
|
|
@@ -31286,9 +31298,11 @@ async function handleCmd(cmd) {
|
|
|
31286
31298
|
|
|
31287
31299
|
// Wait for input data to arrive (invoke protocol sends data after accept)
|
|
31288
31300
|
var waitMs = 0;
|
|
31301
|
+
// 50ms poll (was a 10ms spin) — bounded to 5s; input latency is
|
|
31302
|
+
// network-dominated so the coarser tick is invisible.
|
|
31289
31303
|
while (!inputDone && dataChunks.length === 0 && waitMs < 5000) {
|
|
31290
|
-
await new Promise(function(r) { setTimeout(r,
|
|
31291
|
-
waitMs +=
|
|
31304
|
+
await new Promise(function(r) { setTimeout(r, 50); });
|
|
31305
|
+
waitMs += 50;
|
|
31292
31306
|
}
|
|
31293
31307
|
prompt = dataChunks.join('');
|
|
31294
31308
|
dlog('expose: received ' + dataChunks.length + ' chunks, prompt_len=' + prompt.length + ' inputDone=' + inputDone);
|
|
@@ -32228,8 +32242,11 @@ async function handleCmd(cmd) {
|
|
|
32228
32242
|
}
|
|
32229
32243
|
}
|
|
32230
32244
|
|
|
32231
|
-
// Command
|
|
32232
|
-
//
|
|
32245
|
+
// Command intake: fs.watch on the nexus dir delivers cmd.json changes
|
|
32246
|
+
// instantly (registered below); this interval is only a fallback for
|
|
32247
|
+
// platforms/filesystems where fs.watch drops events. 50ms polling here was
|
|
32248
|
+
// measured pinning CPU on deployed boxes — 1s fallback costs nothing since
|
|
32249
|
+
// the watcher provides the low-latency path.
|
|
32233
32250
|
let lastCmdId = '';
|
|
32234
32251
|
function checkCmd() {
|
|
32235
32252
|
try {
|
|
@@ -32243,7 +32260,7 @@ function checkCmd() {
|
|
|
32243
32260
|
});
|
|
32244
32261
|
} catch {}
|
|
32245
32262
|
}
|
|
32246
|
-
setInterval(checkCmd,
|
|
32263
|
+
setInterval(checkCmd, 1000);
|
|
32247
32264
|
// Also watch for cmd.json changes for instant notification (best-effort)
|
|
32248
32265
|
try {
|
|
32249
32266
|
fsWatch(nexusDir, { persistent: false }, function(evType, filename) {
|
|
@@ -32629,18 +32646,35 @@ process.on('unhandledRejection', (reason) => {
|
|
|
32629
32646
|
} catch {}
|
|
32630
32647
|
try {
|
|
32631
32648
|
if (nexus.nats && typeof nexus.nats.subscribe === 'function') {
|
|
32649
|
+
var _peersFileLastWrite = 0;
|
|
32632
32650
|
nexus.nats.subscribe(function(announcement) {
|
|
32633
32651
|
if (!announcement || !announcement.peerId) return;
|
|
32634
32652
|
if (announcement.peerId === nexus.peerId) return; // skip self
|
|
32635
|
-
|
|
32653
|
+
// Peers re-announce every 10-30s. Persisting the full JSON map and
|
|
32654
|
+
// logging on EVERY announcement turned steady-state discovery into
|
|
32655
|
+
// constant file I/O + log churn. Only react to NEW peers or
|
|
32656
|
+
// material changes; lastSeen bumps persist on a 30s throttle.
|
|
32657
|
+
var prev = discoveredPeers[announcement.peerId];
|
|
32658
|
+
var next = {
|
|
32636
32659
|
peerId: announcement.peerId,
|
|
32637
32660
|
agentName: announcement.agentName || '',
|
|
32638
32661
|
capabilities: announcement.capabilities || [],
|
|
32639
32662
|
multiaddrs: announcement.multiaddrs || [],
|
|
32640
32663
|
lastSeen: Date.now(),
|
|
32641
32664
|
};
|
|
32642
|
-
|
|
32643
|
-
|
|
32665
|
+
var changed = !prev
|
|
32666
|
+
|| prev.agentName !== next.agentName
|
|
32667
|
+
|| JSON.stringify(prev.capabilities) !== JSON.stringify(next.capabilities)
|
|
32668
|
+
|| JSON.stringify(prev.multiaddrs) !== JSON.stringify(next.multiaddrs);
|
|
32669
|
+
discoveredPeers[announcement.peerId] = next;
|
|
32670
|
+
var nowMs = Date.now();
|
|
32671
|
+
if (changed || nowMs - _peersFileLastWrite >= 30000) {
|
|
32672
|
+
_peersFileLastWrite = nowMs;
|
|
32673
|
+
try { writeFileSync(discoveredPeersFile, JSON.stringify(discoveredPeers, null, 2)); } catch {}
|
|
32674
|
+
}
|
|
32675
|
+
if (changed) {
|
|
32676
|
+
dlog('NATS peer discovered: ' + String(announcement.peerId).slice(0, 20) + ' caps=' + (announcement.capabilities || []).length);
|
|
32677
|
+
}
|
|
32644
32678
|
});
|
|
32645
32679
|
dlog('NATS peer discovery subscription active');
|
|
32646
32680
|
}
|
|
@@ -603585,7 +603619,9 @@ var init_listen = __esm({
|
|
|
603585
603619
|
case "transcript":
|
|
603586
603620
|
this.emit("transcript", {
|
|
603587
603621
|
text: evt.text,
|
|
603588
|
-
isFinal: evt.isFinal ?? false
|
|
603622
|
+
isFinal: evt.isFinal ?? false,
|
|
603623
|
+
consensus: typeof evt.consensus === "boolean" ? evt.consensus : void 0,
|
|
603624
|
+
doa: typeof evt.doa === "number" ? evt.doa : void 0
|
|
603589
603625
|
});
|
|
603590
603626
|
break;
|
|
603591
603627
|
case "consensus_rejected":
|
|
@@ -603982,7 +604018,7 @@ var init_listen = __esm({
|
|
|
603982
604018
|
this.lastTranscriptTime = Date.now();
|
|
603983
604019
|
this.pendingText = evt.text.trim();
|
|
603984
604020
|
updateListenLiveState({ lastTranscript: this.pendingText, lastTranscriptAt: this.lastTranscriptTime });
|
|
603985
|
-
this.emit("transcript", this.pendingText, evt.isFinal);
|
|
604021
|
+
this.emit("transcript", this.pendingText, evt.isFinal, { consensus: evt.consensus, doa: evt.doa });
|
|
603986
604022
|
if (this.config.mode === "auto") this.resetSilenceTimer();
|
|
603987
604023
|
});
|
|
603988
604024
|
fallback.on("error", (err) => {
|
|
@@ -617195,6 +617231,25 @@ function rms(arr) {
|
|
|
617195
617231
|
return Math.sqrt(sum / arr.length);
|
|
617196
617232
|
}
|
|
617197
617233
|
|
|
617234
|
+
// Linear resampler: browsers frequently IGNORE the requested AudioContext
|
|
617235
|
+
// sample rate and run at the hardware rate (typically 48kHz). Sending those
|
|
617236
|
+
// frames as-if-16kHz made the audio unpack 3x slow/pitch-dropped server-side,
|
|
617237
|
+
// wrecking ASR. Always resample the actual context rate down to 16kHz.
|
|
617238
|
+
function resampleTo16k(input, srcRate) {
|
|
617239
|
+
if (srcRate === 16000) return input;
|
|
617240
|
+
const ratio = srcRate / 16000;
|
|
617241
|
+
const outLen = Math.floor(input.length / ratio);
|
|
617242
|
+
const out = new Float32Array(outLen);
|
|
617243
|
+
for (let i = 0; i < outLen; i++) {
|
|
617244
|
+
const pos = i * ratio;
|
|
617245
|
+
const i0 = Math.floor(pos);
|
|
617246
|
+
const i1 = Math.min(input.length - 1, i0 + 1);
|
|
617247
|
+
const frac = pos - i0;
|
|
617248
|
+
out[i] = input[i0] * (1 - frac) + input[i1] * frac;
|
|
617249
|
+
}
|
|
617250
|
+
return out;
|
|
617251
|
+
}
|
|
617252
|
+
|
|
617198
617253
|
async function toggleMic() {
|
|
617199
617254
|
if (micActive) { stopMic(); return; }
|
|
617200
617255
|
try {
|
|
@@ -617203,17 +617258,19 @@ async function toggleMic() {
|
|
|
617203
617258
|
audio: { sampleRate: 16000, channelCount: 1, echoCancellation: true, noiseSuppression: true }
|
|
617204
617259
|
});
|
|
617205
617260
|
const source = micCtx.createMediaStreamSource(micStream);
|
|
617261
|
+
const actualRate = micCtx.sampleRate; // may be 48000 regardless of the request
|
|
617206
617262
|
scriptProcessor = micCtx.createScriptProcessor(4096, 1, 1);
|
|
617207
617263
|
scriptProcessor.onaudioprocess = (e) => {
|
|
617208
617264
|
if (!micActive || !ws || ws.readyState !== 1) return;
|
|
617209
|
-
const
|
|
617265
|
+
const raw = e.inputBuffer.getChannelData(0);
|
|
617266
|
+
const input = resampleTo16k(raw, actualRate);
|
|
617210
617267
|
const int16 = new Int16Array(input.length);
|
|
617211
617268
|
for (let i = 0; i < input.length; i++) {
|
|
617212
617269
|
const s = Math.max(-1, Math.min(1, input[i]));
|
|
617213
617270
|
int16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
|
617214
617271
|
}
|
|
617215
617272
|
ws.send(int16.buffer);
|
|
617216
|
-
micLevel = Math.min(1, rms(
|
|
617273
|
+
micLevel = Math.min(1, rms(raw) * 5);
|
|
617217
617274
|
};
|
|
617218
617275
|
source.connect(scriptProcessor);
|
|
617219
617276
|
scriptProcessor.connect(micCtx.destination);
|
|
@@ -617222,7 +617279,8 @@ async function toggleMic() {
|
|
|
617222
617279
|
micBtn.textContent = 'stop';
|
|
617223
617280
|
micBtn.classList.add('active');
|
|
617224
617281
|
hintEl.textContent = 'listening...';
|
|
617225
|
-
|
|
617282
|
+
// Declare the post-resample rate (and the raw context rate for debugging)
|
|
617283
|
+
ws.send(JSON.stringify({ type: 'mic_start', username: 'web-user', sampleRate: 16000, contextRate: actualRate }));
|
|
617226
617284
|
} catch (err) {
|
|
617227
617285
|
hintEl.textContent = 'mic error: ' + err.message;
|
|
617228
617286
|
}
|
|
@@ -617595,6 +617653,23 @@ connectPP();
|
|
|
617595
617653
|
</body>
|
|
617596
617654
|
</html>`;
|
|
617597
617655
|
}
|
|
617656
|
+
function resamplePcm16Mono(pcm, srcRate, dstRate) {
|
|
617657
|
+
if (srcRate === dstRate || srcRate <= 0 || dstRate <= 0) return pcm;
|
|
617658
|
+
const srcSamples = Math.floor(pcm.length / 2);
|
|
617659
|
+
if (srcSamples === 0) return pcm;
|
|
617660
|
+
const ratio = srcRate / dstRate;
|
|
617661
|
+
const outSamples = Math.max(1, Math.floor(srcSamples / ratio));
|
|
617662
|
+
const out = Buffer.allocUnsafe(outSamples * 2);
|
|
617663
|
+
for (let i2 = 0; i2 < outSamples; i2++) {
|
|
617664
|
+
const pos = i2 * ratio;
|
|
617665
|
+
const i0 = Math.min(srcSamples - 1, Math.floor(pos));
|
|
617666
|
+
const i1 = Math.min(srcSamples - 1, i0 + 1);
|
|
617667
|
+
const frac = pos - i0;
|
|
617668
|
+
const sample = pcm.readInt16LE(i0 * 2) * (1 - frac) + pcm.readInt16LE(i1 * 2) * frac;
|
|
617669
|
+
out.writeInt16LE(Math.max(-32768, Math.min(32767, Math.round(sample))), i2 * 2);
|
|
617670
|
+
}
|
|
617671
|
+
return out;
|
|
617672
|
+
}
|
|
617598
617673
|
function renderVoiceSessionStart(tunnelUrl) {
|
|
617599
617674
|
renderBoxedBlock({
|
|
617600
617675
|
title: "☁ Live Voice Session",
|
|
@@ -617902,17 +617977,26 @@ var init_voice_session = __esm({
|
|
|
617902
617977
|
clearInterval(keepaliveInterval);
|
|
617903
617978
|
}
|
|
617904
617979
|
}, 1e4);
|
|
617980
|
+
let clientPcmRate = 16e3;
|
|
617905
617981
|
ws.on("message", (data, isBinary) => {
|
|
617906
617982
|
if (isBinary) {
|
|
617907
617983
|
if (!this.ttsSpeaking) {
|
|
617908
|
-
|
|
617984
|
+
let pcm = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
617985
|
+
if (clientPcmRate !== 16e3 && clientPcmRate > 0) {
|
|
617986
|
+
pcm = resamplePcm16Mono(pcm, clientPcmRate, 16e3);
|
|
617987
|
+
}
|
|
617988
|
+
this.onUserAudio?.(pcm, clientId);
|
|
617909
617989
|
}
|
|
617910
617990
|
} else {
|
|
617911
617991
|
try {
|
|
617912
617992
|
const msg = JSON.parse(data.toString());
|
|
617913
|
-
if (msg.type === "mic_start"
|
|
617914
|
-
|
|
617915
|
-
|
|
617993
|
+
if (msg.type === "mic_start") {
|
|
617994
|
+
if (msg.username) {
|
|
617995
|
+
const entry = this.state.connectedUsers.get(clientId);
|
|
617996
|
+
if (entry) entry.username = msg.username;
|
|
617997
|
+
}
|
|
617998
|
+
const declared = Number(msg.sampleRate);
|
|
617999
|
+
clientPcmRate = Number.isFinite(declared) && declared >= 8e3 && declared <= 192e3 ? declared : 16e3;
|
|
617916
618000
|
}
|
|
617917
618001
|
} catch {
|
|
617918
618002
|
}
|
|
@@ -697480,6 +697564,9 @@ Rules:
|
|
|
697480
697564
|
// VAD segment capture
|
|
697481
697565
|
captureBuffer = "";
|
|
697482
697566
|
captureStartTime = 0;
|
|
697567
|
+
consensusMetadataSeen = false;
|
|
697568
|
+
consensusText = null;
|
|
697569
|
+
consensusSignalScore = null;
|
|
697483
697570
|
silenceTimer = null;
|
|
697484
697571
|
maxSegmentTimer = null;
|
|
697485
697572
|
lastSignalScore = null;
|
|
@@ -697570,18 +697657,25 @@ Rules:
|
|
|
697570
697657
|
let isFinal;
|
|
697571
697658
|
let snr;
|
|
697572
697659
|
let confidence2;
|
|
697660
|
+
let consensus;
|
|
697573
697661
|
if (typeof args[0] === "object" && args[0] !== null) {
|
|
697574
697662
|
const evt = args[0];
|
|
697575
697663
|
text2 = evt.text ?? "";
|
|
697576
697664
|
isFinal = evt.isFinal ?? false;
|
|
697577
697665
|
snr = evt.snr;
|
|
697578
697666
|
confidence2 = evt.confidence;
|
|
697667
|
+
consensus = typeof evt.consensus === "boolean" ? evt.consensus : void 0;
|
|
697579
697668
|
} else {
|
|
697580
697669
|
text2 = String(args[0] ?? "");
|
|
697581
697670
|
isFinal = Boolean(args[1]);
|
|
697671
|
+
const meta = args[2];
|
|
697672
|
+
if (meta && typeof meta === "object") {
|
|
697673
|
+
const m2 = meta;
|
|
697674
|
+
if (typeof m2.consensus === "boolean") consensus = m2.consensus;
|
|
697675
|
+
}
|
|
697582
697676
|
}
|
|
697583
697677
|
if (!text2.trim()) return;
|
|
697584
|
-
this.handleTranscript(text2.trim(), isFinal, snr, confidence2);
|
|
697678
|
+
this.handleTranscript(text2.trim(), isFinal, snr, confidence2, consensus);
|
|
697585
697679
|
};
|
|
697586
697680
|
this._onError = (err) => {
|
|
697587
697681
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -697648,7 +697742,7 @@ Rules:
|
|
|
697648
697742
|
// ---------------------------------------------------------------------------
|
|
697649
697743
|
// Transcript handling — VAD-style segment capture (Voryn pattern)
|
|
697650
697744
|
// ---------------------------------------------------------------------------
|
|
697651
|
-
handleTranscript(text2, isFinal, snr, confidence2) {
|
|
697745
|
+
handleTranscript(text2, isFinal, snr, confidence2, consensus) {
|
|
697652
697746
|
if (!this.active) return;
|
|
697653
697747
|
if (this._state !== "LISTENING" && this._state !== "CAPTURING") {
|
|
697654
697748
|
return;
|
|
@@ -697661,7 +697755,7 @@ Rules:
|
|
|
697661
697755
|
}
|
|
697662
697756
|
if (this._state === "LISTENING") {
|
|
697663
697757
|
this.setState("CAPTURING");
|
|
697664
|
-
this.
|
|
697758
|
+
this.resetCaptureState();
|
|
697665
697759
|
this.captureStartTime = Date.now();
|
|
697666
697760
|
this.maxSegmentTimer = setTimeout(() => {
|
|
697667
697761
|
if (this._state === "CAPTURING") {
|
|
@@ -697669,8 +697763,18 @@ Rules:
|
|
|
697669
697763
|
}
|
|
697670
697764
|
}, MAX_SEGMENT_MS);
|
|
697671
697765
|
}
|
|
697672
|
-
|
|
697673
|
-
this.lastSignalScore =
|
|
697766
|
+
const signalScore = typeof snr === "number" && !Number.isNaN(snr) ? clamp0116(snr) : computeSignalFromText(text2, confidence2);
|
|
697767
|
+
this.lastSignalScore = signalScore;
|
|
697768
|
+
if (consensus !== void 0) {
|
|
697769
|
+
this.consensusMetadataSeen = true;
|
|
697770
|
+
if (consensus === true) {
|
|
697771
|
+
this.consensusText = text2;
|
|
697772
|
+
this.consensusSignalScore = signalScore;
|
|
697773
|
+
this.captureBuffer = text2;
|
|
697774
|
+
}
|
|
697775
|
+
} else {
|
|
697776
|
+
this.captureBuffer = text2;
|
|
697777
|
+
}
|
|
697674
697778
|
this.emit("snr", { score: this.lastSignalScore });
|
|
697675
697779
|
this.onPartialTranscript(text2);
|
|
697676
697780
|
if (this.silenceTimer) clearTimeout(this.silenceTimer);
|
|
@@ -697681,11 +697785,22 @@ Rules:
|
|
|
697681
697785
|
}
|
|
697682
697786
|
}, waitMs);
|
|
697683
697787
|
}
|
|
697788
|
+
resetCaptureState() {
|
|
697789
|
+
this.captureBuffer = "";
|
|
697790
|
+
this.captureStartTime = 0;
|
|
697791
|
+
this.lastSignalScore = null;
|
|
697792
|
+
this.consensusMetadataSeen = false;
|
|
697793
|
+
this.consensusText = null;
|
|
697794
|
+
this.consensusSignalScore = null;
|
|
697795
|
+
}
|
|
697684
697796
|
// ---------------------------------------------------------------------------
|
|
697685
697797
|
// Segment finalization → Transcribing → Thinking → Speaking
|
|
697686
697798
|
// ---------------------------------------------------------------------------
|
|
697687
697799
|
finalizeSegment() {
|
|
697688
|
-
const
|
|
697800
|
+
const rawText = this.captureBuffer.trim();
|
|
697801
|
+
const requiresConsensus = this.consensusMetadataSeen;
|
|
697802
|
+
const text2 = requiresConsensus ? (this.consensusText ?? "").trim() : rawText;
|
|
697803
|
+
const signalScore = requiresConsensus ? this.consensusSignalScore ?? this.lastSignalScore : this.lastSignalScore;
|
|
697689
697804
|
if (this.silenceTimer) {
|
|
697690
697805
|
clearTimeout(this.silenceTimer);
|
|
697691
697806
|
this.silenceTimer = null;
|
|
@@ -697694,18 +697809,20 @@ Rules:
|
|
|
697694
697809
|
clearTimeout(this.maxSegmentTimer);
|
|
697695
697810
|
this.maxSegmentTimer = null;
|
|
697696
697811
|
}
|
|
697697
|
-
this.
|
|
697812
|
+
this.resetCaptureState();
|
|
697698
697813
|
if (!text2) {
|
|
697814
|
+
if (requiresConsensus && rawText) {
|
|
697815
|
+
if (this.debugSnr) this.onStatus(`Ignoring utterance without dual-ASR consensus: ${truncateForLog(rawText, 48)}`);
|
|
697816
|
+
this.emit("consensusFiltered", { text: rawText });
|
|
697817
|
+
}
|
|
697699
697818
|
this.setState("LISTENING");
|
|
697700
697819
|
return;
|
|
697701
697820
|
}
|
|
697702
|
-
const score = Math.min(
|
|
697821
|
+
const score = Math.min(signalScore ?? 1, computeSignalFromText(text2));
|
|
697703
697822
|
if (score < MIN_SIGNAL_SCORE || NOISE_ONLY_RE.test(text2)) {
|
|
697704
697823
|
if (this.debugSnr) this.onStatus(`Ignoring low-signal utterance (SNR:${score.toFixed(2)}): ${truncateForLog(text2, 48)}`);
|
|
697705
697824
|
this.emit("snrFiltered", { score, text: text2 });
|
|
697706
697825
|
this.setState("LISTENING");
|
|
697707
|
-
this.captureBuffer = "";
|
|
697708
|
-
this.lastSignalScore = null;
|
|
697709
697826
|
return;
|
|
697710
697827
|
}
|
|
697711
697828
|
this.setState("TRANSCRIBING");
|
|
@@ -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,14 @@ 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 — the
|
|
487
|
+
# downstream voicechat gate requires convergence for pass-through.
|
|
488
|
+
emit_transcript(
|
|
489
|
+
text,
|
|
490
|
+
is_final=True,
|
|
491
|
+
doa=tuning.doa() if tuning else None,
|
|
492
|
+
consensus=consensus_model is not None,
|
|
493
|
+
)
|
|
485
494
|
except Exception as e:
|
|
486
495
|
emit_error(f"Transcription error: {e}")
|
|
487
496
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.427",
|
|
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.427",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED