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

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/resolver.js +9 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/semantic-doc-search",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
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",
package/src/resolver.js CHANGED
@@ -8,7 +8,7 @@ const crypto = require("crypto");
8
8
 
9
9
  const CACHE_PATH = path.join(process.cwd(), "embeddings.json");
10
10
 
11
- // Load cache for ingestion guard
11
+ // ---------------- Cache utils ----------------
12
12
  function loadCache() {
13
13
  try {
14
14
  if (fs.existsSync(CACHE_PATH)) {
@@ -17,25 +17,24 @@ function loadCache() {
17
17
  } catch {}
18
18
  return {};
19
19
  }
20
-
21
20
  function saveCache(cache) {
22
21
  try {
23
22
  fs.writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));
24
23
  } catch {}
25
24
  }
26
-
27
25
  function hashContent(str) {
28
26
  return crypto.createHash("sha256").update(str).digest("hex");
29
27
  }
30
28
 
31
- /**
32
- * Semantic Doc Search Resolver
33
- */
29
+ // ---------------- Resolver ----------------
34
30
  async function resolver(action, context = {}) {
35
31
  if (typeof action !== "string") return;
36
32
  if (!action.toLowerCase().startsWith("ask doc-search")) return;
37
33
 
38
- const query = extractQuery(action);
34
+ // Extract & sanitize query
35
+ const queryRaw = extractQuery(action);
36
+ const query = typeof queryRaw === "string" ? queryRaw.replace(/^["']|["']$/g, "").trim() : "";
37
+ if (!query) throw new Error("Query is empty after sanitization");
39
38
 
40
39
  // Vector backend
41
40
  const vectorStore = VectorRouter.create(context);
@@ -63,16 +62,13 @@ async function resolver(action, context = {}) {
63
62
  const chunks = doc.chunks || [doc.content];
64
63
  for (let i = 0; i < chunks.length; i++) {
65
64
  const text = chunks[i];
65
+ if (!text || typeof text !== "string") continue;
66
+
66
67
  const hash = hashContent(text);
67
68
  if (cache[hash]) continue; // Skip already ingested
68
69
 
69
70
  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
- }
71
+ if (!Array.isArray(vector) || vector.every(v => v === 0)) continue;
76
72
 
77
73
  await vectorStore.upsert({
78
74
  id: `${doc.id}:${i}`,
@@ -89,8 +85,6 @@ async function resolver(action, context = {}) {
89
85
 
90
86
  // Embed query & search
91
87
  const queryVector = await embedder.embed(query);
92
-
93
- // ✅ Defensive: validate query vector
94
88
  if (!Array.isArray(queryVector) || queryVector.length !== embedder.getDimension()) {
95
89
  throw new Error("Query embedding invalid or not a proper array");
96
90
  }