@claritylabs/cl-sdk 0.9.0 → 0.10.0
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 +18 -636
- package/dist/index.d.mts +841 -65
- package/dist/index.d.ts +841 -65
- package/dist/index.js +1175 -335
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1169 -335
- package/dist/index.mjs.map +1 -1
- package/dist/storage-sqlite.d.mts +114 -24
- package/dist/storage-sqlite.d.ts +114 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,674 +1,56 @@
|
|
|
1
1
|
# CL-SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Open infrastructure for building AI agents that work with insurance. Pure TypeScript, provider-agnostic — bring any LLM, any embedding model, any storage backend.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**[Documentation](https://cl-sdk.claritylabs.inc/docs)** | **[npm](https://www.npmjs.com/package/@claritylabs/cl-sdk)** | **[GitHub](https://github.com/claritylabs-inc/cl-sdk)**
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @claritylabs/cl-sdk
|
|
10
|
+
npm install @claritylabs/cl-sdk pdf-lib zod
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## What It Does
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm install better-sqlite3
|
|
22
|
-
```
|
|
15
|
+
- **Document Extraction** — Agentic pipeline with 11 focused extractors that turns insurance PDFs into structured data with page-level provenance and quality gates
|
|
16
|
+
- **Query Agent** — Citation-backed question answering over stored documents with sub-question decomposition and grounding verification
|
|
17
|
+
- **Application Processing** — Eight focused agents handle intake — field extraction, auto-fill from prior answers, topic-based question batching, and PDF mapping
|
|
18
|
+
- **Agent System** — Composable prompt modules for building insurance-aware conversational agents across email, chat, SMS, Slack, and Discord
|
|
19
|
+
- **Storage** — DocumentStore and MemoryStore interfaces with SQLite reference implementation
|
|
23
20
|
|
|
24
21
|
## Quick Start
|
|
25
22
|
|
|
26
|
-
### Document Extraction
|
|
27
|
-
|
|
28
|
-
CL-SDK extracts structured data from insurance PDFs using a multi-agent pipeline. You provide two callback functions — `generateText` and `generateObject` — and the SDK handles the rest:
|
|
29
|
-
|
|
30
23
|
```typescript
|
|
31
24
|
import { createExtractor } from "@claritylabs/cl-sdk";
|
|
32
25
|
|
|
33
26
|
const extractor = createExtractor({
|
|
34
27
|
generateText: async ({ prompt, system, maxTokens, providerOptions }) => {
|
|
35
|
-
// Wrap your preferred LLM provider
|
|
36
28
|
const result = await yourProvider.generate({ prompt, system, maxTokens, providerOptions });
|
|
37
29
|
return { text: result.text, usage: result.usage };
|
|
38
30
|
},
|
|
39
31
|
generateObject: async ({ prompt, system, schema, maxTokens, providerOptions }) => {
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
// through to your model as file/image message parts.
|
|
43
|
-
const result = await yourProvider.generateStructured({
|
|
44
|
-
prompt,
|
|
45
|
-
system,
|
|
46
|
-
schema,
|
|
47
|
-
maxTokens,
|
|
48
|
-
providerOptions,
|
|
49
|
-
});
|
|
32
|
+
// Pass providerOptions.pdfBase64 and/or providerOptions.images to your model
|
|
33
|
+
const result = await yourProvider.generateStructured({ prompt, system, schema, maxTokens, providerOptions });
|
|
50
34
|
return { object: result.object, usage: result.usage };
|
|
51
35
|
},
|
|
52
36
|
});
|
|
53
37
|
|
|
54
|
-
const pdfBase64 = "..."; // base64-encoded insurance PDF
|
|
55
38
|
const result = await extractor.extract(pdfBase64);
|
|
56
|
-
console.log(result.document);
|
|
57
|
-
console.log(result.chunks);
|
|
58
|
-
console.log(result.
|
|
59
|
-
console.log(result.usageReporting); // How many model calls did or did not report usage
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### With PDF-to-Image Conversion
|
|
63
|
-
|
|
64
|
-
For providers that don't support native PDF input (e.g., OpenAI):
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
const extractor = createExtractor({
|
|
68
|
-
generateText: /* ... */,
|
|
69
|
-
generateObject: /* ... */,
|
|
70
|
-
convertPdfToImages: async (pdfBase64, startPage, endPage) => {
|
|
71
|
-
// Convert PDF pages to images using your preferred library
|
|
72
|
-
return [{ imageBase64: "...", mimeType: "image/png" }]; // one per page
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Architecture
|
|
78
|
-
|
|
79
|
-
### Provider-Agnostic Callbacks
|
|
80
|
-
|
|
81
|
-
CL-SDK has **zero framework dependencies**. All LLM interaction happens through two callback types:
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
type GenerateText = (params: {
|
|
85
|
-
prompt: string;
|
|
86
|
-
system?: string;
|
|
87
|
-
maxTokens: number;
|
|
88
|
-
providerOptions?: Record<string, unknown>;
|
|
89
|
-
}) => Promise<{ text: string; usage?: { inputTokens: number; outputTokens: number } }>;
|
|
90
|
-
|
|
91
|
-
type GenerateObject<T> = (params: {
|
|
92
|
-
prompt: string;
|
|
93
|
-
system?: string;
|
|
94
|
-
schema: ZodSchema<T>;
|
|
95
|
-
maxTokens: number;
|
|
96
|
-
providerOptions?: Record<string, unknown>;
|
|
97
|
-
}) => Promise<{ object: T; usage?: { inputTokens: number; outputTokens: number } }>;
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
For extraction calls, `providerOptions` can carry document content:
|
|
101
|
-
|
|
102
|
-
- `providerOptions.pdfBase64` — the PDF to send as a file part
|
|
103
|
-
- `providerOptions.images` — page images to send as image parts
|
|
104
|
-
|
|
105
|
-
The coordinator passes the full PDF to classify and plan. Worker extractors pass a page-scoped PDF produced by `extractPageRange()` unless `convertPdfToImages` is enabled, in which case they pass page images instead. Your callback must include that content in the actual model request; the prompt text alone is not sufficient.
|
|
106
|
-
|
|
107
|
-
Works with any provider: Anthropic, OpenAI, Google, Mistral, Bedrock, Azure, Ollama, etc. You write the adapter once; the SDK calls it throughout the pipeline.
|
|
108
|
-
|
|
109
|
-
> **Strict structured output compatibility:** The SDK automatically transforms Zod schemas before passing them to `generateObject` — converting `.optional()` fields to `.nullable()` so all properties appear in the JSON Schema `required` array. This ensures compatibility with providers like OpenAI that enforce strict structured output validation. No adapter changes needed on your end.
|
|
110
|
-
|
|
111
|
-
### Extraction Pipeline
|
|
112
|
-
|
|
113
|
-
The extraction system uses a **coordinator/worker pattern** with page-aware planning, merged worker outputs, and a document-grounded review loop.
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
|
117
|
-
│ 1. CLASSIFY │────▶│ 2. PAGE MAP │────▶│ 3. PLAN │
|
|
118
|
-
│ │ │ │ │ │
|
|
119
|
-
│ Document │ │ Assign pages │ │ Build tasks │
|
|
120
|
-
│ type, line │ │ to focused │ │ from page │
|
|
121
|
-
│ of business │ │ extractors │ │ assignments │
|
|
122
|
-
└─────────────┘ └──────┬───────┘ └──────┬──────┘
|
|
123
|
-
│ │
|
|
124
|
-
┌──────▼────────────────────▼──────┐
|
|
125
|
-
│ 4. EXTRACT + MERGE (parallel) │
|
|
126
|
-
│ Run focused extractors, merge │
|
|
127
|
-
│ repeat runs instead of overwrite │
|
|
128
|
-
└──────────────┬───────────────────┘
|
|
129
|
-
│
|
|
130
|
-
┌─────────────┐ ┌─────────────▼───────────┐ ┌─────────────┐
|
|
131
|
-
│ 7. FORMAT │◀────│ 6. ASSEMBLE │◀────│ 5. REVIEW │
|
|
132
|
-
│ │ │ │ │ │
|
|
133
|
-
│ Clean up │ │ Merge all data into │ │ Check │
|
|
134
|
-
│ markdown │ │ final document │ │ completeness│
|
|
135
|
-
└──────┬──────┘ └─────────────────────────┘ │ and quality │
|
|
136
|
-
│ └──────┬──────┘
|
|
137
|
-
┌──────▼──────┐ │
|
|
138
|
-
│ 8. CHUNK │◀───────────────────────────────────────────────┘
|
|
139
|
-
│ Break into │
|
|
140
|
-
│ retrieval- │
|
|
141
|
-
│ ready chunks│
|
|
142
|
-
└─────────────┘
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
#### Phase 1: Classify
|
|
146
|
-
|
|
147
|
-
The coordinator sends the document to `generateObject` with the `ClassifyResultSchema`. It determines:
|
|
148
|
-
- **Document type** — policy or quote
|
|
149
|
-
- **Policy types** — one or more lines of business (e.g., `general_liability`, `workers_comp`)
|
|
150
|
-
- **Confidence score**
|
|
151
|
-
|
|
152
|
-
The full document is passed through `providerOptions.pdfBase64` for this step, so your callback must attach that PDF to the model request as a real document/file part.
|
|
153
|
-
|
|
154
|
-
#### Phase 2: Page Map
|
|
155
|
-
|
|
156
|
-
Before planning tasks, the coordinator maps each page to one or more focused extractors. This reduces the chance that declaration pages and schedule pages get mixed into large generic ranges dominated by form language.
|
|
157
|
-
|
|
158
|
-
The page-mapping step uses the relevant PDF pages through `providerOptions.pdfBase64`, chunk by chunk, and produces page-level assignments for the downstream plan.
|
|
159
|
-
|
|
160
|
-
#### Phase 3: Plan
|
|
161
|
-
|
|
162
|
-
Based on the classification, page map, and **line-of-business template** (e.g., `workers_comp`, `cyber`, `homeowners_ho3`), the coordinator builds an **extraction plan** — a list of focused extractor tasks derived from those page assignments.
|
|
163
|
-
|
|
164
|
-
The old prompt-only `plan.ts` module is a deprecated candidate and is no longer the active planning path used by the coordinator.
|
|
165
|
-
|
|
166
|
-
#### Phase 4: Extract And Merge
|
|
167
|
-
|
|
168
|
-
Focused extractor agents are dispatched **in parallel** (concurrency-limited, default 2). Each extractor targets a specific data domain against its assigned page range. The 11 extractor types are:
|
|
169
|
-
|
|
170
|
-
| Extractor | What It Extracts |
|
|
171
|
-
|-----------|-----------------|
|
|
172
|
-
| `carrier_info` | Carrier name, NAIC, AM Best rating, MGA, underwriter, broker |
|
|
173
|
-
| `named_insured` | Insured name, DBA, address, entity type, FEIN, SIC/NAICS |
|
|
174
|
-
| `declarations` | Line-specific structured declarations (varies by policy type) |
|
|
175
|
-
| `coverage_limits` | Coverage names, limits, deductibles, forms, triggers |
|
|
176
|
-
| `endorsements` | Form numbers, titles, types, content, affected parties |
|
|
177
|
-
| `exclusions` | Exclusion titles, content, applicability |
|
|
178
|
-
| `conditions` | Duties after loss, cancellation, other insurance, etc. |
|
|
179
|
-
| `premium_breakdown` | Premium amounts, taxes, fees, payment plans, rating basis |
|
|
180
|
-
| `loss_history` | Loss runs, claim records, experience modification |
|
|
181
|
-
| `supplementary` | Regulatory context, contacts, TPA, claims contacts |
|
|
182
|
-
| `sections` | Raw section content (fallback for unmatched sections) |
|
|
183
|
-
|
|
184
|
-
Each extractor writes its results to an in-memory `Map`. Repeated extractor runs now **merge** instead of overwriting previous results, which is critical for extractors like `coverage_limits`, `endorsements`, `exclusions`, `conditions`, `sections`, and `declarations`.
|
|
185
|
-
|
|
186
|
-
Before each worker call, the SDK slices the requested page range with `extractPageRange()` and passes that page-scoped PDF through `providerOptions.pdfBase64`. If `convertPdfToImages` is configured, it passes `providerOptions.images` instead. The callback layer is responsible for actually including that content in the model input.
|
|
187
|
-
|
|
188
|
-
#### Phase 5: Review
|
|
189
|
-
|
|
190
|
-
After initial extraction, a review loop (up to `maxReviewRounds`, default 2) checks both **completeness and quality**. The reviewer sees the full PDF, a page-map summary, and a summary of extracted results. It is expected to catch issues like missing required fields, generic placeholder outputs such as "shown in declarations" or "per schedule", and outputs that appear to come from generic form text instead of declaration/schedule values.
|
|
191
|
-
|
|
192
|
-
If gaps or quality issues are found, additional focused extractor tasks are dispatched.
|
|
193
|
-
|
|
194
|
-
#### Phase 6: Assemble
|
|
195
|
-
|
|
196
|
-
All extractor results are merged into a final validated `InsuranceDocument`.
|
|
197
|
-
|
|
198
|
-
#### Phase 7: Format
|
|
199
|
-
|
|
200
|
-
A formatting agent pass cleans up markdown in all content-bearing string fields (sections, subsections, endorsements, exclusions, conditions, summary). It fixes:
|
|
201
|
-
|
|
202
|
-
- **Pipe tables missing separator rows** — adds `| --- | --- |` and leading/trailing pipes
|
|
203
|
-
- **Space-aligned tables** — converts whitespace-padded columns into proper markdown tables
|
|
204
|
-
- **Sub-items mixed into tables** — pulls indented sub-items out of tables into lists
|
|
205
|
-
- **Mixed table/prose content** — handles each segment independently
|
|
206
|
-
- **General cleanup** — excessive blank lines, trailing whitespace, orphaned formatting markers
|
|
207
|
-
|
|
208
|
-
Content is batched (up to 20 fields per call) and sent through `generateText` for formatting cleanup. Token usage is tracked the same as other pipeline steps.
|
|
209
|
-
|
|
210
|
-
#### Phase 8: Chunk
|
|
211
|
-
|
|
212
|
-
The formatted document is chunked into `DocumentChunk[]` for vector storage. Chunks are deterministically IDed as `${documentId}:${type}:${index}`.
|
|
213
|
-
|
|
214
|
-
### Configuration
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
const extractor = createExtractor({
|
|
218
|
-
// Required: LLM callbacks
|
|
219
|
-
generateText,
|
|
220
|
-
generateObject,
|
|
221
|
-
|
|
222
|
-
// Optional: PDF vision mode
|
|
223
|
-
convertPdfToImages: async (pdfBase64, startPage, endPage) => [...],
|
|
224
|
-
|
|
225
|
-
// Optional: storage backends
|
|
226
|
-
documentStore, // Persist extracted documents
|
|
227
|
-
memoryStore, // Vector search over chunks + conversation history
|
|
228
|
-
|
|
229
|
-
// Optional: tuning
|
|
230
|
-
concurrency: 2, // Max parallel extractors (default: 2)
|
|
231
|
-
maxReviewRounds: 2, // Review loop iterations (default: 2)
|
|
232
|
-
|
|
233
|
-
// Optional: observability
|
|
234
|
-
onTokenUsage: (usage) => console.log(`${usage.inputTokens} in, ${usage.outputTokens} out`),
|
|
235
|
-
onProgress: (message) => console.log(message),
|
|
236
|
-
log: async (message) => logger.info(message),
|
|
237
|
-
providerOptions: {}, // Passed through to every LLM call
|
|
238
|
-
});
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
`tokenUsage` aggregates whatever usage your callbacks return. `usageReporting` tells you how many model calls reported usage versus how many omitted it, so a `0 in / 0 out` result is diagnosable instead of silent.
|
|
242
|
-
|
|
243
|
-
### Line-of-Business Templates
|
|
244
|
-
|
|
245
|
-
Templates define what the extraction pipeline expects for each policy type. Each template specifies expected sections, page hints, and required vs. optional fields.
|
|
246
|
-
|
|
247
|
-
**Personal lines:** homeowners (HO-3, HO-5), renters (HO-4), condo (HO-6), dwelling fire, personal auto, personal umbrella, personal inland marine, flood (NFIP + private), earthquake, watercraft, recreational vehicle, farm/ranch, mobile home
|
|
248
|
-
|
|
249
|
-
**Commercial lines:** general liability, commercial property, commercial auto, workers' comp, umbrella/excess, professional liability, cyber, directors & officers, crime/fidelity
|
|
250
|
-
|
|
251
|
-
## Storage
|
|
252
|
-
|
|
253
|
-
CL-SDK defines two storage interfaces (`DocumentStore` and `MemoryStore`) and ships a reference SQLite implementation. You can implement these interfaces with any backend.
|
|
254
|
-
|
|
255
|
-
### DocumentStore
|
|
256
|
-
|
|
257
|
-
CRUD for extracted `InsuranceDocument` objects:
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
interface DocumentStore {
|
|
261
|
-
save(doc: InsuranceDocument): Promise<void>;
|
|
262
|
-
get(id: string): Promise<InsuranceDocument | null>;
|
|
263
|
-
query(filters: DocumentFilters): Promise<InsuranceDocument[]>;
|
|
264
|
-
delete(id: string): Promise<void>;
|
|
265
|
-
}
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
Filters support: `type` (policy/quote), `carrier` (fuzzy), `insuredName` (fuzzy), `policyNumber` (exact), `quoteNumber` (exact).
|
|
269
|
-
|
|
270
|
-
### MemoryStore
|
|
271
|
-
|
|
272
|
-
Vector-searchable storage for document chunks and conversation history. Requires an `EmbedText` callback for generating embeddings:
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
type EmbedText = (text: string) => Promise<number[]>;
|
|
276
|
-
|
|
277
|
-
interface MemoryStore {
|
|
278
|
-
// Document chunks with embeddings
|
|
279
|
-
addChunks(chunks: DocumentChunk[]): Promise<void>;
|
|
280
|
-
search(query: string, options?: { limit?: number; filter?: ChunkFilter }): Promise<DocumentChunk[]>;
|
|
281
|
-
|
|
282
|
-
// Conversation turns with embeddings
|
|
283
|
-
addTurn(turn: ConversationTurn): Promise<void>;
|
|
284
|
-
getHistory(conversationId: string, options?: { limit?: number }): Promise<ConversationTurn[]>;
|
|
285
|
-
searchHistory(query: string, conversationId?: string): Promise<ConversationTurn[]>;
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
Search uses **cosine similarity** over embeddings to find semantically relevant chunks or conversation turns. Embedding failures are non-fatal — chunks are still stored, just not searchable by vector.
|
|
290
|
-
|
|
291
|
-
### SQLite Reference Implementation
|
|
292
|
-
|
|
293
|
-
```typescript
|
|
294
|
-
import { createSqliteStore } from "@claritylabs/cl-sdk/storage/sqlite";
|
|
295
|
-
|
|
296
|
-
const store = createSqliteStore({
|
|
297
|
-
path: "./cl-sdk.db",
|
|
298
|
-
embed: async (text) => {
|
|
299
|
-
// Your embedding function (OpenAI, Cohere, local model, etc.)
|
|
300
|
-
return await yourEmbeddingProvider.embed(text);
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
// Use with extractor
|
|
305
|
-
const extractor = createExtractor({
|
|
306
|
-
generateText,
|
|
307
|
-
generateObject,
|
|
308
|
-
documentStore: store.documents,
|
|
309
|
-
memoryStore: store.memory,
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
// Or use standalone
|
|
313
|
-
await store.documents.save(document);
|
|
314
|
-
const results = await store.memory.search("what is the deductible?", { limit: 5 });
|
|
315
|
-
|
|
316
|
-
// Clean up
|
|
317
|
-
store.close();
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
## Agent System
|
|
321
|
-
|
|
322
|
-
CL-SDK includes a composable prompt system for building insurance-aware AI agents. The `buildAgentSystemPrompt` function assembles modular prompt segments based on the agent's context:
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
import { buildAgentSystemPrompt } from "@claritylabs/cl-sdk";
|
|
326
|
-
|
|
327
|
-
const systemPrompt = buildAgentSystemPrompt({
|
|
328
|
-
platform: "email", // email | chat | sms | slack | discord
|
|
329
|
-
intent: "direct", // direct | mediated | observed
|
|
330
|
-
userName: "John",
|
|
331
|
-
companyName: "Acme Insurance",
|
|
332
|
-
});
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
### Prompt Modules
|
|
336
|
-
|
|
337
|
-
The system prompt is composed from these modules:
|
|
338
|
-
|
|
339
|
-
| Module | Purpose |
|
|
340
|
-
|--------|---------|
|
|
341
|
-
| **identity** | Agent role, company context, professional persona |
|
|
342
|
-
| **intent** | Behavioral rules based on platform and interaction mode |
|
|
343
|
-
| **formatting** | Output formatting rules (markdown for chat, plaintext for email/SMS) |
|
|
344
|
-
| **safety** | Security guardrails, prompt injection resistance, data handling |
|
|
345
|
-
| **coverage-gaps** | Coverage gap disclosure rules (only in mediated/observed mode) |
|
|
346
|
-
| **coi-routing** | Certificate of Insurance request handling |
|
|
347
|
-
| **quotes-policies** | Guidance for distinguishing quotes vs. active policies |
|
|
348
|
-
| **conversation-memory** | Context about conversation history and document retrieval |
|
|
349
|
-
|
|
350
|
-
### Message Intent Classification
|
|
351
|
-
|
|
352
|
-
Classify incoming messages to route them appropriately:
|
|
353
|
-
|
|
354
|
-
```typescript
|
|
355
|
-
import { buildClassifyMessagePrompt } from "@claritylabs/cl-sdk";
|
|
356
|
-
|
|
357
|
-
const prompt = buildClassifyMessagePrompt("email");
|
|
358
|
-
// Returns classification prompt for intents:
|
|
359
|
-
// policy_question, coi_request, renewal_inquiry, claim_report,
|
|
360
|
-
// coverage_shopping, general, unrelated
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
## Application Processing Pipeline
|
|
364
|
-
|
|
365
|
-
The application pipeline processes insurance applications through an agentic coordinator/worker system — small focused agents handle classification, field extraction, auto-fill, question batching, reply routing, and PDF mapping. Supports persistent state and vector-based answer backfill from prior applications.
|
|
366
|
-
|
|
367
|
-
### Quick Start
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
import { createApplicationPipeline } from "@claritylabs/cl-sdk";
|
|
371
|
-
|
|
372
|
-
const pipeline = createApplicationPipeline({
|
|
373
|
-
generateText,
|
|
374
|
-
generateObject,
|
|
375
|
-
applicationStore, // persistent state storage
|
|
376
|
-
documentStore, // for policy/quote lookups during auto-fill
|
|
377
|
-
memoryStore, // for vector-based answer backfill
|
|
378
|
-
orgContext: [ // business context for auto-fill
|
|
379
|
-
{ key: "company_name", value: "Acme Corp", category: "company_info" },
|
|
380
|
-
{ key: "company_address", value: "123 Main St", category: "company_info" },
|
|
381
|
-
],
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// Process a new application PDF
|
|
385
|
-
const { state } = await pipeline.processApplication({
|
|
386
|
-
pdfBase64: "...",
|
|
387
|
-
applicationId: "app-123",
|
|
388
|
-
});
|
|
389
|
-
// state.fields → extracted fields, some already auto-filled
|
|
390
|
-
// state.batches → question batches ready for user collection
|
|
391
|
-
|
|
392
|
-
// Generate email for current batch
|
|
393
|
-
const { text: emailBody } = await pipeline.generateCurrentBatchEmail("app-123", {
|
|
394
|
-
companyName: "Acme Corp",
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
// Process user's reply
|
|
398
|
-
const { state: updated, fieldsFilled, responseText } = await pipeline.processReply({
|
|
399
|
-
applicationId: "app-123",
|
|
400
|
-
replyText: "1. Yes\n2. $1,000,000\n3. Check our website for revenue",
|
|
401
|
-
});
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### Pipeline Phases
|
|
405
|
-
|
|
406
|
-
```
|
|
407
|
-
┌─────────────┐ ┌──────────────┐ ┌─────────────────────┐
|
|
408
|
-
│ 1. CLASSIFY │────>│ 2. EXTRACT │────>│ 3. BACKFILL + │
|
|
409
|
-
│ │ │ FIELDS │ │ AUTO-FILL │
|
|
410
|
-
│ Is this an │ │ │ │ (parallel) │
|
|
411
|
-
│ application? │ │ All fillable │ │ │
|
|
412
|
-
│ │ │ fields as │ │ • vector backfill │
|
|
413
|
-
│ │ │ structured │ │ • context auto-fill │
|
|
414
|
-
│ │ │ data │ │ • document search │
|
|
415
|
-
└──────────────┘ └──────────────┘ └──────────┬──────────┘
|
|
416
|
-
│
|
|
417
|
-
┌──────────────┐ ┌──────────v──────────┐
|
|
418
|
-
│ REPLY LOOP │<────│ 4. BATCH QUESTIONS │
|
|
419
|
-
│ │ │ │
|
|
420
|
-
│ Route intent │ │ Group unfilled │
|
|
421
|
-
│ Parse answers│ │ fields by topic │
|
|
422
|
-
│ Handle lookup│ │ Generate emails │
|
|
423
|
-
│ Explain field│ │ │
|
|
424
|
-
└──────┬───────┘ └─────────────────────┘
|
|
425
|
-
│
|
|
426
|
-
┌──────v───────┐
|
|
427
|
-
│ 5. CONFIRM + │
|
|
428
|
-
│ MAP PDF │
|
|
429
|
-
└──────────────┘
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
### Focused Agents (8 types)
|
|
433
|
-
|
|
434
|
-
| Agent | Task | Model Size |
|
|
435
|
-
|-------|------|-----------|
|
|
436
|
-
| `classifier` | Detect if PDF is an application | Tiny |
|
|
437
|
-
| `field-extractor` | Extract all form fields | Medium |
|
|
438
|
-
| `auto-filler` | Match fields to business context | Small |
|
|
439
|
-
| `batcher` | Group fields into topic batches | Small |
|
|
440
|
-
| `reply-router` | Classify reply intent | Tiny |
|
|
441
|
-
| `answer-parser` | Extract answers from replies | Small |
|
|
442
|
-
| `lookup-filler` | Fill from policy/record lookups | Small |
|
|
443
|
-
| `email-generator` | Generate professional batch emails | Small |
|
|
444
|
-
|
|
445
|
-
### Vector-Based Answer Backfill
|
|
446
|
-
|
|
447
|
-
The `BackfillProvider` interface enables searching prior application answers and extracted document data to pre-fill new applications:
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
interface BackfillProvider {
|
|
451
|
-
searchPriorAnswers(
|
|
452
|
-
fields: { id: string; label: string; section: string; fieldType: string }[],
|
|
453
|
-
options?: { limit?: number },
|
|
454
|
-
): Promise<PriorAnswer[]>;
|
|
455
|
-
}
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
This runs in parallel with context-based auto-fill, so the pipeline fills as many fields as possible before asking the user anything.
|
|
459
|
-
|
|
460
|
-
### Application Prompts (for advanced use)
|
|
461
|
-
|
|
462
|
-
The individual prompt functions are still exported for custom pipelines:
|
|
463
|
-
|
|
464
|
-
```typescript
|
|
465
|
-
import {
|
|
466
|
-
buildFieldExtractionPrompt,
|
|
467
|
-
buildAutoFillPrompt,
|
|
468
|
-
buildQuestionBatchPrompt,
|
|
469
|
-
buildAnswerParsingPrompt,
|
|
470
|
-
buildConfirmationSummaryPrompt,
|
|
471
|
-
buildBatchEmailGenerationPrompt,
|
|
472
|
-
buildReplyIntentClassificationPrompt,
|
|
473
|
-
buildFieldExplanationPrompt,
|
|
474
|
-
buildFlatPdfMappingPrompt,
|
|
475
|
-
buildAcroFormMappingPrompt,
|
|
476
|
-
buildLookupFillPrompt,
|
|
477
|
-
} from "@claritylabs/cl-sdk";
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
## Query Agent Pipeline
|
|
481
|
-
|
|
482
|
-
The query agent answers user questions against stored documents with citation-backed provenance. It mirrors the extraction pipeline's coordinator/worker pattern: a classifier decomposes questions, retrievers pull evidence in parallel, reasoners answer from evidence only, and a verifier checks grounding.
|
|
483
|
-
|
|
484
|
-
### Quick Start
|
|
485
|
-
|
|
486
|
-
```typescript
|
|
487
|
-
import { createQueryAgent } from "@claritylabs/cl-sdk";
|
|
488
|
-
|
|
489
|
-
const agent = createQueryAgent({
|
|
490
|
-
generateText,
|
|
491
|
-
generateObject,
|
|
492
|
-
documentStore, // where extracted documents are stored
|
|
493
|
-
memoryStore, // where document chunks + conversation history live
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
const result = await agent.query({
|
|
497
|
-
question: "What is the deductible on our GL policy?",
|
|
498
|
-
conversationId: "conv-123",
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
console.log(result.answer); // Natural language answer
|
|
502
|
-
console.log(result.citations); // Source references with exact quotes
|
|
503
|
-
console.log(result.confidence); // 0-1 confidence score
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
### Pipeline Phases
|
|
507
|
-
|
|
508
|
-
```
|
|
509
|
-
┌─────────────┐ ┌──────────────┐ ┌────────────────────┐
|
|
510
|
-
│ 1. CLASSIFY │────>│ 2. RETRIEVE │────>│ 3. REASON │
|
|
511
|
-
│ │ │ (parallel) │ │ (parallel) │
|
|
512
|
-
│ Intent + │ │ │ │ │
|
|
513
|
-
│ sub-question │ │ chunk search │ │ Answer each sub-Q │
|
|
514
|
-
│ decomposition│ │ doc lookup │ │ from evidence only │
|
|
515
|
-
│ │ │ conv history │ │ │
|
|
516
|
-
└──────────────┘ └──────────────┘ └─────────┬──────────┘
|
|
517
|
-
│
|
|
518
|
-
┌──────────────┐ ┌─────────v──────────┐
|
|
519
|
-
│ 5. RESPOND │<────│ 4. VERIFY │
|
|
520
|
-
│ │ │ │
|
|
521
|
-
│ Format with │ │ Grounding check │
|
|
522
|
-
│ citations, │ │ Consistency check │
|
|
523
|
-
│ store turn │ │ Completeness check │
|
|
524
|
-
└──────────────┘ └────────────────────┘
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
**Phase 1 — Classify:** Determines intent (`policy_question`, `coverage_comparison`, `document_search`, `claims_inquiry`, `general_knowledge`) and decomposes complex questions into atomic sub-questions. Each sub-question specifies which chunk types and document filters to use for retrieval.
|
|
528
|
-
|
|
529
|
-
**Phase 2 — Retrieve (parallel):** For each sub-question, a retriever searches chunk embeddings, does structured document lookups, and pulls conversation history — all in parallel. Returns ranked evidence items.
|
|
530
|
-
|
|
531
|
-
**Phase 3 — Reason (parallel):** For each sub-question, a reasoner receives only the retrieved evidence (never the full document) and produces a sub-answer with citations. Intent-specific prompts guide reasoning (e.g., coverage questions get prompts tuned for interpreting limits and endorsements).
|
|
532
|
-
|
|
533
|
-
**Phase 4 — Verify:** The verifier checks that every claim is grounded in a citation, sub-answers don't contradict each other, and no evidence was overlooked. If issues are found, it can trigger re-retrieval with broader context.
|
|
534
|
-
|
|
535
|
-
**Phase 5 — Respond:** Merges verified sub-answers into a single natural-language response with inline citations (`[1]`, `[2]`), deduplicates references, and stores the exchange as conversation turns.
|
|
536
|
-
|
|
537
|
-
### Configuration
|
|
538
|
-
|
|
539
|
-
```typescript
|
|
540
|
-
const agent = createQueryAgent({
|
|
541
|
-
// Required
|
|
542
|
-
generateText,
|
|
543
|
-
generateObject,
|
|
544
|
-
documentStore,
|
|
545
|
-
memoryStore,
|
|
546
|
-
|
|
547
|
-
// Optional: tuning
|
|
548
|
-
concurrency: 3, // max parallel retrievers/reasoners (default: 3)
|
|
549
|
-
maxVerifyRounds: 1, // verification loop iterations (default: 1)
|
|
550
|
-
retrievalLimit: 10, // max evidence items per sub-question (default: 10)
|
|
551
|
-
|
|
552
|
-
// Optional: observability
|
|
553
|
-
onTokenUsage: (usage) => console.log(`${usage.inputTokens} in, ${usage.outputTokens} out`),
|
|
554
|
-
onProgress: (message) => console.log(message),
|
|
555
|
-
log: async (message) => logger.info(message),
|
|
556
|
-
providerOptions: {},
|
|
557
|
-
});
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
### Citations
|
|
561
|
-
|
|
562
|
-
Every factual claim in the answer references its source:
|
|
563
|
-
|
|
564
|
-
```typescript
|
|
565
|
-
interface Citation {
|
|
566
|
-
index: number; // [1], [2], etc.
|
|
567
|
-
chunkId: string; // e.g. "doc-123:coverage:2"
|
|
568
|
-
documentId: string;
|
|
569
|
-
documentType?: "policy" | "quote";
|
|
570
|
-
field?: string; // e.g. "coverages[0].deductible"
|
|
571
|
-
quote: string; // exact text from source
|
|
572
|
-
relevance: number; // 0-1 similarity score
|
|
573
|
-
}
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
## PDF Operations
|
|
577
|
-
|
|
578
|
-
```typescript
|
|
579
|
-
import {
|
|
580
|
-
extractPageRange, // Extract specific pages from a PDF
|
|
581
|
-
getPdfPageCount, // Get total page count
|
|
582
|
-
getAcroFormFields, // Enumerate form fields (text, checkbox, dropdown, radio)
|
|
583
|
-
fillAcroForm, // Fill and flatten AcroForm fields
|
|
584
|
-
overlayTextOnPdf, // Overlay text at coordinates on flat PDFs
|
|
585
|
-
} from "@claritylabs/cl-sdk";
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
## Tool Definitions
|
|
589
|
-
|
|
590
|
-
Claude `tool_use`-compatible schemas for agent integrations:
|
|
591
|
-
|
|
592
|
-
```typescript
|
|
593
|
-
import {
|
|
594
|
-
AGENT_TOOLS, // All tools as an array
|
|
595
|
-
DOCUMENT_LOOKUP_TOOL, // Search/retrieve policies and quotes
|
|
596
|
-
COI_GENERATION_TOOL, // Generate Certificates of Insurance
|
|
597
|
-
COVERAGE_COMPARISON_TOOL, // Compare coverages across documents
|
|
598
|
-
} from "@claritylabs/cl-sdk";
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
These are schema-only definitions (input schemas + descriptions). You provide the implementations that call your storage and PDF layers.
|
|
602
|
-
|
|
603
|
-
## Document Types
|
|
604
|
-
|
|
605
|
-
All types are derived from Zod schemas, providing both runtime validation and TypeScript types:
|
|
606
|
-
|
|
607
|
-
```typescript
|
|
608
|
-
import type {
|
|
609
|
-
InsuranceDocument, // PolicyDocument | QuoteDocument (discriminated union)
|
|
610
|
-
PolicyDocument, // Extracted policy with all enrichments
|
|
611
|
-
QuoteDocument, // Extracted quote with subjectivities, premium breakdown
|
|
612
|
-
Coverage, // Coverage name, limits, deductibles, form
|
|
613
|
-
EnrichedCoverage, // Coverage + additional metadata
|
|
614
|
-
Endorsement, // Form number, title, type, content
|
|
615
|
-
Exclusion, // Title, content, applicability
|
|
616
|
-
Condition, // Type, title, content
|
|
617
|
-
Declaration, // Line-specific declarations (19 types)
|
|
618
|
-
Platform, // email | chat | sms | slack | discord
|
|
619
|
-
AgentContext, // Platform + intent + user/company context
|
|
620
|
-
} from "@claritylabs/cl-sdk";
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
### Supported Policy Types
|
|
624
|
-
|
|
625
|
-
42 policy types across personal and commercial lines — including general liability, commercial property, workers' comp, cyber, D&O, homeowners (HO-3/HO-5/HO-4/HO-6), personal auto, flood (NFIP + private), earthquake, and more.
|
|
626
|
-
|
|
627
|
-
## Core Utilities
|
|
628
|
-
|
|
629
|
-
```typescript
|
|
630
|
-
import {
|
|
631
|
-
withRetry, // Exponential backoff with jitter (5 retries, 2–32s) for rate limits + transient errors
|
|
632
|
-
pLimit, // Concurrency limiter for parallel async tasks
|
|
633
|
-
sanitizeNulls, // Recursively convert null → undefined (for database compatibility)
|
|
634
|
-
stripFences, // Remove markdown code fences from LLM JSON responses
|
|
635
|
-
safeGenerateObject, // generateObject wrapper with retry, schema strictification, and fallback
|
|
636
|
-
toStrictSchema, // Convert .optional() → .nullable() for strict structured output APIs
|
|
637
|
-
} from "@claritylabs/cl-sdk";
|
|
39
|
+
console.log(result.document); // Typed InsuranceDocument
|
|
40
|
+
console.log(result.chunks); // DocumentChunk[] for vector storage
|
|
41
|
+
console.log(result.reviewReport); // Quality gate results
|
|
638
42
|
```
|
|
639
43
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
The SDK automatically handles schema compatibility with strict structured output APIs (like OpenAI). Two key mechanisms:
|
|
643
|
-
|
|
644
|
-
**`toStrictSchema(schema)`** — Recursively transforms Zod schemas so `.optional()` properties become `.nullable()`. This ensures all properties appear in the JSON Schema `required` array, which OpenAI requires. Applied automatically inside the pipeline — you don't need to call this yourself unless building custom pipelines.
|
|
645
|
-
|
|
646
|
-
**`safeGenerateObject(generateObject, params, options?)`** — Wraps a `generateObject` call with:
|
|
647
|
-
1. Automatic schema strictification via `toStrictSchema`
|
|
648
|
-
2. Retry on schema validation errors and transient API failures
|
|
649
|
-
3. Optional fallback value when all retries are exhausted
|
|
650
|
-
|
|
651
|
-
```typescript
|
|
652
|
-
import { safeGenerateObject } from "@claritylabs/cl-sdk";
|
|
653
|
-
|
|
654
|
-
const { object, usage } = await safeGenerateObject(
|
|
655
|
-
myGenerateObject,
|
|
656
|
-
{ prompt: "...", schema: MySchema, maxTokens: 1024 },
|
|
657
|
-
{
|
|
658
|
-
fallback: { field: "default" }, // Return this if all retries fail
|
|
659
|
-
maxRetries: 2, // Schema validation retries (default: 1)
|
|
660
|
-
log: async (msg) => console.log(msg),
|
|
661
|
-
},
|
|
662
|
-
);
|
|
663
|
-
```
|
|
44
|
+
See the [full documentation](https://cl-sdk.claritylabs.inc/docs) for architecture, provider setup, API reference, and more.
|
|
664
45
|
|
|
665
46
|
## Development
|
|
666
47
|
|
|
667
48
|
```bash
|
|
668
49
|
npm install
|
|
669
|
-
npm run build #
|
|
50
|
+
npm run build # ESM + CJS + types via tsup
|
|
670
51
|
npm run dev # Watch mode
|
|
671
|
-
npm run typecheck #
|
|
52
|
+
npm run typecheck # tsc --noEmit
|
|
53
|
+
npm test # vitest
|
|
672
54
|
```
|
|
673
55
|
|
|
674
56
|
Zero framework dependencies. Peer deps: `pdf-lib`, `zod`. Optional: `better-sqlite3`.
|