gheim 0.1.1 → 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 +3 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -41,6 +41,7 @@ var exports_src = {};
|
|
|
41
41
|
__export(exports_src, {
|
|
42
42
|
renderSentinel: () => renderSentinel,
|
|
43
43
|
parseSentinel: () => parseSentinel,
|
|
44
|
+
mergeAdjacent: () => mergeAdjacent,
|
|
44
45
|
labelTag: () => labelTag,
|
|
45
46
|
isPossibleSentinelPrefix: () => isPossibleSentinelPrefix,
|
|
46
47
|
defaultDetector: () => defaultDetector,
|
|
@@ -96,98 +97,6 @@ function isPossibleSentinelPrefix(buffer) {
|
|
|
96
97
|
return PARTIAL_SENTINEL_RE.test(buffer);
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
// src/normalizers.ts
|
|
100
|
-
var import_node_module = require("node:module");
|
|
101
|
-
var _require = null;
|
|
102
|
-
var _requireResolved = false;
|
|
103
|
-
function _getRequire() {
|
|
104
|
-
if (_requireResolved)
|
|
105
|
-
return _require;
|
|
106
|
-
_requireResolved = true;
|
|
107
|
-
try {
|
|
108
|
-
_require = import_node_module.createRequire("file:///home/runner/work/gheim/gheim/packages/gheim-js/src/normalizers.ts");
|
|
109
|
-
} catch {
|
|
110
|
-
_require = null;
|
|
111
|
-
}
|
|
112
|
-
return _require;
|
|
113
|
-
}
|
|
114
|
-
function loadSync(name, errMsg) {
|
|
115
|
-
const req = _getRequire();
|
|
116
|
-
if (!req)
|
|
117
|
-
throw new Error(errMsg);
|
|
118
|
-
try {
|
|
119
|
-
return req(name);
|
|
120
|
-
} catch {
|
|
121
|
-
throw new Error(errMsg);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
function loadPhoneLib() {
|
|
125
|
-
return loadSync("libphonenumber-js", "the e164 normalizer requires libphonenumber-js. " + "Install with: bun add libphonenumber-js");
|
|
126
|
-
}
|
|
127
|
-
function e164(opts = {}) {
|
|
128
|
-
const lib = loadPhoneLib();
|
|
129
|
-
return (s) => {
|
|
130
|
-
try {
|
|
131
|
-
const parsed = lib.parsePhoneNumberFromString(s, opts.region);
|
|
132
|
-
if (!parsed || !parsed.isValid())
|
|
133
|
-
return null;
|
|
134
|
-
return parsed.format("E.164");
|
|
135
|
-
} catch {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
function loadChrono(opts) {
|
|
141
|
-
const mod = loadSync("chrono-node", "the isoDate normalizer requires chrono-node. " + "Install with: bun add chrono-node");
|
|
142
|
-
if (opts.locale) {
|
|
143
|
-
const localeParser = mod[opts.locale];
|
|
144
|
-
if (localeParser)
|
|
145
|
-
return localeParser;
|
|
146
|
-
}
|
|
147
|
-
return mod;
|
|
148
|
-
}
|
|
149
|
-
function isoDate(opts = {}) {
|
|
150
|
-
const parser = loadChrono(opts);
|
|
151
|
-
return (s) => {
|
|
152
|
-
const d = parser.parseDate(s);
|
|
153
|
-
if (!d)
|
|
154
|
-
return null;
|
|
155
|
-
return d.toISOString().slice(0, 10);
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function germanTransliteration() {
|
|
159
|
-
const table = {
|
|
160
|
-
ä: "ae",
|
|
161
|
-
Ä: "ae",
|
|
162
|
-
ö: "oe",
|
|
163
|
-
Ö: "oe",
|
|
164
|
-
ü: "ue",
|
|
165
|
-
Ü: "ue",
|
|
166
|
-
ß: "ss"
|
|
167
|
-
};
|
|
168
|
-
return (s) => {
|
|
169
|
-
let out = "";
|
|
170
|
-
for (const ch of s.normalize("NFKC")) {
|
|
171
|
-
out += table[ch] ?? ch;
|
|
172
|
-
}
|
|
173
|
-
return out.toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
var BUILTINS = {
|
|
177
|
-
e164: () => e164(),
|
|
178
|
-
iso_date: () => isoDate(),
|
|
179
|
-
german: () => germanTransliteration()
|
|
180
|
-
};
|
|
181
|
-
function resolveNormalizer(spec) {
|
|
182
|
-
if (typeof spec === "function")
|
|
183
|
-
return spec;
|
|
184
|
-
const factory = BUILTINS[spec];
|
|
185
|
-
if (!factory) {
|
|
186
|
-
throw new Error(`unknown built-in normalizer: ${JSON.stringify(spec)}. ` + `Available: ${Object.keys(BUILTINS).join(", ")}, or pass a callable.`);
|
|
187
|
-
}
|
|
188
|
-
return factory();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
100
|
// src/core/session.ts
|
|
192
101
|
function normalizeSurface(s) {
|
|
193
102
|
return s.normalize("NFKC").toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
@@ -200,8 +109,8 @@ class Session {
|
|
|
200
109
|
resolvedNormalizers = {};
|
|
201
110
|
constructor(opts = {}) {
|
|
202
111
|
if (opts.normalizers) {
|
|
203
|
-
for (const [label,
|
|
204
|
-
this.resolvedNormalizers[label] =
|
|
112
|
+
for (const [label, fn] of Object.entries(opts.normalizers)) {
|
|
113
|
+
this.resolvedNormalizers[label] = fn;
|
|
205
114
|
}
|
|
206
115
|
}
|
|
207
116
|
}
|
|
@@ -225,8 +134,11 @@ class Session {
|
|
|
225
134
|
return sentinel;
|
|
226
135
|
}
|
|
227
136
|
applySpans(text, spans) {
|
|
137
|
+
return this.applySpansDetailed(text, spans).redacted;
|
|
138
|
+
}
|
|
139
|
+
applySpansDetailed(text, spans) {
|
|
228
140
|
if (spans.length === 0)
|
|
229
|
-
return text;
|
|
141
|
+
return { redacted: text, merged: [] };
|
|
230
142
|
const sorted = [...spans].sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
|
|
231
143
|
const merged = mergeAdjacent(text, sorted);
|
|
232
144
|
const out = [];
|
|
@@ -242,7 +154,7 @@ class Session {
|
|
|
242
154
|
cursor = end;
|
|
243
155
|
}
|
|
244
156
|
out.push(text.slice(cursor));
|
|
245
|
-
return out.join("");
|
|
157
|
+
return { redacted: out.join(""), merged };
|
|
246
158
|
}
|
|
247
159
|
restore(text) {
|
|
248
160
|
if (Object.keys(this.mapping).length === 0)
|
|
@@ -440,8 +352,10 @@ class LocalDetector {
|
|
|
440
352
|
chunkTokens;
|
|
441
353
|
chunkOverlapTokens;
|
|
442
354
|
batchSize;
|
|
355
|
+
actualDevice = null;
|
|
443
356
|
_pipeline = null;
|
|
444
357
|
_loadPromise = null;
|
|
358
|
+
_onProgress;
|
|
445
359
|
constructor(opts = {}) {
|
|
446
360
|
this.model = opts.model ?? DEFAULT_MODEL2;
|
|
447
361
|
this.device = opts.device ?? "auto";
|
|
@@ -450,11 +364,13 @@ class LocalDetector {
|
|
|
450
364
|
this.chunkTokens = opts.chunkTokens ?? DEFAULT_CHUNK_TOKENS;
|
|
451
365
|
this.chunkOverlapTokens = opts.chunkOverlapTokens ?? DEFAULT_OVERLAP_TOKENS;
|
|
452
366
|
this.batchSize = opts.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
367
|
+
this._onProgress = opts.onProgress;
|
|
453
368
|
if (this.chunkOverlapTokens >= this.chunkTokens) {
|
|
454
369
|
throw new Error(`chunkOverlapTokens (${this.chunkOverlapTokens}) must be smaller than chunkTokens (${this.chunkTokens})`);
|
|
455
370
|
}
|
|
456
371
|
if (opts.pipelineInstance) {
|
|
457
372
|
this._pipeline = opts.pipelineInstance;
|
|
373
|
+
this.actualDevice = this.device === "auto" ? null : this.device;
|
|
458
374
|
}
|
|
459
375
|
}
|
|
460
376
|
async load() {
|
|
@@ -463,24 +379,70 @@ class LocalDetector {
|
|
|
463
379
|
if (this._loadPromise)
|
|
464
380
|
return this._loadPromise;
|
|
465
381
|
this._loadPromise = (async () => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
382
|
+
const mod = await loadTransformersJs();
|
|
383
|
+
const sequence = await this._planDeviceSequence();
|
|
384
|
+
let lastErr;
|
|
385
|
+
for (let i = 0;i < sequence.length; i++) {
|
|
386
|
+
const dev = sequence[i] ?? "wasm";
|
|
387
|
+
try {
|
|
388
|
+
const pipe = await mod.pipeline("token-classification", this.model, {
|
|
389
|
+
device: dev,
|
|
390
|
+
dtype: this.dtype,
|
|
391
|
+
progress_callback: this._onProgress ? (raw) => this._emitProgress(raw, dev) : undefined
|
|
392
|
+
});
|
|
393
|
+
this._pipeline = pipe;
|
|
394
|
+
this.actualDevice = dev;
|
|
395
|
+
return pipe;
|
|
396
|
+
} catch (e) {
|
|
397
|
+
lastErr = e;
|
|
398
|
+
if (i < sequence.length - 1)
|
|
399
|
+
continue;
|
|
400
|
+
throw e;
|
|
401
|
+
}
|
|
471
402
|
}
|
|
472
|
-
|
|
473
|
-
device: this.device,
|
|
474
|
-
dtype: this.dtype
|
|
475
|
-
});
|
|
476
|
-
this._pipeline = pipe;
|
|
477
|
-
return pipe;
|
|
403
|
+
throw lastErr ?? new Error("LocalDetector.load(): no candidate backends");
|
|
478
404
|
})();
|
|
479
405
|
return this._loadPromise;
|
|
480
406
|
}
|
|
407
|
+
async _planDeviceSequence() {
|
|
408
|
+
if (this.device !== "auto")
|
|
409
|
+
return [this.device];
|
|
410
|
+
const seq = [];
|
|
411
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
412
|
+
if (nav?.gpu?.requestAdapter) {
|
|
413
|
+
try {
|
|
414
|
+
const adapter = await nav.gpu.requestAdapter();
|
|
415
|
+
if (adapter)
|
|
416
|
+
seq.push("webgpu");
|
|
417
|
+
} catch {}
|
|
418
|
+
}
|
|
419
|
+
seq.push("wasm");
|
|
420
|
+
return seq;
|
|
421
|
+
}
|
|
422
|
+
_emitProgress(raw, device) {
|
|
423
|
+
if (!this._onProgress)
|
|
424
|
+
return;
|
|
425
|
+
const event = {
|
|
426
|
+
phase: raw.status ?? "progress",
|
|
427
|
+
device
|
|
428
|
+
};
|
|
429
|
+
if (raw.file)
|
|
430
|
+
event.file = raw.file;
|
|
431
|
+
if (raw.status === "progress") {
|
|
432
|
+
if (raw.loaded != null)
|
|
433
|
+
event.loadedBytes = raw.loaded;
|
|
434
|
+
if (raw.total != null)
|
|
435
|
+
event.totalBytes = raw.total;
|
|
436
|
+
if (raw.progress != null) {
|
|
437
|
+
const norm = raw.progress > 1 ? raw.progress / 100 : raw.progress;
|
|
438
|
+
event.fraction = Math.max(0, Math.min(1, norm));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
this._onProgress(event);
|
|
442
|
+
}
|
|
481
443
|
async detect(text, opts) {
|
|
482
444
|
if (opts?.model && opts.model !== this.model) {
|
|
483
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
445
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
484
446
|
}
|
|
485
447
|
if (!text)
|
|
486
448
|
return [];
|
|
@@ -489,7 +451,7 @@ class LocalDetector {
|
|
|
489
451
|
}
|
|
490
452
|
async detectBatch(texts, opts) {
|
|
491
453
|
if (opts?.model && opts.model !== this.model) {
|
|
492
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
454
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
493
455
|
}
|
|
494
456
|
if (texts.length === 0)
|
|
495
457
|
return [];
|
|
@@ -650,6 +612,13 @@ function chunkByChars(text, chunkChars, overlapChars) {
|
|
|
650
612
|
function normalizeWord(word) {
|
|
651
613
|
return word.replace(/##/g, "").trim();
|
|
652
614
|
}
|
|
615
|
+
async function loadTransformersJs() {
|
|
616
|
+
try {
|
|
617
|
+
return await import("@huggingface/transformers");
|
|
618
|
+
} catch {
|
|
619
|
+
throw new Error("LocalDetector requires `@huggingface/transformers`. Install with: bun add @huggingface/transformers");
|
|
620
|
+
}
|
|
621
|
+
}
|
|
653
622
|
// src/detectors/index.ts
|
|
654
623
|
function defaultDetector() {
|
|
655
624
|
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
@@ -704,4 +673,4 @@ async function* deanonymizeStream(chunks, session) {
|
|
|
704
673
|
}
|
|
705
674
|
|
|
706
675
|
// src/index.ts
|
|
707
|
-
var VERSION = "0.1.
|
|
676
|
+
var VERSION = "0.1.2";
|
|
@@ -276,8 +276,10 @@ class LocalDetector {
|
|
|
276
276
|
chunkTokens;
|
|
277
277
|
chunkOverlapTokens;
|
|
278
278
|
batchSize;
|
|
279
|
+
actualDevice = null;
|
|
279
280
|
_pipeline = null;
|
|
280
281
|
_loadPromise = null;
|
|
282
|
+
_onProgress;
|
|
281
283
|
constructor(opts = {}) {
|
|
282
284
|
this.model = opts.model ?? DEFAULT_MODEL2;
|
|
283
285
|
this.device = opts.device ?? "auto";
|
|
@@ -286,11 +288,13 @@ class LocalDetector {
|
|
|
286
288
|
this.chunkTokens = opts.chunkTokens ?? DEFAULT_CHUNK_TOKENS;
|
|
287
289
|
this.chunkOverlapTokens = opts.chunkOverlapTokens ?? DEFAULT_OVERLAP_TOKENS;
|
|
288
290
|
this.batchSize = opts.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
291
|
+
this._onProgress = opts.onProgress;
|
|
289
292
|
if (this.chunkOverlapTokens >= this.chunkTokens) {
|
|
290
293
|
throw new Error(`chunkOverlapTokens (${this.chunkOverlapTokens}) must be smaller than chunkTokens (${this.chunkTokens})`);
|
|
291
294
|
}
|
|
292
295
|
if (opts.pipelineInstance) {
|
|
293
296
|
this._pipeline = opts.pipelineInstance;
|
|
297
|
+
this.actualDevice = this.device === "auto" ? null : this.device;
|
|
294
298
|
}
|
|
295
299
|
}
|
|
296
300
|
async load() {
|
|
@@ -299,24 +303,70 @@ class LocalDetector {
|
|
|
299
303
|
if (this._loadPromise)
|
|
300
304
|
return this._loadPromise;
|
|
301
305
|
this._loadPromise = (async () => {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
306
|
+
const mod = await loadTransformersJs();
|
|
307
|
+
const sequence = await this._planDeviceSequence();
|
|
308
|
+
let lastErr;
|
|
309
|
+
for (let i = 0;i < sequence.length; i++) {
|
|
310
|
+
const dev = sequence[i] ?? "wasm";
|
|
311
|
+
try {
|
|
312
|
+
const pipe = await mod.pipeline("token-classification", this.model, {
|
|
313
|
+
device: dev,
|
|
314
|
+
dtype: this.dtype,
|
|
315
|
+
progress_callback: this._onProgress ? (raw) => this._emitProgress(raw, dev) : undefined
|
|
316
|
+
});
|
|
317
|
+
this._pipeline = pipe;
|
|
318
|
+
this.actualDevice = dev;
|
|
319
|
+
return pipe;
|
|
320
|
+
} catch (e) {
|
|
321
|
+
lastErr = e;
|
|
322
|
+
if (i < sequence.length - 1)
|
|
323
|
+
continue;
|
|
324
|
+
throw e;
|
|
325
|
+
}
|
|
307
326
|
}
|
|
308
|
-
|
|
309
|
-
device: this.device,
|
|
310
|
-
dtype: this.dtype
|
|
311
|
-
});
|
|
312
|
-
this._pipeline = pipe;
|
|
313
|
-
return pipe;
|
|
327
|
+
throw lastErr ?? new Error("LocalDetector.load(): no candidate backends");
|
|
314
328
|
})();
|
|
315
329
|
return this._loadPromise;
|
|
316
330
|
}
|
|
331
|
+
async _planDeviceSequence() {
|
|
332
|
+
if (this.device !== "auto")
|
|
333
|
+
return [this.device];
|
|
334
|
+
const seq = [];
|
|
335
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
336
|
+
if (nav?.gpu?.requestAdapter) {
|
|
337
|
+
try {
|
|
338
|
+
const adapter = await nav.gpu.requestAdapter();
|
|
339
|
+
if (adapter)
|
|
340
|
+
seq.push("webgpu");
|
|
341
|
+
} catch {}
|
|
342
|
+
}
|
|
343
|
+
seq.push("wasm");
|
|
344
|
+
return seq;
|
|
345
|
+
}
|
|
346
|
+
_emitProgress(raw, device) {
|
|
347
|
+
if (!this._onProgress)
|
|
348
|
+
return;
|
|
349
|
+
const event = {
|
|
350
|
+
phase: raw.status ?? "progress",
|
|
351
|
+
device
|
|
352
|
+
};
|
|
353
|
+
if (raw.file)
|
|
354
|
+
event.file = raw.file;
|
|
355
|
+
if (raw.status === "progress") {
|
|
356
|
+
if (raw.loaded != null)
|
|
357
|
+
event.loadedBytes = raw.loaded;
|
|
358
|
+
if (raw.total != null)
|
|
359
|
+
event.totalBytes = raw.total;
|
|
360
|
+
if (raw.progress != null) {
|
|
361
|
+
const norm = raw.progress > 1 ? raw.progress / 100 : raw.progress;
|
|
362
|
+
event.fraction = Math.max(0, Math.min(1, norm));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
this._onProgress(event);
|
|
366
|
+
}
|
|
317
367
|
async detect(text, opts) {
|
|
318
368
|
if (opts?.model && opts.model !== this.model) {
|
|
319
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
369
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
320
370
|
}
|
|
321
371
|
if (!text)
|
|
322
372
|
return [];
|
|
@@ -325,7 +375,7 @@ class LocalDetector {
|
|
|
325
375
|
}
|
|
326
376
|
async detectBatch(texts, opts) {
|
|
327
377
|
if (opts?.model && opts.model !== this.model) {
|
|
328
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
378
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
329
379
|
}
|
|
330
380
|
if (texts.length === 0)
|
|
331
381
|
return [];
|
|
@@ -486,102 +536,17 @@ function chunkByChars(text, chunkChars, overlapChars) {
|
|
|
486
536
|
function normalizeWord(word) {
|
|
487
537
|
return word.replace(/##/g, "").trim();
|
|
488
538
|
}
|
|
489
|
-
|
|
490
|
-
function defaultDetector() {
|
|
491
|
-
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
492
|
-
return hasKey ? new RemoteDetector : new LocalDetector;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// src/normalizers.ts
|
|
496
|
-
var import_node_module = require("node:module");
|
|
497
|
-
var _require = null;
|
|
498
|
-
var _requireResolved = false;
|
|
499
|
-
function _getRequire() {
|
|
500
|
-
if (_requireResolved)
|
|
501
|
-
return _require;
|
|
502
|
-
_requireResolved = true;
|
|
503
|
-
try {
|
|
504
|
-
_require = import_node_module.createRequire("file:///home/runner/work/gheim/gheim/packages/gheim-js/src/normalizers.ts");
|
|
505
|
-
} catch {
|
|
506
|
-
_require = null;
|
|
507
|
-
}
|
|
508
|
-
return _require;
|
|
509
|
-
}
|
|
510
|
-
function loadSync(name, errMsg) {
|
|
511
|
-
const req = _getRequire();
|
|
512
|
-
if (!req)
|
|
513
|
-
throw new Error(errMsg);
|
|
539
|
+
async function loadTransformersJs() {
|
|
514
540
|
try {
|
|
515
|
-
return
|
|
541
|
+
return await import("@huggingface/transformers");
|
|
516
542
|
} catch {
|
|
517
|
-
throw new Error(
|
|
543
|
+
throw new Error("LocalDetector requires `@huggingface/transformers`. Install with: bun add @huggingface/transformers");
|
|
518
544
|
}
|
|
519
545
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const lib = loadPhoneLib();
|
|
525
|
-
return (s) => {
|
|
526
|
-
try {
|
|
527
|
-
const parsed = lib.parsePhoneNumberFromString(s, opts.region);
|
|
528
|
-
if (!parsed || !parsed.isValid())
|
|
529
|
-
return null;
|
|
530
|
-
return parsed.format("E.164");
|
|
531
|
-
} catch {
|
|
532
|
-
return null;
|
|
533
|
-
}
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
function loadChrono(opts) {
|
|
537
|
-
const mod = loadSync("chrono-node", "the isoDate normalizer requires chrono-node. " + "Install with: bun add chrono-node");
|
|
538
|
-
if (opts.locale) {
|
|
539
|
-
const localeParser = mod[opts.locale];
|
|
540
|
-
if (localeParser)
|
|
541
|
-
return localeParser;
|
|
542
|
-
}
|
|
543
|
-
return mod;
|
|
544
|
-
}
|
|
545
|
-
function isoDate(opts = {}) {
|
|
546
|
-
const parser = loadChrono(opts);
|
|
547
|
-
return (s) => {
|
|
548
|
-
const d = parser.parseDate(s);
|
|
549
|
-
if (!d)
|
|
550
|
-
return null;
|
|
551
|
-
return d.toISOString().slice(0, 10);
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
function germanTransliteration() {
|
|
555
|
-
const table = {
|
|
556
|
-
ä: "ae",
|
|
557
|
-
Ä: "ae",
|
|
558
|
-
ö: "oe",
|
|
559
|
-
Ö: "oe",
|
|
560
|
-
ü: "ue",
|
|
561
|
-
Ü: "ue",
|
|
562
|
-
ß: "ss"
|
|
563
|
-
};
|
|
564
|
-
return (s) => {
|
|
565
|
-
let out = "";
|
|
566
|
-
for (const ch of s.normalize("NFKC")) {
|
|
567
|
-
out += table[ch] ?? ch;
|
|
568
|
-
}
|
|
569
|
-
return out.toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
var BUILTINS = {
|
|
573
|
-
e164: () => e164(),
|
|
574
|
-
iso_date: () => isoDate(),
|
|
575
|
-
german: () => germanTransliteration()
|
|
576
|
-
};
|
|
577
|
-
function resolveNormalizer(spec) {
|
|
578
|
-
if (typeof spec === "function")
|
|
579
|
-
return spec;
|
|
580
|
-
const factory = BUILTINS[spec];
|
|
581
|
-
if (!factory) {
|
|
582
|
-
throw new Error(`unknown built-in normalizer: ${JSON.stringify(spec)}. ` + `Available: ${Object.keys(BUILTINS).join(", ")}, or pass a callable.`);
|
|
583
|
-
}
|
|
584
|
-
return factory();
|
|
546
|
+
// src/detectors/index.ts
|
|
547
|
+
function defaultDetector() {
|
|
548
|
+
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
549
|
+
return hasKey ? new RemoteDetector : new LocalDetector;
|
|
585
550
|
}
|
|
586
551
|
|
|
587
552
|
// src/core/session.ts
|
|
@@ -596,8 +561,8 @@ class Session {
|
|
|
596
561
|
resolvedNormalizers = {};
|
|
597
562
|
constructor(opts = {}) {
|
|
598
563
|
if (opts.normalizers) {
|
|
599
|
-
for (const [label,
|
|
600
|
-
this.resolvedNormalizers[label] =
|
|
564
|
+
for (const [label, fn] of Object.entries(opts.normalizers)) {
|
|
565
|
+
this.resolvedNormalizers[label] = fn;
|
|
601
566
|
}
|
|
602
567
|
}
|
|
603
568
|
}
|
|
@@ -621,8 +586,11 @@ class Session {
|
|
|
621
586
|
return sentinel;
|
|
622
587
|
}
|
|
623
588
|
applySpans(text, spans) {
|
|
589
|
+
return this.applySpansDetailed(text, spans).redacted;
|
|
590
|
+
}
|
|
591
|
+
applySpansDetailed(text, spans) {
|
|
624
592
|
if (spans.length === 0)
|
|
625
|
-
return text;
|
|
593
|
+
return { redacted: text, merged: [] };
|
|
626
594
|
const sorted = [...spans].sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
|
|
627
595
|
const merged = mergeAdjacent(text, sorted);
|
|
628
596
|
const out = [];
|
|
@@ -638,7 +606,7 @@ class Session {
|
|
|
638
606
|
cursor = end;
|
|
639
607
|
}
|
|
640
608
|
out.push(text.slice(cursor));
|
|
641
|
-
return out.join("");
|
|
609
|
+
return { redacted: out.join(""), merged };
|
|
642
610
|
}
|
|
643
611
|
restore(text) {
|
|
644
612
|
if (Object.keys(this.mapping).length === 0)
|
|
@@ -871,6 +839,16 @@ function restoreChatResponse(response, session) {
|
|
|
871
839
|
if (msg && typeof msg.content === "string") {
|
|
872
840
|
msg.content = session.restore(msg.content);
|
|
873
841
|
}
|
|
842
|
+
if (msg && typeof msg.reasoning === "string") {
|
|
843
|
+
msg.reasoning = session.restore(msg.reasoning);
|
|
844
|
+
}
|
|
845
|
+
if (msg && Array.isArray(msg.reasoning_details)) {
|
|
846
|
+
for (const r of msg.reasoning_details) {
|
|
847
|
+
if (r && typeof r.text === "string") {
|
|
848
|
+
r.text = session.restore(r.text);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
874
852
|
for (const tc of msg?.tool_calls ?? []) {
|
|
875
853
|
const fn = tc.function;
|
|
876
854
|
if (fn && typeof fn.arguments === "string") {
|
|
@@ -885,6 +863,16 @@ function patchChatChunk(chunk, buf) {
|
|
|
885
863
|
if (typeof delta.content === "string") {
|
|
886
864
|
delta.content = buf.feed(delta.content);
|
|
887
865
|
}
|
|
866
|
+
if (typeof delta.reasoning === "string") {
|
|
867
|
+
delta.reasoning = buf.feed(delta.reasoning);
|
|
868
|
+
}
|
|
869
|
+
if (Array.isArray(delta.reasoning_details)) {
|
|
870
|
+
for (const r of delta.reasoning_details) {
|
|
871
|
+
if (r && typeof r.text === "string") {
|
|
872
|
+
r.text = buf.feed(r.text);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
888
876
|
}
|
|
889
877
|
}
|
|
890
878
|
function makeChatTailChunk(template, content) {
|
|
@@ -1211,9 +1199,20 @@ class OpenAI {
|
|
|
1211
1199
|
gheimStrict;
|
|
1212
1200
|
_clientPromise;
|
|
1213
1201
|
constructor(opts = {}) {
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1216
|
-
|
|
1202
|
+
const {
|
|
1203
|
+
gheimDetector,
|
|
1204
|
+
gheimStrict,
|
|
1205
|
+
openaiClient,
|
|
1206
|
+
clientOptions,
|
|
1207
|
+
...passthrough
|
|
1208
|
+
} = opts;
|
|
1209
|
+
const detector = gheimDetector ?? defaultDetector();
|
|
1210
|
+
this.gheimStrict = gheimStrict ?? true;
|
|
1211
|
+
const innerOpts = {
|
|
1212
|
+
...clientOptions ?? {},
|
|
1213
|
+
...passthrough
|
|
1214
|
+
};
|
|
1215
|
+
this._clientPromise = openaiClient ? Promise.resolve(openaiClient) : loadOpenAI(innerOpts);
|
|
1217
1216
|
this._clientPromise.then((c) => {
|
|
1218
1217
|
this.raw = c;
|
|
1219
1218
|
});
|