@hraness/wordcell 0.26.2 → 0.27.0
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 +36 -21
- package/dist/capture.js +15 -7
- package/dist/cli-bin.js +2 -2
- package/dist/cli.js +21 -19
- package/dist/clip/acquire.js +4 -3
- package/dist/clip/bundle-reader.js +3 -2
- package/dist/clip/cli.js +8 -7
- package/dist/clip/defuddle-worker.js +4 -1
- package/dist/clip/doctor.js +5 -4
- package/dist/clip/jobs.js +2 -1
- package/dist/clip/persist.js +2 -1
- package/dist/clip/refresh.js +4 -3
- package/dist/evaluation-builder-bin.js +2 -2
- package/dist/evaluation-builder.js +3 -3
- package/dist/evaluation-kb.js +3 -3
- package/dist/{index-mp5y3ty3.js → index-30v9w4cy.js} +36 -20
- package/dist/{index-9paxge84.js → index-3hts6yy9.js} +1 -1
- package/dist/{index-h9vyd03z.js → index-58azscg5.js} +7 -6
- package/dist/{index-9zkba8hr.js → index-7bqwzjba.js} +7 -6
- package/dist/{index-j4zgmzjr.js → index-8djdwjjd.js} +1 -1
- package/dist/{index-5h5awwh4.js → index-b40e2dkx.js} +2 -2
- package/dist/index-e2wstc7t.js +323 -0
- package/dist/{index-hacpnaew.js → index-fd8j527z.js} +1 -1
- package/dist/{index-mxxxytys.js → index-g63d3937.js} +4 -0
- package/dist/{index-k86wepd8.js → index-h2w7syy2.js} +5 -1
- package/dist/{index-9fywek7g.js → index-ht14xne3.js} +1 -1
- package/dist/{index-82r6g4py.js → index-k9yzc4vn.js} +1 -1
- package/dist/{index-m9p2a4mj.js → index-my50xsa3.js} +221 -36
- package/dist/{index-ncwzmsge.js → index-nwdzhj1h.js} +2 -2
- package/dist/{index-9rf81m0p.js → index-pbz3qw1s.js} +35 -20
- package/dist/index-pn14a2m5.js +59 -0
- package/dist/{index-nd8v7r0z.js → index-sp43012q.js} +1 -1
- package/dist/{index-8pabwzqg.js → index-t4hjm1kk.js} +2 -1
- package/dist/{index-npg9z1a4.js → index-xn5q4kne.js} +5 -1
- package/dist/{index-j2jt49gr.js → index-yatn62qq.js} +1 -1
- package/dist/index.js +14 -4
- package/dist/pdf.js +438 -4
- package/dist/portfolio.js +3 -3
- package/dist/rerank-clef.js +16 -0
- package/dist/rerank-typesafe.js +3 -1
- package/dist/sdk.js +2 -2
- package/dist/{support-9qdr4hb8.js → support-s059eytv.js} +20 -18
- package/dist/url-intelligence.js +4 -3
- package/dist/workflows/decision-context.js +3 -3
- package/dist/workflows/index.js +3 -3
- package/package.json +23 -9
- package/skills/wordcell/SKILL.md +1 -1
- package/skills/wordcell/references/query.md +11 -6
- package/src/capture.ts +5 -0
- package/src/cli-help.ts +2 -1
- package/src/cli-program.ts +23 -8
- package/src/clip/browser-profiles.ts +1 -1
- package/src/clip/bundle-reader.ts +3 -0
- package/src/clip/capture.ts +1 -0
- package/src/clip/extract.ts +2 -0
- package/src/clip/lib.ts +3 -0
- package/src/clip/metadata-search-tool/Cargo.lock +6 -6
- package/src/clip/metadata-search.ts +1 -1
- package/src/clip/metadata.ts +232 -0
- package/src/clip/persist.ts +3 -0
- package/src/index.ts +2 -1
- package/src/pdf/bundle-reader.ts +457 -0
- package/src/pdf/capture.ts +6 -2
- package/src/pdf/cli.ts +2 -1
- package/src/pdf/errors.ts +11 -0
- package/src/pdf/extract.ts +16 -13
- package/src/pdf/layout.ts +136 -3
- package/src/pdf/markdown.ts +7 -1
- package/src/pdf/model.ts +9 -0
- package/src/pdf/persist.ts +8 -2
- package/src/pdf/source.ts +5 -4
- package/src/pdf/tools.ts +8 -6
- package/src/pdf.ts +10 -0
- package/src/rerank-clef.ts +52 -0
- package/src/rerank-credentials.ts +11 -2
- package/src/rerank-typesafe.ts +44 -20
- package/src/rerank.ts +1 -1
- package/src/sdk.ts +4 -2
- /package/dist/{index-cd75vky9.js → index-jbtyp6q5.js} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
countWords,
|
|
4
4
|
sniffImage
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-h2w7syy2.js";
|
|
6
6
|
import {
|
|
7
7
|
renderFailure,
|
|
8
8
|
sentence,
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
stderrStyle,
|
|
11
11
|
terminalOutput,
|
|
12
12
|
yamlString
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-jbtyp6q5.js";
|
|
14
14
|
import {
|
|
15
15
|
redactSensitiveText,
|
|
16
16
|
sanitizeArtifactUrl
|
|
17
|
-
} from "./index-
|
|
17
|
+
} from "./index-g63d3937.js";
|
|
18
18
|
import {
|
|
19
19
|
safeFetch
|
|
20
20
|
} from "./index-e5fbsywq.js";
|
|
@@ -26,6 +26,16 @@ import {
|
|
|
26
26
|
sanitizeTerminalText
|
|
27
27
|
} from "./index-1xxnjn0d.js";
|
|
28
28
|
|
|
29
|
+
// src/pdf/errors.ts
|
|
30
|
+
class PdfCaptureError extends Error {
|
|
31
|
+
code;
|
|
32
|
+
constructor(code, message, options) {
|
|
33
|
+
super(message, options);
|
|
34
|
+
this.code = code;
|
|
35
|
+
this.name = "PdfCaptureError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
// src/pdf/args.ts
|
|
30
40
|
var pdfUsage = `wordcell pdf \u2014 save a local or public remote PDF as an auditable Markdown bundle
|
|
31
41
|
|
|
@@ -521,7 +531,7 @@ function layoutBlocks(pages) {
|
|
|
521
531
|
const repeatedMargins = repeatedMarginLines(pages, linesByPage);
|
|
522
532
|
const output = [];
|
|
523
533
|
for (const page of pages) {
|
|
524
|
-
const lines = (linesByPage.get(page.page) ?? []).filter((line) => !repeatedMargins.has(normalizedMarginText(line.text)));
|
|
534
|
+
const lines = (linesByPage.get(page.page) ?? []).filter((line) => !(line.top <= page.height * 0.06 || line.top + line.height >= page.height * 0.94) || !repeatedMargins.has(normalizedMarginText(line.text)));
|
|
525
535
|
const steps = lines.slice(1).map((line, index) => Math.max(0, line.top - (lines[index]?.top ?? line.top)));
|
|
526
536
|
const normalStep = median(steps.filter((step) => step > 0), Math.max(1, bodySize * 1.4));
|
|
527
537
|
const events = [
|
|
@@ -548,7 +558,7 @@ function layoutBlocks(pages) {
|
|
|
548
558
|
const { line, index } = event;
|
|
549
559
|
const previous = lines[index - 1];
|
|
550
560
|
const gapBefore = previous === undefined ? Number.POSITIVE_INFINITY : line.top - previous.top;
|
|
551
|
-
const fontHeading = line.fontSize > bodySize * 1.08;
|
|
561
|
+
const fontHeading = line.fontSize > bodySize * 1.08 && !/[=\u222B\u2211\u221A\u2070\u00B9\u00B2\u00B3\u2074\u2075\u2076\u2077\u2078\u2079]/u.test(line.text);
|
|
552
562
|
const boldHeading = line.boldRatio >= 0.78 && line.text.length <= 180 && gapBefore >= normalStep * 1.45;
|
|
553
563
|
if (line.text.length <= 240 && (fontHeading || boldHeading)) {
|
|
554
564
|
flushParagraph();
|
|
@@ -572,7 +582,7 @@ function layoutBlocks(pages) {
|
|
|
572
582
|
}
|
|
573
583
|
flushParagraph();
|
|
574
584
|
}
|
|
575
|
-
return output.filter((block) => block.kind === "image" || block.text.trim() !== "");
|
|
585
|
+
return output.filter((block) => block.kind === "image" || block.kind === "table" || block.text.trim() !== "");
|
|
576
586
|
}
|
|
577
587
|
function parsePdfImageInterpretations(value) {
|
|
578
588
|
if (!Array.isArray(value))
|
|
@@ -673,6 +683,160 @@ function parsePdfImageInterpretations(value) {
|
|
|
673
683
|
}
|
|
674
684
|
return output;
|
|
675
685
|
}
|
|
686
|
+
function analyzePdfLayout(pages) {
|
|
687
|
+
const issues = [];
|
|
688
|
+
const blocks = [];
|
|
689
|
+
const marginLines = new Map(pages.filter((page) => page.text.length <= 4000).map((page) => [page.page, pageLines(page)]));
|
|
690
|
+
const margins = repeatedMarginLines(pages, marginLines);
|
|
691
|
+
for (const originalInput of pages) {
|
|
692
|
+
const original = { ...originalInput, text: originalInput.text.filter((fragment) => !(fragment.top <= originalInput.height * 0.06 || fragment.top + fragment.height >= originalInput.height * 0.94) || !margins.has(normalizedMarginText(fragment.text))) };
|
|
693
|
+
const warn = (code) => {
|
|
694
|
+
if (!issues.some((issue) => issue.page === original.page && issue.code === code))
|
|
695
|
+
issues.push({ page: original.page, code });
|
|
696
|
+
};
|
|
697
|
+
if (original.text.length > 4000) {
|
|
698
|
+
warn("reading-order-uncertain");
|
|
699
|
+
blocks.push(...layoutBlocks([original]));
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (original.text.some((fragment) => /[\u222B\u2211\u221A]/u.test(fragment.text)))
|
|
703
|
+
warn("math-structure-uncertain");
|
|
704
|
+
const text = [...original.text].sort((a, b) => a.top - b.top || a.left - b.left);
|
|
705
|
+
const consumed = new Set;
|
|
706
|
+
const replacements = new Map;
|
|
707
|
+
const superscripts = { "0": "\u2070", "1": "\xB9", "2": "\xB2", "3": "\xB3", "4": "\u2074", "5": "\u2075", "6": "\u2076", "7": "\u2077", "8": "\u2078", "9": "\u2079", "+": "\u207A", "-": "\u207B", "=": "\u207C", "(": "\u207D", ")": "\u207E", n: "\u207F", i: "\u2071" };
|
|
708
|
+
for (const small of text) {
|
|
709
|
+
const bases = text.filter((base2) => base2 !== small && base2.fontSize >= small.fontSize * 1.3 && small.top < base2.top && small.top + small.height > base2.top - base2.height * 0.25 && Math.abs(small.left - (base2.left + base2.width)) < base2.fontSize * 0.6);
|
|
710
|
+
if (bases.length === 0)
|
|
711
|
+
continue;
|
|
712
|
+
const base = bases[0];
|
|
713
|
+
const glyphs = [...small.text.trim()];
|
|
714
|
+
if (bases.length === 1 && glyphs.length > 0 && glyphs.length <= 8 && glyphs.every((glyph) => superscripts[glyph] !== undefined) && !replacements.has(base)) {
|
|
715
|
+
replacements.set(base, { ...base, text: base.text + glyphs.map((glyph) => superscripts[glyph]).join(""), width: small.left + small.width - base.left });
|
|
716
|
+
consumed.add(small);
|
|
717
|
+
} else
|
|
718
|
+
warn("math-structure-uncertain");
|
|
719
|
+
}
|
|
720
|
+
const fragments = text.filter((fragment) => !consumed.has(fragment)).map((fragment) => replacements.get(fragment) ?? fragment);
|
|
721
|
+
const page = { ...original, text: fragments };
|
|
722
|
+
const rows = [];
|
|
723
|
+
for (const fragment of fragments) {
|
|
724
|
+
const row = rows.at(-1);
|
|
725
|
+
if (row === undefined || Math.abs(fragment.top - row[0].top) > Math.max(2, fragment.height * 0.2))
|
|
726
|
+
rows.push([fragment]);
|
|
727
|
+
else
|
|
728
|
+
row.push(fragment);
|
|
729
|
+
}
|
|
730
|
+
const cellRows = rows.map((row) => {
|
|
731
|
+
const groups = [];
|
|
732
|
+
for (const fragment of [...row].sort((a, b) => a.left - b.left)) {
|
|
733
|
+
const prior = groups.at(-1), last = prior?.at(-1);
|
|
734
|
+
if (last === undefined || fragment.left - (last.left + last.width) > Math.max(12, fragment.fontSize * 1.5))
|
|
735
|
+
groups.push([fragment]);
|
|
736
|
+
else
|
|
737
|
+
prior.push(fragment);
|
|
738
|
+
}
|
|
739
|
+
return groups;
|
|
740
|
+
});
|
|
741
|
+
const tables = [];
|
|
742
|
+
for (let index = 0;index < cellRows.length; index++) {
|
|
743
|
+
const first = cellRows[index];
|
|
744
|
+
if (first.length < 2 || first.length > 12)
|
|
745
|
+
continue;
|
|
746
|
+
const group = [first];
|
|
747
|
+
let next = index + 1;
|
|
748
|
+
for (;next < cellRows.length; next++) {
|
|
749
|
+
const candidate = cellRows[next];
|
|
750
|
+
if (candidate.length !== first.length || candidate.some((cell, column) => Math.abs(cell[0].left - first[column][0].left) > 4))
|
|
751
|
+
break;
|
|
752
|
+
if (candidate[0][0].top - group.at(-1)[0][0].top > Math.max(40, candidate[0][0].height * 3))
|
|
753
|
+
break;
|
|
754
|
+
group.push(candidate);
|
|
755
|
+
}
|
|
756
|
+
const cells = group.map((row) => row.map((cell) => joinFragments(cell)));
|
|
757
|
+
const header = first.every((cell) => cell.every((fragment) => fragment.bold));
|
|
758
|
+
const numeric = cells.slice(1).some((row) => row.slice(1).some((cell) => /^[-+\d$\u20AC\u00A3]/u.test(cell.trim())));
|
|
759
|
+
const compact = cells.every((row) => row.every((cell) => cell.length <= 100));
|
|
760
|
+
if (group.length >= 3 && compact && header && numeric) {
|
|
761
|
+
const members = group.flat(2);
|
|
762
|
+
tables.push({ top: Math.min(...members.map((fragment) => fragment.top)), bottom: Math.max(...members.map((fragment) => fragment.top + fragment.height)), block: { kind: "table", page: page.page, rows: cells }, fragments: members });
|
|
763
|
+
index = next - 1;
|
|
764
|
+
} else if (group.length >= 3 && first.length >= 3)
|
|
765
|
+
warn("table-structure-uncertain");
|
|
766
|
+
}
|
|
767
|
+
const tableFragments = new Set(tables.flatMap((table) => table.fragments));
|
|
768
|
+
const remaining = fragments.filter((fragment) => !tableFragments.has(fragment));
|
|
769
|
+
const emitBand = (band, images) => {
|
|
770
|
+
if (band.length === 0 && images.length === 0)
|
|
771
|
+
return;
|
|
772
|
+
const candidates = new Set;
|
|
773
|
+
for (const row of cellRows) {
|
|
774
|
+
for (let i = 1;i < row.length; i++) {
|
|
775
|
+
const left = row[i - 1].at(-1), right = row[i][0];
|
|
776
|
+
const gap = right.left - (left.left + left.width);
|
|
777
|
+
if (gap >= Math.max(page.width * 0.025, left.fontSize * 1.5))
|
|
778
|
+
candidates.add(Math.round((left.left + left.width + right.left) / 2));
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
const valid = [...candidates].filter((gutter) => {
|
|
782
|
+
const left = band.filter((fragment) => fragment.left + fragment.width <= gutter);
|
|
783
|
+
const right = band.filter((fragment) => fragment.left >= gutter);
|
|
784
|
+
if (left.length + right.length !== band.length || left.length < 3 || right.length < 3)
|
|
785
|
+
return false;
|
|
786
|
+
if (left.filter((fragment) => fragment.width > page.width * 0.18).length < 2 || right.filter((fragment) => fragment.width > page.width * 0.18).length < 2)
|
|
787
|
+
return false;
|
|
788
|
+
const overlap = Math.min(Math.max(...left.map((fragment) => fragment.top)), Math.max(...right.map((fragment) => fragment.top))) - Math.max(Math.min(...left.map((fragment) => fragment.top)), Math.min(...right.map((fragment) => fragment.top)));
|
|
789
|
+
return overlap > Math.max(...band.map((fragment) => fragment.height)) * 1.5;
|
|
790
|
+
});
|
|
791
|
+
if (valid.length > 0) {
|
|
792
|
+
const gutter = valid[Math.floor(valid.length / 2)];
|
|
793
|
+
if (images.some((image) => image.left < gutter && image.left + image.width > gutter)) {
|
|
794
|
+
warn("reading-order-uncertain");
|
|
795
|
+
blocks.push(...layoutBlocks([{ ...page, text: band, images }]));
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
for (const left of [true, false])
|
|
799
|
+
blocks.push(...layoutBlocks([{ ...page, text: band.filter((fragment) => left ? fragment.left + fragment.width <= gutter : fragment.left >= gutter), images: images.filter((image) => left ? image.left + image.width <= gutter : image.left >= gutter) }]));
|
|
800
|
+
} else {
|
|
801
|
+
if (band.length >= 6 && cellRows.filter((row) => row.length >= 2 && row.some((cell) => band.includes(cell[0]))).length >= 3)
|
|
802
|
+
warn("reading-order-uncertain");
|
|
803
|
+
blocks.push(...layoutBlocks([{ ...page, text: band, images }]));
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
const bodyFont = weightedBodyFontSize(pageLines(page));
|
|
807
|
+
const gutterVotes = new Map;
|
|
808
|
+
for (const row of cellRows)
|
|
809
|
+
for (let i = 1;i < row.length; i++) {
|
|
810
|
+
const left = row[i - 1].at(-1), right = row[i][0];
|
|
811
|
+
if (right.left - (left.left + left.width) < Math.max(12, left.fontSize * 1.5))
|
|
812
|
+
continue;
|
|
813
|
+
const gutter = Math.round((left.left + left.width + right.left) / 20) * 10;
|
|
814
|
+
gutterVotes.set(gutter, (gutterVotes.get(gutter) ?? 0) + 1);
|
|
815
|
+
}
|
|
816
|
+
const repeatedGutters = [...gutterVotes].filter(([, votes]) => votes >= 3).map(([gutter]) => gutter);
|
|
817
|
+
const spans = remaining.filter((fragment) => fragment.width > page.width * 0.65 || fragment.fontSize > bodyFont * 1.08 && repeatedGutters.some((gutter) => fragment.left < gutter && fragment.left + fragment.width > gutter));
|
|
818
|
+
const boundaries = [...tables.map((table) => ({ top: table.top, bottom: table.bottom, table, span: null })), ...spans.map((span) => ({ top: span.top, bottom: span.top + span.height, table: null, span }))].sort((a, b) => a.top - b.top);
|
|
819
|
+
const emitted = new Set, emittedImages = new Set;
|
|
820
|
+
for (const boundary of boundaries) {
|
|
821
|
+
const band = remaining.filter((fragment) => !emitted.has(fragment) && fragment !== boundary.span && fragment.top < boundary.top);
|
|
822
|
+
const images = page.images.filter((image) => !emittedImages.has(image) && image.top < boundary.top);
|
|
823
|
+
emitBand(band, images);
|
|
824
|
+
for (const fragment of band)
|
|
825
|
+
emitted.add(fragment);
|
|
826
|
+
for (const image of images)
|
|
827
|
+
emittedImages.add(image);
|
|
828
|
+
if (boundary.table !== null)
|
|
829
|
+
blocks.push(boundary.table.block);
|
|
830
|
+
if (boundary.span !== null) {
|
|
831
|
+
const span = boundary.span;
|
|
832
|
+
blocks.push(span.fontSize > bodyFont * 1.08 ? { kind: "heading", page: page.page, level: 2, text: span.text } : { kind: "paragraph", page: page.page, text: span.text });
|
|
833
|
+
emitted.add(span);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
emitBand(remaining.filter((fragment) => !emitted.has(fragment)), page.images.filter((image) => !emittedImages.has(image)));
|
|
837
|
+
}
|
|
838
|
+
return { blocks, issues };
|
|
839
|
+
}
|
|
676
840
|
|
|
677
841
|
// src/pdf/tools.ts
|
|
678
842
|
import { spawn } from "child_process";
|
|
@@ -696,7 +860,7 @@ async function readBoundedStream(stream, maxBytes) {
|
|
|
696
860
|
else
|
|
697
861
|
throw new Error("PDF tool returned an unsupported output chunk");
|
|
698
862
|
if (!bytes.append(chunk))
|
|
699
|
-
throw new
|
|
863
|
+
throw new PdfCaptureError("limit-exceeded", `PDF tool output exceeded ${maxBytes} bytes`);
|
|
700
864
|
}
|
|
701
865
|
return new TextDecoder().decode(bytes.toUint8Array());
|
|
702
866
|
}
|
|
@@ -704,7 +868,9 @@ var runPdfToolCommand = async (specification) => {
|
|
|
704
868
|
const executable = specification.command[0];
|
|
705
869
|
if (executable === undefined)
|
|
706
870
|
throw new Error("PDF tool command is empty");
|
|
707
|
-
|
|
871
|
+
if (specification.processGroup !== undefined && !["new", "inherit"].includes(specification.processGroup))
|
|
872
|
+
throw new PdfCaptureError("invalid-input", "Invalid PDF process group policy");
|
|
873
|
+
const useProcessGroup = process.platform !== "win32" && specification.processGroup !== "inherit";
|
|
708
874
|
const child = spawn(executable, specification.command.slice(1), {
|
|
709
875
|
cwd: specification.cwd,
|
|
710
876
|
detached: useProcessGroup,
|
|
@@ -718,7 +884,7 @@ var runPdfToolCommand = async (specification) => {
|
|
|
718
884
|
windowsHide: true
|
|
719
885
|
});
|
|
720
886
|
const exited = new Promise((resolveExit, rejectExit) => {
|
|
721
|
-
child.once("error", rejectExit);
|
|
887
|
+
child.once("error", (error) => rejectExit(error.code === "ENOENT" ? new PdfCaptureError("tool-unavailable", "PDF tool executable is unavailable", { cause: error }) : error));
|
|
722
888
|
child.once("close", (code) => resolveExit(code ?? 1));
|
|
723
889
|
});
|
|
724
890
|
const signalProcessTree = (signal) => {
|
|
@@ -742,7 +908,7 @@ var runPdfToolCommand = async (specification) => {
|
|
|
742
908
|
forceKillTimer = setTimeout(() => signalProcessTree("SIGKILL"), 1000);
|
|
743
909
|
};
|
|
744
910
|
const timer = setTimeout(() => {
|
|
745
|
-
requestStop(new
|
|
911
|
+
requestStop(new PdfCaptureError("timeout", `PDF tool timed out after ${specification.timeoutMs}ms`));
|
|
746
912
|
}, specification.timeoutMs);
|
|
747
913
|
try {
|
|
748
914
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -791,10 +957,10 @@ function resolvePdfTools(dependencies = {}) {
|
|
|
791
957
|
const pdftohtml = dependencies.tools?.pdftohtml ?? discoverExecutable("pdftohtml", dependencies);
|
|
792
958
|
const tesseract = dependencies.tools?.tesseract === undefined ? discoverExecutable("tesseract", dependencies) : dependencies.tools.tesseract;
|
|
793
959
|
if (pdfinfo === null) {
|
|
794
|
-
throw new
|
|
960
|
+
throw new PdfCaptureError("tool-unavailable", "pdfinfo is required for PDF ingestion; install the Poppler command-line tools");
|
|
795
961
|
}
|
|
796
962
|
if (pdftohtml === null) {
|
|
797
|
-
throw new
|
|
963
|
+
throw new PdfCaptureError("tool-unavailable", "pdftohtml is required for PDF ingestion; install the Poppler command-line tools");
|
|
798
964
|
}
|
|
799
965
|
return { pdfinfo, pdftohtml, tesseract };
|
|
800
966
|
}
|
|
@@ -828,7 +994,7 @@ var pdfCaptureDefaults = {
|
|
|
828
994
|
function positiveBound(value, fallback, maximum, label) {
|
|
829
995
|
const selected = value ?? fallback;
|
|
830
996
|
if (!Number.isSafeInteger(selected) || selected < 1 || selected > maximum) {
|
|
831
|
-
throw new
|
|
997
|
+
throw new PdfCaptureError("invalid-input", `${label} must be an integer from 1 through ${maximum}`);
|
|
832
998
|
}
|
|
833
999
|
return selected;
|
|
834
1000
|
}
|
|
@@ -841,10 +1007,10 @@ function prepareWorkspace(path) {
|
|
|
841
1007
|
if (existsSync2(absolute)) {
|
|
842
1008
|
const stats = lstatSync(absolute);
|
|
843
1009
|
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
844
|
-
throw new
|
|
1010
|
+
throw new PdfCaptureError("invalid-input", "PDF inspection workspace must be a real directory");
|
|
845
1011
|
}
|
|
846
1012
|
if (readdirSync(absolute).length !== 0) {
|
|
847
|
-
throw new
|
|
1013
|
+
throw new PdfCaptureError("invalid-input", "PDF inspection workspace must be empty");
|
|
848
1014
|
}
|
|
849
1015
|
} else {
|
|
850
1016
|
mkdirSync(absolute, { recursive: false, mode: 448 });
|
|
@@ -857,17 +1023,17 @@ function sourceIdentity(path, maxPdfBytes) {
|
|
|
857
1023
|
const canonical = realpathSync(resolve(path));
|
|
858
1024
|
const stats = statSync(canonical);
|
|
859
1025
|
if (!stats.isFile())
|
|
860
|
-
throw new
|
|
1026
|
+
throw new PdfCaptureError("invalid-input", "PDF input must be a regular file");
|
|
861
1027
|
if (stats.size < 5)
|
|
862
|
-
throw new
|
|
1028
|
+
throw new PdfCaptureError("invalid-input", "PDF input is too small to contain a PDF header");
|
|
863
1029
|
if (stats.size > maxPdfBytes)
|
|
864
|
-
throw new
|
|
1030
|
+
throw new PdfCaptureError("limit-exceeded", `PDF input exceeds the ${maxPdfBytes}-byte limit`);
|
|
865
1031
|
const descriptor = openSync(canonical, "r");
|
|
866
1032
|
try {
|
|
867
1033
|
const signature = Buffer.alloc(5);
|
|
868
1034
|
const count = readSync(descriptor, signature, 0, signature.length, 0);
|
|
869
1035
|
if (count !== signature.length || signature.toString("ascii") !== "%PDF-") {
|
|
870
|
-
throw new
|
|
1036
|
+
throw new PdfCaptureError("invalid-input", "PDF input does not have a valid PDF signature");
|
|
871
1037
|
}
|
|
872
1038
|
} finally {
|
|
873
1039
|
closeSync(descriptor);
|
|
@@ -923,18 +1089,18 @@ function safeGeneratedImage(image, workspace, index, limits) {
|
|
|
923
1089
|
function generatedTreeSize(workspace, maximumFiles, maximumBytes) {
|
|
924
1090
|
const entries = readdirSync(workspace, { withFileTypes: true });
|
|
925
1091
|
if (entries.length > maximumFiles) {
|
|
926
|
-
throw new
|
|
1092
|
+
throw new PdfCaptureError("limit-exceeded", `PDF extraction created more than ${maximumFiles} files`);
|
|
927
1093
|
}
|
|
928
1094
|
let totalBytes = 0;
|
|
929
1095
|
for (const entry of entries) {
|
|
930
1096
|
const path = join2(workspace, entry.name);
|
|
931
1097
|
const stats = lstatSync(path);
|
|
932
1098
|
if (!entry.isFile() || stats.isSymbolicLink()) {
|
|
933
|
-
throw new
|
|
1099
|
+
throw new PdfCaptureError("extraction-failed", "PDF extraction created an unexpected non-file output");
|
|
934
1100
|
}
|
|
935
1101
|
totalBytes += stats.size;
|
|
936
1102
|
if (totalBytes > maximumBytes) {
|
|
937
|
-
throw new
|
|
1103
|
+
throw new PdfCaptureError("limit-exceeded", `PDF extraction exceeded the ${maximumBytes}-byte workspace limit`);
|
|
938
1104
|
}
|
|
939
1105
|
}
|
|
940
1106
|
}
|
|
@@ -957,7 +1123,8 @@ async function inspectPdf(options, dependencies = {}) {
|
|
|
957
1123
|
cwd: workspaceDirectory
|
|
958
1124
|
});
|
|
959
1125
|
if (info.exitCode !== 0) {
|
|
960
|
-
|
|
1126
|
+
const encrypted = info.stderr.split(/\r?\n/u).some((line) => line.trim() === "Command Line Error: Incorrect password");
|
|
1127
|
+
throw new PdfCaptureError(encrypted ? "encrypted" : "extraction-failed", "pdfinfo could not inspect the input PDF");
|
|
961
1128
|
}
|
|
962
1129
|
const metadata = parsePdfInfo(info.stdout);
|
|
963
1130
|
const processedPageLimit = Math.min(metadata.pageCount, maxPages);
|
|
@@ -982,12 +1149,12 @@ async function inspectPdf(options, dependencies = {}) {
|
|
|
982
1149
|
cwd: workspaceDirectory
|
|
983
1150
|
});
|
|
984
1151
|
if (extracted.exitCode !== 0) {
|
|
985
|
-
throw new
|
|
1152
|
+
throw new PdfCaptureError("extraction-failed", "pdftohtml could not extract the input PDF");
|
|
986
1153
|
}
|
|
987
1154
|
generatedTreeSize(workspaceDirectory, 10002, Math.min(Number.MAX_SAFE_INTEGER, 8 * 1024 ** 3 + pdfCaptureDefaults.maxLayoutBytes));
|
|
988
1155
|
const layoutStats = statSync(layoutPath);
|
|
989
1156
|
if (!layoutStats.isFile() || layoutStats.size > pdfCaptureDefaults.maxLayoutBytes) {
|
|
990
|
-
throw new
|
|
1157
|
+
throw new PdfCaptureError("limit-exceeded", `Poppler layout XML exceeds the ${pdfCaptureDefaults.maxLayoutBytes}-byte limit`);
|
|
991
1158
|
}
|
|
992
1159
|
const parsed = parsePopplerXml(readFileSync(layoutPath, "utf8"), {
|
|
993
1160
|
maxPages: processedPageLimit,
|
|
@@ -1214,8 +1381,14 @@ function persistPdfCapture(input) {
|
|
|
1214
1381
|
copyFileSync(image.sourcePath, destination);
|
|
1215
1382
|
chmodSync2(destination, 420);
|
|
1216
1383
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1384
|
+
const markdownBytes = `${redactSensitiveText(input.markdown).trimEnd()}
|
|
1385
|
+
`;
|
|
1386
|
+
const markdownDescriptor = { path: `${slug}.md`, bytes: Buffer.byteLength(markdownBytes), sha256: createHash2("sha256").update(markdownBytes).digest("hex") };
|
|
1387
|
+
if (input.manifest.markdown !== undefined && (input.manifest.markdown.path !== markdownDescriptor.path || input.manifest.markdown.bytes !== markdownDescriptor.bytes || input.manifest.markdown.sha256 !== markdownDescriptor.sha256)) {
|
|
1388
|
+
throw new Error("PDF Markdown does not match its manifest provenance");
|
|
1389
|
+
}
|
|
1390
|
+
const manifest = { ...input.manifest, markdown: markdownDescriptor };
|
|
1391
|
+
writeFileSync(join3(stagingDirectory, `${slug}.md`), markdownBytes, { encoding: "utf8", flag: "wx", mode: 420 });
|
|
1219
1392
|
if (input.manifest.annotations === null) {
|
|
1220
1393
|
if (input.annotationsJson !== undefined) {
|
|
1221
1394
|
throw new Error("PDF annotations were supplied without manifest provenance");
|
|
@@ -1239,7 +1412,7 @@ function persistPdfCapture(input) {
|
|
|
1239
1412
|
}
|
|
1240
1413
|
writeFileSync(join3(stagingDirectory, PDF_CAPTURE_ANNOTATIONS_FILENAME), annotationsJson, { encoding: "utf8", flag: "wx", mode: 420 });
|
|
1241
1414
|
}
|
|
1242
|
-
writeFileSync(join3(stagingDirectory, PDF_CAPTURE_MANIFEST_FILENAME), `${JSON.stringify(
|
|
1415
|
+
writeFileSync(join3(stagingDirectory, PDF_CAPTURE_MANIFEST_FILENAME), `${JSON.stringify(manifest, null, 2)}
|
|
1243
1416
|
`, { encoding: "utf8", flag: "wx", mode: 420 });
|
|
1244
1417
|
assertStagingTreeSafe(stagingDirectory);
|
|
1245
1418
|
if (existsSync3(targetDirectory) !== (expectedIdentity !== null)) {
|
|
@@ -1297,7 +1470,7 @@ function pdfMarkdownFilename(slug) {
|
|
|
1297
1470
|
|
|
1298
1471
|
// src/pdf/markdown.ts
|
|
1299
1472
|
function escapeInline(value) {
|
|
1300
|
-
return sanitizeTerminalText(value).replace(/\\/gu, "\\\\").replace(/([`*_[\]{}
|
|
1473
|
+
return sanitizeTerminalText(value).replace(/\\/gu, "\\\\").replace(/([`*_[\]{}<>#|])/gu, "\\$1").replace(/\s+/gu, " ").trim();
|
|
1301
1474
|
}
|
|
1302
1475
|
function metadataLine(metadata) {
|
|
1303
1476
|
if (metadata === null)
|
|
@@ -1347,6 +1520,13 @@ function markdownContent(blocks, images) {
|
|
|
1347
1520
|
lines.push(imageMarkdown(resolved), "");
|
|
1348
1521
|
continue;
|
|
1349
1522
|
}
|
|
1523
|
+
if (block.kind === "table") {
|
|
1524
|
+
textBlockCount += 1;
|
|
1525
|
+
const rows = block.rows.map((row) => `| ${row.map((cell) => escapeInline(cell)).join(" | ")} |`);
|
|
1526
|
+
if (rows.length > 0)
|
|
1527
|
+
lines.push(rows[0], `| ${block.rows[0].map(() => "---").join(" | ")} |`, ...rows.slice(1), "");
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1350
1530
|
if (block.kind === "heading") {
|
|
1351
1531
|
headingCount += 1;
|
|
1352
1532
|
lines.push(`${"#".repeat(Math.max(2, Math.min(6, block.level)))} ${escapeInline(block.text)}`, "");
|
|
@@ -1662,13 +1842,16 @@ async function runPdfCapture(options, dependencies = {}) {
|
|
|
1662
1842
|
});
|
|
1663
1843
|
}
|
|
1664
1844
|
const platforms = embeddedPlatforms(resolvedImages);
|
|
1845
|
+
const layout = analyzePdfLayout(inspection.pages);
|
|
1846
|
+
for (const issue of layout.issues)
|
|
1847
|
+
warnings.push(`Page ${issue.page}: ${issue.code}; consult the retained source PDF.`);
|
|
1665
1848
|
const status = warnings.length > 0 || unclassifiedImages > 0 || ocrWarningCount > 0 ? "partial" : "complete";
|
|
1666
1849
|
const filenameTitle = inspection.originalFilename.replace(/\.pdf$/iu, "").trim();
|
|
1667
1850
|
const requestedSlug = options.slug ?? inspection.metadata.title ?? filenameTitle;
|
|
1668
1851
|
const slug = slugify(sanitizeTerminalText(requestedSlug));
|
|
1669
1852
|
if (slug === "")
|
|
1670
1853
|
throw new Error("could not derive a safe PDF capture slug; pass an explicit slug");
|
|
1671
|
-
const blocks =
|
|
1854
|
+
const blocks = layout.blocks;
|
|
1672
1855
|
const byId = new Map(resolvedImages.map((image) => [image.image.id, image]));
|
|
1673
1856
|
const capturedAt = (dependencies.now ?? (() => new Date))().toISOString();
|
|
1674
1857
|
const built = buildPdfMarkdown({
|
|
@@ -1701,6 +1884,8 @@ async function runPdfCapture(options, dependencies = {}) {
|
|
|
1701
1884
|
const manifest = {
|
|
1702
1885
|
schemaVersion: PDF_CAPTURE_MANIFEST_SCHEMA_VERSION,
|
|
1703
1886
|
kind: "pdf",
|
|
1887
|
+
quality: { schemaVersion: 1, issues: layout.issues },
|
|
1888
|
+
markdown: { path: pdfMarkdownFilename(slug), bytes: Buffer.byteLength(built.markdown), sha256: createHash3("sha256").update(built.markdown).digest("hex") },
|
|
1704
1889
|
capturedAt,
|
|
1705
1890
|
status,
|
|
1706
1891
|
source: {
|
|
@@ -1785,13 +1970,13 @@ function parseRemoteUrl(input) {
|
|
|
1785
1970
|
try {
|
|
1786
1971
|
url = new URL(input);
|
|
1787
1972
|
} catch {
|
|
1788
|
-
throw new
|
|
1973
|
+
throw new PdfCaptureError("invalid-input", "PDF URL is invalid");
|
|
1789
1974
|
}
|
|
1790
1975
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
1791
|
-
throw new
|
|
1976
|
+
throw new PdfCaptureError("invalid-input", "PDF URL must use HTTP or HTTPS");
|
|
1792
1977
|
}
|
|
1793
1978
|
if (url.username !== "" || url.password !== "") {
|
|
1794
|
-
throw new
|
|
1979
|
+
throw new PdfCaptureError("invalid-input", "PDF URL must not contain embedded credentials");
|
|
1795
1980
|
}
|
|
1796
1981
|
return url;
|
|
1797
1982
|
}
|
|
@@ -1808,7 +1993,7 @@ function remoteFilename(url) {
|
|
|
1808
1993
|
}
|
|
1809
1994
|
function assertPdfSignature(bytes) {
|
|
1810
1995
|
if (bytes.byteLength < 5 || new TextDecoder().decode(bytes.subarray(0, 5)) !== "%PDF-") {
|
|
1811
|
-
throw new
|
|
1996
|
+
throw new PdfCaptureError("invalid-input", "remote PDF input does not have a valid PDF signature");
|
|
1812
1997
|
}
|
|
1813
1998
|
}
|
|
1814
1999
|
async function preparePdfSource(input, options = {}, dependencies = {}) {
|
|
@@ -1961,7 +2146,7 @@ async function main(rawArguments = process.argv.slice(2), environment = process.
|
|
|
1961
2146
|
} catch (error) {
|
|
1962
2147
|
const message = safe(error instanceof Error ? error.message : String(error));
|
|
1963
2148
|
if (arguments_.json)
|
|
1964
|
-
output.stdout(terminalSafeJson({ ok: false, error: message }));
|
|
2149
|
+
output.stdout(terminalSafeJson({ ok: false, error: message, code: error instanceof PdfCaptureError ? error.code : "unknown" }));
|
|
1965
2150
|
else
|
|
1966
2151
|
output.stderr(renderFailure(message, "wordcell doctor", stderrStyle(environment, output)));
|
|
1967
2152
|
return 1;
|
|
@@ -1972,4 +2157,4 @@ async function main(rawArguments = process.argv.slice(2), environment = process.
|
|
|
1972
2157
|
if (false)
|
|
1973
2158
|
;
|
|
1974
2159
|
|
|
1975
|
-
export { pdfUsage, parsePdfArguments, decodePopplerText, parsePopplerXml, parsePdfInfo, layoutBlocks, parsePdfImageInterpretations, runPdfToolCommand, resolvePdfTools, pdfCaptureDefaults, inspectPdf, PDF_CAPTURE_MANIFEST_SCHEMA_VERSION, PDF_CAPTURE_MANIFEST_FILENAME, PDF_CAPTURE_SOURCE_FILENAME, PDF_CAPTURE_ANNOTATIONS_FILENAME, pdfImageAssetPath, persistPdfCapture, pdfMarkdownFilename, buildPdfMarkdown, parseTesseractTsv, ocrPdfImage, runPdfCapture, pdfCaptureSummary, main };
|
|
2160
|
+
export { PdfCaptureError, pdfUsage, parsePdfArguments, decodePopplerText, parsePopplerXml, parsePdfInfo, layoutBlocks, parsePdfImageInterpretations, runPdfToolCommand, resolvePdfTools, pdfCaptureDefaults, inspectPdf, PDF_CAPTURE_MANIFEST_SCHEMA_VERSION, PDF_CAPTURE_MANIFEST_FILENAME, PDF_CAPTURE_SOURCE_FILENAME, PDF_CAPTURE_ANNOTATIONS_FILENAME, pdfImageAssetPath, persistPdfCapture, pdfMarkdownFilename, buildPdfMarkdown, parseTesseractTsv, ocrPdfImage, runPdfCapture, pdfCaptureSummary, main };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
classifyPlatformUrl,
|
|
4
4
|
symbol
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-jbtyp6q5.js";
|
|
6
6
|
import {
|
|
7
7
|
filterCookieProviderResult,
|
|
8
8
|
readCookieFile,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./index-2gv8y733.js";
|
|
11
11
|
import {
|
|
12
12
|
sanitizeArtifactUrl
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-g63d3937.js";
|
|
14
14
|
import {
|
|
15
15
|
startNetworkProxy
|
|
16
16
|
} from "./index-w2zc0vwa.js";
|
|
@@ -105,10 +105,10 @@ function hasExactKeys(value, expected) {
|
|
|
105
105
|
function tokenCount(value) {
|
|
106
106
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= 1e9 ? value : null;
|
|
107
107
|
}
|
|
108
|
-
function acceptedResponseModel(value) {
|
|
109
|
-
return value ===
|
|
108
|
+
function acceptedResponseModel(value, model) {
|
|
109
|
+
return value === model;
|
|
110
110
|
}
|
|
111
|
-
function parseSystemOneResponse(value) {
|
|
111
|
+
function parseSystemOneResponse(value, model) {
|
|
112
112
|
const root = recordValue(value);
|
|
113
113
|
if (root === null || !hasExactKeys(root, ["model", "answers", "usage"]))
|
|
114
114
|
return null;
|
|
@@ -126,7 +126,7 @@ function parseSystemOneResponse(value) {
|
|
|
126
126
|
return null;
|
|
127
127
|
const inputTokens = tokenCount(usage["input_tokens"]);
|
|
128
128
|
const outputTokens = tokenCount(usage["output_tokens"]);
|
|
129
|
-
if (!acceptedResponseModel(root["model"]) || inputTokens === null || outputTokens === null)
|
|
129
|
+
if (!acceptedResponseModel(root["model"], model) || inputTokens === null || outputTokens === null)
|
|
130
130
|
return null;
|
|
131
131
|
return { noul, model: root["model"], inputTokens, outputTokens };
|
|
132
132
|
}
|
|
@@ -144,7 +144,7 @@ async function mapWithConcurrency(items, concurrency, operation) {
|
|
|
144
144
|
return results;
|
|
145
145
|
}
|
|
146
146
|
function validApiKey(value) {
|
|
147
|
-
return value
|
|
147
|
+
return typeof value === "string" && /^[\x21-\x7e]{1,512}$/u.test(value);
|
|
148
148
|
}
|
|
149
149
|
function boundedPositiveInteger(value, fallback, maximum) {
|
|
150
150
|
const resolved = value ?? fallback;
|
|
@@ -164,14 +164,25 @@ function validRerankRequest(value) {
|
|
|
164
164
|
return true;
|
|
165
165
|
}
|
|
166
166
|
function createTypeSafeReranker(options = {}) {
|
|
167
|
+
const environment = options.environment ?? process.env;
|
|
168
|
+
return createHostedReranker(options, {
|
|
169
|
+
id: "typesafe",
|
|
170
|
+
label: "TypeSafe",
|
|
171
|
+
model: DEFAULT_SYSTEMONE_MODEL,
|
|
172
|
+
endpoint: DEFAULT_SYSTEMONE_ENDPOINT,
|
|
173
|
+
apiKey: Object.hasOwn(environment, "TYPESAFE_API_KEY") ? environment["TYPESAFE_API_KEY"] : undefined,
|
|
174
|
+
unavailableMessage: "TYPESAFE_API_KEY is missing or invalid; the TypeSafe rerank lane is unavailable.",
|
|
175
|
+
unwrap: (value) => value
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function createHostedReranker(options, provider) {
|
|
167
179
|
const transport = options.transport ?? fetchSystemOneTransport;
|
|
168
180
|
const timeoutMs = boundedPositiveInteger(options.timeoutMs, DEFAULT_RERANK_TIMEOUT_MS, MAX_RERANK_TIMEOUT_MS);
|
|
169
181
|
const maxResponseBytes = boundedPositiveInteger(options.maxResponseBytes, DEFAULT_RERANK_MAX_RESPONSE_BYTES, MAX_RERANK_RESPONSE_BYTES);
|
|
170
182
|
const concurrency = boundedPositiveInteger(options.concurrency, DEFAULT_RERANK_CONCURRENCY, MAX_RERANK_CONCURRENCY);
|
|
171
|
-
const
|
|
172
|
-
const apiKey = Object.hasOwn(environment, "TYPESAFE_API_KEY") ? environment["TYPESAFE_API_KEY"] : undefined;
|
|
183
|
+
const apiKey = provider.apiKey;
|
|
173
184
|
return {
|
|
174
|
-
id:
|
|
185
|
+
id: provider.id,
|
|
175
186
|
rerank: async (request) => {
|
|
176
187
|
const started = performance.now();
|
|
177
188
|
const controller = new AbortController;
|
|
@@ -209,7 +220,7 @@ function createTypeSafeReranker(options = {}) {
|
|
|
209
220
|
};
|
|
210
221
|
const failed = (message) => ({
|
|
211
222
|
status: "failed",
|
|
212
|
-
message,
|
|
223
|
+
message: message.replaceAll("TypeSafe", provider.label),
|
|
213
224
|
...details()
|
|
214
225
|
});
|
|
215
226
|
try {
|
|
@@ -224,8 +235,8 @@ function createTypeSafeReranker(options = {}) {
|
|
|
224
235
|
if (callerSignal?.aborted === true) {
|
|
225
236
|
return failed("TypeSafe rerank request was aborted.");
|
|
226
237
|
}
|
|
227
|
-
if (!validApiKey(apiKey)) {
|
|
228
|
-
return { status: "unavailable", message:
|
|
238
|
+
if (!validApiKey(apiKey) || provider.endpoint === undefined) {
|
|
239
|
+
return { status: "unavailable", message: provider.unavailableMessage };
|
|
229
240
|
}
|
|
230
241
|
if (candidateCount === 0)
|
|
231
242
|
return { status: "ready", ordering: [], probabilities: {} };
|
|
@@ -242,15 +253,18 @@ function createTypeSafeReranker(options = {}) {
|
|
|
242
253
|
if (encoder.encode(JSON.stringify(state)).byteLength > MAX_RERANK_STATE_BYTES) {
|
|
243
254
|
return failed(`TypeSafe rerank state exceeds the ${MAX_RERANK_STATE_BYTES}-byte limit.`);
|
|
244
255
|
}
|
|
245
|
-
|
|
246
|
-
model:
|
|
256
|
+
const body = encoder.encode(JSON.stringify({
|
|
257
|
+
model: provider.model,
|
|
247
258
|
state,
|
|
248
259
|
questions: { [RELEVANCE_QUESTION]: {
|
|
249
260
|
type: "noul",
|
|
250
261
|
instructions: RELEVANCE_INSTRUCTIONS,
|
|
251
262
|
criteria: RELEVANCE_CRITERIA
|
|
252
263
|
} }
|
|
253
|
-
}))
|
|
264
|
+
}));
|
|
265
|
+
if (body.byteLength > 32 * 1024)
|
|
266
|
+
return failed("TypeSafe rerank body exceeds the 32768-byte limit.");
|
|
267
|
+
bodies.push(body);
|
|
254
268
|
}
|
|
255
269
|
const deadline = started + timeoutMs;
|
|
256
270
|
const timeoutMessage = "TypeSafe rerank window exceeded its deadline.";
|
|
@@ -271,7 +285,7 @@ function createTypeSafeReranker(options = {}) {
|
|
|
271
285
|
let response;
|
|
272
286
|
try {
|
|
273
287
|
response = await transport({
|
|
274
|
-
url:
|
|
288
|
+
url: provider.endpoint,
|
|
275
289
|
body,
|
|
276
290
|
headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
|
|
277
291
|
timeoutMs: Math.max(1, Math.ceil(deadline - performance.now())),
|
|
@@ -302,7 +316,8 @@ function createTypeSafeReranker(options = {}) {
|
|
|
302
316
|
stop(response.status === 200 ? "TypeSafe rerank response was not valid UTF-8 JSON." : `TypeSafe rerank request returned HTTP ${response.status}.`);
|
|
303
317
|
return { ok: false, message: firstFailure };
|
|
304
318
|
}
|
|
305
|
-
const
|
|
319
|
+
const result = provider.unwrap(parsed);
|
|
320
|
+
const root = recordValue(result);
|
|
306
321
|
const usage = recordValue(root?.["usage"]);
|
|
307
322
|
const input = tokenCount(usage?.["input_tokens"]);
|
|
308
323
|
const output = tokenCount(usage?.["output_tokens"]);
|
|
@@ -315,13 +330,13 @@ function createTypeSafeReranker(options = {}) {
|
|
|
315
330
|
outputTokens += output;
|
|
316
331
|
usageReceipts += 1;
|
|
317
332
|
}
|
|
318
|
-
if (acceptedResponseModel(root?.["model"]))
|
|
319
|
-
observedModel =
|
|
333
|
+
if (acceptedResponseModel(root?.["model"], provider.model))
|
|
334
|
+
observedModel = provider.model;
|
|
320
335
|
if (response.status !== 200) {
|
|
321
336
|
stop(`TypeSafe rerank request returned HTTP ${response.status}.`);
|
|
322
337
|
return { ok: false, message: firstFailure };
|
|
323
338
|
}
|
|
324
|
-
const answer = parseSystemOneResponse(
|
|
339
|
+
const answer = parseSystemOneResponse(result, provider.model);
|
|
325
340
|
if (answer === null) {
|
|
326
341
|
stop("TypeSafe rerank response was malformed.");
|
|
327
342
|
return { ok: false, message: firstFailure };
|
|
@@ -365,4 +380,4 @@ function createTypeSafeReranker(options = {}) {
|
|
|
365
380
|
};
|
|
366
381
|
}
|
|
367
382
|
|
|
368
|
-
export { DEFAULT_SYSTEMONE_ENDPOINT, DEFAULT_SYSTEMONE_MODEL, createTypeSafeReranker };
|
|
383
|
+
export { DEFAULT_SYSTEMONE_ENDPOINT, DEFAULT_SYSTEMONE_MODEL, createTypeSafeReranker, createHostedReranker };
|