@claritylabs/cl-sdk 3.2.1 → 3.2.3

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
@@ -3542,7 +3542,7 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
3542
3542
  ];
3543
3543
  return PolicyOperationalProfileSchema.parse({
3544
3544
  ...base,
3545
- documentType: candidate.documentType === "quote" ? "quote" : candidate.documentType === "policy" ? "policy" : base.documentType,
3545
+ documentType: candidate.documentType === "policy" ? "policy" : base.documentType,
3546
3546
  policyTypes: Array.isArray(candidate.policyTypes) && candidate.policyTypes.length > 0 ? candidate.policyTypes : base.policyTypes,
3547
3547
  policyNumber,
3548
3548
  namedInsured,
@@ -5168,6 +5168,94 @@ function nodesByParent(sourceTree) {
5168
5168
  }
5169
5169
  return byParent;
5170
5170
  }
5171
+ function sourceNodeIdsBySpanId(sourceTree) {
5172
+ const bySpan = /* @__PURE__ */ new Map();
5173
+ for (const node of sourceTree) {
5174
+ for (const spanId of node.sourceSpanIds) {
5175
+ const nodes = bySpan.get(spanId) ?? [];
5176
+ nodes.push(node.id);
5177
+ bySpan.set(spanId, nodes);
5178
+ }
5179
+ }
5180
+ return bySpan;
5181
+ }
5182
+ function operationalEvidenceScore(span) {
5183
+ const text = cleanText([
5184
+ span.text,
5185
+ span.formNumber,
5186
+ span.sourceUnit,
5187
+ span.metadata?.elementType,
5188
+ span.metadata?.sourceUnit
5189
+ ].filter(Boolean).join(" "), "");
5190
+ if (!text) return 0;
5191
+ let score = 0;
5192
+ if (span.sourceUnit === "table_row" || span.sourceUnit === "table") score += 5;
5193
+ if (span.metadata?.elementType === "title") score += 4;
5194
+ if (span.sourceUnit === "page") score -= 3;
5195
+ if (/\b(policy\s*(number|period|term)|effective date|expiration date|expiry date|named insured|insurer|carrier|security|broker|producer|premium|total due)\b/i.test(text)) score += 12;
5196
+ if (/\b(coverage part|limit(?:s)? of liability|deductible|retention|retroactive date|aggregate|sublimit|sub-limit|each claim|each loss|each occurrence|coinsurance)\b/i.test(text)) score += 14;
5197
+ if (/\bendorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9]|forms? and endorsements?|attached at inception|schedule\b/i.test(text)) score += 8;
5198
+ if (/\bitem\s+\d+\.?\s*(?:named insured|policy number|policy period|renewal|form of business|coverage parts?|premium|extended reporting|producer|forms? and endorsements?)\b/i.test(text)) score += 10;
5199
+ if (/\$[\d,.]+|[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{2,4}|[0-9]{1,2}\s+[A-Za-z]{3,9}\s+[0-9]{4}/.test(text)) score += 3;
5200
+ return score;
5201
+ }
5202
+ function spanTableId(span) {
5203
+ return span.table?.tableId ?? span.metadata?.tableId;
5204
+ }
5205
+ function isTableCellSpan(span) {
5206
+ return spanSourceUnit(span) === "table_cell";
5207
+ }
5208
+ function operationalProfileEvidence(sourceTree, sourceSpans) {
5209
+ const sorted = [...sourceSpans].sort(
5210
+ (left, right) => (spanPageStart(left) ?? Number.MAX_SAFE_INTEGER) - (spanPageStart(right) ?? Number.MAX_SAFE_INTEGER) || (left.location?.charStart ?? Number.MAX_SAFE_INTEGER) - (right.location?.charStart ?? Number.MAX_SAFE_INTEGER) || left.id.localeCompare(right.id)
5211
+ );
5212
+ const selected = /* @__PURE__ */ new Set();
5213
+ const selectedTableIds = /* @__PURE__ */ new Set();
5214
+ for (let index = 0; index < sorted.length; index += 1) {
5215
+ const score = operationalEvidenceScore(sorted[index]);
5216
+ if (score < 8) continue;
5217
+ const tableId2 = spanTableId(sorted[index]);
5218
+ if (tableId2 && !isTableCellSpan(sorted[index])) selectedTableIds.add(tableId2);
5219
+ const page = spanPageStart(sorted[index]);
5220
+ for (let offset = -2; offset <= 2; offset += 1) {
5221
+ const neighborIndex = index + offset;
5222
+ const neighbor = sorted[neighborIndex];
5223
+ if (!neighbor || spanPageStart(neighbor) !== page) continue;
5224
+ const neighborText = cleanText(neighbor.text, "");
5225
+ if (!neighborText || neighborText.length > 5e3) continue;
5226
+ selected.add(neighborIndex);
5227
+ }
5228
+ }
5229
+ if (selectedTableIds.size > 0) {
5230
+ sorted.forEach((span, index) => {
5231
+ const tableId2 = spanTableId(span);
5232
+ if (!tableId2 || !selectedTableIds.has(tableId2) || isTableCellSpan(span)) return;
5233
+ const text = cleanText(span.text, "");
5234
+ if (text && text.length <= 5e3) selected.add(index);
5235
+ });
5236
+ }
5237
+ const nodeIdsBySpanId = sourceNodeIdsBySpanId(sourceTree);
5238
+ const seenText = /* @__PURE__ */ new Set();
5239
+ const entries = [...selected].sort((left, right) => left - right).map((index) => sorted[index]).filter((span) => !isTableCellSpan(span)).flatMap((span) => {
5240
+ const text = cleanText(span.text, "");
5241
+ if (!text) return [];
5242
+ const key = `${spanPageStart(span) ?? "na"}:${text.toLowerCase().slice(0, 240)}`;
5243
+ if (seenText.has(key)) return [];
5244
+ seenText.add(key);
5245
+ return [{
5246
+ sourceSpanId: span.id,
5247
+ sourceNodeIds: [...new Set(nodeIdsBySpanId.get(span.id) ?? [])].slice(0, 4),
5248
+ pageStart: spanPageStart(span),
5249
+ pageEnd: spanPageEnd(span),
5250
+ sourceUnit: spanSourceUnit(span),
5251
+ formNumber: span.formNumber,
5252
+ text: text.slice(0, span.sourceUnit === "page" ? 1200 : 900)
5253
+ }];
5254
+ });
5255
+ const detailEntries = entries.filter((entry) => entry.sourceUnit !== "page");
5256
+ const pageEntries = entries.filter((entry) => entry.sourceUnit === "page");
5257
+ return (detailEntries.length >= 20 ? detailEntries : [...detailEntries, ...pageEntries]).slice(0, 180);
5258
+ }
5171
5259
  function sourceTreeRootId(sourceTree) {
5172
5260
  return sourceTree.find((node) => node.kind === "document")?.id;
5173
5261
  }
@@ -5192,11 +5280,12 @@ function emptyOperationalProfile() {
5192
5280
  warnings: []
5193
5281
  };
5194
5282
  }
