@o-lang/semantic-doc-search 1.0.2 → 1.0.3
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/package.json +1 -1
- package/src/index.js +73 -22
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,12 +1,67 @@
|
|
|
1
1
|
// doc-search.js
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
// Load your utility modules - you'll need to convert these to CommonJS too
|
|
6
|
+
// For now, we'll create simplified versions inline
|
|
7
|
+
|
|
8
|
+
// ✅ Simplified LocalEmbedding (replace with your actual implementation)
|
|
9
|
+
class LocalEmbedding {
|
|
10
|
+
embed(text) {
|
|
11
|
+
// Simple hash-based "embedding" for demo
|
|
12
|
+
// Replace with your actual embedding logic
|
|
13
|
+
const hash = text.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
14
|
+
return Array(1536).fill(0).map((_, i) => Math.sin(hash + i));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ✅ Simplified utility functions
|
|
19
|
+
function chunkText(text, chunkSize, overlap) {
|
|
20
|
+
const chunks = [];
|
|
21
|
+
let start = 0;
|
|
22
|
+
while (start < text.length) {
|
|
23
|
+
chunks.push(text.slice(start, start + chunkSize));
|
|
24
|
+
start += chunkSize - overlap;
|
|
25
|
+
}
|
|
26
|
+
return chunks;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function extractKeywords(text) {
|
|
30
|
+
return text.toLowerCase().match(/\b\w{3,}\b/g) || [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cosine(vec1, vec2) {
|
|
34
|
+
if (!vec1 || !vec2) return 0;
|
|
35
|
+
const dot = vec1.reduce((sum, val, i) => sum + val * (vec2[i] || 0), 0);
|
|
36
|
+
const mag1 = Math.sqrt(vec1.reduce((sum, val) => sum + val * val, 0));
|
|
37
|
+
const mag2 = Math.sqrt(vec2.reduce((sum, val) => sum + val * val, 0));
|
|
38
|
+
return dot / (mag1 * mag2) || 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function highlightMatches(text, keywords) {
|
|
42
|
+
let result = text;
|
|
43
|
+
keywords.forEach(keyword => {
|
|
44
|
+
const regex = new RegExp(`(${keyword})`, 'gi');
|
|
45
|
+
result = result.replace(regex, '**$1**');
|
|
46
|
+
});
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// LLM creation - simplified for now
|
|
51
|
+
function createLLM(config) {
|
|
52
|
+
// This would integrate with your actual LLM router
|
|
53
|
+
// For now, return a mock that just returns the prompt
|
|
54
|
+
return {
|
|
55
|
+
async generate({ prompt }) {
|
|
56
|
+
return { text: prompt };
|
|
57
|
+
},
|
|
58
|
+
async stream({ prompt, onToken }) {
|
|
59
|
+
// Mock streaming
|
|
60
|
+
const words = prompt.split(' ');
|
|
61
|
+
words.forEach(word => onToken?.(word + ' '));
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
10
65
|
|
|
11
66
|
const CACHE_PATH = path.join(process.cwd(), "embeddings.json");
|
|
12
67
|
|
|
@@ -33,7 +88,7 @@ function saveCache(cache) {
|
|
|
33
88
|
} catch {}
|
|
34
89
|
}
|
|
35
90
|
|
|
36
|
-
// ✅ UNIVERSAL DATABASE ADAPTER
|
|
91
|
+
// ✅ UNIVERSAL DATABASE ADAPTER
|
|
37
92
|
class DatabaseAdapter {
|
|
38
93
|
constructor() {
|
|
39
94
|
this.initialized = false;
|
|
@@ -42,7 +97,6 @@ class DatabaseAdapter {
|
|
|
42
97
|
async initialize(context) {
|
|
43
98
|
if (this.initialized) return;
|
|
44
99
|
|
|
45
|
-
// Initialize based on context configuration
|
|
46
100
|
if (context.db_type === 'mongodb' || context.MONGO_URI) {
|
|
47
101
|
await this.initMongo(context);
|
|
48
102
|
} else if (context.db_type === 'sqlite' || context.db_path) {
|
|
@@ -95,7 +149,6 @@ class DatabaseAdapter {
|
|
|
95
149
|
password: context.DB_PASSWORD,
|
|
96
150
|
database: context.DB_NAME || 'olang'
|
|
97
151
|
};
|
|
98
|
-
// Remove undefined/null values
|
|
99
152
|
Object.keys(poolConfig).forEach(key => {
|
|
100
153
|
if (poolConfig[key] === undefined || poolConfig[key] === null) {
|
|
101
154
|
delete poolConfig[key];
|
|
@@ -158,7 +211,6 @@ class DatabaseAdapter {
|
|
|
158
211
|
try {
|
|
159
212
|
filter = JSON.parse(doc_filter);
|
|
160
213
|
} catch {
|
|
161
|
-
// Text search fallback
|
|
162
214
|
filter = { $text: { $search: doc_filter } };
|
|
163
215
|
}
|
|
164
216
|
} else if (typeof doc_filter === 'object' && Object.keys(doc_filter).length > 0) {
|
|
@@ -180,7 +232,6 @@ class DatabaseAdapter {
|
|
|
180
232
|
doc_params = []
|
|
181
233
|
} = context;
|
|
182
234
|
|
|
183
|
-
// Parse doc_params from string if needed
|
|
184
235
|
let params = doc_params;
|
|
185
236
|
if (typeof doc_params === 'string') {
|
|
186
237
|
try {
|
|
@@ -215,7 +266,7 @@ class DatabaseAdapter {
|
|
|
215
266
|
// ✅ LOAD DOCUMENTS FROM DATABASE (if configured)
|
|
216
267
|
async function loadDocumentsFromDatabase(context) {
|
|
217
268
|
if (!context.db_type && !context.db_path && !context.MONGO_URI && !context.POSTGRES_URL) {
|
|
218
|
-
return null;
|
|
269
|
+
return null;
|
|
219
270
|
}
|
|
220
271
|
|
|
221
272
|
const dbAdapter = new DatabaseAdapter();
|
|
@@ -232,13 +283,11 @@ async function loadDocumentsFromDatabase(context) {
|
|
|
232
283
|
async function loadAllDocuments(context) {
|
|
233
284
|
const documents = [];
|
|
234
285
|
|
|
235
|
-
// 1. Load from database first (if configured)
|
|
236
286
|
const dbDocs = await loadDocumentsFromDatabase(context);
|
|
237
287
|
if (dbDocs) {
|
|
238
288
|
documents.push(...dbDocs);
|
|
239
289
|
}
|
|
240
290
|
|
|
241
|
-
// 2. Load from file system (existing behavior)
|
|
242
291
|
const baseDir = context.doc_root
|
|
243
292
|
? safeResolve(process.cwd(), context.doc_root)
|
|
244
293
|
: path.join(process.cwd(), "docs");
|
|
@@ -262,7 +311,7 @@ async function loadAllDocuments(context) {
|
|
|
262
311
|
return documents;
|
|
263
312
|
}
|
|
264
313
|
|
|
265
|
-
// ✅ MAIN SEARCH FUNCTION
|
|
314
|
+
// ✅ MAIN SEARCH FUNCTION
|
|
266
315
|
async function performDocQA(query, context = {}) {
|
|
267
316
|
const { doc_root, stream = false } = context;
|
|
268
317
|
const options = context.options || {};
|
|
@@ -276,7 +325,6 @@ async function performDocQA(query, context = {}) {
|
|
|
276
325
|
return { text: "Missing required input: query" };
|
|
277
326
|
}
|
|
278
327
|
|
|
279
|
-
// Load documents from both database and files
|
|
280
328
|
const allDocs = await loadAllDocuments(context);
|
|
281
329
|
if (!allDocs || !allDocs.length) {
|
|
282
330
|
return { text: "No documents available." };
|
|
@@ -396,8 +444,8 @@ async function performDocQA(query, context = {}) {
|
|
|
396
444
|
};
|
|
397
445
|
}
|
|
398
446
|
|
|
399
|
-
// ✅ O-Lang Resolver Interface
|
|
400
|
-
|
|
447
|
+
// ✅ O-Lang Resolver Interface - COMMONJS STYLE
|
|
448
|
+
async function docSearchResolver(action, context) {
|
|
401
449
|
if (action.startsWith('Ask doc-search ')) {
|
|
402
450
|
const match = action.match(/"(.*)"|'(.*)'/);
|
|
403
451
|
const query = match ? (match[1] || match[2]) : action.replace(/^Ask doc-search\s+/, '').trim();
|
|
@@ -406,5 +454,8 @@ export default async function docSearchResolver(action, context) {
|
|
|
406
454
|
return undefined;
|
|
407
455
|
}
|
|
408
456
|
|
|
409
|
-
// ✅ Resolver name
|
|
410
|
-
docSearchResolver.resolverName = 'doc-search';
|
|
457
|
+
// ✅ Resolver name
|
|
458
|
+
docSearchResolver.resolverName = 'doc-search';
|
|
459
|
+
|
|
460
|
+
// ✅ COMMONJS EXPORT
|
|
461
|
+
module.exports = docSearchResolver;
|