@claritylabs/cl-sdk 0.9.0 → 0.10.1

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 CHANGED
@@ -1,674 +1,56 @@
1
1
  # CL-SDK
2
2
 
3
- [Clarity Labs](https://claritylabs.inc) allows insurers to understand their clients as well as they know themselves. A better understanding of clients means insurers can automate servicing to reduce costs and identify coverage gaps to cross-sell products.
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
- CL-SDK is the open infrastructure layer that makes this possible — a pure TypeScript library for extracting, reasoning about, and acting on insurance documents. Provider-agnostic by design: bring any LLM, any embedding model, any storage backend.
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
- ### Peer Dependencies
13
+ ## What It Does
14
14
 
15
- ```bash
16
- npm install pdf-lib zod
17
- ```
18
-
19
- Optional (for SQLite storage):
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
- // schema is a Zod schema use it for structured output
41
- // IMPORTANT: pass providerOptions.pdfBase64 and/or providerOptions.images
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); // Typed InsuranceDocument (policy or quote)
57
- console.log(result.chunks); // DocumentChunk[] ready for vector storage
58
- console.log(result.tokenUsage); // Aggregate input/output tokens when available
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
- ### Schema Compatibility
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 # Build ESM + CJS + types via tsup
50
+ npm run build # ESM + CJS + types via tsup
670
51
  npm run dev # Watch mode
671
- npm run typecheck # Type check (tsc --noEmit)
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`.