@nathanvale/chatline 0.0.1 → 0.0.2-next.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/CHANGELOG.md +12 -0
- package/dist/bin/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/cli/commands/clean.d.ts +0 -17
- package/dist/cli/commands/clean.d.ts.map +0 -1
- package/dist/cli/commands/clean.js +0 -142
- package/dist/cli/commands/clean.js.map +0 -1
- package/dist/cli/commands/doctor.d.ts +0 -17
- package/dist/cli/commands/doctor.d.ts.map +0 -1
- package/dist/cli/commands/doctor.js +0 -202
- package/dist/cli/commands/doctor.js.map +0 -1
- package/dist/cli/commands/enrich-ai.d.ts +0 -17
- package/dist/cli/commands/enrich-ai.d.ts.map +0 -1
- package/dist/cli/commands/enrich-ai.js +0 -371
- package/dist/cli/commands/enrich-ai.js.map +0 -1
- package/dist/cli/commands/index.d.ts +0 -16
- package/dist/cli/commands/index.d.ts.map +0 -1
- package/dist/cli/commands/index.js +0 -16
- package/dist/cli/commands/index.js.map +0 -1
- package/dist/cli/commands/ingest-csv.d.ts +0 -17
- package/dist/cli/commands/ingest-csv.d.ts.map +0 -1
- package/dist/cli/commands/ingest-csv.js +0 -138
- package/dist/cli/commands/ingest-csv.js.map +0 -1
- package/dist/cli/commands/ingest-db.d.ts +0 -17
- package/dist/cli/commands/ingest-db.d.ts.map +0 -1
- package/dist/cli/commands/ingest-db.js +0 -159
- package/dist/cli/commands/ingest-db.js.map +0 -1
- package/dist/cli/commands/init.d.ts +0 -17
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/init.js +0 -110
- package/dist/cli/commands/init.js.map +0 -1
- package/dist/cli/commands/normalize-link.d.ts +0 -16
- package/dist/cli/commands/normalize-link.d.ts.map +0 -1
- package/dist/cli/commands/normalize-link.js +0 -144
- package/dist/cli/commands/normalize-link.js.map +0 -1
- package/dist/cli/commands/render-markdown.d.ts +0 -17
- package/dist/cli/commands/render-markdown.d.ts.map +0 -1
- package/dist/cli/commands/render-markdown.js +0 -218
- package/dist/cli/commands/render-markdown.js.map +0 -1
- package/dist/cli/commands/stats.d.ts +0 -17
- package/dist/cli/commands/stats.d.ts.map +0 -1
- package/dist/cli/commands/stats.js +0 -175
- package/dist/cli/commands/stats.js.map +0 -1
- package/dist/cli/commands/validate.d.ts +0 -17
- package/dist/cli/commands/validate.d.ts.map +0 -1
- package/dist/cli/commands/validate.js +0 -152
- package/dist/cli/commands/validate.js.map +0 -1
- package/dist/cli/index.d.ts +0 -13
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -121
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/types.d.ts +0 -93
- package/dist/cli/types.d.ts.map +0 -1
- package/dist/cli/types.js +0 -7
- package/dist/cli/types.js.map +0 -1
- package/dist/cli/utils.d.ts +0 -29
- package/dist/cli/utils.d.ts.map +0 -1
- package/dist/cli/utils.js +0 -53
- package/dist/cli/utils.js.map +0 -1
- package/dist/cli.d.ts +0 -9
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -1805
- package/dist/config/generator.d.ts +0 -90
- package/dist/config/generator.d.ts.map +0 -1
- package/dist/config/generator.js +0 -320
- package/dist/config/generator.js.map +0 -1
- package/dist/config/loader.d.ts +0 -107
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js +0 -251
- package/dist/config/loader.js.map +0 -1
- package/dist/config/schema.d.ts +0 -107
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -169
- package/dist/config/schema.js.map +0 -1
- package/dist/enrich/audio-transcription.d.ts +0 -77
- package/dist/enrich/audio-transcription.d.ts.map +0 -1
- package/dist/enrich/audio-transcription.js +0 -370
- package/dist/enrich/audio-transcription.js.map +0 -1
- package/dist/enrich/checkpoint.d.ts +0 -137
- package/dist/enrich/checkpoint.d.ts.map +0 -1
- package/dist/enrich/checkpoint.js +0 -205
- package/dist/enrich/checkpoint.js.map +0 -1
- package/dist/enrich/idempotency.d.ts +0 -90
- package/dist/enrich/idempotency.d.ts.map +0 -1
- package/dist/enrich/idempotency.js +0 -188
- package/dist/enrich/idempotency.js.map +0 -1
- package/dist/enrich/image-analysis.d.ts +0 -62
- package/dist/enrich/image-analysis.d.ts.map +0 -1
- package/dist/enrich/image-analysis.js +0 -264
- package/dist/enrich/image-analysis.js.map +0 -1
- package/dist/enrich/index.d.ts +0 -60
- package/dist/enrich/index.d.ts.map +0 -1
- package/dist/enrich/index.js +0 -74
- package/dist/enrich/index.js.map +0 -1
- package/dist/enrich/link-enrichment.d.ts +0 -37
- package/dist/enrich/link-enrichment.d.ts.map +0 -1
- package/dist/enrich/link-enrichment.js +0 -202
- package/dist/enrich/link-enrichment.js.map +0 -1
- package/dist/enrich/pdf-video-handling.d.ts +0 -49
- package/dist/enrich/pdf-video-handling.d.ts.map +0 -1
- package/dist/enrich/pdf-video-handling.js +0 -325
- package/dist/enrich/pdf-video-handling.js.map +0 -1
- package/dist/enrich/progress-tracker.d.ts +0 -120
- package/dist/enrich/progress-tracker.d.ts.map +0 -1
- package/dist/enrich/progress-tracker.js +0 -220
- package/dist/enrich/progress-tracker.js.map +0 -1
- package/dist/enrich/providers/firecrawl.d.ts +0 -18
- package/dist/enrich/providers/firecrawl.d.ts.map +0 -1
- package/dist/enrich/providers/firecrawl.js +0 -48
- package/dist/enrich/providers/firecrawl.js.map +0 -1
- package/dist/enrich/providers/generic.d.ts +0 -16
- package/dist/enrich/providers/generic.d.ts.map +0 -1
- package/dist/enrich/providers/generic.js +0 -36
- package/dist/enrich/providers/generic.js.map +0 -1
- package/dist/enrich/providers/index.d.ts +0 -14
- package/dist/enrich/providers/index.d.ts.map +0 -1
- package/dist/enrich/providers/index.js +0 -13
- package/dist/enrich/providers/index.js.map +0 -1
- package/dist/enrich/providers/instagram.d.ts +0 -16
- package/dist/enrich/providers/instagram.d.ts.map +0 -1
- package/dist/enrich/providers/instagram.js +0 -43
- package/dist/enrich/providers/instagram.js.map +0 -1
- package/dist/enrich/providers/spotify.d.ts +0 -16
- package/dist/enrich/providers/spotify.d.ts.map +0 -1
- package/dist/enrich/providers/spotify.js +0 -45
- package/dist/enrich/providers/spotify.js.map +0 -1
- package/dist/enrich/providers/twitter.d.ts +0 -16
- package/dist/enrich/providers/twitter.d.ts.map +0 -1
- package/dist/enrich/providers/twitter.js +0 -43
- package/dist/enrich/providers/twitter.js.map +0 -1
- package/dist/enrich/providers/types.d.ts +0 -47
- package/dist/enrich/providers/types.d.ts.map +0 -1
- package/dist/enrich/providers/types.js +0 -15
- package/dist/enrich/providers/types.js.map +0 -1
- package/dist/enrich/providers/youtube.d.ts +0 -16
- package/dist/enrich/providers/youtube.d.ts.map +0 -1
- package/dist/enrich/providers/youtube.js +0 -43
- package/dist/enrich/providers/youtube.js.map +0 -1
- package/dist/enrich/rate-limiting.d.ts +0 -118
- package/dist/enrich/rate-limiting.d.ts.map +0 -1
- package/dist/enrich/rate-limiting.js +0 -258
- package/dist/enrich/rate-limiting.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/ingest/dedup-merge.d.ts +0 -82
- package/dist/ingest/dedup-merge.d.ts.map +0 -1
- package/dist/ingest/dedup-merge.js +0 -262
- package/dist/ingest/dedup-merge.js.map +0 -1
- package/dist/ingest/ingest-csv.d.ts +0 -62
- package/dist/ingest/ingest-csv.d.ts.map +0 -1
- package/dist/ingest/ingest-csv.js +0 -300
- package/dist/ingest/ingest-csv.js.map +0 -1
- package/dist/ingest/ingest-db.d.ts +0 -64
- package/dist/ingest/ingest-db.d.ts.map +0 -1
- package/dist/ingest/ingest-db.js +0 -172
- package/dist/ingest/ingest-db.js.map +0 -1
- package/dist/ingest/link-replies-and-tapbacks.d.ts +0 -53
- package/dist/ingest/link-replies-and-tapbacks.d.ts.map +0 -1
- package/dist/ingest/link-replies-and-tapbacks.js +0 -381
- package/dist/ingest/link-replies-and-tapbacks.js.map +0 -1
- package/dist/normalize/date-converters.d.ts +0 -45
- package/dist/normalize/date-converters.d.ts.map +0 -1
- package/dist/normalize/date-converters.js +0 -166
- package/dist/normalize/date-converters.js.map +0 -1
- package/dist/normalize/path-validator.d.ts +0 -65
- package/dist/normalize/path-validator.d.ts.map +0 -1
- package/dist/normalize/path-validator.js +0 -221
- package/dist/normalize/path-validator.js.map +0 -1
- package/dist/normalize/validate-normalized.d.ts +0 -45
- package/dist/normalize/validate-normalized.d.ts.map +0 -1
- package/dist/normalize/validate-normalized.js +0 -144
- package/dist/normalize/validate-normalized.js.map +0 -1
- package/dist/render/embeds-blockquotes.d.ts +0 -84
- package/dist/render/embeds-blockquotes.d.ts.map +0 -1
- package/dist/render/embeds-blockquotes.js +0 -204
- package/dist/render/embeds-blockquotes.js.map +0 -1
- package/dist/render/grouping.d.ts +0 -78
- package/dist/render/grouping.d.ts.map +0 -1
- package/dist/render/grouping.js +0 -134
- package/dist/render/grouping.js.map +0 -1
- package/dist/render/index.d.ts +0 -47
- package/dist/render/index.d.ts.map +0 -1
- package/dist/render/index.js +0 -245
- package/dist/render/index.js.map +0 -1
- package/dist/render/reply-rendering.d.ts +0 -88
- package/dist/render/reply-rendering.d.ts.map +0 -1
- package/dist/render/reply-rendering.js +0 -196
- package/dist/render/reply-rendering.js.map +0 -1
- package/dist/schema/message.d.ts +0 -125
- package/dist/schema/message.d.ts.map +0 -1
- package/dist/schema/message.js +0 -331
- package/dist/schema/message.js.map +0 -1
- package/dist/utils/delta-detection.d.ts +0 -107
- package/dist/utils/delta-detection.d.ts.map +0 -1
- package/dist/utils/delta-detection.js +0 -199
- package/dist/utils/delta-detection.js.map +0 -1
- package/dist/utils/enrichment-merge.d.ts +0 -135
- package/dist/utils/enrichment-merge.d.ts.map +0 -1
- package/dist/utils/enrichment-merge.js +0 -280
- package/dist/utils/enrichment-merge.js.map +0 -1
- package/dist/utils/human.d.ts +0 -15
- package/dist/utils/human.d.ts.map +0 -1
- package/dist/utils/human.js +0 -27
- package/dist/utils/human.js.map +0 -1
- package/dist/utils/incremental-state.d.ts +0 -133
- package/dist/utils/incremental-state.d.ts.map +0 -1
- package/dist/utils/incremental-state.js +0 -237
- package/dist/utils/incremental-state.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -40
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -176
- package/dist/utils/logger.js.map +0 -1
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image Analysis Module (ENRICH--T01)
|
|
3
|
-
*
|
|
4
|
-
* Implements image analysis with preview generation:
|
|
5
|
-
* - AC01: HEIC → JPG conversion with ≥90% quality
|
|
6
|
-
* - AC02: TIFF → JPG conversion
|
|
7
|
-
* - AC03: Preview caching by filename (generate once, skip if exists)
|
|
8
|
-
* - AC04: Gemini Vision API with structured prompt
|
|
9
|
-
* - AC05: Parse response into enrichment array with kind='image_analysis'
|
|
10
|
-
* - AC06: Store provenance (provider, model, version, timestamp)
|
|
11
|
-
*
|
|
12
|
-
* Architecture:
|
|
13
|
-
* - convertToJpgPreview: Handles format conversion with caching
|
|
14
|
-
* - analyzeImageWithGemini: Calls Gemini Vision API with structured prompt
|
|
15
|
-
* - analyzeImage: Main entry point, handles single message enrichment
|
|
16
|
-
* - analyzeImages: Batch processing wrapper
|
|
17
|
-
*
|
|
18
|
-
* Error Handling:
|
|
19
|
-
* - Non-fatal errors are logged and original message is returned
|
|
20
|
-
* - Preview generation failures don't block Gemini analysis
|
|
21
|
-
* - Pipeline never crashes on enrichment errors
|
|
22
|
-
*/
|
|
23
|
-
import { access, writeFile } from 'node:fs/promises';
|
|
24
|
-
import path from 'node:path';
|
|
25
|
-
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
26
|
-
import sharp from 'sharp';
|
|
27
|
-
import { createLogger } from '#utils/logger';
|
|
28
|
-
const logger = createLogger('enrich:image-analysis');
|
|
29
|
-
/**
|
|
30
|
-
* Structured prompt for Gemini Vision analysis
|
|
31
|
-
* First classifies image type, then provides detailed + short descriptions
|
|
32
|
-
*/
|
|
33
|
-
const GEMINI_VISION_PROMPT = `You are an expert at analyzing images. First, classify the image type:
|
|
34
|
-
- photo (real-world scene, landscape, portrait, food, outdoor, indoor, etc.)
|
|
35
|
-
- screenshot (UI, application, website, text content)
|
|
36
|
-
- diagram (chart, graph, whiteboard, flowchart)
|
|
37
|
-
- artwork (drawing, illustration, design, painting)
|
|
38
|
-
- other (specify what it is)
|
|
39
|
-
|
|
40
|
-
Then provide:
|
|
41
|
-
1. visionSummary: A detailed 2-3 sentence description of the image content, context, and notable details
|
|
42
|
-
2. shortDescription: A concise 1-sentence summary for quick scanning
|
|
43
|
-
|
|
44
|
-
Format your response exactly as:
|
|
45
|
-
IMAGE_TYPE: [classification]
|
|
46
|
-
visionSummary: [detailed description here]
|
|
47
|
-
shortDescription: [one sentence summary]`;
|
|
48
|
-
/**
|
|
49
|
-
* AC03: Convert image to JPG preview
|
|
50
|
-
* - Input: path to HEIC, TIFF, or other format
|
|
51
|
-
* - Output: path to cached JPG preview
|
|
52
|
-
* - Behavior: Generate once, cache by filename, skip if exists
|
|
53
|
-
*/
|
|
54
|
-
export async function convertToJpgPreview(inputPath, cacheDir, quality = 90) {
|
|
55
|
-
if (!inputPath || !cacheDir) {
|
|
56
|
-
throw new Error('inputPath and cacheDir are required');
|
|
57
|
-
}
|
|
58
|
-
const filename = path.parse(path.basename(inputPath)).name;
|
|
59
|
-
const previewFilename = `preview-${filename}.jpg`;
|
|
60
|
-
const previewPath = path.join(cacheDir, previewFilename);
|
|
61
|
-
try {
|
|
62
|
-
// AC03: Check if preview already exists
|
|
63
|
-
await access(previewPath);
|
|
64
|
-
// If we get here, file exists - return cached path
|
|
65
|
-
logger.debug(`Preview cache hit: ${previewPath}`);
|
|
66
|
-
return previewPath;
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// File doesn't exist, proceed with conversion
|
|
70
|
-
}
|
|
71
|
-
// AC01/AC02: Convert to JPG with quality preservation
|
|
72
|
-
try {
|
|
73
|
-
const imageBuffer = await sharp(inputPath)
|
|
74
|
-
.toFormat('jpeg')
|
|
75
|
-
.jpeg({ quality, progressive: true })
|
|
76
|
-
.toBuffer();
|
|
77
|
-
// Write to cache
|
|
78
|
-
await writeFile(previewPath, imageBuffer);
|
|
79
|
-
logger.info(`Generated preview: ${previewPath}`, { inputPath, quality });
|
|
80
|
-
return previewPath;
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
logger.error(`Failed to convert image: ${inputPath}`, { error });
|
|
84
|
-
throw new Error(`Failed to convert image to JPG: ${inputPath}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* AC04: Call Gemini Vision with structured prompt
|
|
89
|
-
* AC05: Parse response into enrichment array
|
|
90
|
-
* AC06: Store provenance
|
|
91
|
-
*/
|
|
92
|
-
export async function analyzeImageWithGemini(imagePath, config) {
|
|
93
|
-
const apiKey = config.geminiApiKey;
|
|
94
|
-
const modelName = config.geminiModel || 'gemini-1.5-pro';
|
|
95
|
-
if (!apiKey) {
|
|
96
|
-
throw new Error('GEMINI_API_KEY is required for image analysis');
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
// AC04: Create Gemini client and call with structured prompt
|
|
100
|
-
const genAI = new GoogleGenerativeAI(apiKey);
|
|
101
|
-
const model = genAI.getGenerativeModel({ model: modelName });
|
|
102
|
-
// Read image file
|
|
103
|
-
const imageData = await sharp(imagePath).toFormat('jpeg').toBuffer();
|
|
104
|
-
const base64Image = imageData.toString('base64');
|
|
105
|
-
// AC04: Call Gemini with structured prompt
|
|
106
|
-
// Call Gemini with image
|
|
107
|
-
const response = await model.generateContent([
|
|
108
|
-
{
|
|
109
|
-
inlineData: {
|
|
110
|
-
mimeType: 'image/jpeg',
|
|
111
|
-
data: base64Image,
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
GEMINI_VISION_PROMPT,
|
|
115
|
-
]);
|
|
116
|
-
const responseText = response.response.text();
|
|
117
|
-
logger.debug(`Gemini response received: ${responseText.substring(0, 150)}...`);
|
|
118
|
-
// AC05: Parse response
|
|
119
|
-
const visionSummaryMatch = responseText.match(/visionSummary:\s*(.+?)(?=\n(?:shortDescription|$))/is);
|
|
120
|
-
const shortDescriptionMatch = responseText.match(/shortDescription:\s*(.+?)(?=\n|$)/is);
|
|
121
|
-
if (!visionSummaryMatch || !shortDescriptionMatch) {
|
|
122
|
-
logger.warn(`Failed to parse Gemini response for ${imagePath}`);
|
|
123
|
-
}
|
|
124
|
-
const visionSummary = visionSummaryMatch?.[1]?.trim() || 'Image analysis unavailable';
|
|
125
|
-
const shortDescription = shortDescriptionMatch?.[1]?.trim() || 'Image';
|
|
126
|
-
// AC06: Create enrichment entry with full provenance
|
|
127
|
-
const version = new Date().toISOString().split('T')[0] || 'unknown';
|
|
128
|
-
const enrichment = {
|
|
129
|
-
kind: 'image_analysis',
|
|
130
|
-
provider: 'gemini',
|
|
131
|
-
model: modelName,
|
|
132
|
-
version, // YYYY-MM-DD
|
|
133
|
-
createdAt: new Date().toISOString(),
|
|
134
|
-
visionSummary,
|
|
135
|
-
shortDescription,
|
|
136
|
-
};
|
|
137
|
-
logger.info(`Image analysis complete for ${imagePath}`, {
|
|
138
|
-
kind: enrichment.kind,
|
|
139
|
-
});
|
|
140
|
-
return enrichment;
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
logger.error(`Gemini API error for ${imagePath}`, { error });
|
|
144
|
-
throw error;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Main entry point - analyze image media message and enrich it
|
|
149
|
-
* Handles all ACs (AC01-AC06) through helper functions
|
|
150
|
-
*
|
|
151
|
-
* Responsibilities:
|
|
152
|
-
* 1. Check if media is image type (skip non-images)
|
|
153
|
-
* 2. Convert HEIC/TIFF to JPG preview (AC01-AC03)
|
|
154
|
-
* 3. Call Gemini Vision API (AC04)
|
|
155
|
-
* 4. Parse response (AC05)
|
|
156
|
-
* 5. Add enrichment with provenance (AC06)
|
|
157
|
-
*/
|
|
158
|
-
export async function analyzeImage(message, config) {
|
|
159
|
-
// Skip if not enabled
|
|
160
|
-
if (!config.enableVisionAnalysis) {
|
|
161
|
-
logger.debug('Vision analysis disabled in config');
|
|
162
|
-
return message;
|
|
163
|
-
}
|
|
164
|
-
// Skip if not a media message
|
|
165
|
-
if (message.messageKind !== 'media' || !message.media) {
|
|
166
|
-
return message;
|
|
167
|
-
}
|
|
168
|
-
// Skip if media is not an image
|
|
169
|
-
if (message.media.mediaKind !== 'image') {
|
|
170
|
-
logger.debug('Skipping non-image media', {
|
|
171
|
-
mediaKind: message.media.mediaKind,
|
|
172
|
-
});
|
|
173
|
-
return message;
|
|
174
|
-
}
|
|
175
|
-
// Skip if path is missing
|
|
176
|
-
if (!message.media.path) {
|
|
177
|
-
logger.warn('Skipping image with missing path', {
|
|
178
|
-
filename: message.media.filename,
|
|
179
|
-
});
|
|
180
|
-
return message;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
const imageCacheDir = config.imageCacheDir || '/tmp/image-cache';
|
|
184
|
-
// AC01-AC03: Generate preview (cached)
|
|
185
|
-
let _previewPath;
|
|
186
|
-
try {
|
|
187
|
-
_previewPath = await convertToJpgPreview(message.media.path, imageCacheDir, 90);
|
|
188
|
-
}
|
|
189
|
-
catch (err) {
|
|
190
|
-
logger.warn('Failed to create preview - continuing with Gemini analysis', {
|
|
191
|
-
filename: message.media.filename,
|
|
192
|
-
error: err instanceof Error ? err.message : String(err),
|
|
193
|
-
});
|
|
194
|
-
// Continue with Gemini analysis even if preview fails
|
|
195
|
-
}
|
|
196
|
-
// AC04-AC06: Analyze with Gemini
|
|
197
|
-
const enrichment = await analyzeImageWithGemini(message.media.path, config);
|
|
198
|
-
// Update message with enrichment
|
|
199
|
-
const updatedMedia = {
|
|
200
|
-
...message.media,
|
|
201
|
-
enrichment: [...(message.media.enrichment || []), enrichment],
|
|
202
|
-
};
|
|
203
|
-
logger.info('Image enriched', {
|
|
204
|
-
filename: message.media.filename,
|
|
205
|
-
guid: message.guid,
|
|
206
|
-
});
|
|
207
|
-
return {
|
|
208
|
-
...message,
|
|
209
|
-
media: updatedMedia,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
logger.error('Error analyzing image', {
|
|
214
|
-
filename: message.media?.filename,
|
|
215
|
-
guid: message.guid,
|
|
216
|
-
error: error instanceof Error ? error.message : String(error),
|
|
217
|
-
});
|
|
218
|
-
// Don't crash pipeline - return original message
|
|
219
|
-
return message;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Batch analyze multiple messages
|
|
224
|
-
* Useful for enrichment stage that processes arrays of messages
|
|
225
|
-
* Each message is processed independently; errors don't stop the batch
|
|
226
|
-
*/
|
|
227
|
-
export async function analyzeImages(messages, config) {
|
|
228
|
-
const results = [];
|
|
229
|
-
let successCount = 0;
|
|
230
|
-
let skipCount = 0;
|
|
231
|
-
let errorCount = 0;
|
|
232
|
-
for (const message of messages) {
|
|
233
|
-
try {
|
|
234
|
-
const analyzed = await analyzeImage(message, config);
|
|
235
|
-
// Track if enrichment was added
|
|
236
|
-
if (analyzed.media?.enrichment &&
|
|
237
|
-
analyzed.media.enrichment.length >
|
|
238
|
-
(message.media?.enrichment?.length || 0)) {
|
|
239
|
-
successCount++;
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
skipCount++;
|
|
243
|
-
}
|
|
244
|
-
results.push(analyzed);
|
|
245
|
-
}
|
|
246
|
-
catch (err) {
|
|
247
|
-
errorCount++;
|
|
248
|
-
logger.error('Failed to analyze message', {
|
|
249
|
-
guid: message.guid,
|
|
250
|
-
error: err instanceof Error ? err.message : String(err),
|
|
251
|
-
});
|
|
252
|
-
// Keep original message if analysis fails
|
|
253
|
-
results.push(message);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
logger.info('Batch image analysis complete', {
|
|
257
|
-
successCount,
|
|
258
|
-
skipCount,
|
|
259
|
-
errorCount,
|
|
260
|
-
total: messages.length,
|
|
261
|
-
});
|
|
262
|
-
return results;
|
|
263
|
-
}
|
|
264
|
-
//# sourceMappingURL=image-analysis.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"image-analysis.js","sourceRoot":"","sources":["../../src/enrich/image-analysis.ts"],"names":[],"mappings":"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"}
|
package/dist/enrich/index.d.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enrich Module - Main Entry Point
|
|
3
|
-
*
|
|
4
|
-
* Provides consolidated access to all enrichment operations with idempotency
|
|
5
|
-
* support built-in.
|
|
6
|
-
*
|
|
7
|
-
* Implements:
|
|
8
|
-
* - ENRICH--T01: Image analysis with preview generation
|
|
9
|
-
* - ENRICH--T02: Audio transcription
|
|
10
|
-
* - ENRICH--T03: PDF/video handling
|
|
11
|
-
* - ENRICH--T04: Link enrichment (pending)
|
|
12
|
-
* - ENRICH--T05: Enrichment idempotency
|
|
13
|
-
*/
|
|
14
|
-
export * from './audio-transcription';
|
|
15
|
-
export * from './idempotency';
|
|
16
|
-
export * from './image-analysis';
|
|
17
|
-
export * from './link-enrichment';
|
|
18
|
-
export * from './pdf-video-handling';
|
|
19
|
-
import type { MediaEnrichment, Message } from '#schema/message';
|
|
20
|
-
type EnrichmentConfig = {
|
|
21
|
-
enableVisionAnalysis?: boolean;
|
|
22
|
-
enableLinkAnalysis?: boolean;
|
|
23
|
-
geminiApiKey?: string;
|
|
24
|
-
geminiModel?: string;
|
|
25
|
-
imageCacheDir?: string;
|
|
26
|
-
firecrawlApiKey?: string;
|
|
27
|
-
rateLimitDelay?: number;
|
|
28
|
-
forceRefresh?: boolean;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Apply enrichment to a message with idempotency checks
|
|
32
|
-
*
|
|
33
|
-
* This is the main entry point for enrichment operations that ensures
|
|
34
|
-
* idempotency: re-running enrichment won't create duplicate entries.
|
|
35
|
-
*
|
|
36
|
-
* @param message - Message to enrich
|
|
37
|
-
* @param enrichment - New enrichment to add
|
|
38
|
-
* @param config - Enrichment configuration including forceRefresh flag
|
|
39
|
-
* @returns Updated message with enrichment applied idempotently
|
|
40
|
-
*/
|
|
41
|
-
export declare function applyEnrichmentIdempotent(message: Message, enrichment: MediaEnrichment, config?: EnrichmentConfig): Message;
|
|
42
|
-
/**
|
|
43
|
-
* Check if a message should skip enrichment of a specific kind
|
|
44
|
-
*
|
|
45
|
-
* @param message - Message to check
|
|
46
|
-
* @param kind - Enrichment kind to check
|
|
47
|
-
* @param forceRefresh - If true, always proceed (don't skip)
|
|
48
|
-
* @returns true if should skip, false if should proceed
|
|
49
|
-
*/
|
|
50
|
-
export declare function shouldSkipEnrichmentForKind(message: Message, kind: MediaEnrichment['kind'], forceRefresh?: boolean): boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Ensure message enrichment is deduplicated
|
|
53
|
-
*
|
|
54
|
-
* Useful for cleanup after multiple enrichment operations.
|
|
55
|
-
*
|
|
56
|
-
* @param message - Message to deduplicate
|
|
57
|
-
* @returns Message with deduplicated enrichment
|
|
58
|
-
*/
|
|
59
|
-
export declare function ensureDeduplicatedEnrichment(message: Message): Message;
|
|
60
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,uBAAuB,CAAA;AACrC,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AAEpC,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAO/D,KAAK,gBAAgB,GAAG;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,eAAe,EAC3B,MAAM,GAAE,gBAAqB,GAC3B,OAAO,CAIT;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAC1C,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,YAAY,UAAQ,GAClB,OAAO,CAMT;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAmBtE"}
|
package/dist/enrich/index.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enrich Module - Main Entry Point
|
|
3
|
-
*
|
|
4
|
-
* Provides consolidated access to all enrichment operations with idempotency
|
|
5
|
-
* support built-in.
|
|
6
|
-
*
|
|
7
|
-
* Implements:
|
|
8
|
-
* - ENRICH--T01: Image analysis with preview generation
|
|
9
|
-
* - ENRICH--T02: Audio transcription
|
|
10
|
-
* - ENRICH--T03: PDF/video handling
|
|
11
|
-
* - ENRICH--T04: Link enrichment (pending)
|
|
12
|
-
* - ENRICH--T05: Enrichment idempotency
|
|
13
|
-
*/
|
|
14
|
-
export * from './audio-transcription';
|
|
15
|
-
export * from './idempotency';
|
|
16
|
-
export * from './image-analysis';
|
|
17
|
-
export * from './link-enrichment';
|
|
18
|
-
export * from './pdf-video-handling';
|
|
19
|
-
import { addEnrichmentIdempotent, deduplicateEnrichmentByKind, shouldSkipEnrichment, } from './idempotency';
|
|
20
|
-
/**
|
|
21
|
-
* Apply enrichment to a message with idempotency checks
|
|
22
|
-
*
|
|
23
|
-
* This is the main entry point for enrichment operations that ensures
|
|
24
|
-
* idempotency: re-running enrichment won't create duplicate entries.
|
|
25
|
-
*
|
|
26
|
-
* @param message - Message to enrich
|
|
27
|
-
* @param enrichment - New enrichment to add
|
|
28
|
-
* @param config - Enrichment configuration including forceRefresh flag
|
|
29
|
-
* @returns Updated message with enrichment applied idempotently
|
|
30
|
-
*/
|
|
31
|
-
export function applyEnrichmentIdempotent(message, enrichment, config = {}) {
|
|
32
|
-
const { forceRefresh = false } = config;
|
|
33
|
-
return addEnrichmentIdempotent(message, enrichment, { forceRefresh });
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Check if a message should skip enrichment of a specific kind
|
|
37
|
-
*
|
|
38
|
-
* @param message - Message to check
|
|
39
|
-
* @param kind - Enrichment kind to check
|
|
40
|
-
* @param forceRefresh - If true, always proceed (don't skip)
|
|
41
|
-
* @returns true if should skip, false if should proceed
|
|
42
|
-
*/
|
|
43
|
-
export function shouldSkipEnrichmentForKind(message, kind, forceRefresh = false) {
|
|
44
|
-
if (forceRefresh) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
return shouldSkipEnrichment(message, kind);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Ensure message enrichment is deduplicated
|
|
51
|
-
*
|
|
52
|
-
* Useful for cleanup after multiple enrichment operations.
|
|
53
|
-
*
|
|
54
|
-
* @param message - Message to deduplicate
|
|
55
|
-
* @returns Message with deduplicated enrichment
|
|
56
|
-
*/
|
|
57
|
-
export function ensureDeduplicatedEnrichment(message) {
|
|
58
|
-
if (!message.media?.enrichment) {
|
|
59
|
-
return message;
|
|
60
|
-
}
|
|
61
|
-
const deduped = deduplicateEnrichmentByKind(message.media.enrichment);
|
|
62
|
-
if (deduped.length === message.media.enrichment.length) {
|
|
63
|
-
// No change needed
|
|
64
|
-
return message;
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
...message,
|
|
68
|
-
media: {
|
|
69
|
-
...message.media,
|
|
70
|
-
enrichment: deduped,
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
//# sourceMappingURL=index.js.map
|
package/dist/enrich/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,cAAc,uBAAuB,CAAA;AACrC,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AAGpC,OAAO,EACN,uBAAuB,EACvB,2BAA2B,EAC3B,oBAAoB,GACpB,MAAM,eAAe,CAAA;AAatB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CACxC,OAAgB,EAChB,UAA2B,EAC3B,SAA2B,EAAE;IAE7B,MAAM,EAAE,YAAY,GAAG,KAAK,EAAE,GAAG,MAAM,CAAA;IAEvC,OAAO,uBAAuB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;AACtE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAC1C,OAAgB,EAChB,IAA6B,EAC7B,YAAY,GAAG,KAAK;IAEpB,IAAI,YAAY,EAAE,CAAC;QAClB,OAAO,KAAK,CAAA;IACb,CAAC;IAED,OAAO,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AAC3C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAgB;IAC5D,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC;QAChC,OAAO,OAAO,CAAA;IACf,CAAC;IAED,MAAM,OAAO,GAAG,2BAA2B,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IAErE,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACxD,mBAAmB;QACnB,OAAO,OAAO,CAAA;IACf,CAAC;IAED,OAAO;QACN,GAAG,OAAO;QACV,KAAK,EAAE;YACN,GAAG,OAAO,CAAC,KAAK;YAChB,UAAU,EAAE,OAAO;SACnB;KACD,CAAA;AACF,CAAC"}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Link Enrichment Module (ENRICH--T04)
|
|
3
|
-
*
|
|
4
|
-
* Implements link context extraction with cascading fallbacks:
|
|
5
|
-
* - AC01: Firecrawl as primary provider for generic link context
|
|
6
|
-
* - AC02: YouTube fallback with title/channel extraction
|
|
7
|
-
* - AC03: Spotify fallback with track/artist extraction
|
|
8
|
-
* - AC04: Social media fallbacks (Twitter/Instagram) using meta tags
|
|
9
|
-
* - AC05: Never crash - comprehensive error handling with fallbacks
|
|
10
|
-
* - AC06: Store enrichment with kind='link_context' and provenance
|
|
11
|
-
*
|
|
12
|
-
* Architecture:
|
|
13
|
-
* - Provider factory pattern with priority-based cascading
|
|
14
|
-
* - Firecrawl (priority 0) → Provider-specific (1-4) → Generic fallback (5)
|
|
15
|
-
* - URL detection regex for each provider type
|
|
16
|
-
* - Meta tag extraction for social media
|
|
17
|
-
* - Comprehensive error logging and statistics
|
|
18
|
-
*
|
|
19
|
-
* Error Handling:
|
|
20
|
-
* - Non-fatal errors logged and fall through to next provider
|
|
21
|
-
* - Pipeline never crashes - always return message
|
|
22
|
-
* - Rate limit detection with fallback triggering
|
|
23
|
-
*/
|
|
24
|
-
import type { Message } from '#schema/message';
|
|
25
|
-
import { type LinkEnrichmentConfig } from './providers/index.js';
|
|
26
|
-
export type { LinkContext, LinkEnrichmentConfig } from './providers/index.js';
|
|
27
|
-
/**
|
|
28
|
-
* Main entry point - enrich text message with link context
|
|
29
|
-
* AC01-AC06: Full implementation
|
|
30
|
-
*/
|
|
31
|
-
export declare function enrichLinkContext(message: Message, config: Partial<LinkEnrichmentConfig>): Promise<Message>;
|
|
32
|
-
/**
|
|
33
|
-
* Batch analyze multiple messages
|
|
34
|
-
* Tracks statistics and handles errors per message
|
|
35
|
-
*/
|
|
36
|
-
export declare function enrichLinksContext(messages: Message[], config: Partial<LinkEnrichmentConfig>): Promise<Message[]>;
|
|
37
|
-
//# sourceMappingURL=link-enrichment.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-enrichment.d.ts","sourceRoot":"","sources":["../../src/enrich/link-enrichment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAmB,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAG/D,OAAO,EAKN,KAAK,oBAAoB,EAKzB,MAAM,sBAAsB,CAAA;AAG7B,YAAY,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAkF7E;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACnC,OAAO,CAAC,OAAO,CAAC,CA4ElB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,OAAO,EAAE,EACnB,MAAM,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACnC,OAAO,CAAC,OAAO,EAAE,CAAC,CAmCpB"}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Link Enrichment Module (ENRICH--T04)
|
|
3
|
-
*
|
|
4
|
-
* Implements link context extraction with cascading fallbacks:
|
|
5
|
-
* - AC01: Firecrawl as primary provider for generic link context
|
|
6
|
-
* - AC02: YouTube fallback with title/channel extraction
|
|
7
|
-
* - AC03: Spotify fallback with track/artist extraction
|
|
8
|
-
* - AC04: Social media fallbacks (Twitter/Instagram) using meta tags
|
|
9
|
-
* - AC05: Never crash - comprehensive error handling with fallbacks
|
|
10
|
-
* - AC06: Store enrichment with kind='link_context' and provenance
|
|
11
|
-
*
|
|
12
|
-
* Architecture:
|
|
13
|
-
* - Provider factory pattern with priority-based cascading
|
|
14
|
-
* - Firecrawl (priority 0) → Provider-specific (1-4) → Generic fallback (5)
|
|
15
|
-
* - URL detection regex for each provider type
|
|
16
|
-
* - Meta tag extraction for social media
|
|
17
|
-
* - Comprehensive error logging and statistics
|
|
18
|
-
*
|
|
19
|
-
* Error Handling:
|
|
20
|
-
* - Non-fatal errors logged and fall through to next provider
|
|
21
|
-
* - Pipeline never crashes - always return message
|
|
22
|
-
* - Rate limit detection with fallback triggering
|
|
23
|
-
*/
|
|
24
|
-
import { createLogger } from '#utils/logger';
|
|
25
|
-
import { FirecrawlProvider, GenericProvider, InstagramProvider, SpotifyProvider, TwitterProvider, YouTubeProvider, } from './providers/index.js';
|
|
26
|
-
/**
|
|
27
|
-
* Logger for structured output
|
|
28
|
-
*/
|
|
29
|
-
const logger = createLogger('enrich:link-enrichment');
|
|
30
|
-
/**
|
|
31
|
-
* AC01-AC04: Provider factory with cascading fallback strategy
|
|
32
|
-
*/
|
|
33
|
-
function createProviders(config) {
|
|
34
|
-
const providers = [];
|
|
35
|
-
// AC01: Firecrawl primary
|
|
36
|
-
if (config.firecrawlApiKey) {
|
|
37
|
-
providers.push(new FirecrawlProvider(config.firecrawlApiKey));
|
|
38
|
-
}
|
|
39
|
-
// AC02-AC04: Add provider-specific handlers
|
|
40
|
-
providers.push(new YouTubeProvider());
|
|
41
|
-
providers.push(new SpotifyProvider());
|
|
42
|
-
providers.push(new TwitterProvider());
|
|
43
|
-
providers.push(new InstagramProvider());
|
|
44
|
-
// Generic fallback (always last)
|
|
45
|
-
providers.push(new GenericProvider());
|
|
46
|
-
// Sort by priority
|
|
47
|
-
return providers.sort((a, b) => a.priority - b.priority);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Extract URLs from message text
|
|
51
|
-
*/
|
|
52
|
-
function extractUrls(text) {
|
|
53
|
-
const urlRegex = /https?:\/\/[^\s)]+/g;
|
|
54
|
-
const matches = text.match(urlRegex) || [];
|
|
55
|
-
return matches;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* AC05: Cascading fallback with error handling
|
|
59
|
-
* Never crashes - always returns result or original message
|
|
60
|
-
*/
|
|
61
|
-
async function enrichUrl(url, config) {
|
|
62
|
-
const providers = createProviders(config);
|
|
63
|
-
const failedProviders = [];
|
|
64
|
-
// AC05: Try each provider in order until one succeeds
|
|
65
|
-
for (const provider of providers) {
|
|
66
|
-
try {
|
|
67
|
-
if (!provider.detect(url)) {
|
|
68
|
-
logger.debug(`Provider ${provider.name} skipped (not applicable for URL)`);
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
const context = await provider.extract(url);
|
|
72
|
-
logger.info(`Link enrichment succeeded with ${provider.name}`, { url });
|
|
73
|
-
const result = { ...context };
|
|
74
|
-
if (failedProviders.length > 0) {
|
|
75
|
-
result.failedProviders = failedProviders;
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
failedProviders.push(provider.name);
|
|
81
|
-
logger.warn(`Provider ${provider.name} failed for ${url}`, {
|
|
82
|
-
error: error instanceof Error ? error.message : String(error),
|
|
83
|
-
});
|
|
84
|
-
// Continue to next provider
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// AC05: All providers failed - return null but don't crash
|
|
88
|
-
logger.warn(`All providers failed for URL ${url}`, { failedProviders });
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Main entry point - enrich text message with link context
|
|
93
|
-
* AC01-AC06: Full implementation
|
|
94
|
-
*/
|
|
95
|
-
export async function enrichLinkContext(message, config) {
|
|
96
|
-
// Skip if not enabled
|
|
97
|
-
if (!config.enableLinkAnalysis) {
|
|
98
|
-
logger.debug('Link analysis disabled in config');
|
|
99
|
-
return message;
|
|
100
|
-
}
|
|
101
|
-
// Skip if not a text message
|
|
102
|
-
if (message.messageKind !== 'text' || !message.text) {
|
|
103
|
-
return message;
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
// Extract URLs from message text
|
|
107
|
-
const urls = extractUrls(message.text);
|
|
108
|
-
if (urls.length === 0) {
|
|
109
|
-
logger.debug('No URLs found in message', { guid: message.guid });
|
|
110
|
-
return message;
|
|
111
|
-
}
|
|
112
|
-
// Process first URL found (could extend to handle multiple)
|
|
113
|
-
const url = urls[0];
|
|
114
|
-
if (!url) {
|
|
115
|
-
logger.debug('URL array is empty', { guid: message.guid });
|
|
116
|
-
return message;
|
|
117
|
-
}
|
|
118
|
-
const context = await enrichUrl(url, config);
|
|
119
|
-
if (!context) {
|
|
120
|
-
logger.debug(`Failed to enrich link for ${url}`, { guid: message.guid });
|
|
121
|
-
return message;
|
|
122
|
-
}
|
|
123
|
-
// AC06: Create enrichment entry with full provenance
|
|
124
|
-
const version = new Date().toISOString().split('T')[0];
|
|
125
|
-
if (!version) {
|
|
126
|
-
throw new Error('Failed to generate version string');
|
|
127
|
-
}
|
|
128
|
-
const enrichment = {
|
|
129
|
-
kind: 'link_context',
|
|
130
|
-
provider: context.provider,
|
|
131
|
-
model: 'link-extractor',
|
|
132
|
-
version,
|
|
133
|
-
createdAt: new Date().toISOString(),
|
|
134
|
-
url: context.url,
|
|
135
|
-
};
|
|
136
|
-
if (context.title)
|
|
137
|
-
enrichment.title = context.title;
|
|
138
|
-
if (context.description)
|
|
139
|
-
enrichment.summary = context.description;
|
|
140
|
-
if (context.usedFallback)
|
|
141
|
-
enrichment.usedFallback = context.usedFallback;
|
|
142
|
-
logger.info('Link enriched', {
|
|
143
|
-
url,
|
|
144
|
-
provider: context.provider,
|
|
145
|
-
guid: message.guid,
|
|
146
|
-
});
|
|
147
|
-
// For text messages, store enrichment in a linkEnrichments array on the message
|
|
148
|
-
// This preserves the original message structure while adding the enrichment data
|
|
149
|
-
const existingEnrichments = message
|
|
150
|
-
.linkEnrichments || [];
|
|
151
|
-
return {
|
|
152
|
-
...message,
|
|
153
|
-
linkEnrichments: [...existingEnrichments, enrichment],
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
logger.error('Error enriching link', {
|
|
158
|
-
guid: message.guid,
|
|
159
|
-
error: error instanceof Error ? error.message : String(error),
|
|
160
|
-
});
|
|
161
|
-
// AC05: Never crash - return original message
|
|
162
|
-
return message;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Batch analyze multiple messages
|
|
167
|
-
* Tracks statistics and handles errors per message
|
|
168
|
-
*/
|
|
169
|
-
export async function enrichLinksContext(messages, config) {
|
|
170
|
-
const results = [];
|
|
171
|
-
let enrichedCount = 0;
|
|
172
|
-
const skippedCount = 0;
|
|
173
|
-
let failedCount = 0;
|
|
174
|
-
const providerStats = {};
|
|
175
|
-
for (const message of messages) {
|
|
176
|
-
try {
|
|
177
|
-
const enriched = await enrichLinkContext(message, config);
|
|
178
|
-
// Check if enrichment was added (would need to check media.enrichment in production)
|
|
179
|
-
// For now, count as processed
|
|
180
|
-
enrichedCount++;
|
|
181
|
-
results.push(enriched);
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
failedCount++;
|
|
185
|
-
logger.error('Failed to analyze message', {
|
|
186
|
-
guid: message.guid,
|
|
187
|
-
error: error instanceof Error ? error.message : String(error),
|
|
188
|
-
});
|
|
189
|
-
// Keep original message if enrichment fails
|
|
190
|
-
results.push(message);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
logger.info('Batch link enrichment complete', {
|
|
194
|
-
enrichedCount,
|
|
195
|
-
skippedCount,
|
|
196
|
-
failedCount,
|
|
197
|
-
total: messages.length,
|
|
198
|
-
providerStats,
|
|
199
|
-
});
|
|
200
|
-
return results;
|
|
201
|
-
}
|
|
202
|
-
//# sourceMappingURL=link-enrichment.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-enrichment.js","sourceRoot":"","sources":["../../src/enrich/link-enrichment.ts"],"names":[],"mappings":"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"}
|