doc2vec 1.3.0 → 2.0.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 +153 -4
- package/dist/content-processor copy.js +819 -0
- package/dist/content-processor.js +283 -60
- package/dist/database.js +13 -17
- package/dist/doc2vec.js +48 -6
- package/dist/electron-app/src/database.js +218 -0
- package/dist/electron-app/src/embeddings.js +80 -0
- package/dist/electron-app/src/main.js +627 -0
- package/dist/electron-app/src/mcp-server.js +496 -0
- package/dist/electron-app/src/preload.js +24 -0
- package/dist/electron-app/src/processor.js +248 -0
- package/dist/electron-app/src/syncer.js +146 -0
- package/dist/macos-app/migrate-to-vec0.js +156 -0
- package/package.json +4 -2
- package/dist/embedding-providers.js +0 -157
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.DatabaseManager = void 0;
|
|
40
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
41
|
+
const sqliteVec = __importStar(require("sqlite-vec"));
|
|
42
|
+
class DatabaseManager {
|
|
43
|
+
constructor(dbPath) {
|
|
44
|
+
// Cache counts for immediate UI feedback
|
|
45
|
+
this._trackedFilesCount = 0;
|
|
46
|
+
this._totalChunksCount = 0;
|
|
47
|
+
this.db = new better_sqlite3_1.default(dbPath);
|
|
48
|
+
sqliteVec.load(this.db);
|
|
49
|
+
this.createTables();
|
|
50
|
+
// Prepare statements for insert/update (matching doc2vec's approach)
|
|
51
|
+
this.insertStmt = this.db.prepare(`
|
|
52
|
+
INSERT INTO vec_items (embedding, version, heading_hierarchy, section, chunk_id, content, url, hash, chunk_index, total_chunks)
|
|
53
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
54
|
+
`);
|
|
55
|
+
this.updateStmt = this.db.prepare(`
|
|
56
|
+
UPDATE vec_items SET embedding = ?, version = ?, heading_hierarchy = ?, section = ?, content = ?, url = ?, hash = ?, chunk_index = ?, total_chunks = ?
|
|
57
|
+
WHERE chunk_id = ?
|
|
58
|
+
`);
|
|
59
|
+
// Initialize counts from database
|
|
60
|
+
this._trackedFilesCount = this._queryTrackedFilesCount();
|
|
61
|
+
this._totalChunksCount = this._queryTotalChunksCount();
|
|
62
|
+
console.log(`Database initialized at: ${dbPath}`);
|
|
63
|
+
}
|
|
64
|
+
_queryTrackedFilesCount() {
|
|
65
|
+
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM files');
|
|
66
|
+
const row = stmt.get();
|
|
67
|
+
return Number(row.count);
|
|
68
|
+
}
|
|
69
|
+
_queryTotalChunksCount() {
|
|
70
|
+
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM vec_items');
|
|
71
|
+
const row = stmt.get();
|
|
72
|
+
return Number(row.count);
|
|
73
|
+
}
|
|
74
|
+
createTables() {
|
|
75
|
+
// Create vec0 virtual table (sqlite-vec)
|
|
76
|
+
this.db.exec(`
|
|
77
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS vec_items USING vec0(
|
|
78
|
+
embedding FLOAT[3072],
|
|
79
|
+
version TEXT,
|
|
80
|
+
heading_hierarchy TEXT,
|
|
81
|
+
section TEXT,
|
|
82
|
+
chunk_id TEXT UNIQUE,
|
|
83
|
+
content TEXT,
|
|
84
|
+
url TEXT,
|
|
85
|
+
hash TEXT,
|
|
86
|
+
chunk_index INTEGER,
|
|
87
|
+
total_chunks INTEGER
|
|
88
|
+
)
|
|
89
|
+
`);
|
|
90
|
+
// Create files tracking table
|
|
91
|
+
this.db.exec(`
|
|
92
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
93
|
+
path TEXT PRIMARY KEY,
|
|
94
|
+
hash TEXT,
|
|
95
|
+
modified_at TEXT,
|
|
96
|
+
chunk_count INTEGER
|
|
97
|
+
)
|
|
98
|
+
`);
|
|
99
|
+
console.log('Database tables created');
|
|
100
|
+
}
|
|
101
|
+
insertChunk(chunk, embedding) {
|
|
102
|
+
const embeddingData = embedding ? new Float32Array(embedding) : new Float32Array(3072).fill(0);
|
|
103
|
+
// Use JSON.stringify for heading_hierarchy to match doc2vec format
|
|
104
|
+
const headingHierarchyJson = JSON.stringify(chunk.headingHierarchy);
|
|
105
|
+
try {
|
|
106
|
+
this.insertStmt.run(embeddingData, chunk.version, headingHierarchyJson, chunk.section, chunk.chunkId, chunk.content, chunk.url, chunk.hash, BigInt(chunk.chunkIndex), BigInt(chunk.totalChunks));
|
|
107
|
+
this._totalChunksCount++;
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
// If insert fails due to UNIQUE constraint, update instead
|
|
111
|
+
if (error.code === 'SQLITE_CONSTRAINT_UNIQUE' || error.message?.includes('UNIQUE constraint failed')) {
|
|
112
|
+
this.updateStmt.run(embeddingData, chunk.version, headingHierarchyJson, chunk.section, chunk.content, chunk.url, chunk.hash, BigInt(chunk.chunkIndex), BigInt(chunk.totalChunks), chunk.chunkId);
|
|
113
|
+
// Update doesn't change count
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
removeChunksForFile(filePath) {
|
|
121
|
+
const url = `file://${filePath}`;
|
|
122
|
+
// Get count before delete
|
|
123
|
+
const countStmt = this.db.prepare('SELECT COUNT(*) as count FROM vec_items WHERE url = ?');
|
|
124
|
+
const countRow = countStmt.get(url);
|
|
125
|
+
const deletedCount = Number(countRow.count);
|
|
126
|
+
const stmt = this.db.prepare('DELETE FROM vec_items WHERE url = ?');
|
|
127
|
+
stmt.run(url);
|
|
128
|
+
this._totalChunksCount -= deletedCount;
|
|
129
|
+
}
|
|
130
|
+
getExistingChunkHash(chunkId) {
|
|
131
|
+
const stmt = this.db.prepare('SELECT hash FROM vec_items WHERE chunk_id = ?');
|
|
132
|
+
const row = stmt.get(chunkId);
|
|
133
|
+
return row?.hash ?? null;
|
|
134
|
+
}
|
|
135
|
+
getTotalChunksCount() {
|
|
136
|
+
return this._totalChunksCount;
|
|
137
|
+
}
|
|
138
|
+
upsertFileInfo(filePath, hash, modifiedAt, chunkCount) {
|
|
139
|
+
// Check if file already exists
|
|
140
|
+
const checkStmt = this.db.prepare('SELECT 1 FROM files WHERE path = ?');
|
|
141
|
+
const exists = checkStmt.get(filePath);
|
|
142
|
+
const stmt = this.db.prepare(`
|
|
143
|
+
INSERT OR REPLACE INTO files (path, hash, modified_at, chunk_count)
|
|
144
|
+
VALUES (?, ?, ?, ?)
|
|
145
|
+
`);
|
|
146
|
+
stmt.run(filePath, hash, modifiedAt.toISOString(), chunkCount);
|
|
147
|
+
// Only increment if this is a new file
|
|
148
|
+
if (!exists) {
|
|
149
|
+
this._trackedFilesCount++;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
getFileInfo(filePath) {
|
|
153
|
+
const stmt = this.db.prepare('SELECT * FROM files WHERE path = ?');
|
|
154
|
+
const row = stmt.get(filePath);
|
|
155
|
+
if (!row)
|
|
156
|
+
return null;
|
|
157
|
+
return {
|
|
158
|
+
path: row.path,
|
|
159
|
+
hash: row.hash,
|
|
160
|
+
modifiedAt: new Date(row.modified_at),
|
|
161
|
+
chunkCount: row.chunk_count
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
removeFileInfo(filePath) {
|
|
165
|
+
// Check if file exists before deleting
|
|
166
|
+
const checkStmt = this.db.prepare('SELECT 1 FROM files WHERE path = ?');
|
|
167
|
+
const exists = checkStmt.get(filePath);
|
|
168
|
+
const stmt = this.db.prepare('DELETE FROM files WHERE path = ?');
|
|
169
|
+
stmt.run(filePath);
|
|
170
|
+
if (exists) {
|
|
171
|
+
this._trackedFilesCount--;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
getAllTrackedFiles() {
|
|
175
|
+
const stmt = this.db.prepare('SELECT path FROM files');
|
|
176
|
+
const rows = stmt.all();
|
|
177
|
+
return rows.map(r => r.path);
|
|
178
|
+
}
|
|
179
|
+
getAllTrackedFilesWithInfo() {
|
|
180
|
+
const stmt = this.db.prepare('SELECT path, chunk_count, modified_at FROM files ORDER BY modified_at DESC');
|
|
181
|
+
const rows = stmt.all();
|
|
182
|
+
return rows.map(r => ({
|
|
183
|
+
path: r.path,
|
|
184
|
+
chunkCount: r.chunk_count,
|
|
185
|
+
modifiedAt: r.modified_at
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
getChunksForFile(filePath) {
|
|
189
|
+
const url = `file://${filePath}`;
|
|
190
|
+
const stmt = this.db.prepare(`
|
|
191
|
+
SELECT chunk_id, content, section, chunk_index
|
|
192
|
+
FROM vec_items
|
|
193
|
+
WHERE url = ?
|
|
194
|
+
ORDER BY chunk_index
|
|
195
|
+
`);
|
|
196
|
+
const rows = stmt.all(url);
|
|
197
|
+
return rows.map(r => ({
|
|
198
|
+
chunkId: r.chunk_id,
|
|
199
|
+
content: r.content,
|
|
200
|
+
section: r.section,
|
|
201
|
+
chunkIndex: Number(r.chunk_index)
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
getTrackedFilesCount() {
|
|
205
|
+
return this._trackedFilesCount;
|
|
206
|
+
}
|
|
207
|
+
clearAllData() {
|
|
208
|
+
this.db.exec('DELETE FROM vec_items');
|
|
209
|
+
this.db.exec('DELETE FROM files');
|
|
210
|
+
this._trackedFilesCount = 0;
|
|
211
|
+
this._totalChunksCount = 0;
|
|
212
|
+
console.log('Cleared all data');
|
|
213
|
+
}
|
|
214
|
+
close() {
|
|
215
|
+
this.db.close();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
exports.DatabaseManager = DatabaseManager;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.EmbeddingService = exports.InvalidApiKeyError = void 0;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
class InvalidApiKeyError extends Error {
|
|
9
|
+
constructor(message = 'Invalid OpenAI API key') {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'InvalidApiKeyError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.InvalidApiKeyError = InvalidApiKeyError;
|
|
15
|
+
class EmbeddingService {
|
|
16
|
+
constructor(apiKey) {
|
|
17
|
+
this.model = 'text-embedding-3-large';
|
|
18
|
+
this._isValid = true;
|
|
19
|
+
this.client = new openai_1.default({ apiKey });
|
|
20
|
+
}
|
|
21
|
+
get isValid() {
|
|
22
|
+
return this._isValid;
|
|
23
|
+
}
|
|
24
|
+
async validateApiKey() {
|
|
25
|
+
try {
|
|
26
|
+
// Make a minimal API call to validate the key
|
|
27
|
+
await this.client.embeddings.create({
|
|
28
|
+
model: this.model,
|
|
29
|
+
input: 'test'
|
|
30
|
+
});
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error?.status === 401 || error?.code === 'invalid_api_key' ||
|
|
35
|
+
error?.message?.includes('Incorrect API key') ||
|
|
36
|
+
error?.message?.includes('invalid_api_key')) {
|
|
37
|
+
this._isValid = false;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Other errors (rate limit, etc.) - key might still be valid
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async generateEmbedding(text) {
|
|
45
|
+
if (!this._isValid) {
|
|
46
|
+
throw new InvalidApiKeyError();
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const response = await this.client.embeddings.create({
|
|
50
|
+
model: this.model,
|
|
51
|
+
input: text
|
|
52
|
+
});
|
|
53
|
+
if (!response.data?.[0]?.embedding) {
|
|
54
|
+
throw new Error('Failed to get embedding from OpenAI');
|
|
55
|
+
}
|
|
56
|
+
return response.data[0].embedding;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
// Check for authentication errors (401)
|
|
60
|
+
if (error?.status === 401 || error?.code === 'invalid_api_key' ||
|
|
61
|
+
error?.message?.includes('Incorrect API key') ||
|
|
62
|
+
error?.message?.includes('invalid_api_key')) {
|
|
63
|
+
this._isValid = false;
|
|
64
|
+
throw new InvalidApiKeyError(error.message || 'Invalid OpenAI API key');
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async generateEmbeddings(texts) {
|
|
70
|
+
const embeddings = [];
|
|
71
|
+
for (const text of texts) {
|
|
72
|
+
const embedding = await this.generateEmbedding(text);
|
|
73
|
+
embeddings.push(embedding);
|
|
74
|
+
// Small delay to avoid rate limits
|
|
75
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
76
|
+
}
|
|
77
|
+
return embeddings;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.EmbeddingService = EmbeddingService;
|