@o-lang/semantic-doc-search 1.0.30 → 1.0.31

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/semantic-doc-search",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
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": "commonjs",
@@ -6,7 +6,7 @@
6
6
  * - No silent failures
7
7
  * - No zero vectors
8
8
  * - Deterministic behavior
9
- * - DEFENSIVE against method detaching
9
+ * - DEFENSIVE against method detaching & invalid vectors
10
10
  */
11
11
 
12
12
  class LocalEmbedding {
@@ -15,7 +15,7 @@ class LocalEmbedding {
15
15
  this.model = null;
16
16
  this.loading = null;
17
17
 
18
- // 🔒 CRITICAL: bind methods to prevent resolver breakage
18
+ // 🔒 Bind methods to prevent resolver breakage
19
19
  this.loadModel = this.loadModel.bind(this);
20
20
  this.embed = this.embed.bind(this);
21
21
  this.embedBatch = this.embedBatch.bind(this);
@@ -73,18 +73,19 @@ class LocalEmbedding {
73
73
  normalize: true,
74
74
  });
75
75
 
76
- const vector = Array.from(output.data);
76
+ // ✅ Defensive: ensure vector is a valid array
77
+ const vector = Array.isArray(output?.data) ? Array.from(output.data) : null;
77
78
 
78
- if (vector.length !== this.dim) {
79
- throw new Error(
80
- `Invalid embedding dimension: ${vector.length} (expected ${this.dim})`
81
- );
79
+ if (!Array.isArray(vector) || vector.length !== this.dim) {
80
+ console.error("❌ Invalid embedding vector returned:", vector);
81
+ throw new Error(`Invalid embedding dimension: ${vector?.length || 0} (expected ${this.dim})`);
82
82
  }
83
83
 
84
84
  return vector;
85
85
  } catch (err) {
86
86
  console.error(
87
- `❌ Embedding failed for text: "${text.slice(0, 60)}..."`
87
+ `❌ Embedding failed for text: "${text.slice(0, 60)}..."`,
88
+ err
88
89
  );
89
90
  throw err;
90
91
  }
@@ -115,7 +116,5 @@ class LocalEmbedding {
115
116
 
116
117
  /* ---------------- SINGLETON EXPORT ---------------- */
117
118
 
118
- // One embedder per process (CRITICAL)
119
119
  const embedder = new LocalEmbedding();
120
-
121
120
  module.exports = embedder;
package/src/resolver.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const VectorRouter = require("./adapters/vectorRouter");
2
- const { embed } = require("./embeddings/local");
2
+ const embedder = require("./embeddings/local"); // ✅ singleton embedder
3
3
  const { extractQuery } = require("./utils/extractQuery");
4
4
  const { formatResults } = require("./utils/formatResults");
5
5
  const fs = require("fs");
@@ -17,11 +17,13 @@ function loadCache() {
17
17
  } catch {}
18
18
  return {};
19
19
  }
20
+
20
21
  function saveCache(cache) {
21
22
  try {
22
23
  fs.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));
23
24
  } catch {}
24
25
  }
26
+
25
27
  function hashContent(str) {
26
28
  return crypto.createHash("sha256").update(str).digest("hex");
27
29
  }
@@ -64,8 +66,13 @@ async function resolver(action, context = {}) {
64
66
  const hash = hashContent(text);
65
67
  if (cache[hash]) continue; // Skip already ingested
66
68
 
67
- const vector = await embed(text);
68
- if (!vector || vector.every(v => v === 0)) continue;
69
+ const vector = await embedder.embed(text);
70
+
71
+ // ✅ Defensive: skip invalid vectors
72
+ if (!Array.isArray(vector) || vector.length !== embedder.getDimension() || vector.every(v => v === 0)) {
73
+ console.warn(`⚠️ Skipping invalid vector for doc ${doc.id}:${i}`);
74
+ continue;
75
+ }
69
76
 
70
77
  await vectorStore.upsert({
71
78
  id: `${doc.id}:${i}`,
@@ -81,9 +88,15 @@ async function resolver(action, context = {}) {
81
88
  }
82
89
 
83
90
  // Embed query & search
84
- const vector = await embed(query);
91
+ const queryVector = await embedder.embed(query);
92
+
93
+ // ✅ Defensive: validate query vector
94
+ if (!Array.isArray(queryVector) || queryVector.length !== embedder.getDimension()) {
95
+ throw new Error("Query embedding invalid or not a proper array");
96
+ }
97
+
85
98
  const results = await vectorStore.query({
86
- vector,
99
+ vector: queryVector,
87
100
  topK: context.topK || 5,
88
101
  minScore: context.minScore || 0,
89
102
  });