@claritylabs/cl-sdk 1.3.3 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -572,6 +572,7 @@ var FormReferenceSchema = z3.object({
572
572
  var TaxFeeItemSchema = z3.object({
573
573
  name: z3.string(),
574
574
  amount: z3.string(),
575
+ amountValue: z3.number().optional(),
575
576
  type: z3.enum(["tax", "fee", "surcharge", "assessment"]).optional(),
576
577
  description: z3.string().optional()
577
578
  });
@@ -616,9 +617,11 @@ var CoverageValueTypeSchema = z4.enum([
616
617
  var CoverageSchema = z4.object({
617
618
  name: z4.string(),
618
619
  limit: z4.string(),
620
+ limitAmount: z4.number().optional(),
619
621
  limitType: LimitTypeSchema.optional(),
620
622
  limitValueType: CoverageValueTypeSchema.optional(),
621
623
  deductible: z4.string().optional(),
624
+ deductibleAmount: z4.number().optional(),
622
625
  deductibleValueType: CoverageValueTypeSchema.optional(),
623
626
  formNumber: z4.string().optional(),
624
627
  pageNumber: z4.number().optional(),
@@ -634,9 +637,11 @@ var EnrichedCoverageSchema = z4.object({
634
637
  formNumber: z4.string().optional(),
635
638
  formEditionDate: z4.string().optional(),
636
639
  limit: z4.string(),
640
+ limitAmount: z4.number().optional(),
637
641
  limitType: LimitTypeSchema.optional(),
638
642
  limitValueType: CoverageValueTypeSchema.optional(),
639
643
  deductible: z4.string().optional(),
644
+ deductibleAmount: z4.number().optional(),
640
645
  deductibleType: DeductibleTypeSchema.optional(),
641
646
  deductibleValueType: CoverageValueTypeSchema.optional(),
642
647
  sir: z4.string().optional(),
@@ -1317,7 +1322,8 @@ var UnderwritingConditionSchema = z16.object({
1317
1322
  });
1318
1323
  var PremiumLineSchema = z16.object({
1319
1324
  line: z16.string(),
1320
- amount: z16.string()
1325
+ amount: z16.string(),
1326
+ amountValue: z16.number().optional()
1321
1327
  });
1322
1328
  var AuxiliaryFactSchema = z16.object({
1323
1329
  key: z16.string(),
@@ -1360,6 +1366,7 @@ var BaseDocumentFields = {
1360
1366
  security: z16.string().optional(),
1361
1367
  insuredName: z16.string(),
1362
1368
  premium: z16.string().optional(),
1369
+ premiumAmount: z16.number().optional(),
1363
1370
  summary: z16.string().optional(),
1364
1371
  policyTypes: z16.array(z16.string()).optional(),
1365
1372
  coverages: z16.array(CoverageSchema),
@@ -1411,8 +1418,11 @@ var BaseDocumentFields = {
1411
1418
  mortgageHolders: z16.array(EndorsementPartySchema).optional(),
1412
1419
  taxesAndFees: z16.array(TaxFeeItemSchema).optional(),
1413
1420
  totalCost: z16.string().optional(),
1421
+ totalCostAmount: z16.number().optional(),
1414
1422
  minimumPremium: z16.string().optional(),
1423
+ minimumPremiumAmount: z16.number().optional(),
1415
1424
  depositPremium: z16.string().optional(),
1425
+ depositPremiumAmount: z16.number().optional(),
1416
1426
  paymentPlan: PaymentPlanSchema.optional(),
1417
1427
  auditType: AuditTypeSchema.optional(),
1418
1428
  ratingBasis: z16.array(RatingBasisSchema).optional(),
@@ -2901,16 +2911,6 @@ function findFieldValue(fields, patterns, reject) {
2901
2911
  const match = fields.find((f) => fieldMatches(f.field, patterns) && !reject?.(f));
2902
2912
  return match?.value;
2903
2913
  }
2904
- function stringValue(value) {
2905
- return typeof value === "string" && value.trim() ? value : void 0;
2906
- }
2907
- function findRawString(raw, keys) {
2908
- for (const key of keys) {
2909
- const value = stringValue(raw[key]);
2910
- if (value) return value;
2911
- }
2912
- return void 0;
2913
- }
2914
2914
  function promoteRawFields(raw, mappings) {
2915
2915
  for (const { from, to } of mappings) {
2916
2916
  if (!raw[to] && raw[from]) {
@@ -2922,11 +2922,6 @@ function promoteRawFields(raw, mappings) {
2922
2922
  function findRawOrDeclarationValue(raw, fields, lookup) {
2923
2923
  return (lookup.rawKey ? raw[lookup.rawKey] : void 0) || findFieldValue(fields, lookup.patterns, lookup.reject);
2924
2924
  }
2925
- function promoteRawOrDeclarationString(raw, fields, targetKey, rawKeys, lookup) {
2926
- if (raw[targetKey]) return;
2927
- const value = findRawString(raw, rawKeys) ?? findFieldValue(fields, lookup.patterns, lookup.reject);
2928
- if (value) raw[targetKey] = value;
2929
- }
2930
2925
  function promoteCarrierFields(doc) {
2931
2926
  const raw = doc;
2932
2927
  promoteRawFields(raw, [
@@ -3126,298 +3121,11 @@ function promoteLocations(doc) {
3126
3121
  raw.locations = locations;
3127
3122
  }
3128
3123
  }
3129
- function normalizeName(name) {
3130
- return name.toLowerCase().replace(/[^a-z0-9]/g, "");
3131
- }
3132
- var LIMIT_COVERAGE_MAP = [
3133
- // GL standard
3134
- [["eachoccurrence", "peroccurrence", "occurrencecombined"], "perOccurrence"],
3135
- [["generalaggregate"], "generalAggregate"],
3136
- [["productscompletedoperationsaggregate", "productscompletedopsaggregate", "prodcompopsagg"], "productsCompletedOpsAggregate"],
3137
- [["personaladvertisinginjury", "personaladvinjury", "pai"], "personalAdvertisingInjury"],
3138
- [["firedamage", "firedamagelegalliability", "damagetorentedpremises", "damagetopremisesrentedtoyou"], "fireDamage"],
3139
- [["medicalexpense", "medexp", "medicalexpenseanypersonanyperson", "medicalexpenseanyone"], "medicalExpense"],
3140
- // Auto
3141
- [["combinedsingle", "combinedsinglelimit", "csl"], "combinedSingleLimit"],
3142
- [["bodilyinjuryperperson", "biperperson"], "bodilyInjuryPerPerson"],
3143
- [["bodilyinjuryperaccident", "biperaccident"], "bodilyInjuryPerAccident"],
3144
- [["propertydamage", "pdperaccident"], "propertyDamage"],
3145
- // Umbrella/Excess
3146
- [["umbrellaoccurrence", "eachoccurrenceumbrella", "excessoccurrence", "excesseachoccurrence"], "eachOccurrenceUmbrella"],
3147
- [["umbrellaaggregate", "excessaggregate"], "umbrellaAggregate"],
3148
- [["umbrella retention", "selfinsuredretention", "sir", "excessretention"], "umbrellaRetention"]
3149
- ];
3150
- function synthesizeLimits(doc) {
3151
- const raw = doc;
3152
- if (raw.limits && typeof raw.limits === "object" && Object.keys(raw.limits).length > 0) return;
3153
- const coverages = doc.coverages;
3154
- if (!coverages || coverages.length === 0) return;
3155
- const limits = {};
3156
- for (const cov of coverages) {
3157
- if (!cov.name || !cov.limit) continue;
3158
- const normalized = normalizeName(cov.name);
3159
- for (const [patterns, fieldName] of LIMIT_COVERAGE_MAP) {
3160
- if (patterns.some((p) => normalized.includes(p) || p.includes(normalized))) {
3161
- if (fieldName.includes("Aggregate") || fieldName.includes("aggregate")) {
3162
- if (!cov.limitType || cov.limitType === "aggregate") {
3163
- limits[fieldName] = cov.limit;
3164
- }
3165
- } else {
3166
- if (!limits[fieldName]) {
3167
- limits[fieldName] = cov.limit;
3168
- }
3169
- }
3170
- break;
3171
- }
3172
- }
3173
- }
3174
- const hasStatutory = coverages.some(
3175
- (c) => c.limitType === "statutory" || normalizeName(c.name ?? "").includes("statutory")
3176
- );
3177
- if (hasStatutory) {
3178
- limits.statutory = "true";
3179
- }
3180
- const elCoverages = coverages.filter(
3181
- (c) => normalizeName(c.name ?? "").includes("employersliability")
3182
- );
3183
- if (elCoverages.length > 0) {
3184
- const el = {};
3185
- for (const c of elCoverages) {
3186
- if (!c.limit) continue;
3187
- const n = normalizeName(c.name ?? "");
3188
- if (n.includes("accident") || n.includes("eachaccident")) el.eachAccident = c.limit;
3189
- else if (n.includes("diseasepolicy") || n.includes("diseasepolicylimit")) el.diseasePolicyLimit = c.limit;
3190
- else if (n.includes("diseaseemployee") || n.includes("diseaseeachemployee")) el.diseaseEachEmployee = c.limit;
3191
- else if (!el.eachAccident) el.eachAccident = c.limit;
3192
- }
3193
- if (Object.keys(el).length > 0) {
3194
- limits.employersLiability = el;
3195
- }
3196
- }
3197
- if (Object.keys(limits).length > 0) {
3198
- const result = { ...limits };
3199
- if (result.statutory === "true") result.statutory = true;
3200
- raw.limits = result;
3201
- }
3202
- }
3203
- function synthesizeDeductibles(doc) {
3204
- const raw = doc;
3205
- if (raw.deductibles && typeof raw.deductibles === "object" && Object.keys(raw.deductibles).length > 0) return;
3206
- const coverages = doc.coverages;
3207
- if (!coverages || coverages.length === 0) return;
3208
- const deductibleValues = coverages.filter((c) => c.deductible && c.deductible.trim() !== "" && c.deductible !== "N/A" && c.deductible !== "None").map((c) => c.deductible);
3209
- if (deductibleValues.length === 0) return;
3210
- const freq = /* @__PURE__ */ new Map();
3211
- for (const d of deductibleValues) {
3212
- freq.set(d, (freq.get(d) ?? 0) + 1);
3213
- }
3214
- let mostCommon = deductibleValues[0];
3215
- let maxFreq = 0;
3216
- for (const [val, count] of freq) {
3217
- if (count > maxFreq) {
3218
- mostCommon = val;
3219
- maxFreq = count;
3220
- }
3221
- }
3222
- const deductibles = {};
3223
- const hasPerClaim = coverages.some(
3224
- (c) => c.deductible && normalizeName(c.name ?? "").includes("perclaim")
3225
- );
3226
- if (hasPerClaim) {
3227
- deductibles.perClaim = mostCommon;
3228
- } else {
3229
- deductibles.perOccurrence = mostCommon;
3230
- }
3231
- const sirCoverage = coverages.find(
3232
- (c) => c.deductible && (normalizeName(c.name ?? "").includes("selfinsuredretention") || normalizeName(c.name ?? "").includes("sir"))
3233
- );
3234
- if (sirCoverage?.deductible) {
3235
- deductibles.selfInsuredRetention = sirCoverage.deductible;
3236
- }
3237
- const aggDed = coverages.find(
3238
- (c) => c.deductible && normalizeName(c.name ?? "").includes("aggregatedeductible")
3239
- );
3240
- if (aggDed?.deductible) {
3241
- deductibles.aggregateDeductible = aggDed.deductible;
3242
- }
3243
- if (Object.keys(deductibles).length > 0) {
3244
- raw.deductibles = deductibles;
3245
- }
3246
- }
3247
- var PREMIUM_PATTERNS = [
3248
- "premium",
3249
- "premiumAmount",
3250
- "premium amount",
3251
- "totalPremium",
3252
- "total premium",
3253
- "totalPolicyPremium",
3254
- "total policy premium",
3255
- "annualPremium",
3256
- "annual premium",
3257
- "estimatedAnnualPremium",
3258
- "estimated annual premium",
3259
- "policyPremium",
3260
- "policy premium",
3261
- "basePremium",
3262
- "base premium",
3263
- "planCost",
3264
- "plan cost",
3265
- "policyCost",
3266
- "policy cost",
3267
- "premiumSubtotal",
3268
- "premium subtotal",
3269
- "subtotalPremium",
3270
- "subtotal premium",
3271
- "quotedPremium",
3272
- "quoted premium"
3273
- ];
3274
- var TOTAL_COST_PATTERNS = [
3275
- "totalCost",
3276
- "total cost",
3277
- "total",
3278
- "totalDue",
3279
- "total due",
3280
- "amountPaid",
3281
- "amount paid",
3282
- "totalPaid",
3283
- "total paid",
3284
- "totalPrice",
3285
- "total price",
3286
- "totalTripCost",
3287
- "total trip cost",
3288
- "amountCharged",
3289
- "amount charged",
3290
- "amountDue",
3291
- "amount due",
3292
- "totalAmountDue",
3293
- "total amount due",
3294
- "totalAmount",
3295
- "total amount",
3296
- "grandTotal",
3297
- "grand total",
3298
- "totalPayable",
3299
- "total payable",
3300
- "totalCharges",
3301
- "total charges",
3302
- "totalPolicyCost",
3303
- "total policy cost"
3304
- ];
3305
- var PREMIUM_RAW_KEYS = [
3306
- "premium",
3307
- "premiumAmount",
3308
- "premium_amount",
3309
- "totalPremium",
3310
- "totalPolicyPremium",
3311
- "annualPremium",
3312
- "estimatedAnnualPremium",
3313
- "policyPremium",
3314
- "basePremium",
3315
- "planCost",
3316
- "policyCost",
3317
- "premiumSubtotal",
3318
- "subtotalPremium",
3319
- "quotedPremium"
3320
- ];
3321
- var TOTAL_COST_RAW_KEYS = [
3322
- "totalCost",
3323
- "total_cost",
3324
- "total",
3325
- "totalDue",
3326
- "amountPaid",
3327
- "amount_paid",
3328
- "totalPaid",
3329
- "total_paid",
3330
- "totalPrice",
3331
- "totalTripCost",
3332
- "amountCharged",
3333
- "amountDue",
3334
- "totalAmountDue",
3335
- "totalAmount",
3336
- "grandTotal",
3337
- "totalPayable",
3338
- "totalCharges",
3339
- "totalPolicyCost"
3340
- ];
3341
- function isTaxOrFeeField(fieldName) {
3342
- const normalized = normalizeFieldName(fieldName);
3343
- return /tax|gst|hst|pst|qst|fee|surcharge|assessment|stamp|filing|inspection/.test(normalized);
3344
- }
3345
- function isTotalCostField(fieldName) {
3346
- return fieldMatches(fieldName, TOTAL_COST_PATTERNS);
3347
- }
3348
- function taxFeeType(fieldName) {
3349
- const normalized = normalizeFieldName(fieldName);
3350
- if (normalized.includes("tax") || ["gst", "hst", "pst", "qst"].some((token) => normalized.includes(token))) return "tax";
3351
- if (normalized.includes("surcharge")) return "surcharge";
3352
- if (normalized.includes("assessment")) return "assessment";
3353
- if (normalized.includes("fee") || normalized.includes("stamp") || normalized.includes("filing")) return "fee";
3354
- return void 0;
3355
- }
3356
- function titleizeFieldName(fieldName) {
3357
- const spaced = fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
3358
- return spaced.replace(/\b\w/g, (letter) => letter.toUpperCase());
3359
- }
3360
- function taxFeeKey(item) {
3361
- return [
3362
- normalizeFieldName(item.name),
3363
- normalizeFieldName(item.amount),
3364
- item.type ?? ""
3365
- ].join("|");
3366
- }
3367
- function taxFeeItemFromField(field) {
3368
- const type = taxFeeType(field.field);
3369
- return {
3370
- name: titleizeFieldName(field.field),
3371
- amount: absorbNegative(field.value),
3372
- ...type ? { type } : {}
3373
- };
3374
- }
3375
- function absorbNegative(value) {
3376
- return value.replace(/^-\s*/, "").replace(/^\(\s*(.*?)\s*\)$/, "$1");
3377
- }
3378
- function promotePremium(doc) {
3379
- const raw = doc;
3380
- const fields = getDeclarationFields(doc);
3381
- promoteRawOrDeclarationString(raw, fields, "premium", PREMIUM_RAW_KEYS, {
3382
- patterns: PREMIUM_PATTERNS,
3383
- reject: (field) => isTaxOrFeeField(field.field)
3384
- });
3385
- promoteRawOrDeclarationString(raw, fields, "totalCost", TOTAL_COST_RAW_KEYS, {
3386
- patterns: TOTAL_COST_PATTERNS
3387
- });
3388
- if (typeof raw.premium === "string") raw.premium = absorbNegative(raw.premium);
3389
- if (typeof raw.totalCost === "string") raw.totalCost = absorbNegative(raw.totalCost);
3390
- }
3391
- function synthesizeTaxesAndFees(doc) {
3392
- const raw = doc;
3393
- const fields = getDeclarationFields(doc);
3394
- if (fields.length === 0) return;
3395
- const existing = Array.isArray(raw.taxesAndFees) ? raw.taxesAndFees : [];
3396
- const byKey = /* @__PURE__ */ new Map();
3397
- for (const item of existing) {
3398
- if (!item?.name || !item?.amount) continue;
3399
- byKey.set(taxFeeKey(item), item);
3400
- }
3401
- for (const field of fields) {
3402
- if (!field.value?.trim()) continue;
3403
- if (!isTaxOrFeeField(field.field)) continue;
3404
- if (isTotalCostField(field.field)) continue;
3405
- const item = taxFeeItemFromField(field);
3406
- byKey.set(taxFeeKey(item), item);
3407
- }
3408
- if (byKey.size > 0) {
3409
- raw.taxesAndFees = [...byKey.values()];
3410
- }
3411
- }
3412
3124
  function promoteExtractedFields(doc) {
3413
3125
  promoteCarrierFields(doc);
3414
3126
  promoteBroker(doc);
3415
3127
  promoteLossPayees(doc);
3416
3128
  promoteLocations(doc);
3417
- synthesizeLimits(doc);
3418
- synthesizeDeductibles(doc);
3419
- synthesizeTaxesAndFees(doc);
3420
- promotePremium(doc);
3421
3129
  }
3422
3130
 
3423
3131
  // src/extraction/alignment.ts
@@ -6140,6 +5848,12 @@ Focus on:
6140
5848
 
6141
5849
  Look on the declarations page, named insured schedule, loss payee schedule, mortgagee schedule, and any endorsements that add or modify named insureds, loss payees, or mortgage holders.
6142
5850
 
5851
+ Critical rules:
5852
+ - Prefer declaration-table labels such as "Named Insured", "Named Insured and Address", "Applicant", or "Insured" over contact blocks, notice contacts, authorized officers, licensing statements, signatures, and corporate-authority wording.
5853
+ - Do not use an authorized officer, broker, producer, contact person, officer title, email address owner, or licensing/entity-status statement as the primary insured unless that exact person/entity is explicitly labeled as the named insured.
5854
+ - If a row combines the insured name with a mailing address, put the legal name in insuredName and the mailing address in insuredAddress.
5855
+ - Entity type must come from the insured's own legal suffix or an explicit declaration field, not from generic incorporation/licensing notices elsewhere in the policy.
5856
+
6143
5857
  Return JSON only.`;
6144
5858
  }
6145
5859
 
@@ -6173,7 +5887,9 @@ For EACH coverage, also extract:
6173
5887
  - sectionRef: the declarations/schedule/endorsement section heading where it appears
6174
5888
  - originalContent: the verbatim row or short source snippet used for this coverage
6175
5889
  - limitType: when applicable, classify the limit as per_occurrence, per_claim, aggregate, per_person, per_accident, statutory, blanket, or scheduled
5890
+ - limitAmount: the limit as a plain number with no currency symbols or commas when the source states a fixed numeric currency amount
6176
5891
  - limitValueType: classify the limit as numeric, included, not_included, as_stated, waiting_period, referential, or other
5892
+ - deductibleAmount: the deductible or retention as a plain number with no currency symbols or commas when the source states a fixed numeric currency amount
6177
5893
  - deductibleValueType: classify the deductible/value term similarly when deductible is present
6178
5894
 
6179
5895
  Critical rules:
@@ -6182,6 +5898,7 @@ Critical rules:
6182
5898
  - Do not treat a generic waiting period, deductible explanation, limits clause, coinsurance clause, or definitions text as a standalone coverage unless the page contains an actual policy-specific schedule row or declaration entry.
6183
5899
  - Values like "Included" or "Not Included" are valid only when they appear as an explicit declarations/schedule/endorsement entry for a named coverage. Do not infer them from narrative form language.
6184
5900
  - If a waiting period or hour deductible is shown as part of a specific declarations/schedule row, it may be captured in deductible. Otherwise omit it.
5901
+ - Only populate limitAmount and deductibleAmount from actual policy-specific declaration, schedule, or endorsement values. Do not calculate them from examples, definitions, rating narratives, or generic form language.
6185
5902
  - Use limitValueType or deductibleValueType to preserve non-numeric terms precisely instead of forcing them into numeric semantics.
6186
5903
  - Preserve one row per real coverage entry. Do not merge adjacent schedule rows into malformed names.
6187
5904
  - Keep individual/per-occurrence limits separate from aggregate limits even when they have the same coverage name, limit amount, deductible, and form number. Use limitType to distinguish them.
@@ -6401,22 +6118,28 @@ Return JSON only.`;
6401
6118
  import { z as z32 } from "zod";
6402
6119
  var PremiumBreakdownSchema = z32.object({
6403
6120
  premium: z32.string().optional().describe("Total premium amount, e.g. '$5,000'"),
6121
+ premiumAmount: z32.number().optional().describe("Total premium as a plain number with no currency symbols or commas"),
6404
6122
  totalCost: z32.string().optional().describe("Total cost including taxes and fees, e.g. '$5,250'"),
6123
+ totalCostAmount: z32.number().optional().describe("Total cost as a plain number with no currency symbols or commas"),
6405
6124
  premiumBreakdown: z32.array(
6406
6125
  z32.object({
6407
6126
  line: z32.string().describe("Coverage line name"),
6408
- amount: z32.string().describe("Premium amount for this line")
6127
+ amount: z32.string().describe("Premium amount for this line"),
6128
+ amountValue: z32.number().optional().describe("Premium amount as a plain number with no currency symbols or commas")
6409
6129
  })
6410
6130
  ).optional().describe("Per-coverage-line premium breakdown"),
6411
6131
  taxesAndFees: z32.array(
6412
6132
  z32.object({
6413
6133
  name: z32.string().describe("Fee or tax name"),
6414
6134
  amount: z32.string().describe("Dollar amount"),
6135
+ amountValue: z32.number().optional().describe("Fee or tax amount as a plain number with no currency symbols or commas"),
6415
6136
  type: z32.enum(["tax", "fee", "surcharge", "assessment"]).optional().describe("Fee category")
6416
6137
  })
6417
6138
  ).optional().describe("Taxes, fees, surcharges, and assessments"),
6418
6139
  minimumPremium: z32.string().optional().describe("Minimum premium if stated"),
6140
+ minimumPremiumAmount: z32.number().optional().describe("Minimum premium as a plain number when the source states a fixed amount"),
6419
6141
  depositPremium: z32.string().optional().describe("Deposit premium if stated"),
6142
+ depositPremiumAmount: z32.number().optional().describe("Deposit premium as a plain number when the source states a fixed amount"),
6420
6143
  paymentPlan: z32.string().optional().describe("Payment plan description"),
6421
6144
  auditType: z32.enum(["annual", "semi_annual", "quarterly", "monthly", "final", "self"]).optional().describe("Premium audit type"),
6422
6145
  ratingBasis: z32.string().optional().describe("Rating basis, e.g. payroll, revenue, area, units")
@@ -6426,6 +6149,7 @@ function buildPremiumBreakdownPrompt() {
6426
6149
 
6427
6150
  Focus on:
6428
6151
  - Total premium and total cost (including taxes/fees)
6152
+ - Plain numeric amount fields for stated money values, without currency symbols or commas
6429
6153
  - Per-coverage-line premium breakdown if available
6430
6154
  - Taxes, fees, surcharges, and assessments with their amounts and types
6431
6155
  - Minimum premium and deposit premium if stated
@@ -6434,6 +6158,7 @@ Focus on:
6434
6158
  - Rating basis: payroll, revenue, area, units, or other
6435
6159
 
6436
6160
  Look on the declarations page, premium summary, and any premium/cost schedules.
6161
+ Prefer premium tables and schedules over definitions, exclusions, rating-basis narratives, licensing statements, or descriptions of premium trust funds. Do not use unrelated business volume, controlled written premium, deductible, limit, tax-only, fee-only, or percentage-only values as the policy premium.
6437
6162
 
6438
6163
  Return JSON only.`;
6439
6164
  }
@@ -7841,41 +7566,11 @@ function toReviewRoundRecord(round, review) {
7841
7566
  }
7842
7567
 
7843
7568
  // src/extraction/planning.ts
7844
- function normalizePageAssignments(pageAssignments, formInventory) {
7845
- const pageFormTypes = /* @__PURE__ */ new Map();
7846
- if (formInventory) {
7847
- for (const form of formInventory.forms) {
7848
- if (form.pageStart != null) {
7849
- const end = form.pageEnd ?? form.pageStart;
7850
- for (let p = form.pageStart; p <= end; p += 1) {
7851
- const types = pageFormTypes.get(p) ?? /* @__PURE__ */ new Set();
7852
- types.add(form.formType);
7853
- pageFormTypes.set(p, types);
7854
- }
7855
- }
7856
- }
7857
- }
7569
+ function normalizePageAssignments(pageAssignments, _formInventory) {
7858
7570
  return pageAssignments.map((assignment) => {
7859
- let extractorNames = [...new Set(
7571
+ const extractorNames = [...new Set(
7860
7572
  assignment.extractorNames.filter(Boolean)
7861
7573
  )];
7862
- const hasDeclarations = extractorNames.includes("declarations");
7863
- const hasConditions = extractorNames.includes("conditions");
7864
- const hasExclusions = extractorNames.includes("exclusions");
7865
- const hasEndorsements = extractorNames.includes("endorsements");
7866
- const looksLikeScheduleValues = assignment.hasScheduleValues === true;
7867
- const roleBlocksCoverageLimits = assignment.pageRole === "policy_form" || assignment.pageRole === "condition_exclusion_form" || assignment.pageRole === "endorsement_form";
7868
- const inventoryTypes = pageFormTypes.get(assignment.localPageNumber);
7869
- const inventoryBlocksCoverageLimits = inventoryTypes != null && !looksLikeScheduleValues && !hasDeclarations && (inventoryTypes.has("endorsement") || inventoryTypes.has("notice") || inventoryTypes.has("application"));
7870
- if (extractorNames.includes("coverage_limits")) {
7871
- const shouldDropCoverageLimits = inventoryBlocksCoverageLimits || !looksLikeScheduleValues && roleBlocksCoverageLimits || !hasDeclarations && !looksLikeScheduleValues && (hasConditions || hasExclusions) || !hasDeclarations && !looksLikeScheduleValues && hasEndorsements;
7872
- if (shouldDropCoverageLimits) {
7873
- extractorNames = extractorNames.filter((name) => name !== "coverage_limits");
7874
- }
7875
- }
7876
- if (inventoryTypes?.has("endorsement") && !extractorNames.includes("endorsements")) {
7877
- extractorNames = [...extractorNames, "endorsements"];
7878
- }
7879
7574
  return {
7880
7575
  ...assignment,
7881
7576
  extractorNames
@@ -8214,6 +7909,18 @@ function createExtractor(config) {
8214
7909
  return typeof start === "number" && typeof end === "number" && start <= endPage && end >= startPage;
8215
7910
  });
8216
7911
  }
7912
+ function formatSourceSpanText(spans) {
7913
+ return spans.filter((span) => span.text.trim().length > 0).map((span) => {
7914
+ const page = pageNumberForSpan(span);
7915
+ const label = [
7916
+ page ? `Page ${page}` : void 0,
7917
+ span.sectionId,
7918
+ span.formNumber
7919
+ ].filter(Boolean).join(" | ");
7920
+ return label ? `${label}
7921
+ ${span.text}` : span.text;
7922
+ }).join("\n\n---\n\n");
7923
+ }
8217
7924
  function inferSectionType(title, text) {
8218
7925
  const value = `${title} ${text.slice(0, 500)}`.toLowerCase();
8219
7926
  if (/\bdefinition|defined terms?\b/.test(value)) return "definition";
@@ -8250,93 +7957,6 @@ function createExtractor(config) {
8250
7957
  if (!line) return void 0;
8251
7958
  return line.slice(0, 100);
8252
7959
  }
8253
- function buildDeterministicFormInventory(spans, pageCount) {
8254
- if (spans.length === 0) return void 0;
8255
- const formPattern = /\b([A-Z]{1,4}\s?\d{2,5}(?:\s?\d{2,4})?|[A-Z]{2,8}-\d{2,8})\b/g;
8256
- const seen = /* @__PURE__ */ new Map();
8257
- for (const span of spans) {
8258
- const page = pageNumberForSpan(span);
8259
- if (!page) continue;
8260
- const title = firstHeadingLine(span.text) ?? span.sectionId;
8261
- const matches = /* @__PURE__ */ new Set([
8262
- ...span.formNumber ? [span.formNumber] : [],
8263
- ...Array.from(span.text.slice(0, 1200).matchAll(formPattern), (match) => match[1].trim())
8264
- ]);
8265
- for (const formNumber of matches) {
8266
- const key = `${formNumber}:${title ?? ""}`;
8267
- const existing = seen.get(key);
8268
- const formType = inferFormType(title ?? "", span.text);
8269
- if (existing) {
8270
- existing.pageStart = Math.min(existing.pageStart ?? page, page);
8271
- existing.pageEnd = Math.max(existing.pageEnd ?? page, page);
8272
- continue;
8273
- }
8274
- seen.set(key, {
8275
- formNumber,
8276
- title,
8277
- formType,
8278
- pageStart: Math.min(page, pageCount),
8279
- pageEnd: Math.min(page, pageCount)
8280
- });
8281
- }
8282
- }
8283
- const forms = [...seen.values()].sort((a, b) => (a.pageStart ?? 0) - (b.pageStart ?? 0));
8284
- return forms.length > 0 ? { forms } : void 0;
8285
- }
8286
- function inferFormType(title, text) {
8287
- const value = `${title} ${text.slice(0, 700)}`.toLowerCase();
8288
- if (/\bdeclarations?|schedule\b/.test(value)) return "declarations";
8289
- if (/\bendorsement|amendatory|additional insured\b/.test(value)) return "endorsement";
8290
- if (/\bnotice|department of insurance|complaint|privacy\b/.test(value)) return "notice";
8291
- if (/\bapplication|questionnaire\b/.test(value)) return "application";
8292
- if (/\bcoverage|policy form|conditions|exclusions|definitions|causes of loss\b/.test(value)) return "coverage";
8293
- return "other";
8294
- }
8295
- function buildDeterministicPageAssignments(spans, pageCount) {
8296
- if (spans.length === 0) return [];
8297
- const pageTexts = /* @__PURE__ */ new Map();
8298
- for (const span of spans) {
8299
- const page = pageNumberForSpan(span);
8300
- if (!page) continue;
8301
- pageTexts.set(page, [pageTexts.get(page), span.sectionId, span.text].filter(Boolean).join("\n"));
8302
- }
8303
- return Array.from({ length: pageCount }, (_, index) => {
8304
- const page = index + 1;
8305
- const text = pageTexts.get(page) ?? "";
8306
- const lower = text.toLowerCase();
8307
- const extractorNames = [];
8308
- if (/\bdeclarations?|policy period|named insured|producer|schedule\b/.test(lower)) {
8309
- extractorNames.push("carrier_info", "named_insured", "declarations");
8310
- }
8311
- if (/\blimit|deductible|premium|coinsurance|scheduled amount|blanket\b/.test(lower) && /\$|\b\d{2,}(?:,\d{3})*\b/.test(lower)) {
8312
- extractorNames.push("coverage_limits");
8313
- }
8314
- if (/\bpremium|tax|fee|surcharge\b/.test(lower) && /\$/.test(lower)) extractorNames.push("premium_breakdown");
8315
- if (/\bendorsement|additional insured|amendatory\b/.test(lower)) extractorNames.push("endorsements");
8316
- if (/\bexclusion|not covered|does not apply\b/.test(lower)) extractorNames.push("exclusions");
8317
- if (/\bcondition|duties in the event|loss condition|general condition\b/.test(lower)) extractorNames.push("conditions");
8318
- if (/\bdefinition|defined terms?\b/.test(lower)) extractorNames.push("definitions");
8319
- if (/\bcovered cause|covered peril|cause of loss|perils insured\b/.test(lower)) extractorNames.push("covered_reasons");
8320
- if (textIncludesSupplementarySignal(text)) extractorNames.push("supplementary");
8321
- if (extractorNames.length === 0 && text.trim().length > 180) extractorNames.push("sections");
8322
- return {
8323
- localPageNumber: page,
8324
- extractorNames: [...new Set(extractorNames)],
8325
- pageRole: inferPageRole(lower),
8326
- hasScheduleValues: /\$|\b\d{2,}(?:,\d{3})*\b/.test(lower) && /\blimit|deductible|premium|schedule|class|location\b/.test(lower),
8327
- confidence: text.trim().length > 0 ? 0.8 : 0,
8328
- notes: "Deterministic source-span page assignment"
8329
- };
8330
- });
8331
- }
8332
- function inferPageRole(lowerText) {
8333
- if (/\bdeclarations?|schedule\b/.test(lowerText)) return "declarations_schedule";
8334
- if (/\bendorsement\b/.test(lowerText)) return "endorsement_form";
8335
- if (/\bexclusion|condition\b/.test(lowerText)) return "condition_exclusion_form";
8336
- if (/\bnotice|department of insurance|complaint\b/.test(lowerText)) return "supplementary";
8337
- if (lowerText.trim()) return "policy_form";
8338
- return "other";
8339
- }
8340
7960
  function shouldRunLlmReview(mode, report, sourceSpansAvailable) {
8341
7961
  if (mode === "skip" || maxReviewRounds <= 0) return false;
8342
7962
  if (mode === "always") return true;
@@ -8557,21 +8177,28 @@ function createExtractor(config) {
8557
8177
  return promise;
8558
8178
  }
8559
8179
  async function getPageRangeText(startPage, endPage) {
8560
- return doclingDocument ? getDoclingPageRangeText(doclingDocument, startPage, endPage) : "";
8180
+ if (doclingDocument) return getDoclingPageRangeText(doclingDocument, startPage, endPage);
8181
+ return formatSourceSpanText(spansForPageRange(sourceSpans, startPage, endPage));
8561
8182
  }
8562
8183
  function withFullDocumentTextContext(prompt) {
8563
- if (!doclingDocument) return prompt;
8564
- return `${prompt}
8184
+ if (doclingDocument) return `${prompt}
8565
8185
 
8566
8186
  DOCLING DOCUMENT TEXT:
8567
8187
  ${doclingDocument.fullText}`;
8188
+ const sourceText = formatSourceSpanText(sourceSpans);
8189
+ if (!sourceText) return prompt;
8190
+ return `${prompt}
8191
+
8192
+ SOURCE SPAN DOCUMENT TEXT:
8193
+ ${sourceText}`;
8568
8194
  }
8569
8195
  function withPageRangeTextContext(prompt, startPage, endPage, pageText) {
8570
- if (!doclingDocument) return prompt;
8196
+ if (!pageText) return prompt;
8197
+ const label = doclingDocument ? "DOCLING DOCUMENT" : "SOURCE SPAN DOCUMENT";
8571
8198
  return `${prompt}
8572
8199
 
8573
- DOCLING DOCUMENT PAGES ${startPage}-${endPage}:
8574
- ${pageText || "(No Docling text was available for this page range.)"}`;
8200
+ ${label} PAGES ${startPage}-${endPage}:
8201
+ ${pageText}`;
8575
8202
  }
8576
8203
  let classifyResult;
8577
8204
  if (resumed?.classifyResult && pipelineCtx.isPhaseComplete("classify")) {
@@ -8628,17 +8255,6 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8628
8255
  formInventory = resumed.formInventory;
8629
8256
  memory.set("form_inventory", formInventory);
8630
8257
  onProgress?.("Resuming from checkpoint (form inventory complete)...");
8631
- } else if (sourceSpans.length > 0) {
8632
- formInventory = buildDeterministicFormInventory(sourceSpans, pageCount) ?? { forms: [] };
8633
- onProgress?.(`Built deterministic form inventory for ${primaryType} ${documentType}.`);
8634
- memory.set("form_inventory", formInventory);
8635
- await pipelineCtx.save("form_inventory", {
8636
- id,
8637
- pageCount,
8638
- classifyResult,
8639
- formInventory,
8640
- memory: Object.fromEntries(memory)
8641
- });
8642
8258
  } else {
8643
8259
  onProgress?.(`Building form inventory for ${primaryType} ${documentType}...`);
8644
8260
  const budget = resolveBudget("extraction_form_inventory", 2048);
@@ -8679,20 +8295,6 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8679
8295
  if (resumed?.pageAssignments && pipelineCtx.isPhaseComplete("page_map")) {
8680
8296
  pageAssignments = resumed.pageAssignments;
8681
8297
  onProgress?.("Resuming from checkpoint (page map complete)...");
8682
- } else if (sourceSpans.length > 0) {
8683
- onProgress?.(`Mapping document pages from source spans for ${primaryType} ${documentType}...`);
8684
- pageAssignments = normalizePageAssignments(
8685
- buildDeterministicPageAssignments(sourceSpans, pageCount),
8686
- formInventory
8687
- );
8688
- await pipelineCtx.save("page_map", {
8689
- id,
8690
- pageCount,
8691
- classifyResult,
8692
- formInventory,
8693
- pageAssignments,
8694
- memory: Object.fromEntries(memory)
8695
- });
8696
8298
  } else {
8697
8299
  onProgress?.(`Mapping document pages for ${primaryType} ${documentType}...`);
8698
8300
  const chunkSize = 8;
@@ -8709,7 +8311,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8709
8311
  pageMapChunks.map(
8710
8312
  ({ startPage, endPage }) => pageMapLimit(async () => {
8711
8313
  const pagesPdf = doclingDocument ? void 0 : await getPageRangePdf(startPage, endPage);
8712
- const pagesText = doclingDocument ? await getPageRangeText(startPage, endPage) : "";
8314
+ const pagesText = await getPageRangeText(startPage, endPage);
8713
8315
  const budget = resolveBudget("extraction_page_map", 2048);
8714
8316
  const startedAt = Date.now();
8715
8317
  const mapResponse = await safeGenerateObject(
@@ -8816,7 +8418,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8816
8418
  completedPageRangePdfCache,
8817
8419
  getPageRangePdf,
8818
8420
  convertPdfToImages ? getPageImages : void 0,
8819
- doclingDocument ? getPageRangeText : void 0
8421
+ sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0
8820
8422
  );
8821
8423
  })
8822
8424
  )
@@ -8849,7 +8451,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8849
8451
  pageRangeCache: completedPageRangePdfCache,
8850
8452
  getPageRangePdf,
8851
8453
  getPageImages: convertPdfToImages ? getPageImages : void 0,
8852
- getPageRangeText: doclingDocument ? getPageRangeText : void 0
8454
+ getPageRangeText: sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0
8853
8455
  });
8854
8456
  trackUsage(supplementaryResult.usage, {
8855
8457
  taskKind: "extraction_focused",
@@ -8885,7 +8487,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8885
8487
  concurrency,
8886
8488
  getPageRangePdf,
8887
8489
  getPageImages: convertPdfToImages ? getPageImages : void 0,
8888
- getPageRangeText: doclingDocument ? getPageRangeText : void 0,
8490
+ getPageRangeText: sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0,
8889
8491
  providerOptions: activeProviderOptions,
8890
8492
  modelCapabilities,
8891
8493
  modelBudgetConstraints,
@@ -8981,7 +8583,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8981
8583
  completedPageRangePdfCache,
8982
8584
  getPageRangePdf,
8983
8585
  convertPdfToImages ? getPageImages : void 0,
8984
- doclingDocument ? getPageRangeText : void 0
8586
+ sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0
8985
8587
  );
8986
8588
  })
8987
8589
  )