@o-lang/semantic-doc-search 1.0.3 → 1.0.5
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 +21 -69
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,67 +1,12 @@
|
|
|
1
1
|
// doc-search.js
|
|
2
|
-
const fs = require(
|
|
3
|
-
const path = require(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
}
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const { createLLM } = require("./llm/router.js");
|
|
5
|
+
const { LocalEmbedding } = require("./embeddings/local.js");
|
|
6
|
+
const { chunkText } = require("./utils/chunker.js");
|
|
7
|
+
const { extractKeywords } = require("./utils/extractText.js");
|
|
8
|
+
const { cosine } = require("./utils/similarity.js");
|
|
9
|
+
const { highlightMatches } = require("./utils/highlight.js");
|
|
65
10
|
|
|
66
11
|
const CACHE_PATH = path.join(process.cwd(), "embeddings.json");
|
|
67
12
|
|
|
@@ -88,7 +33,7 @@ function saveCache(cache) {
|
|
|
88
33
|
} catch {}
|
|
89
34
|
}
|
|
90
35
|
|
|
91
|
-
// ✅ UNIVERSAL DATABASE ADAPTER
|
|
36
|
+
// ✅ UNIVERSAL DATABASE ADAPTER (NEW - Keep your existing imports!)
|
|
92
37
|
class DatabaseAdapter {
|
|
93
38
|
constructor() {
|
|
94
39
|
this.initialized = false;
|
|
@@ -97,6 +42,7 @@ class DatabaseAdapter {
|
|
|
97
42
|
async initialize(context) {
|
|
98
43
|
if (this.initialized) return;
|
|
99
44
|
|
|
45
|
+
// Initialize based on context configuration
|
|
100
46
|
if (context.db_type === 'mongodb' || context.MONGO_URI) {
|
|
101
47
|
await this.initMongo(context);
|
|
102
48
|
} else if (context.db_type === 'sqlite' || context.db_path) {
|
|
@@ -149,6 +95,7 @@ class DatabaseAdapter {
|
|
|
149
95
|
password: context.DB_PASSWORD,
|
|
150
96
|
database: context.DB_NAME || 'olang'
|
|
151
97
|
};
|
|
98
|
+
// Remove undefined/null values
|
|
152
99
|
Object.keys(poolConfig).forEach(key => {
|
|
153
100
|
if (poolConfig[key] === undefined || poolConfig[key] === null) {
|
|
154
101
|
delete poolConfig[key];
|
|
@@ -211,6 +158,7 @@ class DatabaseAdapter {
|
|
|
211
158
|
try {
|
|
212
159
|
filter = JSON.parse(doc_filter);
|
|
213
160
|
} catch {
|
|
161
|
+
// Text search fallback
|
|
214
162
|
filter = { $text: { $search: doc_filter } };
|
|
215
163
|
}
|
|
216
164
|
} else if (typeof doc_filter === 'object' && Object.keys(doc_filter).length > 0) {
|
|
@@ -232,6 +180,7 @@ class DatabaseAdapter {
|
|
|
232
180
|
doc_params = []
|
|
233
181
|
} = context;
|
|
234
182
|
|
|
183
|
+
// Parse doc_params from string if needed
|
|
235
184
|
let params = doc_params;
|
|
236
185
|
if (typeof doc_params === 'string') {
|
|
237
186
|
try {
|
|
@@ -266,7 +215,7 @@ class DatabaseAdapter {
|
|
|
266
215
|
// ✅ LOAD DOCUMENTS FROM DATABASE (if configured)
|
|
267
216
|
async function loadDocumentsFromDatabase(context) {
|
|
268
217
|
if (!context.db_type && !context.db_path && !context.MONGO_URI && !context.POSTGRES_URL) {
|
|
269
|
-
return null;
|
|
218
|
+
return null; // No database configured
|
|
270
219
|
}
|
|
271
220
|
|
|
272
221
|
const dbAdapter = new DatabaseAdapter();
|
|
@@ -283,11 +232,13 @@ async function loadDocumentsFromDatabase(context) {
|
|
|
283
232
|
async function loadAllDocuments(context) {
|
|
284
233
|
const documents = [];
|
|
285
234
|
|
|
235
|
+
// 1. Load from database first (if configured)
|
|
286
236
|
const dbDocs = await loadDocumentsFromDatabase(context);
|
|
287
237
|
if (dbDocs) {
|
|
288
238
|
documents.push(...dbDocs);
|
|
289
239
|
}
|
|
290
240
|
|
|
241
|
+
// 2. Load from file system (existing behavior)
|
|
291
242
|
const baseDir = context.doc_root
|
|
292
243
|
? safeResolve(process.cwd(), context.doc_root)
|
|
293
244
|
: path.join(process.cwd(), "docs");
|
|
@@ -311,7 +262,7 @@ async function loadAllDocuments(context) {
|
|
|
311
262
|
return documents;
|
|
312
263
|
}
|
|
313
264
|
|
|
314
|
-
// ✅ MAIN SEARCH FUNCTION
|
|
265
|
+
// ✅ MAIN SEARCH FUNCTION (Your existing logic + universal docs)
|
|
315
266
|
async function performDocQA(query, context = {}) {
|
|
316
267
|
const { doc_root, stream = false } = context;
|
|
317
268
|
const options = context.options || {};
|
|
@@ -325,6 +276,7 @@ async function performDocQA(query, context = {}) {
|
|
|
325
276
|
return { text: "Missing required input: query" };
|
|
326
277
|
}
|
|
327
278
|
|
|
279
|
+
// Load documents from both database and files
|
|
328
280
|
const allDocs = await loadAllDocuments(context);
|
|
329
281
|
if (!allDocs || !allDocs.length) {
|
|
330
282
|
return { text: "No documents available." };
|
|
@@ -444,7 +396,7 @@ async function performDocQA(query, context = {}) {
|
|
|
444
396
|
};
|
|
445
397
|
}
|
|
446
398
|
|
|
447
|
-
// ✅ O-Lang Resolver Interface -
|
|
399
|
+
// ✅ O-Lang Resolver Interface (Your existing interface - converted to CommonJS)
|
|
448
400
|
async function docSearchResolver(action, context) {
|
|
449
401
|
if (action.startsWith('Ask doc-search ')) {
|
|
450
402
|
const match = action.match(/"(.*)"|'(.*)'/);
|
|
@@ -454,8 +406,8 @@ async function docSearchResolver(action, context) {
|
|
|
454
406
|
return undefined;
|
|
455
407
|
}
|
|
456
408
|
|
|
457
|
-
// ✅ Resolver name
|
|
409
|
+
// ✅ Resolver name matches package name: @o-lang/doc-search → doc-search
|
|
458
410
|
docSearchResolver.resolverName = 'doc-search';
|
|
459
411
|
|
|
460
|
-
// ✅ COMMONJS EXPORT
|
|
412
|
+
// ✅ COMMONJS EXPORT (this is the key change)
|
|
461
413
|
module.exports = docSearchResolver;
|