gheim 0.1.0 → 0.1.2
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/cjs/index.cjs +79 -110
- package/dist/cjs/openai/index.cjs +111 -112
- package/dist/esm/index.js +86 -112
- package/dist/esm/openai/index.js +118 -114
- package/dist/types/core/session.d.ts +42 -6
- package/dist/types/core/session.d.ts.map +1 -1
- package/dist/types/detectors/index.d.ts +1 -1
- package/dist/types/detectors/index.d.ts.map +1 -1
- package/dist/types/detectors/local.d.ts +65 -2
- package/dist/types/detectors/local.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/normalizers.d.ts +5 -2
- package/dist/types/normalizers.d.ts.map +1 -1
- package/dist/types/openai/_chat.d.ts.map +1 -1
- package/dist/types/openai/_client.d.ts +15 -2
- package/dist/types/openai/_client.d.ts.map +1 -1
- package/package.json +31 -9
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
3
8
|
|
|
4
9
|
// src/core/sentinels.ts
|
|
5
10
|
var LABEL_TO_TAG = {
|
|
@@ -37,98 +42,6 @@ function isPossibleSentinelPrefix(buffer) {
|
|
|
37
42
|
return PARTIAL_SENTINEL_RE.test(buffer);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
// src/normalizers.ts
|
|
41
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
42
|
-
var _require = null;
|
|
43
|
-
var _requireResolved = false;
|
|
44
|
-
function _getRequire() {
|
|
45
|
-
if (_requireResolved)
|
|
46
|
-
return _require;
|
|
47
|
-
_requireResolved = true;
|
|
48
|
-
try {
|
|
49
|
-
_require = createRequire2(import.meta.url);
|
|
50
|
-
} catch {
|
|
51
|
-
_require = null;
|
|
52
|
-
}
|
|
53
|
-
return _require;
|
|
54
|
-
}
|
|
55
|
-
function loadSync(name, errMsg) {
|
|
56
|
-
const req = _getRequire();
|
|
57
|
-
if (!req)
|
|
58
|
-
throw new Error(errMsg);
|
|
59
|
-
try {
|
|
60
|
-
return req(name);
|
|
61
|
-
} catch {
|
|
62
|
-
throw new Error(errMsg);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function loadPhoneLib() {
|
|
66
|
-
return loadSync("libphonenumber-js", "the e164 normalizer requires libphonenumber-js. " + "Install with: bun add libphonenumber-js");
|
|
67
|
-
}
|
|
68
|
-
function e164(opts = {}) {
|
|
69
|
-
const lib = loadPhoneLib();
|
|
70
|
-
return (s) => {
|
|
71
|
-
try {
|
|
72
|
-
const parsed = lib.parsePhoneNumberFromString(s, opts.region);
|
|
73
|
-
if (!parsed || !parsed.isValid())
|
|
74
|
-
return null;
|
|
75
|
-
return parsed.format("E.164");
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function loadChrono(opts) {
|
|
82
|
-
const mod = loadSync("chrono-node", "the isoDate normalizer requires chrono-node. " + "Install with: bun add chrono-node");
|
|
83
|
-
if (opts.locale) {
|
|
84
|
-
const localeParser = mod[opts.locale];
|
|
85
|
-
if (localeParser)
|
|
86
|
-
return localeParser;
|
|
87
|
-
}
|
|
88
|
-
return mod;
|
|
89
|
-
}
|
|
90
|
-
function isoDate(opts = {}) {
|
|
91
|
-
const parser = loadChrono(opts);
|
|
92
|
-
return (s) => {
|
|
93
|
-
const d = parser.parseDate(s);
|
|
94
|
-
if (!d)
|
|
95
|
-
return null;
|
|
96
|
-
return d.toISOString().slice(0, 10);
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
function germanTransliteration() {
|
|
100
|
-
const table = {
|
|
101
|
-
ä: "ae",
|
|
102
|
-
Ä: "ae",
|
|
103
|
-
ö: "oe",
|
|
104
|
-
Ö: "oe",
|
|
105
|
-
ü: "ue",
|
|
106
|
-
Ü: "ue",
|
|
107
|
-
ß: "ss"
|
|
108
|
-
};
|
|
109
|
-
return (s) => {
|
|
110
|
-
let out = "";
|
|
111
|
-
for (const ch of s.normalize("NFKC")) {
|
|
112
|
-
out += table[ch] ?? ch;
|
|
113
|
-
}
|
|
114
|
-
return out.toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
var BUILTINS = {
|
|
118
|
-
e164: () => e164(),
|
|
119
|
-
iso_date: () => isoDate(),
|
|
120
|
-
german: () => germanTransliteration()
|
|
121
|
-
};
|
|
122
|
-
function resolveNormalizer(spec) {
|
|
123
|
-
if (typeof spec === "function")
|
|
124
|
-
return spec;
|
|
125
|
-
const factory = BUILTINS[spec];
|
|
126
|
-
if (!factory) {
|
|
127
|
-
throw new Error(`unknown built-in normalizer: ${JSON.stringify(spec)}. ` + `Available: ${Object.keys(BUILTINS).join(", ")}, or pass a callable.`);
|
|
128
|
-
}
|
|
129
|
-
return factory();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
45
|
// src/core/session.ts
|
|
133
46
|
function normalizeSurface(s) {
|
|
134
47
|
return s.normalize("NFKC").toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
@@ -141,8 +54,8 @@ class Session {
|
|
|
141
54
|
resolvedNormalizers = {};
|
|
142
55
|
constructor(opts = {}) {
|
|
143
56
|
if (opts.normalizers) {
|
|
144
|
-
for (const [label,
|
|
145
|
-
this.resolvedNormalizers[label] =
|
|
57
|
+
for (const [label, fn] of Object.entries(opts.normalizers)) {
|
|
58
|
+
this.resolvedNormalizers[label] = fn;
|
|
146
59
|
}
|
|
147
60
|
}
|
|
148
61
|
}
|
|
@@ -166,8 +79,11 @@ class Session {
|
|
|
166
79
|
return sentinel;
|
|
167
80
|
}
|
|
168
81
|
applySpans(text, spans) {
|
|
82
|
+
return this.applySpansDetailed(text, spans).redacted;
|
|
83
|
+
}
|
|
84
|
+
applySpansDetailed(text, spans) {
|
|
169
85
|
if (spans.length === 0)
|
|
170
|
-
return text;
|
|
86
|
+
return { redacted: text, merged: [] };
|
|
171
87
|
const sorted = [...spans].sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
|
|
172
88
|
const merged = mergeAdjacent(text, sorted);
|
|
173
89
|
const out = [];
|
|
@@ -183,7 +99,7 @@ class Session {
|
|
|
183
99
|
cursor = end;
|
|
184
100
|
}
|
|
185
101
|
out.push(text.slice(cursor));
|
|
186
|
-
return out.join("");
|
|
102
|
+
return { redacted: out.join(""), merged };
|
|
187
103
|
}
|
|
188
104
|
restore(text) {
|
|
189
105
|
if (Object.keys(this.mapping).length === 0)
|
|
@@ -381,8 +297,10 @@ class LocalDetector {
|
|
|
381
297
|
chunkTokens;
|
|
382
298
|
chunkOverlapTokens;
|
|
383
299
|
batchSize;
|
|
300
|
+
actualDevice = null;
|
|
384
301
|
_pipeline = null;
|
|
385
302
|
_loadPromise = null;
|
|
303
|
+
_onProgress;
|
|
386
304
|
constructor(opts = {}) {
|
|
387
305
|
this.model = opts.model ?? DEFAULT_MODEL2;
|
|
388
306
|
this.device = opts.device ?? "auto";
|
|
@@ -391,11 +309,13 @@ class LocalDetector {
|
|
|
391
309
|
this.chunkTokens = opts.chunkTokens ?? DEFAULT_CHUNK_TOKENS;
|
|
392
310
|
this.chunkOverlapTokens = opts.chunkOverlapTokens ?? DEFAULT_OVERLAP_TOKENS;
|
|
393
311
|
this.batchSize = opts.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
312
|
+
this._onProgress = opts.onProgress;
|
|
394
313
|
if (this.chunkOverlapTokens >= this.chunkTokens) {
|
|
395
314
|
throw new Error(`chunkOverlapTokens (${this.chunkOverlapTokens}) must be smaller than chunkTokens (${this.chunkTokens})`);
|
|
396
315
|
}
|
|
397
316
|
if (opts.pipelineInstance) {
|
|
398
317
|
this._pipeline = opts.pipelineInstance;
|
|
318
|
+
this.actualDevice = this.device === "auto" ? null : this.device;
|
|
399
319
|
}
|
|
400
320
|
}
|
|
401
321
|
async load() {
|
|
@@ -404,24 +324,70 @@ class LocalDetector {
|
|
|
404
324
|
if (this._loadPromise)
|
|
405
325
|
return this._loadPromise;
|
|
406
326
|
this._loadPromise = (async () => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
327
|
+
const mod = await loadTransformersJs();
|
|
328
|
+
const sequence = await this._planDeviceSequence();
|
|
329
|
+
let lastErr;
|
|
330
|
+
for (let i = 0;i < sequence.length; i++) {
|
|
331
|
+
const dev = sequence[i] ?? "wasm";
|
|
332
|
+
try {
|
|
333
|
+
const pipe = await mod.pipeline("token-classification", this.model, {
|
|
334
|
+
device: dev,
|
|
335
|
+
dtype: this.dtype,
|
|
336
|
+
progress_callback: this._onProgress ? (raw) => this._emitProgress(raw, dev) : undefined
|
|
337
|
+
});
|
|
338
|
+
this._pipeline = pipe;
|
|
339
|
+
this.actualDevice = dev;
|
|
340
|
+
return pipe;
|
|
341
|
+
} catch (e) {
|
|
342
|
+
lastErr = e;
|
|
343
|
+
if (i < sequence.length - 1)
|
|
344
|
+
continue;
|
|
345
|
+
throw e;
|
|
346
|
+
}
|
|
412
347
|
}
|
|
413
|
-
|
|
414
|
-
device: this.device,
|
|
415
|
-
dtype: this.dtype
|
|
416
|
-
});
|
|
417
|
-
this._pipeline = pipe;
|
|
418
|
-
return pipe;
|
|
348
|
+
throw lastErr ?? new Error("LocalDetector.load(): no candidate backends");
|
|
419
349
|
})();
|
|
420
350
|
return this._loadPromise;
|
|
421
351
|
}
|
|
352
|
+
async _planDeviceSequence() {
|
|
353
|
+
if (this.device !== "auto")
|
|
354
|
+
return [this.device];
|
|
355
|
+
const seq = [];
|
|
356
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
357
|
+
if (nav?.gpu?.requestAdapter) {
|
|
358
|
+
try {
|
|
359
|
+
const adapter = await nav.gpu.requestAdapter();
|
|
360
|
+
if (adapter)
|
|
361
|
+
seq.push("webgpu");
|
|
362
|
+
} catch {}
|
|
363
|
+
}
|
|
364
|
+
seq.push("wasm");
|
|
365
|
+
return seq;
|
|
366
|
+
}
|
|
367
|
+
_emitProgress(raw, device) {
|
|
368
|
+
if (!this._onProgress)
|
|
369
|
+
return;
|
|
370
|
+
const event = {
|
|
371
|
+
phase: raw.status ?? "progress",
|
|
372
|
+
device
|
|
373
|
+
};
|
|
374
|
+
if (raw.file)
|
|
375
|
+
event.file = raw.file;
|
|
376
|
+
if (raw.status === "progress") {
|
|
377
|
+
if (raw.loaded != null)
|
|
378
|
+
event.loadedBytes = raw.loaded;
|
|
379
|
+
if (raw.total != null)
|
|
380
|
+
event.totalBytes = raw.total;
|
|
381
|
+
if (raw.progress != null) {
|
|
382
|
+
const norm = raw.progress > 1 ? raw.progress / 100 : raw.progress;
|
|
383
|
+
event.fraction = Math.max(0, Math.min(1, norm));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
this._onProgress(event);
|
|
387
|
+
}
|
|
422
388
|
async detect(text, opts) {
|
|
423
389
|
if (opts?.model && opts.model !== this.model) {
|
|
424
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
390
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
425
391
|
}
|
|
426
392
|
if (!text)
|
|
427
393
|
return [];
|
|
@@ -430,7 +396,7 @@ class LocalDetector {
|
|
|
430
396
|
}
|
|
431
397
|
async detectBatch(texts, opts) {
|
|
432
398
|
if (opts?.model && opts.model !== this.model) {
|
|
433
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
399
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
434
400
|
}
|
|
435
401
|
if (texts.length === 0)
|
|
436
402
|
return [];
|
|
@@ -591,6 +557,13 @@ function chunkByChars(text, chunkChars, overlapChars) {
|
|
|
591
557
|
function normalizeWord(word) {
|
|
592
558
|
return word.replace(/##/g, "").trim();
|
|
593
559
|
}
|
|
560
|
+
async function loadTransformersJs() {
|
|
561
|
+
try {
|
|
562
|
+
return await import("@huggingface/transformers");
|
|
563
|
+
} catch {
|
|
564
|
+
throw new Error("LocalDetector requires `@huggingface/transformers`. Install with: bun add @huggingface/transformers");
|
|
565
|
+
}
|
|
566
|
+
}
|
|
594
567
|
// src/detectors/index.ts
|
|
595
568
|
function defaultDetector() {
|
|
596
569
|
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
@@ -645,10 +618,11 @@ async function* deanonymizeStream(chunks, session) {
|
|
|
645
618
|
}
|
|
646
619
|
|
|
647
620
|
// src/index.ts
|
|
648
|
-
var VERSION = "0.1.
|
|
621
|
+
var VERSION = "0.1.2";
|
|
649
622
|
export {
|
|
650
623
|
renderSentinel,
|
|
651
624
|
parseSentinel,
|
|
625
|
+
mergeAdjacent,
|
|
652
626
|
labelTag,
|
|
653
627
|
isPossibleSentinelPrefix,
|
|
654
628
|
defaultDetector,
|
package/dist/esm/openai/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
3
8
|
|
|
4
9
|
// src/core/sentinels.ts
|
|
5
10
|
var LABEL_TO_TAG = {
|
|
@@ -228,8 +233,10 @@ class LocalDetector {
|
|
|
228
233
|
chunkTokens;
|
|
229
234
|
chunkOverlapTokens;
|
|
230
235
|
batchSize;
|
|
236
|
+
actualDevice = null;
|
|
231
237
|
_pipeline = null;
|
|
232
238
|
_loadPromise = null;
|
|
239
|
+
_onProgress;
|
|
233
240
|
constructor(opts = {}) {
|
|
234
241
|
this.model = opts.model ?? DEFAULT_MODEL2;
|
|
235
242
|
this.device = opts.device ?? "auto";
|
|
@@ -238,11 +245,13 @@ class LocalDetector {
|
|
|
238
245
|
this.chunkTokens = opts.chunkTokens ?? DEFAULT_CHUNK_TOKENS;
|
|
239
246
|
this.chunkOverlapTokens = opts.chunkOverlapTokens ?? DEFAULT_OVERLAP_TOKENS;
|
|
240
247
|
this.batchSize = opts.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
248
|
+
this._onProgress = opts.onProgress;
|
|
241
249
|
if (this.chunkOverlapTokens >= this.chunkTokens) {
|
|
242
250
|
throw new Error(`chunkOverlapTokens (${this.chunkOverlapTokens}) must be smaller than chunkTokens (${this.chunkTokens})`);
|
|
243
251
|
}
|
|
244
252
|
if (opts.pipelineInstance) {
|
|
245
253
|
this._pipeline = opts.pipelineInstance;
|
|
254
|
+
this.actualDevice = this.device === "auto" ? null : this.device;
|
|
246
255
|
}
|
|
247
256
|
}
|
|
248
257
|
async load() {
|
|
@@ -251,24 +260,70 @@ class LocalDetector {
|
|
|
251
260
|
if (this._loadPromise)
|
|
252
261
|
return this._loadPromise;
|
|
253
262
|
this._loadPromise = (async () => {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
263
|
+
const mod = await loadTransformersJs();
|
|
264
|
+
const sequence = await this._planDeviceSequence();
|
|
265
|
+
let lastErr;
|
|
266
|
+
for (let i = 0;i < sequence.length; i++) {
|
|
267
|
+
const dev = sequence[i] ?? "wasm";
|
|
268
|
+
try {
|
|
269
|
+
const pipe = await mod.pipeline("token-classification", this.model, {
|
|
270
|
+
device: dev,
|
|
271
|
+
dtype: this.dtype,
|
|
272
|
+
progress_callback: this._onProgress ? (raw) => this._emitProgress(raw, dev) : undefined
|
|
273
|
+
});
|
|
274
|
+
this._pipeline = pipe;
|
|
275
|
+
this.actualDevice = dev;
|
|
276
|
+
return pipe;
|
|
277
|
+
} catch (e) {
|
|
278
|
+
lastErr = e;
|
|
279
|
+
if (i < sequence.length - 1)
|
|
280
|
+
continue;
|
|
281
|
+
throw e;
|
|
282
|
+
}
|
|
259
283
|
}
|
|
260
|
-
|
|
261
|
-
device: this.device,
|
|
262
|
-
dtype: this.dtype
|
|
263
|
-
});
|
|
264
|
-
this._pipeline = pipe;
|
|
265
|
-
return pipe;
|
|
284
|
+
throw lastErr ?? new Error("LocalDetector.load(): no candidate backends");
|
|
266
285
|
})();
|
|
267
286
|
return this._loadPromise;
|
|
268
287
|
}
|
|
288
|
+
async _planDeviceSequence() {
|
|
289
|
+
if (this.device !== "auto")
|
|
290
|
+
return [this.device];
|
|
291
|
+
const seq = [];
|
|
292
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
293
|
+
if (nav?.gpu?.requestAdapter) {
|
|
294
|
+
try {
|
|
295
|
+
const adapter = await nav.gpu.requestAdapter();
|
|
296
|
+
if (adapter)
|
|
297
|
+
seq.push("webgpu");
|
|
298
|
+
} catch {}
|
|
299
|
+
}
|
|
300
|
+
seq.push("wasm");
|
|
301
|
+
return seq;
|
|
302
|
+
}
|
|
303
|
+
_emitProgress(raw, device) {
|
|
304
|
+
if (!this._onProgress)
|
|
305
|
+
return;
|
|
306
|
+
const event = {
|
|
307
|
+
phase: raw.status ?? "progress",
|
|
308
|
+
device
|
|
309
|
+
};
|
|
310
|
+
if (raw.file)
|
|
311
|
+
event.file = raw.file;
|
|
312
|
+
if (raw.status === "progress") {
|
|
313
|
+
if (raw.loaded != null)
|
|
314
|
+
event.loadedBytes = raw.loaded;
|
|
315
|
+
if (raw.total != null)
|
|
316
|
+
event.totalBytes = raw.total;
|
|
317
|
+
if (raw.progress != null) {
|
|
318
|
+
const norm = raw.progress > 1 ? raw.progress / 100 : raw.progress;
|
|
319
|
+
event.fraction = Math.max(0, Math.min(1, norm));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
this._onProgress(event);
|
|
323
|
+
}
|
|
269
324
|
async detect(text, opts) {
|
|
270
325
|
if (opts?.model && opts.model !== this.model) {
|
|
271
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
326
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
272
327
|
}
|
|
273
328
|
if (!text)
|
|
274
329
|
return [];
|
|
@@ -277,7 +332,7 @@ class LocalDetector {
|
|
|
277
332
|
}
|
|
278
333
|
async detectBatch(texts, opts) {
|
|
279
334
|
if (opts?.model && opts.model !== this.model) {
|
|
280
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
335
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
281
336
|
}
|
|
282
337
|
if (texts.length === 0)
|
|
283
338
|
return [];
|
|
@@ -438,102 +493,17 @@ function chunkByChars(text, chunkChars, overlapChars) {
|
|
|
438
493
|
function normalizeWord(word) {
|
|
439
494
|
return word.replace(/##/g, "").trim();
|
|
440
495
|
}
|
|
441
|
-
|
|
442
|
-
function defaultDetector() {
|
|
443
|
-
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
444
|
-
return hasKey ? new RemoteDetector : new LocalDetector;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// src/normalizers.ts
|
|
448
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
449
|
-
var _require = null;
|
|
450
|
-
var _requireResolved = false;
|
|
451
|
-
function _getRequire() {
|
|
452
|
-
if (_requireResolved)
|
|
453
|
-
return _require;
|
|
454
|
-
_requireResolved = true;
|
|
455
|
-
try {
|
|
456
|
-
_require = createRequire2(import.meta.url);
|
|
457
|
-
} catch {
|
|
458
|
-
_require = null;
|
|
459
|
-
}
|
|
460
|
-
return _require;
|
|
461
|
-
}
|
|
462
|
-
function loadSync(name, errMsg) {
|
|
463
|
-
const req = _getRequire();
|
|
464
|
-
if (!req)
|
|
465
|
-
throw new Error(errMsg);
|
|
496
|
+
async function loadTransformersJs() {
|
|
466
497
|
try {
|
|
467
|
-
return
|
|
498
|
+
return await import("@huggingface/transformers");
|
|
468
499
|
} catch {
|
|
469
|
-
throw new Error(
|
|
500
|
+
throw new Error("LocalDetector requires `@huggingface/transformers`. Install with: bun add @huggingface/transformers");
|
|
470
501
|
}
|
|
471
502
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const lib = loadPhoneLib();
|
|
477
|
-
return (s) => {
|
|
478
|
-
try {
|
|
479
|
-
const parsed = lib.parsePhoneNumberFromString(s, opts.region);
|
|
480
|
-
if (!parsed || !parsed.isValid())
|
|
481
|
-
return null;
|
|
482
|
-
return parsed.format("E.164");
|
|
483
|
-
} catch {
|
|
484
|
-
return null;
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
function loadChrono(opts) {
|
|
489
|
-
const mod = loadSync("chrono-node", "the isoDate normalizer requires chrono-node. " + "Install with: bun add chrono-node");
|
|
490
|
-
if (opts.locale) {
|
|
491
|
-
const localeParser = mod[opts.locale];
|
|
492
|
-
if (localeParser)
|
|
493
|
-
return localeParser;
|
|
494
|
-
}
|
|
495
|
-
return mod;
|
|
496
|
-
}
|
|
497
|
-
function isoDate(opts = {}) {
|
|
498
|
-
const parser = loadChrono(opts);
|
|
499
|
-
return (s) => {
|
|
500
|
-
const d = parser.parseDate(s);
|
|
501
|
-
if (!d)
|
|
502
|
-
return null;
|
|
503
|
-
return d.toISOString().slice(0, 10);
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
function germanTransliteration() {
|
|
507
|
-
const table = {
|
|
508
|
-
ä: "ae",
|
|
509
|
-
Ä: "ae",
|
|
510
|
-
ö: "oe",
|
|
511
|
-
Ö: "oe",
|
|
512
|
-
ü: "ue",
|
|
513
|
-
Ü: "ue",
|
|
514
|
-
ß: "ss"
|
|
515
|
-
};
|
|
516
|
-
return (s) => {
|
|
517
|
-
let out = "";
|
|
518
|
-
for (const ch of s.normalize("NFKC")) {
|
|
519
|
-
out += table[ch] ?? ch;
|
|
520
|
-
}
|
|
521
|
-
return out.toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
var BUILTINS = {
|
|
525
|
-
e164: () => e164(),
|
|
526
|
-
iso_date: () => isoDate(),
|
|
527
|
-
german: () => germanTransliteration()
|
|
528
|
-
};
|
|
529
|
-
function resolveNormalizer(spec) {
|
|
530
|
-
if (typeof spec === "function")
|
|
531
|
-
return spec;
|
|
532
|
-
const factory = BUILTINS[spec];
|
|
533
|
-
if (!factory) {
|
|
534
|
-
throw new Error(`unknown built-in normalizer: ${JSON.stringify(spec)}. ` + `Available: ${Object.keys(BUILTINS).join(", ")}, or pass a callable.`);
|
|
535
|
-
}
|
|
536
|
-
return factory();
|
|
503
|
+
// src/detectors/index.ts
|
|
504
|
+
function defaultDetector() {
|
|
505
|
+
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
506
|
+
return hasKey ? new RemoteDetector : new LocalDetector;
|
|
537
507
|
}
|
|
538
508
|
|
|
539
509
|
// src/core/session.ts
|
|
@@ -548,8 +518,8 @@ class Session {
|
|
|
548
518
|
resolvedNormalizers = {};
|
|
549
519
|
constructor(opts = {}) {
|
|
550
520
|
if (opts.normalizers) {
|
|
551
|
-
for (const [label,
|
|
552
|
-
this.resolvedNormalizers[label] =
|
|
521
|
+
for (const [label, fn] of Object.entries(opts.normalizers)) {
|
|
522
|
+
this.resolvedNormalizers[label] = fn;
|
|
553
523
|
}
|
|
554
524
|
}
|
|
555
525
|
}
|
|
@@ -573,8 +543,11 @@ class Session {
|
|
|
573
543
|
return sentinel;
|
|
574
544
|
}
|
|
575
545
|
applySpans(text, spans) {
|
|
546
|
+
return this.applySpansDetailed(text, spans).redacted;
|
|
547
|
+
}
|
|
548
|
+
applySpansDetailed(text, spans) {
|
|
576
549
|
if (spans.length === 0)
|
|
577
|
-
return text;
|
|
550
|
+
return { redacted: text, merged: [] };
|
|
578
551
|
const sorted = [...spans].sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
|
|
579
552
|
const merged = mergeAdjacent(text, sorted);
|
|
580
553
|
const out = [];
|
|
@@ -590,7 +563,7 @@ class Session {
|
|
|
590
563
|
cursor = end;
|
|
591
564
|
}
|
|
592
565
|
out.push(text.slice(cursor));
|
|
593
|
-
return out.join("");
|
|
566
|
+
return { redacted: out.join(""), merged };
|
|
594
567
|
}
|
|
595
568
|
restore(text) {
|
|
596
569
|
if (Object.keys(this.mapping).length === 0)
|
|
@@ -823,6 +796,16 @@ function restoreChatResponse(response, session) {
|
|
|
823
796
|
if (msg && typeof msg.content === "string") {
|
|
824
797
|
msg.content = session.restore(msg.content);
|
|
825
798
|
}
|
|
799
|
+
if (msg && typeof msg.reasoning === "string") {
|
|
800
|
+
msg.reasoning = session.restore(msg.reasoning);
|
|
801
|
+
}
|
|
802
|
+
if (msg && Array.isArray(msg.reasoning_details)) {
|
|
803
|
+
for (const r of msg.reasoning_details) {
|
|
804
|
+
if (r && typeof r.text === "string") {
|
|
805
|
+
r.text = session.restore(r.text);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
826
809
|
for (const tc of msg?.tool_calls ?? []) {
|
|
827
810
|
const fn = tc.function;
|
|
828
811
|
if (fn && typeof fn.arguments === "string") {
|
|
@@ -837,6 +820,16 @@ function patchChatChunk(chunk, buf) {
|
|
|
837
820
|
if (typeof delta.content === "string") {
|
|
838
821
|
delta.content = buf.feed(delta.content);
|
|
839
822
|
}
|
|
823
|
+
if (typeof delta.reasoning === "string") {
|
|
824
|
+
delta.reasoning = buf.feed(delta.reasoning);
|
|
825
|
+
}
|
|
826
|
+
if (Array.isArray(delta.reasoning_details)) {
|
|
827
|
+
for (const r of delta.reasoning_details) {
|
|
828
|
+
if (r && typeof r.text === "string") {
|
|
829
|
+
r.text = buf.feed(r.text);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
840
833
|
}
|
|
841
834
|
}
|
|
842
835
|
function makeChatTailChunk(template, content) {
|
|
@@ -1163,9 +1156,20 @@ class OpenAI {
|
|
|
1163
1156
|
gheimStrict;
|
|
1164
1157
|
_clientPromise;
|
|
1165
1158
|
constructor(opts = {}) {
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
|
|
1159
|
+
const {
|
|
1160
|
+
gheimDetector,
|
|
1161
|
+
gheimStrict,
|
|
1162
|
+
openaiClient,
|
|
1163
|
+
clientOptions,
|
|
1164
|
+
...passthrough
|
|
1165
|
+
} = opts;
|
|
1166
|
+
const detector = gheimDetector ?? defaultDetector();
|
|
1167
|
+
this.gheimStrict = gheimStrict ?? true;
|
|
1168
|
+
const innerOpts = {
|
|
1169
|
+
...clientOptions ?? {},
|
|
1170
|
+
...passthrough
|
|
1171
|
+
};
|
|
1172
|
+
this._clientPromise = openaiClient ? Promise.resolve(openaiClient) : loadOpenAI(innerOpts);
|
|
1169
1173
|
this._clientPromise.then((c) => {
|
|
1170
1174
|
this.raw = c;
|
|
1171
1175
|
});
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
/** Session state — mirrors gheim-py's Session. */
|
|
2
2
|
import type { Span } from "../detectors/base.ts";
|
|
3
|
-
|
|
3
|
+
/** A surface canonicaliser. Maps a raw surface to a canonical key, or
|
|
4
|
+
* null to fall back to the default NFKC + lowercase key. Re-declared
|
|
5
|
+
* locally so this module doesn't pull in `gheim/normalizers` (which
|
|
6
|
+
* uses Node's `createRequire` and breaks browser bundlers). */
|
|
7
|
+
export type Normalizer = (s: string) => string | null;
|
|
4
8
|
/** Constructor options for Session. */
|
|
5
9
|
export interface SessionOptions {
|
|
6
10
|
/**
|
|
7
11
|
* Optional per-label canonical-form normalizers. Maps a label name
|
|
8
|
-
* (e.g. "private_phone") to
|
|
9
|
-
*
|
|
10
|
-
*
|
|
12
|
+
* (e.g. "private_phone") to a callable Normalizer. Surfaces that fail
|
|
13
|
+
* to parse fall back to the default NFKC+lowercase key.
|
|
14
|
+
*
|
|
15
|
+
* Built-in factories (e164, isoDate, germanTransliteration) live in
|
|
16
|
+
* the `gheim/normalizers` subpath — they pull in optional peer deps
|
|
17
|
+
* (`libphonenumber-js`, `chrono-node`) so they're not loaded by
|
|
18
|
+
* default to keep the browser bundle small and avoid Node-only
|
|
19
|
+
* imports leaking into the main entry.
|
|
11
20
|
*/
|
|
12
|
-
normalizers?: Record<string,
|
|
21
|
+
normalizers?: Record<string, Normalizer>;
|
|
13
22
|
}
|
|
14
23
|
export declare class Session {
|
|
15
24
|
/** Sentinel string ("<PERSON_1>") → original surface form. */
|
|
@@ -19,7 +28,19 @@ export declare class Session {
|
|
|
19
28
|
private resolvedNormalizers;
|
|
20
29
|
constructor(opts?: SessionOptions);
|
|
21
30
|
allocate(label: string, surface: string): string;
|
|
22
|
-
applySpans(text: string, spans: Span[]): string;
|
|
31
|
+
applySpans(text: string, spans: readonly (Span | MergedSpan)[]): string;
|
|
32
|
+
/**
|
|
33
|
+
* Like {@link applySpans} but also returns the post-merge span list. UI
|
|
34
|
+
* consumers (segment rendering, bar overlays) typically need both the
|
|
35
|
+
* redacted text and the merged spans so they can highlight the redacted
|
|
36
|
+
* runs at their original character positions; without this method they'd
|
|
37
|
+
* have to call {@link mergeAdjacent} again on the same input. Pure
|
|
38
|
+
* convenience — no extra cost over `applySpans`.
|
|
39
|
+
*/
|
|
40
|
+
applySpansDetailed(text: string, spans: readonly (Span | MergedSpan)[]): {
|
|
41
|
+
redacted: string;
|
|
42
|
+
merged: MergedSpan[];
|
|
43
|
+
};
|
|
23
44
|
restore(text: string): string;
|
|
24
45
|
toJSON(): {
|
|
25
46
|
mapping: Record<string, string>;
|
|
@@ -28,4 +49,19 @@ export declare class Session {
|
|
|
28
49
|
mapping: Record<string, string>;
|
|
29
50
|
}, opts?: SessionOptions): Session;
|
|
30
51
|
}
|
|
52
|
+
/** A span after merge: same shape as input, but no `text`/`score` carried. */
|
|
53
|
+
export interface MergedSpan {
|
|
54
|
+
start: number;
|
|
55
|
+
end: number;
|
|
56
|
+
label: string;
|
|
57
|
+
}
|
|
58
|
+
/** Merge runs of adjacent same-label spans (BPE-split surface fix).
|
|
59
|
+
*
|
|
60
|
+
* Two same-label spans separated by ≤ MERGE_GAP characters of pure
|
|
61
|
+
* whitespace (or directly touching) collapse into one. Models tokenize
|
|
62
|
+
* a phone like "+41 44 268 12 34" into many single-space-separated
|
|
63
|
+
* pieces with the same label; merging recovers the original entity so
|
|
64
|
+
* the demo and SDK both see one PHONE span instead of five.
|
|
65
|
+
*/
|
|
66
|
+
export declare function mergeAdjacent(text: string, spans: readonly (Span | MergedSpan)[]): MergedSpan[];
|
|
31
67
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD;;;gEAGgE;AAChE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAkBtD,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC1C;AAED,qBAAa,OAAO;IAClB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IACrC,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,mBAAmB,CAAkC;gBAEjD,IAAI,GAAE,cAAmB;IAQrC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAiBhD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GAAG,MAAM;IAIvE;;;;;;;OAOG;IACH,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GACpC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;IAoB7C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAK7B,MAAM,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;IAI7C,MAAM,CAAC,QAAQ,CACb,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EACxC,IAAI,GAAE,cAAmB,GACxB,OAAO;CAqBX;AAID,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GACpC,UAAU,EAAE,CAqBd"}
|