@claritylabs/cl-sdk 3.1.16 → 3.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -9210,11 +9210,20 @@ function coverageTextForSelection(coverage) {
9210
9210
  ])
9211
9211
  ].filter(Boolean).join(" ");
9212
9212
  }
9213
+ function coverageCleanupEntries(profile, coverageIndexes) {
9214
+ if (!coverageIndexes) {
9215
+ return profile.coverages.map((coverage, coverageIndex) => ({ coverage, coverageIndex }));
9216
+ }
9217
+ return [...new Set(coverageIndexes)].sort((left, right) => left - right).map((coverageIndex) => {
9218
+ const coverage = profile.coverages[coverageIndex];
9219
+ return coverage ? { coverage, coverageIndex } : void 0;
9220
+ }).filter((entry) => Boolean(entry));
9221
+ }
9213
9222
  function nodeTextMatchesCoverage(node, coverageTerms) {
9214
9223
  const text = nodeTextForSelection(node).toLowerCase();
9215
9224
  return coverageTerms.some((term) => term.length >= 5 && text.includes(term));
9216
9225
  }
9217
- function selectCoverageCleanupNodes(sourceTree, profile) {
9226
+ function selectCoverageCleanupNodes(sourceTree, coverages) {
9218
9227
  const nodeById = new Map(sourceTree.map((node) => [node.id, node]));
9219
9228
  const childrenByParent = /* @__PURE__ */ new Map();
9220
9229
  for (const node of sourceTree) {
@@ -9232,12 +9241,12 @@ function selectCoverageCleanupNodes(sourceTree, profile) {
9232
9241
  const current = selected.get(node.id);
9233
9242
  if (!current || score > current.score) selected.set(node.id, { node, score });
9234
9243
  };
9235
- const sourceNodeIds = uniqueStrings(profile.coverages.flatMap((coverage) => [
9244
+ const sourceNodeIds = uniqueStrings(coverages.flatMap((coverage) => [
9236
9245
  ...coverage.sourceNodeIds,
9237
9246
  ...coverage.limits.flatMap((term) => term.sourceNodeIds)
9238
9247
  ]));
9239
9248
  const coveragePages2 = /* @__PURE__ */ new Set();
9240
- const coverageTerms = uniqueStrings(profile.coverages.flatMap(
9249
+ const coverageTerms = uniqueStrings(coverages.flatMap(
9241
9250
  (coverage) => coverageTextForSelection(coverage).toLowerCase().split(/[^a-z0-9$,.]+/i).filter((part) => part.length >= 5)
9242
9251
  ));
9243
9252
  for (const id of sourceNodeIds) {
@@ -9280,8 +9289,9 @@ function selectCoverageCleanupNodes(sourceTree, profile) {
9280
9289
  (left, right) => (left.node.pageStart ?? Number.MAX_SAFE_INTEGER) - (right.node.pageStart ?? Number.MAX_SAFE_INTEGER) || left.node.order - right.node.order
9281
9290
  ).map((entry) => entry.node);
9282
9291
  }
9283
- function buildOperationalProfileCleanupPrompt(sourceTree, profile) {
9284
- const nodes = selectCoverageCleanupNodes(sourceTree, profile).map((node) => compactNode(
9292
+ function buildOperationalProfileCleanupPrompt(sourceTree, profile, options = {}) {
9293
+ const coverageEntries = coverageCleanupEntries(profile, options.coverageIndexes);
9294
+ const nodes = selectCoverageCleanupNodes(sourceTree, coverageEntries.map((entry) => entry.coverage)).map((node) => compactNode(
9285
9295
  node,
9286
9296
  node.kind === "page" || node.kind === "page_group" ? 260 : node.kind === "table_row" || node.kind === "table_cell" ? 520 : 360
9287
9297
  ));
@@ -9289,9 +9299,12 @@ function buildOperationalProfileCleanupPrompt(sourceTree, profile) {
9289
9299
  documentType: profile.documentType,
9290
9300
  policyTypes: profile.policyTypes,
9291
9301
  coverageTypes: profile.coverageTypes,
9292
- coverages: profile.coverages.map(compactCoverageForCleanup)
9302
+ coverages: coverageEntries.map(({ coverage, coverageIndex }) => compactCoverageForCleanup(coverage, coverageIndex))
9293
9303
  };
9294
9304
  return `Review and clean a source-backed operational profile projection for an insurance policy.
9305
+ ${options.label ? `
9306
+ Coverage group: ${options.label}
9307
+ ` : ""}
9295
9308
 
9296
9309
  Task:
9297
9310
  - Inspect the candidate coverage projection against the source nodes.
@@ -9355,6 +9368,9 @@ function normalizedTermText(term) {
9355
9368
  function isLimitTerm(term) {
9356
9369
  return ["each_claim_limit", "each_occurrence_limit", "each_loss_limit", "aggregate_limit", "sublimit"].includes(term.kind) || term.kind === "other" && /\b(limit|aggregate|claim|occurrence|loss|proceeding)\b/i.test(normalizedTermText(term));
9357
9370
  }
9371
+ function isPrimaryLimitTerm(term) {
9372
+ return ["each_claim_limit", "each_occurrence_limit", "each_loss_limit", "aggregate_limit"].includes(term.kind);
9373
+ }
9358
9374
  function isDeductibleTerm(term) {
9359
9375
  return term.kind === "deductible" || term.kind === "retention" || /\b(deductible|retention|sir)\b/i.test(normalizedTermText(term));
9360
9376
  }
@@ -9364,8 +9380,41 @@ function isPremiumTerm(term) {
9364
9380
  function isRetroactiveDateTerm(term) {
9365
9381
  return term.kind === "retroactive_date" || /\bretroactive\b/i.test(normalizedTermText(term));
9366
9382
  }
9383
+ function fallbackLimitScope(kind) {
9384
+ switch (kind) {
9385
+ case "each_claim_limit":
9386
+ return "Each Claim";
9387
+ case "each_occurrence_limit":
9388
+ return "Each Occurrence";
9389
+ case "each_loss_limit":
9390
+ return "Each Loss";
9391
+ case "aggregate_limit":
9392
+ return "Aggregate";
9393
+ case "sublimit":
9394
+ return "Sub-Limit";
9395
+ default:
9396
+ return void 0;
9397
+ }
9398
+ }
9399
+ function displayLabelForLimitTerm(term) {
9400
+ const label = cleanProfileValue(term.label)?.replace(/\s+Limit$/i, "");
9401
+ if (label && !/^(?:limit|amount|value)$/i.test(label)) return label;
9402
+ return fallbackLimitScope(term.kind);
9403
+ }
9404
+ function displayValueForLimitTerm(term) {
9405
+ const value = cleanProfileValue(term.value);
9406
+ if (!value) return void 0;
9407
+ if (/\b(each|aggregate|occurrence|claim|loss|proceeding|policy|sublimit|sub-limit)\b/i.test(value)) {
9408
+ return value;
9409
+ }
9410
+ const label = displayLabelForLimitTerm(term);
9411
+ return label ? `${value} ${label}` : value;
9412
+ }
9367
9413
  function primaryLimitFromTerms(terms) {
9368
- return terms.find(isLimitTerm)?.value;
9414
+ const primaryTerms = terms.filter(isPrimaryLimitTerm);
9415
+ const candidateTerms = primaryTerms.length > 0 ? primaryTerms : terms.filter(isLimitTerm);
9416
+ const values = uniqueStrings(candidateTerms.map((term) => displayValueForLimitTerm(term)).filter((value) => Boolean(value)));
9417
+ return values.length > 0 ? values.join(" / ") : void 0;
9369
9418
  }
9370
9419
  function deductibleFromTerms(terms) {
9371
9420
  return terms.find(isDeductibleTerm)?.value;
@@ -9376,6 +9425,13 @@ function premiumFromTerms(terms) {
9376
9425
  function retroactiveDateFromTerms(terms) {
9377
9426
  return terms.find(isRetroactiveDateTerm)?.value;
9378
9427
  }
9428
+ function shouldUseTermLimitDisplay(currentLimit, termLimit) {
9429
+ const current = cleanProfileValue(currentLimit);
9430
+ if (!current) return true;
9431
+ if (/\s+\/\s*$/.test(current)) return true;
9432
+ if (!/\b(each|aggregate|occurrence|claim|loss|proceeding|policy|sublimit|sub-limit)\b/i.test(current)) return true;
9433
+ return !current.includes("/") && termLimit.includes("/");
9434
+ }
9379
9435
  function termDecisionTouches(coverage, decision, predicate) {
9380
9436
  const existing = coverage.limits[decision.termIndex];
9381
9437
  if (existing && predicate(existing)) return true;
@@ -9470,6 +9526,10 @@ function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpa
9470
9526
  else delete next.retroactiveDate;
9471
9527
  }
9472
9528
  }
9529
+ const termLimit = primaryLimitFromTerms(next.limits);
9530
+ if (termLimit && shouldUseTermLimitDisplay(next.limit, termLimit)) {
9531
+ next.limit = termLimit;
9532
+ }
9473
9533
  next.sourceNodeIds = uniqueStrings([
9474
9534
  ...next.sourceNodeIds,
9475
9535
  ...next.limits.flatMap((term) => term.sourceNodeIds)
@@ -11329,7 +11389,8 @@ async function runVisualTableRepair(params) {
11329
11389
  if (candidates.length === 0) return { sourceTree: params.sourceTree, warnings: [] };
11330
11390
  let sourceTree = params.sourceTree;
11331
11391
  const warnings = [];
11332
- for (const [index, candidate] of candidates.entries()) {
11392
+ const repairResults = await Promise.all(candidates.map(async (candidate, index) => {
11393
+ const label = `source_tree_visual_table_repair_p${candidate.page}`;
11333
11394
  try {
11334
11395
  const budget = params.resolveBudget("extraction_source_tree", 1800);
11335
11396
  const maxTokens = Math.min(budget.maxTokens, 2400);
@@ -11343,7 +11404,7 @@ async function runVisualTableRepair(params) {
11343
11404
  taskKind: "extraction_source_tree",
11344
11405
  budgetDiagnostics: { ...budget, maxTokens },
11345
11406
  trace: {
11346
- label: `source_tree_visual_table_repair_p${candidate.page}`,
11407
+ label,
11347
11408
  startPage: candidate.page,
11348
11409
  endPage: candidate.page,
11349
11410
  batchIndex: index + 1,
@@ -11356,20 +11417,38 @@ async function runVisualTableRepair(params) {
11356
11417
  log: params.log
11357
11418
  }
11358
11419
  );
11359
- params.trackUsage(response.usage, {
11360
- taskKind: "extraction_source_tree",
11361
- label: `source_tree_visual_table_repair_p${candidate.page}`,
11420
+ return {
11421
+ index,
11422
+ candidate,
11423
+ label,
11362
11424
  maxTokens,
11363
- durationMs: Date.now() - startedAt
11364
- });
11365
- const repair = response.object;
11366
- sourceTree = applyVisualTableRepair(sourceTree, repair);
11367
- warnings.push(...repair.warnings.map(
11368
- (warning) => `Visual table repair warning on page ${candidate.page}: ${warning}`
11369
- ));
11425
+ durationMs: Date.now() - startedAt,
11426
+ usage: response.usage,
11427
+ repair: response.object
11428
+ };
11370
11429
  } catch (error) {
11371
- warnings.push(`Visual table repair skipped on page ${candidate.page}; parsed table kept (${error instanceof Error ? error.message : String(error)})`);
11430
+ return {
11431
+ index,
11432
+ candidate,
11433
+ error: error instanceof Error ? error.message : String(error)
11434
+ };
11435
+ }
11436
+ }));
11437
+ for (const result of repairResults.sort((left, right) => left.index - right.index)) {
11438
+ if ("error" in result) {
11439
+ warnings.push(`Visual table repair skipped on page ${result.candidate.page}; parsed table kept (${result.error})`);
11440
+ continue;
11372
11441
  }
11442
+ params.trackUsage(result.usage, {
11443
+ taskKind: "extraction_source_tree",
11444
+ label: result.label,
11445
+ maxTokens: result.maxTokens,
11446
+ durationMs: result.durationMs
11447
+ });
11448
+ sourceTree = applyVisualTableRepair(sourceTree, result.repair);
11449
+ warnings.push(...result.repair.warnings.map(
11450
+ (warning) => `Visual table repair warning on page ${result.candidate.page}: ${warning}`
11451
+ ));
11373
11452
  }
11374
11453
  return { sourceTree, warnings };
11375
11454
  }
@@ -11466,6 +11545,32 @@ var NORMALIZED_COMPATIBILITY_FIELDS = /* @__PURE__ */ new Set([
11466
11545
  "insurer",
11467
11546
  "broker"
11468
11547
  ]);
11548
+ function coverageBelongsToEndorsementCleanupChunk(coverage) {
11549
+ return coverage.coverageOrigin === "endorsement" || Boolean(coverage.endorsementNumber);
11550
+ }
11551
+ function coverageCleanupChunks(profile) {
11552
+ const basePolicy = [];
11553
+ const endorsements = [];
11554
+ profile.coverages.forEach((coverage, coverageIndex) => {
11555
+ if (coverageBelongsToEndorsementCleanupChunk(coverage)) {
11556
+ endorsements.push(coverageIndex);
11557
+ } else {
11558
+ basePolicy.push(coverageIndex);
11559
+ }
11560
+ });
11561
+ return [
11562
+ basePolicy.length ? {
11563
+ group: "base_policy",
11564
+ label: "Coverage cleanup: base policy",
11565
+ coverageIndexes: basePolicy
11566
+ } : void 0,
11567
+ endorsements.length ? {
11568
+ group: "endorsements",
11569
+ label: "Coverage cleanup: endorsements",
11570
+ coverageIndexes: endorsements
11571
+ } : void 0
11572
+ ].filter((chunk) => Boolean(chunk));
11573
+ }
11469
11574
  function valueOf(profile, key) {
11470
11575
  const value = profile[key];
11471
11576
  if (!value || typeof value !== "object" || Array.isArray(value) || !("value" in value)) return void 0;
@@ -11622,9 +11727,9 @@ async function runSourceTreeExtraction(params) {
11622
11727
  };
11623
11728
  if (shouldRunSourceTreeOrganizer(sourceTree, sourceSpans)) {
11624
11729
  try {
11625
- const organizations = [];
11626
11730
  const batches = organizationBatches(sourceTree);
11627
- for (const [batchIndex, batch] of batches.entries()) {
11731
+ const organizationResults = await Promise.all(batches.map(async (batch, batchIndex) => {
11732
+ const label = batches.length > 1 ? `source_tree_organizer_${batchIndex + 1}` : "source_tree_organizer";
11628
11733
  const budget = params.resolveBudget("extraction_source_tree", 4096);
11629
11734
  const startedAt = Date.now();
11630
11735
  const response = await safeGenerateObject(
@@ -11634,20 +11739,37 @@ async function runSourceTreeExtraction(params) {
11634
11739
  schema: SourceTreeOrganizationSchema,
11635
11740
  maxTokens: budget.maxTokens,
11636
11741
  taskKind: "extraction_source_tree",
11637
- budgetDiagnostics: budget
11742
+ budgetDiagnostics: budget,
11743
+ trace: {
11744
+ label,
11745
+ batchIndex: batchIndex + 1,
11746
+ batchCount: batches.length,
11747
+ sourceBacked: true
11748
+ }
11638
11749
  },
11639
11750
  {
11640
11751
  fallback: { labels: [], groups: [] },
11641
11752
  log: params.log
11642
11753
  }
11643
11754
  );
11644
- localTrack(response.usage, {
11755
+ return {
11756
+ batchIndex,
11757
+ label,
11758
+ budget,
11759
+ durationMs: Date.now() - startedAt,
11760
+ usage: response.usage,
11761
+ organization: response.object
11762
+ };
11763
+ }));
11764
+ const organizations = [];
11765
+ for (const result of organizationResults.sort((left, right) => left.batchIndex - right.batchIndex)) {
11766
+ localTrack(result.usage, {
11645
11767
  taskKind: "extraction_source_tree",
11646
- label: batches.length > 1 ? `source_tree_organizer_${batchIndex + 1}` : "source_tree_organizer",
11647
- maxTokens: budget.maxTokens,
11648
- durationMs: Date.now() - startedAt
11768
+ label: result.label,
11769
+ maxTokens: result.budget.maxTokens,
11770
+ durationMs: result.durationMs
11649
11771
  });
11650
- organizations.push(response.object);
11772
+ organizations.push(result.organization);
11651
11773
  }
11652
11774
  sourceTree = applyOrganization(sourceTree, mergeOrganizationResults(organizations));
11653
11775
  } catch (error) {
@@ -11736,33 +11858,64 @@ async function runSourceTreeExtraction(params) {
11736
11858
  try {
11737
11859
  const validNodeIds = new Set(sourceTree.map((node) => node.id));
11738
11860
  const validSpanIds = new Set(sourceSpans.map((span) => span.id));
11739
- const budget = params.resolveBudget("extraction_coverage_cleanup", 4096);
11740
- const startedAt = Date.now();
11741
- const response = await safeGenerateObject(
11742
- params.generateObject,
11743
- {
11744
- prompt: buildOperationalProfileCleanupPrompt(sourceTree, operationalProfile),
11745
- schema: OperationalProfileCleanupSchema,
11746
- maxTokens: budget.maxTokens,
11861
+ const chunks = coverageCleanupChunks(operationalProfile);
11862
+ if (chunks.length > 1) {
11863
+ await params.log?.(`Operational profile coverage cleanup reviewing ${chunks.length} coverage groups in parallel`);
11864
+ }
11865
+ const cleanupResults = await Promise.all(chunks.map(async (chunk, batchIndex) => {
11866
+ const budget = params.resolveBudget("extraction_coverage_cleanup", 4096);
11867
+ const startedAt = Date.now();
11868
+ const response = await safeGenerateObject(
11869
+ params.generateObject,
11870
+ {
11871
+ prompt: buildOperationalProfileCleanupPrompt(sourceTree, operationalProfile, {
11872
+ coverageIndexes: chunk.coverageIndexes,
11873
+ label: chunk.label
11874
+ }),
11875
+ schema: OperationalProfileCleanupSchema,
11876
+ maxTokens: budget.maxTokens,
11877
+ taskKind: "extraction_coverage_cleanup",
11878
+ budgetDiagnostics: budget,
11879
+ providerOptions: params.providerOptions,
11880
+ trace: {
11881
+ phase: "coverage_cleanup",
11882
+ label: chunk.label,
11883
+ batchIndex: batchIndex + 1,
11884
+ batchCount: chunks.length,
11885
+ coverageGroup: chunk.group,
11886
+ itemCount: chunk.coverageIndexes.length,
11887
+ sourceBacked: true
11888
+ }
11889
+ },
11890
+ {
11891
+ fallback: { coverageDecisions: [], warnings: [] },
11892
+ maxRetries: 0,
11893
+ log: params.log
11894
+ }
11895
+ );
11896
+ return {
11897
+ batchIndex,
11898
+ chunk,
11899
+ budget,
11900
+ durationMs: Date.now() - startedAt,
11901
+ usage: response.usage,
11902
+ cleanup: response.object
11903
+ };
11904
+ }));
11905
+ const cleanup = { coverageDecisions: [], warnings: [] };
11906
+ for (const result of cleanupResults.sort((left, right) => left.batchIndex - right.batchIndex)) {
11907
+ localTrack(result.usage, {
11747
11908
  taskKind: "extraction_coverage_cleanup",
11748
- budgetDiagnostics: budget,
11749
- providerOptions: params.providerOptions,
11750
- trace: { phase: "coverage_cleanup", sourceBacked: true }
11751
- },
11752
- {
11753
- fallback: { coverageDecisions: [], warnings: [] },
11754
- log: params.log
11755
- }
11756
- );
11757
- localTrack(response.usage, {
11758
- taskKind: "extraction_coverage_cleanup",
11759
- label: "operational_profile_cleanup",
11760
- maxTokens: budget.maxTokens,
11761
- durationMs: Date.now() - startedAt
11762
- });
11909
+ label: result.chunk.label,
11910
+ maxTokens: result.budget.maxTokens,
11911
+ durationMs: result.durationMs
11912
+ });
11913
+ cleanup.coverageDecisions.push(...result.cleanup.coverageDecisions);
11914
+ cleanup.warnings.push(...result.cleanup.warnings);
11915
+ }
11763
11916
  operationalProfile = applyOperationalProfileCleanup(
11764
11917
  operationalProfile,
11765
- response.object,
11918
+ cleanup,
11766
11919
  validNodeIds,
11767
11920
  validSpanIds
11768
11921
  );
@@ -11816,6 +11969,7 @@ function createExtractor(config) {
11816
11969
  sourceStore,
11817
11970
  qualityGate = "warn",
11818
11971
  modelCapabilities,
11972
+ modelCapabilitiesByTaskKind,
11819
11973
  modelBudgetConstraints,
11820
11974
  onCheckpointSave
11821
11975
  } = config;
@@ -11832,10 +11986,11 @@ function createExtractor(config) {
11832
11986
  };
11833
11987
  let activeProviderOptions = providerOptions;
11834
11988
  function resolveBudget(taskKind, hintTokens) {
11989
+ const taskModelCapabilities = modelCapabilitiesByTaskKind?.[taskKind] ?? modelCapabilities;
11835
11990
  return resolveModelBudget({
11836
11991
  taskKind,
11837
11992
  hintTokens,
11838
- modelCapabilities,
11993
+ modelCapabilities: taskModelCapabilities,
11839
11994
  constraint: modelBudgetConstraints?.[taskKind]
11840
11995
  });
11841
11996
  }