@o-lang/semantic-doc-search 1.0.3 → 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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +22 -73
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/semantic-doc-search",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "O-lang Semantic Document Search Resolver with hybrid search, embeddings, rerank, and streaming.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -1,67 +1,12 @@
1
1
  // doc-search.js
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
- }
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { createLLM } from "./llm/router.js";
5
+ import { LocalEmbedding } from "./embeddings/local.js";
6
+ import { chunkText } from "./utils/chunker.js";
7
+ import { extractKeywords } from "./utils/extractText.js";
8
+ import { cosine } from "./utils/similarity.js";
9
+ import { highlightMatches } from "./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,8 +396,8 @@ async function performDocQA(query, context = {}) {
444
396
  };
445
397
  }
446
398
 
447
- // ✅ O-Lang Resolver Interface - COMMONJS STYLE
448
- async function docSearchResolver(action, context) {
399
+ // ✅ O-Lang Resolver Interface (Your existing interface)
400
+ export default async function docSearchResolver(action, context) {
449
401
  if (action.startsWith('Ask doc-search ')) {
450
402
  const match = action.match(/"(.*)"|'(.*)'/);
451
403
  const query = match ? (match[1] || match[2]) : action.replace(/^Ask doc-search\s+/, '').trim();
@@ -454,8 +406,5 @@ async function docSearchResolver(action, context) {
454
406
  return undefined;
455
407
  }
456
408
 
457
- // ✅ Resolver name
458
- docSearchResolver.resolverName = 'doc-search';
459
-
460
- // ✅ COMMONJS EXPORT
461
- module.exports = docSearchResolver;
409
+ // ✅ Resolver name matches package name: @o-lang/doc-search → doc-search
410
+ docSearchResolver.resolverName = 'doc-search';