@claritylabs/cl-sdk 3.1.5 → 3.1.7
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/application.d.mts +1 -1
- package/dist/application.d.ts +1 -1
- package/dist/{index-2WYX6Agm.d.mts → index-C-jIqJru.d.mts} +1 -1
- package/dist/{index-2WYX6Agm.d.ts → index-C-jIqJru.d.ts} +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +358 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +358 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2796,8 +2796,30 @@ function sortSpans(left, right) {
|
|
|
2796
2796
|
const leftCol = left.table?.columnIndex ?? Number(left.metadata?.columnIndex ?? 0);
|
|
2797
2797
|
const rightCol = right.table?.columnIndex ?? Number(right.metadata?.columnIndex ?? 0);
|
|
2798
2798
|
if (leftCol !== rightCol) return leftCol - rightCol;
|
|
2799
|
+
if (tableId(left) && tableId(left) === tableId(right)) {
|
|
2800
|
+
const leftUnitRank = sourceUnitSortRank(left);
|
|
2801
|
+
const rightUnitRank = sourceUnitSortRank(right);
|
|
2802
|
+
if (leftUnitRank !== rightUnitRank) return leftUnitRank - rightUnitRank;
|
|
2803
|
+
}
|
|
2799
2804
|
return left.id.localeCompare(right.id);
|
|
2800
2805
|
}
|
|
2806
|
+
function sourceUnitSortRank(span) {
|
|
2807
|
+
switch (sourceUnit2(span)) {
|
|
2808
|
+
case "page":
|
|
2809
|
+
return 0;
|
|
2810
|
+
case "table":
|
|
2811
|
+
return 1;
|
|
2812
|
+
case "table_row":
|
|
2813
|
+
return 2;
|
|
2814
|
+
case "table_cell":
|
|
2815
|
+
return 3;
|
|
2816
|
+
case "section":
|
|
2817
|
+
case "key_value":
|
|
2818
|
+
case "text":
|
|
2819
|
+
default:
|
|
2820
|
+
return 4;
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2801
2823
|
function normalizeDocumentSourceTreePaths(nodes) {
|
|
2802
2824
|
const byParent = /* @__PURE__ */ new Map();
|
|
2803
2825
|
for (const node of nodes) {
|
|
@@ -10123,6 +10145,23 @@ var SourceTreeOrganizationSchema = z42.object({
|
|
|
10123
10145
|
childNodeIds: z42.array(z42.string()).min(1)
|
|
10124
10146
|
}))
|
|
10125
10147
|
});
|
|
10148
|
+
var SourceTreeVisualTableRepairSchema = z42.object({
|
|
10149
|
+
tables: z42.array(z42.object({
|
|
10150
|
+
tableNodeId: z42.string(),
|
|
10151
|
+
columnLabels: z42.array(z42.object({
|
|
10152
|
+
columnIndex: z42.number().int().nonnegative(),
|
|
10153
|
+
label: z42.string()
|
|
10154
|
+
})).default([]),
|
|
10155
|
+
continuationRows: z42.array(z42.object({
|
|
10156
|
+
sourceRowNodeId: z42.string(),
|
|
10157
|
+
targetRowNodeId: z42.string(),
|
|
10158
|
+
targetColumnIndex: z42.number().int().nonnegative().optional(),
|
|
10159
|
+
targetColumnLabel: z42.string().optional(),
|
|
10160
|
+
reason: z42.string().optional()
|
|
10161
|
+
})).default([])
|
|
10162
|
+
})).default([]),
|
|
10163
|
+
warnings: z42.array(z42.string()).default([])
|
|
10164
|
+
});
|
|
10126
10165
|
var SourceBackedValueForPromptSchema = z42.object({
|
|
10127
10166
|
value: z42.string(),
|
|
10128
10167
|
normalizedValue: z42.string().optional(),
|
|
@@ -11345,6 +11384,316 @@ ${JSON.stringify(nodes, null, 2)}
|
|
|
11345
11384
|
|
|
11346
11385
|
Return JSON for the operational profile.`;
|
|
11347
11386
|
}
|
|
11387
|
+
var VISUAL_TABLE_REPAIR_MAX_TABLES = 4;
|
|
11388
|
+
var VISUAL_TABLE_REPAIR_MAX_ROWS = 28;
|
|
11389
|
+
var VISUAL_TABLE_REPAIR_MAX_CELLS = 140;
|
|
11390
|
+
function isSourceTreeHeaderRow(row) {
|
|
11391
|
+
return row.metadata?.isHeader === true || row.metadata?.isHeader === "true";
|
|
11392
|
+
}
|
|
11393
|
+
function tableCellText(cell) {
|
|
11394
|
+
return cleanText(cell.textExcerpt ?? cell.description ?? cell.title, "");
|
|
11395
|
+
}
|
|
11396
|
+
function tableRowTextForPrompt(row, cells) {
|
|
11397
|
+
return cleanText(
|
|
11398
|
+
cells.length ? cells.map(tableCellText).filter(Boolean).join(" | ") : row.textExcerpt ?? row.description ?? row.title,
|
|
11399
|
+
row.title
|
|
11400
|
+
);
|
|
11401
|
+
}
|
|
11402
|
+
function tableCellColumnIndex(cell, fallbackIndex) {
|
|
11403
|
+
const metadataIndex = cell.metadata?.columnIndex;
|
|
11404
|
+
return typeof metadataIndex === "number" && Number.isInteger(metadataIndex) ? metadataIndex : fallbackIndex;
|
|
11405
|
+
}
|
|
11406
|
+
function isGenericColumnTitle(value) {
|
|
11407
|
+
const title = cleanText(value, "");
|
|
11408
|
+
return !title || /^(?:column\s+\d+|table cell|value)$/i.test(title);
|
|
11409
|
+
}
|
|
11410
|
+
function bboxSummary(node) {
|
|
11411
|
+
const box = node.bbox?.[0];
|
|
11412
|
+
if (!box) return void 0;
|
|
11413
|
+
const round = (value) => Math.round(value * 10) / 10;
|
|
11414
|
+
return {
|
|
11415
|
+
page: box.page,
|
|
11416
|
+
x: round(box.x),
|
|
11417
|
+
y: round(box.y),
|
|
11418
|
+
width: round(box.width),
|
|
11419
|
+
height: round(box.height)
|
|
11420
|
+
};
|
|
11421
|
+
}
|
|
11422
|
+
function tableRowsWithCells(table, byParent) {
|
|
11423
|
+
return (byParent.get(table.id) ?? []).filter((node) => node.kind === "table_row").map((row) => ({
|
|
11424
|
+
row,
|
|
11425
|
+
cells: (byParent.get(row.id) ?? []).filter((child) => child.kind === "table_cell").sort(
|
|
11426
|
+
(left, right) => tableCellColumnIndex(left, 0) - tableCellColumnIndex(right, 0) || left.order - right.order || left.id.localeCompare(right.id)
|
|
11427
|
+
)
|
|
11428
|
+
})).sort((left, right) => left.row.order - right.row.order || left.row.id.localeCompare(right.row.id));
|
|
11429
|
+
}
|
|
11430
|
+
function primaryHeaderColumnCount(rows) {
|
|
11431
|
+
const header = rows.find(({ row, cells }) => isSourceTreeHeaderRow(row) && cells.length > 1);
|
|
11432
|
+
if (header) return header.cells.length;
|
|
11433
|
+
return Math.max(0, ...rows.map(({ cells }) => cells.length));
|
|
11434
|
+
}
|
|
11435
|
+
function visualTableRepairScore(candidate) {
|
|
11436
|
+
const rows = candidate.rows;
|
|
11437
|
+
if (rows.length < 3) return 0;
|
|
11438
|
+
const headerCount = primaryHeaderColumnCount(rows);
|
|
11439
|
+
if (headerCount < 2) return 0;
|
|
11440
|
+
const firstHeaderIndex = rows.findIndex(({ row, cells }) => isSourceTreeHeaderRow(row) && cells.length > 1);
|
|
11441
|
+
const repeatedHeader = firstHeaderIndex >= 0 && rows.some(({ row }, index) => index > firstHeaderIndex && isSourceTreeHeaderRow(row));
|
|
11442
|
+
const shortContinuation = rows.some(
|
|
11443
|
+
({ row, cells }, index) => index > 0 && !isSourceTreeHeaderRow(row) && cells.length > 0 && cells.length < headerCount && !/^(?:item\s+\d+|[A-Z]\.)\b/i.test(tableCellText(cells[0]))
|
|
11444
|
+
);
|
|
11445
|
+
const genericDataLabels = rows.some(
|
|
11446
|
+
({ row, cells }) => !isSourceTreeHeaderRow(row) && cells.length >= 2 && cells.some((cell) => isGenericColumnTitle(cell.title))
|
|
11447
|
+
);
|
|
11448
|
+
const danglingSlash = rows.some(
|
|
11449
|
+
({ row, cells }) => !isSourceTreeHeaderRow(row) && /\/\s*$/.test(tableRowTextForPrompt(row, cells))
|
|
11450
|
+
);
|
|
11451
|
+
return (repeatedHeader ? 4 : 0) + (danglingSlash ? 3 : 0) + (shortContinuation ? 3 : 0) + (genericDataLabels ? 2 : 0);
|
|
11452
|
+
}
|
|
11453
|
+
function visualTableCandidates(sourceTree) {
|
|
11454
|
+
const byParent = nodesByParent(sourceTree);
|
|
11455
|
+
return sourceTree.filter((node) => node.kind === "table" && typeof node.pageStart === "number").map((table) => ({
|
|
11456
|
+
table,
|
|
11457
|
+
page: table.pageStart,
|
|
11458
|
+
rows: tableRowsWithCells(table, byParent)
|
|
11459
|
+
})).map((candidate) => ({ ...candidate, repairScore: visualTableRepairScore(candidate) })).filter((candidate) => candidate.repairScore > 0).sort(
|
|
11460
|
+
(left, right) => right.repairScore - left.repairScore || left.page - right.page || left.table.order - right.table.order || left.table.id.localeCompare(right.table.id)
|
|
11461
|
+
).slice(0, VISUAL_TABLE_REPAIR_MAX_TABLES).map(({ repairScore: _repairScore, ...candidate }) => candidate);
|
|
11462
|
+
}
|
|
11463
|
+
function compactVisualTableCandidate(candidate) {
|
|
11464
|
+
let cellCount = 0;
|
|
11465
|
+
const rows = candidate.rows.slice(0, VISUAL_TABLE_REPAIR_MAX_ROWS).map(({ row, cells }) => {
|
|
11466
|
+
const compactCells = cells.slice(0, Math.max(0, VISUAL_TABLE_REPAIR_MAX_CELLS - cellCount)).map((cell, index) => ({
|
|
11467
|
+
cellNodeId: cell.id,
|
|
11468
|
+
columnIndex: tableCellColumnIndex(cell, index),
|
|
11469
|
+
currentColumnName: cell.title,
|
|
11470
|
+
text: tableCellText(cell),
|
|
11471
|
+
bbox: bboxSummary(cell)
|
|
11472
|
+
}));
|
|
11473
|
+
cellCount += compactCells.length;
|
|
11474
|
+
return {
|
|
11475
|
+
rowNodeId: row.id,
|
|
11476
|
+
order: row.order,
|
|
11477
|
+
isHeader: isSourceTreeHeaderRow(row),
|
|
11478
|
+
text: tableRowTextForPrompt(row, cells),
|
|
11479
|
+
bbox: bboxSummary(row),
|
|
11480
|
+
cells: compactCells
|
|
11481
|
+
};
|
|
11482
|
+
});
|
|
11483
|
+
return {
|
|
11484
|
+
tableNodeId: candidate.table.id,
|
|
11485
|
+
page: candidate.page,
|
|
11486
|
+
title: candidate.table.title,
|
|
11487
|
+
bbox: bboxSummary(candidate.table),
|
|
11488
|
+
rows
|
|
11489
|
+
};
|
|
11490
|
+
}
|
|
11491
|
+
function buildVisualTableRepairPrompt(candidate) {
|
|
11492
|
+
return `Compare a parsed insurance source table against the original page visual layout.
|
|
11493
|
+
|
|
11494
|
+
If a page image is attached, use it as the primary reference. If no image is available, use the bbox coordinates below as the visual layout reference.
|
|
11495
|
+
|
|
11496
|
+
Task:
|
|
11497
|
+
- Identify rows that are not real standalone rows because they are visually wrapped continuation text for a nearby row.
|
|
11498
|
+
- Identify the primary printed column labels for the table.
|
|
11499
|
+
|
|
11500
|
+
Rules:
|
|
11501
|
+
- Return only high-confidence repairs.
|
|
11502
|
+
- Use only rowNodeId/tableNodeId/cellNodeId values from the provided JSON.
|
|
11503
|
+
- Do not invent policy facts, values, row text, source spans, or page numbers.
|
|
11504
|
+
- continuationRows.sourceRowNodeId must be a parsed row that should be removed as a standalone row.
|
|
11505
|
+
- continuationRows.targetRowNodeId must be the row that visually owns that wrapped text, usually the immediately previous non-header row.
|
|
11506
|
+
- targetColumnIndex/targetColumnLabel should point to the visual column that owns the wrapped text, usually the limit/amount/value column.
|
|
11507
|
+
- Do not mark actual data rows as continuations when they begin a new item, coverage part, endorsement, form, location, person, or premium/tax row.
|
|
11508
|
+
- columnLabels should be the primary visual header labels, not later wrapped cell text that the parser misread as a header.
|
|
11509
|
+
|
|
11510
|
+
Parsed table with visual coordinates:
|
|
11511
|
+
${JSON.stringify(compactVisualTableCandidate(candidate), null, 2)}
|
|
11512
|
+
|
|
11513
|
+
Return JSON with tables[].columnLabels and tables[].continuationRows. Return empty arrays if no repair is needed.`;
|
|
11514
|
+
}
|
|
11515
|
+
function sourceSpanIdsForNodes(nodes) {
|
|
11516
|
+
return [...new Set(nodes.flatMap((node) => node.sourceSpanIds))];
|
|
11517
|
+
}
|
|
11518
|
+
function appendDistinctText(base, addition) {
|
|
11519
|
+
const current = cleanText(base, "");
|
|
11520
|
+
const next = cleanText(addition, "");
|
|
11521
|
+
if (!current) return next || void 0;
|
|
11522
|
+
if (!next) return current;
|
|
11523
|
+
if (current.toLowerCase().includes(next.toLowerCase())) return current;
|
|
11524
|
+
const delimiter = /(?:[/(:;-]|,\s*)$/.test(current) ? " " : " / ";
|
|
11525
|
+
return cleanText(`${current}${delimiter}${next}`, current);
|
|
11526
|
+
}
|
|
11527
|
+
function mergedBbox(nodes) {
|
|
11528
|
+
const boxes = nodes.flatMap((node) => node.bbox ?? []);
|
|
11529
|
+
return boxes.length ? boxes.slice(0, 12) : void 0;
|
|
11530
|
+
}
|
|
11531
|
+
function normalizedRepairLabel(value) {
|
|
11532
|
+
const label = cleanText(value, "");
|
|
11533
|
+
if (!label || label.length > 80) return void 0;
|
|
11534
|
+
if (/^(?:source|page|row|table)$/i.test(label)) return void 0;
|
|
11535
|
+
return label;
|
|
11536
|
+
}
|
|
11537
|
+
function findCellForContinuation(params) {
|
|
11538
|
+
if (typeof params.targetColumnIndex === "number") {
|
|
11539
|
+
const byIndex = params.cells.find(
|
|
11540
|
+
(cell, index) => tableCellColumnIndex(cell, index) === params.targetColumnIndex
|
|
11541
|
+
);
|
|
11542
|
+
if (byIndex) return byIndex;
|
|
11543
|
+
}
|
|
11544
|
+
const label = normalizedRepairLabel(params.targetColumnLabel);
|
|
11545
|
+
if (label) {
|
|
11546
|
+
const byLabel = params.cells.find(
|
|
11547
|
+
(cell) => cleanText(cell.title, "").toLowerCase() === label.toLowerCase()
|
|
11548
|
+
);
|
|
11549
|
+
if (byLabel) return byLabel;
|
|
11550
|
+
}
|
|
11551
|
+
return params.cells[1] ?? params.cells[params.cells.length - 1];
|
|
11552
|
+
}
|
|
11553
|
+
function applyVisualTableRepair(sourceTree, repair) {
|
|
11554
|
+
if (repair.tables.length === 0) return sourceTree;
|
|
11555
|
+
const byId = new Map(sourceTree.map((node) => [node.id, node]));
|
|
11556
|
+
const byParent = nodesByParent(sourceTree);
|
|
11557
|
+
const updates = /* @__PURE__ */ new Map();
|
|
11558
|
+
const removeIds = /* @__PURE__ */ new Set();
|
|
11559
|
+
const currentNode = (id) => updates.get(id) ?? byId.get(id);
|
|
11560
|
+
for (const tableRepair of repair.tables) {
|
|
11561
|
+
const table = byId.get(tableRepair.tableNodeId);
|
|
11562
|
+
if (!table || table.kind !== "table") continue;
|
|
11563
|
+
const rows = tableRowsWithCells(table, byParent);
|
|
11564
|
+
const rowIds = new Set(rows.map(({ row }) => row.id));
|
|
11565
|
+
const rowOrder = new Map(rows.map(({ row }, index) => [row.id, index]));
|
|
11566
|
+
const columnLabels = /* @__PURE__ */ new Map();
|
|
11567
|
+
for (const label of tableRepair.columnLabels) {
|
|
11568
|
+
const normalized = normalizedRepairLabel(label.label);
|
|
11569
|
+
if (normalized) columnLabels.set(label.columnIndex, normalized);
|
|
11570
|
+
}
|
|
11571
|
+
if (columnLabels.size > 0) {
|
|
11572
|
+
for (const { row, cells } of rows) {
|
|
11573
|
+
if (removeIds.has(row.id)) continue;
|
|
11574
|
+
for (const [fallbackIndex, cell] of cells.entries()) {
|
|
11575
|
+
const columnIndex = tableCellColumnIndex(cell, fallbackIndex);
|
|
11576
|
+
const label = columnLabels.get(columnIndex);
|
|
11577
|
+
if (!label || cell.title === label) continue;
|
|
11578
|
+
updates.set(cell.id, {
|
|
11579
|
+
...currentNode(cell.id) ?? cell,
|
|
11580
|
+
title: label,
|
|
11581
|
+
metadata: {
|
|
11582
|
+
...cell.metadata ?? {},
|
|
11583
|
+
visualTableRepairColumnLabel: label
|
|
11584
|
+
}
|
|
11585
|
+
});
|
|
11586
|
+
}
|
|
11587
|
+
}
|
|
11588
|
+
}
|
|
11589
|
+
for (const continuation of tableRepair.continuationRows) {
|
|
11590
|
+
if (!rowIds.has(continuation.sourceRowNodeId) || !rowIds.has(continuation.targetRowNodeId)) continue;
|
|
11591
|
+
if (continuation.sourceRowNodeId === continuation.targetRowNodeId) continue;
|
|
11592
|
+
const sourceIndex = rowOrder.get(continuation.sourceRowNodeId);
|
|
11593
|
+
const targetIndex = rowOrder.get(continuation.targetRowNodeId);
|
|
11594
|
+
if (sourceIndex === void 0 || targetIndex === void 0) continue;
|
|
11595
|
+
if (Math.abs(sourceIndex - targetIndex) > 3) continue;
|
|
11596
|
+
const sourceRow = currentNode(continuation.sourceRowNodeId);
|
|
11597
|
+
const targetRow = currentNode(continuation.targetRowNodeId);
|
|
11598
|
+
if (!sourceRow || !targetRow || sourceRow.kind !== "table_row" || targetRow.kind !== "table_row") continue;
|
|
11599
|
+
if (isSourceTreeHeaderRow(targetRow)) continue;
|
|
11600
|
+
const sourceCells = (byParent.get(sourceRow.id) ?? []).filter((node) => node.kind === "table_cell").map((node) => currentNode(node.id) ?? node);
|
|
11601
|
+
const targetCells = (byParent.get(targetRow.id) ?? []).filter((node) => node.kind === "table_cell").map((node) => currentNode(node.id) ?? node);
|
|
11602
|
+
const sourceText = tableRowTextForPrompt(sourceRow, sourceCells);
|
|
11603
|
+
if (!sourceText) continue;
|
|
11604
|
+
const targetCell = findCellForContinuation({
|
|
11605
|
+
cells: targetCells,
|
|
11606
|
+
targetColumnIndex: continuation.targetColumnIndex,
|
|
11607
|
+
targetColumnLabel: continuation.targetColumnLabel
|
|
11608
|
+
});
|
|
11609
|
+
const sourceNodes = [sourceRow, ...sourceCells];
|
|
11610
|
+
const sourceSpanIds = sourceSpanIdsForNodes(sourceNodes);
|
|
11611
|
+
if (targetCell) {
|
|
11612
|
+
const nextCellText = appendDistinctText(tableCellText(targetCell), sourceText);
|
|
11613
|
+
if (nextCellText) {
|
|
11614
|
+
const mergedNodes = [targetCell, ...sourceNodes];
|
|
11615
|
+
updates.set(targetCell.id, {
|
|
11616
|
+
...currentNode(targetCell.id) ?? targetCell,
|
|
11617
|
+
textExcerpt: nextCellText,
|
|
11618
|
+
description: cleanText([targetCell.title, nextCellText].filter(Boolean).join(" | "), targetCell.description),
|
|
11619
|
+
sourceSpanIds: [.../* @__PURE__ */ new Set([...targetCell.sourceSpanIds, ...sourceSpanIds])],
|
|
11620
|
+
bbox: mergedBbox(mergedNodes),
|
|
11621
|
+
metadata: {
|
|
11622
|
+
...targetCell.metadata ?? {},
|
|
11623
|
+
visualTableRepair: "merged_continuation"
|
|
11624
|
+
}
|
|
11625
|
+
});
|
|
11626
|
+
}
|
|
11627
|
+
}
|
|
11628
|
+
const nextRowText = appendDistinctText(targetRow.textExcerpt ?? targetRow.description, sourceText);
|
|
11629
|
+
updates.set(targetRow.id, {
|
|
11630
|
+
...currentNode(targetRow.id) ?? targetRow,
|
|
11631
|
+
textExcerpt: nextRowText,
|
|
11632
|
+
description: cleanText([targetRow.title, nextRowText].filter(Boolean).join(" | "), targetRow.description),
|
|
11633
|
+
sourceSpanIds: [.../* @__PURE__ */ new Set([...targetRow.sourceSpanIds, ...sourceSpanIds])],
|
|
11634
|
+
bbox: mergedBbox([targetRow, ...sourceNodes]),
|
|
11635
|
+
metadata: {
|
|
11636
|
+
...targetRow.metadata ?? {},
|
|
11637
|
+
visualTableRepair: "merged_continuation"
|
|
11638
|
+
}
|
|
11639
|
+
});
|
|
11640
|
+
removeIds.add(sourceRow.id);
|
|
11641
|
+
for (const sourceCell of sourceCells) removeIds.add(sourceCell.id);
|
|
11642
|
+
}
|
|
11643
|
+
}
|
|
11644
|
+
if (updates.size === 0 && removeIds.size === 0) return sourceTree;
|
|
11645
|
+
const repaired = sourceTree.filter((node) => !removeIds.has(node.id)).map((node) => updates.get(node.id) ?? node);
|
|
11646
|
+
return normalizeDocumentSourceTreePaths(normalizeContainerEvidenceFromChildren(repaired));
|
|
11647
|
+
}
|
|
11648
|
+
async function runVisualTableRepair(params) {
|
|
11649
|
+
const candidates = visualTableCandidates(params.sourceTree);
|
|
11650
|
+
if (candidates.length === 0) return { sourceTree: params.sourceTree, warnings: [] };
|
|
11651
|
+
let sourceTree = params.sourceTree;
|
|
11652
|
+
const warnings = [];
|
|
11653
|
+
for (const [index, candidate] of candidates.entries()) {
|
|
11654
|
+
try {
|
|
11655
|
+
const budget = params.resolveBudget("extraction_source_tree", 1800);
|
|
11656
|
+
const maxTokens = Math.min(budget.maxTokens, 2400);
|
|
11657
|
+
const startedAt = Date.now();
|
|
11658
|
+
const response = await safeGenerateObject(
|
|
11659
|
+
params.generateObject,
|
|
11660
|
+
{
|
|
11661
|
+
prompt: buildVisualTableRepairPrompt(candidate),
|
|
11662
|
+
schema: SourceTreeVisualTableRepairSchema,
|
|
11663
|
+
maxTokens,
|
|
11664
|
+
taskKind: "extraction_source_tree",
|
|
11665
|
+
budgetDiagnostics: { ...budget, maxTokens },
|
|
11666
|
+
trace: {
|
|
11667
|
+
label: `source_tree_visual_table_repair_p${candidate.page}`,
|
|
11668
|
+
startPage: candidate.page,
|
|
11669
|
+
endPage: candidate.page,
|
|
11670
|
+
batchIndex: index + 1,
|
|
11671
|
+
batchCount: candidates.length,
|
|
11672
|
+
sourceBacked: true
|
|
11673
|
+
}
|
|
11674
|
+
},
|
|
11675
|
+
{
|
|
11676
|
+
fallback: { tables: [], warnings: [] },
|
|
11677
|
+
log: params.log
|
|
11678
|
+
}
|
|
11679
|
+
);
|
|
11680
|
+
params.trackUsage(response.usage, {
|
|
11681
|
+
taskKind: "extraction_source_tree",
|
|
11682
|
+
label: `source_tree_visual_table_repair_p${candidate.page}`,
|
|
11683
|
+
maxTokens,
|
|
11684
|
+
durationMs: Date.now() - startedAt
|
|
11685
|
+
});
|
|
11686
|
+
const repair = response.object;
|
|
11687
|
+
sourceTree = applyVisualTableRepair(sourceTree, repair);
|
|
11688
|
+
warnings.push(...repair.warnings.map(
|
|
11689
|
+
(warning) => `Visual table repair warning on page ${candidate.page}: ${warning}`
|
|
11690
|
+
));
|
|
11691
|
+
} catch (error) {
|
|
11692
|
+
warnings.push(`Visual table repair skipped on page ${candidate.page}; parsed table kept (${error instanceof Error ? error.message : String(error)})`);
|
|
11693
|
+
}
|
|
11694
|
+
}
|
|
11695
|
+
return { sourceTree, warnings };
|
|
11696
|
+
}
|
|
11348
11697
|
function groupNodeId(documentId, group) {
|
|
11349
11698
|
return [
|
|
11350
11699
|
documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
|
|
@@ -11658,6 +12007,15 @@ async function runSourceTreeExtraction(params) {
|
|
|
11658
12007
|
warnings.push(`Source-tree outline cleanup failed; deterministic tree used (${error instanceof Error ? error.message : String(error)})`);
|
|
11659
12008
|
}
|
|
11660
12009
|
}
|
|
12010
|
+
const visualTableRepair = await runVisualTableRepair({
|
|
12011
|
+
sourceTree,
|
|
12012
|
+
generateObject: params.generateObject,
|
|
12013
|
+
resolveBudget: params.resolveBudget,
|
|
12014
|
+
trackUsage: localTrack,
|
|
12015
|
+
log: params.log
|
|
12016
|
+
});
|
|
12017
|
+
sourceTree = visualTableRepair.sourceTree;
|
|
12018
|
+
warnings.push(...visualTableRepair.warnings);
|
|
11661
12019
|
const deterministicProfile = buildDeterministicOperationalProfile({
|
|
11662
12020
|
sourceTree,
|
|
11663
12021
|
sourceSpans
|