@chatpanel/gateway 0.6.1 → 0.6.3
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 +2 -2
- package/src/ner-engine.js +33 -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.3",
|
|
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": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"node": ">=18"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@chatpanel/pii": "^0.2.
|
|
30
|
+
"@chatpanel/pii": "^0.2.6",
|
|
31
31
|
"@huggingface/transformers": "^4.2.0",
|
|
32
32
|
"onnxruntime-web": "1.26.0-dev.20260416-b7804b056c"
|
|
33
33
|
},
|
package/src/ner-engine.js
CHANGED
|
@@ -76,7 +76,8 @@ export function health() {
|
|
|
76
76
|
export async function detect(text, { signal } = {}) {
|
|
77
77
|
if (!isReady()) return [];
|
|
78
78
|
if (signal?.aborted) return [];
|
|
79
|
-
const
|
|
79
|
+
const src = String(text || '');
|
|
80
|
+
const out = await _pipe(src, { aggregation_strategy: 'simple' });
|
|
80
81
|
const ents = [];
|
|
81
82
|
for (const e of out || []) {
|
|
82
83
|
let word = String(e.word ?? '');
|
|
@@ -90,8 +91,37 @@ export async function detect(text, { signal } = {}) {
|
|
|
90
91
|
if (!word) continue;
|
|
91
92
|
ents.push({ value: word, type });
|
|
92
93
|
}
|
|
93
|
-
//
|
|
94
|
-
|
|
94
|
+
// Heal subword truncation AFTER merging (expanding before would corrupt a span the
|
|
95
|
+
// next "##" token still extends, e.g. "Sure"→"Suresh" then +"sh Kumar"). Then drop
|
|
96
|
+
// 1-char noise + dedupe (expansion can make two spans collide).
|
|
97
|
+
const seen = new Set();
|
|
98
|
+
const out2 = [];
|
|
99
|
+
for (const e of ents) {
|
|
100
|
+
const value = expandToWord(src, e.value.trim());
|
|
101
|
+
if (value.length <= 1) continue;
|
|
102
|
+
const k = `${e.type}:${value.toLowerCase()}`;
|
|
103
|
+
if (seen.has(k)) continue;
|
|
104
|
+
seen.add(k);
|
|
105
|
+
out2.push({ value, type: e.type });
|
|
106
|
+
}
|
|
107
|
+
return out2;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Grow a detected span to its full surrounding word in the ORIGINAL text. A cased
|
|
111
|
+
// model can truncate a name to a subword ("Suresh" → "Sure"), which would leave a
|
|
112
|
+
// dangling fragment ("…[[PERSON]]sh") un-redacted — a leak. We extend across word
|
|
113
|
+
// characters at both edges so the whole token is captured. Multi-word spans are
|
|
114
|
+
// unaffected (their edges are already at spaces).
|
|
115
|
+
function expandToWord(text, value) {
|
|
116
|
+
if (!value) return value;
|
|
117
|
+
const idx = text.indexOf(value);
|
|
118
|
+
if (idx < 0) return value;
|
|
119
|
+
const isWord = (c) => !!c && /[\p{L}\p{N}'’-]/u.test(c);
|
|
120
|
+
let s = idx;
|
|
121
|
+
let e = idx + value.length;
|
|
122
|
+
while (s > 0 && isWord(text[s - 1])) s--;
|
|
123
|
+
while (e < text.length && isWord(text[e])) e++;
|
|
124
|
+
return text.slice(s, e);
|
|
95
125
|
}
|
|
96
126
|
|
|
97
127
|
// Lets @chatpanel/pii's existing `endpoint` detection backend call us with NO real
|
package/src/server.js
CHANGED
|
@@ -35,7 +35,7 @@ import * as openai from './openai.js';
|
|
|
35
35
|
import * as responses from './responses.js';
|
|
36
36
|
import * as anthropic from './anthropic.js';
|
|
37
37
|
|
|
38
|
-
export const VERSION = '0.6.
|
|
38
|
+
export const VERSION = '0.6.3';
|
|
39
39
|
|
|
40
40
|
const KNOWN_AGENTS = new Set(['codex', 'claude', 'opencode', 'pi', 'kiro', 'antigravity']);
|
|
41
41
|
|