doc2vec 1.0.2 → 1.0.4

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.
Files changed (27) hide show
  1. package/dist/doc2vec.js +1 -1
  2. package/doc2vec.ts +1 -1
  3. package/package.json +1 -1
  4. package/Dockerfile.old +0 -19
  5. package/config copy.yaml +0 -182
  6. package/config-github.yaml +0 -15
  7. package/config.yaml.new +0 -253
  8. package/config.yaml.ok +0 -164
  9. package/config.yaml.solo +0 -251
  10. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/lib/acm-cdk-stack.js +0 -20
  11. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/lib/route53-cdk-stack.js +0 -33
  12. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/lib/waf-cdk-stack.js +0 -66
  13. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/test/s3-cloudfront-cdk.test.js +0 -16
  14. package/dist/solo-reference-architectures/Gloo_Edge/Cloudfront_NLB/data/s3-cloudfront-cdk/lib/acm-cdk-stack.js +0 -21
  15. package/dist/solo-reference-architectures/Gloo_Edge/Cloudfront_NLB/data/s3-cloudfront-cdk/lib/s3-cloudfront-cdk-stack.js +0 -95
  16. package/dist/solo-reference-architectures/Gloo_Edge/Cloudfront_NLB/data/s3-cloudfront-cdk/test/s3-cloudfront-cdk.test.js +0 -16
  17. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/bin/acm-cdk.js +0 -55
  18. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/lib/acm-cdk-stack.js +0 -20
  19. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/lib/route53-cdk-stack.js +0 -33
  20. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/test/s3-cloudfront-cdk.test.js +0 -16
  21. package/doc2vec.js +0 -1805
  22. package/doc2vec.ts.new +0 -1583
  23. package/doc2vec.ts.ok +0 -1730
  24. package/istio-issues.db +0 -0
  25. package/logger.js +0 -260
  26. package/tmp/README.md.old +0 -1
  27. package/tmp.db +0 -0
