@claritylabs/cl-sdk 3.1.20 → 3.1.21
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 +1 -1
- package/dist/application.d.mts +1 -1
- package/dist/application.d.ts +1 -1
- package/dist/application.js +0 -2
- package/dist/application.js.map +1 -1
- package/dist/application.mjs +0 -2
- package/dist/application.mjs.map +1 -1
- package/dist/{index-DUBsVEev.d.mts → index-B2T7Jvir.d.mts} +1 -12
- package/dist/{index-DUBsVEev.d.ts → index-B2T7Jvir.d.ts} +1 -12
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +119 -884
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +119 -884
- package/dist/index.mjs.map +1 -1
- package/dist/storage-sqlite.js +0 -2
- package/dist/storage-sqlite.js.map +1 -1
- package/dist/storage-sqlite.mjs +0 -2
- package/dist/storage-sqlite.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2102,7 +2102,6 @@ var OperationalCoverageLineSchema = z20.object({
|
|
|
2102
2102
|
retroactiveDate: z20.string().optional(),
|
|
2103
2103
|
formNumber: z20.string().optional(),
|
|
2104
2104
|
sectionRef: z20.string().optional(),
|
|
2105
|
-
coverageOrigin: z20.enum(["core", "endorsement"]).optional(),
|
|
2106
2105
|
endorsementNumber: z20.string().optional(),
|
|
2107
2106
|
limits: z20.array(OperationalCoverageTermSchema).default([]),
|
|
2108
2107
|
sourceNodeIds: z20.array(z20.string().min(1)).default([]),
|
|
@@ -2132,7 +2131,6 @@ var PolicyOperationalProfileSchema = z20.object({
|
|
|
2132
2131
|
expirationDate: SourceBackedValueSchema.optional(),
|
|
2133
2132
|
retroactiveDate: SourceBackedValueSchema.optional(),
|
|
2134
2133
|
premium: SourceBackedValueSchema.optional(),
|
|
2135
|
-
coverageTypes: z20.array(z20.string()).default([]),
|
|
2136
2134
|
coverages: z20.array(OperationalCoverageLineSchema).default([]),
|
|
2137
2135
|
parties: z20.array(OperationalPartySchema).default([]),
|
|
2138
2136
|
endorsementSupport: z20.array(OperationalEndorsementSupportSchema).default([]),
|
|
@@ -3257,9 +3255,9 @@ function normalizeDoclingDocument(document, options) {
|
|
|
3257
3255
|
pageTexts.set(page, appendText(pageTexts.get(page), unit.text));
|
|
3258
3256
|
}
|
|
3259
3257
|
const fullText = Array.from({ length: pageCount }, (_, index) => {
|
|
3260
|
-
const
|
|
3261
|
-
const text = pageTexts.get(
|
|
3262
|
-
return text ? `Page ${
|
|
3258
|
+
const pageNumber = index + 1;
|
|
3259
|
+
const text = pageTexts.get(pageNumber)?.trim();
|
|
3260
|
+
return text ? `Page ${pageNumber}
|
|
3263
3261
|
${text}` : "";
|
|
3264
3262
|
}).filter(Boolean).join("\n\n");
|
|
3265
3263
|
const sourceKind = options.sourceKind ?? "policy_pdf";
|
|
@@ -5588,7 +5586,7 @@ function mergeCoverageLimits(existing, incoming) {
|
|
|
5588
5586
|
const merged = mergeShallowPreferPresent(existing, incoming);
|
|
5589
5587
|
const existingCoverages = Array.isArray(existing.coverages) ? existing.coverages : [];
|
|
5590
5588
|
const incomingCoverages = Array.isArray(incoming.coverages) ? incoming.coverages : [];
|
|
5591
|
-
const
|
|
5589
|
+
const coverageKey = (coverage) => keyFromParts(
|
|
5592
5590
|
coverage.name,
|
|
5593
5591
|
coverage.limitType,
|
|
5594
5592
|
coverage.limit,
|
|
@@ -5597,7 +5595,7 @@ function mergeCoverageLimits(existing, incoming) {
|
|
|
5597
5595
|
);
|
|
5598
5596
|
const byKey = /* @__PURE__ */ new Map();
|
|
5599
5597
|
for (const coverage of [...existingCoverages, ...incomingCoverages]) {
|
|
5600
|
-
const key =
|
|
5598
|
+
const key = coverageKey(coverage);
|
|
5601
5599
|
const current = byKey.get(key);
|
|
5602
5600
|
byKey.set(key, current ? mergeShallowPreferPresent(current, coverage) : coverage);
|
|
5603
5601
|
}
|
|
@@ -6570,8 +6568,11 @@ For EACH form, extract:
|
|
|
6570
6568
|
|
|
6571
6569
|
Critical rules:
|
|
6572
6570
|
- Include declarations page sets even if they do not show a standard form number.
|
|
6573
|
-
- Do not classify policy jackets, claim-reporting notices, privacy notices, OFAC notices, terrorism/TRIA notices, sanctions notices, signatures, countersignatures, or marketing/admin pages as declarations.
|
|
6571
|
+
- Do not classify policy jackets, claim-reporting notices, privacy notices, OFAC notices, terrorism/TRIA notices, sanctions notices, signatures, countersignatures, or marketing/admin pages as declarations when they are only administrative content.
|
|
6574
6572
|
- Declarations pages contain policy-specific schedules such as named insured, policy number, policy period, premium, limits, retentions, coverage parts, or forms-and-endorsements schedules.
|
|
6573
|
+
- Declarations pages may be internally itemized as "Item 1.", "Item 2.", etc. Keep the whole itemized declarations set together, including continuation pages for coverage schedules, premium, ERP options, producer information, and forms or endorsements attached at inception.
|
|
6574
|
+
- A page that lists declaration items or form/endorsement schedules remains part of the declarations set even if the bottom of that page also contains countersignature, authorized-representative, or policy-execution language.
|
|
6575
|
+
- When "Forms and Endorsements Attached at Inception" or similar form schedules continue onto the next page, extend the declarations pageEnd through that continuation instead of treating the continuation as a standalone signature page.
|
|
6575
6576
|
- Use original document page numbers, not local chunk page numbers.
|
|
6576
6577
|
- Do not emit duplicate entries for repeated headers/footers.
|
|
6577
6578
|
- Multi-page forms should be represented once with pageStart/pageEnd covering the full span when visible.
|
|
@@ -7048,7 +7049,7 @@ var ExclusionsSchema = z30.object({
|
|
|
7048
7049
|
exceptions: z30.array(z30.string()).optional().describe("Exceptions to the exclusion, if any"),
|
|
7049
7050
|
buybackAvailable: z30.boolean().optional().describe("Whether coverage can be bought back via endorsement"),
|
|
7050
7051
|
buybackEndorsement: z30.string().optional().describe("Form number of the buyback endorsement if available"),
|
|
7051
|
-
appliesTo: z30.array(z30.string()).optional().describe("
|
|
7052
|
+
appliesTo: z30.array(z30.string()).optional().describe("Policy types this exclusion applies to"),
|
|
7052
7053
|
content: z30.string().describe("Full verbatim exclusion text"),
|
|
7053
7054
|
pageNumber: z30.number().optional().describe("Page number where exclusion appears")
|
|
7054
7055
|
})
|
|
@@ -7065,7 +7066,7 @@ For EACH exclusion, extract:
|
|
|
7065
7066
|
- exceptions: any exceptions to the exclusion (things still covered despite the exclusion)
|
|
7066
7067
|
- buybackAvailable: whether coverage can be purchased back via endorsement
|
|
7067
7068
|
- buybackEndorsement: the form number of the buyback endorsement if known
|
|
7068
|
-
- appliesTo: which
|
|
7069
|
+
- appliesTo: which policy types or lines this exclusion applies to (as an array)
|
|
7069
7070
|
- content: full verbatim exclusion text \u2014 REQUIRED
|
|
7070
7071
|
- pageNumber: page number where the exclusion appears
|
|
7071
7072
|
|
|
@@ -7929,190 +7930,6 @@ async function resolveReferentialCoverages(params) {
|
|
|
7929
7930
|
};
|
|
7930
7931
|
}
|
|
7931
7932
|
|
|
7932
|
-
// src/extraction/coverage-schedule-recovery.ts
|
|
7933
|
-
function textValue(value) {
|
|
7934
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
7935
|
-
}
|
|
7936
|
-
function numberValue2(value) {
|
|
7937
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
7938
|
-
}
|
|
7939
|
-
function normalize(value) {
|
|
7940
|
-
return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, " ").trim();
|
|
7941
|
-
}
|
|
7942
|
-
function sourceUnit5(span) {
|
|
7943
|
-
return span.sourceUnit ?? span.metadata?.sourceUnit;
|
|
7944
|
-
}
|
|
7945
|
-
function pageNumber(span) {
|
|
7946
|
-
return span.pageStart ?? span.location?.page ?? span.location?.startPage;
|
|
7947
|
-
}
|
|
7948
|
-
function coveragePages(pageAssignments) {
|
|
7949
|
-
const pages = /* @__PURE__ */ new Set();
|
|
7950
|
-
for (const assignment of pageAssignments) {
|
|
7951
|
-
if (assignment.extractorNames.includes("coverage_limits") || assignment.hasScheduleValues) {
|
|
7952
|
-
pages.add(assignment.localPageNumber);
|
|
7953
|
-
}
|
|
7954
|
-
}
|
|
7955
|
-
return pages;
|
|
7956
|
-
}
|
|
7957
|
-
function parseCurrencyAmount(value) {
|
|
7958
|
-
const match = value.match(/(?:CAD|USD|US)?\s*\$?\s*([0-9][0-9,]*(?:\.\d+)?)/i);
|
|
7959
|
-
if (!match) return void 0;
|
|
7960
|
-
const amount = Number(match[1].replace(/,/g, ""));
|
|
7961
|
-
return Number.isFinite(amount) ? amount : void 0;
|
|
7962
|
-
}
|
|
7963
|
-
function limitTypeFrom(value) {
|
|
7964
|
-
const normalized = normalize(value);
|
|
7965
|
-
if (normalized.includes("/") || normalized.includes("each claim") && normalized.includes("aggregate")) return "scheduled";
|
|
7966
|
-
if (normalized.includes("each claim") || normalized.includes("per claim")) return "per_claim";
|
|
7967
|
-
if (normalized.includes("each occurrence") || normalized.includes("per occurrence")) return "per_occurrence";
|
|
7968
|
-
if (normalized.includes("aggregate")) return "aggregate";
|
|
7969
|
-
if (normalized.includes("shared within") || normalized.includes("within coverage")) return "scheduled";
|
|
7970
|
-
if (normalized.includes("/")) return "scheduled";
|
|
7971
|
-
return void 0;
|
|
7972
|
-
}
|
|
7973
|
-
function limitValueTypeFrom(value) {
|
|
7974
|
-
const normalized = normalize(value);
|
|
7975
|
-
if (parseCurrencyAmount(value) !== void 0) return "numeric";
|
|
7976
|
-
if (normalized.includes("shared within") || normalized.includes("within coverage") || normalized.includes("shown above")) {
|
|
7977
|
-
return "referential";
|
|
7978
|
-
}
|
|
7979
|
-
if (normalized.includes("included")) return "included";
|
|
7980
|
-
if (normalized.includes("not included") || normalized === "nil" || normalized === "none") return "not_included";
|
|
7981
|
-
if (normalized.includes("as stated")) return "as_stated";
|
|
7982
|
-
return "other";
|
|
7983
|
-
}
|
|
7984
|
-
function cleanName(value) {
|
|
7985
|
-
return value.replace(/\s*\([^)]*part of and not in addition to[^)]*\)\s*/gi, " ").replace(/\s+/g, " ").trim().replace(/[.:;-]+$/, "").trim();
|
|
7986
|
-
}
|
|
7987
|
-
function isDeductibleOnly(name, rowText) {
|
|
7988
|
-
const normalizedName = normalize(name);
|
|
7989
|
-
const normalizedRow = normalize(rowText);
|
|
7990
|
-
if (!/\b(deductible|retention|sir)\b/.test(`${normalizedName} ${normalizedRow}`)) return false;
|
|
7991
|
-
if (/\b(coverage|sub limit|sublimit|limit)\b/.test(normalizedName) && !/\b(enhanced|standard)\s+deductible\b/.test(normalizedName)) {
|
|
7992
|
-
return false;
|
|
7993
|
-
}
|
|
7994
|
-
return /\b(enhanced|standard)?\s*(deductible|retention|sir)\b/.test(normalizedName) || /^\s*(deductible|retention|sir)\b/.test(normalizedRow);
|
|
7995
|
-
}
|
|
7996
|
-
function splitRowFields(rowText) {
|
|
7997
|
-
return rowText.split(/\s+\|\s+/).map((part) => part.trim()).filter(Boolean).map((part) => {
|
|
7998
|
-
const match = part.match(/^([^:]{1,100}):\s*(.*)$/);
|
|
7999
|
-
if (!match) return { value: part };
|
|
8000
|
-
return { key: match[1].trim(), value: match[2].trim() };
|
|
8001
|
-
});
|
|
8002
|
-
}
|
|
8003
|
-
function firstField(fields, patterns) {
|
|
8004
|
-
for (const field of fields) {
|
|
8005
|
-
const target = `${field.key ?? ""} ${field.value}`;
|
|
8006
|
-
if (patterns.some((pattern) => pattern.test(target))) return field.value || field.key;
|
|
8007
|
-
}
|
|
8008
|
-
return void 0;
|
|
8009
|
-
}
|
|
8010
|
-
function coverageFromRow(span) {
|
|
8011
|
-
const rowText = span.text.trim();
|
|
8012
|
-
if (!rowText || sourceUnit5(span) !== "table_row") return void 0;
|
|
8013
|
-
if (span.table?.isHeader || span.metadata?.isHeader === "true") return void 0;
|
|
8014
|
-
const fields = splitRowFields(rowText);
|
|
8015
|
-
let name;
|
|
8016
|
-
let limit;
|
|
8017
|
-
for (const field of fields) {
|
|
8018
|
-
const key = field.key?.trim();
|
|
8019
|
-
const value = field.value.trim();
|
|
8020
|
-
if (!key || !value) continue;
|
|
8021
|
-
if (!name && /^coverage$/i.test(key)) name = cleanName(value);
|
|
8022
|
-
if (!limit && /\blimit\b/i.test(key)) limit = value;
|
|
8023
|
-
if (!name && /\b(sub[-\s]?limit|aggregate|each claim)\b/i.test(key) && parseCurrencyAmount(value) !== void 0) {
|
|
8024
|
-
name = cleanName(key);
|
|
8025
|
-
limit = limit ?? value;
|
|
8026
|
-
}
|
|
8027
|
-
}
|
|
8028
|
-
if (!name || !limit) {
|
|
8029
|
-
for (const field of fields) {
|
|
8030
|
-
const key = field.key?.trim();
|
|
8031
|
-
const value = field.value.trim();
|
|
8032
|
-
if (!key || !value) continue;
|
|
8033
|
-
if (!name && /\b(sub[-\s]?limit|coverage|aggregate|each claim)\b/i.test(key) && !parseCurrencyAmount(key)) {
|
|
8034
|
-
name = cleanName(key);
|
|
8035
|
-
limit = limit ?? value;
|
|
8036
|
-
}
|
|
8037
|
-
}
|
|
8038
|
-
}
|
|
8039
|
-
if (!name || !limit) return void 0;
|
|
8040
|
-
if (isDeductibleOnly(name, rowText)) return void 0;
|
|
8041
|
-
const normalizedLimit = normalize(limit);
|
|
8042
|
-
const hasUsableLimit = parseCurrencyAmount(limit) !== void 0 || /\b(shared within|within coverage|as stated|included|not included)\b/i.test(normalizedLimit);
|
|
8043
|
-
if (!hasUsableLimit) return void 0;
|
|
8044
|
-
const deductible = firstField(fields, [/\bdeductible\b/i, /\bretention\b/i, /\bsir\b/i]);
|
|
8045
|
-
const basis = firstField(fields, [/\bbasis\b/i]);
|
|
8046
|
-
const retroactiveDate = firstField(fields, [/\bretroactive date\b/i, /\bretro date\b/i]);
|
|
8047
|
-
const page = pageNumber(span);
|
|
8048
|
-
const limitAmount = parseCurrencyAmount(limit);
|
|
8049
|
-
const deductibleAmount = deductible ? parseCurrencyAmount(deductible) : void 0;
|
|
8050
|
-
return {
|
|
8051
|
-
name,
|
|
8052
|
-
limit,
|
|
8053
|
-
...limitAmount !== void 0 ? { limitAmount } : {},
|
|
8054
|
-
...limitTypeFrom(`${name} ${limit}`) ? { limitType: limitTypeFrom(`${name} ${limit}`) } : {},
|
|
8055
|
-
limitValueType: limitValueTypeFrom(limit),
|
|
8056
|
-
...deductible && !/^nil|none$/i.test(deductible.trim()) ? { deductible } : {},
|
|
8057
|
-
...deductibleAmount !== void 0 ? { deductibleAmount } : {},
|
|
8058
|
-
...deductible ? { deductibleValueType: limitValueTypeFrom(deductible) } : {},
|
|
8059
|
-
...basis && /claims[- ]made/i.test(basis) ? { trigger: "claims_made" } : {},
|
|
8060
|
-
...retroactiveDate ? { retroactiveDate } : {},
|
|
8061
|
-
...span.formNumber ? { formNumber: span.formNumber } : {},
|
|
8062
|
-
...page ? { pageNumber: page } : {},
|
|
8063
|
-
sectionRef: span.sectionId ?? "SCHEDULE",
|
|
8064
|
-
originalContent: rowText,
|
|
8065
|
-
sourceSpanIds: [span.id],
|
|
8066
|
-
sourceTextHash: span.textHash ?? span.hash
|
|
8067
|
-
};
|
|
8068
|
-
}
|
|
8069
|
-
function coverageKey(coverage) {
|
|
8070
|
-
return [
|
|
8071
|
-
textValue(coverage.name) ?? "",
|
|
8072
|
-
textValue(coverage.limit) ?? "",
|
|
8073
|
-
textValue(coverage.limitType) ?? "",
|
|
8074
|
-
numberValue2(coverage.pageNumber) ?? ""
|
|
8075
|
-
].map((part) => normalize(String(part))).join("|");
|
|
8076
|
-
}
|
|
8077
|
-
function rowMatchesExisting(row, existing) {
|
|
8078
|
-
const rowKey = coverageKey(row);
|
|
8079
|
-
const rowName = normalize(textValue(row.name) ?? "");
|
|
8080
|
-
const rowLimit = normalize(textValue(row.limit) ?? "");
|
|
8081
|
-
return existing.some((coverage) => {
|
|
8082
|
-
if (coverageKey(coverage) === rowKey) return true;
|
|
8083
|
-
const name = normalize(textValue(coverage.name) ?? "");
|
|
8084
|
-
const limit = normalize(textValue(coverage.limit) ?? "");
|
|
8085
|
-
return Boolean(rowName && rowLimit && name === rowName && limit === rowLimit);
|
|
8086
|
-
});
|
|
8087
|
-
}
|
|
8088
|
-
function recoverCoverageScheduleRows(params) {
|
|
8089
|
-
const payload = params.memory.get("coverage_limits");
|
|
8090
|
-
const existing = Array.isArray(payload?.coverages) ? payload.coverages : [];
|
|
8091
|
-
const pages = coveragePages(params.pageAssignments);
|
|
8092
|
-
const candidates = params.sourceSpans.filter((span) => sourceUnit5(span) === "table_row").filter((span) => {
|
|
8093
|
-
const page = pageNumber(span);
|
|
8094
|
-
return page !== void 0 && pages.has(page);
|
|
8095
|
-
}).map(coverageFromRow).filter((coverage) => Boolean(coverage));
|
|
8096
|
-
const recovered = [];
|
|
8097
|
-
for (const coverage of candidates) {
|
|
8098
|
-
if (rowMatchesExisting(coverage, [...existing, ...recovered])) continue;
|
|
8099
|
-
recovered.push(coverage);
|
|
8100
|
-
}
|
|
8101
|
-
if (recovered.length > 0) {
|
|
8102
|
-
params.memory.set("coverage_limits", {
|
|
8103
|
-
...payload ?? {},
|
|
8104
|
-
coverages: [...existing, ...recovered]
|
|
8105
|
-
});
|
|
8106
|
-
}
|
|
8107
|
-
return {
|
|
8108
|
-
recovered,
|
|
8109
|
-
missingSourceRows: recovered.map((coverage) => {
|
|
8110
|
-
const id = Array.isArray(coverage.sourceSpanIds) ? coverage.sourceSpanIds[0] : void 0;
|
|
8111
|
-
return params.sourceSpans.find((span) => span.id === id);
|
|
8112
|
-
}).filter((span) => Boolean(span))
|
|
8113
|
-
};
|
|
8114
|
-
}
|
|
8115
|
-
|
|
8116
7933
|
// src/extraction/focused-dispatch.ts
|
|
8117
7934
|
async function runFocusedExtractorWithFallback(params) {
|
|
8118
7935
|
const {
|
|
@@ -8315,29 +8132,6 @@ function buildExtractionReviewReport(params) {
|
|
|
8315
8132
|
addMissingSourceGroundingIssues(deterministicIssues, "definitions", "definitions", definitions, "term");
|
|
8316
8133
|
addMissingSourceGroundingIssues(deterministicIssues, "covered_reasons", "coveredReasons", coveredReasons, "name");
|
|
8317
8134
|
}
|
|
8318
|
-
if (params.sourceSpans?.length) {
|
|
8319
|
-
const tempCoveragePayload = {
|
|
8320
|
-
...memory.get("coverage_limits") ?? {},
|
|
8321
|
-
coverages: coverages.map((coverage) => ({ ...coverage }))
|
|
8322
|
-
};
|
|
8323
|
-
const tempMemory = new Map(memory);
|
|
8324
|
-
tempMemory.set("coverage_limits", tempCoveragePayload);
|
|
8325
|
-
const scheduleRecovery = recoverCoverageScheduleRows({
|
|
8326
|
-
memory: tempMemory,
|
|
8327
|
-
sourceSpans: params.sourceSpans,
|
|
8328
|
-
pageAssignments: params.pageAssignments
|
|
8329
|
-
});
|
|
8330
|
-
for (const recovered of scheduleRecovery.recovered) {
|
|
8331
|
-
deterministicIssues.push({
|
|
8332
|
-
code: "coverage_schedule_row_missing",
|
|
8333
|
-
severity: "blocking",
|
|
8334
|
-
message: `Coverage schedule row "${String(recovered.name ?? "unknown")}" is present in source table evidence but missing from extracted coverages.`,
|
|
8335
|
-
extractorName: "coverage_limits",
|
|
8336
|
-
pageNumber: typeof recovered.pageNumber === "number" ? recovered.pageNumber : void 0,
|
|
8337
|
-
itemName: typeof recovered.name === "string" ? recovered.name : void 0
|
|
8338
|
-
});
|
|
8339
|
-
}
|
|
8340
|
-
}
|
|
8341
8135
|
if (mappedDefinitions && definitions.length === 0) {
|
|
8342
8136
|
deterministicIssues.push({
|
|
8343
8137
|
code: "definitions_mapped_but_empty",
|
|
@@ -8780,17 +8574,17 @@ var ARRAY_PATHS = [
|
|
|
8780
8574
|
{ memoryKey: "supplementary", arrayKeys: ["auxiliaryFacts", "supplementaryFacts"] },
|
|
8781
8575
|
{ memoryKey: "form_inventory", arrayKeys: ["forms"] }
|
|
8782
8576
|
];
|
|
8783
|
-
function
|
|
8577
|
+
function normalize(value) {
|
|
8784
8578
|
return value.replace(/\s+/g, " ").trim().toLowerCase();
|
|
8785
8579
|
}
|
|
8786
|
-
function
|
|
8580
|
+
function textValue(record, ...keys) {
|
|
8787
8581
|
for (const key of keys) {
|
|
8788
8582
|
const value = record[key];
|
|
8789
8583
|
if (typeof value === "string" && value.trim()) return value.trim();
|
|
8790
8584
|
}
|
|
8791
8585
|
return void 0;
|
|
8792
8586
|
}
|
|
8793
|
-
function
|
|
8587
|
+
function numberValue2(record, ...keys) {
|
|
8794
8588
|
for (const key of keys) {
|
|
8795
8589
|
const value = record[key];
|
|
8796
8590
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
@@ -8807,27 +8601,27 @@ function pageOverlaps(recordStart, recordEnd, span) {
|
|
|
8807
8601
|
return start <= (spanEnd ?? spanStart) && end >= spanStart;
|
|
8808
8602
|
}
|
|
8809
8603
|
function formMatches(record, span) {
|
|
8810
|
-
const formNumber =
|
|
8604
|
+
const formNumber = textValue(record, "formNumber");
|
|
8811
8605
|
if (!formNumber || !span.formNumber) return false;
|
|
8812
|
-
return
|
|
8606
|
+
return normalize(formNumber) === normalize(span.formNumber);
|
|
8813
8607
|
}
|
|
8814
8608
|
function textMatches(record, span) {
|
|
8815
|
-
const spanText =
|
|
8609
|
+
const spanText = normalize(span.text);
|
|
8816
8610
|
const candidates = [
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8611
|
+
textValue(record, "originalContent", "content", "definition", "value"),
|
|
8612
|
+
textValue(record, "name", "title", "term", "field", "coverageName"),
|
|
8613
|
+
textValue(record, "limit", "deductible", "premium")
|
|
8820
8614
|
].filter((value) => !!value && value.length >= 3);
|
|
8821
|
-
return candidates.some((candidate) => spanText.includes(
|
|
8615
|
+
return candidates.some((candidate) => spanText.includes(normalize(candidate)));
|
|
8822
8616
|
}
|
|
8823
8617
|
function sourceHashFor(spans) {
|
|
8824
8618
|
return spans.map((span) => span.textHash ?? span.hash).filter(Boolean).join(":") || void 0;
|
|
8825
8619
|
}
|
|
8826
|
-
function
|
|
8620
|
+
function sourceUnit5(span) {
|
|
8827
8621
|
return span.sourceUnit ?? span.metadata?.sourceUnit;
|
|
8828
8622
|
}
|
|
8829
8623
|
function hierarchyScore(span) {
|
|
8830
|
-
switch (
|
|
8624
|
+
switch (sourceUnit5(span)) {
|
|
8831
8625
|
case "table_row":
|
|
8832
8626
|
return 5;
|
|
8833
8627
|
case "table":
|
|
@@ -8850,8 +8644,8 @@ function preferParentRows(matches, sourceSpans) {
|
|
|
8850
8644
|
const expanded = [];
|
|
8851
8645
|
const seen = /* @__PURE__ */ new Set();
|
|
8852
8646
|
for (const match of matches) {
|
|
8853
|
-
const parent =
|
|
8854
|
-
const preferred = parent &&
|
|
8647
|
+
const parent = sourceUnit5(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
|
|
8648
|
+
const preferred = parent && sourceUnit5(parent) === "table_row" ? parent : match;
|
|
8855
8649
|
if (seen.has(preferred.id)) continue;
|
|
8856
8650
|
seen.add(preferred.id);
|
|
8857
8651
|
expanded.push(preferred);
|
|
@@ -8860,8 +8654,8 @@ function preferParentRows(matches, sourceSpans) {
|
|
|
8860
8654
|
}
|
|
8861
8655
|
function findSourceSpansForRecord(record, sourceSpans) {
|
|
8862
8656
|
if (sourceSpans.length === 0) return [];
|
|
8863
|
-
const pageStart2 =
|
|
8864
|
-
const pageEnd2 =
|
|
8657
|
+
const pageStart2 = numberValue2(record, "pageNumber", "pageStart");
|
|
8658
|
+
const pageEnd2 = numberValue2(record, "pageNumber", "pageEnd");
|
|
8865
8659
|
const scored = sourceSpans.map((span) => {
|
|
8866
8660
|
let score = 0;
|
|
8867
8661
|
if (pageOverlaps(pageStart2, pageEnd2, span)) score += 4;
|
|
@@ -9003,7 +8797,6 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
|
|
|
9003
8797
|
retroactiveDate: typeof record.retroactiveDate === "string" ? cleanValue(record.retroactiveDate) : void 0,
|
|
9004
8798
|
formNumber: typeof record.formNumber === "string" ? cleanValue(record.formNumber) : void 0,
|
|
9005
8799
|
sectionRef: typeof record.sectionRef === "string" ? cleanValue(record.sectionRef) : void 0,
|
|
9006
|
-
coverageOrigin: record.coverageOrigin === "core" || record.coverageOrigin === "endorsement" ? record.coverageOrigin : void 0,
|
|
9007
8800
|
endorsementNumber: typeof record.endorsementNumber === "string" ? cleanValue(record.endorsementNumber) : void 0,
|
|
9008
8801
|
limits,
|
|
9009
8802
|
sourceNodeIds: sourceNodeIds2,
|
|
@@ -9089,7 +8882,6 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
|
|
|
9089
8882
|
expirationDate,
|
|
9090
8883
|
retroactiveDate,
|
|
9091
8884
|
premium,
|
|
9092
|
-
coverageTypes: Array.isArray(candidate.coverageTypes) && candidate.coverageTypes.length > 0 ? candidate.coverageTypes : base.coverageTypes,
|
|
9093
8885
|
coverages,
|
|
9094
8886
|
parties,
|
|
9095
8887
|
endorsementSupport,
|
|
@@ -9124,7 +8916,6 @@ var OperationalProfileCleanupSchema = z41.object({
|
|
|
9124
8916
|
deductible: z41.string().nullable().optional(),
|
|
9125
8917
|
premium: z41.string().nullable().optional(),
|
|
9126
8918
|
retroactiveDate: z41.string().nullable().optional(),
|
|
9127
|
-
coverageOrigin: z41.enum(["core", "endorsement"]).optional(),
|
|
9128
8919
|
sourceNodeIds: z41.array(z41.string()).optional(),
|
|
9129
8920
|
sourceSpanIds: z41.array(z41.string()).optional(),
|
|
9130
8921
|
termDecisions: z41.array(z41.object({
|
|
@@ -9169,7 +8960,6 @@ function compactCoverageForCleanup(coverage, coverageIndex) {
|
|
|
9169
8960
|
deductible: coverage.deductible,
|
|
9170
8961
|
premium: coverage.premium,
|
|
9171
8962
|
retroactiveDate: coverage.retroactiveDate,
|
|
9172
|
-
coverageOrigin: coverage.coverageOrigin,
|
|
9173
8963
|
sourceNodeIds: compactIds(coverage.sourceNodeIds),
|
|
9174
8964
|
sourceSpanIds: compactIds(coverage.sourceSpanIds),
|
|
9175
8965
|
terms: coverage.limits.map((term, termIndex) => ({
|
|
@@ -9245,7 +9035,7 @@ function selectCoverageCleanupNodes(sourceTree, coverages) {
|
|
|
9245
9035
|
...coverage.sourceNodeIds,
|
|
9246
9036
|
...coverage.limits.flatMap((term) => term.sourceNodeIds)
|
|
9247
9037
|
]));
|
|
9248
|
-
const
|
|
9038
|
+
const coveragePages = /* @__PURE__ */ new Set();
|
|
9249
9039
|
const coverageTerms = uniqueStrings(coverages.flatMap(
|
|
9250
9040
|
(coverage) => coverageTextForSelection(coverage).toLowerCase().split(/[^a-z0-9$,.]+/i).filter((part) => part.length >= 5)
|
|
9251
9041
|
));
|
|
@@ -9253,8 +9043,8 @@ function selectCoverageCleanupNodes(sourceTree, coverages) {
|
|
|
9253
9043
|
const node = nodeById.get(id);
|
|
9254
9044
|
if (!node) continue;
|
|
9255
9045
|
addNode(node, 1e3);
|
|
9256
|
-
if (node.pageStart)
|
|
9257
|
-
if (node.pageEnd)
|
|
9046
|
+
if (node.pageStart) coveragePages.add(node.pageStart);
|
|
9047
|
+
if (node.pageEnd) coveragePages.add(node.pageEnd);
|
|
9258
9048
|
let parentId = node.parentId;
|
|
9259
9049
|
let parentScore = 940;
|
|
9260
9050
|
while (parentId) {
|
|
@@ -9277,7 +9067,7 @@ function selectCoverageCleanupNodes(sourceTree, coverages) {
|
|
|
9277
9067
|
}
|
|
9278
9068
|
for (const node of sourceTree) {
|
|
9279
9069
|
if (node.kind === "document") continue;
|
|
9280
|
-
if (!node.pageStart || !
|
|
9070
|
+
if (!node.pageStart || !coveragePages.has(node.pageStart)) continue;
|
|
9281
9071
|
const text = nodeTextForSelection(node);
|
|
9282
9072
|
if (CLEANUP_KEYWORD.test(text) || nodeTextMatchesCoverage(node, coverageTerms)) {
|
|
9283
9073
|
addNode(node, 600);
|
|
@@ -9298,7 +9088,6 @@ function buildOperationalProfileCleanupPrompt(sourceTree, profile, options = {})
|
|
|
9298
9088
|
const candidate = {
|
|
9299
9089
|
documentType: profile.documentType,
|
|
9300
9090
|
policyTypes: profile.policyTypes,
|
|
9301
|
-
coverageTypes: profile.coverageTypes,
|
|
9302
9091
|
coverages: coverageEntries.map(({ coverage, coverageIndex }) => compactCoverageForCleanup(coverage, coverageIndex))
|
|
9303
9092
|
};
|
|
9304
9093
|
return `Review and clean a source-backed operational profile projection for an insurance policy.
|
|
@@ -9484,7 +9273,6 @@ function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpa
|
|
|
9484
9273
|
};
|
|
9485
9274
|
const name = cleanProfileValue(decision.name);
|
|
9486
9275
|
if (name) next.name = name;
|
|
9487
|
-
if (decision.coverageOrigin) next.coverageOrigin = decision.coverageOrigin;
|
|
9488
9276
|
if (decision.limit != null) {
|
|
9489
9277
|
const value = cleanProfileValue(decision.limit);
|
|
9490
9278
|
if (value) next.limit = value;
|
|
@@ -9580,7 +9368,6 @@ function applyOperationalProfileCleanup(profile, cleanup, validNodeIds, validSpa
|
|
|
9580
9368
|
const nextProfile = {
|
|
9581
9369
|
...profile,
|
|
9582
9370
|
coverages,
|
|
9583
|
-
coverageTypes: uniqueStrings(coverages.map((coverage) => coverage.name)),
|
|
9584
9371
|
warnings: uniqueStrings([
|
|
9585
9372
|
...profile.warnings,
|
|
9586
9373
|
...cleanupWarnings.map((warning) => `Operational profile cleanup warning: ${warning}`)
|
|
@@ -9601,9 +9388,9 @@ var ORGANIZABLE_KINDS = [
|
|
|
9601
9388
|
"schedule",
|
|
9602
9389
|
"clause"
|
|
9603
9390
|
];
|
|
9604
|
-
var ORGANIZATION_TOP_LEVEL_BATCH_SIZE =
|
|
9605
|
-
var
|
|
9606
|
-
var
|
|
9391
|
+
var ORGANIZATION_TOP_LEVEL_BATCH_SIZE = 12;
|
|
9392
|
+
var ORGANIZER_MAX_TOP_LEVEL_NODES = 120;
|
|
9393
|
+
var ORGANIZER_MAX_BATCH_NODES = 180;
|
|
9607
9394
|
var OUTLINE_CLEANUP_MAX_TOP_LEVEL_NODES = 80;
|
|
9608
9395
|
var SourceTreeOrganizationSchema = z42.object({
|
|
9609
9396
|
labels: z42.array(z42.object({
|
|
@@ -9629,26 +9416,10 @@ var SourceTreeOrganizationSchema = z42.object({
|
|
|
9629
9416
|
kind: z42.enum(ORGANIZABLE_KINDS),
|
|
9630
9417
|
title: z42.string(),
|
|
9631
9418
|
description: z42.string().optional(),
|
|
9419
|
+
parentNodeId: z42.string().optional(),
|
|
9632
9420
|
childNodeIds: z42.array(z42.string()).min(1)
|
|
9633
9421
|
}))
|
|
9634
9422
|
});
|
|
9635
|
-
var SourceTreeVisualTableRepairSchema = z42.object({
|
|
9636
|
-
tables: z42.array(z42.object({
|
|
9637
|
-
tableNodeId: z42.string(),
|
|
9638
|
-
columnLabels: z42.array(z42.object({
|
|
9639
|
-
columnIndex: z42.number().int().nonnegative(),
|
|
9640
|
-
label: z42.string()
|
|
9641
|
-
})).default([]),
|
|
9642
|
-
continuationRows: z42.array(z42.object({
|
|
9643
|
-
sourceRowNodeId: z42.string(),
|
|
9644
|
-
targetRowNodeId: z42.string(),
|
|
9645
|
-
targetColumnIndex: z42.number().int().nonnegative().optional(),
|
|
9646
|
-
targetColumnLabel: z42.string().optional(),
|
|
9647
|
-
reason: z42.string().optional()
|
|
9648
|
-
})).default([])
|
|
9649
|
-
})).default([]),
|
|
9650
|
-
warnings: z42.array(z42.string()).default([])
|
|
9651
|
-
});
|
|
9652
9423
|
var SourceBackedValueForPromptSchema = z42.object({
|
|
9653
9424
|
value: z42.string(),
|
|
9654
9425
|
normalizedValue: z42.string().optional(),
|
|
@@ -9667,7 +9438,6 @@ var OperationalProfilePromptSchema = z42.object({
|
|
|
9667
9438
|
expirationDate: SourceBackedValueForPromptSchema.optional(),
|
|
9668
9439
|
retroactiveDate: SourceBackedValueForPromptSchema.optional(),
|
|
9669
9440
|
premium: SourceBackedValueForPromptSchema.optional(),
|
|
9670
|
-
coverageTypes: z42.array(z42.string()).optional(),
|
|
9671
9441
|
coverages: z42.array(z42.object({
|
|
9672
9442
|
name: z42.string(),
|
|
9673
9443
|
coverageCode: z42.string().optional(),
|
|
@@ -9677,7 +9447,6 @@ var OperationalProfilePromptSchema = z42.object({
|
|
|
9677
9447
|
retroactiveDate: z42.string().optional(),
|
|
9678
9448
|
formNumber: z42.string().optional(),
|
|
9679
9449
|
sectionRef: z42.string().optional(),
|
|
9680
|
-
coverageOrigin: z42.enum(["core", "endorsement"]).optional(),
|
|
9681
9450
|
endorsementNumber: z42.string().optional(),
|
|
9682
9451
|
limits: z42.array(z42.object({
|
|
9683
9452
|
kind: OperationalCoverageTermKindSchema.optional(),
|
|
@@ -9867,13 +9636,18 @@ function pageHeadingTitleFromText(text, fallback) {
|
|
|
9867
9636
|
return fallback;
|
|
9868
9637
|
}
|
|
9869
9638
|
function pageFormTypeFromText(text) {
|
|
9639
|
+
if (hasSubstantiveDeclarationsScheduleText(text)) return "declarations";
|
|
9870
9640
|
if (/\b(declarations?\s+page|declarations?\s+schedule)\b/i.test(text)) return "declarations";
|
|
9871
9641
|
if (/\b(endorsement\s+(?:no\.?|number|#)|this endorsement changes the policy|[A-Z]{2,}-END\s+\d{2,})\b/i.test(text)) return "endorsement";
|
|
9872
9642
|
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";
|
|
9873
9643
|
if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions)\b/i.test(text)) return "notice";
|
|
9874
9644
|
return "other";
|
|
9875
9645
|
}
|
|
9646
|
+
function hasSubstantiveDeclarationsScheduleText(text) {
|
|
9647
|
+
return /\bitem\s+\d+\.?\s*(?:named insured|policy number|policy period|renewal|form of business|coverage parts?|limits?|premium|extended reporting|producer|forms? and endorsements?)\b/i.test(text) || /\bforms? and endorsements attached at inception\b/i.test(text) || /\bcoverage parts?,?\s+limits? of liability,?\s+deductibles?,?\s+and retroactive dates\b/i.test(text) || /\bannual premium\s*\(all coverage parts?\)\b/i.test(text) || /\berp option\b/i.test(text) || /\bproducer\b[\s\S]{0,240}\blicense\b/i.test(text);
|
|
9648
|
+
}
|
|
9876
9649
|
function administrativeFormTypeFromText(text) {
|
|
9650
|
+
if (hasSubstantiveDeclarationsScheduleText(text)) return void 0;
|
|
9877
9651
|
if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions|economic sanctions limitation|how to report a claim)\b/i.test(text)) {
|
|
9878
9652
|
return "notice";
|
|
9879
9653
|
}
|
|
@@ -10274,6 +10048,7 @@ function isDeclarationsNode(node) {
|
|
|
10274
10048
|
}
|
|
10275
10049
|
function isAdministrativeNoticeNode(node) {
|
|
10276
10050
|
const text = sourceNodeText(node);
|
|
10051
|
+
if (hasSubstantiveDeclarationsScheduleText(text)) return false;
|
|
10277
10052
|
return /\b(specimen policy|policy jacket|important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions|economic sanctions limitation|signature|countersignature|how to report a claim)\b/i.test(text);
|
|
10278
10053
|
}
|
|
10279
10054
|
function mergeAdministrativeNoticesIntoFrontMatter(sourceTree) {
|
|
@@ -10791,18 +10566,36 @@ function rootChildren(sourceTree) {
|
|
|
10791
10566
|
const rootId = sourceTreeRootId(sourceTree);
|
|
10792
10567
|
return (byParent.get(rootId) ?? []).filter((node) => node.kind !== "document");
|
|
10793
10568
|
}
|
|
10794
|
-
function
|
|
10795
|
-
const children = rootChildren(sourceTree);
|
|
10796
|
-
const semanticCount = children.filter(
|
|
10797
|
-
(node) => node.kind === "page_group" || node.kind === "form" || node.kind === "endorsement" || node.kind === "section" || node.kind === "schedule"
|
|
10798
|
-
).length;
|
|
10799
|
-
return semanticCount >= 2 || children.some((node) => node.title === "Declarations") || children.some((node) => node.title === "Policy Form") || children.some((node) => node.title === "Endorsements");
|
|
10800
|
-
}
|
|
10801
|
-
function shouldRunSourceTreeOrganizer(sourceTree, sourceSpans) {
|
|
10569
|
+
function shouldRunSourceTreeOrganizer(sourceTree, _sourceSpans) {
|
|
10802
10570
|
const topLevelCount = rootChildren(sourceTree).length;
|
|
10803
|
-
if (sourceSpans.length > ORGANIZER_MAX_SOURCE_SPANS) return false;
|
|
10804
10571
|
if (topLevelCount > ORGANIZER_MAX_TOP_LEVEL_NODES) return false;
|
|
10805
|
-
return
|
|
10572
|
+
return true;
|
|
10573
|
+
}
|
|
10574
|
+
function organizationCandidateText(node) {
|
|
10575
|
+
return [node.title, node.description, node.textExcerpt].filter(Boolean).join(" ");
|
|
10576
|
+
}
|
|
10577
|
+
function isHighSignalOrganizationNode(node) {
|
|
10578
|
+
if (node.kind === "document") return false;
|
|
10579
|
+
if (["page", "page_group", "form", "endorsement", "section", "schedule", "clause", "table", "table_row"].includes(node.kind)) {
|
|
10580
|
+
return true;
|
|
10581
|
+
}
|
|
10582
|
+
if (node.kind !== "text" && node.kind !== "table_cell") return false;
|
|
10583
|
+
const text = organizationCandidateText(node);
|
|
10584
|
+
return /\b(SECTION|PART|ARTICLE|SCHEDULE)\s+[IVXLCDM0-9]+/i.test(text) || /\bItem\s+\d+[\.:]/i.test(text) || /^[A-Z]\.\s+\S/.test(cleanText(node.textExcerpt ?? node.title, "")) || /\b(forms? and endorsements?|coverage parts?|limits? of liability|extended reporting period|producer|premium|aggregate|retroactive date|endorsement no\.?)\b/i.test(text);
|
|
10585
|
+
}
|
|
10586
|
+
function organizationBatchNodes(topLevelBatch, byParent) {
|
|
10587
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
10588
|
+
const queue = [...topLevelBatch];
|
|
10589
|
+
while (queue.length > 0 && nodes.size < ORGANIZER_MAX_BATCH_NODES) {
|
|
10590
|
+
const node = queue.shift();
|
|
10591
|
+
if (!node || nodes.has(node.id) || !isHighSignalOrganizationNode(node)) continue;
|
|
10592
|
+
nodes.set(node.id, node);
|
|
10593
|
+
for (const child of byParent.get(node.id) ?? []) {
|
|
10594
|
+
if (nodes.size + queue.length >= ORGANIZER_MAX_BATCH_NODES) break;
|
|
10595
|
+
if (isHighSignalOrganizationNode(child)) queue.push(child);
|
|
10596
|
+
}
|
|
10597
|
+
}
|
|
10598
|
+
return [...nodes.values()];
|
|
10806
10599
|
}
|
|
10807
10600
|
function organizationBatches(sourceTree) {
|
|
10808
10601
|
const byParent = nodesByParent(sourceTree);
|
|
@@ -10819,14 +10612,10 @@ function organizationBatches(sourceTree) {
|
|
|
10819
10612
|
const batches = [];
|
|
10820
10613
|
for (let index = 0; index < topLevelNodes.length; index += ORGANIZATION_TOP_LEVEL_BATCH_SIZE) {
|
|
10821
10614
|
const topLevelBatch = topLevelNodes.slice(index, index + ORGANIZATION_TOP_LEVEL_BATCH_SIZE);
|
|
10822
|
-
const candidates = /* @__PURE__ */ new Map();
|
|
10823
|
-
for (const node of topLevelBatch) {
|
|
10824
|
-
candidates.set(node.id, node);
|
|
10825
|
-
}
|
|
10826
10615
|
batches.push({
|
|
10827
10616
|
label: `top-level nodes ${index + 1}-${index + topLevelBatch.length} of ${topLevelNodes.length}`,
|
|
10828
10617
|
topLevelNodeIds: topLevelBatch.map((node) => node.id),
|
|
10829
|
-
nodes:
|
|
10618
|
+
nodes: organizationBatchNodes(topLevelBatch, byParent)
|
|
10830
10619
|
});
|
|
10831
10620
|
}
|
|
10832
10621
|
return batches;
|
|
@@ -10839,7 +10628,7 @@ function mergeOrganizationResults(results) {
|
|
|
10839
10628
|
labels.set(label.nodeId, { ...labels.get(label.nodeId), ...label });
|
|
10840
10629
|
}
|
|
10841
10630
|
for (const group of result.groups) {
|
|
10842
|
-
const key = `${group.kind}:${group.childNodeIds.join("|")}`;
|
|
10631
|
+
const key = `${group.parentNodeId ?? ""}:${group.kind}:${group.childNodeIds.join("|")}`;
|
|
10843
10632
|
groups.set(key, group);
|
|
10844
10633
|
}
|
|
10845
10634
|
}
|
|
@@ -10863,9 +10652,16 @@ ${formatFormHintsForPrompt(formHints)}
|
|
|
10863
10652
|
Rules:
|
|
10864
10653
|
- Use only node IDs from the provided list.
|
|
10865
10654
|
- Do not invent text, page numbers, source spans, limits, or policy facts.
|
|
10866
|
-
- You may relabel existing nodes and group adjacent
|
|
10655
|
+
- You may relabel existing nodes and group adjacent sibling nodes from this batch only when they are clearly one continuous form, one declarations item, one policy section, one schedule, or one clause family.
|
|
10867
10656
|
- Treat the form inventory as a page-range hint for the expected order: front matter/notices, declarations, policy form, then endorsements.
|
|
10868
10657
|
- Prefer section hierarchy from printed title elements inside a form over page-by-page grouping.
|
|
10658
|
+
- Printed section markers are hard hierarchy boundaries. If source text moves from "SECTION XI" to "SECTION XII", "Section 12", "PART III", or similar sequential headings, create a new sibling section at that marker even when the marker appears mid-page after prior section text.
|
|
10659
|
+
- Declarations pages often use "Item 1.", "Item 2.", ... as the real section structure. Preserve each item as its own section or schedule under "Declarations"; do not bury item labels inside a single table when they mark new declaration fields.
|
|
10660
|
+
- For declaration item tables, a table belongs only to the current item until the next "Item N" marker. Start a new declaration item section at the next marker, even if the parser presents the marker as a table row or cell.
|
|
10661
|
+
- Keep forms-and-endorsements schedules attached to declarations when they appear as "Item 10", "Forms and Endorsements Attached at Inception", or a continuation page listing form numbers. Do not discard that page just because it also contains countersignature or authorized-representative language.
|
|
10662
|
+
- Prefer sections/schedules over table grouping when a table-like node mixes structural labels such as "Item 7", "Item 8", "SECTION XII", "Producer", or "Forms and Endorsements" with values.
|
|
10663
|
+
- Use group.parentNodeId only when the source clearly shows a child was nested under the wrong earlier section/table. Example: a "SECTION XII" marker nested below "SECTION XI" should be grouped with parentNodeId set to the Policy Form node so Section XII becomes a sibling of Section XI.
|
|
10664
|
+
- Use group.parentNodeId to place "Item N" groups under the Declarations node when parser table structure would otherwise leave those items trapped inside one table.
|
|
10869
10665
|
- Group adjacent separately numbered endorsements under a single generic "Endorsements" page_group parent, with each individual endorsement preserved as its own child node.
|
|
10870
10666
|
- Never create rollup titles such as "Endorsements 1-3 (...)" or merge multiple endorsements into one endorsement node.
|
|
10871
10667
|
- Add concise, human-readable titles to generic text, table, row, and cell nodes when the text makes their role clear.
|
|
@@ -10896,7 +10692,7 @@ function buildOutlineCleanupPrompt(sourceTree, formHints) {
|
|
|
10896
10692
|
|
|
10897
10693
|
Expected product-facing order:
|
|
10898
10694
|
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.
|
|
10899
|
-
2. Declarations: declarations page(s), schedules, named insured/policy period/premium rows, coverage limit schedules, forms-and-endorsements schedules.
|
|
10695
|
+
2. Declarations: declarations page(s), schedules, itemized declaration fields, named insured/policy period/premium rows, coverage limit schedules, forms-and-endorsements schedules.
|
|
10900
10696
|
3. Policy Form: the main policy wording, insuring agreements, definitions, exclusions, conditions, claim provisions, and general policy terms.
|
|
10901
10697
|
4. Endorsements: one generic "Endorsements" page_group containing each separately numbered endorsement as its own child.
|
|
10902
10698
|
|
|
@@ -10910,7 +10706,9 @@ Rules:
|
|
|
10910
10706
|
- Do not merge individually numbered endorsements into one endorsement node; use the generic "Endorsements" parent for the series.
|
|
10911
10707
|
- Use canonical terse titles: "Notices and Jacket", "Declarations", "Policy Form", "Endorsements", or the printed endorsement number.
|
|
10912
10708
|
- Keep page_group descriptions short and include the page range when pages are known, for example "Declarations pages 6-8".
|
|
10913
|
-
- 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.
|
|
10709
|
+
- If a page is only an OFAC, privacy, terrorism/TRIA, claim-reporting notice, signature page, or jacket, do not label it as declarations or policy form.
|
|
10710
|
+
- If a page contains declaration items, coverage schedules, premium rows, producer rows, or a forms-and-endorsements schedule, keep it in Declarations even if it also contains countersignature or authorized-representative text.
|
|
10711
|
+
- If a policy-form page contains a later printed section marker such as "SECTION XII \u2014 EXTENDED REPORTING PERIOD", preserve that marker as the start of a new sibling section rather than a paragraph under the previous section.
|
|
10914
10712
|
- If the form inventory provides page ranges, keep groups aligned to those ranges unless the source node text clearly contradicts them.
|
|
10915
10713
|
- If the existing deterministic outline is already correct, return empty labels and groups.
|
|
10916
10714
|
|
|
@@ -10932,7 +10730,6 @@ function emptyOperationalProfile() {
|
|
|
10932
10730
|
return {
|
|
10933
10731
|
documentType: "policy",
|
|
10934
10732
|
policyTypes: ["other"],
|
|
10935
|
-
coverageTypes: [],
|
|
10936
10733
|
coverages: [],
|
|
10937
10734
|
parties: [],
|
|
10938
10735
|
endorsementSupport: [],
|
|
@@ -10956,6 +10753,10 @@ Rules:
|
|
|
10956
10753
|
- Every returned value must include sourceNodeIds or sourceSpanIds from the provided nodes.
|
|
10957
10754
|
- If a value is not directly supported, omit it.
|
|
10958
10755
|
- Prefer declarations, schedules, premium tables, and endorsement schedules over generic policy wording.
|
|
10756
|
+
- On declarations pages, treat "Item N" labels as section boundaries. Use Item 6 or equivalent coverage-schedule rows for coverage limits, deductibles, aggregate terms, and retroactive dates; do not merge Item 7 premium, Item 8 ERP, Item 9 producer, or Item 10 forms into Item 6 coverage facts.
|
|
10757
|
+
- A coverage schedule row's coverage name should come from the "Coverage Part" or equivalent row label. Limit, deductible, aggregate, sublimit, retention, and retroactive-date values belong as nested terms under that coverage, not in the coverage title.
|
|
10758
|
+
- If a coverage schedule continues onto the next page before the next item marker, include the continuation rows in the same coverage or declaration item.
|
|
10759
|
+
- Forms-and-endorsements schedules are operational form inventory evidence, not coverage limits. Do not turn form schedule rows into coverage units unless the row also states a coverage-specific limit or deductible.
|
|
10959
10760
|
- Keep each coverage unit tied to one evidence scope: a declaration/core schedule row, a core policy form section, or one specific endorsement schedule. Do not merge declaration facts and endorsement schedule facts into the same coverage unit, even when they use the same coverage name.
|
|
10960
10761
|
- If the declarations schedule and an endorsement schedule both list Network Security, Social Engineering Fraud, Regulatory Proceedings, or another same-named coverage, return separate coverage units for each supported source scope.
|
|
10961
10762
|
- Use the declaration coverage name for declaration/core schedule rows. Use the endorsement title or endorsement schedule coverage name for endorsement rows, and include formNumber and endorsementNumber when source-backed.
|
|
@@ -10963,7 +10764,6 @@ Rules:
|
|
|
10963
10764
|
- Treat an endorsement as one coverage unit when it contains a schedule. Do not split an endorsement schedule into generic rows like "Aggregate Limit".
|
|
10964
10765
|
- For coverage schedules, put each claim, aggregate, sublimit, retention, deductible, and retroactive date values in coverages[].limits with labels and source IDs. Keep the legacy coverages[].limit as the primary display value only.
|
|
10965
10766
|
- Extract coinsurance, participation percentage, or insurer/named-insured split terms as coverages[].limits entries with kind "other" when they are part of a coverage schedule.
|
|
10966
|
-
- Use coverageOrigin: "endorsement" for endorsement units and "core" for declarations/core policy coverage units.
|
|
10967
10767
|
- Do not copy entire policy wording into fields.
|
|
10968
10768
|
- Extract facts directly from source nodes. There is no deterministic fact baseline.
|
|
10969
10769
|
|
|
@@ -10972,15 +10772,6 @@ ${JSON.stringify(nodes, null, 2)}
|
|
|
10972
10772
|
|
|
10973
10773
|
Return JSON for the operational profile.`;
|
|
10974
10774
|
}
|
|
10975
|
-
var VISUAL_TABLE_REPAIR_MAX_TABLES = 4;
|
|
10976
|
-
var VISUAL_TABLE_REPAIR_MAX_ROWS = 28;
|
|
10977
|
-
var VISUAL_TABLE_REPAIR_MAX_CELLS = 140;
|
|
10978
|
-
function isSourceTreeHeaderRow(row) {
|
|
10979
|
-
return row.metadata?.isHeader === true || row.metadata?.isHeader === "true";
|
|
10980
|
-
}
|
|
10981
|
-
function tableCellText(cell) {
|
|
10982
|
-
return cleanText(cell.textExcerpt ?? cell.description ?? cell.title, "");
|
|
10983
|
-
}
|
|
10984
10775
|
function tableCellValueText(cell) {
|
|
10985
10776
|
return cleanText(cell.textExcerpt ?? cell.description ?? "", "");
|
|
10986
10777
|
}
|
|
@@ -11034,526 +10825,10 @@ function normalizeSourceTreeTableDisplayText(sourceTree) {
|
|
|
11034
10825
|
if (updates.size === 0) return sourceTree;
|
|
11035
10826
|
return sourceTree.map((node) => updates.get(node.id) ?? node);
|
|
11036
10827
|
}
|
|
11037
|
-
function tableRowTextForPrompt(row, cells) {
|
|
11038
|
-
return cleanText(
|
|
11039
|
-
cells.length ? cells.map(tableCellText).filter(Boolean).join(" | ") : row.textExcerpt ?? row.description ?? row.title,
|
|
11040
|
-
row.title
|
|
11041
|
-
);
|
|
11042
|
-
}
|
|
11043
10828
|
function tableCellColumnIndex(cell, fallbackIndex) {
|
|
11044
10829
|
const metadataIndex = cell.metadata?.columnIndex;
|
|
11045
10830
|
return typeof metadataIndex === "number" && Number.isInteger(metadataIndex) ? metadataIndex : fallbackIndex;
|
|
11046
10831
|
}
|
|
11047
|
-
function isGenericColumnTitle(value) {
|
|
11048
|
-
const title = cleanText(value, "");
|
|
11049
|
-
return !title || /^(?:column\s+\d+|table cell|value)$/i.test(title);
|
|
11050
|
-
}
|
|
11051
|
-
function metadataColumnName(metadata) {
|
|
11052
|
-
const value = metadata?.columnName;
|
|
11053
|
-
return typeof value === "string" ? cleanText(value, "") || void 0 : void 0;
|
|
11054
|
-
}
|
|
11055
|
-
function metadataTableColumnName(metadata) {
|
|
11056
|
-
const table = metadata?.table;
|
|
11057
|
-
if (!table || typeof table !== "object" || Array.isArray(table)) return void 0;
|
|
11058
|
-
if (!("columnName" in table)) return void 0;
|
|
11059
|
-
const value = table.columnName;
|
|
11060
|
-
return typeof value === "string" ? cleanText(value, "") || void 0 : void 0;
|
|
11061
|
-
}
|
|
11062
|
-
function bboxSummary(node) {
|
|
11063
|
-
const box = node.bbox?.[0];
|
|
11064
|
-
if (!box) return void 0;
|
|
11065
|
-
const round = (value) => Math.round(value * 10) / 10;
|
|
11066
|
-
return {
|
|
11067
|
-
page: box.page,
|
|
11068
|
-
x: round(box.x),
|
|
11069
|
-
y: round(box.y),
|
|
11070
|
-
width: round(box.width),
|
|
11071
|
-
height: round(box.height)
|
|
11072
|
-
};
|
|
11073
|
-
}
|
|
11074
|
-
function tableRowsWithCells(table, byParent) {
|
|
11075
|
-
return (byParent.get(table.id) ?? []).filter((node) => node.kind === "table_row").map((row) => ({
|
|
11076
|
-
row,
|
|
11077
|
-
cells: (byParent.get(row.id) ?? []).filter((child) => child.kind === "table_cell").sort(
|
|
11078
|
-
(left, right) => tableCellColumnIndex(left, 0) - tableCellColumnIndex(right, 0) || left.order - right.order || left.id.localeCompare(right.id)
|
|
11079
|
-
)
|
|
11080
|
-
})).sort((left, right) => left.row.order - right.row.order || left.row.id.localeCompare(right.row.id));
|
|
11081
|
-
}
|
|
11082
|
-
function primaryHeaderColumnCount(rows) {
|
|
11083
|
-
const header = rows.find(({ row, cells }) => isSourceTreeHeaderRow(row) && cells.length > 1);
|
|
11084
|
-
if (header) return header.cells.length;
|
|
11085
|
-
return Math.max(0, ...rows.map(({ cells }) => cells.length));
|
|
11086
|
-
}
|
|
11087
|
-
function visualTableRepairScore(candidate) {
|
|
11088
|
-
const rows = candidate.rows;
|
|
11089
|
-
if (rows.length < 3) return 0;
|
|
11090
|
-
const headerCount = primaryHeaderColumnCount(rows);
|
|
11091
|
-
if (headerCount < 2) return 0;
|
|
11092
|
-
const firstHeaderIndex = rows.findIndex(({ row, cells }) => isSourceTreeHeaderRow(row) && cells.length > 1);
|
|
11093
|
-
const repeatedHeader = firstHeaderIndex >= 0 && rows.some(({ row }, index) => index > firstHeaderIndex && isSourceTreeHeaderRow(row));
|
|
11094
|
-
const shortContinuation = rows.some(
|
|
11095
|
-
({ row, cells }, index) => index > 0 && !isSourceTreeHeaderRow(row) && cells.length > 0 && cells.length < headerCount && !/^(?:item\s+\d+|[A-Z]\.)\b/i.test(tableCellText(cells[0]))
|
|
11096
|
-
);
|
|
11097
|
-
const genericDataLabels = rows.some(
|
|
11098
|
-
({ row, cells }) => !isSourceTreeHeaderRow(row) && cells.length >= 2 && cells.some((cell) => isGenericColumnTitle(cell.title))
|
|
11099
|
-
);
|
|
11100
|
-
const danglingSlash = rows.some(
|
|
11101
|
-
({ row, cells }) => !isSourceTreeHeaderRow(row) && /\/\s*$/.test(tableRowTextForPrompt(row, cells))
|
|
11102
|
-
);
|
|
11103
|
-
return (repeatedHeader ? 4 : 0) + (danglingSlash ? 3 : 0) + (shortContinuation ? 3 : 0) + (genericDataLabels ? 2 : 0);
|
|
11104
|
-
}
|
|
11105
|
-
function visualTableCandidates(sourceTree) {
|
|
11106
|
-
const byParent = nodesByParent(sourceTree);
|
|
11107
|
-
return sourceTree.filter((node) => node.kind === "table" && typeof node.pageStart === "number").map((table) => ({
|
|
11108
|
-
table,
|
|
11109
|
-
page: table.pageStart,
|
|
11110
|
-
rows: tableRowsWithCells(table, byParent)
|
|
11111
|
-
})).map((candidate) => ({ ...candidate, repairScore: visualTableRepairScore(candidate) })).filter((candidate) => candidate.repairScore > 0).sort(
|
|
11112
|
-
(left, right) => right.repairScore - left.repairScore || left.page - right.page || left.table.order - right.table.order || left.table.id.localeCompare(right.table.id)
|
|
11113
|
-
).slice(0, VISUAL_TABLE_REPAIR_MAX_TABLES).map(({ repairScore: _repairScore, ...candidate }) => candidate);
|
|
11114
|
-
}
|
|
11115
|
-
function compactVisualTableCandidate(candidate) {
|
|
11116
|
-
let cellCount = 0;
|
|
11117
|
-
const rows = candidate.rows.slice(0, VISUAL_TABLE_REPAIR_MAX_ROWS).map(({ row, cells }) => {
|
|
11118
|
-
const compactCells = cells.slice(0, Math.max(0, VISUAL_TABLE_REPAIR_MAX_CELLS - cellCount)).map((cell, index) => ({
|
|
11119
|
-
cellNodeId: cell.id,
|
|
11120
|
-
columnIndex: tableCellColumnIndex(cell, index),
|
|
11121
|
-
currentColumnName: cell.title,
|
|
11122
|
-
text: tableCellText(cell),
|
|
11123
|
-
bbox: bboxSummary(cell)
|
|
11124
|
-
}));
|
|
11125
|
-
cellCount += compactCells.length;
|
|
11126
|
-
return {
|
|
11127
|
-
rowNodeId: row.id,
|
|
11128
|
-
order: row.order,
|
|
11129
|
-
isHeader: isSourceTreeHeaderRow(row),
|
|
11130
|
-
text: tableRowTextForPrompt(row, cells),
|
|
11131
|
-
bbox: bboxSummary(row),
|
|
11132
|
-
cells: compactCells
|
|
11133
|
-
};
|
|
11134
|
-
});
|
|
11135
|
-
return {
|
|
11136
|
-
tableNodeId: candidate.table.id,
|
|
11137
|
-
page: candidate.page,
|
|
11138
|
-
title: candidate.table.title,
|
|
11139
|
-
bbox: bboxSummary(candidate.table),
|
|
11140
|
-
rows
|
|
11141
|
-
};
|
|
11142
|
-
}
|
|
11143
|
-
function buildVisualTableRepairPrompt(candidate) {
|
|
11144
|
-
return `Compare a parsed insurance source table against the original page visual layout.
|
|
11145
|
-
|
|
11146
|
-
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.
|
|
11147
|
-
|
|
11148
|
-
Task:
|
|
11149
|
-
- Identify rows that are not real standalone rows because they are visually wrapped continuation text for a nearby row.
|
|
11150
|
-
- Identify the primary printed column labels for the table.
|
|
11151
|
-
|
|
11152
|
-
Rules:
|
|
11153
|
-
- Return only high-confidence repairs.
|
|
11154
|
-
- Use only rowNodeId/tableNodeId/cellNodeId values from the provided JSON.
|
|
11155
|
-
- Do not invent policy facts, values, row text, source spans, or page numbers.
|
|
11156
|
-
- continuationRows.sourceRowNodeId must be a parsed row that should be removed as a standalone row.
|
|
11157
|
-
- continuationRows.targetRowNodeId must be the row that visually owns that wrapped text, usually the immediately previous non-header row.
|
|
11158
|
-
- targetColumnIndex/targetColumnLabel should point to the visual column that owns the wrapped text, usually the limit/amount/value column.
|
|
11159
|
-
- Do not mark actual data rows as continuations when they begin a new item, coverage part, endorsement, form, location, person, or premium/tax row.
|
|
11160
|
-
- columnLabels should be the primary visual header labels, not later wrapped cell text that the parser misread as a header.
|
|
11161
|
-
|
|
11162
|
-
Parsed table with visual coordinates:
|
|
11163
|
-
${JSON.stringify(compactVisualTableCandidate(candidate), null, 2)}
|
|
11164
|
-
|
|
11165
|
-
Return JSON with tables[].columnLabels and tables[].continuationRows. Return empty arrays if no repair is needed.`;
|
|
11166
|
-
}
|
|
11167
|
-
function sourceSpanIdsForNodes(nodes) {
|
|
11168
|
-
return [...new Set(nodes.flatMap((node) => node.sourceSpanIds))];
|
|
11169
|
-
}
|
|
11170
|
-
function appendDistinctText(base, addition) {
|
|
11171
|
-
const current = cleanText(base, "");
|
|
11172
|
-
const next = cleanText(addition, "");
|
|
11173
|
-
if (!current) return next || void 0;
|
|
11174
|
-
if (!next) return current;
|
|
11175
|
-
if (current.toLowerCase().includes(next.toLowerCase())) return current;
|
|
11176
|
-
const joinsWithSpace = /(?:[/(:;-]|,\s*)$/.test(current) || /\b(?:part of|including|subject to|not in addition to)$/i.test(current) || /^(?:aggregate|claim|loss|proceeding|occurrence|each\s+(?:claim|loss|proceeding|occurrence)|coverage\s+part\b)/i.test(next);
|
|
11177
|
-
const delimiter = joinsWithSpace ? " " : " / ";
|
|
11178
|
-
return cleanText(`${current}${delimiter}${next}`, current);
|
|
11179
|
-
}
|
|
11180
|
-
function mergedBbox(nodes) {
|
|
11181
|
-
const boxes = nodes.flatMap((node) => node.bbox ?? []);
|
|
11182
|
-
return boxes.length ? boxes.slice(0, 12) : void 0;
|
|
11183
|
-
}
|
|
11184
|
-
function normalizedRepairLabel(value) {
|
|
11185
|
-
const label = cleanText(value, "");
|
|
11186
|
-
if (!label || label.length > 80) return void 0;
|
|
11187
|
-
if (/^(?:source|page|row|table)$/i.test(label)) return void 0;
|
|
11188
|
-
return label;
|
|
11189
|
-
}
|
|
11190
|
-
function findCellForContinuation(params) {
|
|
11191
|
-
if (typeof params.targetColumnIndex === "number") {
|
|
11192
|
-
const byIndex = params.cells.find(
|
|
11193
|
-
(cell, index) => tableCellColumnIndex(cell, index) === params.targetColumnIndex
|
|
11194
|
-
);
|
|
11195
|
-
if (byIndex) return byIndex;
|
|
11196
|
-
}
|
|
11197
|
-
const label = normalizedRepairLabel(params.targetColumnLabel);
|
|
11198
|
-
if (label) {
|
|
11199
|
-
const byLabel = params.cells.find(
|
|
11200
|
-
(cell) => cleanText(cell.title, "").toLowerCase() === label.toLowerCase()
|
|
11201
|
-
);
|
|
11202
|
-
if (byLabel) return byLabel;
|
|
11203
|
-
}
|
|
11204
|
-
return params.cells[1] ?? params.cells[params.cells.length - 1];
|
|
11205
|
-
}
|
|
11206
|
-
function bboxCenterX(node) {
|
|
11207
|
-
const box = node.bbox?.[0];
|
|
11208
|
-
if (!box) return void 0;
|
|
11209
|
-
return box.x + box.width / 2;
|
|
11210
|
-
}
|
|
11211
|
-
function findCellByVisualPosition(sourceCell, targetCells) {
|
|
11212
|
-
const sourceX = bboxCenterX(sourceCell);
|
|
11213
|
-
if (sourceX === void 0) return void 0;
|
|
11214
|
-
const positioned = targetCells.map((cell) => ({ cell, centerX: bboxCenterX(cell) })).filter(
|
|
11215
|
-
(entry) => entry.centerX !== void 0
|
|
11216
|
-
);
|
|
11217
|
-
if (positioned.length === 0) return void 0;
|
|
11218
|
-
positioned.sort(
|
|
11219
|
-
(left, right) => Math.abs(left.centerX - sourceX) - Math.abs(right.centerX - sourceX)
|
|
11220
|
-
);
|
|
11221
|
-
return positioned[0]?.cell;
|
|
11222
|
-
}
|
|
11223
|
-
function isDateLikeColumn(label) {
|
|
11224
|
-
return /\b(?:date|effective|expiration|retroactive)\b/i.test(cleanText(label, ""));
|
|
11225
|
-
}
|
|
11226
|
-
function containsDateValue(text) {
|
|
11227
|
-
return /\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b/.test(text) || /\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\.?\s+\d{1,2},?\s+\d{2,4}\b/i.test(text);
|
|
11228
|
-
}
|
|
11229
|
-
function looksLikePolicyProse(text) {
|
|
11230
|
-
const words = text.split(/\s+/).filter(Boolean);
|
|
11231
|
-
if (words.length < 8) return false;
|
|
11232
|
-
return /\b(?:are|is|will|shall|must|may|includes?|included|reduce|subject|payment|terms?|conditions?|provided|pursuant)\b/i.test(text);
|
|
11233
|
-
}
|
|
11234
|
-
function shouldMergeVisualContinuation(params) {
|
|
11235
|
-
if (startsNewVisualTableItem(params.sourceCells)) return false;
|
|
11236
|
-
const targetLabel = params.targetCell?.title;
|
|
11237
|
-
if (isDateLikeColumn(targetLabel) && !containsDateValue(params.sourceText)) return false;
|
|
11238
|
-
if (looksLikePolicyProse(params.sourceText) && !/\b(?:description|coverage|remarks?|notes?)\b/i.test(cleanText(targetLabel, ""))) {
|
|
11239
|
-
return false;
|
|
11240
|
-
}
|
|
11241
|
-
return true;
|
|
11242
|
-
}
|
|
11243
|
-
function columnLabelStartIndex(rows) {
|
|
11244
|
-
const headerIndex = rows.findIndex(
|
|
11245
|
-
({ row, cells }) => isSourceTreeHeaderRow(row) && cells.length > 1
|
|
11246
|
-
);
|
|
11247
|
-
return headerIndex >= 0 ? headerIndex : 0;
|
|
11248
|
-
}
|
|
11249
|
-
function startsNewVisualTableItem(cells) {
|
|
11250
|
-
const firstText = tableCellText(cells[0]);
|
|
11251
|
-
return /^(?:item\s+\d+\b|[A-Z][.)]\s+|coverage\s+part\b|endorsement\s+(?:no\.?|number|#|\d+)\b)/i.test(firstText);
|
|
11252
|
-
}
|
|
11253
|
-
function rowMatchesColumnLabels(cells, columnLabels) {
|
|
11254
|
-
if (columnLabels.size === 0 || cells.length < columnLabels.size) return false;
|
|
11255
|
-
return cells.every((cell, index) => {
|
|
11256
|
-
const label = columnLabels.get(tableCellColumnIndex(cell, index));
|
|
11257
|
-
return !label || tableCellText(cell).toLowerCase() === label.toLowerCase();
|
|
11258
|
-
});
|
|
11259
|
-
}
|
|
11260
|
-
function implicitContinuationRows(rows, columnLabels, firstLabelRowIndex) {
|
|
11261
|
-
const continuations = [];
|
|
11262
|
-
let targetRowId;
|
|
11263
|
-
const targetColumnLabel = columnLabels.get(1);
|
|
11264
|
-
for (const [rowIndex, { row, cells }] of rows.entries()) {
|
|
11265
|
-
if (rowIndex < firstLabelRowIndex) continue;
|
|
11266
|
-
if (rowIndex === firstLabelRowIndex && rowMatchesColumnLabels(cells, columnLabels)) continue;
|
|
11267
|
-
if (startsNewVisualTableItem(cells)) {
|
|
11268
|
-
targetRowId = row.id;
|
|
11269
|
-
continue;
|
|
11270
|
-
}
|
|
11271
|
-
if (!targetRowId || cells.length === 0) continue;
|
|
11272
|
-
const isSpuriousHeader = isSourceTreeHeaderRow(row) && !rowMatchesColumnLabels(cells, columnLabels);
|
|
11273
|
-
const isShortContinuation = !isSourceTreeHeaderRow(row) && cells.length < Math.max(2, columnLabels.size);
|
|
11274
|
-
if (!isSpuriousHeader && !isShortContinuation) continue;
|
|
11275
|
-
continuations.push({
|
|
11276
|
-
sourceRowNodeId: row.id,
|
|
11277
|
-
targetRowNodeId: targetRowId,
|
|
11278
|
-
targetColumnIndex: 1,
|
|
11279
|
-
targetColumnLabel,
|
|
11280
|
-
reason: "Continuation row inferred from repaired table header."
|
|
11281
|
-
});
|
|
11282
|
-
}
|
|
11283
|
-
return continuations;
|
|
11284
|
-
}
|
|
11285
|
-
function metadataWithColumnLabel(metadata, label) {
|
|
11286
|
-
const next = {
|
|
11287
|
-
...metadata ?? {},
|
|
11288
|
-
columnName: label,
|
|
11289
|
-
visualTableRepairColumnLabel: label
|
|
11290
|
-
};
|
|
11291
|
-
if (metadata?.table && typeof metadata.table === "object" && !Array.isArray(metadata.table)) {
|
|
11292
|
-
next.table = {
|
|
11293
|
-
...metadata.table,
|
|
11294
|
-
columnName: label
|
|
11295
|
-
};
|
|
11296
|
-
}
|
|
11297
|
-
return next;
|
|
11298
|
-
}
|
|
11299
|
-
function tableCellDescription(cell, label) {
|
|
11300
|
-
const text = tableCellText(cell);
|
|
11301
|
-
return cleanText(
|
|
11302
|
-
text && text.toLowerCase() !== label.toLowerCase() ? `${label} | ${text}` : label,
|
|
11303
|
-
cell.description
|
|
11304
|
-
);
|
|
11305
|
-
}
|
|
11306
|
-
function applyVisualTableRepair(sourceTree, repair) {
|
|
11307
|
-
if (repair.tables.length === 0) return sourceTree;
|
|
11308
|
-
const byId = new Map(sourceTree.map((node) => [node.id, node]));
|
|
11309
|
-
const byParent = nodesByParent(sourceTree);
|
|
11310
|
-
const updates = /* @__PURE__ */ new Map();
|
|
11311
|
-
const removeIds = /* @__PURE__ */ new Set();
|
|
11312
|
-
const rowsToRebuildText = /* @__PURE__ */ new Set();
|
|
11313
|
-
const currentNode = (id) => updates.get(id) ?? byId.get(id);
|
|
11314
|
-
for (const tableRepair of repair.tables) {
|
|
11315
|
-
const table = byId.get(tableRepair.tableNodeId);
|
|
11316
|
-
if (!table || table.kind !== "table") continue;
|
|
11317
|
-
const rows = tableRowsWithCells(table, byParent);
|
|
11318
|
-
const rowIds = new Set(rows.map(({ row }) => row.id));
|
|
11319
|
-
const rowOrder = new Map(rows.map(({ row }, index) => [row.id, index]));
|
|
11320
|
-
const columnLabels = /* @__PURE__ */ new Map();
|
|
11321
|
-
for (const label of tableRepair.columnLabels) {
|
|
11322
|
-
const normalized = normalizedRepairLabel(label.label);
|
|
11323
|
-
if (normalized) columnLabels.set(label.columnIndex, normalized);
|
|
11324
|
-
}
|
|
11325
|
-
const firstLabelRowIndex = columnLabelStartIndex(rows);
|
|
11326
|
-
if (columnLabels.size > 0) {
|
|
11327
|
-
const normalizedLabels = new Set([...columnLabels.values()].map((label) => label.toLowerCase()));
|
|
11328
|
-
for (const [rowIndex, { row, cells }] of rows.entries()) {
|
|
11329
|
-
if (removeIds.has(row.id)) continue;
|
|
11330
|
-
if (rowIndex < firstLabelRowIndex) {
|
|
11331
|
-
for (const cell of cells) {
|
|
11332
|
-
const metadataLabel = metadataColumnName(cell.metadata);
|
|
11333
|
-
if (metadataLabel && isGenericColumnTitle(metadataLabel) && normalizedLabels.has(cleanText(cell.title, "").toLowerCase())) {
|
|
11334
|
-
updates.set(cell.id, {
|
|
11335
|
-
...currentNode(cell.id) ?? cell,
|
|
11336
|
-
title: metadataLabel,
|
|
11337
|
-
description: tableCellDescription(cell, metadataLabel),
|
|
11338
|
-
metadata: metadataWithColumnLabel(cell.metadata, metadataLabel)
|
|
11339
|
-
});
|
|
11340
|
-
rowsToRebuildText.add(row.id);
|
|
11341
|
-
}
|
|
11342
|
-
}
|
|
11343
|
-
continue;
|
|
11344
|
-
}
|
|
11345
|
-
for (const [fallbackIndex, cell] of cells.entries()) {
|
|
11346
|
-
const columnIndex = tableCellColumnIndex(cell, fallbackIndex);
|
|
11347
|
-
const label = columnLabels.get(columnIndex);
|
|
11348
|
-
if (!label) continue;
|
|
11349
|
-
const current = currentNode(cell.id) ?? cell;
|
|
11350
|
-
if (current.title === label && metadataColumnName(current.metadata) === label && (metadataTableColumnName(current.metadata) ?? label) === label) {
|
|
11351
|
-
continue;
|
|
11352
|
-
}
|
|
11353
|
-
updates.set(cell.id, {
|
|
11354
|
-
...current,
|
|
11355
|
-
title: label,
|
|
11356
|
-
description: tableCellDescription(current, label),
|
|
11357
|
-
metadata: metadataWithColumnLabel(current.metadata, label)
|
|
11358
|
-
});
|
|
11359
|
-
rowsToRebuildText.add(row.id);
|
|
11360
|
-
}
|
|
11361
|
-
if (cells.length > 0) rowsToRebuildText.add(row.id);
|
|
11362
|
-
}
|
|
11363
|
-
}
|
|
11364
|
-
const continuationRows = [
|
|
11365
|
-
...tableRepair.continuationRows,
|
|
11366
|
-
...implicitContinuationRows(rows, columnLabels, firstLabelRowIndex)
|
|
11367
|
-
];
|
|
11368
|
-
const seenContinuationRows = /* @__PURE__ */ new Set();
|
|
11369
|
-
for (const continuation of continuationRows) {
|
|
11370
|
-
const continuationKey = `${continuation.sourceRowNodeId}:${continuation.targetRowNodeId}`;
|
|
11371
|
-
if (seenContinuationRows.has(continuationKey)) continue;
|
|
11372
|
-
seenContinuationRows.add(continuationKey);
|
|
11373
|
-
if (!rowIds.has(continuation.sourceRowNodeId) || !rowIds.has(continuation.targetRowNodeId)) continue;
|
|
11374
|
-
if (continuation.sourceRowNodeId === continuation.targetRowNodeId) continue;
|
|
11375
|
-
const sourceIndex = rowOrder.get(continuation.sourceRowNodeId);
|
|
11376
|
-
const targetIndex = rowOrder.get(continuation.targetRowNodeId);
|
|
11377
|
-
if (sourceIndex === void 0 || targetIndex === void 0) continue;
|
|
11378
|
-
if (Math.abs(sourceIndex - targetIndex) > 3) continue;
|
|
11379
|
-
const sourceRow = currentNode(continuation.sourceRowNodeId);
|
|
11380
|
-
const targetRow = currentNode(continuation.targetRowNodeId);
|
|
11381
|
-
if (!sourceRow || !targetRow || sourceRow.kind !== "table_row" || targetRow.kind !== "table_row") continue;
|
|
11382
|
-
if (isSourceTreeHeaderRow(targetRow)) continue;
|
|
11383
|
-
const sourceCells = (byParent.get(sourceRow.id) ?? []).filter((node) => node.kind === "table_cell").map((node) => currentNode(node.id) ?? node);
|
|
11384
|
-
const targetCells = (byParent.get(targetRow.id) ?? []).filter((node) => node.kind === "table_cell").map((node) => currentNode(node.id) ?? node);
|
|
11385
|
-
const sourceText = tableRowTextForPrompt(sourceRow, sourceCells);
|
|
11386
|
-
if (!sourceText) continue;
|
|
11387
|
-
const targetCell = findCellForContinuation({
|
|
11388
|
-
cells: targetCells,
|
|
11389
|
-
targetColumnIndex: continuation.targetColumnIndex,
|
|
11390
|
-
targetColumnLabel: continuation.targetColumnLabel
|
|
11391
|
-
});
|
|
11392
|
-
if (!shouldMergeVisualContinuation({ sourceText, sourceCells, targetCell })) continue;
|
|
11393
|
-
const sourceNodes = [sourceRow, ...sourceCells];
|
|
11394
|
-
const sourceSpanIds = sourceSpanIdsForNodes(sourceNodes);
|
|
11395
|
-
let mergedIntoCells = false;
|
|
11396
|
-
if (sourceCells.length > 0 && targetCells.length > 0) {
|
|
11397
|
-
for (const sourceCell of sourceCells) {
|
|
11398
|
-
const cellText = tableCellValueText(sourceCell);
|
|
11399
|
-
if (!cellText) continue;
|
|
11400
|
-
const visualTargetCell = findCellByVisualPosition(sourceCell, targetCells) ?? targetCell;
|
|
11401
|
-
if (!visualTargetCell) continue;
|
|
11402
|
-
const currentTargetCell = currentNode(visualTargetCell.id) ?? visualTargetCell;
|
|
11403
|
-
const nextCellText = appendDistinctText(tableCellText(currentTargetCell), cellText);
|
|
11404
|
-
if (!nextCellText) continue;
|
|
11405
|
-
updates.set(visualTargetCell.id, {
|
|
11406
|
-
...currentTargetCell,
|
|
11407
|
-
textExcerpt: nextCellText,
|
|
11408
|
-
description: cleanText(
|
|
11409
|
-
[currentTargetCell.title, nextCellText].filter(Boolean).join(" | "),
|
|
11410
|
-
currentTargetCell.description
|
|
11411
|
-
),
|
|
11412
|
-
sourceSpanIds: [
|
|
11413
|
-
.../* @__PURE__ */ new Set([
|
|
11414
|
-
...currentTargetCell.sourceSpanIds,
|
|
11415
|
-
...sourceRow.sourceSpanIds,
|
|
11416
|
-
...sourceCell.sourceSpanIds
|
|
11417
|
-
])
|
|
11418
|
-
],
|
|
11419
|
-
bbox: mergedBbox([currentTargetCell, sourceCell]),
|
|
11420
|
-
metadata: {
|
|
11421
|
-
...currentTargetCell.metadata ?? {},
|
|
11422
|
-
visualTableRepair: "merged_continuation"
|
|
11423
|
-
}
|
|
11424
|
-
});
|
|
11425
|
-
mergedIntoCells = true;
|
|
11426
|
-
}
|
|
11427
|
-
if (mergedIntoCells) rowsToRebuildText.add(targetRow.id);
|
|
11428
|
-
}
|
|
11429
|
-
if (!mergedIntoCells && targetCell) {
|
|
11430
|
-
const nextCellText = appendDistinctText(tableCellText(targetCell), sourceText);
|
|
11431
|
-
if (nextCellText) {
|
|
11432
|
-
const mergedNodes = [targetCell, ...sourceNodes];
|
|
11433
|
-
updates.set(targetCell.id, {
|
|
11434
|
-
...currentNode(targetCell.id) ?? targetCell,
|
|
11435
|
-
textExcerpt: nextCellText,
|
|
11436
|
-
description: cleanText([targetCell.title, nextCellText].filter(Boolean).join(" | "), targetCell.description),
|
|
11437
|
-
sourceSpanIds: [.../* @__PURE__ */ new Set([...targetCell.sourceSpanIds, ...sourceSpanIds])],
|
|
11438
|
-
bbox: mergedBbox(mergedNodes),
|
|
11439
|
-
metadata: {
|
|
11440
|
-
...targetCell.metadata ?? {},
|
|
11441
|
-
visualTableRepair: "merged_continuation"
|
|
11442
|
-
}
|
|
11443
|
-
});
|
|
11444
|
-
rowsToRebuildText.add(targetRow.id);
|
|
11445
|
-
}
|
|
11446
|
-
}
|
|
11447
|
-
const fallbackRowText = mergedIntoCells ? void 0 : appendDistinctText(targetRow.textExcerpt ?? targetRow.description, sourceText);
|
|
11448
|
-
updates.set(targetRow.id, {
|
|
11449
|
-
...currentNode(targetRow.id) ?? targetRow,
|
|
11450
|
-
...mergedIntoCells ? {} : {
|
|
11451
|
-
textExcerpt: fallbackRowText,
|
|
11452
|
-
description: cleanText(
|
|
11453
|
-
[targetRow.title, fallbackRowText].filter(Boolean).join(" | "),
|
|
11454
|
-
targetRow.description
|
|
11455
|
-
)
|
|
11456
|
-
},
|
|
11457
|
-
sourceSpanIds: [.../* @__PURE__ */ new Set([...targetRow.sourceSpanIds, ...sourceSpanIds])],
|
|
11458
|
-
bbox: mergedBbox([targetRow, ...sourceNodes]),
|
|
11459
|
-
metadata: {
|
|
11460
|
-
...targetRow.metadata ?? {},
|
|
11461
|
-
visualTableRepair: "merged_continuation"
|
|
11462
|
-
}
|
|
11463
|
-
});
|
|
11464
|
-
removeIds.add(sourceRow.id);
|
|
11465
|
-
for (const sourceCell of sourceCells) removeIds.add(sourceCell.id);
|
|
11466
|
-
}
|
|
11467
|
-
}
|
|
11468
|
-
for (const rowId of rowsToRebuildText) {
|
|
11469
|
-
if (removeIds.has(rowId)) continue;
|
|
11470
|
-
const row = currentNode(rowId);
|
|
11471
|
-
if (!row || row.kind !== "table_row") continue;
|
|
11472
|
-
const cells = (byParent.get(row.id) ?? []).filter((node) => node.kind === "table_cell" && !removeIds.has(node.id)).map((node) => currentNode(node.id) ?? node).sort(
|
|
11473
|
-
(left, right) => tableCellColumnIndex(left, 0) - tableCellColumnIndex(right, 0) || left.order - right.order || left.id.localeCompare(right.id)
|
|
11474
|
-
);
|
|
11475
|
-
const textExcerpt = tableRowTextFromCells(cells);
|
|
11476
|
-
if (!textExcerpt) continue;
|
|
11477
|
-
updates.set(row.id, {
|
|
11478
|
-
...row,
|
|
11479
|
-
textExcerpt,
|
|
11480
|
-
description: cleanText([row.title, textExcerpt].filter(Boolean).join(" | "), row.description)
|
|
11481
|
-
});
|
|
11482
|
-
}
|
|
11483
|
-
if (updates.size === 0 && removeIds.size === 0) return sourceTree;
|
|
11484
|
-
const repaired = sourceTree.filter((node) => !removeIds.has(node.id)).map((node) => updates.get(node.id) ?? node);
|
|
11485
|
-
return normalizeDocumentSourceTreePaths(normalizeContainerEvidenceFromChildren(repaired));
|
|
11486
|
-
}
|
|
11487
|
-
async function runVisualTableRepair(params) {
|
|
11488
|
-
const candidates = visualTableCandidates(params.sourceTree);
|
|
11489
|
-
if (candidates.length === 0) return { sourceTree: params.sourceTree, warnings: [] };
|
|
11490
|
-
let sourceTree = params.sourceTree;
|
|
11491
|
-
const warnings = [];
|
|
11492
|
-
const repairResults = await Promise.all(candidates.map(async (candidate, index) => {
|
|
11493
|
-
const label = `source_tree_visual_table_repair_p${candidate.page}`;
|
|
11494
|
-
try {
|
|
11495
|
-
const budget = params.resolveBudget("extraction_visual_table_repair", 1800);
|
|
11496
|
-
const maxTokens = Math.min(budget.maxTokens, 2400);
|
|
11497
|
-
const startedAt = Date.now();
|
|
11498
|
-
const response = await safeGenerateObject(
|
|
11499
|
-
params.generateObject,
|
|
11500
|
-
{
|
|
11501
|
-
prompt: buildVisualTableRepairPrompt(candidate),
|
|
11502
|
-
schema: SourceTreeVisualTableRepairSchema,
|
|
11503
|
-
maxTokens,
|
|
11504
|
-
taskKind: "extraction_visual_table_repair",
|
|
11505
|
-
budgetDiagnostics: { ...budget, maxTokens },
|
|
11506
|
-
trace: {
|
|
11507
|
-
label,
|
|
11508
|
-
startPage: candidate.page,
|
|
11509
|
-
endPage: candidate.page,
|
|
11510
|
-
batchIndex: index + 1,
|
|
11511
|
-
batchCount: candidates.length,
|
|
11512
|
-
sourceBacked: true
|
|
11513
|
-
}
|
|
11514
|
-
},
|
|
11515
|
-
{
|
|
11516
|
-
fallback: { tables: [], warnings: [] },
|
|
11517
|
-
maxRetries: 0,
|
|
11518
|
-
log: params.log,
|
|
11519
|
-
retry: false
|
|
11520
|
-
}
|
|
11521
|
-
);
|
|
11522
|
-
return {
|
|
11523
|
-
index,
|
|
11524
|
-
candidate,
|
|
11525
|
-
label,
|
|
11526
|
-
maxTokens,
|
|
11527
|
-
durationMs: Date.now() - startedAt,
|
|
11528
|
-
usage: response.usage,
|
|
11529
|
-
repair: response.object
|
|
11530
|
-
};
|
|
11531
|
-
} catch (error) {
|
|
11532
|
-
return {
|
|
11533
|
-
index,
|
|
11534
|
-
candidate,
|
|
11535
|
-
error: error instanceof Error ? error.message : String(error)
|
|
11536
|
-
};
|
|
11537
|
-
}
|
|
11538
|
-
}));
|
|
11539
|
-
for (const result of repairResults.sort((left, right) => left.index - right.index)) {
|
|
11540
|
-
if ("error" in result) {
|
|
11541
|
-
warnings.push(`Visual table repair skipped on page ${result.candidate.page}; parsed table kept (${result.error})`);
|
|
11542
|
-
continue;
|
|
11543
|
-
}
|
|
11544
|
-
params.trackUsage(result.usage, {
|
|
11545
|
-
taskKind: "extraction_visual_table_repair",
|
|
11546
|
-
label: result.label,
|
|
11547
|
-
maxTokens: result.maxTokens,
|
|
11548
|
-
durationMs: result.durationMs
|
|
11549
|
-
});
|
|
11550
|
-
sourceTree = applyVisualTableRepair(sourceTree, result.repair);
|
|
11551
|
-
warnings.push(...result.repair.warnings.map(
|
|
11552
|
-
(warning) => `Visual table repair warning on page ${result.candidate.page}: ${warning}`
|
|
11553
|
-
));
|
|
11554
|
-
}
|
|
11555
|
-
return { sourceTree, warnings };
|
|
11556
|
-
}
|
|
11557
10832
|
function groupNodeId(documentId, group) {
|
|
11558
10833
|
return [
|
|
11559
10834
|
documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
|
|
@@ -11562,6 +10837,17 @@ function groupNodeId(documentId, group) {
|
|
|
11562
10837
|
group.childNodeIds.join("_").replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 80)
|
|
11563
10838
|
].join(":");
|
|
11564
10839
|
}
|
|
10840
|
+
function isDescendantOf(nodeId2, ancestorId, byId) {
|
|
10841
|
+
let parentId = nodeId2 ? byId.get(nodeId2)?.parentId : void 0;
|
|
10842
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10843
|
+
while (parentId) {
|
|
10844
|
+
if (parentId === ancestorId) return true;
|
|
10845
|
+
if (seen.has(parentId)) return false;
|
|
10846
|
+
seen.add(parentId);
|
|
10847
|
+
parentId = byId.get(parentId)?.parentId;
|
|
10848
|
+
}
|
|
10849
|
+
return false;
|
|
10850
|
+
}
|
|
11565
10851
|
function applyOrganization(sourceTree, organization) {
|
|
11566
10852
|
const byId = new Map(sourceTree.map((node) => [node.id, node]));
|
|
11567
10853
|
const labels = new Map(organization.labels.map((label) => [label.nodeId, label]));
|
|
@@ -11579,8 +10865,19 @@ function applyOrganization(sourceTree, organization) {
|
|
|
11579
10865
|
const children = group.childNodeIds.map((id2) => byId.get(id2)).filter((node2) => Boolean(node2));
|
|
11580
10866
|
if (children.length === 0) continue;
|
|
11581
10867
|
if (rejectsOrganizerGroup(group, children)) continue;
|
|
11582
|
-
const
|
|
11583
|
-
|
|
10868
|
+
const originalParentId = children[0].parentId;
|
|
10869
|
+
const requestedParent = group.parentNodeId ? byId.get(group.parentNodeId) : void 0;
|
|
10870
|
+
if (requestedParent && children.some((child) => child.id === requestedParent.id || isDescendantOf(requestedParent.id, child.id, byId))) {
|
|
10871
|
+
continue;
|
|
10872
|
+
}
|
|
10873
|
+
if (requestedParent) {
|
|
10874
|
+
if (!children.every((child) => child.parentId === requestedParent.id || hasAncestor(child, requestedParent.id, byId))) {
|
|
10875
|
+
continue;
|
|
10876
|
+
}
|
|
10877
|
+
} else if (!children.every((child) => child.parentId === originalParentId)) {
|
|
10878
|
+
continue;
|
|
10879
|
+
}
|
|
10880
|
+
const parentId = requestedParent?.id ?? originalParentId;
|
|
11584
10881
|
const documentId = children[0].documentId;
|
|
11585
10882
|
const title = simplifyOrganizerTitle(group.title, group.title, group.kind);
|
|
11586
10883
|
const description = descriptionWithPages(cleanText(group.description, title), children);
|
|
@@ -11682,7 +10979,6 @@ function materializeDocument(params) {
|
|
|
11682
10979
|
retroactiveDate: coverage.retroactiveDate,
|
|
11683
10980
|
formNumber: coverage.formNumber,
|
|
11684
10981
|
sectionRef: coverage.sectionRef,
|
|
11685
|
-
coverageOrigin: coverage.coverageOrigin,
|
|
11686
10982
|
endorsementNumber: coverage.endorsementNumber,
|
|
11687
10983
|
limits: coverage.limits,
|
|
11688
10984
|
sourceSpanIds: coverage.sourceSpanIds,
|
|
@@ -11715,7 +11011,7 @@ function materializeDocument(params) {
|
|
|
11715
11011
|
carrier !== "Unknown" ? carrier : void 0,
|
|
11716
11012
|
policyNumber !== "Unknown" ? `#${policyNumber}` : void 0,
|
|
11717
11013
|
insuredName !== "Unknown" ? `for ${insuredName}` : void 0,
|
|
11718
|
-
profile.
|
|
11014
|
+
profile.policyTypes.length ? `covering ${profile.policyTypes.slice(0, 5).join(", ")}` : void 0
|
|
11719
11015
|
].filter(Boolean).join(" ");
|
|
11720
11016
|
const base = {
|
|
11721
11017
|
id: params.id,
|
|
@@ -11777,44 +11073,7 @@ function materializeDocument(params) {
|
|
|
11777
11073
|
};
|
|
11778
11074
|
}
|
|
11779
11075
|
function coverageCleanupGroups(profile) {
|
|
11780
|
-
|
|
11781
|
-
const coreIndexes = [];
|
|
11782
|
-
const endorsementIndexes = [];
|
|
11783
|
-
const unclassifiedIndexes = [];
|
|
11784
|
-
profile.coverages.forEach((coverage, coverageIndex) => {
|
|
11785
|
-
if (coverage.coverageOrigin === "core") {
|
|
11786
|
-
coreIndexes.push(coverageIndex);
|
|
11787
|
-
} else if (coverage.coverageOrigin === "endorsement") {
|
|
11788
|
-
endorsementIndexes.push(coverageIndex);
|
|
11789
|
-
} else {
|
|
11790
|
-
unclassifiedIndexes.push(coverageIndex);
|
|
11791
|
-
}
|
|
11792
|
-
});
|
|
11793
|
-
if (coreIndexes.length) {
|
|
11794
|
-
groups.push({
|
|
11795
|
-
id: "policy",
|
|
11796
|
-
label: "Coverage schedule cleanup: policy schedules",
|
|
11797
|
-
coverageIndexes: coreIndexes
|
|
11798
|
-
});
|
|
11799
|
-
}
|
|
11800
|
-
if (endorsementIndexes.length) {
|
|
11801
|
-
groups.push({
|
|
11802
|
-
id: "endorsements",
|
|
11803
|
-
label: "Coverage schedule cleanup: endorsement schedules",
|
|
11804
|
-
coverageIndexes: endorsementIndexes
|
|
11805
|
-
});
|
|
11806
|
-
}
|
|
11807
|
-
if (unclassifiedIndexes.length) {
|
|
11808
|
-
groups.push({
|
|
11809
|
-
id: "source_backed",
|
|
11810
|
-
label: "Coverage schedule cleanup: source-backed schedules",
|
|
11811
|
-
coverageIndexes: unclassifiedIndexes
|
|
11812
|
-
});
|
|
11813
|
-
}
|
|
11814
|
-
return groups.length > 1 ? groups : [{
|
|
11815
|
-
id: "all",
|
|
11816
|
-
label: "Coverage schedule cleanup"
|
|
11817
|
-
}];
|
|
11076
|
+
return profile.coverages.length ? [{ id: "all", label: "Coverage schedule cleanup" }] : [];
|
|
11818
11077
|
}
|
|
11819
11078
|
async function cleanupOperationalCoverageSchedules(params) {
|
|
11820
11079
|
const groups = coverageCleanupGroups(params.operationalProfile);
|
|
@@ -11829,7 +11088,7 @@ async function cleanupOperationalCoverageSchedules(params) {
|
|
|
11829
11088
|
prompt: buildOperationalProfileCleanupPrompt(
|
|
11830
11089
|
params.sourceTree,
|
|
11831
11090
|
params.operationalProfile,
|
|
11832
|
-
{
|
|
11091
|
+
{ label: group.label }
|
|
11833
11092
|
),
|
|
11834
11093
|
schema: OperationalProfileCleanupSchema,
|
|
11835
11094
|
maxTokens: budget.maxTokens,
|
|
@@ -11839,7 +11098,7 @@ async function cleanupOperationalCoverageSchedules(params) {
|
|
|
11839
11098
|
trace: {
|
|
11840
11099
|
phase: "coverage_cleanup",
|
|
11841
11100
|
label: group.label,
|
|
11842
|
-
itemCount:
|
|
11101
|
+
itemCount: params.operationalProfile.coverages.length,
|
|
11843
11102
|
coverageGroup: group.id,
|
|
11844
11103
|
batchIndex: groups.length > 1 ? groupIndex + 1 : void 0,
|
|
11845
11104
|
batchCount: groups.length > 1 ? groups.length : void 0,
|
|
@@ -11987,15 +11246,7 @@ async function runSourceTreeExtraction(params) {
|
|
|
11987
11246
|
warnings.push(`Source-tree outline cleanup failed; deterministic tree used (${error instanceof Error ? error.message : String(error)})`);
|
|
11988
11247
|
}
|
|
11989
11248
|
}
|
|
11990
|
-
|
|
11991
|
-
sourceTree,
|
|
11992
|
-
generateObject: params.generateObject,
|
|
11993
|
-
resolveBudget: params.resolveBudget,
|
|
11994
|
-
trackUsage: localTrack,
|
|
11995
|
-
log: params.log
|
|
11996
|
-
});
|
|
11997
|
-
sourceTree = normalizeSourceTreeTableDisplayText(visualTableRepair.sourceTree);
|
|
11998
|
-
warnings.push(...visualTableRepair.warnings);
|
|
11249
|
+
sourceTree = normalizeSourceTreeTableDisplayText(sourceTree);
|
|
11999
11250
|
const emptyProfile = emptyOperationalProfile();
|
|
12000
11251
|
let operationalProfile = emptyProfile;
|
|
12001
11252
|
try {
|
|
@@ -12859,14 +12110,6 @@ ${pageText}`;
|
|
|
12859
12110
|
mergeMemoryResult(result.name, result.data, memory);
|
|
12860
12111
|
}
|
|
12861
12112
|
}
|
|
12862
|
-
const recoveredCoverages = recoverCoverageScheduleRows({
|
|
12863
|
-
memory,
|
|
12864
|
-
sourceSpans,
|
|
12865
|
-
pageAssignments
|
|
12866
|
-
});
|
|
12867
|
-
if (recoveredCoverages.recovered.length > 0) {
|
|
12868
|
-
await log?.(`Recovered ${recoveredCoverages.recovered.length} source-backed coverage schedule row(s) from table evidence`);
|
|
12869
|
-
}
|
|
12870
12113
|
const planIncludesSupplementary = tasks.some((task) => task.extractorName === "supplementary");
|
|
12871
12114
|
if (!planIncludesSupplementary && hasSupplementaryExtractionSignal(pageAssignments, formInventory, memory)) {
|
|
12872
12115
|
onProgress?.("Extracting supplementary retrieval facts...");
|
|
@@ -13033,14 +12276,6 @@ ${pageText}`;
|
|
|
13033
12276
|
mergeMemoryResult(result.name, result.data, memory);
|
|
13034
12277
|
}
|
|
13035
12278
|
}
|
|
13036
|
-
const recoveredCoverages = recoverCoverageScheduleRows({
|
|
13037
|
-
memory,
|
|
13038
|
-
sourceSpans,
|
|
13039
|
-
pageAssignments
|
|
13040
|
-
});
|
|
13041
|
-
if (recoveredCoverages.recovered.length > 0) {
|
|
13042
|
-
await log?.(`Recovered ${recoveredCoverages.recovered.length} source-backed coverage schedule row(s) from follow-up table evidence`);
|
|
13043
|
-
}
|
|
13044
12279
|
}
|
|
13045
12280
|
} else {
|
|
13046
12281
|
onProgress?.("Skipping LLM extraction review; deterministic checks passed.");
|
|
@@ -17293,7 +16528,7 @@ var COI_GENERATION_TOOL = {
|
|
|
17293
16528
|
};
|
|
17294
16529
|
var COVERAGE_COMPARISON_TOOL = {
|
|
17295
16530
|
name: "coverage_comparison",
|
|
17296
|
-
description: "Compare coverages across two or more insurance documents (policies and/or quotes). Returns a side-by-side comparison of
|
|
16531
|
+
description: "Compare coverages across two or more insurance documents (policies and/or quotes). Returns a side-by-side comparison of policy types, limits, and deductibles.",
|
|
17297
16532
|
input_schema: {
|
|
17298
16533
|
type: "object",
|
|
17299
16534
|
properties: {
|
|
@@ -17302,10 +16537,10 @@ var COVERAGE_COMPARISON_TOOL = {
|
|
|
17302
16537
|
items: { type: "string" },
|
|
17303
16538
|
description: "Array of document IDs (policies or quotes) to compare."
|
|
17304
16539
|
},
|
|
17305
|
-
|
|
16540
|
+
policyTypes: {
|
|
17306
16541
|
type: "array",
|
|
17307
16542
|
items: { type: "string" },
|
|
17308
|
-
description: "Optional filter: only compare these
|
|
16543
|
+
description: "Optional filter: only compare these policy types (e.g. 'General Liability', 'Workers Compensation'). Omit to compare all."
|
|
17309
16544
|
}
|
|
17310
16545
|
},
|
|
17311
16546
|
required: ["documentIds"]
|