@claritylabs/cl-sdk 1.3.1 → 1.3.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/README.md +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +174 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +174 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,7 +137,7 @@ If your callback ignores those fields, the model will only see the text prompt.
|
|
|
137
137
|
|
|
138
138
|
## Model routing metadata
|
|
139
139
|
|
|
140
|
-
Every SDK model callback may receive `taskKind`, `budgetDiagnostics`, and `trace`. Hosts can use these provider-agnostic fields for cheap-first routing, fallback, and telemetry without the SDK hardcoding model names. Example task kinds include `extraction_classify`, `extraction_focused`, `extraction_review`, `query_reason`, `application_extract_fields`, and `pce_impact_analysis`. `budgetDiagnostics` includes the resolved
|
|
140
|
+
Every SDK model callback may receive `taskKind`, `budgetDiagnostics`, and `trace`. Hosts can use these provider-agnostic fields for cheap-first routing, fallback, and telemetry without the SDK hardcoding model names. Example task kinds include `extraction_classify`, `extraction_focused`, `extraction_review`, `query_reason`, `application_extract_fields`, and `pce_impact_analysis`. `budgetDiagnostics` includes the resolved output-token cap, the lower preferred task budget, and truncation-risk warnings for the current subtask. When model capabilities include `maxOutputTokens`, the SDK uses that model maximum as the request cap instead of treating low task preferences as hard limits. `trace` identifies the current extractor, page range, format batch, or source-backed call so host logs can show what was being generated instead of a generic model-call label.
|
|
141
141
|
|
|
142
142
|
## Bounded Agentic Workflows
|
|
143
143
|
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -559,7 +559,8 @@ function resolveModelBudget(params) {
|
|
|
559
559
|
const schemaTokens = estimateTokens(params.schemaSizeBytes) ?? 0;
|
|
560
560
|
const expectedListLength = positiveInteger(params.expectedListLength) ?? 0;
|
|
561
561
|
const warnings = [];
|
|
562
|
-
|
|
562
|
+
const preferredOutputTokens = constrainedPreference ?? taskCapability ?? longListCapability ?? defaultCapability ?? hintTokens;
|
|
563
|
+
let maxTokens = hardMaxOutputTokens ?? modelMaxOutputTokens ?? preferredOutputTokens;
|
|
563
564
|
if (minOutputTokens) {
|
|
564
565
|
maxTokens = Math.max(maxTokens, minOutputTokens);
|
|
565
566
|
}
|
|
@@ -588,6 +589,7 @@ function resolveModelBudget(params) {
|
|
|
588
589
|
taskKind,
|
|
589
590
|
maxTokens,
|
|
590
591
|
hintTokens,
|
|
592
|
+
preferredOutputTokens,
|
|
591
593
|
modelMaxOutputTokens,
|
|
592
594
|
hardMaxOutputTokens,
|
|
593
595
|
estimatedInputTokens,
|
|
@@ -8183,7 +8185,7 @@ function normalizePageAssignments(pageAssignments, formInventory) {
|
|
|
8183
8185
|
}
|
|
8184
8186
|
return pageAssignments.map((assignment) => {
|
|
8185
8187
|
let extractorNames = [...new Set(
|
|
8186
|
-
|
|
8188
|
+
assignment.extractorNames.filter(Boolean)
|
|
8187
8189
|
)];
|
|
8188
8190
|
const hasDeclarations = extractorNames.includes("declarations");
|
|
8189
8191
|
const hasConditions = extractorNames.includes("conditions");
|
|
@@ -8202,9 +8204,6 @@ function normalizePageAssignments(pageAssignments, formInventory) {
|
|
|
8202
8204
|
if (inventoryTypes?.has("endorsement") && !extractorNames.includes("endorsements")) {
|
|
8203
8205
|
extractorNames = [...extractorNames, "endorsements"];
|
|
8204
8206
|
}
|
|
8205
|
-
if (extractorNames.length === 0) {
|
|
8206
|
-
extractorNames = ["sections"];
|
|
8207
|
-
}
|
|
8208
8207
|
return {
|
|
8209
8208
|
...assignment,
|
|
8210
8209
|
extractorNames
|
|
@@ -8241,20 +8240,12 @@ function groupContiguousPages(pages) {
|
|
|
8241
8240
|
function buildPlanFromPageAssignments(pageAssignments, pageCount, formInventory) {
|
|
8242
8241
|
const extractorPages = /* @__PURE__ */ new Map();
|
|
8243
8242
|
for (const assignment of pageAssignments) {
|
|
8244
|
-
const
|
|
8243
|
+
const focusedExtractors = assignment.extractorNames.filter((name) => name !== "sections");
|
|
8244
|
+
const extractors = focusedExtractors.length > 0 ? focusedExtractors : assignment.extractorNames;
|
|
8245
8245
|
for (const extractorName of extractors) {
|
|
8246
8246
|
extractorPages.set(extractorName, [...extractorPages.get(extractorName) ?? [], assignment.localPageNumber]);
|
|
8247
8247
|
}
|
|
8248
8248
|
}
|
|
8249
|
-
const coveredPages = /* @__PURE__ */ new Set();
|
|
8250
|
-
for (const pages of extractorPages.values()) {
|
|
8251
|
-
for (const page of pages) coveredPages.add(page);
|
|
8252
|
-
}
|
|
8253
|
-
for (let page = 1; page <= pageCount; page += 1) {
|
|
8254
|
-
if (!coveredPages.has(page)) {
|
|
8255
|
-
extractorPages.set("sections", [...extractorPages.get("sections") ?? [], page]);
|
|
8256
|
-
}
|
|
8257
|
-
}
|
|
8258
8249
|
const contextualExtractors = /* @__PURE__ */ new Set(["conditions", "covered_reasons", "definitions", "exclusions", "endorsements"]);
|
|
8259
8250
|
const contextualForms = (formInventory?.forms ?? []).filter(
|
|
8260
8251
|
(form) => form.pageStart != null && (form.pageEnd ?? form.pageStart) != null
|
|
@@ -8541,6 +8532,139 @@ function createExtractor(config) {
|
|
|
8541
8532
|
ranges.push({ startPage, endPage: previousPage });
|
|
8542
8533
|
return ranges;
|
|
8543
8534
|
}
|
|
8535
|
+
function pageNumberForSpan(span) {
|
|
8536
|
+
return span.pageStart ?? span.location?.startPage ?? span.location?.page;
|
|
8537
|
+
}
|
|
8538
|
+
function spansForPageRange(spans, startPage, endPage) {
|
|
8539
|
+
return spans.filter((span) => {
|
|
8540
|
+
const start = span.pageStart ?? span.location?.startPage ?? span.location?.page;
|
|
8541
|
+
const end = span.pageEnd ?? span.location?.endPage ?? start;
|
|
8542
|
+
return typeof start === "number" && typeof end === "number" && start <= endPage && end >= startPage;
|
|
8543
|
+
});
|
|
8544
|
+
}
|
|
8545
|
+
function inferSectionType(title, text) {
|
|
8546
|
+
const value = `${title} ${text.slice(0, 500)}`.toLowerCase();
|
|
8547
|
+
if (/\bdefinition|defined terms?\b/.test(value)) return "definition";
|
|
8548
|
+
if (/\bexclusion|not covered|does not apply\b/.test(value)) return "exclusion";
|
|
8549
|
+
if (/\bcondition|duties|loss condition|general condition\b/.test(value)) return "condition";
|
|
8550
|
+
if (/\bendorsement|amend|additional insured\b/.test(value)) return "endorsement";
|
|
8551
|
+
if (/\bcovered cause|covered reason|covered peril|cause of loss|perils insured\b/.test(value)) return "covered_reason";
|
|
8552
|
+
if (/\bdeclaration|schedule\b/.test(value)) return "declarations";
|
|
8553
|
+
return "policy_form";
|
|
8554
|
+
}
|
|
8555
|
+
function buildSourceBackedSectionIndex(spans, startPage, endPage) {
|
|
8556
|
+
const candidateSpans = spansForPageRange(spans, startPage, endPage).filter((span) => span.text.trim().length > 0).filter((span) => span.metadata?.sourceUnit === "section_candidate" || span.sectionId || span.text.length >= 160);
|
|
8557
|
+
return {
|
|
8558
|
+
sections: candidateSpans.map((span, index) => {
|
|
8559
|
+
const pageStart = span.pageStart ?? span.location?.startPage ?? span.location?.page ?? startPage;
|
|
8560
|
+
const pageEnd = span.pageEnd ?? span.location?.endPage ?? pageStart;
|
|
8561
|
+
const title = span.sectionId ?? span.formNumber ?? firstHeadingLine(span.text) ?? `Policy text page ${pageStart}`;
|
|
8562
|
+
return {
|
|
8563
|
+
title,
|
|
8564
|
+
sectionNumber: span.formNumber,
|
|
8565
|
+
pageStart,
|
|
8566
|
+
pageEnd,
|
|
8567
|
+
type: inferSectionType(title, span.text),
|
|
8568
|
+
excerpt: span.text.slice(0, 240),
|
|
8569
|
+
recordId: `section_index_${pageStart}_${index}`,
|
|
8570
|
+
sourceSpanIds: [span.id],
|
|
8571
|
+
sourceTextHash: span.textHash ?? sourceSpanTextHash(span.text)
|
|
8572
|
+
};
|
|
8573
|
+
})
|
|
8574
|
+
};
|
|
8575
|
+
}
|
|
8576
|
+
function firstHeadingLine(text) {
|
|
8577
|
+
const line = text.split(/\r?\n/).map((item) => item.trim()).find((item) => item.length > 0);
|
|
8578
|
+
if (!line) return void 0;
|
|
8579
|
+
return line.slice(0, 100);
|
|
8580
|
+
}
|
|
8581
|
+
function buildDeterministicFormInventory(spans, pageCount) {
|
|
8582
|
+
if (spans.length === 0) return void 0;
|
|
8583
|
+
const formPattern = /\b([A-Z]{1,4}\s?\d{2,5}(?:\s?\d{2,4})?|[A-Z]{2,8}-\d{2,8})\b/g;
|
|
8584
|
+
const seen = /* @__PURE__ */ new Map();
|
|
8585
|
+
for (const span of spans) {
|
|
8586
|
+
const page = pageNumberForSpan(span);
|
|
8587
|
+
if (!page) continue;
|
|
8588
|
+
const title = firstHeadingLine(span.text) ?? span.sectionId;
|
|
8589
|
+
const matches = /* @__PURE__ */ new Set([
|
|
8590
|
+
...span.formNumber ? [span.formNumber] : [],
|
|
8591
|
+
...Array.from(span.text.slice(0, 1200).matchAll(formPattern), (match) => match[1].trim())
|
|
8592
|
+
]);
|
|
8593
|
+
for (const formNumber of matches) {
|
|
8594
|
+
const key = `${formNumber}:${title ?? ""}`;
|
|
8595
|
+
const existing = seen.get(key);
|
|
8596
|
+
const formType = inferFormType(title ?? "", span.text);
|
|
8597
|
+
if (existing) {
|
|
8598
|
+
existing.pageStart = Math.min(existing.pageStart ?? page, page);
|
|
8599
|
+
existing.pageEnd = Math.max(existing.pageEnd ?? page, page);
|
|
8600
|
+
continue;
|
|
8601
|
+
}
|
|
8602
|
+
seen.set(key, {
|
|
8603
|
+
formNumber,
|
|
8604
|
+
title,
|
|
8605
|
+
formType,
|
|
8606
|
+
pageStart: Math.min(page, pageCount),
|
|
8607
|
+
pageEnd: Math.min(page, pageCount)
|
|
8608
|
+
});
|
|
8609
|
+
}
|
|
8610
|
+
}
|
|
8611
|
+
const forms = [...seen.values()].sort((a, b) => (a.pageStart ?? 0) - (b.pageStart ?? 0));
|
|
8612
|
+
return forms.length > 0 ? { forms } : void 0;
|
|
8613
|
+
}
|
|
8614
|
+
function inferFormType(title, text) {
|
|
8615
|
+
const value = `${title} ${text.slice(0, 700)}`.toLowerCase();
|
|
8616
|
+
if (/\bdeclarations?|schedule\b/.test(value)) return "declarations";
|
|
8617
|
+
if (/\bendorsement|amendatory|additional insured\b/.test(value)) return "endorsement";
|
|
8618
|
+
if (/\bnotice|department of insurance|complaint|privacy\b/.test(value)) return "notice";
|
|
8619
|
+
if (/\bapplication|questionnaire\b/.test(value)) return "application";
|
|
8620
|
+
if (/\bcoverage|policy form|conditions|exclusions|definitions|causes of loss\b/.test(value)) return "coverage";
|
|
8621
|
+
return "other";
|
|
8622
|
+
}
|
|
8623
|
+
function buildDeterministicPageAssignments(spans, pageCount) {
|
|
8624
|
+
if (spans.length === 0) return [];
|
|
8625
|
+
const pageTexts = /* @__PURE__ */ new Map();
|
|
8626
|
+
for (const span of spans) {
|
|
8627
|
+
const page = pageNumberForSpan(span);
|
|
8628
|
+
if (!page) continue;
|
|
8629
|
+
pageTexts.set(page, [pageTexts.get(page), span.sectionId, span.text].filter(Boolean).join("\n"));
|
|
8630
|
+
}
|
|
8631
|
+
return Array.from({ length: pageCount }, (_, index) => {
|
|
8632
|
+
const page = index + 1;
|
|
8633
|
+
const text = pageTexts.get(page) ?? "";
|
|
8634
|
+
const lower = text.toLowerCase();
|
|
8635
|
+
const extractorNames = [];
|
|
8636
|
+
if (/\bdeclarations?|policy period|named insured|producer|schedule\b/.test(lower)) {
|
|
8637
|
+
extractorNames.push("carrier_info", "named_insured", "declarations");
|
|
8638
|
+
}
|
|
8639
|
+
if (/\blimit|deductible|premium|coinsurance|scheduled amount|blanket\b/.test(lower) && /\$|\b\d{2,}(?:,\d{3})*\b/.test(lower)) {
|
|
8640
|
+
extractorNames.push("coverage_limits");
|
|
8641
|
+
}
|
|
8642
|
+
if (/\bpremium|tax|fee|surcharge\b/.test(lower) && /\$/.test(lower)) extractorNames.push("premium_breakdown");
|
|
8643
|
+
if (/\bendorsement|additional insured|amendatory\b/.test(lower)) extractorNames.push("endorsements");
|
|
8644
|
+
if (/\bexclusion|not covered|does not apply\b/.test(lower)) extractorNames.push("exclusions");
|
|
8645
|
+
if (/\bcondition|duties in the event|loss condition|general condition\b/.test(lower)) extractorNames.push("conditions");
|
|
8646
|
+
if (/\bdefinition|defined terms?\b/.test(lower)) extractorNames.push("definitions");
|
|
8647
|
+
if (/\bcovered cause|covered peril|cause of loss|perils insured\b/.test(lower)) extractorNames.push("covered_reasons");
|
|
8648
|
+
if (textIncludesSupplementarySignal(text)) extractorNames.push("supplementary");
|
|
8649
|
+
if (extractorNames.length === 0 && text.trim().length > 180) extractorNames.push("sections");
|
|
8650
|
+
return {
|
|
8651
|
+
localPageNumber: page,
|
|
8652
|
+
extractorNames: [...new Set(extractorNames)],
|
|
8653
|
+
pageRole: inferPageRole(lower),
|
|
8654
|
+
hasScheduleValues: /\$|\b\d{2,}(?:,\d{3})*\b/.test(lower) && /\blimit|deductible|premium|schedule|class|location\b/.test(lower),
|
|
8655
|
+
confidence: text.trim().length > 0 ? 0.8 : 0,
|
|
8656
|
+
notes: "Deterministic source-span page assignment"
|
|
8657
|
+
};
|
|
8658
|
+
});
|
|
8659
|
+
}
|
|
8660
|
+
function inferPageRole(lowerText) {
|
|
8661
|
+
if (/\bdeclarations?|schedule\b/.test(lowerText)) return "declarations_schedule";
|
|
8662
|
+
if (/\bendorsement\b/.test(lowerText)) return "endorsement_form";
|
|
8663
|
+
if (/\bexclusion|condition\b/.test(lowerText)) return "condition_exclusion_form";
|
|
8664
|
+
if (/\bnotice|department of insurance|complaint\b/.test(lowerText)) return "supplementary";
|
|
8665
|
+
if (lowerText.trim()) return "policy_form";
|
|
8666
|
+
return "other";
|
|
8667
|
+
}
|
|
8544
8668
|
function shouldRunLlmReview(mode, report, sourceSpansAvailable) {
|
|
8545
8669
|
if (mode === "skip" || maxReviewRounds <= 0) return false;
|
|
8546
8670
|
if (mode === "always") return true;
|
|
@@ -8576,7 +8700,14 @@ function createExtractor(config) {
|
|
|
8576
8700
|
}
|
|
8577
8701
|
return lines.length > 0 ? lines.join("\n") : "";
|
|
8578
8702
|
}
|
|
8579
|
-
async function runFocusedExtractorTask(task, pdfInput, memory, pageRangeCache, getPageRangePdf, getPageImages, getPageRangeText) {
|
|
8703
|
+
async function runFocusedExtractorTask(task, pdfInput, memory, sourceSpansForSections, pageRangeCache, getPageRangePdf, getPageImages, getPageRangeText) {
|
|
8704
|
+
if (task.extractorName === "sections" && sourceSpansForSections.length > 0) {
|
|
8705
|
+
return {
|
|
8706
|
+
name: "sections",
|
|
8707
|
+
data: buildSourceBackedSectionIndex(sourceSpansForSections, task.startPage, task.endPage),
|
|
8708
|
+
usage: void 0
|
|
8709
|
+
};
|
|
8710
|
+
}
|
|
8580
8711
|
if (task.extractorName === "supplementary") {
|
|
8581
8712
|
const alreadyExtractedSummary = buildAlreadyExtractedSummary(memory);
|
|
8582
8713
|
const budget = resolveBudget("extraction_focused", 4096);
|
|
@@ -8825,6 +8956,17 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
|
|
|
8825
8956
|
formInventory = resumed.formInventory;
|
|
8826
8957
|
memory.set("form_inventory", formInventory);
|
|
8827
8958
|
onProgress?.("Resuming from checkpoint (form inventory complete)...");
|
|
8959
|
+
} else if (sourceSpans.length > 0) {
|
|
8960
|
+
formInventory = buildDeterministicFormInventory(sourceSpans, pageCount) ?? { forms: [] };
|
|
8961
|
+
onProgress?.(`Built deterministic form inventory for ${primaryType} ${documentType}.`);
|
|
8962
|
+
memory.set("form_inventory", formInventory);
|
|
8963
|
+
await pipelineCtx.save("form_inventory", {
|
|
8964
|
+
id,
|
|
8965
|
+
pageCount,
|
|
8966
|
+
classifyResult,
|
|
8967
|
+
formInventory,
|
|
8968
|
+
memory: Object.fromEntries(memory)
|
|
8969
|
+
});
|
|
8828
8970
|
} else {
|
|
8829
8971
|
onProgress?.(`Building form inventory for ${primaryType} ${documentType}...`);
|
|
8830
8972
|
const budget = resolveBudget("extraction_form_inventory", 2048);
|
|
@@ -8865,6 +9007,20 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
|
|
|
8865
9007
|
if (resumed?.pageAssignments && pipelineCtx.isPhaseComplete("page_map")) {
|
|
8866
9008
|
pageAssignments = resumed.pageAssignments;
|
|
8867
9009
|
onProgress?.("Resuming from checkpoint (page map complete)...");
|
|
9010
|
+
} else if (sourceSpans.length > 0) {
|
|
9011
|
+
onProgress?.(`Mapping document pages from source spans for ${primaryType} ${documentType}...`);
|
|
9012
|
+
pageAssignments = normalizePageAssignments(
|
|
9013
|
+
buildDeterministicPageAssignments(sourceSpans, pageCount),
|
|
9014
|
+
formInventory
|
|
9015
|
+
);
|
|
9016
|
+
await pipelineCtx.save("page_map", {
|
|
9017
|
+
id,
|
|
9018
|
+
pageCount,
|
|
9019
|
+
classifyResult,
|
|
9020
|
+
formInventory,
|
|
9021
|
+
pageAssignments,
|
|
9022
|
+
memory: Object.fromEntries(memory)
|
|
9023
|
+
});
|
|
8868
9024
|
} else {
|
|
8869
9025
|
onProgress?.(`Mapping document pages for ${primaryType} ${documentType}...`);
|
|
8870
9026
|
const chunkSize = 8;
|
|
@@ -8984,6 +9140,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
|
|
|
8984
9140
|
task,
|
|
8985
9141
|
extractionPdfInput,
|
|
8986
9142
|
memory,
|
|
9143
|
+
sourceSpans,
|
|
8987
9144
|
completedPageRangePdfCache,
|
|
8988
9145
|
getPageRangePdf,
|
|
8989
9146
|
convertPdfToImages ? getPageImages : void 0,
|
|
@@ -9148,6 +9305,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
|
|
|
9148
9305
|
task,
|
|
9149
9306
|
extractionPdfInput,
|
|
9150
9307
|
memory,
|
|
9308
|
+
sourceSpans,
|
|
9151
9309
|
completedPageRangePdfCache,
|
|
9152
9310
|
getPageRangePdf,
|
|
9153
9311
|
convertPdfToImages ? getPageImages : void 0,
|