@claritylabs/cl-sdk 3.1.17 → 3.1.19

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
@@ -20,17 +20,19 @@ function isRetryableError(error) {
20
20
  }
21
21
  return false;
22
22
  }
23
- async function withRetry(fn, log) {
23
+ async function withRetry(fn, log, options) {
24
+ const maxRetries = options?.maxRetries ?? MAX_RETRIES;
25
+ const baseDelayMs = options?.baseDelayMs ?? BASE_DELAY_MS;
24
26
  for (let attempt = 0; ; attempt++) {
25
27
  try {
26
28
  return await fn();
27
29
  } catch (error) {
28
- if (!isRetryableError(error) || attempt >= MAX_RETRIES) {
30
+ if (!isRetryableError(error) || attempt >= maxRetries) {
29
31
  throw error;
30
32
  }
31
33
  const jitter = Math.random() * 1e3;
32
- const delay = BASE_DELAY_MS * Math.pow(2, attempt) + jitter;
33
- await log?.(`Retryable error, retrying in ${(delay / 1e3).toFixed(1)}s (attempt ${attempt + 1}/${MAX_RETRIES})...`);
34
+ const delay = baseDelayMs * Math.pow(2, attempt) + jitter;
35
+ await log?.(`Retryable error, retrying in ${(delay / 1e3).toFixed(1)}s (attempt ${attempt + 1}/${maxRetries})...`);
34
36
  await new Promise((resolve) => setTimeout(resolve, delay));
35
37
  }
36
38
  }
@@ -157,10 +159,8 @@ async function safeGenerateObject(generateObject, params, options) {
157
159
  const strictParams = { ...params, schema: toStrictSchema(params.schema) };
158
160
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
159
161
  try {
160
- const result = await withRetry(
161
- () => generateObject(strictParams),
162
- options?.log
163
- );
162
+ const generate = () => generateObject(strictParams);
163
+ const result = options?.retry === false ? await generate() : await withRetry(generate, options?.log, options?.retry);
164
164
  return {
165
165
  ...result,
166
166
  object: params.schema.parse(sanitizeNulls(result.object))
@@ -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.
@@ -11376,7 +11389,8 @@ async function runVisualTableRepair(params) {
11376
11389
  if (candidates.length === 0) return { sourceTree: params.sourceTree, warnings: [] };
11377
11390
  let sourceTree = params.sourceTree;
11378
11391
  const warnings = [];
11379
- 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}`;
11380
11394
  try {
11381
11395
  const budget = params.resolveBudget("extraction_source_tree", 1800);
11382
11396
  const maxTokens = Math.min(budget.maxTokens, 2400);
@@ -11390,7 +11404,7 @@ async function runVisualTableRepair(params) {
11390
11404
  taskKind: "extraction_source_tree",
11391
11405
  budgetDiagnostics: { ...budget, maxTokens },
11392
11406
  trace: {
11393
- label: `source_tree_visual_table_repair_p${candidate.page}`,
11407
+ label,
11394
11408
  startPage: candidate.page,
11395
11409
  endPage: candidate.page,
11396
11410
  batchIndex: index + 1,
@@ -11400,23 +11414,43 @@ async function runVisualTableRepair(params) {
11400
11414
  },
11401
11415
  {
11402
11416
  fallback: { tables: [], warnings: [] },
11403
- log: params.log
11417
+ maxRetries: 0,
11418
+ log: params.log,
11419
+ retry: false
11404
11420
  }
11405
11421
  );
11406
- params.trackUsage(response.usage, {
11407
- taskKind: "extraction_source_tree",
11408
- label: `source_tree_visual_table_repair_p${candidate.page}`,
11422
+ return {
11423
+ index,
11424
+ candidate,
11425
+ label,
11409
11426
  maxTokens,
11410
- durationMs: Date.now() - startedAt
11411
- });
11412
- const repair = response.object;
11413
- sourceTree = applyVisualTableRepair(sourceTree, repair);
11414
- warnings.push(...repair.warnings.map(
11415
- (warning) => `Visual table repair warning on page ${candidate.page}: ${warning}`
11416
- ));
11427
+ durationMs: Date.now() - startedAt,
11428
+ usage: response.usage,
11429
+ repair: response.object
11430
+ };
11417
11431
  } catch (error) {
11418
- warnings.push(`Visual table repair skipped on page ${candidate.page}; parsed table kept (${error instanceof Error ? error.message : String(error)})`);
11432
+ return {
11433
+ index,
11434
+ candidate,
11435
+ error: error instanceof Error ? error.message : String(error)
11436
+ };
11419
11437
  }
11438
+ }));
11439
+ for (const result of repairResults.sort((left, right) => left.index - right.index)) {
11440
+ if ("error" in result) {
11441
+ warnings.push(`Visual table repair skipped on page ${result.candidate.page}; parsed table kept (${result.error})`);
11442
+ continue;
11443
+ }
11444
+ params.trackUsage(result.usage, {
11445
+ taskKind: "extraction_source_tree",
11446
+ label: result.label,
11447
+ maxTokens: result.maxTokens,
11448
+ durationMs: result.durationMs
11449
+ });
11450
+ sourceTree = applyVisualTableRepair(sourceTree, result.repair);
11451
+ warnings.push(...result.repair.warnings.map(
11452
+ (warning) => `Visual table repair warning on page ${result.candidate.page}: ${warning}`
11453
+ ));
11420
11454
  }
11421
11455
  return { sourceTree, warnings };
11422
11456
  }
@@ -11669,9 +11703,9 @@ async function runSourceTreeExtraction(params) {
11669
11703
  };
11670
11704
  if (shouldRunSourceTreeOrganizer(sourceTree, sourceSpans)) {
11671
11705
  try {
11672
- const organizations = [];
11673
11706
  const batches = organizationBatches(sourceTree);
11674
- for (const [batchIndex, batch] of batches.entries()) {
11707
+ const organizationResults = await Promise.all(batches.map(async (batch, batchIndex) => {
11708
+ const label = batches.length > 1 ? `source_tree_organizer_${batchIndex + 1}` : "source_tree_organizer";
11675
11709
  const budget = params.resolveBudget("extraction_source_tree", 4096);
11676
11710
  const startedAt = Date.now();
11677
11711
  const response = await safeGenerateObject(
@@ -11681,20 +11715,39 @@ async function runSourceTreeExtraction(params) {
11681
11715
  schema: SourceTreeOrganizationSchema,
11682
11716
  maxTokens: budget.maxTokens,
11683
11717
  taskKind: "extraction_source_tree",
11684
- budgetDiagnostics: budget
11718
+ budgetDiagnostics: budget,
11719
+ trace: {
11720
+ label,
11721
+ batchIndex: batchIndex + 1,
11722
+ batchCount: batches.length,
11723
+ sourceBacked: true
11724
+ }
11685
11725
  },
11686
11726
  {
11687
11727
  fallback: { labels: [], groups: [] },
11688
- log: params.log
11728
+ maxRetries: 0,
11729
+ log: params.log,
11730
+ retry: false
11689
11731
  }
11690
11732
  );
11691
- localTrack(response.usage, {
11733
+ return {
11734
+ batchIndex,
11735
+ label,
11736
+ budget,
11737
+ durationMs: Date.now() - startedAt,
11738
+ usage: response.usage,
11739
+ organization: response.object
11740
+ };
11741
+ }));
11742
+ const organizations = [];
11743
+ for (const result of organizationResults.sort((left, right) => left.batchIndex - right.batchIndex)) {
11744
+ localTrack(result.usage, {
11692
11745
  taskKind: "extraction_source_tree",
11693
- label: batches.length > 1 ? `source_tree_organizer_${batchIndex + 1}` : "source_tree_organizer",
11694
- maxTokens: budget.maxTokens,
11695
- durationMs: Date.now() - startedAt
11746
+ label: result.label,
11747
+ maxTokens: result.budget.maxTokens,
11748
+ durationMs: result.durationMs
11696
11749
  });
11697
- organizations.push(response.object);
11750
+ organizations.push(result.organization);
11698
11751
  }
11699
11752
  sourceTree = applyOrganization(sourceTree, mergeOrganizationResults(organizations));
11700
11753
  } catch (error) {
@@ -11719,7 +11772,9 @@ async function runSourceTreeExtraction(params) {
11719
11772
  },
11720
11773
  {
11721
11774
  fallback: { labels: [], groups: [] },
11722
- log: params.log
11775
+ maxRetries: 0,
11776
+ log: params.log,
11777
+ retry: false
11723
11778
  }
11724
11779
  );
11725
11780
  localTrack(response.usage, {
@@ -11761,7 +11816,9 @@ async function runSourceTreeExtraction(params) {
11761
11816
  },
11762
11817
  {
11763
11818
  fallback: emptyProfile,
11764
- log: params.log
11819
+ maxRetries: 0,
11820
+ log: params.log,
11821
+ retry: false
11765
11822
  }
11766
11823
  );
11767
11824
  localTrack(response.usage, {
@@ -11794,16 +11851,23 @@ async function runSourceTreeExtraction(params) {
11794
11851
  taskKind: "extraction_coverage_cleanup",
11795
11852
  budgetDiagnostics: budget,
11796
11853
  providerOptions: params.providerOptions,
11797
- trace: { phase: "coverage_cleanup", sourceBacked: true }
11854
+ trace: {
11855
+ phase: "coverage_cleanup",
11856
+ label: "Coverage cleanup",
11857
+ itemCount: operationalProfile.coverages.length,
11858
+ sourceBacked: true
11859
+ }
11798
11860
  },
11799
11861
  {
11800
11862
  fallback: { coverageDecisions: [], warnings: [] },
11801
- log: params.log
11863
+ maxRetries: 0,
11864
+ log: params.log,
11865
+ retry: false
11802
11866
  }
11803
11867
  );
11804
11868
  localTrack(response.usage, {
11805
11869
  taskKind: "extraction_coverage_cleanup",
11806
- label: "operational_profile_cleanup",
11870
+ label: "coverage_cleanup",
11807
11871
  maxTokens: budget.maxTokens,
11808
11872
  durationMs: Date.now() - startedAt
11809
11873
  });
@@ -11863,6 +11927,7 @@ function createExtractor(config) {
11863
11927
  sourceStore,
11864
11928
  qualityGate = "warn",
11865
11929
  modelCapabilities,
11930
+ modelCapabilitiesByTaskKind,
11866
11931
  modelBudgetConstraints,
11867
11932
  onCheckpointSave
11868
11933
  } = config;
@@ -11879,10 +11944,11 @@ function createExtractor(config) {
11879
11944
  };
11880
11945
  let activeProviderOptions = providerOptions;
11881
11946
  function resolveBudget(taskKind, hintTokens) {
11947
+ const taskModelCapabilities = modelCapabilitiesByTaskKind?.[taskKind] ?? modelCapabilities;
11882
11948
  return resolveModelBudget({
11883
11949
  taskKind,
11884
11950
  hintTokens,
11885
- modelCapabilities,
11951
+ modelCapabilities: taskModelCapabilities,
11886
11952
  constraint: modelBudgetConstraints?.[taskKind]
11887
11953
  });
11888
11954
  }
@@ -12205,7 +12271,9 @@ ${sourceText}`;
12205
12271
  },
12206
12272
  {
12207
12273
  fallback: { forms: [] },
12274
+ maxRetries: 0,
12208
12275
  log,
12276
+ retry: false,
12209
12277
  onError: (err, attempt) => log?.(`Form inventory attempt ${attempt + 1} failed: ${err instanceof Error ? err.message : String(err)}`)
12210
12278
  }
12211
12279
  );