package/doc2vec.js DELETED
@@ -1,1805 +0,0 @@
1
- #!/usr/bin/env ts-node
2
- "use strict";
3
- var __assign = (this && this.__assign) || function () {
4
- __assign = Object.assign || function(t) {
5
- for (var s, i = 1, n = arguments.length; i < n; i++) {
6
- s = arguments[i];
7
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
8
- t[p] = s[p];
9
- }
10
- return t;
11
- };
12
- return __assign.apply(this, arguments);
13
- };
14
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
- return new (P || (P = Promise))(function (resolve, reject) {
17
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
- step((generator = generator.apply(thisArg, _arguments || [])).next());
21
- });
22
- };
23
- var __generator = (this && this.__generator) || function (thisArg, body) {
24
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
25
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
26
- function verb(n) { return function (v) { return step([n, v]); }; }
27
- function step(op) {
28
- if (f) throw new TypeError("Generator is already executing.");
29
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
30
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
31
- if (y = 0, t) op = [op[0] & 2, t.value];
32
- switch (op[0]) {
33
- case 0: case 1: t = op; break;
34
- case 4: _.label++; return { value: op[1], done: false };
35
- case 5: _.label++; y = op[1]; op = [0]; continue;
36
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
37
- default:
38
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
39
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
40
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
41
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
42
- if (t[2]) _.ops.pop();
43
- _.trys.pop(); continue;
44
- }
45
- op = body.call(thisArg, _);
46
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
47
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
48
- }
49
- };
50
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
51
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
52
- if (ar || !(i in from)) {
53
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
54
- ar[i] = from[i];
55
- }
56
- }
57
- return to.concat(ar || Array.prototype.slice.call(from));
58
- };
59
- Object.defineProperty(exports, "__esModule", { value: true });
60
- var readability_1 = require("@mozilla/readability");
61
- var axios_1 = require("axios");
62
- var cheerio_1 = require("cheerio");
63
- var crypto_1 = require("crypto");
64
- var jsdom_1 = require("jsdom");
65
- var puppeteer_1 = require("puppeteer");
66
- var sanitize_html_1 = require("sanitize-html");
67
- var turndown_1 = require("turndown");
68
- var yaml = require("js-yaml");
69
- var fs = require("fs");
70
- var path = require("path");
71
- var better_sqlite3_1 = require("better-sqlite3");
72
- var openai_1 = require("openai");
73
- var dotenv = require("dotenv");
74
- var sqliteVec = require("sqlite-vec");
75
- var js_client_rest_1 = require("@qdrant/js-client-rest");
76
- var logger_1 = require("./logger");
77
- var GITHUB_TOKEN = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
78
- dotenv.config();
79
- var Doc2Vec = /** @class */ (function () {
80
- function Doc2Vec(configPath) {
81
- this.logger = new logger_1.Logger('Doc2Vec', {
82
- level: logger_1.LogLevel.DEBUG,
83
- useTimestamp: true,
84
- useColor: true,
85
- prettyPrint: true
86
- });
87
- this.logger.info('Initializing Doc2Vec');
88
- this.config = this.loadConfig(configPath);
89
- this.openai = new openai_1.OpenAI({ apiKey: process.env.OPENAI_API_KEY });
90
- this.turndownService = new turndown_1.default({
91
- codeBlockStyle: 'fenced',
92
- headingStyle: 'atx'
93
- });
94
- this.setupTurndownRules();
95
- }
96
- Doc2Vec.prototype.loadConfig = function (configPath) {
97
- try {
98
- var logger = this.logger.child('config');
99
- logger.info("Loading configuration from ".concat(configPath));
100
- var configFile = fs.readFileSync(configPath, 'utf8');
101
- var config = yaml.load(configFile);
102
- var typedConfig = config;
103
- logger.info("Configuration loaded successfully, found ".concat(typedConfig.sources.length, " sources"));
104
- return typedConfig;
105
- }
106
- catch (error) {
107
- this.logger.error("Failed to load or parse config file at ".concat(configPath, ":"), error);
108
- process.exit(1);
109
- }
110
- };
111
- Doc2Vec.prototype.setupTurndownRules = function () {
112
- var logger = this.logger.child('markdown');
113
- logger.debug('Setting up Turndown rules for markdown conversion');
114
- this.turndownService.addRule('codeBlocks', {
115
- filter: function (node) { return node.nodeName === 'PRE'; },
116
- replacement: function (content, node) {
117
- var _a;
118
- var htmlNode = node;
119
- var code = htmlNode.querySelector('code');
120
- var codeContent;
121
- if (code) {
122
- codeContent = code.textContent || '';
123
- }
124
- else {
125
- codeContent = htmlNode.textContent || '';
126
- }
127
- var lines = codeContent.split('\n');
128
- var minIndent = Infinity;
129
- for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
130
- var line = lines_1[_i];
131
- if (line.trim() === '')
132
- continue;
133
- var leadingWhitespace = ((_a = line.match(/^\s*/)) === null || _a === void 0 ? void 0 : _a[0]) || '';
134
- minIndent = Math.min(minIndent, leadingWhitespace.length);
135
- }
136
- var cleanedLines = lines.map(function (line) {
137
- return line.substring(minIndent);
138
- });
139
- var cleanContent = cleanedLines.join('\n');
140
- cleanContent = cleanContent.replace(/^\s+|\s+$/g, '');
141
- cleanContent = cleanContent.replace(/\n{2,}/g, '\n');
142
- return "\n```\n".concat(cleanContent, "\n```\n");
143
- }
144
- });
145
- this.turndownService.addRule('tableCell', {
146
- filter: ['th', 'td'],
147
- replacement: function (content, node) {
148
- var htmlNode = node;
149
- var cellContent = '';
150
- if (htmlNode.querySelector('p')) {
151
- cellContent = Array.from(htmlNode.querySelectorAll('p'))
152
- .map(function (p) { return p.textContent || ''; })
153
- .join(' ')
154
- .trim();
155
- }
156
- else {
157
- cellContent = content.trim();
158
- }
159
- return " ".concat(cellContent.replace(/\|/g, '\\|'), " |");
160
- }
161
- });
162
- this.turndownService.addRule('tableRow', {
163
- filter: 'tr',
164
- replacement: function (content, node) {
165
- var _a;
166
- var htmlNode = node;
167
- var cells = Array.from(htmlNode.cells);
168
- var isHeader = ((_a = htmlNode.parentNode) === null || _a === void 0 ? void 0 : _a.nodeName) === 'THEAD';
169
- var output = '|' + content.trimEnd();
170
- if (isHeader) {
171
- var separator = cells.map(function () { return '---'; }).join(' | ');
172
- output += '\n|' + separator + '|';
173
- }
174
- if (!isHeader || !htmlNode.nextElementSibling) {
175
- output += '\n';
176
- }
177
- return output;
178
- }
179
- });
180
- this.turndownService.addRule('table', {
181
- filter: 'table',
182
- replacement: function (content) {
183
- return '\n' + content.replace(/\n+/g, '\n').trim() + '\n';
184
- }
185
- });
186
- this.turndownService.addRule('preserveTableWhitespace', {
187
- filter: function (node) {
188
- var _a;
189
- return ((node.nodeName === 'TD' || node.nodeName === 'TH') &&
190
- (((_a = node.textContent) === null || _a === void 0 ? void 0 : _a.trim().length) === 0));
191
- },
192
- replacement: function () {
193
- return ' |';
194
- }
195
- });
196
- logger.debug('Turndown rules setup complete');
197
- };
198
- Doc2Vec.prototype.run = function () {
199
- return __awaiter(this, void 0, void 0, function () {
200
- var _i, _a, sourceConfig, sourceLogger;
201
- return __generator(this, function (_b) {
202
- switch (_b.label) {
203
- case 0:
204
- this.logger.section('PROCESSING SOURCES');
205
- _i = 0, _a = this.config.sources;
206
- _b.label = 1;
207
- case 1:
208
- if (!(_i < _a.length)) return [3 /*break*/, 9];
209
- sourceConfig = _a[_i];
210
- sourceLogger = this.logger.child("source:".concat(sourceConfig.product_name));
211
- sourceLogger.info("Processing ".concat(sourceConfig.type, " source for ").concat(sourceConfig.product_name, "@").concat(sourceConfig.version));
212
- if (!(sourceConfig.type === 'github')) return [3 /*break*/, 3];
213
- return [4 /*yield*/, this.processGithubRepo(sourceConfig, sourceLogger)];
214
- case 2:
215
- _b.sent();
216
- return [3 /*break*/, 8];
217
- case 3:
218
- if (!(sourceConfig.type === 'website')) return [3 /*break*/, 5];
219
- return [4 /*yield*/, this.processWebsite(sourceConfig, sourceLogger)];
220
- case 4:
221
- _b.sent();
222
- return [3 /*break*/, 8];
223
- case 5:
224
- if (!(sourceConfig.type === 'local_directory')) return [3 /*break*/, 7];
225
- return [4 /*yield*/, this.processLocalDirectory(sourceConfig, sourceLogger)];
226
- case 6:
227
- _b.sent();
228
- return [3 /*break*/, 8];
229
- case 7:
230
- sourceLogger.error("Unknown source type: ".concat(sourceConfig.type));
231
- _b.label = 8;
232
- case 8:
233
- _i++;
234
- return [3 /*break*/, 1];
235
- case 9:
236
- this.logger.section('PROCESSING COMPLETE');
237
- return [2 /*return*/];
238
- }
239
- });
240
- });
241
- };
242
- Doc2Vec.prototype.getUrlPrefix = function (url) {
243
- try {
244
- var parsedUrl = new URL(url);
245
- return parsedUrl.origin + parsedUrl.pathname;
246
- }
247
- catch (error) {
248
- return url;
249
- }
250
- };
251
- Doc2Vec.prototype.generateMetadataUUID = function (repo) {
252
- // Simple deterministic approach - hash the repo name and convert to UUID format
253
- var hash = crypto_1.default.createHash('md5').update("metadata_".concat(repo)).digest('hex');
254
- // Format as UUID with version bits set correctly (version 4)
255
- return "".concat(hash.substr(0, 8), "-").concat(hash.substr(8, 4), "-4").concat(hash.substr(13, 3), "-").concat(hash.substr(16, 4), "-").concat(hash.substr(20, 12));
256
- };
257
- Doc2Vec.prototype.initDatabaseMetadata = function (dbConnection) {
258
- return __awaiter(this, void 0, void 0, function () {
259
- var logger, db;
260
- return __generator(this, function (_a) {
261
- logger = this.logger.child('metadata');
262
- if (dbConnection.type === 'sqlite') {
263
- db = dbConnection.db;
264
- logger.debug('Creating metadata table if it doesn\'t exist');
265
- db.exec("\n CREATE TABLE IF NOT EXISTS vec_metadata (\n key TEXT PRIMARY KEY,\n value TEXT\n );\n ");
266
- logger.info('SQLite metadata table initialized');
267
- }
268
- else if (dbConnection.type === 'qdrant') {
269
- // For Qdrant, we'll use the same collection but verify it exists
270
- logger.info("Using existing Qdrant collection for metadata: ".concat(dbConnection.collectionName));
271
- // Nothing special to initialize as we'll use the same collection
272
- }
273
- return [2 /*return*/];
274
- });
275
- });
276
- };
277
- Doc2Vec.prototype.getLastRunDate = function (dbConnection, repo, defaultDate) {
278
- return __awaiter(this, void 0, void 0, function () {
279
- var logger, metadataKey, stmt, result, metadataUUID, response, lastRunDate, error_1, error_2;
280
- var _a;
281
- return __generator(this, function (_b) {
282
- switch (_b.label) {
283
- case 0:
284
- logger = this.logger.child('metadata');
285
- metadataKey = "last_run_".concat(repo.replace('/', '_'));
286
- _b.label = 1;
287
- case 1:
288
- _b.trys.push([1, 7, , 8]);
289
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 2];
290
- stmt = dbConnection.db.prepare('SELECT value FROM vec_metadata WHERE key = ?');
291
- result = stmt.get(metadataKey);
292
- if (result) {
293
- logger.info("Retrieved last run date for ".concat(repo, ": ").concat(result.value));
294
- return [2 /*return*/, result.value];
295
- }
296
- return [3 /*break*/, 6];
297
- case 2:
298
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 6];
299
- metadataUUID = this.generateMetadataUUID(repo);
300
- logger.debug("Looking up metadata with UUID: ".concat(metadataUUID));
301
- _b.label = 3;
302
- case 3:
303
- _b.trys.push([3, 5, , 6]);
304
- return [4 /*yield*/, dbConnection.client.retrieve(dbConnection.collectionName, {
305
- ids: [metadataUUID],
306
- with_payload: true,
307
- with_vector: false
308
- })];
309
- case 4:
310
- response = _b.sent();
311
- if (response.length > 0 && ((_a = response[0].payload) === null || _a === void 0 ? void 0 : _a.metadata_value)) {
312
- lastRunDate = response[0].payload.metadata_value;
313
- logger.info("Retrieved last run date for ".concat(repo, ": ").concat(lastRunDate));
314
- return [2 /*return*/, lastRunDate];
315
- }
316
- return [3 /*break*/, 6];
317
- case 5:
318
- error_1 = _b.sent();
319
- logger.warn("Failed to retrieve metadata for ".concat(repo, ":"), error_1);
320
- return [3 /*break*/, 6];
321
- case 6: return [3 /*break*/, 8];
322
- case 7:
323
- error_2 = _b.sent();
324
- logger.warn("Error retrieving last run date:", error_2);
325
- return [3 /*break*/, 8];
326
- case 8:
327
- logger.info("No saved run date found for ".concat(repo, ", using default: ").concat(defaultDate));
328
- return [2 /*return*/, defaultDate];
329
- }
330
- });
331
- });
332
- };
333
- Doc2Vec.prototype.updateLastRunDate = function (dbConnection, repo) {
334
- return __awaiter(this, void 0, void 0, function () {
335
- var logger, now, metadataKey, stmt, metadataUUID, metadataKey, dummyEmbeddingSize, dummyEmbedding, metadataPoint, error_3;
336
- return __generator(this, function (_a) {
337
- switch (_a.label) {
338
- case 0:
339
- logger = this.logger.child('metadata');
340
- now = new Date().toISOString();
341
- _a.label = 1;
342
- case 1:
343
- _a.trys.push([1, 5, , 6]);
344
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 2];
345
- metadataKey = "last_run_".concat(repo.replace('/', '_'));
346
- stmt = dbConnection.db.prepare("\n INSERT INTO vec_metadata (key, value) VALUES (?, ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value\n ");
347
- stmt.run(metadataKey, now);
348
- logger.info("Updated last run date for ".concat(repo, " to ").concat(now));
349
- return [3 /*break*/, 4];
350
- case 2:
351
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 4];
352
- metadataUUID = this.generateMetadataUUID(repo);
353
- metadataKey = "last_run_".concat(repo.replace('/', '_'));
354
- logger.debug("Using UUID: ".concat(metadataUUID, " for metadata"));
355
- dummyEmbeddingSize = 3072;
356
- dummyEmbedding = new Array(dummyEmbeddingSize).fill(0);
357
- metadataPoint = {
358
- id: metadataUUID,
359
- vector: dummyEmbedding,
360
- payload: {
361
- metadata_key: metadataKey,
362
- metadata_value: now,
363
- is_metadata: true, // Flag to identify metadata points
364
- content: "Metadata: Last run date for ".concat(repo),
365
- product_name: 'system',
366
- version: 'metadata',
367
- url: 'metadata://' + repo
368
- }
369
- };
370
- return [4 /*yield*/, dbConnection.client.upsert(dbConnection.collectionName, {
371
- wait: true,
372
- points: [metadataPoint]
373
- })];
374
- case 3:
375
- _a.sent();
376
- logger.info("Updated last run date for ".concat(repo, " to ").concat(now));
377
- _a.label = 4;
378
- case 4: return [3 /*break*/, 6];
379
- case 5:
380
- error_3 = _a.sent();
381
- logger.error("Failed to update last run date for ".concat(repo, ":"), error_3);
382
- return [3 /*break*/, 6];
383
- case 6: return [2 /*return*/];
384
- }
385
- });
386
- });
387
- };
388
- Doc2Vec.prototype.fetchAndProcessGitHubIssues = function (repo, sourceConfig, dbConnection, logger) {
389
- return __awaiter(this, void 0, void 0, function () {
390
- var _a, owner, repoName, GITHUB_API_URL, startDate, lastRunDate, fetchWithRetry, fetchAllIssues, fetchIssueComments, generateMarkdownForIssue, processIssue, issues, i;
391
- var _this = this;
392
- return __generator(this, function (_b) {
393
- switch (_b.label) {
394
- case 0:
395
- _a = repo.split('/'), owner = _a[0], repoName = _a[1];
396
- GITHUB_API_URL = "https://api.github.com/repos/".concat(owner, "/").concat(repoName, "/issues");
397
- // Initialize metadata storage if needed
398
- return [4 /*yield*/, this.initDatabaseMetadata(dbConnection)];
399
- case 1:
400
- // Initialize metadata storage if needed
401
- _b.sent();
402
- startDate = sourceConfig.start_date || '2025-01-01';
403
- return [4 /*yield*/, this.getLastRunDate(dbConnection, repo, "".concat(startDate, "T00:00:00Z"))];
404
- case 2:
405
- lastRunDate = _b.sent();
406
- fetchWithRetry = function (url_1) {
407
- var args_1 = [];
408
- for (var _i = 1; _i < arguments.length; _i++) {
409
- args_1[_i - 1] = arguments[_i];
410
- }
411
- return __awaiter(_this, __spreadArray([url_1], args_1, true), void 0, function (url, params, retries, delay) {
412
- var _loop_1, attempt, state_1;
413
- if (params === void 0) { params = {}; }
414
- if (retries === void 0) { retries = 5; }
415
- if (delay === void 0) { delay = 5000; }
416
- return __generator(this, function (_a) {
417
- switch (_a.label) {
418
- case 0:
419
- _loop_1 = function (attempt) {
420
- var response, error_4, resetTime, currentTime, waitTime_1;
421
- return __generator(this, function (_b) {
422
- switch (_b.label) {
423
- case 0:
424
- _b.trys.push([0, 2, , 6]);
425
- return [4 /*yield*/, axios_1.default.get(url, {
426
- headers: {
427
- Authorization: "token ".concat(GITHUB_TOKEN),
428
- Accept: 'application/vnd.github.v3+json',
429
- },
430
- params: params,
431
- })];
432
- case 1:
433
- response = _b.sent();
434
- return [2 /*return*/, { value: response.data }];
435
- case 2:
436
- error_4 = _b.sent();
437
- if (!(error_4.response && error_4.response.status === 403)) return [3 /*break*/, 4];
438
- resetTime = error_4.response.headers['x-ratelimit-reset'];
439
- currentTime = Math.floor(Date.now() / 1000);
440
- waitTime_1 = resetTime ? (resetTime - currentTime) * 1000 : delay * 2;
441
- logger.warn("GitHub rate limit exceeded. Waiting ".concat(waitTime_1 / 1000, "s"));
442
- return [4 /*yield*/, new Promise(function (res) { return setTimeout(res, waitTime_1); })];
443
- case 3:
444
- _b.sent();
445
- return [3 /*break*/, 5];
446
- case 4:
447
- logger.error("GitHub fetch failed: ".concat(error_4.message));
448
- throw error_4;
449
- case 5: return [3 /*break*/, 6];
450
- case 6: return [2 /*return*/];
451
- }
452
- });
453
- };
454
- attempt = 0;
455
- _a.label = 1;
456
- case 1:
457
- if (!(attempt < retries)) return [3 /*break*/, 4];
458
- return [5 /*yield**/, _loop_1(attempt)];
459
- case 2:
460
- state_1 = _a.sent();
461
- if (typeof state_1 === "object")
462
- return [2 /*return*/, state_1.value];
463
- _a.label = 3;
464
- case 3:
465
- attempt++;
466
- return [3 /*break*/, 1];
467
- case 4: throw new Error('Max retries reached');
468
- }
469
- });
470
- });
471
- };
472
- fetchAllIssues = function (sinceDate) { return __awaiter(_this, void 0, void 0, function () {
473
- var issues, page, perPage, sinceTimestamp, data, filtered;
474
- return __generator(this, function (_a) {
475
- switch (_a.label) {
476
- case 0:
477
- issues = [];
478
- page = 1;
479
- perPage = 100;
480
- sinceTimestamp = new Date(sinceDate);
481
- _a.label = 1;
482
- case 1:
483
- if (!true) return [3 /*break*/, 3];
484
- return [4 /*yield*/, fetchWithRetry(GITHUB_API_URL, {
485
- per_page: perPage,
486
- page: page,
487
- state: 'all',
488
- since: sinceDate,
489
- })];
490
- case 2:
491
- data = _a.sent();
492
- if (data.length === 0)
493
- return [3 /*break*/, 3];
494
- filtered = data.filter(function (issue) { return new Date(issue.created_at) >= sinceTimestamp; });
495
- issues = issues.concat(filtered);
496
- if (filtered.length < data.length)
497
- return [3 /*break*/, 3];
498
- page++;
499
- return [3 /*break*/, 1];
500
- case 3: return [2 /*return*/, issues];
501
- }
502
- });
503
- }); };
504
- fetchIssueComments = function (issueNumber) { return __awaiter(_this, void 0, void 0, function () {
505
- var url;
506
- return __generator(this, function (_a) {
507
- switch (_a.label) {
508
- case 0:
509
- url = "".concat(GITHUB_API_URL, "/").concat(issueNumber, "/comments");
510
- return [4 /*yield*/, fetchWithRetry(url)];
511
- case 1: return [2 /*return*/, _a.sent()];
512
- }
513
- });
514
- }); };
515
- generateMarkdownForIssue = function (issue) { return __awaiter(_this, void 0, void 0, function () {
516
- var comments, md, _i, comments_1, c;
517
- return __generator(this, function (_a) {
518
- switch (_a.label) {
519
- case 0: return [4 /*yield*/, fetchIssueComments(issue.number)];
520
- case 1:
521
- comments = _a.sent();
522
- md = "# Issue #".concat(issue.number, ": ").concat(issue.title, "\n\n");
523
- md += "- **Author:** ".concat(issue.user.login, "\n");
524
- md += "- **State:** ".concat(issue.state, "\n");
525
- md += "- **Created on:** ".concat(new Date(issue.created_at).toDateString(), "\n");
526
- md += "- **Updated on:** ".concat(new Date(issue.updated_at).toDateString(), "\n");
527
- md += "- **Labels:** ".concat(issue.labels.map(function (l) { return "`".concat(l.name, "`"); }).join(', ') || 'None', "\n\n");
528
- md += "## Description\n\n".concat(issue.body || '_No description._', "\n\n## Comments\n\n");
529
- if (comments.length === 0) {
530
- md += '_No comments._\n';
531
- }
532
- else {
533
- for (_i = 0, comments_1 = comments; _i < comments_1.length; _i++) {
534
- c = comments_1[_i];
535
- md += "### ".concat(c.user.login, " - ").concat(new Date(c.created_at).toDateString(), "\n\n").concat(c.body, "\n\n---\n\n");
536
- }
537
- }
538
- return [2 /*return*/, md];
539
- }
540
- });
541
- }); };
542
- processIssue = function (issue) { return __awaiter(_this, void 0, void 0, function () {
543
- var issueNumber, url, markdown, issueConfig, chunks, _i, chunks_1, chunk, chunkHash, chunkId, checkHashStmt, existing, embeddings, pointId, existingPoints, embeddings, error_5;
544
- return __generator(this, function (_a) {
545
- switch (_a.label) {
546
- case 0:
547
- issueNumber = issue.number;
548
- url = "https://github.com/".concat(repo, "/issues/").concat(issueNumber);
549
- logger.info("Processing issue #".concat(issueNumber));
550
- return [4 /*yield*/, generateMarkdownForIssue(issue)];
551
- case 1:
552
- markdown = _a.sent();
553
- issueConfig = __assign(__assign({}, sourceConfig), { product_name: sourceConfig.product_name || repo, max_size: sourceConfig.max_size || Infinity });
554
- return [4 /*yield*/, this.chunkMarkdown(markdown, issueConfig, url)];
555
- case 2:
556
- chunks = _a.sent();
557
- logger.info("Issue #".concat(issueNumber, ": Created ").concat(chunks.length, " chunks"));
558
- _i = 0, chunks_1 = chunks;
559
- _a.label = 3;
560
- case 3:
561
- if (!(_i < chunks_1.length)) return [3 /*break*/, 14];
562
- chunk = chunks_1[_i];
563
- chunkHash = this.generateHash(chunk.content);
564
- chunkId = chunk.metadata.chunk_id.substring(0, 8) + '...';
565
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 5];
566
- checkHashStmt = this.prepareSQLiteStatements(dbConnection.db).checkHashStmt;
567
- existing = checkHashStmt.get(chunk.metadata.chunk_id);
568
- if (existing && existing.hash === chunkHash) {
569
- logger.info("Skipping unchanged chunk: ".concat(chunkId));
570
- return [3 /*break*/, 13];
571
- }
572
- return [4 /*yield*/, this.createEmbeddings([chunk.content])];
573
- case 4:
574
- embeddings = _a.sent();
575
- if (embeddings.length) {
576
- this.insertVectorsSQLite(dbConnection.db, chunk, embeddings[0], logger, chunkHash);
577
- logger.debug("Stored chunk ".concat(chunkId, " in SQLite"));
578
- }
579
- else {
580
- logger.error("Embedding failed for chunk: ".concat(chunkId));
581
- }
582
- return [3 /*break*/, 13];
583
- case 5:
584
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 13];
585
- _a.label = 6;
586
- case 6:
587
- _a.trys.push([6, 12, , 13]);
588
- pointId = void 0;
589
- try {
590
- pointId = chunk.metadata.chunk_id;
591
- if (!this.isValidUuid(pointId)) {
592
- pointId = this.hashToUuid(chunk.metadata.chunk_id);
593
- }
594
- }
595
- catch (e) {
596
- pointId = crypto_1.default.randomUUID();
597
- }
598
- return [4 /*yield*/, dbConnection.client.retrieve(dbConnection.collectionName, {
599
- ids: [pointId],
600
- with_payload: true,
601
- with_vector: false,
602
- })];
603
- case 7:
604
- existingPoints = _a.sent();
605
- if (existingPoints.length > 0 && existingPoints[0].payload && existingPoints[0].payload.hash === chunkHash) {
606
- logger.info("Skipping unchanged chunk: ".concat(chunkId));
607
- return [3 /*break*/, 13];
608
- }
609
- return [4 /*yield*/, this.createEmbeddings([chunk.content])];
610
- case 8:
611
- embeddings = _a.sent();
612
- if (!embeddings.length) return [3 /*break*/, 10];
613
- return [4 /*yield*/, this.storeChunkInQdrant(dbConnection, chunk, embeddings[0], chunkHash)];
614
- case 9:
615
- _a.sent();
616
- logger.debug("Stored chunk ".concat(chunkId, " in Qdrant (").concat(dbConnection.collectionName, ")"));
617
- return [3 /*break*/, 11];
618
- case 10:
619
- logger.error("Embedding failed for chunk: ".concat(chunkId));
620
- _a.label = 11;
621
- case 11: return [3 /*break*/, 13];
622
- case 12:
623
- error_5 = _a.sent();
624
- logger.error("Error processing chunk in Qdrant:", error_5);
625
- return [3 /*break*/, 13];
626
- case 13:
627
- _i++;
628
- return [3 /*break*/, 3];
629
- case 14: return [2 /*return*/];
630
- }
631
- });
632
- }); };
633
- logger.info("Fetching GitHub issues for ".concat(repo, " since ").concat(lastRunDate));
634
- return [4 /*yield*/, fetchAllIssues(lastRunDate)];
635
- case 3:
636
- issues = _b.sent();
637
- logger.info("Found ".concat(issues.length, " updated/new issues"));
638
- i = 0;
639
- _b.label = 4;
640
- case 4:
641
- if (!(i < issues.length)) return [3 /*break*/, 7];
642
- logger.info("Processing issue ".concat(i + 1, "/").concat(issues.length));
643
- return [4 /*yield*/, processIssue(issues[i])];
644
- case 5:
645
- _b.sent();
646
- _b.label = 6;
647
- case 6:
648
- i++;
649
- return [3 /*break*/, 4];
650
- case 7:
651
- // Update the last run date in the database after processing all issues
652
- return [4 /*yield*/, this.updateLastRunDate(dbConnection, repo)];
653
- case 8:
654
- // Update the last run date in the database after processing all issues
655
- _b.sent();
656
- logger.info("Successfully processed ".concat(issues.length, " issues"));
657
- return [2 /*return*/];
658
- }
659
- });
660
- });
661
- };
662
- Doc2Vec.prototype.processGithubRepo = function (config, parentLogger) {
663
- return __awaiter(this, void 0, void 0, function () {
664
- var logger, dbConnection;
665
- return __generator(this, function (_a) {
666
- switch (_a.label) {
667
- case 0:
668
- logger = parentLogger.child('process');
669
- logger.info("Starting processing for GitHub repo: ".concat(config.repo));
670
- return [4 /*yield*/, this.initDatabase(config, logger)];
671
- case 1:
672
- dbConnection = _a.sent();
673
- // Initialize metadata storage
674
- return [4 /*yield*/, this.initDatabaseMetadata(dbConnection)];
675
- case 2:
676
- // Initialize metadata storage
677
- _a.sent();
678
- logger.section('GITHUB ISSUES');
679
- // Process GitHub issues
680
- return [4 /*yield*/, this.fetchAndProcessGitHubIssues(config.repo, config, dbConnection, logger)];
681
- case 3:
682
- // Process GitHub issues
683
- _a.sent();
684
- logger.info("Finished processing GitHub repo: ".concat(config.repo));
685
- return [2 /*return*/];
686
- }
687
- });
688
- });
689
- };
690
- Doc2Vec.prototype.processWebsite = function (config, parentLogger) {
691
- return __awaiter(this, void 0, void 0, function () {
692
- var logger, dbConnection, validChunkIds, visitedUrls, urlPrefix;
693
- var _this = this;
694
- return __generator(this, function (_a) {
695
- switch (_a.label) {
696
- case 0:
697
- logger = parentLogger.child('process');
698
- logger.info("Starting processing for website: ".concat(config.url));
699
- return [4 /*yield*/, this.initDatabase(config, logger)];
700
- case 1:
701
- dbConnection = _a.sent();
702
- validChunkIds = new Set();
703
- visitedUrls = new Set();
704
- urlPrefix = this.getUrlPrefix(config.url);
705
- logger.section('CRAWL AND EMBEDDING');
706
- return [4 /*yield*/, this.crawlWebsite(config.url, config, function (url, content) { return __awaiter(_this, void 0, void 0, function () {
707
- var chunks, chunkProgress, i, chunk, chunkId, needsEmbedding, chunkHash, checkHashStmt, existing, pointId, existingPoints, error_6, embeddings, embedding, error_7;
708
- return __generator(this, function (_a) {
709
- switch (_a.label) {
710
- case 0:
711
- visitedUrls.add(url);
712
- logger.info("Processing content from ".concat(url, " (").concat(content.length, " chars markdown)"));
713
- _a.label = 1;
714
- case 1:
715
- _a.trys.push([1, 17, , 18]);
716
- return [4 /*yield*/, this.chunkMarkdown(content, config, url)];
717
- case 2:
718
- chunks = _a.sent();
719
- logger.info("Created ".concat(chunks.length, " chunks"));
720
- if (!(chunks.length > 0)) return [3 /*break*/, 16];
721
- chunkProgress = logger.progress("Embedding chunks for ".concat(url), chunks.length);
722
- i = 0;
723
- _a.label = 3;
724
- case 3:
725
- if (!(i < chunks.length)) return [3 /*break*/, 15];
726
- chunk = chunks[i];
727
- validChunkIds.add(chunk.metadata.chunk_id);
728
- chunkId = chunk.metadata.chunk_id.substring(0, 8) + '...';
729
- needsEmbedding = true;
730
- chunkHash = this.generateHash(chunk.content);
731
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 4];
732
- checkHashStmt = this.prepareSQLiteStatements(dbConnection.db).checkHashStmt;
733
- existing = checkHashStmt.get(chunk.metadata.chunk_id);
734
- if (existing && existing.hash === chunkHash) {
735
- needsEmbedding = false;
736
- chunkProgress.update(1, "Skipping unchanged chunk ".concat(chunkId));
737
- logger.info("Skipping unchanged chunk: ".concat(chunkId));
738
- }
739
- return [3 /*break*/, 8];
740
- case 4:
741
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 8];
742
- _a.label = 5;
743
- case 5:
744
- _a.trys.push([5, 7, , 8]);
745
- pointId = void 0;
746
- try {
747
- pointId = chunk.metadata.chunk_id;
748
- if (!this.isValidUuid(pointId)) {
749
- pointId = this.hashToUuid(chunk.metadata.chunk_id);
750
- }
751
- }
752
- catch (e) {
753
- pointId = crypto_1.default.randomUUID();
754
- }
755
- return [4 /*yield*/, dbConnection.client.retrieve(dbConnection.collectionName, {
756
- ids: [pointId],
757
- with_payload: true,
758
- with_vector: false,
759
- })];
760
- case 6:
761
- existingPoints = _a.sent();
762
- if (existingPoints.length > 0 && existingPoints[0].payload && existingPoints[0].payload.hash === chunkHash) {
763
- needsEmbedding = false;
764
- chunkProgress.update(1, "Skipping unchanged chunk ".concat(chunkId));
765
- logger.info("Skipping unchanged chunk: ".concat(chunkId));
766
- }
767
- return [3 /*break*/, 8];
768
- case 7:
769
- error_6 = _a.sent();
770
- logger.error("Error checking existing point in Qdrant:", error_6);
771
- return [3 /*break*/, 8];
772
- case 8:
773
- if (!needsEmbedding) return [3 /*break*/, 14];
774
- return [4 /*yield*/, this.createEmbeddings([chunk.content])];
775
- case 9:
776
- embeddings = _a.sent();
777
- if (!(embeddings.length > 0)) return [3 /*break*/, 13];
778
- embedding = embeddings[0];
779
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 10];
780
- this.insertVectorsSQLite(dbConnection.db, chunk, embedding, logger, chunkHash);
781
- chunkProgress.update(1, "Stored chunk ".concat(chunkId, " in SQLite"));
782
- return [3 /*break*/, 12];
783
- case 10:
784
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 12];
785
- return [4 /*yield*/, this.storeChunkInQdrant(dbConnection, chunk, embedding, chunkHash)];
786
- case 11:
787
- _a.sent();
788
- chunkProgress.update(1, "Stored chunk ".concat(chunkId, " in Qdrant (").concat(dbConnection.collectionName, ")"));
789
- _a.label = 12;
790
- case 12: return [3 /*break*/, 14];
791
- case 13:
792
- logger.error("Embedding failed for chunk: ".concat(chunkId));
793
- chunkProgress.update(1, "Failed to embed chunk ".concat(chunkId));
794
- _a.label = 14;
795
- case 14:
796
- i++;
797
- return [3 /*break*/, 3];
798
- case 15:
799
- chunkProgress.complete();
800
- _a.label = 16;
801
- case 16: return [3 /*break*/, 18];
802
- case 17:
803
- error_7 = _a.sent();
804
- logger.error("Error during chunking or embedding for ".concat(url, ":"), error_7);
805
- return [3 /*break*/, 18];
806
- case 18: return [2 /*return*/];
807
- }
808
- });
809
- }); }, logger, visitedUrls)];
810
- case 2:
811
- _a.sent();
812
- logger.info("Found ".concat(validChunkIds.size, " valid chunks across processed pages for ").concat(config.url));
813
- logger.section('CLEANUP');
814
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 3];
815
- logger.info("Running SQLite cleanup for ".concat(urlPrefix));
816
- this.removeObsoleteChunksSQLite(dbConnection.db, visitedUrls, urlPrefix, logger);
817
- return [3 /*break*/, 5];
818
- case 3:
819
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 5];
820
- logger.info("Running Qdrant cleanup for ".concat(urlPrefix, " in collection ").concat(dbConnection.collectionName));
821
- return [4 /*yield*/, this.removeObsoleteChunksQdrant(dbConnection, visitedUrls, urlPrefix, logger)];
822
- case 4:
823
- _a.sent();
824
- _a.label = 5;
825
- case 5:
826
- logger.info("Finished processing website: ".concat(config.url));
827
- return [2 /*return*/];
828
- }
829
- });
830
- });
831
- };
832
- Doc2Vec.prototype.processLocalDirectory = function (config, parentLogger) {
833
- return __awaiter(this, void 0, void 0, function () {
834
- var logger, dbConnection, validChunkIds, processedFiles;
835
- var _this = this;
836
- return __generator(this, function (_a) {
837
- switch (_a.label) {
838
- case 0:
839
- logger = parentLogger.child('process');
840
- logger.info("Starting processing for local directory: ".concat(config.path));
841
- return [4 /*yield*/, this.initDatabase(config, logger)];
842
- case 1:
843
- dbConnection = _a.sent();
844
- validChunkIds = new Set();
845
- processedFiles = new Set();
846
- logger.section('FILE SCANNING AND EMBEDDING');
847
- return [4 /*yield*/, this.processDirectory(config.path, config, function (filePath, content) { return __awaiter(_this, void 0, void 0, function () {
848
- var fileUrl, chunks, chunkProgress, i, chunk, chunkId, needsEmbedding, chunkHash, checkHashStmt, existing, pointId, existingPoints, error_8, embeddings, embedding, error_9;
849
- return __generator(this, function (_a) {
850
- switch (_a.label) {
851
- case 0:
852
- processedFiles.add(filePath);
853
- logger.info("Processing content from ".concat(filePath, " (").concat(content.length, " chars)"));
854
- _a.label = 1;
855
- case 1:
856
- _a.trys.push([1, 17, , 18]);
857
- fileUrl = "file://".concat(filePath);
858
- return [4 /*yield*/, this.chunkMarkdown(content, config, fileUrl)];
859
- case 2:
860
- chunks = _a.sent();
861
- logger.info("Created ".concat(chunks.length, " chunks"));
862
- if (!(chunks.length > 0)) return [3 /*break*/, 16];
863
- chunkProgress = logger.progress("Embedding chunks for ".concat(filePath), chunks.length);
864
- i = 0;
865
- _a.label = 3;
866
- case 3:
867
- if (!(i < chunks.length)) return [3 /*break*/, 15];
868
- chunk = chunks[i];
869
- validChunkIds.add(chunk.metadata.chunk_id);
870
- chunkId = chunk.metadata.chunk_id.substring(0, 8) + '...';
871
- needsEmbedding = true;
872
- chunkHash = this.generateHash(chunk.content);
873
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 4];
874
- checkHashStmt = this.prepareSQLiteStatements(dbConnection.db).checkHashStmt;
875
- existing = checkHashStmt.get(chunk.metadata.chunk_id);
876
- if (existing && existing.hash === chunkHash) {
877
- needsEmbedding = false;
878
- chunkProgress.update(1, "Skipping unchanged chunk ".concat(chunkId));
879
- logger.info("Skipping unchanged chunk: ".concat(chunkId));
880
- }
881
- return [3 /*break*/, 8];
882
- case 4:
883
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 8];
884
- _a.label = 5;
885
- case 5:
886
- _a.trys.push([5, 7, , 8]);
887
- pointId = void 0;
888
- try {
889
- pointId = chunk.metadata.chunk_id;
890
- if (!this.isValidUuid(pointId)) {
891
- pointId = this.hashToUuid(chunk.metadata.chunk_id);
892
- }
893
- }
894
- catch (e) {
895
- pointId = crypto_1.default.randomUUID();
896
- }
897
- return [4 /*yield*/, dbConnection.client.retrieve(dbConnection.collectionName, {
898
- ids: [pointId],
899
- with_payload: true,
900
- with_vector: false,
901
- })];
902
- case 6:
903
- existingPoints = _a.sent();
904
- if (existingPoints.length > 0 && existingPoints[0].payload && existingPoints[0].payload.hash === chunkHash) {
905
- needsEmbedding = false;
906
- chunkProgress.update(1, "Skipping unchanged chunk ".concat(chunkId));
907
- logger.info("Skipping unchanged chunk: ".concat(chunkId));
908
- }
909
- return [3 /*break*/, 8];
910
- case 7:
911
- error_8 = _a.sent();
912
- logger.error("Error checking existing point in Qdrant:", error_8);
913
- return [3 /*break*/, 8];
914
- case 8:
915
- if (!needsEmbedding) return [3 /*break*/, 14];
916
- return [4 /*yield*/, this.createEmbeddings([chunk.content])];
917
- case 9:
918
- embeddings = _a.sent();
919
- if (!(embeddings.length > 0)) return [3 /*break*/, 13];
920
- embedding = embeddings[0];
921
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 10];
922
- this.insertVectorsSQLite(dbConnection.db, chunk, embedding, logger, chunkHash);
923
- chunkProgress.update(1, "Stored chunk ".concat(chunkId, " in SQLite"));
924
- return [3 /*break*/, 12];
925
- case 10:
926
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 12];
927
- return [4 /*yield*/, this.storeChunkInQdrant(dbConnection, chunk, embedding, chunkHash)];
928
- case 11:
929
- _a.sent();
930
- chunkProgress.update(1, "Stored chunk ".concat(chunkId, " in Qdrant (").concat(dbConnection.collectionName, ")"));
931
- _a.label = 12;
932
- case 12: return [3 /*break*/, 14];
933
- case 13:
934
- logger.error("Embedding failed for chunk: ".concat(chunkId));
935
- chunkProgress.update(1, "Failed to embed chunk ".concat(chunkId));
936
- _a.label = 14;
937
- case 14:
938
- i++;
939
- return [3 /*break*/, 3];
940
- case 15:
941
- chunkProgress.complete();
942
- _a.label = 16;
943
- case 16: return [3 /*break*/, 18];
944
- case 17:
945
- error_9 = _a.sent();
946
- logger.error("Error during chunking or embedding for ".concat(filePath, ":"), error_9);
947
- return [3 /*break*/, 18];
948
- case 18: return [2 /*return*/];
949
- }
950
- });
951
- }); }, logger)];
952
- case 2:
953
- _a.sent();
954
- logger.info("Found ".concat(validChunkIds.size, " valid chunks across processed files for ").concat(config.path));
955
- logger.section('CLEANUP');
956
- if (!(dbConnection.type === 'sqlite')) return [3 /*break*/, 3];
957
- logger.info("Running SQLite cleanup for local directory ".concat(config.path));
958
- this.removeObsoleteFilesSQLite(dbConnection.db, processedFiles, config.path, logger);
959
- return [3 /*break*/, 5];
960
- case 3:
961
- if (!(dbConnection.type === 'qdrant')) return [3 /*break*/, 5];
962
- logger.info("Running Qdrant cleanup for local directory ".concat(config.path, " in collection ").concat(dbConnection.collectionName));
963
- return [4 /*yield*/, this.removeObsoleteFilesQdrant(dbConnection, processedFiles, config.path, logger)];
964
- case 4:
965
- _a.sent();
966
- _a.label = 5;
967
- case 5:
968
- logger.info("Finished processing local directory: ".concat(config.path));
969
- return [2 /*return*/];
970
- }
971
- });
972
- });
973
- };
974
- Doc2Vec.prototype.processDirectory = function (dirPath_1, config_1, processFileContent_1, parentLogger_1) {
975
- return __awaiter(this, arguments, void 0, function (dirPath, config, processFileContent, parentLogger, visitedPaths) {
976
- var logger, recursive, includeExtensions, excludeExtensions, encoding, files, processedFiles, skippedFiles, _i, files_1, file, filePath, stat, extension, content, processedContent, cleanHtml, error_10, error_11;
977
- if (visitedPaths === void 0) { visitedPaths = new Set(); }
978
- return __generator(this, function (_a) {
979
- switch (_a.label) {
980
- case 0:
981
- logger = parentLogger.child('directory-processor');
982
- logger.info("Processing directory: ".concat(dirPath));
983
- recursive = config.recursive !== undefined ? config.recursive : true;
984
- includeExtensions = config.include_extensions || ['.md', '.txt', '.html', '.htm'];
985
- excludeExtensions = config.exclude_extensions || [];
986
- encoding = config.encoding || 'utf8';
987
- _a.label = 1;
988
- case 1:
989
- _a.trys.push([1, 12, , 13]);
990
- files = fs.readdirSync(dirPath);
991
- processedFiles = 0;
992
- skippedFiles = 0;
993
- _i = 0, files_1 = files;
994
- _a.label = 2;
995
- case 2:
996
- if (!(_i < files_1.length)) return [3 /*break*/, 11];
997
- file = files_1[_i];
998
- filePath = path.join(dirPath, file);
999
- stat = fs.statSync(filePath);
1000
- // Skip already visited paths
1001
- if (visitedPaths.has(filePath)) {
1002
- logger.debug("Skipping already visited path: ".concat(filePath));
1003
- return [3 /*break*/, 10];
1004
- }
1005
- visitedPaths.add(filePath);
1006
- if (!stat.isDirectory()) return [3 /*break*/, 6];
1007
- if (!recursive) return [3 /*break*/, 4];
1008
- return [4 /*yield*/, this.processDirectory(filePath, config, processFileContent, logger, visitedPaths)];
1009
- case 3:
1010
- _a.sent();
1011
- return [3 /*break*/, 5];
1012
- case 4:
1013
- logger.debug("Skipping directory ".concat(filePath, " (recursive=false)"));
1014
- _a.label = 5;
1015
- case 5: return [3 /*break*/, 10];
1016
- case 6:
1017
- if (!stat.isFile()) return [3 /*break*/, 10];
1018
- extension = path.extname(file).toLowerCase();
1019
- // Apply extension filters
1020
- if (excludeExtensions.includes(extension)) {
1021
- logger.debug("Skipping file with excluded extension: ".concat(filePath));
1022
- skippedFiles++;
1023
- return [3 /*break*/, 10];
1024
- }
1025
- if (includeExtensions.length > 0 && !includeExtensions.includes(extension)) {
1026
- logger.debug("Skipping file with non-included extension: ".concat(filePath));
1027
- skippedFiles++;
1028
- return [3 /*break*/, 10];
1029
- }
1030
- _a.label = 7;
1031
- case 7:
1032
- _a.trys.push([7, 9, , 10]);
1033
- logger.info("Reading file: ".concat(filePath));
1034
- content = fs.readFileSync(filePath, { encoding: encoding });
1035
- if (content.length > config.max_size) {
1036
- logger.warn("File content (".concat(content.length, " chars) exceeds max size (").concat(config.max_size, "). Skipping ").concat(filePath, "."));
1037
- skippedFiles++;
1038
- return [3 /*break*/, 10];
1039
- }
1040
- processedContent = void 0;
1041
- if (extension === '.html' || extension === '.htm') {
1042
- logger.debug("Converting HTML to Markdown for ".concat(filePath));
1043
- cleanHtml = (0, sanitize_html_1.default)(content, {
1044
- allowedTags: [
1045
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', 'ul', 'ol',
1046
- 'li', 'b', 'i', 'strong', 'em', 'code', 'pre',
1047
- 'div', 'span', 'table', 'thead', 'tbody', 'tr', 'th', 'td'
1048
- ],
1049
- allowedAttributes: {
1050
- 'a': ['href'],
1051
- 'pre': ['class', 'data-language'],
1052
- 'code': ['class', 'data-language'],
1053
- 'div': ['class'],
1054
- 'span': ['class']
1055
- }
1056
- });
1057
- processedContent = this.turndownService.turndown(cleanHtml);
1058
- }
1059
- else {
1060
- processedContent = content;
1061
- }
1062
- return [4 /*yield*/, processFileContent(filePath, processedContent)];
1063
- case 8:
1064
- _a.sent();
1065
- processedFiles++;
1066
- return [3 /*break*/, 10];
1067
- case 9:
1068
- error_10 = _a.sent();
1069
- logger.error("Error processing file ".concat(filePath, ":"), error_10);
1070
- return [3 /*break*/, 10];
1071
- case 10:
1072
- _i++;
1073
- return [3 /*break*/, 2];
1074
- case 11:
1075
- logger.info("Directory processed. Processed: ".concat(processedFiles, ", Skipped: ").concat(skippedFiles));
1076
- return [3 /*break*/, 13];
1077
- case 12:
1078
- error_11 = _a.sent();
1079
- logger.error("Error reading directory ".concat(dirPath, ":"), error_11);
1080
- return [3 /*break*/, 13];
1081
- case 13: return [2 /*return*/];
1082
- }
1083
- });
1084
- });
1085
- };
1086
- Doc2Vec.prototype.initDatabase = function (config, parentLogger) {
1087
- return __awaiter(this, void 0, void 0, function () {
1088
- var logger, dbConfig, params, dbPath, db, params, qdrantUrl, qdrantPort, collectionName, qdrantClient, errMsg;
1089
- return __generator(this, function (_a) {
1090
- switch (_a.label) {
1091
- case 0:
1092
- logger = parentLogger.child('database');
1093
- dbConfig = config.database_config;
1094
- if (!(dbConfig.type === 'sqlite')) return [3 /*break*/, 1];
1095
- params = dbConfig.params;
1096
- dbPath = params.db_path || path.join(process.cwd(), "".concat(config.product_name.replace(/\s+/g, '_'), "-").concat(config.version, ".db"));
1097
- logger.info("Opening SQLite database at ".concat(dbPath));
1098
- db = new better_sqlite3_1.default(dbPath, { allowExtension: true });
1099
- sqliteVec.load(db);
1100
- logger.debug("Creating vec_items table if it doesn't exist");
1101
- db.exec("\n CREATE VIRTUAL TABLE IF NOT EXISTS vec_items USING vec0(\n embedding FLOAT[3072],\n product_name TEXT,\n version TEXT,\n heading_hierarchy TEXT,\n section TEXT,\n chunk_id TEXT UNIQUE,\n content TEXT,\n url TEXT,\n hash TEXT\n );\n ");
1102
- logger.info("SQLite database initialized successfully");
1103
- return [2 /*return*/, { db: db, type: 'sqlite' }];
1104
- case 1:
1105
- if (!(dbConfig.type === 'qdrant')) return [3 /*break*/, 3];
1106
- params = dbConfig.params;
1107
- qdrantUrl = params.qdrant_url || 'http://localhost:6333';
1108
- qdrantPort = params.qdrant_port || 443;
1109
- collectionName = params.collection_name || "".concat(config.product_name.toLowerCase().replace(/\s+/g, '_'), "_").concat(config.version);
1110
- logger.info("Connecting to Qdrant at ".concat(qdrantUrl, ":").concat(qdrantPort, ", collection: ").concat(collectionName));
1111
- qdrantClient = new js_client_rest_1.QdrantClient({ url: qdrantUrl, apiKey: process.env.QDRANT_API_KEY, port: qdrantPort });
1112
- return [4 /*yield*/, this.createCollectionQdrant(qdrantClient, collectionName, logger)];
1113
- case 2:
1114
- _a.sent();
1115
- logger.info("Qdrant connection established successfully");
1116
- return [2 /*return*/, { client: qdrantClient, collectionName: collectionName, type: 'qdrant' }];
1117
- case 3:
1118
- errMsg = "Unsupported database type: ".concat(dbConfig.type);
1119
- logger.error(errMsg);
1120
- throw new Error(errMsg);
1121
- }
1122
- });
1123
- });
1124
- };
1125
- Doc2Vec.prototype.createCollectionQdrant = function (qdrantClient, collectionName, logger) {
1126
- return __awaiter(this, void 0, void 0, function () {
1127
- var collections, collectionExists, error_12, errorMsg, errorString;
1128
- return __generator(this, function (_a) {
1129
- switch (_a.label) {
1130
- case 0:
1131
- _a.trys.push([0, 3, , 4]);
1132
- logger.debug("Checking if collection ".concat(collectionName, " exists"));
1133
- return [4 /*yield*/, qdrantClient.getCollections()];
1134
- case 1:
1135
- collections = _a.sent();
1136
- collectionExists = collections.collections.some(function (collection) { return collection.name === collectionName; });
1137
- if (collectionExists) {
1138
- logger.info("Collection ".concat(collectionName, " already exists"));
1139
- return [2 /*return*/];
1140
- }
1141
- logger.info("Creating new collection ".concat(collectionName));
1142
- return [4 /*yield*/, qdrantClient.createCollection(collectionName, {
1143
- vectors: {
1144
- size: 3072,
1145
- distance: "Cosine",
1146
- },
1147
- })];
1148
- case 2:
1149
- _a.sent();
1150
- logger.info("Collection ".concat(collectionName, " created successfully"));
1151
- return [3 /*break*/, 4];
1152
- case 3:
1153
- error_12 = _a.sent();
1154
- if (error_12 instanceof Error) {
1155
- errorMsg = error_12.message.toLowerCase();
1156
- errorString = JSON.stringify(error_12).toLowerCase();
1157
- if (errorMsg.includes("already exists") ||
1158
- errorString.includes("already exists") ||
1159
- (error_12 === null || error_12 === void 0 ? void 0 : error_12.status) === 409 ||
1160
- errorString.includes("conflict")) {
1161
- logger.info("Collection ".concat(collectionName, " already exists (from error response)"));
1162
- return [2 /*return*/];
1163
- }
1164
- }
1165
- logger.error("Error creating Qdrant collection:", error_12);
1166
- logger.warn("Continuing with existing collection...");
1167
- return [3 /*break*/, 4];
1168
- case 4: return [2 /*return*/];
1169
- }
1170
- });
1171
- });
1172
- };
1173
- Doc2Vec.prototype.prepareSQLiteStatements = function (db) {
1174
- return {
1175
- insertStmt: db.prepare("\n INSERT INTO vec_items (embedding, product_name, version, heading_hierarchy, section, chunk_id, content, url, hash)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n "),
1176
- checkHashStmt: db.prepare("SELECT hash FROM vec_items WHERE chunk_id = ?"),
1177
- updateStmt: db.prepare("\n UPDATE vec_items SET embedding = ?, product_name = ?, version = ?, heading_hierarchy = ?, section = ?, content = ?, url = ?, hash = ?\n WHERE chunk_id = ?\n "),
1178
- getAllChunkIdsStmt: db.prepare("SELECT chunk_id FROM vec_items"),
1179
- deleteChunkStmt: db.prepare("DELETE FROM vec_items WHERE chunk_id = ?")
1180
- };
1181
- };
1182
- Doc2Vec.prototype.insertVectorsSQLite = function (db, chunk, embedding, logger, chunkHash) {
1183
- var _a = this.prepareSQLiteStatements(db), insertStmt = _a.insertStmt, updateStmt = _a.updateStmt;
1184
- var hash = chunkHash || this.generateHash(chunk.content);
1185
- var transaction = db.transaction(function () {
1186
- var params = [
1187
- new Float32Array(embedding),
1188
- chunk.metadata.product_name,
1189
- chunk.metadata.version,
1190
- JSON.stringify(chunk.metadata.heading_hierarchy),
1191
- chunk.metadata.section,
1192
- chunk.metadata.chunk_id,
1193
- chunk.content,
1194
- chunk.metadata.url,
1195
- hash
1196
- ];
1197
- try {
1198
- insertStmt.run(params);
1199
- }
1200
- catch (error) {
1201
- updateStmt.run(__spreadArray(__spreadArray([], params.slice(0, 8), true), [chunk.metadata.chunk_id], false));
1202
- }
1203
- });
1204
- transaction();
1205
- };
1206
- Doc2Vec.prototype.storeChunkInQdrant = function (db, chunk, embedding, chunkHash) {
1207
- return __awaiter(this, void 0, void 0, function () {
1208
- var client, collectionName, pointId, hash, pointItem, error_13;
1209
- return __generator(this, function (_a) {
1210
- switch (_a.label) {
1211
- case 0:
1212
- client = db.client, collectionName = db.collectionName;
1213
- _a.label = 1;
1214
- case 1:
1215
- _a.trys.push([1, 3, , 4]);
1216
- pointId = void 0;
1217
- try {
1218
- pointId = chunk.metadata.chunk_id;
1219
- if (!this.isValidUuid(pointId)) {
1220
- pointId = this.hashToUuid(chunk.metadata.chunk_id);
1221
- }
1222
- }
1223
- catch (e) {
1224
- pointId = crypto_1.default.randomUUID();
1225
- }
1226
- hash = chunkHash || this.generateHash(chunk.content);
1227
- pointItem = {
1228
- id: pointId,
1229
- vector: embedding,
1230
- payload: {
1231
- content: chunk.content,
1232
- product_name: chunk.metadata.product_name,
1233
- version: chunk.metadata.version,
1234
- heading_hierarchy: chunk.metadata.heading_hierarchy,
1235
- section: chunk.metadata.section,
1236
- url: chunk.metadata.url,
1237
- hash: hash,
1238
- original_chunk_id: chunk.metadata.chunk_id,
1239
- },
1240
- };
1241
- return [4 /*yield*/, client.upsert(collectionName, {
1242
- wait: true,
1243
- points: [pointItem],
1244
- })];
1245
- case 2:
1246
- _a.sent();
1247
- return [3 /*break*/, 4];
1248
- case 3:
1249
- error_13 = _a.sent();
1250
- this.logger.error("Error storing chunk in Qdrant:", error_13);
1251
- return [3 /*break*/, 4];
1252
- case 4: return [2 /*return*/];
1253
- }
1254
- });
1255
- });
1256
- };
1257
- Doc2Vec.prototype.removeObsoleteChunksSQLite = function (db, visitedUrls, urlPrefix, logger) {
1258
- var getChunksForUrlStmt = db.prepare("\n SELECT chunk_id, url FROM vec_items\n WHERE url LIKE ? || '%'\n ");
1259
- var deleteChunkStmt = db.prepare("DELETE FROM vec_items WHERE chunk_id = ?");
1260
- var existingChunks = getChunksForUrlStmt.all(urlPrefix);
1261
- var deletedCount = 0;
1262
- var transaction = db.transaction(function () {
1263
- for (var _i = 0, existingChunks_1 = existingChunks; _i < existingChunks_1.length; _i++) {
1264
- var _a = existingChunks_1[_i], chunk_id = _a.chunk_id, url = _a.url;
1265
- if (!visitedUrls.has(url)) {
1266
- logger.debug("Deleting obsolete chunk from SQLite: ".concat(chunk_id.substring(0, 8), "... (URL not visited)"));
1267
- deleteChunkStmt.run(chunk_id);
1268
- deletedCount++;
1269
- }
1270
- }
1271
- });
1272
- transaction();
1273
- logger.info("Deleted ".concat(deletedCount, " obsolete chunks from SQLite for URL ").concat(urlPrefix));
1274
- };
1275
- Doc2Vec.prototype.removeObsoleteFilesSQLite = function (db, processedFiles, dirPrefix, logger) {
1276
- var getChunksForDirStmt = db.prepare("\n SELECT chunk_id, url FROM vec_items\n WHERE url LIKE 'file://%' AND url LIKE ?\n ");
1277
- var deleteChunkStmt = db.prepare("DELETE FROM vec_items WHERE chunk_id = ?");
1278
- var cleanedDirPrefix = dirPrefix.replace(/^\.\/+/, '');
1279
- var filePrefix = "file://".concat(cleanedDirPrefix);
1280
- var existingChunks = getChunksForDirStmt.all("".concat(filePrefix, "%"));
1281
- var deletedCount = 0;
1282
- var transaction = db.transaction(function () {
1283
- for (var _i = 0, existingChunks_2 = existingChunks; _i < existingChunks_2.length; _i++) {
1284
- var _a = existingChunks_2[_i], chunk_id = _a.chunk_id, url = _a.url;
1285
- // Remove file:// prefix to match with processedFiles
1286
- var filePath = url.substring(7);
1287
- if (!processedFiles.has(filePath)) {
1288
- logger.debug("Deleting obsolete chunk from SQLite: ".concat(chunk_id.substring(0, 8), "... (File not processed)"));
1289
- deleteChunkStmt.run(chunk_id);
1290
- deletedCount++;
1291
- }
1292
- }
1293
- });
1294
- transaction();
1295
- logger.info("Deleted ".concat(deletedCount, " obsolete chunks from SQLite for directory ").concat(dirPrefix));
1296
- };
1297
- Doc2Vec.prototype.removeObsoleteFilesQdrant = function (db, processedFiles, dirPrefix, logger) {
1298
- return __awaiter(this, void 0, void 0, function () {
1299
- var client, collectionName, cleanedDirPrefix, filePrefix, response, obsoletePointIds, error_14;
1300
- return __generator(this, function (_a) {
1301
- switch (_a.label) {
1302
- case 0:
1303
- client = db.client, collectionName = db.collectionName;
1304
- _a.label = 1;
1305
- case 1:
1306
- _a.trys.push([1, 6, , 7]);
1307
- cleanedDirPrefix = dirPrefix.replace(/^\.\/+/, '');
1308
- filePrefix = "file://".concat(cleanedDirPrefix);
1309
- return [4 /*yield*/, client.scroll(collectionName, {
1310
- limit: 10000,
1311
- with_payload: true,
1312
- with_vector: false,
1313
- filter: {
1314
- must: [
1315
- {
1316
- key: "url",
1317
- match: {
1318
- text: "".concat(filePrefix)
1319
- }
1320
- }
1321
- ],
1322
- must_not: [
1323
- {
1324
- key: "is_metadata",
1325
- match: {
1326
- value: true
1327
- }
1328
- }
1329
- ]
1330
- }
1331
- })];
1332
- case 2:
1333
- response = _a.sent();
1334
- obsoletePointIds = response.points
1335
- .filter(function (point) {
1336
- var _a, _b;
1337
- var url = (_a = point.payload) === null || _a === void 0 ? void 0 : _a.url;
1338
- // Double check it's not a metadata record
1339
- if (((_b = point.payload) === null || _b === void 0 ? void 0 : _b.is_metadata) === true) {
1340
- return false;
1341
- }
1342
- // Remove file:// prefix to match with processedFiles
1343
- var filePath = url && url.startsWith('file://') ? url.substring(7) : '';
1344
- return filePath && !processedFiles.has(filePath);
1345
- })
1346
- .map(function (point) { return point.id; });
1347
- if (!(obsoletePointIds.length > 0)) return [3 /*break*/, 4];
1348
- return [4 /*yield*/, client.delete(collectionName, {
1349
- points: obsoletePointIds,
1350
- })];
1351
- case 3:
1352
- _a.sent();
1353
- logger.info("Deleted ".concat(obsoletePointIds.length, " obsolete chunks from Qdrant for directory ").concat(dirPrefix));
1354
- return [3 /*break*/, 5];
1355
- case 4:
1356
- logger.info("No obsolete chunks to delete from Qdrant for directory ".concat(dirPrefix));
1357
- _a.label = 5;
1358
- case 5: return [3 /*break*/, 7];
1359
- case 6:
1360
- error_14 = _a.sent();
1361
- logger.error("Error removing obsolete chunks from Qdrant:", error_14);
1362
- return [3 /*break*/, 7];
1363
- case 7: return [2 /*return*/];
1364
- }
1365
- });
1366
- });
1367
- };
1368
- Doc2Vec.prototype.isValidUuid = function (str) {
1369
- var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1370
- return uuidRegex.test(str);
1371
- };
1372
- Doc2Vec.prototype.hashToUuid = function (hash) {
1373
- var truncatedHash = hash.substring(0, 32);
1374
- return [
1375
- truncatedHash.substring(0, 8),
1376
- truncatedHash.substring(8, 12),
1377
- '5' + truncatedHash.substring(13, 16),
1378
- '8' + truncatedHash.substring(17, 20),
1379
- truncatedHash.substring(20, 32)
1380
- ].join('-');
1381
- };
1382
- Doc2Vec.prototype.removeObsoleteChunksQdrant = function (db, visitedUrls, urlPrefix, logger) {
1383
- return __awaiter(this, void 0, void 0, function () {
1384
- var client, collectionName, response, obsoletePointIds, error_15;
1385
- return __generator(this, function (_a) {
1386
- switch (_a.label) {
1387
- case 0:
1388
- client = db.client, collectionName = db.collectionName;
1389
- _a.label = 1;
1390
- case 1:
1391
- _a.trys.push([1, 6, , 7]);
1392
- return [4 /*yield*/, client.scroll(collectionName, {
1393
- limit: 10000,
1394
- with_payload: true,
1395
- with_vector: false,
1396
- filter: {
1397
- must: [
1398
- {
1399
- key: "url",
1400
- match: {
1401
- text: urlPrefix
1402
- }
1403
- }
1404
- ],
1405
- must_not: [
1406
- {
1407
- key: "is_metadata",
1408
- match: {
1409
- value: true
1410
- }
1411
- }
1412
- ]
1413
- }
1414
- })];
1415
- case 2:
1416
- response = _a.sent();
1417
- obsoletePointIds = response.points
1418
- .filter(function (point) {
1419
- var _a, _b;
1420
- var url = (_a = point.payload) === null || _a === void 0 ? void 0 : _a.url;
1421
- // Double check it's not a metadata record
1422
- if (((_b = point.payload) === null || _b === void 0 ? void 0 : _b.is_metadata) === true) {
1423
- return false;
1424
- }
1425
- return url && !visitedUrls.has(url);
1426
- })
1427
- .map(function (point) { return point.id; });
1428
- if (!(obsoletePointIds.length > 0)) return [3 /*break*/, 4];
1429
- return [4 /*yield*/, client.delete(collectionName, {
1430
- points: obsoletePointIds,
1431
- })];
1432
- case 3:
1433
- _a.sent();
1434
- logger.info("Deleted ".concat(obsoletePointIds.length, " obsolete chunks from Qdrant for URL ").concat(urlPrefix));
1435
- return [3 /*break*/, 5];
1436
- case 4:
1437
- logger.info("No obsolete chunks to delete from Qdrant for URL ".concat(urlPrefix));
1438
- _a.label = 5;
1439
- case 5: return [3 /*break*/, 7];
1440
- case 6:
1441
- error_15 = _a.sent();
1442
- logger.error("Error removing obsolete chunks from Qdrant:", error_15);
1443
- return [3 /*break*/, 7];
1444
- case 7: return [2 /*return*/];
1445
- }
1446
- });
1447
- });
1448
- };
1449
- Doc2Vec.prototype.crawlWebsite = function (baseUrl, sourceConfig, processPageContent, parentLogger, visitedUrls) {
1450
- return __awaiter(this, void 0, void 0, function () {
1451
- var logger, queue, processedCount, skippedCount, skippedSizeCount, errorCount, _loop_2, this_1;
1452
- var _this = this;
1453
- return __generator(this, function (_a) {
1454
- switch (_a.label) {
1455
- case 0:
1456
- logger = parentLogger.child('crawler');
1457
- queue = [baseUrl];
1458
- logger.info("Starting crawl from ".concat(baseUrl));
1459
- processedCount = 0;
1460
- skippedCount = 0;
1461
- skippedSizeCount = 0;
1462
- errorCount = 0;
1463
- _loop_2 = function () {
1464
- var url, normalizedUrl, content, response, $_1, newLinksFound_1, error_16;
1465
- return __generator(this, function (_b) {
1466
- switch (_b.label) {
1467
- case 0:
1468
- url = queue.shift();
1469
- if (!url)
1470
- return [2 /*return*/, "continue"];
1471
- normalizedUrl = this_1.normalizeUrl(url);
1472
- if (visitedUrls.has(normalizedUrl))
1473
- return [2 /*return*/, "continue"];
1474
- visitedUrls.add(normalizedUrl);
1475
- if (!this_1.shouldProcessUrl(url)) {
1476
- logger.debug("Skipping URL with unsupported extension: ".concat(url));
1477
- skippedCount++;
1478
- return [2 /*return*/, "continue"];
1479
- }
1480
- _b.label = 1;
1481
- case 1:
1482
- _b.trys.push([1, 7, , 8]);
1483
- logger.info("Crawling: ".concat(url));
1484
- return [4 /*yield*/, this_1.processPage(url, sourceConfig)];
1485
- case 2:
1486
- content = _b.sent();
1487
- if (!(content !== null)) return [3 /*break*/, 4];
1488
- return [4 /*yield*/, processPageContent(url, content)];
1489
- case 3:
1490
- _b.sent();
1491
- processedCount++;
1492
- return [3 /*break*/, 5];
1493
- case 4:
1494
- skippedSizeCount++;
1495
- _b.label = 5;
1496
- case 5: return [4 /*yield*/, axios_1.default.get(url)];
1497
- case 6:
1498
- response = _b.sent();
1499
- $_1 = (0, cheerio_1.load)(response.data);
1500
- logger.debug("Finding links on page ".concat(url));
1501
- newLinksFound_1 = 0;
1502
- $_1('a[href]').each(function (_, element) {
1503
- var href = $_1(element).attr('href');
1504
- if (!href || href.startsWith('#') || href.startsWith('mailto:'))
1505
- return;
1506
- var fullUrl = _this.buildUrl(href, url);
1507
- if (fullUrl.startsWith(sourceConfig.url) && !visitedUrls.has(_this.normalizeUrl(fullUrl))) {
1508
- if (!queue.includes(fullUrl)) {
1509
- queue.push(fullUrl);
1510
- newLinksFound_1++;
1511
- }
1512
- }
1513
- });
1514
- logger.debug("Found ".concat(newLinksFound_1, " new links on ").concat(url));
1515
- return [3 /*break*/, 8];
1516
- case 7:
1517
- error_16 = _b.sent();
1518
- logger.error("Failed during link discovery or initial fetch for ".concat(url, ":"), error_16);
1519
- errorCount++;
1520
- return [3 /*break*/, 8];
1521
- case 8: return [2 /*return*/];
1522
- }
1523
- });
1524
- };
1525
- this_1 = this;
1526
- _a.label = 1;
1527
- case 1:
1528
- if (!(queue.length > 0)) return [3 /*break*/, 3];
1529
- return [5 /*yield**/, _loop_2()];
1530
- case 2:
1531
- _a.sent();
1532
- return [3 /*break*/, 1];
1533
- case 3:
1534
- logger.info("Crawl completed. Processed: ".concat(processedCount, ", Skipped (Extension): ").concat(skippedCount, ", Skipped (Size): ").concat(skippedSizeCount, ", Errors: ").concat(errorCount));
1535
- return [2 /*return*/];
1536
- }
1537
- });
1538
- });
1539
- };
1540
- Doc2Vec.prototype.shouldProcessUrl = function (url) {
1541
- var parsedUrl = new URL(url);
1542
- var pathname = parsedUrl.pathname;
1543
- var ext = path.extname(pathname);
1544
- if (!ext)
1545
- return true;
1546
- return ['.html', '.htm'].includes(ext.toLowerCase());
1547
- };
1548
- Doc2Vec.prototype.normalizeUrl = function (url) {
1549
- try {
1550
- var urlObj = new URL(url);
1551
- urlObj.hash = '';
1552
- urlObj.search = '';
1553
- return urlObj.toString();
1554
- }
1555
- catch (error) {
1556
- return url;
1557
- }
1558
- };
1559
- Doc2Vec.prototype.buildUrl = function (href, currentUrl) {
1560
- try {
1561
- return new URL(href, currentUrl).toString();
1562
- }
1563
- catch (error) {
1564
- this.logger.warn("Invalid URL found: ".concat(href));
1565
- return '';
1566
- }
1567
- };
1568
- Doc2Vec.prototype.processPage = function (url, sourceConfig) {
1569
- return __awaiter(this, void 0, void 0, function () {
1570
- var logger, browser, page, htmlContent, dom, document_1, reader, article, cleanHtml, markdown, error_17;
1571
- var _this = this;
1572
- return __generator(this, function (_a) {
1573
- switch (_a.label) {
1574
- case 0:
1575
- logger = this.logger.child('page-processor');
1576
- logger.debug("Processing page content from ".concat(url));
1577
- return [4 /*yield*/, puppeteer_1.default.launch({
1578
- args: ['--no-sandbox', '--disable-setuid-sandbox'],
1579
- })];
1580
- case 1:
1581
- browser = _a.sent();
1582
- _a.label = 2;
1583
- case 2:
1584
- _a.trys.push([2, 10, 11, 14]);
1585
- return [4 /*yield*/, browser.newPage()];
1586
- case 3:
1587
- page = _a.sent();
1588
- logger.debug("Navigating to ".concat(url));
1589
- return [4 /*yield*/, page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 })];
1590
- case 4:
1591
- _a.sent();
1592
- return [4 /*yield*/, page.evaluate(function () {
1593
- var mainContentElement = document_1.querySelector('div[role="main"].document') || document_1.querySelector('main') || document_1.body;
1594
- return mainContentElement.innerHTML;
1595
- })];
1596
- case 5:
1597
- htmlContent = _a.sent();
1598
- if (!(htmlContent.length > sourceConfig.max_size)) return [3 /*break*/, 7];
1599
- logger.warn("Raw HTML content (".concat(htmlContent.length, " chars) exceeds max size (").concat(sourceConfig.max_size, "). Skipping detailed processing for ").concat(url, "."));
1600
- return [4 /*yield*/, browser.close()];
1601
- case 6:
1602
- _a.sent();
1603
- return [2 /*return*/, null];
1604
- case 7:
1605
- logger.debug("Got HTML content (".concat(htmlContent.length, " chars), creating DOM"));
1606
- dom = new jsdom_1.JSDOM(htmlContent);
1607
- document_1 = dom.window.document;
1608
- document_1.querySelectorAll('pre').forEach(function (pre) {
1609
- pre.classList.add('article-content');
1610
- pre.setAttribute('data-readable-content-score', '100');
1611
- _this.markCodeParents(pre.parentElement);
1612
- });
1613
- logger.debug("Applying Readability to extract main content");
1614
- reader = new readability_1.Readability(document_1, {
1615
- charThreshold: 20,
1616
- classesToPreserve: ['article-content'],
1617
- });
1618
- article = reader.parse();
1619
- if (!!article) return [3 /*break*/, 9];
1620
- logger.warn("Failed to parse article content with Readability for ".concat(url));
1621
- return [4 /*yield*/, browser.close()];
1622
- case 8:
1623
- _a.sent();
1624
- return [2 /*return*/, null];
1625
- case 9:
1626
- logger.debug("Sanitizing HTML (".concat(article.content.length, " chars)"));
1627
- cleanHtml = (0, sanitize_html_1.default)(article.content, {
1628
- allowedTags: [
1629
- 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', 'ul', 'ol',
1630
- 'li', 'b', 'i', 'strong', 'em', 'code', 'pre',
1631
- 'div', 'span', 'table', 'thead', 'tbody', 'tr', 'th', 'td'
1632
- ],
1633
- allowedAttributes: {
1634
- 'a': ['href'],
1635
- 'pre': ['class', 'data-language'],
1636
- 'code': ['class', 'data-language'],
1637
- 'div': ['class'],
1638
- 'span': ['class']
1639
- }
1640
- });
1641
- logger.debug("Converting HTML to Markdown");
1642
- markdown = this.turndownService.turndown(cleanHtml);
1643
- logger.debug("Markdown conversion complete (".concat(markdown.length, " chars)"));
1644
- return [2 /*return*/, markdown];
1645
- case 10:
1646
- error_17 = _a.sent();
1647
- logger.error("Error processing page ".concat(url, ":"), error_17);
1648
- return [2 /*return*/, null];
1649
- case 11:
1650
- if (!(browser && browser.isConnected())) return [3 /*break*/, 13];
1651
- return [4 /*yield*/, browser.close()];
1652
- case 12:
1653
- _a.sent();
1654
- logger.debug("Browser closed for ".concat(url));
1655
- _a.label = 13;
1656
- case 13: return [7 /*endfinally*/];
1657
- case 14: return [2 /*return*/];
1658
- }
1659
- });
1660
- });
1661
- };
1662
- Doc2Vec.prototype.markCodeParents = function (node) {
1663
- if (!node)
1664
- return;
1665
- if (node.querySelector('pre, code')) {
1666
- node.classList.add('article-content');
1667
- node.setAttribute('data-readable-content-score', '100');
1668
- }
1669
- this.markCodeParents(node.parentElement);
1670
- };
1671
- Doc2Vec.prototype.chunkMarkdown = function (markdown, sourceConfig, url) {
1672
- return __awaiter(this, void 0, void 0, function () {
1673
- var logger, MAX_TOKENS, chunks, lines, currentChunk, headingHierarchy, processChunk, createDocumentChunk, _i, lines_2, line, levelMatch, level, heading;
1674
- var _this = this;
1675
- return __generator(this, function (_a) {
1676
- logger = this.logger.child('chunker');
1677
- logger.debug("Chunking markdown from ".concat(url, " (").concat(markdown.length, " chars)"));
1678
- MAX_TOKENS = 1000;
1679
- chunks = [];
1680
- lines = markdown.split("\n");
1681
- currentChunk = "";
1682
- headingHierarchy = [];
1683
- processChunk = function () {
1684
- if (currentChunk.trim()) {
1685
- var tokens = _this.tokenize(currentChunk);
1686
- if (tokens.length > MAX_TOKENS) {
1687
- logger.debug("Chunk exceeds max token count (".concat(tokens.length, "), splitting into smaller chunks"));
1688
- var subChunk = "";
1689
- var tokenCount = 0;
1690
- var overlapSize = Math.floor(MAX_TOKENS * 0.05);
1691
- var lastTokens = [];
1692
- for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
1693
- var token = tokens_1[_i];
1694
- if (tokenCount + 1 > MAX_TOKENS) {
1695
- chunks.push(createDocumentChunk(subChunk, headingHierarchy));
1696
- subChunk = lastTokens.join("") + token;
1697
- tokenCount = lastTokens.length + 1;
1698
- lastTokens = [];
1699
- }
1700
- else {
1701
- subChunk += token;
1702
- tokenCount++;
1703
- lastTokens.push(token);
1704
- if (lastTokens.length > overlapSize) {
1705
- lastTokens.shift();
1706
- }
1707
- }
1708
- }
1709
- if (subChunk) {
1710
- chunks.push(createDocumentChunk(subChunk, headingHierarchy));
1711
- }
1712
- }
1713
- else {
1714
- chunks.push(createDocumentChunk(currentChunk, headingHierarchy));
1715
- }
1716
- }
1717
- currentChunk = "";
1718
- };
1719
- createDocumentChunk = function (content, hierarchy) {
1720
- var chunkId = _this.generateHash(content);
1721
- logger.debug("Created chunk ".concat(chunkId.substring(0, 8), "... with ").concat(content.length, " chars"));
1722
- return {
1723
- content: content,
1724
- metadata: {
1725
- product_name: sourceConfig.product_name,
1726
- version: sourceConfig.version,
1727
- heading_hierarchy: __spreadArray([], hierarchy, true),
1728
- section: hierarchy[hierarchy.length - 1] || "Introduction",
1729
- chunk_id: chunkId,
1730
- url: url,
1731
- hash: _this.generateHash(content)
1732
- }
1733
- };
1734
- };
1735
- for (_i = 0, lines_2 = lines; _i < lines_2.length; _i++) {
1736
- line = lines_2[_i];
1737
- if (line.startsWith("#")) {
1738
- processChunk();
1739
- levelMatch = line.match(/^(#+)/);
1740
- level = levelMatch ? levelMatch[1].length : 1;
1741
- heading = line.replace(/^#+\s*/, "").trim();
1742
- logger.debug("Found heading (level ".concat(level, "): ").concat(heading));
1743
- while (headingHierarchy.length < level - 1) {
1744
- headingHierarchy.push("");
1745
- }
1746
- if (level <= headingHierarchy.length) {
1747
- headingHierarchy = headingHierarchy.slice(0, level - 1);
1748
- }
1749
- headingHierarchy[level - 1] = heading;
1750
- }
1751
- else {
1752
- currentChunk += "".concat(line, "\n");
1753
- }
1754
- }
1755
- processChunk();
1756
- logger.debug("Chunking complete, created ".concat(chunks.length, " chunks"));
1757
- return [2 /*return*/, chunks];
1758
- });
1759
- });
1760
- };
1761
- Doc2Vec.prototype.tokenize = function (text) {
1762
- return text.split(/(\s+)/).filter(function (token) { return token.length > 0; });
1763
- };
1764
- Doc2Vec.prototype.createEmbeddings = function (texts) {
1765
- return __awaiter(this, void 0, void 0, function () {
1766
- var logger, response, error_18;
1767
- return __generator(this, function (_a) {
1768
- switch (_a.label) {
1769
- case 0:
1770
- logger = this.logger.child('embeddings');
1771
- _a.label = 1;
1772
- case 1:
1773
- _a.trys.push([1, 3, , 4]);
1774
- logger.debug("Creating embeddings for ".concat(texts.length, " texts"));
1775
- return [4 /*yield*/, this.openai.embeddings.create({
1776
- model: "text-embedding-3-large",
1777
- input: texts,
1778
- })];
1779
- case 2:
1780
- response = _a.sent();
1781
- logger.debug("Successfully created ".concat(response.data.length, " embeddings"));
1782
- return [2 /*return*/, response.data.map(function (d) { return d.embedding; })];
1783
- case 3:
1784
- error_18 = _a.sent();
1785
- logger.error('Failed to create embeddings:', error_18);
1786
- return [2 /*return*/, []];
1787
- case 4: return [2 /*return*/];
1788
- }
1789
- });
1790
- });
1791
- };
1792
- Doc2Vec.prototype.generateHash = function (content) {
1793
- return crypto_1.default.createHash("sha256").update(content).digest("hex");
1794
- };
1795
- return Doc2Vec;
1796
- }());
1797
- if (require.main === module) {
1798
- var configPath = process.argv[2] || 'config.yaml';
1799
- if (!fs.existsSync(configPath)) {
1800
- console.error('Please provide a valid path to a YAML config file.');
1801
- process.exit(1);
1802
- }
1803
- var doc2Vec = new Doc2Vec(configPath);
1804
- doc2Vec.run().catch(console.error);
1805
- }