doc2vec 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/content-processor.js +1 -1
- package/dist/database.js +11 -13
- package/dist/doc2vec.js +30 -14
- package/dist/utils.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -145,6 +145,9 @@ Configuration is managed through two files:
|
|
|
145
145
|
OPENAI_API_KEY="sk-..."
|
|
146
146
|
OPENAI_MODEL="text-embedding-3-large" # Optional, defaults to text-embedding-3-large
|
|
147
147
|
|
|
148
|
+
# Optional: Embedding dimension size (defaults to 3072)
|
|
149
|
+
EMBEDDING_DIMENSION="3072"
|
|
150
|
+
|
|
148
151
|
# Required: Your Azure OpenAI credentials (if using Azure provider)
|
|
149
152
|
AZURE_OPENAI_KEY="your-azure-key"
|
|
150
153
|
AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com"
|
|
@@ -223,6 +226,10 @@ Configuration is managed through two files:
|
|
|
223
226
|
* `qdrant_port`: (Optional) Port for the Qdrant REST API. Defaults to `443` if `qdrant_url` starts with `https`, otherwise `6333`.
|
|
224
227
|
* `collection_name`: (Optional) Name of the Qdrant collection to use. Defaults to `<product_name>_<version>` (lowercased, spaces replaced with underscores).
|
|
225
228
|
|
|
229
|
+
Optional embedding configuration:
|
|
230
|
+
* `embedding.provider`: Provider for embeddings (`openai` or `azure`).
|
|
231
|
+
* `embedding.dimension`: Embedding vector size. Defaults to `3072` when not set.
|
|
232
|
+
|
|
226
233
|
**Example (`config.yaml`):**
|
|
227
234
|
```yaml
|
|
228
235
|
# Optional: Configure embedding provider
|
|
@@ -230,6 +237,7 @@ Configuration is managed through two files:
|
|
|
230
237
|
# Defaults to OpenAI if not specified
|
|
231
238
|
embedding:
|
|
232
239
|
provider: 'openai' # or 'azure'
|
|
240
|
+
dimension: 3072 # Optional, defaults to 3072
|
|
233
241
|
openai:
|
|
234
242
|
api_key: '${OPENAI_API_KEY}' # Optional, uses env var by default
|
|
235
243
|
model: 'text-embedding-3-large' # Optional, defaults to text-embedding-3-large
|
|
@@ -586,7 +586,7 @@ class ContentProcessor {
|
|
|
586
586
|
logger.debug(`Finding links on page ${url}`);
|
|
587
587
|
let newLinksFound = 0;
|
|
588
588
|
for (const href of result.links) {
|
|
589
|
-
const fullUrl = utils_1.Utils.buildUrl(href, pageUrlForLinks);
|
|
589
|
+
const fullUrl = utils_1.Utils.buildUrl(href, pageUrlForLinks, logger);
|
|
590
590
|
if (fullUrl.startsWith(sourceConfig.url)) {
|
|
591
591
|
addReferrer(fullUrl, pageUrlForLinks);
|
|
592
592
|
if (!visitedUrls.has(utils_1.Utils.normalizeUrl(fullUrl))) {
|
package/dist/database.js
CHANGED
|
@@ -44,7 +44,7 @@ const sqliteVec = __importStar(require("sqlite-vec"));
|
|
|
44
44
|
const js_client_rest_1 = require("@qdrant/js-client-rest");
|
|
45
45
|
const utils_1 = require("./utils");
|
|
46
46
|
class DatabaseManager {
|
|
47
|
-
static async initDatabase(config, parentLogger) {
|
|
47
|
+
static async initDatabase(config, parentLogger, embeddingDimension) {
|
|
48
48
|
const logger = parentLogger.child('database');
|
|
49
49
|
const dbConfig = config.database_config;
|
|
50
50
|
if (dbConfig.type === 'sqlite') {
|
|
@@ -53,10 +53,10 @@ class DatabaseManager {
|
|
|
53
53
|
logger.info(`Opening SQLite database at ${dbPath}`);
|
|
54
54
|
const db = new better_sqlite3_1.default(dbPath, { allowExtension: true });
|
|
55
55
|
sqliteVec.load(db);
|
|
56
|
-
logger.debug(`Creating vec_items table if it doesn't exist`);
|
|
56
|
+
logger.debug(`Creating vec_items table if it doesn't exist (dimension: ${embeddingDimension})`);
|
|
57
57
|
db.exec(`
|
|
58
58
|
CREATE VIRTUAL TABLE IF NOT EXISTS vec_items USING vec0(
|
|
59
|
-
embedding FLOAT[
|
|
59
|
+
embedding FLOAT[${embeddingDimension}],
|
|
60
60
|
product_name TEXT,
|
|
61
61
|
version TEXT,
|
|
62
62
|
branch TEXT,
|
|
@@ -81,7 +81,7 @@ class DatabaseManager {
|
|
|
81
81
|
const collectionName = params.collection_name || `${config.product_name.toLowerCase().replace(/\s+/g, '_')}_${config.version}`;
|
|
82
82
|
logger.info(`Connecting to Qdrant at ${qdrantUrl}:${qdrantPort}, collection: ${collectionName}`);
|
|
83
83
|
const qdrantClient = new js_client_rest_1.QdrantClient({ url: qdrantUrl, apiKey: process.env.QDRANT_API_KEY, port: qdrantPort });
|
|
84
|
-
await this.createCollectionQdrant(qdrantClient, collectionName, logger);
|
|
84
|
+
await this.createCollectionQdrant(qdrantClient, collectionName, logger, embeddingDimension);
|
|
85
85
|
logger.info(`Qdrant connection established successfully`);
|
|
86
86
|
return { client: qdrantClient, collectionName, type: 'qdrant' };
|
|
87
87
|
}
|
|
@@ -91,7 +91,7 @@ class DatabaseManager {
|
|
|
91
91
|
throw new Error(errMsg);
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
-
static async createCollectionQdrant(qdrantClient, collectionName, logger) {
|
|
94
|
+
static async createCollectionQdrant(qdrantClient, collectionName, logger, embeddingDimension) {
|
|
95
95
|
try {
|
|
96
96
|
logger.debug(`Checking if collection ${collectionName} exists`);
|
|
97
97
|
const collections = await qdrantClient.getCollections();
|
|
@@ -100,10 +100,10 @@ class DatabaseManager {
|
|
|
100
100
|
logger.info(`Collection ${collectionName} already exists`);
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
|
-
logger.info(`Creating new collection ${collectionName}`);
|
|
103
|
+
logger.info(`Creating new collection ${collectionName} with dimension ${embeddingDimension}`);
|
|
104
104
|
await qdrantClient.createCollection(collectionName, {
|
|
105
105
|
vectors: {
|
|
106
|
-
size:
|
|
106
|
+
size: embeddingDimension,
|
|
107
107
|
distance: "Cosine",
|
|
108
108
|
},
|
|
109
109
|
});
|
|
@@ -181,7 +181,7 @@ class DatabaseManager {
|
|
|
181
181
|
}
|
|
182
182
|
return defaultValue;
|
|
183
183
|
}
|
|
184
|
-
static async setMetadataValue(dbConnection, key, value, logger) {
|
|
184
|
+
static async setMetadataValue(dbConnection, key, value, logger, embeddingDimension) {
|
|
185
185
|
try {
|
|
186
186
|
if (dbConnection.type === 'sqlite') {
|
|
187
187
|
const stmt = dbConnection.db.prepare(`
|
|
@@ -193,8 +193,7 @@ class DatabaseManager {
|
|
|
193
193
|
}
|
|
194
194
|
else if (dbConnection.type === 'qdrant') {
|
|
195
195
|
const metadataUUID = utils_1.Utils.generateMetadataUUID(key);
|
|
196
|
-
const
|
|
197
|
-
const dummyEmbedding = new Array(dummyEmbeddingSize).fill(0);
|
|
196
|
+
const dummyEmbedding = new Array(embeddingDimension).fill(0);
|
|
198
197
|
const metadataPoint = {
|
|
199
198
|
id: metadataUUID,
|
|
200
199
|
vector: dummyEmbedding,
|
|
@@ -258,7 +257,7 @@ class DatabaseManager {
|
|
|
258
257
|
logger.info(`No saved run date found for ${repo}, using default: ${defaultDate}`);
|
|
259
258
|
return defaultDate;
|
|
260
259
|
}
|
|
261
|
-
static async updateLastRunDate(dbConnection, repo, logger) {
|
|
260
|
+
static async updateLastRunDate(dbConnection, repo, logger, embeddingDimension) {
|
|
262
261
|
const now = new Date().toISOString();
|
|
263
262
|
try {
|
|
264
263
|
if (dbConnection.type === 'sqlite') {
|
|
@@ -276,8 +275,7 @@ class DatabaseManager {
|
|
|
276
275
|
const metadataKey = `last_run_${repo.replace('/', '_')}`;
|
|
277
276
|
logger.debug(`Using UUID: ${metadataUUID} for metadata`);
|
|
278
277
|
// Generate a dummy embedding (all zeros)
|
|
279
|
-
const
|
|
280
|
-
const dummyEmbedding = new Array(dummyEmbeddingSize).fill(0);
|
|
278
|
+
const dummyEmbedding = new Array(embeddingDimension).fill(0);
|
|
281
279
|
// Create a point with special metadata payload
|
|
282
280
|
const metadataPoint = {
|
|
283
281
|
id: metadataUUID,
|
package/dist/doc2vec.js
CHANGED
|
@@ -72,6 +72,7 @@ class Doc2Vec {
|
|
|
72
72
|
// Check environment variable if not specified in config
|
|
73
73
|
const embeddingProvider = this.config.embedding?.provider || process.env.EMBEDDING_PROVIDER || 'openai';
|
|
74
74
|
const embeddingConfig = this.config.embedding || { provider: embeddingProvider };
|
|
75
|
+
this.embeddingDimension = this.resolveEmbeddingDimension(embeddingConfig);
|
|
75
76
|
if (embeddingProvider === 'azure') {
|
|
76
77
|
const azureApiKey = embeddingConfig.azure?.api_key || process.env.AZURE_OPENAI_KEY;
|
|
77
78
|
const azureEndpoint = embeddingConfig.azure?.endpoint || process.env.AZURE_OPENAI_ENDPOINT;
|
|
@@ -88,7 +89,7 @@ class Doc2Vec {
|
|
|
88
89
|
apiVersion: azureApiVersion,
|
|
89
90
|
});
|
|
90
91
|
this.embeddingModel = azureDeploymentName;
|
|
91
|
-
this.logger.info(`Using Azure OpenAI with deployment: ${azureDeploymentName}`);
|
|
92
|
+
this.logger.info(`Using Azure OpenAI with deployment: ${azureDeploymentName} (${this.embeddingDimension} dimensions)`);
|
|
92
93
|
}
|
|
93
94
|
else {
|
|
94
95
|
const openaiApiKey = embeddingConfig.openai?.api_key || process.env.OPENAI_API_KEY;
|
|
@@ -99,7 +100,7 @@ class Doc2Vec {
|
|
|
99
100
|
}
|
|
100
101
|
this.openai = new openai_1.OpenAI({ apiKey: openaiApiKey });
|
|
101
102
|
this.embeddingModel = openaiModel;
|
|
102
|
-
this.logger.info(`Using OpenAI with model: ${openaiModel}`);
|
|
103
|
+
this.logger.info(`Using OpenAI with model: ${openaiModel} (${this.embeddingDimension} dimensions)`);
|
|
103
104
|
}
|
|
104
105
|
this.contentProcessor = new content_processor_1.ContentProcessor(this.logger);
|
|
105
106
|
}
|
|
@@ -144,6 +145,21 @@ class Doc2Vec {
|
|
|
144
145
|
process.exit(1);
|
|
145
146
|
}
|
|
146
147
|
}
|
|
148
|
+
resolveEmbeddingDimension(embeddingConfig) {
|
|
149
|
+
const defaultDimension = 3072;
|
|
150
|
+
const rawConfigValue = embeddingConfig?.dimension;
|
|
151
|
+
const rawEnvValue = process.env.EMBEDDING_DIMENSION;
|
|
152
|
+
const candidate = rawConfigValue ?? (rawEnvValue ? Number(rawEnvValue) : undefined);
|
|
153
|
+
if (candidate === undefined) {
|
|
154
|
+
return defaultDimension;
|
|
155
|
+
}
|
|
156
|
+
const parsedValue = typeof candidate === 'string' ? Number(candidate) : candidate;
|
|
157
|
+
if (!Number.isFinite(parsedValue) || parsedValue <= 0 || !Number.isInteger(parsedValue)) {
|
|
158
|
+
this.logger.warn(`Invalid embedding dimension provided (${candidate}), falling back to ${defaultDimension}`);
|
|
159
|
+
return defaultDimension;
|
|
160
|
+
}
|
|
161
|
+
return parsedValue;
|
|
162
|
+
}
|
|
147
163
|
async run() {
|
|
148
164
|
this.logger.section('PROCESSING SOURCES');
|
|
149
165
|
for (const sourceConfig of this.config.sources) {
|
|
@@ -373,13 +389,13 @@ class Doc2Vec {
|
|
|
373
389
|
await processIssue(issues[i]);
|
|
374
390
|
}
|
|
375
391
|
// Update the last run date in the database after processing all issues
|
|
376
|
-
await database_1.DatabaseManager.updateLastRunDate(dbConnection, repo, logger);
|
|
392
|
+
await database_1.DatabaseManager.updateLastRunDate(dbConnection, repo, logger, this.embeddingDimension);
|
|
377
393
|
logger.info(`Successfully processed ${issues.length} issues`);
|
|
378
394
|
}
|
|
379
395
|
async processGithubRepo(config, parentLogger) {
|
|
380
396
|
const logger = parentLogger.child('process');
|
|
381
397
|
logger.info(`Starting processing for GitHub repo: ${config.repo}`);
|
|
382
|
-
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger);
|
|
398
|
+
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger, this.embeddingDimension);
|
|
383
399
|
// Initialize metadata storage
|
|
384
400
|
await database_1.DatabaseManager.initDatabaseMetadata(dbConnection, logger);
|
|
385
401
|
logger.section('GITHUB ISSUES');
|
|
@@ -390,7 +406,7 @@ class Doc2Vec {
|
|
|
390
406
|
async processWebsite(config, parentLogger) {
|
|
391
407
|
const logger = parentLogger.child('process');
|
|
392
408
|
logger.info(`Starting processing for website: ${config.url}`);
|
|
393
|
-
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger);
|
|
409
|
+
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger, this.embeddingDimension);
|
|
394
410
|
await database_1.DatabaseManager.initDatabaseMetadata(dbConnection, logger);
|
|
395
411
|
const validChunkIds = new Set();
|
|
396
412
|
const visitedUrls = new Set();
|
|
@@ -420,7 +436,7 @@ class Doc2Vec {
|
|
|
420
436
|
return database_1.DatabaseManager.getMetadataValue(dbConnection, `etag:${url}`, undefined, logger);
|
|
421
437
|
},
|
|
422
438
|
set: async (url, etag) => {
|
|
423
|
-
await database_1.DatabaseManager.setMetadataValue(dbConnection, `etag:${url}`, etag, logger);
|
|
439
|
+
await database_1.DatabaseManager.setMetadataValue(dbConnection, `etag:${url}`, etag, logger, this.embeddingDimension);
|
|
424
440
|
},
|
|
425
441
|
};
|
|
426
442
|
const lastmodStore = {
|
|
@@ -428,7 +444,7 @@ class Doc2Vec {
|
|
|
428
444
|
return database_1.DatabaseManager.getMetadataValue(dbConnection, `lastmod:${url}`, undefined, logger);
|
|
429
445
|
},
|
|
430
446
|
set: async (url, lastmod) => {
|
|
431
|
-
await database_1.DatabaseManager.setMetadataValue(dbConnection, `lastmod:${url}`, lastmod, logger);
|
|
447
|
+
await database_1.DatabaseManager.setMetadataValue(dbConnection, `lastmod:${url}`, lastmod, logger, this.embeddingDimension);
|
|
432
448
|
},
|
|
433
449
|
};
|
|
434
450
|
const crawlResult = await this.contentProcessor.crawlWebsite(config.url, config, async (url, content) => {
|
|
@@ -502,7 +518,7 @@ class Doc2Vec {
|
|
|
502
518
|
async processLocalDirectory(config, parentLogger) {
|
|
503
519
|
const logger = parentLogger.child('process');
|
|
504
520
|
logger.info(`Starting processing for local directory: ${config.path}`);
|
|
505
|
-
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger);
|
|
521
|
+
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger, this.embeddingDimension);
|
|
506
522
|
const validChunkIds = new Set();
|
|
507
523
|
const processedFiles = new Set();
|
|
508
524
|
logger.section('FILE SCANNING AND EMBEDDING');
|
|
@@ -560,7 +576,7 @@ class Doc2Vec {
|
|
|
560
576
|
async processCodeSource(config, parentLogger) {
|
|
561
577
|
const logger = parentLogger.child('process');
|
|
562
578
|
logger.info(`Starting processing for code source (${config.source})`);
|
|
563
|
-
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger);
|
|
579
|
+
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger, this.embeddingDimension);
|
|
564
580
|
const validChunkIds = new Set();
|
|
565
581
|
const processedFiles = new Set();
|
|
566
582
|
let basePath;
|
|
@@ -692,10 +708,10 @@ class Doc2Vec {
|
|
|
692
708
|
await database_1.DatabaseManager.removeChunksByUrlQdrant(dbConnection, url, logger);
|
|
693
709
|
}
|
|
694
710
|
}
|
|
695
|
-
await database_1.DatabaseManager.setMetadataValue(dbConnection, fileListKey, JSON.stringify(currentList), logger);
|
|
711
|
+
await database_1.DatabaseManager.setMetadataValue(dbConnection, fileListKey, JSON.stringify(currentList), logger, this.embeddingDimension);
|
|
696
712
|
if (lastMtimeKey) {
|
|
697
713
|
const nextMtime = maxObservedMtime > 0 ? maxObservedMtime : Date.now();
|
|
698
|
-
await database_1.DatabaseManager.setMetadataValue(dbConnection, lastMtimeKey, `${nextMtime}`, logger);
|
|
714
|
+
await database_1.DatabaseManager.setMetadataValue(dbConnection, lastMtimeKey, `${nextMtime}`, logger, this.embeddingDimension);
|
|
699
715
|
}
|
|
700
716
|
}
|
|
701
717
|
}
|
|
@@ -713,7 +729,7 @@ class Doc2Vec {
|
|
|
713
729
|
const headSha = await this.getRepoHeadSha(basePath, logger);
|
|
714
730
|
if (headSha) {
|
|
715
731
|
const shaKey = this.buildCodeShaMetadataKey(config.repo, repoBranch);
|
|
716
|
-
await database_1.DatabaseManager.setMetadataValue(dbConnection, shaKey, headSha, logger);
|
|
732
|
+
await database_1.DatabaseManager.setMetadataValue(dbConnection, shaKey, headSha, logger, this.embeddingDimension);
|
|
717
733
|
}
|
|
718
734
|
}
|
|
719
735
|
if (tempDir) {
|
|
@@ -876,7 +892,7 @@ class Doc2Vec {
|
|
|
876
892
|
async processZendesk(config, parentLogger) {
|
|
877
893
|
const logger = parentLogger.child('process');
|
|
878
894
|
logger.info(`Starting processing for Zendesk: ${config.zendesk_subdomain}.zendesk.com`);
|
|
879
|
-
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger);
|
|
895
|
+
const dbConnection = await database_1.DatabaseManager.initDatabase(config, logger, this.embeddingDimension);
|
|
880
896
|
// Initialize metadata storage
|
|
881
897
|
await database_1.DatabaseManager.initDatabaseMetadata(dbConnection, logger);
|
|
882
898
|
const fetchTickets = config.fetch_tickets !== false; // default true
|
|
@@ -1051,7 +1067,7 @@ class Doc2Vec {
|
|
|
1051
1067
|
}
|
|
1052
1068
|
}
|
|
1053
1069
|
// Update the last run date in the database
|
|
1054
|
-
await database_1.DatabaseManager.updateLastRunDate(dbConnection, `zendesk_tickets_${config.zendesk_subdomain}`, logger);
|
|
1070
|
+
await database_1.DatabaseManager.updateLastRunDate(dbConnection, `zendesk_tickets_${config.zendesk_subdomain}`, logger, this.embeddingDimension);
|
|
1055
1071
|
logger.info(`Successfully processed ${totalTickets} tickets`);
|
|
1056
1072
|
}
|
|
1057
1073
|
async fetchAndProcessZendeskArticles(config, dbConnection, logger) {
|
package/dist/utils.js
CHANGED
|
@@ -69,12 +69,14 @@ class Utils {
|
|
|
69
69
|
return url;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
static buildUrl(href, currentUrl) {
|
|
72
|
+
static buildUrl(href, currentUrl, logger) {
|
|
73
73
|
try {
|
|
74
74
|
return new URL(href, currentUrl).toString();
|
|
75
75
|
}
|
|
76
76
|
catch (error) {
|
|
77
|
-
|
|
77
|
+
if (logger) {
|
|
78
|
+
logger.warn(`Invalid URL found: ${href}`);
|
|
79
|
+
}
|
|
78
80
|
return '';
|
|
79
81
|
}
|
|
80
82
|
}
|