gheim 0.1.1 → 0.1.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/README.md +40 -19
- package/dist/cjs/index.cjs +289 -111
- package/dist/cjs/openai/index.cjs +313 -106
- package/dist/esm/index.js +296 -113
- package/dist/esm/openai/index.js +318 -106
- package/dist/types/core/session.d.ts +42 -6
- package/dist/types/core/session.d.ts.map +1 -1
- package/dist/types/detectors/composite.d.ts +58 -0
- package/dist/types/detectors/composite.d.ts.map +1 -0
- package/dist/types/detectors/index.d.ts +3 -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 +6 -3
- 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
|
@@ -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,225 @@ 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;
|
|
539
|
+
async function loadTransformersJs() {
|
|
503
540
|
try {
|
|
504
|
-
|
|
541
|
+
return await import("@huggingface/transformers");
|
|
505
542
|
} catch {
|
|
506
|
-
|
|
543
|
+
throw new Error("LocalDetector requires `@huggingface/transformers`. Install with: bun add @huggingface/transformers");
|
|
507
544
|
}
|
|
508
|
-
return _require;
|
|
509
545
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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;
|
|
546
|
+
// src/detectors/composite.ts
|
|
547
|
+
function luhnOk(s) {
|
|
548
|
+
const digits = [];
|
|
549
|
+
for (const c of s) {
|
|
550
|
+
if (c >= "0" && c <= "9")
|
|
551
|
+
digits.push(Number(c));
|
|
552
|
+
}
|
|
553
|
+
if (digits.length < 13 || digits.length > 19)
|
|
554
|
+
return false;
|
|
555
|
+
let total = 0;
|
|
556
|
+
for (let i = 0;i < digits.length; i++) {
|
|
557
|
+
let d = digits[digits.length - 1 - i] ?? 0;
|
|
558
|
+
if (i % 2 === 1) {
|
|
559
|
+
d *= 2;
|
|
560
|
+
if (d > 9)
|
|
561
|
+
d -= 9;
|
|
533
562
|
}
|
|
534
|
-
|
|
563
|
+
total += d;
|
|
564
|
+
}
|
|
565
|
+
return total % 10 === 0;
|
|
535
566
|
}
|
|
536
|
-
function
|
|
537
|
-
const
|
|
538
|
-
if (
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
return d.toISOString().slice(0, 10);
|
|
552
|
-
};
|
|
567
|
+
function ibanChOk(raw) {
|
|
568
|
+
const s = raw.replace(/[\s-]/g, "");
|
|
569
|
+
if (s.length !== 21 || !s.startsWith("CH") || !/^\d+$/.test(s.slice(2))) {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
const rearranged = s.slice(4) + s.slice(0, 4);
|
|
573
|
+
let numeric = "";
|
|
574
|
+
for (const c of rearranged) {
|
|
575
|
+
numeric += c >= "A" && c <= "Z" ? String(c.charCodeAt(0) - 55) : c;
|
|
576
|
+
}
|
|
577
|
+
let rem = 0;
|
|
578
|
+
for (const ch of numeric) {
|
|
579
|
+
rem = (rem * 10 + Number(ch)) % 97;
|
|
580
|
+
}
|
|
581
|
+
return rem === 1;
|
|
553
582
|
}
|
|
554
|
-
function
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
583
|
+
function ahvOk(s) {
|
|
584
|
+
const digits = [];
|
|
585
|
+
for (const c of s) {
|
|
586
|
+
if (c >= "0" && c <= "9")
|
|
587
|
+
digits.push(Number(c));
|
|
588
|
+
}
|
|
589
|
+
if (digits.length !== 13)
|
|
590
|
+
return false;
|
|
591
|
+
if (digits[0] !== 7 || digits[1] !== 5 || digits[2] !== 6)
|
|
592
|
+
return false;
|
|
593
|
+
let total = 0;
|
|
594
|
+
for (let i = 0;i < 12; i++) {
|
|
595
|
+
total += (digits[i] ?? 0) * (i % 2 === 0 ? 1 : 3);
|
|
596
|
+
}
|
|
597
|
+
const check = (10 - total % 10) % 10;
|
|
598
|
+
return digits[12] === check;
|
|
599
|
+
}
|
|
600
|
+
function vatCheOk(s) {
|
|
601
|
+
const digits = [];
|
|
602
|
+
for (const c of s) {
|
|
603
|
+
if (c >= "0" && c <= "9")
|
|
604
|
+
digits.push(Number(c));
|
|
605
|
+
}
|
|
606
|
+
if (digits.length < 9)
|
|
607
|
+
return false;
|
|
608
|
+
const weights = [5, 4, 3, 2, 7, 6, 5, 4];
|
|
609
|
+
let total = 0;
|
|
610
|
+
for (let i = 0;i < 8; i++) {
|
|
611
|
+
total += (digits[i] ?? 0) * (weights[i] ?? 0);
|
|
612
|
+
}
|
|
613
|
+
let expected = 11 - total % 11;
|
|
614
|
+
if (expected === 11)
|
|
615
|
+
expected = 0;
|
|
616
|
+
else if (expected === 10)
|
|
617
|
+
return false;
|
|
618
|
+
return digits[8] === expected;
|
|
619
|
+
}
|
|
620
|
+
var REGEXES = [
|
|
621
|
+
{
|
|
622
|
+
label: "private_email",
|
|
623
|
+
pattern: /\b[\w._%+\-]+@[\w.\-]+\.[A-Za-z]{2,}\b/g
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
label: "private_url",
|
|
627
|
+
pattern: /https?:\/\/[^\s)>\]]+/g
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
label: "private_phone",
|
|
631
|
+
pattern: /(?:\+41|0041|0)\s?(?:\(0\)\s?)?\d{2}[\s\-/.]?\d{3}[\s\-/.]?\d{2}[\s\-/.]?\d{2}\b/g
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
label: "account_number",
|
|
635
|
+
pattern: /\bCH\d{2}(?:[\s-]?\d){17}\b/g,
|
|
636
|
+
validator: ibanChOk
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
label: "account_number",
|
|
640
|
+
pattern: /\b756\.\d{4}\.\d{4}\.\d{2}\b/g,
|
|
641
|
+
validator: ahvOk
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
label: "account_number",
|
|
645
|
+
pattern: /\b756\d{10}\b/g,
|
|
646
|
+
validator: ahvOk
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
label: "account_number",
|
|
650
|
+
pattern: /\bCHE-\d{3}\.\d{3}\.\d{3}(?:\s+(?:MWST|TVA|IVA))?\b/g,
|
|
651
|
+
validator: vatCheOk
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
label: "account_number",
|
|
655
|
+
pattern: /\b(?:\d[\s-]?){15,16}\d\b/g,
|
|
656
|
+
validator: luhnOk
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
label: "private_date",
|
|
660
|
+
pattern: /\b(?:\d{1,2}[./]\d{1,2}[./]\d{4}|\d{4}[./-]\d{1,2}[./-]\d{1,2})\b/g
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
label: "secret",
|
|
664
|
+
pattern: /\bsk-(?:proj-)?[A-Za-z0-9_\-]{20,}\b/g
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
label: "secret",
|
|
668
|
+
pattern: /\bghp_[A-Za-z0-9]{36}\b/g
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
label: "secret",
|
|
672
|
+
pattern: /\bAKIA[A-Z0-9]{16}\b/g
|
|
673
|
+
}
|
|
674
|
+
];
|
|
675
|
+
var URL_TRAIL_TRIM = '.,;:!?")>]';
|
|
676
|
+
function trimUrl(text, start, end) {
|
|
677
|
+
while (end > start && URL_TRAIL_TRIM.includes(text[end - 1] ?? ""))
|
|
678
|
+
end--;
|
|
679
|
+
return [start, end];
|
|
680
|
+
}
|
|
681
|
+
function findRegexSpans(text) {
|
|
682
|
+
const candidates = [];
|
|
683
|
+
for (const { label, pattern, validator } of REGEXES) {
|
|
684
|
+
pattern.lastIndex = 0;
|
|
685
|
+
let m;
|
|
686
|
+
while ((m = pattern.exec(text)) !== null) {
|
|
687
|
+
let start = m.index;
|
|
688
|
+
let end = m.index + m[0].length;
|
|
689
|
+
if (label === "private_url")
|
|
690
|
+
[start, end] = trimUrl(text, start, end);
|
|
691
|
+
const value = text.slice(start, end);
|
|
692
|
+
if (!value.trim())
|
|
693
|
+
continue;
|
|
694
|
+
if (validator && !validator(value))
|
|
695
|
+
continue;
|
|
696
|
+
candidates.push({
|
|
697
|
+
start,
|
|
698
|
+
end,
|
|
699
|
+
label,
|
|
700
|
+
text: value,
|
|
701
|
+
score: 1
|
|
702
|
+
});
|
|
568
703
|
}
|
|
569
|
-
|
|
570
|
-
|
|
704
|
+
}
|
|
705
|
+
candidates.sort((a, b) => a.start !== b.start ? a.start - b.start : b.end - b.start - (a.end - a.start));
|
|
706
|
+
const out = [];
|
|
707
|
+
let lastEnd = -1;
|
|
708
|
+
for (const sp of candidates) {
|
|
709
|
+
if (sp.start < lastEnd)
|
|
710
|
+
continue;
|
|
711
|
+
out.push(sp);
|
|
712
|
+
lastEnd = sp.end;
|
|
713
|
+
}
|
|
714
|
+
return out;
|
|
571
715
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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.`);
|
|
716
|
+
function maskSpans(text, spans) {
|
|
717
|
+
if (spans.length === 0)
|
|
718
|
+
return text;
|
|
719
|
+
const chars = [...text];
|
|
720
|
+
for (const sp of spans) {
|
|
721
|
+
for (let i = sp.start;i < sp.end && i < chars.length; i++) {
|
|
722
|
+
chars[i] = " ";
|
|
723
|
+
}
|
|
583
724
|
}
|
|
584
|
-
return
|
|
725
|
+
return chars.join("");
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
class CompositeDetector {
|
|
729
|
+
model;
|
|
730
|
+
constructor(opts) {
|
|
731
|
+
if (!opts || !opts.model) {
|
|
732
|
+
throw new Error("CompositeDetector requires { model: Detector } — pass any object with " + "a .detect(text) method (e.g. new LocalDetector({...})).");
|
|
733
|
+
}
|
|
734
|
+
this.model = opts.model;
|
|
735
|
+
}
|
|
736
|
+
async detect(text, opts) {
|
|
737
|
+
if (!text)
|
|
738
|
+
return [];
|
|
739
|
+
const regexSpans = findRegexSpans(text);
|
|
740
|
+
const masked = maskSpans(text, regexSpans);
|
|
741
|
+
const modelSpans = await this.model.detect(masked, opts);
|
|
742
|
+
const out = [...regexSpans];
|
|
743
|
+
const regexRanges = regexSpans.map((s) => [s.start, s.end]);
|
|
744
|
+
for (const sp of modelSpans) {
|
|
745
|
+
const overlaps = regexRanges.some(([rs, re]) => sp.start < re && rs < sp.end);
|
|
746
|
+
if (overlaps)
|
|
747
|
+
continue;
|
|
748
|
+
out.push(sp);
|
|
749
|
+
}
|
|
750
|
+
out.sort((a, b) => a.start !== b.start ? a.start - b.start : b.end - b.start - (a.end - a.start));
|
|
751
|
+
return out;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
// src/detectors/index.ts
|
|
755
|
+
function defaultDetector() {
|
|
756
|
+
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
757
|
+
return hasKey ? new RemoteDetector : new LocalDetector;
|
|
585
758
|
}
|
|
586
759
|
|
|
587
760
|
// src/core/session.ts
|
|
@@ -596,8 +769,8 @@ class Session {
|
|
|
596
769
|
resolvedNormalizers = {};
|
|
597
770
|
constructor(opts = {}) {
|
|
598
771
|
if (opts.normalizers) {
|
|
599
|
-
for (const [label,
|
|
600
|
-
this.resolvedNormalizers[label] =
|
|
772
|
+
for (const [label, fn] of Object.entries(opts.normalizers)) {
|
|
773
|
+
this.resolvedNormalizers[label] = fn;
|
|
601
774
|
}
|
|
602
775
|
}
|
|
603
776
|
}
|
|
@@ -621,8 +794,11 @@ class Session {
|
|
|
621
794
|
return sentinel;
|
|
622
795
|
}
|
|
623
796
|
applySpans(text, spans) {
|
|
797
|
+
return this.applySpansDetailed(text, spans).redacted;
|
|
798
|
+
}
|
|
799
|
+
applySpansDetailed(text, spans) {
|
|
624
800
|
if (spans.length === 0)
|
|
625
|
-
return text;
|
|
801
|
+
return { redacted: text, merged: [] };
|
|
626
802
|
const sorted = [...spans].sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
|
|
627
803
|
const merged = mergeAdjacent(text, sorted);
|
|
628
804
|
const out = [];
|
|
@@ -638,7 +814,7 @@ class Session {
|
|
|
638
814
|
cursor = end;
|
|
639
815
|
}
|
|
640
816
|
out.push(text.slice(cursor));
|
|
641
|
-
return out.join("");
|
|
817
|
+
return { redacted: out.join(""), merged };
|
|
642
818
|
}
|
|
643
819
|
restore(text) {
|
|
644
820
|
if (Object.keys(this.mapping).length === 0)
|
|
@@ -871,6 +1047,16 @@ function restoreChatResponse(response, session) {
|
|
|
871
1047
|
if (msg && typeof msg.content === "string") {
|
|
872
1048
|
msg.content = session.restore(msg.content);
|
|
873
1049
|
}
|
|
1050
|
+
if (msg && typeof msg.reasoning === "string") {
|
|
1051
|
+
msg.reasoning = session.restore(msg.reasoning);
|
|
1052
|
+
}
|
|
1053
|
+
if (msg && Array.isArray(msg.reasoning_details)) {
|
|
1054
|
+
for (const r of msg.reasoning_details) {
|
|
1055
|
+
if (r && typeof r.text === "string") {
|
|
1056
|
+
r.text = session.restore(r.text);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
874
1060
|
for (const tc of msg?.tool_calls ?? []) {
|
|
875
1061
|
const fn = tc.function;
|
|
876
1062
|
if (fn && typeof fn.arguments === "string") {
|
|
@@ -885,6 +1071,16 @@ function patchChatChunk(chunk, buf) {
|
|
|
885
1071
|
if (typeof delta.content === "string") {
|
|
886
1072
|
delta.content = buf.feed(delta.content);
|
|
887
1073
|
}
|
|
1074
|
+
if (typeof delta.reasoning === "string") {
|
|
1075
|
+
delta.reasoning = buf.feed(delta.reasoning);
|
|
1076
|
+
}
|
|
1077
|
+
if (Array.isArray(delta.reasoning_details)) {
|
|
1078
|
+
for (const r of delta.reasoning_details) {
|
|
1079
|
+
if (r && typeof r.text === "string") {
|
|
1080
|
+
r.text = buf.feed(r.text);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
888
1084
|
}
|
|
889
1085
|
}
|
|
890
1086
|
function makeChatTailChunk(template, content) {
|
|
@@ -1211,9 +1407,20 @@ class OpenAI {
|
|
|
1211
1407
|
gheimStrict;
|
|
1212
1408
|
_clientPromise;
|
|
1213
1409
|
constructor(opts = {}) {
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1216
|
-
|
|
1410
|
+
const {
|
|
1411
|
+
gheimDetector,
|
|
1412
|
+
gheimStrict,
|
|
1413
|
+
openaiClient,
|
|
1414
|
+
clientOptions,
|
|
1415
|
+
...passthrough
|
|
1416
|
+
} = opts;
|
|
1417
|
+
const detector = gheimDetector ?? defaultDetector();
|
|
1418
|
+
this.gheimStrict = gheimStrict ?? true;
|
|
1419
|
+
const innerOpts = {
|
|
1420
|
+
...clientOptions ?? {},
|
|
1421
|
+
...passthrough
|
|
1422
|
+
};
|
|
1423
|
+
this._clientPromise = openaiClient ? Promise.resolve(openaiClient) : loadOpenAI(innerOpts);
|
|
1217
1424
|
this._clientPromise.then((c) => {
|
|
1218
1425
|
this.raw = c;
|
|
1219
1426
|
});
|