@chatpanel/gateway 0.6.27 → 0.6.28
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/package.json +1 -1
- package/src/diarize-engine.js +34 -3
- package/src/server.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/gateway",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.28",
|
|
4
4
|
"description": "Local privacy gateway — redacts PII out of OpenAI/Anthropic API traffic before it reaches a model, then restores it in the reply. Point opencode, codex, aider, Claude Code, etc. at it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/diarize-engine.js
CHANGED
|
@@ -99,9 +99,17 @@ export function download({ onLog } = {}) {
|
|
|
99
99
|
return _initPromise;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
const SR = 16000;
|
|
103
|
+
|
|
104
|
+
function l2norm(v) {
|
|
105
|
+
let n = 0; for (const x of v) n += x * x;
|
|
106
|
+
n = Math.sqrt(n) || 1;
|
|
107
|
+
const o = new Float32Array(v.length);
|
|
108
|
+
for (let i = 0; i < v.length; i++) o[i] = v[i] / n;
|
|
109
|
+
return o;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function embedOne(audio) {
|
|
105
113
|
try {
|
|
106
114
|
const inputs = await _processor(audio);
|
|
107
115
|
const out = await _model(inputs);
|
|
@@ -110,6 +118,29 @@ export async function embed(audio) {
|
|
|
110
118
|
} catch { return null; }
|
|
111
119
|
}
|
|
112
120
|
|
|
121
|
+
// Embed a mono 16 kHz Float32 segment → L2-normalized x-vector, or null if not
|
|
122
|
+
// ready. For longer turns we embed several overlapping ~2 s windows and AVERAGE
|
|
123
|
+
// the normalized vectors — a more stable speaker fingerprint than one embed of
|
|
124
|
+
// the whole (noisy) segment. Cheap on the native runtime (runs once per turn).
|
|
125
|
+
export async function embed(audio) {
|
|
126
|
+
if (!isReady() || !(audio instanceof Float32Array) || !audio.length) return null;
|
|
127
|
+
const win = 2 * SR;
|
|
128
|
+
const hop = Math.round(1.3 * SR);
|
|
129
|
+
if (audio.length <= Math.round(win * 1.4)) {
|
|
130
|
+
const v = await embedOne(audio);
|
|
131
|
+
return v ? l2norm(v) : null;
|
|
132
|
+
}
|
|
133
|
+
const vecs = [];
|
|
134
|
+
for (let s = 0; s + win <= audio.length; s += hop) {
|
|
135
|
+
const v = await embedOne(audio.subarray(s, s + win));
|
|
136
|
+
if (v) vecs.push(l2norm(v));
|
|
137
|
+
}
|
|
138
|
+
if (!vecs.length) { const v = await embedOne(audio); return v ? l2norm(v) : null; }
|
|
139
|
+
const avg = new Float32Array(vecs[0].length);
|
|
140
|
+
for (const v of vecs) for (let i = 0; i < avg.length; i++) avg[i] += v[i];
|
|
141
|
+
return l2norm(avg); // mean of unit vectors, renormalized
|
|
142
|
+
}
|
|
143
|
+
|
|
113
144
|
export function _reset() { _state = 'off'; _model = null; _processor = null; _err = null; _initPromise = null; _progress = null; }
|
|
114
145
|
export function _setForTest(model, processor) { _model = model; _processor = processor; _state = model ? 'ready' : 'off'; }
|
|
115
146
|
|
package/src/server.js
CHANGED
|
@@ -43,7 +43,7 @@ import * as openai from './openai.js';
|
|
|
43
43
|
import * as responses from './responses.js';
|
|
44
44
|
import * as anthropic from './anthropic.js';
|
|
45
45
|
|
|
46
|
-
export const VERSION = '0.6.
|
|
46
|
+
export const VERSION = '0.6.28';
|
|
47
47
|
|
|
48
48
|
// WARM search tier — SQLite + FTS5 record store (falls back to an encrypted-JSON
|
|
49
49
|
// store if SQLite can't load), fed by the extension's ingest sync + backup-ingest.
|