@claritylabs/cl-sdk 3.0.11 → 3.0.13
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/index.js +212 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +212 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2602,7 +2602,7 @@ function normalizeNodeKind(span) {
|
|
|
2602
2602
|
if (unit === "table_cell") return "table_cell";
|
|
2603
2603
|
if (unit === "key_value") return "schedule";
|
|
2604
2604
|
if (unit === "section") return "section";
|
|
2605
|
-
if (element === "
|
|
2605
|
+
if (element === "section_candidate") {
|
|
2606
2606
|
const text = span.text.toLowerCase();
|
|
2607
2607
|
if (/endorsement/.test(text)) return "endorsement";
|
|
2608
2608
|
if (/schedule|declarations?/.test(text)) return "schedule";
|
|
@@ -2638,6 +2638,90 @@ function makeNode(params) {
|
|
|
2638
2638
|
metadata: params.metadata
|
|
2639
2639
|
};
|
|
2640
2640
|
}
|
|
2641
|
+
function metadataString(metadata, key) {
|
|
2642
|
+
const value = metadata?.[key];
|
|
2643
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
2644
|
+
}
|
|
2645
|
+
function isTitleContentNode(node) {
|
|
2646
|
+
if (node.kind !== "text") return false;
|
|
2647
|
+
return metadataString(node.metadata, "elementType") === "title" || metadataString(node.metadata, "sourceUnit") === "title";
|
|
2648
|
+
}
|
|
2649
|
+
function nodePageEnd(node) {
|
|
2650
|
+
return node.pageEnd ?? node.pageStart;
|
|
2651
|
+
}
|
|
2652
|
+
function groupPageContentByTitles(nodes) {
|
|
2653
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
2654
|
+
for (const node of nodes) {
|
|
2655
|
+
const children = byParent.get(node.parentId) ?? [];
|
|
2656
|
+
children.push(node);
|
|
2657
|
+
byParent.set(node.parentId, children);
|
|
2658
|
+
}
|
|
2659
|
+
for (const children of byParent.values()) {
|
|
2660
|
+
children.sort((left, right) => left.order - right.order || left.id.localeCompare(right.id));
|
|
2661
|
+
}
|
|
2662
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
2663
|
+
for (const pageNode of nodes.filter((node) => node.kind === "page")) {
|
|
2664
|
+
const children = (byParent.get(pageNode.id) ?? []).filter((child) => child.kind !== "table_row" && child.kind !== "table_cell");
|
|
2665
|
+
let activeTitle;
|
|
2666
|
+
let activeContent = [];
|
|
2667
|
+
const flush = () => {
|
|
2668
|
+
if (!activeTitle || activeContent.length === 0) {
|
|
2669
|
+
activeTitle = void 0;
|
|
2670
|
+
activeContent = [];
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
const contentNodes = activeContent.map((node) => byId.get(node.id) ?? node).filter((node) => node.parentId === pageNode.id);
|
|
2674
|
+
if (contentNodes.length === 0) {
|
|
2675
|
+
activeTitle = void 0;
|
|
2676
|
+
activeContent = [];
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
const evidenceNodes = [activeTitle, ...contentNodes];
|
|
2680
|
+
const title = truncate(activeTitle.textExcerpt ?? activeTitle.title, 120);
|
|
2681
|
+
const pageStarts = evidenceNodes.map((node) => node.pageStart).filter((page) => typeof page === "number");
|
|
2682
|
+
const pageEnds = evidenceNodes.map(nodePageEnd).filter((page) => typeof page === "number");
|
|
2683
|
+
const sourceSpanIds = [...new Set(evidenceNodes.flatMap((node) => node.sourceSpanIds))];
|
|
2684
|
+
const bbox = evidenceNodes.flatMap((node) => node.bbox ?? []).slice(0, 12);
|
|
2685
|
+
byId.set(activeTitle.id, {
|
|
2686
|
+
...activeTitle,
|
|
2687
|
+
kind: "section",
|
|
2688
|
+
title,
|
|
2689
|
+
description: nodeTextDescription({
|
|
2690
|
+
kind: "section",
|
|
2691
|
+
title,
|
|
2692
|
+
text: evidenceNodes.map((node) => node.textExcerpt).filter(Boolean).join("\n\n"),
|
|
2693
|
+
page: activeTitle.pageStart
|
|
2694
|
+
}),
|
|
2695
|
+
textExcerpt: evidenceNodes.map((node) => node.textExcerpt).filter(Boolean).join("\n\n").slice(0, 1600),
|
|
2696
|
+
sourceSpanIds,
|
|
2697
|
+
pageStart: pageStarts.length ? Math.min(...pageStarts) : activeTitle.pageStart,
|
|
2698
|
+
pageEnd: pageEnds.length ? Math.max(...pageEnds) : activeTitle.pageEnd,
|
|
2699
|
+
bbox,
|
|
2700
|
+
metadata: {
|
|
2701
|
+
...activeTitle.metadata,
|
|
2702
|
+
sourceTreeVersion: "v3",
|
|
2703
|
+
organizer: "title_block"
|
|
2704
|
+
}
|
|
2705
|
+
});
|
|
2706
|
+
for (const node of contentNodes) {
|
|
2707
|
+
byId.set(node.id, { ...node, parentId: activeTitle.id });
|
|
2708
|
+
}
|
|
2709
|
+
activeTitle = void 0;
|
|
2710
|
+
activeContent = [];
|
|
2711
|
+
};
|
|
2712
|
+
for (const child of children) {
|
|
2713
|
+
if (isTitleContentNode(child)) {
|
|
2714
|
+
flush();
|
|
2715
|
+
activeTitle = child;
|
|
2716
|
+
activeContent = [];
|
|
2717
|
+
continue;
|
|
2718
|
+
}
|
|
2719
|
+
if (activeTitle) activeContent.push(child);
|
|
2720
|
+
}
|
|
2721
|
+
flush();
|
|
2722
|
+
}
|
|
2723
|
+
return nodes.map((node) => byId.get(node.id) ?? node);
|
|
2724
|
+
}
|
|
2641
2725
|
function sortSpans(left, right) {
|
|
2642
2726
|
const leftPage = pageStart(left) ?? 0;
|
|
2643
2727
|
const rightPage = pageStart(right) ?? 0;
|
|
@@ -2813,7 +2897,7 @@ function buildDocumentSourceTree(sourceSpans, documentId) {
|
|
|
2813
2897
|
}
|
|
2814
2898
|
addStandaloneSpanNode(span, pageParentId);
|
|
2815
2899
|
}
|
|
2816
|
-
return normalizeDocumentSourceTreePaths([...nodes.values()]);
|
|
2900
|
+
return normalizeDocumentSourceTreePaths(groupPageContentByTitles([...nodes.values()]));
|
|
2817
2901
|
}
|
|
2818
2902
|
|
|
2819
2903
|
// src/source/operational-profile.ts
|
|
@@ -9009,9 +9093,9 @@ var ORGANIZABLE_KINDS = [
|
|
|
9009
9093
|
"clause"
|
|
9010
9094
|
];
|
|
9011
9095
|
var ORGANIZATION_TOP_LEVEL_BATCH_SIZE = 80;
|
|
9012
|
-
var ORGANIZATION_CHILD_CONTEXT_LIMIT = 4;
|
|
9013
9096
|
var ORGANIZER_MAX_SOURCE_SPANS = 400;
|
|
9014
9097
|
var ORGANIZER_MAX_TOP_LEVEL_NODES = 18;
|
|
9098
|
+
var OUTLINE_CLEANUP_MAX_TOP_LEVEL_NODES = 80;
|
|
9015
9099
|
var SourceTreeOrganizationSchema = z41.object({
|
|
9016
9100
|
labels: z41.array(z41.object({
|
|
9017
9101
|
nodeId: z41.string(),
|
|
@@ -9128,6 +9212,22 @@ function endorsementDescription(title, node) {
|
|
|
9128
9212
|
title
|
|
9129
9213
|
);
|
|
9130
9214
|
}
|
|
9215
|
+
function nodePageEnd2(node) {
|
|
9216
|
+
return node.pageEnd ?? node.pageStart;
|
|
9217
|
+
}
|
|
9218
|
+
function pageRangeForNodes(nodes) {
|
|
9219
|
+
const pageStarts = nodes.map((node) => node.pageStart).filter((page) => typeof page === "number");
|
|
9220
|
+
const pageEnds = nodes.map(nodePageEnd2).filter((page) => typeof page === "number");
|
|
9221
|
+
if (pageStarts.length === 0 || pageEnds.length === 0) return void 0;
|
|
9222
|
+
const start = Math.min(...pageStarts);
|
|
9223
|
+
const end = Math.max(...pageEnds);
|
|
9224
|
+
return start === end ? `page ${start}` : `pages ${start}-${end}`;
|
|
9225
|
+
}
|
|
9226
|
+
function descriptionWithPages(description, nodes) {
|
|
9227
|
+
const range = pageRangeForNodes(nodes);
|
|
9228
|
+
if (!range || new RegExp(`\\b${range.replace("-", "\\-")}\\b`, "i").test(description)) return description;
|
|
9229
|
+
return `${description}; ${range}`;
|
|
9230
|
+
}
|
|
9131
9231
|
function semanticGroupNodeId(documentId, kind, title, childNodeIds) {
|
|
9132
9232
|
return [
|
|
9133
9233
|
documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
|
|
@@ -9138,7 +9238,12 @@ function semanticGroupNodeId(documentId, kind, title, childNodeIds) {
|
|
|
9138
9238
|
].join(":");
|
|
9139
9239
|
}
|
|
9140
9240
|
function looksLikeDeclarationsStart(node) {
|
|
9141
|
-
|
|
9241
|
+
const title = cleanText(node.title, "");
|
|
9242
|
+
const text = sourceNodeText(node);
|
|
9243
|
+
if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|how to report a claim)\b/i.test(text)) {
|
|
9244
|
+
return false;
|
|
9245
|
+
}
|
|
9246
|
+
return /^declarations?$/i.test(title) || /\bdeclarations?\s+(page|schedule|section)\b/i.test(text) || /^declarations?\b/i.test(cleanText(node.textExcerpt, ""));
|
|
9142
9247
|
}
|
|
9143
9248
|
function looksLikeDeclarationsContinuation(node) {
|
|
9144
9249
|
const text = sourceNodeText(node);
|
|
@@ -9173,7 +9278,7 @@ function groupAdjacentChildren(params) {
|
|
|
9173
9278
|
parentId,
|
|
9174
9279
|
kind: params.kind,
|
|
9175
9280
|
title: params.title,
|
|
9176
|
-
description: params.description,
|
|
9281
|
+
description: descriptionWithPages(params.description, children),
|
|
9177
9282
|
textExcerpt: children.map((child) => child.textExcerpt ?? child.description).filter(Boolean).join("\n\n").slice(0, 1600),
|
|
9178
9283
|
sourceSpanIds,
|
|
9179
9284
|
pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
|
|
@@ -9226,6 +9331,22 @@ function applySemanticPageGrouping(sourceTree) {
|
|
|
9226
9331
|
const children = (nodesByParent(relabeled).get(rootId) ?? []).filter((node) => node.kind !== "document").sort((left, right) => left.order - right.order);
|
|
9227
9332
|
let nextTree = relabeled;
|
|
9228
9333
|
const declarationsStartIndex = children.findIndex(looksLikeDeclarationsStart);
|
|
9334
|
+
const firstCoreIndex = children.findIndex(
|
|
9335
|
+
(child) => looksLikeDeclarationsStart(child) || looksLikePolicyFormStart(child) || looksLikeEndorsementStart(child)
|
|
9336
|
+
);
|
|
9337
|
+
const frontMatterBoundary = declarationsStartIndex >= 0 ? declarationsStartIndex : firstCoreIndex;
|
|
9338
|
+
if (frontMatterBoundary > 0) {
|
|
9339
|
+
const frontMatterIds = children.slice(0, frontMatterBoundary).map((child) => child.id);
|
|
9340
|
+
nextTree = groupAdjacentChildren({
|
|
9341
|
+
sourceTree: nextTree,
|
|
9342
|
+
children,
|
|
9343
|
+
childIds: frontMatterIds,
|
|
9344
|
+
kind: "page_group",
|
|
9345
|
+
title: "Notices and Jacket",
|
|
9346
|
+
description: "Policy jacket, notices, and administrative pages grouped by source order",
|
|
9347
|
+
organizer: "semantic_front_matter_grouping"
|
|
9348
|
+
});
|
|
9349
|
+
}
|
|
9229
9350
|
if (declarationsStartIndex >= 0) {
|
|
9230
9351
|
const declarationIds = [];
|
|
9231
9352
|
for (let index = declarationsStartIndex; index < children.length; index += 1) {
|
|
@@ -9394,17 +9515,19 @@ function normalizeContainerEvidenceFromChildren(sourceTree) {
|
|
|
9394
9515
|
return sourceTree.map((node) => byId.get(node.id) ?? node);
|
|
9395
9516
|
}
|
|
9396
9517
|
function normalizeSemanticHierarchy(sourceTree) {
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
normalizePolicyFormStructure(
|
|
9401
|
-
normalizeDocumentSourceTreePaths(sourceTree)
|
|
9402
|
-
)
|
|
9403
|
-
)
|
|
9518
|
+
const normalized = normalizeDocumentSourceTreePaths(
|
|
9519
|
+
normalizePolicyFormStructure(
|
|
9520
|
+
normalizeDocumentSourceTreePaths(sourceTree)
|
|
9404
9521
|
)
|
|
9405
9522
|
);
|
|
9523
|
+
const nested = normalizeDocumentSourceTreePaths(nestEndorsementContinuationPages(normalized));
|
|
9524
|
+
const withEvidence = normalizeContainerEvidenceFromChildren(nested);
|
|
9525
|
+
return normalizeDocumentSourceTreePaths(
|
|
9526
|
+
normalizeContainerEvidenceFromChildren(withEvidence)
|
|
9527
|
+
);
|
|
9406
9528
|
}
|
|
9407
9529
|
function applyEndorsementGrouping(sourceTree) {
|
|
9530
|
+
const rootId = sourceTreeRootId(sourceTree);
|
|
9408
9531
|
const relabeledTree = sourceTree.map((node) => {
|
|
9409
9532
|
if (node.kind === "document" || isEndorsementGroup(node)) return node;
|
|
9410
9533
|
const title = endorsementStartTitle(node);
|
|
@@ -9442,7 +9565,7 @@ function applyEndorsementGrouping(sourceTree) {
|
|
|
9442
9565
|
...node,
|
|
9443
9566
|
kind: "page_group",
|
|
9444
9567
|
title: "Endorsements",
|
|
9445
|
-
description: cleanText(node.description, "Endorsement forms grouped by source order"),
|
|
9568
|
+
description: descriptionWithPages(cleanText(node.description, "Endorsement forms grouped by source order"), byParent.get(node.id) ?? [node]),
|
|
9446
9569
|
metadata: {
|
|
9447
9570
|
...node.metadata,
|
|
9448
9571
|
sourceTreeVersion: "v3",
|
|
@@ -9469,9 +9592,10 @@ function applyEndorsementGrouping(sourceTree) {
|
|
|
9469
9592
|
};
|
|
9470
9593
|
});
|
|
9471
9594
|
for (const [parentId, children] of byParent) {
|
|
9595
|
+
if (parentId !== rootId) continue;
|
|
9472
9596
|
if (endorsementGroupIds.has(parentId ?? "")) continue;
|
|
9473
9597
|
const endorsementChildren = children.filter((child) => child.kind === "endorsement" && !isEndorsementGroup(child));
|
|
9474
|
-
if (endorsementChildren.length <
|
|
9598
|
+
if (endorsementChildren.length < 1) continue;
|
|
9475
9599
|
const endorsementGroupChildren = [];
|
|
9476
9600
|
let hasSeenEndorsementStart = false;
|
|
9477
9601
|
for (const child of children) {
|
|
@@ -9484,6 +9608,7 @@ function applyEndorsementGrouping(sourceTree) {
|
|
|
9484
9608
|
endorsementGroupChildren.push(child);
|
|
9485
9609
|
}
|
|
9486
9610
|
}
|
|
9611
|
+
if (endorsementGroupChildren.length < 1) continue;
|
|
9487
9612
|
const documentId = endorsementChildren[0].documentId;
|
|
9488
9613
|
const pageStarts = endorsementGroupChildren.map((child) => child.pageStart).filter((page) => typeof page === "number");
|
|
9489
9614
|
const pageEnds = endorsementGroupChildren.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
|
|
@@ -9496,7 +9621,7 @@ function applyEndorsementGrouping(sourceTree) {
|
|
|
9496
9621
|
parentId,
|
|
9497
9622
|
kind: "page_group",
|
|
9498
9623
|
title: "Endorsements",
|
|
9499
|
-
description: "Endorsement forms grouped by source order",
|
|
9624
|
+
description: descriptionWithPages("Endorsement forms grouped by source order", endorsementGroupChildren),
|
|
9500
9625
|
textExcerpt: void 0,
|
|
9501
9626
|
sourceSpanIds: [],
|
|
9502
9627
|
pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
|
|
@@ -9507,11 +9632,13 @@ function applyEndorsementGrouping(sourceTree) {
|
|
|
9507
9632
|
metadata: { sourceTreeVersion: "v3", organizer: "endorsement_grouping" }
|
|
9508
9633
|
};
|
|
9509
9634
|
const childSpanIds = [...new Set(endorsementGroupChildren.flatMap((child) => child.sourceSpanIds))];
|
|
9635
|
+
const childPageStart = pageStarts.length ? Math.min(...pageStarts) : void 0;
|
|
9636
|
+
const childPageEnd = pageEnds.length ? Math.max(...pageEnds) : void 0;
|
|
9510
9637
|
const normalizedGroup = {
|
|
9511
9638
|
...groupNode,
|
|
9512
9639
|
sourceSpanIds: groupNode.sourceSpanIds.length ? groupNode.sourceSpanIds : childSpanIds,
|
|
9513
|
-
pageStart: groupNode.pageStart
|
|
9514
|
-
pageEnd: groupNode.pageEnd
|
|
9640
|
+
pageStart: childPageStart === void 0 ? groupNode.pageStart : groupNode.pageStart === void 0 ? childPageStart : Math.min(groupNode.pageStart, childPageStart),
|
|
9641
|
+
pageEnd: childPageEnd === void 0 ? groupNode.pageEnd : groupNode.pageEnd === void 0 ? childPageEnd : Math.max(groupNode.pageEnd, childPageEnd),
|
|
9515
9642
|
order
|
|
9516
9643
|
};
|
|
9517
9644
|
groupsByParent.set(parentId, normalizedGroup);
|
|
@@ -9587,10 +9714,6 @@ function organizationBatches(sourceTree) {
|
|
|
9587
9714
|
const candidates = /* @__PURE__ */ new Map();
|
|
9588
9715
|
for (const node of topLevelBatch) {
|
|
9589
9716
|
candidates.set(node.id, node);
|
|
9590
|
-
const childContext = (byParent.get(node.id) ?? []).filter((child) => child.kind !== "text" && child.kind !== "table_cell").slice(0, ORGANIZATION_CHILD_CONTEXT_LIMIT);
|
|
9591
|
-
for (const child of childContext) {
|
|
9592
|
-
candidates.set(child.id, child);
|
|
9593
|
-
}
|
|
9594
9717
|
}
|
|
9595
9718
|
batches.push({
|
|
9596
9719
|
label: `top-level nodes ${index + 1}-${index + topLevelBatch.length} of ${topLevelNodes.length}`,
|
|
@@ -9641,6 +9764,42 @@ Rules:
|
|
|
9641
9764
|
Source nodes:
|
|
9642
9765
|
${JSON.stringify(nodes, null, 2)}
|
|
9643
9766
|
|
|
9767
|
+
Return JSON with labels and groups only.`;
|
|
9768
|
+
}
|
|
9769
|
+
function shouldRunOutlineCleanup(sourceTree) {
|
|
9770
|
+
const topLevel = rootChildren(sourceTree);
|
|
9771
|
+
if (topLevel.length === 0 || topLevel.length > OUTLINE_CLEANUP_MAX_TOP_LEVEL_NODES) return false;
|
|
9772
|
+
const genericPages = topLevel.filter((node) => node.kind === "page" && /^Page\s+\d+$/i.test(node.title));
|
|
9773
|
+
const hasDeclarations = topLevel.some((node) => node.title === "Declarations");
|
|
9774
|
+
const hasPolicyForm = topLevel.some((node) => node.title === "Policy Form");
|
|
9775
|
+
const hasEndorsements = topLevel.some((node) => node.title === "Endorsements");
|
|
9776
|
+
if (hasDeclarations && hasPolicyForm && hasEndorsements) return false;
|
|
9777
|
+
return genericPages.length > 0 || topLevel.length > 6 || !hasDeclarations || !hasPolicyForm || !hasEndorsements;
|
|
9778
|
+
}
|
|
9779
|
+
function buildOutlineCleanupPrompt(sourceTree) {
|
|
9780
|
+
const topLevel = rootChildren(sourceTree).slice(0, OUTLINE_CLEANUP_MAX_TOP_LEVEL_NODES);
|
|
9781
|
+
const nodes = topLevel.map((node) => compactNode(node, 900));
|
|
9782
|
+
return `You clean a top-level source outline for an insurance policy.
|
|
9783
|
+
|
|
9784
|
+
Expected product-facing order:
|
|
9785
|
+
1. Optional front matter: policy jacket, important notices, privacy notices, OFAC notices, TRIA/terrorism notices, marketing/admin pages, signatures, countersignatures, or other pages that are not the declarations, policy wording, or endorsements.
|
|
9786
|
+
2. Declarations: declarations page(s), schedules, named insured/policy period/premium rows, coverage limit schedules, forms-and-endorsements schedules.
|
|
9787
|
+
3. Policy Form: the main policy wording, insuring agreements, definitions, exclusions, conditions, claim provisions, and general policy terms.
|
|
9788
|
+
4. Endorsements: one generic "Endorsements" page_group containing each separately numbered endorsement as its own child.
|
|
9789
|
+
|
|
9790
|
+
Rules:
|
|
9791
|
+
- Use only node IDs from this top-level list: ${JSON.stringify(topLevel.map((node) => node.id))}
|
|
9792
|
+
- Group only adjacent top-level nodes.
|
|
9793
|
+
- Do not invent text, pages, source spans, limits, or policy facts.
|
|
9794
|
+
- Do not merge individually numbered endorsements into one endorsement node; use the generic "Endorsements" parent for the series.
|
|
9795
|
+
- Use canonical terse titles: "Notices and Jacket", "Declarations", "Policy Form", "Endorsements", or the printed endorsement number.
|
|
9796
|
+
- Keep page_group descriptions short and include the page range when pages are known, for example "Declarations pages 6-8".
|
|
9797
|
+
- If a page is an OFAC, privacy, terrorism/TRIA, claim-reporting notice, signature page, or jacket, do not label it as declarations or policy form.
|
|
9798
|
+
- If the existing deterministic outline is already correct, return empty labels and groups.
|
|
9799
|
+
|
|
9800
|
+
Top-level source nodes:
|
|
9801
|
+
${JSON.stringify(nodes, null, 2)}
|
|
9802
|
+
|
|
9644
9803
|
Return JSON with labels and groups only.`;
|
|
9645
9804
|
}
|
|
9646
9805
|
function buildOperationalProfilePrompt(sourceTree, fallback) {
|
|
@@ -9695,7 +9854,7 @@ function applyOrganization(sourceTree, organization) {
|
|
|
9695
9854
|
if (!children.every((child) => child.parentId === parentId)) continue;
|
|
9696
9855
|
const documentId = children[0].documentId;
|
|
9697
9856
|
const title = simplifyOrganizerTitle(group.title, group.title, group.kind);
|
|
9698
|
-
const description = cleanText(group.description, title);
|
|
9857
|
+
const description = descriptionWithPages(cleanText(group.description, title), children);
|
|
9699
9858
|
const id = groupNodeId(documentId, { ...group, title });
|
|
9700
9859
|
if (byId.has(id)) continue;
|
|
9701
9860
|
const sourceSpanIds = [...new Set(children.flatMap((child) => child.sourceSpanIds))];
|
|
@@ -9886,8 +10045,7 @@ async function runSourceTreeExtraction(params) {
|
|
|
9886
10045
|
schema: SourceTreeOrganizationSchema,
|
|
9887
10046
|
maxTokens: budget.maxTokens,
|
|
9888
10047
|
taskKind: "extraction_source_tree",
|
|
9889
|
-
budgetDiagnostics: budget
|
|
9890
|
-
providerOptions: params.providerOptions
|
|
10048
|
+
budgetDiagnostics: budget
|
|
9891
10049
|
},
|
|
9892
10050
|
{
|
|
9893
10051
|
fallback: { labels: [], groups: [] },
|
|
@@ -9909,6 +10067,36 @@ async function runSourceTreeExtraction(params) {
|
|
|
9909
10067
|
} else {
|
|
9910
10068
|
await params.log?.("Deterministic source tree ready; skipped model organizer");
|
|
9911
10069
|
}
|
|
10070
|
+
if (shouldRunOutlineCleanup(sourceTree)) {
|
|
10071
|
+
try {
|
|
10072
|
+
const budget = params.resolveBudget("extraction_source_tree", 1600);
|
|
10073
|
+
const maxTokens = Math.min(budget.maxTokens, 1600);
|
|
10074
|
+
const startedAt = Date.now();
|
|
10075
|
+
const response = await safeGenerateObject(
|
|
10076
|
+
params.generateObject,
|
|
10077
|
+
{
|
|
10078
|
+
prompt: buildOutlineCleanupPrompt(sourceTree),
|
|
10079
|
+
schema: SourceTreeOrganizationSchema,
|
|
10080
|
+
maxTokens,
|
|
10081
|
+
taskKind: "extraction_source_tree",
|
|
10082
|
+
budgetDiagnostics: { ...budget, maxTokens }
|
|
10083
|
+
},
|
|
10084
|
+
{
|
|
10085
|
+
fallback: { labels: [], groups: [] },
|
|
10086
|
+
log: params.log
|
|
10087
|
+
}
|
|
10088
|
+
);
|
|
10089
|
+
localTrack(response.usage, {
|
|
10090
|
+
taskKind: "extraction_source_tree",
|
|
10091
|
+
label: "source_tree_outline_cleanup",
|
|
10092
|
+
maxTokens,
|
|
10093
|
+
durationMs: Date.now() - startedAt
|
|
10094
|
+
});
|
|
10095
|
+
sourceTree = applyOrganization(sourceTree, response.object);
|
|
10096
|
+
} catch (error) {
|
|
10097
|
+
warnings.push(`Source-tree outline cleanup failed; deterministic tree used (${error instanceof Error ? error.message : String(error)})`);
|
|
10098
|
+
}
|
|
10099
|
+
}
|
|
9912
10100
|
const deterministicProfile = buildDeterministicOperationalProfile({
|
|
9913
10101
|
sourceTree,
|
|
9914
10102
|
sourceSpans
|