@claritylabs/cl-sdk 3.0.17 → 3.0.18
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 +2 -2
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +395 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +395 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -9168,6 +9168,17 @@ var OperationalProfilePromptSchema = z41.object({
|
|
|
9168
9168
|
sourceNodeIds: z41.array(z41.string()).optional(),
|
|
9169
9169
|
sourceSpanIds: z41.array(z41.string()).optional()
|
|
9170
9170
|
});
|
|
9171
|
+
function formatFormHintsForPrompt(forms) {
|
|
9172
|
+
const usable = forms.filter((form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number").slice(0, 120).map((form) => ({
|
|
9173
|
+
title: form.title,
|
|
9174
|
+
formType: form.formType,
|
|
9175
|
+
formNumber: form.formNumber,
|
|
9176
|
+
editionDate: form.editionDate,
|
|
9177
|
+
pageStart: form.pageStart,
|
|
9178
|
+
pageEnd: form.pageEnd
|
|
9179
|
+
}));
|
|
9180
|
+
return usable.length ? JSON.stringify(usable, null, 2) : "[]";
|
|
9181
|
+
}
|
|
9171
9182
|
function cleanText(value, fallback) {
|
|
9172
9183
|
const text = value?.replace(/\s+/g, " ").trim();
|
|
9173
9184
|
return text || fallback;
|
|
@@ -9265,6 +9276,197 @@ function semanticGroupNodeId(documentId, kind, title, childNodeIds) {
|
|
|
9265
9276
|
childNodeIds.join("_").replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 80)
|
|
9266
9277
|
].join(":");
|
|
9267
9278
|
}
|
|
9279
|
+
function spanPageStart(span) {
|
|
9280
|
+
return span.pageStart ?? span.location?.page ?? span.location?.startPage;
|
|
9281
|
+
}
|
|
9282
|
+
function spanPageEnd2(span) {
|
|
9283
|
+
return span.pageEnd ?? span.location?.endPage ?? spanPageStart(span);
|
|
9284
|
+
}
|
|
9285
|
+
function spanSourceUnit(span) {
|
|
9286
|
+
return span.sourceUnit ?? span.metadata?.sourceUnit ?? span.metadata?.elementType;
|
|
9287
|
+
}
|
|
9288
|
+
function formNumberFromText(value) {
|
|
9289
|
+
return cleanText(value, "").match(/\b[A-Z]{2,}(?:-[A-Z0-9]+)+\s+\d{2}\s+\d{2}\b/)?.[0]?.replace(/\s+/g, " ");
|
|
9290
|
+
}
|
|
9291
|
+
function editionDateFromFormNumber(formNumber) {
|
|
9292
|
+
const match = formNumber?.match(/\b(\d{2})\s+(\d{2})$/);
|
|
9293
|
+
return match ? `${match[1]}/${match[2]}` : void 0;
|
|
9294
|
+
}
|
|
9295
|
+
function pageTitleFromText(text, fallback) {
|
|
9296
|
+
const normalized = cleanText(text, fallback);
|
|
9297
|
+
const patterns = [
|
|
9298
|
+
/\bIMPORTANT NOTICE\s+[—-]\s+HOW TO REPORT A CLAIM\b/i,
|
|
9299
|
+
/\bPRIVACY NOTICE TO POLICYHOLDERS\b/i,
|
|
9300
|
+
/\bOFAC ADVISORY NOTICE\b/i,
|
|
9301
|
+
/\bTERRORISM RISK INSURANCE ACT\s*\(TRIA\)\s*DISCLOSURE AND REJECTION\b/i,
|
|
9302
|
+
/\bDECLARATIONS PAGE\b/i,
|
|
9303
|
+
/\bTECHNOLOGY ERRORS?\s*&\s*OMISSIONS AND CYBER LIABILITY INSURANCE POLICY\b/i,
|
|
9304
|
+
/\bTRADE OR ECONOMIC SANCTIONS LIMITATION\b/i,
|
|
9305
|
+
/\bFORMS? AND ENDORSEMENTS\b/i
|
|
9306
|
+
];
|
|
9307
|
+
for (const pattern of patterns) {
|
|
9308
|
+
const match = normalized.match(pattern)?.[0];
|
|
9309
|
+
if (match) return cleanText(match, fallback);
|
|
9310
|
+
}
|
|
9311
|
+
const endorsement = normalized.match(/\bENDORSEMENT\s+(?:NO\.?|NUMBER|#)\s*[A-Z0-9][A-Z0-9.-]*\b/i)?.[0];
|
|
9312
|
+
if (endorsement) return cleanText(endorsement, fallback);
|
|
9313
|
+
const firstSentence = normalized.split(/(?<=\.)\s+/)[0];
|
|
9314
|
+
if (firstSentence && firstSentence.length <= 120) return firstSentence.replace(/[.]$/, "");
|
|
9315
|
+
return fallback;
|
|
9316
|
+
}
|
|
9317
|
+
function pageFormTypeFromText(text) {
|
|
9318
|
+
if (/\b(declarations?\s+page|declarations?\s+schedule)\b/i.test(text)) return "declarations";
|
|
9319
|
+
if (/\b(endorsement\s+(?:no\.?|number|#)|this endorsement changes the policy|[A-Z]{2,}-END\s+\d{2,})\b/i.test(text)) return "endorsement";
|
|
9320
|
+
if (/\b(technology errors?\s*&?\s*omissions.*liability insurance policy|policy form|coverage form|insuring agreement|definitions?|exclusions?|conditions?)\b/i.test(text)) return "coverage";
|
|
9321
|
+
if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions)\b/i.test(text)) return "notice";
|
|
9322
|
+
return "other";
|
|
9323
|
+
}
|
|
9324
|
+
function inferFormHintsFromSourceSpans(sourceSpans) {
|
|
9325
|
+
const pageTexts = /* @__PURE__ */ new Map();
|
|
9326
|
+
const pageSpanTexts = /* @__PURE__ */ new Map();
|
|
9327
|
+
for (const span of sourceSpans) {
|
|
9328
|
+
const start = spanPageStart(span);
|
|
9329
|
+
if (typeof start !== "number") continue;
|
|
9330
|
+
const end = spanPageEnd2(span) ?? start;
|
|
9331
|
+
for (let page = start; page <= end; page += 1) {
|
|
9332
|
+
if (spanSourceUnit(span) === "page") {
|
|
9333
|
+
pageSpanTexts.set(page, cleanText(span.text, ""));
|
|
9334
|
+
continue;
|
|
9335
|
+
}
|
|
9336
|
+
const existing = pageTexts.get(page) ?? "";
|
|
9337
|
+
if (existing.length < 4e3) pageTexts.set(page, cleanText([existing, span.text].filter(Boolean).join(" "), ""));
|
|
9338
|
+
}
|
|
9339
|
+
}
|
|
9340
|
+
if (pageSpanTexts.size === 0) return [];
|
|
9341
|
+
const pageHints = [.../* @__PURE__ */ new Set([...pageTexts.keys(), ...pageSpanTexts.keys()])].sort((left, right) => left - right).map((page) => {
|
|
9342
|
+
const text = pageSpanTexts.get(page) ?? pageTexts.get(page) ?? "";
|
|
9343
|
+
const formNumber = formNumberFromText(text);
|
|
9344
|
+
return {
|
|
9345
|
+
formNumber,
|
|
9346
|
+
editionDate: editionDateFromFormNumber(formNumber),
|
|
9347
|
+
title: pageTitleFromText(text, `Page ${page}`),
|
|
9348
|
+
formType: pageFormTypeFromText(text),
|
|
9349
|
+
pageStart: page,
|
|
9350
|
+
pageEnd: page
|
|
9351
|
+
};
|
|
9352
|
+
});
|
|
9353
|
+
const merged = [];
|
|
9354
|
+
for (const hint of pageHints) {
|
|
9355
|
+
const previous = merged[merged.length - 1];
|
|
9356
|
+
const startsNewEndorsement = hint.formType === "endorsement" && /\bendorsement\s+(?:no\.?|number|#)\s*[A-Z0-9]|this endorsement changes the policy/i.test(hint.title ?? "");
|
|
9357
|
+
const canMerge = previous && previous.formType === hint.formType && previous.pageEnd !== void 0 && hint.pageStart === previous.pageEnd + 1 && (hint.formType === "declarations" || hint.formType === "coverage" || hint.formType === "endorsement" && !startsNewEndorsement);
|
|
9358
|
+
if (!canMerge) {
|
|
9359
|
+
merged.push(hint);
|
|
9360
|
+
continue;
|
|
9361
|
+
}
|
|
9362
|
+
previous.pageEnd = hint.pageEnd;
|
|
9363
|
+
previous.title = previous.title ?? hint.title;
|
|
9364
|
+
previous.formNumber = previous.formNumber ?? hint.formNumber;
|
|
9365
|
+
previous.editionDate = previous.editionDate ?? hint.editionDate;
|
|
9366
|
+
}
|
|
9367
|
+
return merged;
|
|
9368
|
+
}
|
|
9369
|
+
function normalizeFormHints(forms, sourceSpans) {
|
|
9370
|
+
const provided = (forms ?? []).filter(
|
|
9371
|
+
(form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && form.pageStart > 0 && form.pageEnd >= form.pageStart
|
|
9372
|
+
).map((form) => ({
|
|
9373
|
+
...form,
|
|
9374
|
+
title: form.title ? cleanText(form.title, "") : void 0
|
|
9375
|
+
})).sort(
|
|
9376
|
+
(left, right) => (left.pageStart ?? Number.MAX_SAFE_INTEGER) - (right.pageStart ?? Number.MAX_SAFE_INTEGER) || (left.pageEnd ?? Number.MAX_SAFE_INTEGER) - (right.pageEnd ?? Number.MAX_SAFE_INTEGER)
|
|
9377
|
+
);
|
|
9378
|
+
return provided.length ? provided : inferFormHintsFromSourceSpans(sourceSpans);
|
|
9379
|
+
}
|
|
9380
|
+
function formHintForPage(forms, page) {
|
|
9381
|
+
return forms.find(
|
|
9382
|
+
(form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && page >= form.pageStart && page <= form.pageEnd
|
|
9383
|
+
);
|
|
9384
|
+
}
|
|
9385
|
+
function titleFromFormHint(form, fallback) {
|
|
9386
|
+
if (form.formType === "declarations") return "Declarations";
|
|
9387
|
+
if (form.formType === "coverage") return "Policy Form";
|
|
9388
|
+
if (form.formType === "endorsement") return endorsementTitle([form.title, form.formNumber].filter(Boolean).join(" ")) ?? cleanText(form.title, fallback);
|
|
9389
|
+
return cleanText(form.title, fallback);
|
|
9390
|
+
}
|
|
9391
|
+
function formGroupConfig(form) {
|
|
9392
|
+
if (form.formType === "declarations") {
|
|
9393
|
+
return {
|
|
9394
|
+
kind: "page_group",
|
|
9395
|
+
title: "Declarations",
|
|
9396
|
+
description: "Declarations pages and schedules grouped from form inventory",
|
|
9397
|
+
organizer: "form_inventory_declarations_grouping"
|
|
9398
|
+
};
|
|
9399
|
+
}
|
|
9400
|
+
if (form.formType === "coverage") {
|
|
9401
|
+
return {
|
|
9402
|
+
kind: "form",
|
|
9403
|
+
title: "Policy Form",
|
|
9404
|
+
description: "Policy form pages grouped from form inventory",
|
|
9405
|
+
organizer: "form_inventory_policy_form_grouping"
|
|
9406
|
+
};
|
|
9407
|
+
}
|
|
9408
|
+
if (form.formType === "endorsement") {
|
|
9409
|
+
const title = titleFromFormHint(form, "Endorsement");
|
|
9410
|
+
return {
|
|
9411
|
+
kind: "endorsement",
|
|
9412
|
+
title,
|
|
9413
|
+
description: `${title} grouped from form inventory`,
|
|
9414
|
+
organizer: "form_inventory_endorsement_grouping"
|
|
9415
|
+
};
|
|
9416
|
+
}
|
|
9417
|
+
return void 0;
|
|
9418
|
+
}
|
|
9419
|
+
function applyFormInventoryHints(sourceTree, forms) {
|
|
9420
|
+
if (forms.length === 0) return sourceTree;
|
|
9421
|
+
const rootId = sourceTreeRootId(sourceTree);
|
|
9422
|
+
if (!rootId) return sourceTree;
|
|
9423
|
+
const byParent = nodesByParent(sourceTree);
|
|
9424
|
+
const children = (byParent.get(rootId) ?? []).filter((node) => node.kind !== "document").sort((left, right) => left.order - right.order);
|
|
9425
|
+
const rootPages = children.filter((node) => node.kind === "page" && typeof node.pageStart === "number");
|
|
9426
|
+
let nextTree = sourceTree.map((node) => {
|
|
9427
|
+
if (node.kind !== "page" || typeof node.pageStart !== "number") return node;
|
|
9428
|
+
const form = formHintForPage(forms, node.pageStart);
|
|
9429
|
+
if (!form) return node;
|
|
9430
|
+
const isStartPage = form.pageStart === node.pageStart;
|
|
9431
|
+
const shouldRetitle = isStartPage && form.formType !== "other";
|
|
9432
|
+
return {
|
|
9433
|
+
...node,
|
|
9434
|
+
title: shouldRetitle ? titleFromFormHint(form, node.title) : node.title,
|
|
9435
|
+
metadata: {
|
|
9436
|
+
...node.metadata,
|
|
9437
|
+
formInventoryHint: {
|
|
9438
|
+
formType: form.formType,
|
|
9439
|
+
formNumber: form.formNumber,
|
|
9440
|
+
title: form.title,
|
|
9441
|
+
pageStart: form.pageStart,
|
|
9442
|
+
pageEnd: form.pageEnd
|
|
9443
|
+
}
|
|
9444
|
+
}
|
|
9445
|
+
};
|
|
9446
|
+
});
|
|
9447
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
9448
|
+
for (const form of forms) {
|
|
9449
|
+
const config = formGroupConfig(form);
|
|
9450
|
+
const pageStart2 = form.pageStart;
|
|
9451
|
+
const pageEnd2 = form.pageEnd;
|
|
9452
|
+
if (!config || typeof pageStart2 !== "number" || typeof pageEnd2 !== "number") continue;
|
|
9453
|
+
const childIds = rootPages.filter(
|
|
9454
|
+
(page) => !claimed.has(page.id) && typeof page.pageStart === "number" && page.pageStart >= pageStart2 && page.pageStart <= pageEnd2
|
|
9455
|
+
).map((page) => page.id);
|
|
9456
|
+
if (childIds.length === 0) continue;
|
|
9457
|
+
nextTree = groupAdjacentChildren({
|
|
9458
|
+
sourceTree: nextTree,
|
|
9459
|
+
children,
|
|
9460
|
+
childIds,
|
|
9461
|
+
kind: config.kind,
|
|
9462
|
+
title: config.title,
|
|
9463
|
+
description: config.description,
|
|
9464
|
+
organizer: config.organizer
|
|
9465
|
+
});
|
|
9466
|
+
childIds.forEach((id) => claimed.add(id));
|
|
9467
|
+
}
|
|
9468
|
+
return normalizeDocumentSourceTreePaths(nextTree);
|
|
9469
|
+
}
|
|
9268
9470
|
function looksLikeDeclarationsStart(node) {
|
|
9269
9471
|
const title = cleanText(node.title, "");
|
|
9270
9472
|
const text = sourceNodeText(node);
|
|
@@ -9325,6 +9527,21 @@ function groupAdjacentChildren(params) {
|
|
|
9325
9527
|
groupNode
|
|
9326
9528
|
];
|
|
9327
9529
|
}
|
|
9530
|
+
function reparentNodes(sourceTree, childIds, parentId, organizerRepair) {
|
|
9531
|
+
const wanted = new Set(childIds);
|
|
9532
|
+
if (wanted.size === 0) return sourceTree;
|
|
9533
|
+
return sourceTree.map(
|
|
9534
|
+
(node) => wanted.has(node.id) ? {
|
|
9535
|
+
...node,
|
|
9536
|
+
parentId,
|
|
9537
|
+
order: node.order + 1e-3,
|
|
9538
|
+
metadata: {
|
|
9539
|
+
...node.metadata,
|
|
9540
|
+
organizerRepair
|
|
9541
|
+
}
|
|
9542
|
+
} : node
|
|
9543
|
+
);
|
|
9544
|
+
}
|
|
9328
9545
|
function applySemanticPageGrouping(sourceTree) {
|
|
9329
9546
|
const relabeled = sourceTree.map((node) => {
|
|
9330
9547
|
if (node.kind === "document" || node.kind === "page_group") return node;
|
|
@@ -9384,7 +9601,13 @@ function applySemanticPageGrouping(sourceTree) {
|
|
|
9384
9601
|
if (!looksLikeDeclarationsContinuation(child)) break;
|
|
9385
9602
|
declarationIds.push(child.id);
|
|
9386
9603
|
}
|
|
9387
|
-
|
|
9604
|
+
const existingDeclarations = children[declarationsStartIndex];
|
|
9605
|
+
nextTree = isDeclarationsNode(existingDeclarations) ? reparentNodes(
|
|
9606
|
+
nextTree,
|
|
9607
|
+
declarationIds.filter((id) => id !== existingDeclarations.id),
|
|
9608
|
+
existingDeclarations.id,
|
|
9609
|
+
"semantic_declarations_continuation"
|
|
9610
|
+
) : groupAdjacentChildren({
|
|
9388
9611
|
sourceTree: nextTree,
|
|
9389
9612
|
children,
|
|
9390
9613
|
childIds: declarationIds,
|
|
@@ -9408,7 +9631,13 @@ function applySemanticPageGrouping(sourceTree) {
|
|
|
9408
9631
|
if (!looksLikePolicyFormContinuation(child)) break;
|
|
9409
9632
|
policyIds.push(child.id);
|
|
9410
9633
|
}
|
|
9411
|
-
|
|
9634
|
+
const existingPolicyForm = children[policyStartIndex];
|
|
9635
|
+
nextTree = isPolicyFormNode(existingPolicyForm) ? reparentNodes(
|
|
9636
|
+
nextTree,
|
|
9637
|
+
policyIds.filter((id) => id !== existingPolicyForm.id),
|
|
9638
|
+
existingPolicyForm.id,
|
|
9639
|
+
"semantic_policy_form_continuation"
|
|
9640
|
+
) : groupAdjacentChildren({
|
|
9412
9641
|
sourceTree: nextTree,
|
|
9413
9642
|
children,
|
|
9414
9643
|
childIds: policyIds,
|
|
@@ -9605,6 +9834,130 @@ function normalizeContainerEvidenceFromChildren(sourceTree) {
|
|
|
9605
9834
|
}
|
|
9606
9835
|
return sourceTree.map((node) => byId.get(node.id) ?? node);
|
|
9607
9836
|
}
|
|
9837
|
+
function metadataText(node, key) {
|
|
9838
|
+
const value = node.metadata?.[key];
|
|
9839
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
9840
|
+
}
|
|
9841
|
+
function isTitleBlockNode(node) {
|
|
9842
|
+
if (node.kind !== "text") return false;
|
|
9843
|
+
return metadataText(node, "organizer") === "title_block" || metadataText(node, "elementType") === "title" || metadataText(node, "sourceUnit") === "title";
|
|
9844
|
+
}
|
|
9845
|
+
function isRejectableSectionHeading(text, container) {
|
|
9846
|
+
const normalized = cleanText(text, "");
|
|
9847
|
+
if (!normalized) return true;
|
|
9848
|
+
if (normalized.length > 160) return true;
|
|
9849
|
+
if (/^page\s+\d+$/i.test(normalized)) return true;
|
|
9850
|
+
if (/^table\s+\d+$/i.test(normalized)) return true;
|
|
9851
|
+
if (/^(document|text|header row|row\s+\d+)$/i.test(normalized)) return true;
|
|
9852
|
+
if (/^(northwoods continental insurance company|specimen policy|for testing only)$/i.test(normalized)) return true;
|
|
9853
|
+
if (/^technology errors?\s*&\s*omissions and cyber liability insurance policy$/i.test(normalized)) return true;
|
|
9854
|
+
if (/^declarations(?:\s+page)?$/i.test(normalized) && /^declarations$/i.test(container.title)) return true;
|
|
9855
|
+
if (/^policy\s+form$/i.test(normalized) && /^policy\s+form$/i.test(container.title)) return true;
|
|
9856
|
+
if (/^endorsement\s+(?:no\.?|number|#)/i.test(normalized) && container.kind === "endorsement") return true;
|
|
9857
|
+
if (/\b(policyholder|policyholders)\b/i.test(normalized) && normalized.length < 40) return true;
|
|
9858
|
+
return false;
|
|
9859
|
+
}
|
|
9860
|
+
function sectionHeadingTitle(node, container) {
|
|
9861
|
+
if (!isTitleBlockNode(node)) return void 0;
|
|
9862
|
+
const text = cleanText(node.title || node.textExcerpt, "");
|
|
9863
|
+
if (isRejectableSectionHeading(text, container)) return void 0;
|
|
9864
|
+
const words = text.split(/\s+/);
|
|
9865
|
+
if (words.length > 18) return void 0;
|
|
9866
|
+
const structured = /^(SECTION|PART|ARTICLE|SCHEDULE)\b/i.test(text) || /^Item\s+\d+[\.:]/i.test(text) || /^Coverage\s+Part\b/i.test(text) || /^Endorsement\s+(?:No\.?|Number|#)\s+/i.test(text);
|
|
9867
|
+
const uppercaseLetters = [...text].filter((char) => /[A-Z]/.test(char)).length;
|
|
9868
|
+
const lowercaseLetters = [...text].filter((char) => /[a-z]/.test(char)).length;
|
|
9869
|
+
const mostlyUppercase = uppercaseLetters > 0 && uppercaseLetters >= lowercaseLetters * 1.5;
|
|
9870
|
+
const hasSentencePunctuation = /[.;:]\s+\S/.test(text) || /[.;:]$/.test(text);
|
|
9871
|
+
const sentenceLike = /\b(is|are|was|were|will|shall|may|must|means|includes|provided|subject|available|attached|remain|constitutes)\b/i.test(text) && /[a-z]/.test(text);
|
|
9872
|
+
if (!structured && (!mostlyUppercase || hasSentencePunctuation || sentenceLike)) return void 0;
|
|
9873
|
+
return simplifyOrganizerTitle(text, text, node.kind);
|
|
9874
|
+
}
|
|
9875
|
+
function sectionKindForTitle(title) {
|
|
9876
|
+
if (/^schedule\b/i.test(title) || /\b(forms? and endorsements?|coverage parts?|limits?|premium|declarations?)\b/i.test(title)) return "schedule";
|
|
9877
|
+
if (/^(section|part|article|item)\b/i.test(title)) return "section";
|
|
9878
|
+
return "section";
|
|
9879
|
+
}
|
|
9880
|
+
function hasAncestor(node, ancestorId, byId) {
|
|
9881
|
+
let parentId = node.parentId;
|
|
9882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9883
|
+
while (parentId) {
|
|
9884
|
+
if (parentId === ancestorId) return true;
|
|
9885
|
+
if (seen.has(parentId)) return false;
|
|
9886
|
+
seen.add(parentId);
|
|
9887
|
+
parentId = byId.get(parentId)?.parentId;
|
|
9888
|
+
}
|
|
9889
|
+
return false;
|
|
9890
|
+
}
|
|
9891
|
+
function shouldBuildSectionsForContainer(node) {
|
|
9892
|
+
if (isNoticesGroup(node)) return false;
|
|
9893
|
+
if (isEndorsementGroup(node)) return false;
|
|
9894
|
+
return node.kind === "form" || node.kind === "page_group" || node.kind === "endorsement";
|
|
9895
|
+
}
|
|
9896
|
+
function applyTitleSectionHierarchy(sourceTree) {
|
|
9897
|
+
const byId = new Map(sourceTree.map((node) => [node.id, node]));
|
|
9898
|
+
const byParent = nodesByParent(sourceTree);
|
|
9899
|
+
const updates = /* @__PURE__ */ new Map();
|
|
9900
|
+
for (const container of sourceTree.filter(shouldBuildSectionsForContainer)) {
|
|
9901
|
+
const pageIds = new Set(
|
|
9902
|
+
sourceTree.filter((node) => node.kind === "page" && hasAncestor(node, container.id, byId)).map((node) => node.id)
|
|
9903
|
+
);
|
|
9904
|
+
if (pageIds.size === 0) continue;
|
|
9905
|
+
const directPageChildren = sourceTree.filter(
|
|
9906
|
+
(node) => node.parentId !== void 0 && pageIds.has(node.parentId) && node.kind !== "table_row" && node.kind !== "table_cell"
|
|
9907
|
+
).sort(
|
|
9908
|
+
(left, right) => (left.pageStart ?? Number.MAX_SAFE_INTEGER) - (right.pageStart ?? Number.MAX_SAFE_INTEGER) || left.order - right.order || left.id.localeCompare(right.id)
|
|
9909
|
+
);
|
|
9910
|
+
if (directPageChildren.length === 0) continue;
|
|
9911
|
+
let currentSectionId;
|
|
9912
|
+
for (let index = 0; index < directPageChildren.length; index += 1) {
|
|
9913
|
+
const child = directPageChildren[index];
|
|
9914
|
+
const current = updates.get(child.id) ?? child;
|
|
9915
|
+
const heading = sectionHeadingTitle(current, container);
|
|
9916
|
+
if (heading) {
|
|
9917
|
+
const descendants = byParent.get(child.id) ?? [];
|
|
9918
|
+
const hasOwnContent = descendants.some((descendant) => descendant.kind !== "table_row" && descendant.kind !== "table_cell");
|
|
9919
|
+
let hasFollowingContent = false;
|
|
9920
|
+
for (const nextChild of directPageChildren.slice(index + 1)) {
|
|
9921
|
+
const next = updates.get(nextChild.id) ?? nextChild;
|
|
9922
|
+
if (sectionHeadingTitle(next, container)) break;
|
|
9923
|
+
hasFollowingContent = true;
|
|
9924
|
+
break;
|
|
9925
|
+
}
|
|
9926
|
+
if (!hasOwnContent && !hasFollowingContent) {
|
|
9927
|
+
currentSectionId = void 0;
|
|
9928
|
+
continue;
|
|
9929
|
+
}
|
|
9930
|
+
currentSectionId = child.id;
|
|
9931
|
+
updates.set(child.id, {
|
|
9932
|
+
...current,
|
|
9933
|
+
parentId: container.id,
|
|
9934
|
+
kind: sectionKindForTitle(heading),
|
|
9935
|
+
title: heading,
|
|
9936
|
+
description: descriptionWithPages(cleanText([heading, "section"].join(" "), heading), [current, ...descendants]),
|
|
9937
|
+
metadata: {
|
|
9938
|
+
...current.metadata,
|
|
9939
|
+
organizer: "title_section",
|
|
9940
|
+
sourceTreeVersion: "v3"
|
|
9941
|
+
}
|
|
9942
|
+
});
|
|
9943
|
+
continue;
|
|
9944
|
+
}
|
|
9945
|
+
if (!currentSectionId) continue;
|
|
9946
|
+
const parent = current.parentId ? byId.get(current.parentId) : void 0;
|
|
9947
|
+
if (!parent || parent.kind !== "page") continue;
|
|
9948
|
+
updates.set(child.id, {
|
|
9949
|
+
...current,
|
|
9950
|
+
parentId: currentSectionId,
|
|
9951
|
+
metadata: {
|
|
9952
|
+
...current.metadata,
|
|
9953
|
+
organizerRepair: "title_section_continuation"
|
|
9954
|
+
}
|
|
9955
|
+
});
|
|
9956
|
+
}
|
|
9957
|
+
}
|
|
9958
|
+
if (updates.size === 0) return sourceTree;
|
|
9959
|
+
return normalizeDocumentSourceTreePaths(sourceTree.map((node) => updates.get(node.id) ?? node));
|
|
9960
|
+
}
|
|
9608
9961
|
function normalizeSemanticHierarchy(sourceTree) {
|
|
9609
9962
|
const normalized = normalizeDocumentSourceTreePaths(
|
|
9610
9963
|
normalizePolicyFormStructure(
|
|
@@ -9613,7 +9966,8 @@ function normalizeSemanticHierarchy(sourceTree) {
|
|
|
9613
9966
|
);
|
|
9614
9967
|
const nested = normalizeDocumentSourceTreePaths(nestEndorsementContinuationPages(normalized));
|
|
9615
9968
|
const mergedNotices = normalizeDocumentSourceTreePaths(mergeAdministrativeNoticesIntoFrontMatter(nested));
|
|
9616
|
-
const
|
|
9969
|
+
const sectioned = normalizeDocumentSourceTreePaths(applyTitleSectionHierarchy(mergedNotices));
|
|
9970
|
+
const withEvidence = normalizeContainerEvidenceFromChildren(sectioned);
|
|
9617
9971
|
return normalizeDocumentSourceTreePaths(
|
|
9618
9972
|
normalizeRootSemanticOrder(normalizeContainerEvidenceFromChildren(withEvidence))
|
|
9619
9973
|
);
|
|
@@ -9832,7 +10186,7 @@ function mergeOrganizationResults(results) {
|
|
|
9832
10186
|
groups: [...groups.values()]
|
|
9833
10187
|
};
|
|
9834
10188
|
}
|
|
9835
|
-
function buildOrganizationPrompt(batch) {
|
|
10189
|
+
function buildOrganizationPrompt(batch, formHints) {
|
|
9836
10190
|
const nodes = batch.nodes.map((node) => compactNode(node, node.kind === "page" ? 900 : 320));
|
|
9837
10191
|
return `You organize an insurance document source tree.
|
|
9838
10192
|
|
|
@@ -9841,10 +10195,15 @@ Scope:
|
|
|
9841
10195
|
- The provided list is a bounded extraction-time batch. It is not necessarily the whole document.
|
|
9842
10196
|
- Top-level page/form candidates in this batch: ${JSON.stringify(batch.topLevelNodeIds)}
|
|
9843
10197
|
|
|
10198
|
+
Expected form inventory / page ranges:
|
|
10199
|
+
${formatFormHintsForPrompt(formHints)}
|
|
10200
|
+
|
|
9844
10201
|
Rules:
|
|
9845
10202
|
- Use only node IDs from the provided list.
|
|
9846
10203
|
- Do not invent text, page numbers, source spans, limits, or policy facts.
|
|
9847
10204
|
- You may relabel existing nodes and group adjacent top-level/page nodes from this batch only when they are clearly one continuous form, one declarations set, one schedule, or one clause family.
|
|
10205
|
+
- Treat the form inventory as a page-range hint for the expected order: front matter/notices, declarations, policy form, then endorsements.
|
|
10206
|
+
- Prefer section hierarchy from printed title elements inside a form over page-by-page grouping.
|
|
9848
10207
|
- Group adjacent separately numbered endorsements under a single generic "Endorsements" page_group parent, with each individual endorsement preserved as its own child node.
|
|
9849
10208
|
- Never create rollup titles such as "Endorsements 1-3 (...)" or merge multiple endorsements into one endorsement node.
|
|
9850
10209
|
- Add concise, human-readable titles to generic text, table, row, and cell nodes when the text makes their role clear.
|
|
@@ -9868,7 +10227,7 @@ function shouldRunOutlineCleanup(sourceTree) {
|
|
|
9868
10227
|
if (hasDeclarations && hasPolicyForm && hasEndorsements) return false;
|
|
9869
10228
|
return genericPages.length > 0 || topLevel.length > 6 || !hasDeclarations || !hasPolicyForm || !hasEndorsements;
|
|
9870
10229
|
}
|
|
9871
|
-
function buildOutlineCleanupPrompt(sourceTree) {
|
|
10230
|
+
function buildOutlineCleanupPrompt(sourceTree, formHints) {
|
|
9872
10231
|
const topLevel = rootChildren(sourceTree).slice(0, OUTLINE_CLEANUP_MAX_TOP_LEVEL_NODES);
|
|
9873
10232
|
const nodes = topLevel.map((node) => compactNode(node, 900));
|
|
9874
10233
|
return `You clean a top-level source outline for an insurance policy.
|
|
@@ -9879,6 +10238,9 @@ Expected product-facing order:
|
|
|
9879
10238
|
3. Policy Form: the main policy wording, insuring agreements, definitions, exclusions, conditions, claim provisions, and general policy terms.
|
|
9880
10239
|
4. Endorsements: one generic "Endorsements" page_group containing each separately numbered endorsement as its own child.
|
|
9881
10240
|
|
|
10241
|
+
Expected form inventory / page ranges:
|
|
10242
|
+
${formatFormHintsForPrompt(formHints)}
|
|
10243
|
+
|
|
9882
10244
|
Rules:
|
|
9883
10245
|
- Use only node IDs from this top-level list: ${JSON.stringify(topLevel.map((node) => node.id))}
|
|
9884
10246
|
- Group only adjacent top-level nodes.
|
|
@@ -9887,6 +10249,7 @@ Rules:
|
|
|
9887
10249
|
- Use canonical terse titles: "Notices and Jacket", "Declarations", "Policy Form", "Endorsements", or the printed endorsement number.
|
|
9888
10250
|
- Keep page_group descriptions short and include the page range when pages are known, for example "Declarations pages 6-8".
|
|
9889
10251
|
- 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.
|
|
10252
|
+
- If the form inventory provides page ranges, keep groups aligned to those ranges unless the source node text clearly contradicts them.
|
|
9890
10253
|
- If the existing deterministic outline is already correct, return empty labels and groups.
|
|
9891
10254
|
|
|
9892
10255
|
Top-level source nodes:
|
|
@@ -10061,6 +10424,14 @@ function materializeDocument(params) {
|
|
|
10061
10424
|
insuredName,
|
|
10062
10425
|
premium,
|
|
10063
10426
|
policyTypes: profile.policyTypes,
|
|
10427
|
+
formInventory: params.formInventory.filter((form) => typeof form.formNumber === "string" && form.formNumber.trim().length > 0).map((form) => ({
|
|
10428
|
+
formNumber: form.formNumber,
|
|
10429
|
+
editionDate: form.editionDate,
|
|
10430
|
+
title: form.title,
|
|
10431
|
+
formType: form.formType,
|
|
10432
|
+
pageStart: form.pageStart,
|
|
10433
|
+
pageEnd: form.pageEnd
|
|
10434
|
+
})),
|
|
10064
10435
|
coverages,
|
|
10065
10436
|
documentMetadata,
|
|
10066
10437
|
documentOutline,
|
|
@@ -10101,7 +10472,8 @@ function materializeDocument(params) {
|
|
|
10101
10472
|
}
|
|
10102
10473
|
async function runSourceTreeExtraction(params) {
|
|
10103
10474
|
const sourceSpans = normalizeSourceSpans(params.sourceSpans);
|
|
10104
|
-
|
|
10475
|
+
const formHints = normalizeFormHints(params.formInventory?.forms, sourceSpans);
|
|
10476
|
+
let sourceTree = applySemanticPageGrouping(applyFormInventoryHints(buildDocumentSourceTree(sourceSpans, params.id), formHints));
|
|
10105
10477
|
const warnings = [];
|
|
10106
10478
|
let modelCalls = 0;
|
|
10107
10479
|
let callsWithUsage = 0;
|
|
@@ -10133,7 +10505,7 @@ async function runSourceTreeExtraction(params) {
|
|
|
10133
10505
|
const response = await safeGenerateObject(
|
|
10134
10506
|
params.generateObject,
|
|
10135
10507
|
{
|
|
10136
|
-
prompt: buildOrganizationPrompt(batch),
|
|
10508
|
+
prompt: buildOrganizationPrompt(batch, formHints),
|
|
10137
10509
|
schema: SourceTreeOrganizationSchema,
|
|
10138
10510
|
maxTokens: budget.maxTokens,
|
|
10139
10511
|
taskKind: "extraction_source_tree",
|
|
@@ -10167,7 +10539,7 @@ async function runSourceTreeExtraction(params) {
|
|
|
10167
10539
|
const response = await safeGenerateObject(
|
|
10168
10540
|
params.generateObject,
|
|
10169
10541
|
{
|
|
10170
|
-
prompt: buildOutlineCleanupPrompt(sourceTree),
|
|
10542
|
+
prompt: buildOutlineCleanupPrompt(sourceTree, formHints),
|
|
10171
10543
|
schema: SourceTreeOrganizationSchema,
|
|
10172
10544
|
maxTokens,
|
|
10173
10545
|
taskKind: "extraction_source_tree",
|
|
@@ -10232,12 +10604,14 @@ async function runSourceTreeExtraction(params) {
|
|
|
10232
10604
|
const document = materializeDocument({
|
|
10233
10605
|
id: params.id,
|
|
10234
10606
|
sourceTree,
|
|
10607
|
+
formInventory: formHints,
|
|
10235
10608
|
operationalProfile
|
|
10236
10609
|
});
|
|
10237
10610
|
return {
|
|
10238
10611
|
sourceTree,
|
|
10239
10612
|
sourceSpans,
|
|
10240
10613
|
sourceChunks: chunkSourceSpans(sourceSpans),
|
|
10614
|
+
formInventory: formHints,
|
|
10241
10615
|
operationalProfile,
|
|
10242
10616
|
document,
|
|
10243
10617
|
chunks: [],
|
|
@@ -10591,12 +10965,24 @@ ${span.text}` : span.text;
|
|
|
10591
10965
|
const v3 = await runSourceTreeExtraction({
|
|
10592
10966
|
id,
|
|
10593
10967
|
sourceSpans,
|
|
10968
|
+
formInventory: options?.formInventory,
|
|
10594
10969
|
generateObject,
|
|
10595
10970
|
providerOptions: activeProviderOptions,
|
|
10596
10971
|
resolveBudget,
|
|
10597
10972
|
trackUsage,
|
|
10598
10973
|
log
|
|
10599
10974
|
});
|
|
10975
|
+
const sourceTreeFormInventory = v3.formInventory.flatMap((form) => {
|
|
10976
|
+
const formNumber = typeof form.formNumber === "string" ? form.formNumber.trim() : "";
|
|
10977
|
+
if (!formNumber) return [];
|
|
10978
|
+
return [{
|
|
10979
|
+
formNumber,
|
|
10980
|
+
title: form.title,
|
|
10981
|
+
pageStart: form.pageStart,
|
|
10982
|
+
pageEnd: form.pageEnd,
|
|
10983
|
+
sources: ["source_tree"]
|
|
10984
|
+
}];
|
|
10985
|
+
});
|
|
10600
10986
|
const reviewReport2 = {
|
|
10601
10987
|
issues: v3.warnings.map((warning) => ({
|
|
10602
10988
|
code: "source_tree_warning",
|
|
@@ -10610,7 +10996,7 @@ ${span.text}` : span.text;
|
|
|
10610
10996
|
{ kind: "operational_profile", label: "Operational Profile", itemCount: v3.operationalProfile.coverages.length }
|
|
10611
10997
|
],
|
|
10612
10998
|
reviewRoundRecords: [],
|
|
10613
|
-
formInventory:
|
|
10999
|
+
formInventory: sourceTreeFormInventory,
|
|
10614
11000
|
qualityGateStatus: v3.warnings.length > 0 ? "warning" : "passed"
|
|
10615
11001
|
};
|
|
10616
11002
|
if (shouldFailQualityGate(qualityGate, reviewReport2.qualityGateStatus)) {
|