@claritylabs/cl-sdk 0.11.0 → 0.13.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 +49 -1
- package/dist/index.d.mts +198 -11
- package/dist/index.d.ts +198 -11
- package/dist/index.js +316 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +311 -17
- package/dist/index.mjs.map +1 -1
- package/dist/storage-sqlite.d.mts +56 -0
- package/dist/storage-sqlite.d.ts +56 -0
- package/package.json +2 -6
package/dist/index.js
CHANGED
|
@@ -36,9 +36,11 @@ __export(index_exports, {
|
|
|
36
36
|
ApplicationQualityReportSchema: () => ApplicationQualityReportSchema,
|
|
37
37
|
ApplicationQualityRoundSchema: () => ApplicationQualityRoundSchema,
|
|
38
38
|
ApplicationStateSchema: () => ApplicationStateSchema,
|
|
39
|
+
AttachmentInterpretationSchema: () => AttachmentInterpretationSchema,
|
|
39
40
|
AuditTypeSchema: () => AuditTypeSchema,
|
|
40
41
|
AutoFillMatchSchema: () => AutoFillMatchSchema,
|
|
41
42
|
AutoFillResultSchema: () => AutoFillResultSchema,
|
|
43
|
+
AuxiliaryFactSchema: () => AuxiliaryFactSchema,
|
|
42
44
|
BOAT_TYPES: () => BOAT_TYPES,
|
|
43
45
|
BindingAuthoritySchema: () => BindingAuthoritySchema,
|
|
44
46
|
BoatTypeSchema: () => BoatTypeSchema,
|
|
@@ -157,6 +159,8 @@ __export(index_exports, {
|
|
|
157
159
|
ProducerInfoSchema: () => ProducerInfoSchema,
|
|
158
160
|
ProfessionalLiabilityDeclarationsSchema: () => ProfessionalLiabilityDeclarationsSchema,
|
|
159
161
|
QUOTE_SECTION_TYPES: () => QUOTE_SECTION_TYPES,
|
|
162
|
+
QueryAttachmentKindSchema: () => QueryAttachmentKindSchema,
|
|
163
|
+
QueryAttachmentSchema: () => QueryAttachmentSchema,
|
|
160
164
|
QueryClassifyResultSchema: () => QueryClassifyResultSchema,
|
|
161
165
|
QueryIntentSchema: () => QueryIntentSchema,
|
|
162
166
|
QueryResultSchema: () => QueryResultSchema,
|
|
@@ -215,6 +219,7 @@ __export(index_exports, {
|
|
|
215
219
|
buildFormattingPrompt: () => buildFormattingPrompt,
|
|
216
220
|
buildIdentityPrompt: () => buildIdentityPrompt,
|
|
217
221
|
buildIntentPrompt: () => buildIntentPrompt,
|
|
222
|
+
buildInterpretAttachmentPrompt: () => buildInterpretAttachmentPrompt,
|
|
218
223
|
buildLookupFillPrompt: () => buildLookupFillPrompt,
|
|
219
224
|
buildQueryClassifyPrompt: () => buildQueryClassifyPrompt,
|
|
220
225
|
buildQuestionBatchPrompt: () => buildQuestionBatchPrompt,
|
|
@@ -1467,6 +1472,12 @@ var PremiumLineSchema = import_zod16.z.object({
|
|
|
1467
1472
|
line: import_zod16.z.string(),
|
|
1468
1473
|
amount: import_zod16.z.string()
|
|
1469
1474
|
});
|
|
1475
|
+
var AuxiliaryFactSchema = import_zod16.z.object({
|
|
1476
|
+
key: import_zod16.z.string(),
|
|
1477
|
+
value: import_zod16.z.string(),
|
|
1478
|
+
subject: import_zod16.z.string().optional(),
|
|
1479
|
+
context: import_zod16.z.string().optional()
|
|
1480
|
+
});
|
|
1470
1481
|
var BaseDocumentFields = {
|
|
1471
1482
|
id: import_zod16.z.string(),
|
|
1472
1483
|
carrier: import_zod16.z.string(),
|
|
@@ -1532,7 +1543,8 @@ var BaseDocumentFields = {
|
|
|
1532
1543
|
individualClaims: import_zod16.z.array(ClaimRecordSchema).optional(),
|
|
1533
1544
|
experienceMod: ExperienceModSchema.optional(),
|
|
1534
1545
|
cancellationNoticeDays: import_zod16.z.number().optional(),
|
|
1535
|
-
nonrenewalNoticeDays: import_zod16.z.number().optional()
|
|
1546
|
+
nonrenewalNoticeDays: import_zod16.z.number().optional(),
|
|
1547
|
+
supplementaryFacts: import_zod16.z.array(AuxiliaryFactSchema).optional()
|
|
1536
1548
|
};
|
|
1537
1549
|
var PolicyDocumentSchema = import_zod16.z.object({
|
|
1538
1550
|
...BaseDocumentFields,
|
|
@@ -2195,6 +2207,7 @@ function assembleDocument(documentId, documentType, memory) {
|
|
|
2195
2207
|
...sanitizeNulls(coverages ?? {}),
|
|
2196
2208
|
...sanitizeNulls(premium ?? {}),
|
|
2197
2209
|
...sanitizeNulls(supplementary ?? {}),
|
|
2210
|
+
supplementaryFacts: supplementary?.auxiliaryFacts,
|
|
2198
2211
|
endorsements: endorsements?.endorsements,
|
|
2199
2212
|
exclusions: exclusions?.exclusions,
|
|
2200
2213
|
conditions: conditions?.conditions,
|
|
@@ -2547,6 +2560,43 @@ Total Cost: ${doc.totalCost}` : ""}`,
|
|
|
2547
2560
|
metadata: stringMetadata({ premium: doc.premium, documentType: doc.type })
|
|
2548
2561
|
});
|
|
2549
2562
|
}
|
|
2563
|
+
const supplementaryLines = [
|
|
2564
|
+
...doc.claimsContacts?.map((contact) => `Claims Contact: ${[
|
|
2565
|
+
contact.name,
|
|
2566
|
+
contact.phone,
|
|
2567
|
+
contact.email,
|
|
2568
|
+
contact.hours
|
|
2569
|
+
].filter(Boolean).join(" | ")}`) ?? [],
|
|
2570
|
+
...doc.regulatoryContacts?.map((contact) => `Regulatory Contact: ${[
|
|
2571
|
+
contact.name,
|
|
2572
|
+
contact.phone,
|
|
2573
|
+
contact.email
|
|
2574
|
+
].filter(Boolean).join(" | ")}`) ?? [],
|
|
2575
|
+
...doc.thirdPartyAdministrators?.map((contact) => `TPA: ${[
|
|
2576
|
+
contact.name,
|
|
2577
|
+
contact.phone,
|
|
2578
|
+
contact.email
|
|
2579
|
+
].filter(Boolean).join(" | ")}`) ?? [],
|
|
2580
|
+
...doc.supplementaryFacts?.map((fact) => [
|
|
2581
|
+
fact.subject ? `Subject: ${fact.subject}` : null,
|
|
2582
|
+
`${fact.key}: ${fact.value}`,
|
|
2583
|
+
fact.context ? `Context: ${fact.context}` : null
|
|
2584
|
+
].filter(Boolean).join(" | ")) ?? [],
|
|
2585
|
+
doc.cancellationNoticeDays != null ? `Cancellation Notice Days: ${doc.cancellationNoticeDays}` : null,
|
|
2586
|
+
doc.nonrenewalNoticeDays != null ? `Nonrenewal Notice Days: ${doc.nonrenewalNoticeDays}` : null
|
|
2587
|
+
].filter((line) => Boolean(line));
|
|
2588
|
+
if (supplementaryLines.length > 0) {
|
|
2589
|
+
chunks.push({
|
|
2590
|
+
id: `${docId}:supplementary:0`,
|
|
2591
|
+
documentId: docId,
|
|
2592
|
+
type: "supplementary",
|
|
2593
|
+
text: supplementaryLines.join("\n"),
|
|
2594
|
+
metadata: stringMetadata({
|
|
2595
|
+
documentType: doc.type,
|
|
2596
|
+
supplementaryFactCount: doc.supplementaryFacts?.length
|
|
2597
|
+
})
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2550
2600
|
return chunks;
|
|
2551
2601
|
}
|
|
2552
2602
|
|
|
@@ -2630,6 +2680,32 @@ function mergeArrayPayload(existing, incoming, arrayKey, keyFn) {
|
|
|
2630
2680
|
merged[arrayKey] = mergeUniqueObjects(existingItems, incomingItems, keyFn);
|
|
2631
2681
|
return merged;
|
|
2632
2682
|
}
|
|
2683
|
+
function mergeSupplementary(existing, incoming) {
|
|
2684
|
+
const merged = mergeShallowPreferPresent(existing, incoming);
|
|
2685
|
+
const mergeContactArray = (arrayKey) => {
|
|
2686
|
+
const existingItems = Array.isArray(existing[arrayKey]) ? existing[arrayKey] : [];
|
|
2687
|
+
const incomingItems = Array.isArray(incoming[arrayKey]) ? incoming[arrayKey] : [];
|
|
2688
|
+
merged[arrayKey] = mergeUniqueObjects(existingItems, incomingItems, (item) => [
|
|
2689
|
+
String(item.name ?? "").toLowerCase(),
|
|
2690
|
+
String(item.phone ?? "").toLowerCase(),
|
|
2691
|
+
String(item.email ?? "").toLowerCase(),
|
|
2692
|
+
String(item.address ?? "").toLowerCase(),
|
|
2693
|
+
String(item.type ?? "").toLowerCase()
|
|
2694
|
+
].join("|"));
|
|
2695
|
+
};
|
|
2696
|
+
mergeContactArray("regulatoryContacts");
|
|
2697
|
+
mergeContactArray("claimsContacts");
|
|
2698
|
+
mergeContactArray("thirdPartyAdministrators");
|
|
2699
|
+
const existingFacts = Array.isArray(existing.auxiliaryFacts) ? existing.auxiliaryFacts : [];
|
|
2700
|
+
const incomingFacts = Array.isArray(incoming.auxiliaryFacts) ? incoming.auxiliaryFacts : [];
|
|
2701
|
+
merged.auxiliaryFacts = mergeUniqueObjects(existingFacts, incomingFacts, (item) => [
|
|
2702
|
+
String(item.key ?? "").toLowerCase(),
|
|
2703
|
+
String(item.value ?? "").toLowerCase(),
|
|
2704
|
+
String(item.subject ?? "").toLowerCase(),
|
|
2705
|
+
String(item.context ?? "").toLowerCase()
|
|
2706
|
+
].join("|"));
|
|
2707
|
+
return merged;
|
|
2708
|
+
}
|
|
2633
2709
|
function mergeExtractorResult(extractorName, existing, incoming) {
|
|
2634
2710
|
if (!existing) return incoming;
|
|
2635
2711
|
if (!incoming) return existing;
|
|
@@ -2640,9 +2716,10 @@ function mergeExtractorResult(extractorName, existing, incoming) {
|
|
|
2640
2716
|
case "carrier_info":
|
|
2641
2717
|
case "named_insured":
|
|
2642
2718
|
case "loss_history":
|
|
2643
|
-
case "supplementary":
|
|
2644
2719
|
case "premium_breakdown":
|
|
2645
2720
|
return mergeShallowPreferPresent(current, next);
|
|
2721
|
+
case "supplementary":
|
|
2722
|
+
return mergeSupplementary(current, next);
|
|
2646
2723
|
case "coverage_limits":
|
|
2647
2724
|
return mergeCoverageLimits(current, next);
|
|
2648
2725
|
case "declarations":
|
|
@@ -4239,15 +4316,22 @@ var ContactSchema2 = import_zod33.z.object({
|
|
|
4239
4316
|
address: import_zod33.z.string().optional().describe("Mailing address"),
|
|
4240
4317
|
type: import_zod33.z.string().optional().describe("Contact type, e.g. 'State Department of Insurance'")
|
|
4241
4318
|
});
|
|
4319
|
+
var AuxiliaryFactSchema2 = import_zod33.z.object({
|
|
4320
|
+
key: import_zod33.z.string().describe("Normalized machine-readable fact key, e.g. 'policyholder_age' or 'insured_name'"),
|
|
4321
|
+
value: import_zod33.z.string().describe("Concrete extracted fact value"),
|
|
4322
|
+
subject: import_zod33.z.string().optional().describe("Person, entity, vehicle, property, or schedule item this fact belongs to"),
|
|
4323
|
+
context: import_zod33.z.string().optional().describe("Short disambiguating context, such as 'Driver Schedule' or 'Named Insured'")
|
|
4324
|
+
});
|
|
4242
4325
|
var SupplementarySchema = import_zod33.z.object({
|
|
4243
4326
|
regulatoryContacts: import_zod33.z.array(ContactSchema2).optional().describe("Regulatory body contacts (state department of insurance, ombudsman)"),
|
|
4244
4327
|
claimsContacts: import_zod33.z.array(ContactSchema2).optional().describe("Claims reporting contacts and instructions"),
|
|
4245
4328
|
thirdPartyAdministrators: import_zod33.z.array(ContactSchema2).optional().describe("Third-party administrators for claims handling"),
|
|
4246
4329
|
cancellationNoticeDays: import_zod33.z.number().optional().describe("Required notice period for cancellation in days"),
|
|
4247
|
-
nonrenewalNoticeDays: import_zod33.z.number().optional().describe("Required notice period for nonrenewal in days")
|
|
4330
|
+
nonrenewalNoticeDays: import_zod33.z.number().optional().describe("Required notice period for nonrenewal in days"),
|
|
4331
|
+
auxiliaryFacts: import_zod33.z.array(AuxiliaryFactSchema2).optional().describe("Additional retrieval-only facts that do not fit the strict primary schema")
|
|
4248
4332
|
});
|
|
4249
4333
|
function buildSupplementaryPrompt() {
|
|
4250
|
-
return `You are an expert insurance document analyst. Extract supplementary
|
|
4334
|
+
return `You are an expert insurance document analyst. Extract supplementary, retrieval-only information from this document.
|
|
4251
4335
|
|
|
4252
4336
|
Focus on:
|
|
4253
4337
|
- Regulatory contacts: state department of insurance, regulatory bodies, ombudsman offices \u2014 with phone, email, address
|
|
@@ -4257,9 +4341,19 @@ Focus on:
|
|
|
4257
4341
|
- Nonrenewal notice period in days
|
|
4258
4342
|
- Complaint filing procedures and contacts
|
|
4259
4343
|
- Governing law or jurisdiction provisions
|
|
4344
|
+
- Additional policy-specific facts that are useful for memory and retrieval even if they do not belong in the strict primary schema
|
|
4260
4345
|
|
|
4261
4346
|
Look for regulatory notices, complaint contact sections, claims reporting instructions, and cancellation/nonrenewal provisions throughout the document.
|
|
4262
4347
|
|
|
4348
|
+
For auxiliaryFacts:
|
|
4349
|
+
- Capture concrete, policy-specific facts as structured key/value pairs.
|
|
4350
|
+
- Prioritize facts that agents may need later but that are often omitted from strict schemas: policyholder names, insured person names, driver names, ages, dates of birth, marital status, garaging information, lienholders, household members, vehicle assignments, schedule row details, and other discrete identifiers.
|
|
4351
|
+
- Use short normalized keys like "policyholder_name", "policyholder_age", "insured_name", "driver_age", "driver_date_of_birth", "garaging_zip", "vehicle_principal_driver".
|
|
4352
|
+
- Use subject when the fact belongs to a specific person, vehicle, property, or scheduled item.
|
|
4353
|
+
- Do not invent facts.
|
|
4354
|
+
- Do not include vague boilerplate or generic form language.
|
|
4355
|
+
- Do not repeat large narrative excerpts; keep facts atomic.
|
|
4356
|
+
|
|
4263
4357
|
Return JSON only.`;
|
|
4264
4358
|
}
|
|
4265
4359
|
|
|
@@ -4986,6 +5080,28 @@ function createExtractor(config) {
|
|
|
4986
5080
|
mergeMemoryResult(result.name, result.data, memory);
|
|
4987
5081
|
}
|
|
4988
5082
|
}
|
|
5083
|
+
const supplementaryExtractor = getExtractor("supplementary");
|
|
5084
|
+
if (supplementaryExtractor) {
|
|
5085
|
+
onProgress?.("Extracting supplementary retrieval facts...");
|
|
5086
|
+
try {
|
|
5087
|
+
const supplementaryResult = await runExtractor({
|
|
5088
|
+
name: "supplementary",
|
|
5089
|
+
prompt: supplementaryExtractor.buildPrompt(),
|
|
5090
|
+
schema: supplementaryExtractor.schema,
|
|
5091
|
+
pdfBase64,
|
|
5092
|
+
startPage: 1,
|
|
5093
|
+
endPage: pageCount,
|
|
5094
|
+
generateObject,
|
|
5095
|
+
convertPdfToImages,
|
|
5096
|
+
maxTokens: supplementaryExtractor.maxTokens ?? 4096,
|
|
5097
|
+
providerOptions
|
|
5098
|
+
});
|
|
5099
|
+
trackUsage(supplementaryResult.usage);
|
|
5100
|
+
mergeMemoryResult(supplementaryResult.name, supplementaryResult.data, memory);
|
|
5101
|
+
} catch (error) {
|
|
5102
|
+
await log?.(`Supplementary extractor failed: ${error}`);
|
|
5103
|
+
}
|
|
5104
|
+
}
|
|
4989
5105
|
await pipelineCtx.save("extract", {
|
|
4990
5106
|
id,
|
|
4991
5107
|
pageCount,
|
|
@@ -6575,7 +6691,7 @@ Respond with the explanation text only \u2014 no JSON, no field ID, no extra for
|
|
|
6575
6691
|
}
|
|
6576
6692
|
|
|
6577
6693
|
// src/prompts/query/classify.ts
|
|
6578
|
-
function buildQueryClassifyPrompt(question, conversationContext) {
|
|
6694
|
+
function buildQueryClassifyPrompt(question, conversationContext, attachmentContext) {
|
|
6579
6695
|
return `You are a query classifier for an insurance document intelligence system.
|
|
6580
6696
|
|
|
6581
6697
|
Analyze the user's question and produce a structured classification.
|
|
@@ -6585,6 +6701,9 @@ ${question}
|
|
|
6585
6701
|
${conversationContext ? `
|
|
6586
6702
|
CONVERSATION CONTEXT:
|
|
6587
6703
|
${conversationContext}` : ""}
|
|
6704
|
+
${attachmentContext ? `
|
|
6705
|
+
ATTACHMENT CONTEXT:
|
|
6706
|
+
${attachmentContext}` : ""}
|
|
6588
6707
|
|
|
6589
6708
|
INSTRUCTIONS:
|
|
6590
6709
|
|
|
@@ -6605,6 +6724,7 @@ INSTRUCTIONS:
|
|
|
6605
6724
|
- requiresDocumentLookup: true if a specific document needs to be fetched by ID/number/carrier
|
|
6606
6725
|
- requiresChunkSearch: true if semantic search over document chunks is needed
|
|
6607
6726
|
- requiresConversationHistory: true if the question references prior conversation
|
|
6727
|
+
- If the user's attachment already contains critical facts, still request chunk/document lookup when policy or quote details should be cross-checked against stored records
|
|
6608
6728
|
|
|
6609
6729
|
CHUNK TYPES (for chunkTypes filter):
|
|
6610
6730
|
carrier_info, named_insured, coverage, endorsement, exclusion, condition, section, declaration, loss_history, premium, supplementary
|
|
@@ -6646,6 +6766,16 @@ var QueryIntentSchema = import_zod35.z.enum([
|
|
|
6646
6766
|
"claims_inquiry",
|
|
6647
6767
|
"general_knowledge"
|
|
6648
6768
|
]);
|
|
6769
|
+
var QueryAttachmentKindSchema = import_zod35.z.enum(["image", "pdf", "text"]);
|
|
6770
|
+
var QueryAttachmentSchema = import_zod35.z.object({
|
|
6771
|
+
id: import_zod35.z.string().optional().describe("Optional stable attachment ID from the caller"),
|
|
6772
|
+
kind: QueryAttachmentKindSchema,
|
|
6773
|
+
name: import_zod35.z.string().optional().describe("Original filename or user-facing label"),
|
|
6774
|
+
mimeType: import_zod35.z.string().optional().describe("MIME type such as image/jpeg or application/pdf"),
|
|
6775
|
+
base64: import_zod35.z.string().optional().describe("Base64-encoded file content for image/pdf attachments"),
|
|
6776
|
+
text: import_zod35.z.string().optional().describe("Plain-text attachment content when available"),
|
|
6777
|
+
description: import_zod35.z.string().optional().describe("Caller-provided description of the attachment")
|
|
6778
|
+
});
|
|
6649
6779
|
var SubQuestionSchema = import_zod35.z.object({
|
|
6650
6780
|
question: import_zod35.z.string().describe("Atomic sub-question to retrieve and answer independently"),
|
|
6651
6781
|
intent: QueryIntentSchema,
|
|
@@ -6666,14 +6796,21 @@ var QueryClassifyResultSchema = import_zod35.z.object({
|
|
|
6666
6796
|
requiresConversationHistory: import_zod35.z.boolean().describe("Whether conversation history is relevant")
|
|
6667
6797
|
});
|
|
6668
6798
|
var EvidenceItemSchema = import_zod35.z.object({
|
|
6669
|
-
source: import_zod35.z.enum(["chunk", "document", "conversation"]),
|
|
6799
|
+
source: import_zod35.z.enum(["chunk", "document", "conversation", "attachment"]),
|
|
6670
6800
|
chunkId: import_zod35.z.string().optional(),
|
|
6671
6801
|
documentId: import_zod35.z.string().optional(),
|
|
6672
6802
|
turnId: import_zod35.z.string().optional(),
|
|
6803
|
+
attachmentId: import_zod35.z.string().optional(),
|
|
6673
6804
|
text: import_zod35.z.string().describe("Text excerpt from the source"),
|
|
6674
6805
|
relevance: import_zod35.z.number().min(0).max(1),
|
|
6675
6806
|
metadata: import_zod35.z.array(import_zod35.z.object({ key: import_zod35.z.string(), value: import_zod35.z.string() })).optional()
|
|
6676
6807
|
});
|
|
6808
|
+
var AttachmentInterpretationSchema = import_zod35.z.object({
|
|
6809
|
+
summary: import_zod35.z.string().describe("Concise summary of what the attachment shows or contains"),
|
|
6810
|
+
extractedFacts: import_zod35.z.array(import_zod35.z.string()).describe("Specific observable or document facts grounded in the attachment"),
|
|
6811
|
+
recommendedFocus: import_zod35.z.array(import_zod35.z.string()).describe("Important details to incorporate when answering follow-up questions"),
|
|
6812
|
+
confidence: import_zod35.z.number().min(0).max(1)
|
|
6813
|
+
});
|
|
6677
6814
|
var RetrievalResultSchema = import_zod35.z.object({
|
|
6678
6815
|
subQuestion: import_zod35.z.string(),
|
|
6679
6816
|
evidence: import_zod35.z.array(EvidenceItemSchema)
|
|
@@ -6994,7 +7131,7 @@ async function verify(originalQuestion, subAnswers, allEvidence, config) {
|
|
|
6994
7131
|
|
|
6995
7132
|
// src/query/quality.ts
|
|
6996
7133
|
function sourceIdForEvidence(evidence) {
|
|
6997
|
-
return evidence.chunkId ?? evidence.documentId ?? evidence.turnId;
|
|
7134
|
+
return evidence.chunkId ?? evidence.documentId ?? evidence.turnId ?? evidence.attachmentId;
|
|
6998
7135
|
}
|
|
6999
7136
|
function citationSourceId(citation) {
|
|
7000
7137
|
return citation.chunkId || citation.documentId;
|
|
@@ -7098,6 +7235,152 @@ function buildQueryReviewReport(params) {
|
|
|
7098
7235
|
};
|
|
7099
7236
|
}
|
|
7100
7237
|
|
|
7238
|
+
// src/prompts/query/interpret-attachment.ts
|
|
7239
|
+
function buildInterpretAttachmentPrompt(question, attachment) {
|
|
7240
|
+
const attachmentLabel = attachment.name ?? attachment.id ?? "attachment";
|
|
7241
|
+
const descriptor = [
|
|
7242
|
+
`Attachment: ${attachmentLabel}`,
|
|
7243
|
+
`Kind: ${attachment.kind}`,
|
|
7244
|
+
attachment.mimeType ? `MIME type: ${attachment.mimeType}` : null,
|
|
7245
|
+
attachment.description ? `Caller description: ${attachment.description}` : null
|
|
7246
|
+
].filter(Boolean).join("\n");
|
|
7247
|
+
return `You are interpreting a user-supplied attachment for an insurance-support question.
|
|
7248
|
+
|
|
7249
|
+
USER QUESTION:
|
|
7250
|
+
${question}
|
|
7251
|
+
|
|
7252
|
+
ATTACHMENT METADATA:
|
|
7253
|
+
${descriptor}
|
|
7254
|
+
|
|
7255
|
+
${attachment.kind === "text" && attachment.text ? `ATTACHMENT TEXT:
|
|
7256
|
+
${attachment.text}
|
|
7257
|
+
` : "The attachment content is provided separately as a file or image input.\n"}
|
|
7258
|
+
INSTRUCTIONS:
|
|
7259
|
+
1. Describe what the attachment appears to show or contain in a concise summary.
|
|
7260
|
+
2. Extract concrete facts that may matter when answering the user's question.
|
|
7261
|
+
3. Note the most important details to carry forward into follow-up questions.
|
|
7262
|
+
4. If the attachment is a document, identify the key business or insurance details visible.
|
|
7263
|
+
5. If the attachment is a photo of damage or a real-world issue, describe the observable issue without guessing beyond what is visible.
|
|
7264
|
+
6. Do not invent unreadable text. If something is unclear, say so in the summary or extracted facts.
|
|
7265
|
+
|
|
7266
|
+
Respond with the structured interpretation.`;
|
|
7267
|
+
}
|
|
7268
|
+
|
|
7269
|
+
// src/query/multimodal.ts
|
|
7270
|
+
function attachmentSourceId(attachment, index) {
|
|
7271
|
+
return attachment.id ?? `attachment-${index + 1}`;
|
|
7272
|
+
}
|
|
7273
|
+
function buildAttachmentProviderOptions(attachment, providerOptions) {
|
|
7274
|
+
const merged = {
|
|
7275
|
+
...providerOptions,
|
|
7276
|
+
attachments: [
|
|
7277
|
+
{
|
|
7278
|
+
kind: attachment.kind,
|
|
7279
|
+
name: attachment.name,
|
|
7280
|
+
mimeType: attachment.mimeType,
|
|
7281
|
+
base64: attachment.base64,
|
|
7282
|
+
text: attachment.text,
|
|
7283
|
+
description: attachment.description
|
|
7284
|
+
}
|
|
7285
|
+
]
|
|
7286
|
+
};
|
|
7287
|
+
if (attachment.kind === "pdf" && attachment.base64) {
|
|
7288
|
+
merged.pdfBase64 = attachment.base64;
|
|
7289
|
+
}
|
|
7290
|
+
if (attachment.kind === "image" && attachment.base64) {
|
|
7291
|
+
merged.images = [
|
|
7292
|
+
{
|
|
7293
|
+
imageBase64: attachment.base64,
|
|
7294
|
+
mimeType: attachment.mimeType ?? "image/jpeg"
|
|
7295
|
+
}
|
|
7296
|
+
];
|
|
7297
|
+
}
|
|
7298
|
+
return merged;
|
|
7299
|
+
}
|
|
7300
|
+
function buildAttachmentEvidenceText(attachment, interpretation) {
|
|
7301
|
+
const lines = [
|
|
7302
|
+
`Attachment kind: ${attachment.kind}`,
|
|
7303
|
+
attachment.name ? `Attachment name: ${attachment.name}` : null,
|
|
7304
|
+
attachment.mimeType ? `MIME type: ${attachment.mimeType}` : null,
|
|
7305
|
+
attachment.description ? `Caller description: ${attachment.description}` : null,
|
|
7306
|
+
`Summary: ${interpretation.summary}`,
|
|
7307
|
+
interpretation.extractedFacts.length > 0 ? `Extracted facts:
|
|
7308
|
+
${interpretation.extractedFacts.map((fact) => `- ${fact}`).join("\n")}` : null,
|
|
7309
|
+
interpretation.recommendedFocus.length > 0 ? `Important follow-up details:
|
|
7310
|
+
${interpretation.recommendedFocus.map((item) => `- ${item}`).join("\n")}` : null,
|
|
7311
|
+
attachment.kind === "text" && attachment.text ? `Original text:
|
|
7312
|
+
${attachment.text}` : null
|
|
7313
|
+
];
|
|
7314
|
+
return lines.filter(Boolean).join("\n");
|
|
7315
|
+
}
|
|
7316
|
+
async function interpretAttachments(params) {
|
|
7317
|
+
const { attachments = [], question, generateObject, providerOptions, log, onUsage } = params;
|
|
7318
|
+
if (attachments.length === 0) {
|
|
7319
|
+
return { evidence: [] };
|
|
7320
|
+
}
|
|
7321
|
+
const evidence = [];
|
|
7322
|
+
for (const [index, attachment] of attachments.entries()) {
|
|
7323
|
+
const id = attachmentSourceId(attachment, index);
|
|
7324
|
+
if (attachment.kind === "text" && attachment.text) {
|
|
7325
|
+
const textEvidence = buildAttachmentEvidenceText(attachment, {
|
|
7326
|
+
summary: attachment.description ?? "User supplied text context.",
|
|
7327
|
+
extractedFacts: [attachment.text],
|
|
7328
|
+
recommendedFocus: [],
|
|
7329
|
+
confidence: 1
|
|
7330
|
+
});
|
|
7331
|
+
evidence.push({
|
|
7332
|
+
source: "attachment",
|
|
7333
|
+
attachmentId: id,
|
|
7334
|
+
chunkId: id,
|
|
7335
|
+
documentId: id,
|
|
7336
|
+
text: textEvidence,
|
|
7337
|
+
relevance: 0.95,
|
|
7338
|
+
metadata: [
|
|
7339
|
+
{ key: "kind", value: attachment.kind },
|
|
7340
|
+
...attachment.name ? [{ key: "name", value: attachment.name }] : []
|
|
7341
|
+
]
|
|
7342
|
+
});
|
|
7343
|
+
continue;
|
|
7344
|
+
}
|
|
7345
|
+
const prompt = buildInterpretAttachmentPrompt(question, attachment);
|
|
7346
|
+
const { object, usage } = await safeGenerateObject(
|
|
7347
|
+
generateObject,
|
|
7348
|
+
{
|
|
7349
|
+
prompt,
|
|
7350
|
+
schema: AttachmentInterpretationSchema,
|
|
7351
|
+
maxTokens: 2048,
|
|
7352
|
+
providerOptions: buildAttachmentProviderOptions(attachment, providerOptions)
|
|
7353
|
+
},
|
|
7354
|
+
{
|
|
7355
|
+
fallback: {
|
|
7356
|
+
summary: attachment.description ?? `User supplied ${attachment.kind} attachment.`,
|
|
7357
|
+
extractedFacts: [],
|
|
7358
|
+
recommendedFocus: [],
|
|
7359
|
+
confidence: 0.2
|
|
7360
|
+
},
|
|
7361
|
+
log,
|
|
7362
|
+
onError: (error, attempt) => log?.(`Attachment interpretation attempt ${attempt + 1} failed for "${attachment.name ?? id}": ${error}`)
|
|
7363
|
+
}
|
|
7364
|
+
);
|
|
7365
|
+
onUsage?.(usage);
|
|
7366
|
+
evidence.push({
|
|
7367
|
+
source: "attachment",
|
|
7368
|
+
attachmentId: id,
|
|
7369
|
+
chunkId: id,
|
|
7370
|
+
documentId: id,
|
|
7371
|
+
text: buildAttachmentEvidenceText(attachment, object),
|
|
7372
|
+
relevance: Math.max(0.7, object.confidence),
|
|
7373
|
+
metadata: [
|
|
7374
|
+
{ key: "kind", value: attachment.kind },
|
|
7375
|
+
...attachment.name ? [{ key: "name", value: attachment.name }] : []
|
|
7376
|
+
]
|
|
7377
|
+
});
|
|
7378
|
+
}
|
|
7379
|
+
const contextSummary = evidence.map((item, index) => `Attachment ${index + 1}:
|
|
7380
|
+
${item.text}`).join("\n\n");
|
|
7381
|
+
return { evidence, contextSummary };
|
|
7382
|
+
}
|
|
7383
|
+
|
|
7101
7384
|
// src/query/coordinator.ts
|
|
7102
7385
|
function createQueryAgent(config) {
|
|
7103
7386
|
const {
|
|
@@ -7125,13 +7408,23 @@ function createQueryAgent(config) {
|
|
|
7125
7408
|
}
|
|
7126
7409
|
async function query(input) {
|
|
7127
7410
|
totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
7128
|
-
const { question, conversationId, context } = input;
|
|
7411
|
+
const { question, conversationId, context, attachments } = input;
|
|
7129
7412
|
const pipelineCtx = createPipelineContext({
|
|
7130
7413
|
id: `query-${Date.now()}`
|
|
7131
7414
|
});
|
|
7415
|
+
onProgress?.("Interpreting attachments...");
|
|
7416
|
+
const { evidence: attachmentEvidence, contextSummary: attachmentContext } = await interpretAttachments({
|
|
7417
|
+
attachments,
|
|
7418
|
+
question,
|
|
7419
|
+
generateObject,
|
|
7420
|
+
providerOptions,
|
|
7421
|
+
log,
|
|
7422
|
+
onUsage: trackUsage
|
|
7423
|
+
});
|
|
7424
|
+
await pipelineCtx.save("attachments", { attachmentEvidence });
|
|
7132
7425
|
onProgress?.("Classifying query...");
|
|
7133
|
-
const classification = await classify(question, conversationId);
|
|
7134
|
-
await pipelineCtx.save("classify", { classification });
|
|
7426
|
+
const classification = await classify(question, conversationId, attachmentContext);
|
|
7427
|
+
await pipelineCtx.save("classify", { classification, attachmentEvidence });
|
|
7135
7428
|
onProgress?.(`Retrieving evidence for ${classification.subQuestions.length} sub-question(s)...`);
|
|
7136
7429
|
const retrieverConfig = {
|
|
7137
7430
|
documentStore,
|
|
@@ -7144,8 +7437,8 @@ function createQueryAgent(config) {
|
|
|
7144
7437
|
(sq) => limit(() => retrieve(sq, conversationId, retrieverConfig))
|
|
7145
7438
|
)
|
|
7146
7439
|
);
|
|
7147
|
-
const allEvidence = retrievalResults.flatMap((r) => r.evidence);
|
|
7148
|
-
await pipelineCtx.save("retrieve", { classification, evidence: allEvidence });
|
|
7440
|
+
const allEvidence = [...attachmentEvidence, ...retrievalResults.flatMap((r) => r.evidence)];
|
|
7441
|
+
await pipelineCtx.save("retrieve", { classification, attachmentEvidence, evidence: allEvidence });
|
|
7149
7442
|
onProgress?.("Reasoning over evidence...");
|
|
7150
7443
|
const reasonerConfig = { generateObject, providerOptions };
|
|
7151
7444
|
const reasonResults = await Promise.allSettled(
|
|
@@ -7154,7 +7447,7 @@ function createQueryAgent(config) {
|
|
|
7154
7447
|
const { subAnswer, usage } = await reason(
|
|
7155
7448
|
sq.question,
|
|
7156
7449
|
sq.intent,
|
|
7157
|
-
retrievalResults[i].evidence,
|
|
7450
|
+
[...attachmentEvidence, ...retrievalResults[i].evidence],
|
|
7158
7451
|
reasonerConfig
|
|
7159
7452
|
);
|
|
7160
7453
|
trackUsage(usage);
|
|
@@ -7178,7 +7471,7 @@ function createQueryAgent(config) {
|
|
|
7178
7471
|
});
|
|
7179
7472
|
}
|
|
7180
7473
|
}
|
|
7181
|
-
await pipelineCtx.save("reason", { classification, evidence: allEvidence, subAnswers });
|
|
7474
|
+
await pipelineCtx.save("reason", { classification, attachmentEvidence, evidence: allEvidence, subAnswers });
|
|
7182
7475
|
onProgress?.("Verifying answer grounding...");
|
|
7183
7476
|
const verifierConfig = { generateObject, providerOptions };
|
|
7184
7477
|
const verifyRounds = [];
|
|
@@ -7226,7 +7519,7 @@ function createQueryAgent(config) {
|
|
|
7226
7519
|
const { subAnswer, usage: u } = await reason(
|
|
7227
7520
|
sq.question,
|
|
7228
7521
|
sq.intent,
|
|
7229
|
-
retryRetrievals[i].evidence,
|
|
7522
|
+
[...attachmentEvidence, ...retryRetrievals[i].evidence],
|
|
7230
7523
|
reasonerConfig
|
|
7231
7524
|
);
|
|
7232
7525
|
trackUsage(u);
|
|
@@ -7261,6 +7554,7 @@ function createQueryAgent(config) {
|
|
|
7261
7554
|
});
|
|
7262
7555
|
await pipelineCtx.save("review", {
|
|
7263
7556
|
classification,
|
|
7557
|
+
attachmentEvidence,
|
|
7264
7558
|
evidence: allEvidence,
|
|
7265
7559
|
subAnswers,
|
|
7266
7560
|
reviewReport
|
|
@@ -7293,7 +7587,7 @@ function createQueryAgent(config) {
|
|
|
7293
7587
|
}
|
|
7294
7588
|
return { ...queryResult, tokenUsage: totalUsage, reviewReport };
|
|
7295
7589
|
}
|
|
7296
|
-
async function classify(question, conversationId) {
|
|
7590
|
+
async function classify(question, conversationId, attachmentContext) {
|
|
7297
7591
|
let conversationContext;
|
|
7298
7592
|
if (conversationId) {
|
|
7299
7593
|
try {
|
|
@@ -7304,7 +7598,7 @@ function createQueryAgent(config) {
|
|
|
7304
7598
|
} catch {
|
|
7305
7599
|
}
|
|
7306
7600
|
}
|
|
7307
|
-
const prompt = buildQueryClassifyPrompt(question, conversationContext);
|
|
7601
|
+
const prompt = buildQueryClassifyPrompt(question, conversationContext, attachmentContext);
|
|
7308
7602
|
const { object, usage } = await safeGenerateObject(
|
|
7309
7603
|
generateObject,
|
|
7310
7604
|
{
|
|
@@ -7520,9 +7814,11 @@ var AGENT_TOOLS = [
|
|
|
7520
7814
|
ApplicationQualityReportSchema,
|
|
7521
7815
|
ApplicationQualityRoundSchema,
|
|
7522
7816
|
ApplicationStateSchema,
|
|
7817
|
+
AttachmentInterpretationSchema,
|
|
7523
7818
|
AuditTypeSchema,
|
|
7524
7819
|
AutoFillMatchSchema,
|
|
7525
7820
|
AutoFillResultSchema,
|
|
7821
|
+
AuxiliaryFactSchema,
|
|
7526
7822
|
BOAT_TYPES,
|
|
7527
7823
|
BindingAuthoritySchema,
|
|
7528
7824
|
BoatTypeSchema,
|
|
@@ -7641,6 +7937,8 @@ var AGENT_TOOLS = [
|
|
|
7641
7937
|
ProducerInfoSchema,
|
|
7642
7938
|
ProfessionalLiabilityDeclarationsSchema,
|
|
7643
7939
|
QUOTE_SECTION_TYPES,
|
|
7940
|
+
QueryAttachmentKindSchema,
|
|
7941
|
+
QueryAttachmentSchema,
|
|
7644
7942
|
QueryClassifyResultSchema,
|
|
7645
7943
|
QueryIntentSchema,
|
|
7646
7944
|
QueryResultSchema,
|
|
@@ -7699,6 +7997,7 @@ var AGENT_TOOLS = [
|
|
|
7699
7997
|
buildFormattingPrompt,
|
|
7700
7998
|
buildIdentityPrompt,
|
|
7701
7999
|
buildIntentPrompt,
|
|
8000
|
+
buildInterpretAttachmentPrompt,
|
|
7702
8001
|
buildLookupFillPrompt,
|
|
7703
8002
|
buildQueryClassifyPrompt,
|
|
7704
8003
|
buildQuestionBatchPrompt,
|