@o-lang/semantic-doc-search 1.0.40 → 1.0.42

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.
@@ -2,20 +2,22 @@ const VectorRouter = require("../adapters/vectorRouter");
2
2
  const embedder = require("../embeddings/local");
3
3
  const extractText = require("../utils/extractText");
4
4
  const chunkText = require("../utils/chunker");
5
+ const formatResults = require("../utils/formatResults");
5
6
  const fs = require("fs");
6
7
  const path = require("path");
7
8
 
8
9
  async function performDocQA(
9
10
  query,
10
11
  {
11
- doc_root,
12
- vectorBackend = "pgvector",
12
+ doc_root = "./docs",
13
+ vectorBackend = "memory", // 🔥 default to memory like Python fallback
13
14
  dimension = 384,
14
- migrate_on_demand = false,
15
15
  POSTGRES_URL,
16
+ topK = 5,
16
17
  ...config
17
18
  } = {}
18
19
  ) {
20
+ // ── Create vector store
19
21
  const store = VectorRouter.create({
20
22
  backend: vectorBackend,
21
23
  dimension,
@@ -25,30 +27,65 @@ async function performDocQA(
25
27
 
26
28
  const embed = await embedder({ dimension });
27
29
 
28
- if (migrate_on_demand && doc_root) {
29
- for (const file of fs.readdirSync(doc_root)) {
30
+ // ─────────────────────────────────────────────
31
+ // 🔥 ALWAYS INGEST (Python parity)
32
+ // ─────────────────────────────────────────────
33
+ if (doc_root && fs.existsSync(doc_root)) {
34
+ const files = fs.readdirSync(doc_root);
35
+
36
+ for (const file of files) {
30
37
  const fullPath = path.join(doc_root, file);
38
+
31
39
  if (!fs.statSync(fullPath).isFile()) continue;
40
+ if (!file.endsWith(".txt") && !file.endsWith(".md")) continue;
41
+
42
+ try {
43
+ const text = await extractText(fullPath);
44
+ if (!text || !text.trim()) continue;
45
+
46
+ const chunks = chunkText(text, 500, 50) || [text];
47
+
48
+ for (let i = 0; i < chunks.length; i++) {
49
+ const chunk = chunks[i];
50
+ if (!chunk.trim()) continue;
32
51
 
33
- const text = await extractText(fullPath);
34
- const chunks = chunkText(text);
35
-
36
- for (let i = 0; i < chunks.length; i++) {
37
- await store.upsert({
38
- id: `${file}-${i}`,
39
- vector: await embed(chunks[i]),
40
- content: chunks[i],
41
- source: file,
42
- metadata: { chunk: i }
43
- });
52
+ try {
53
+ await store.upsert({
54
+ id: `${file}:${i}`,
55
+ vector: await embed(chunk),
56
+ content: chunk,
57
+ source: `file:${file}`,
58
+ metadata: { chunk: i }
59
+ });
60
+ } catch (err) {
61
+ console.warn("⚠️ Chunk failed:", err.message);
62
+ }
63
+ }
64
+
65
+ } catch (err) {
66
+ console.error("❌ Failed to process file:", file, err.message);
44
67
  }
45
68
  }
46
69
  }
47
70
 
48
- const results = await store.query(await embed(query), { topK: 5 });
71
+ // ─────────────────────────────────────────────
72
+ // 🔍 SEARCH
73
+ // ─────────────────────────────────────────────
74
+ let matches = [];
75
+
76
+ try {
77
+ const queryVector = await embed(query);
78
+ matches = await store.query(queryVector, { topK });
79
+ } catch (err) {
80
+ console.error("❌ Search failed:", err.message);
81
+ }
49
82
 
50
83
  if (store.close) await store.close();
51
- return results;
84
+
85
+ // ─────────────────────────────────────────────
86
+ // ✅ FORMAT LIKE PYTHON
87
+ // ─────────────────────────────────────────────
88
+ return formatResults(matches, query);
52
89
  }
53
90
 
54
- module.exports = performDocQA;
91
+ module.exports = performDocQA;
@@ -1,15 +1,14 @@
1
- /**
2
- * Normalizes vector search results for O-Lang workflows.
3
- * Returns both structured matches AND a plain .text field for LLM prompts.
4
- */
5
- function formatResults(results = [], query) {
6
- // ✅ Generate plain text from all matches
7
- const text = results.map(r => r.content).join('\n\n');
8
-
1
+ function formatResults(results = [], query = "") {
2
+ const safeResults = Array.isArray(results) ? results : [];
3
+
4
+ const text = safeResults.length
5
+ ? safeResults.map(r => r.content).join('\n\n')
6
+ : "";
7
+
9
8
  return {
10
9
  query,
11
- text, // ← THIS IS THE KEY ADDITION
12
- matches: results.map(r => ({
10
+ text,
11
+ matches: safeResults.map(r => ({
13
12
  id: r.id,
14
13
  content: r.content,
15
14
  source: r.source,
@@ -1,25 +0,0 @@
1
- {
2
- "_name_or_path": "sentence-transformers/all-MiniLM-L6-v2",
3
- "architectures": [
4
- "BertModel"
5
- ],
6
- "attention_probs_dropout_prob": 0.1,
7
- "classifier_dropout": null,
8
- "gradient_checkpointing": false,
9
- "hidden_act": "gelu",
10
- "hidden_dropout_prob": 0.1,
11
- "hidden_size": 384,
12
- "initializer_range": 0.02,
13
- "intermediate_size": 1536,
14
- "layer_norm_eps": 1e-12,
15
- "max_position_embeddings": 512,
16
- "model_type": "bert",
17
- "num_attention_heads": 12,
18
- "num_hidden_layers": 6,
19
- "pad_token_id": 0,
20
- "position_embedding_type": "absolute",
21
- "transformers_version": "4.29.2",
22
- "type_vocab_size": 2,
23
- "use_cache": true,
24
- "vocab_size": 30522
25
- }