5195
- function buildOperationalProfilePrompt(sourceTree) {
5196
- const nodes = operationalProfilePromptNodes(sourceTree).map(
5283
+ function buildOperationalProfilePrompt(sourceTree, sourceSpans) {
5284
+ const evidence = operationalProfileEvidence(sourceTree, sourceSpans);
5285
+ const fallbackNodes = evidence.length ? [] : operationalProfilePromptNodes(sourceTree).map(
5197
5286
  (node) => compactNode2(node, node.kind === "page" || node.kind === "endorsement" ? 900 : 700)
5198
5287
  );
5199
- return `Extract a source-backed operational profile for an insurance policy or quote.
5288
+ return `Extract a source-backed operational profile for an insurance policy.
5200
5289
 
5201
5290
  Return only high-value operational facts needed for policy lists, Q&A, compliance, and certificate generation:
5202
5291
  - policy number, named insured, insurer/carrier/security, broker/producer, policy period, retroactive date, premium
@@ -5204,7 +5293,8 @@ Return only high-value operational facts needed for policy lists, Q&A, complianc
5204
5293
  - coverage type labels
5205
5294
 
5206
5295
  Rules:
5207
- - Every returned value must include sourceNodeIds or sourceSpanIds from the provided nodes.
5296
+ - Every returned value must include sourceNodeIds or sourceSpanIds from the provided evidence.
5297
+ - When citing an evidence entry, copy its sourceSpanId into the returned sourceSpanIds array.
5208
5298
  - If a value is not directly supported, omit it.
5209
5299
  - Prefer declarations, schedules, premium tables, and endorsement schedules over generic policy wording.
5210
5300
  - For broker/producer, extract the agency or company legal name, not the license role, credential, or type. In a block like "Bayshore Insurance Brokers, LLC" followed by "Surplus Lines Broker - CA License No. ...", broker.value must be "Bayshore Insurance Brokers, LLC"; the surplus-lines role and license number are not the broker name.
@@ -5220,10 +5310,10 @@ Rules:
5220
5310
  - 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.
5221
5311
  - 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.
5222
5312
  - Do not copy entire policy wording into fields.
5223
- - Extract facts directly from source nodes. There is no deterministic fact baseline.
5313
+ - Extract facts directly from source evidence. There is no deterministic fact baseline.
5224
5314
 
5225
- Source nodes:
5226
- ${JSON.stringify(nodes, null, 2)}
5315
+ Source evidence:
5316
+ ${JSON.stringify(evidence.length ? evidence : fallbackNodes, null, 2)}
5227
5317
 
5228
5318
  Return JSON for the operational profile.`;
5229
5319
  }
@@ -5483,7 +5573,7 @@ async function runSourceTreeExtraction(params) {
5483
5573
  const response = await safeGenerateObject(
5484
5574
  params.generateObject,
5485
5575
  {
5486
- prompt: buildOperationalProfilePrompt(sourceTree),
5576
+ prompt: buildOperationalProfilePrompt(sourceTree, sourceSpans),
5487
5577
  schema: OperationalProfilePromptSchema,
5488
5578
  maxTokens: budget.maxTokens,
5489
5579
  taskKind: "extraction_operational_profile",