@o-lang/semantic-doc-search 1.1.0 → 1.1.2

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 +27 -68
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/semantic-doc-search",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "O-Lang semantic document search resolver with vector embeddings",
5
5
  "main": "src/index.js",
6
6
  "exports": {
package/src/resolver.js CHANGED
@@ -36,125 +36,84 @@ function hashText(str) {
36
36
  }
37
37
 
38
38
  // ─────────────────────────────────────────────
39
- // 🔥 Parse O-Lang Action String
40
- // Format: Action <resolver> "<actionType>" "<arg1>" "<arg2>" ...
41
- // ─────────────────────────────────────────────
42
- function parseActionString(action) {
43
- if (!action || typeof action !== "string") return null;
44
-
45
- // Remove "Action doc-search " prefix if present
46
- const cleaned = action.replace(/^Action\s+doc-search\s+/, "").trim();
47
-
48
- // Extract quoted strings: "vector.search" "query" "./docs"
49
- const matches = [...cleaned.matchAll(/"([^"]*)"/g)].map(m => m[1]);
50
-
51
- if (matches.length < 1) return null;
52
-
53
- return {
54
- actionType: matches[0], // "vector.search" or "vector.insert"
55
- args: matches.slice(1), // remaining args
56
- raw: action
57
- };
58
- }
59
-
60
- // ─────────────────────────────────────────────
61
- // 🔥 MAIN RESOLVER
39
+ // 🔥 MAIN RESOLVER - Simplified: action type = resolver name
40
+ // Format: Action vector.search "arg1" "arg2" ...
41
+ // - 1 arg = ingest: "{doc_root}"
42
+ // - 2+ args = search: "{query}" "{doc_root}"
62
43
  // ─────────────────────────────────────────────
63
44
  async function resolver(action, context = {}) {
64
45
  if (typeof action !== "string") return;
65
46
 
66
- // Parse the action string
67
- const parsed = parseActionString(action);
68
- if (!parsed) {
69
- console.error("❌ Invalid doc-search action format:", action);
70
- throw new Error("Invalid doc-search action format");
71
- }
47
+ // Extract ALL quoted args from action string
48
+ const args = [...action.matchAll(/"([^"]*)"/g)].map(m => m[1]);
72
49
 
73
- const { actionType, args } = parsed;
74
-
75
50
  const vectorStore = VectorRouter.create(context);
76
51
  const getEmbedFn = await embedder({ dimension: 384 });
77
52
 
78
- const doc_root = context.doc_root || args[1] || "./docs";
79
- const useCache = !!context.POSTGRES_URL || !!context.REDIS_URL;
80
- const cache = useCache ? loadCache() : {};
53
+ // Always use cache for local persistence — no Postgres/Redis required
54
+ const useCache = true;
55
+ const cache = loadCache();
81
56
 
82
57
  // =====================================================
83
- // ✅ 1. VECTOR INSERT (INGEST)
58
+ // ✅ INGEST: 1 arg = doc_root
84
59
  // =====================================================
85
- if (actionType.includes("vector.insert")) {
60
+ if (args.length === 1) {
86
61
  let inserted = 0;
87
- const ingestRoot = args[0] || doc_root;
62
+ const ingestRoot = args[0] || context.doc_root || "./docs";
88
63
 
89
64
  if (fs.existsSync(ingestRoot)) {
90
65
  const files = fs.readdirSync(ingestRoot);
91
-
92
66
  for (const file of files) {
93
67
  const fullPath = path.join(ingestRoot, file);
94
68
  if (!fs.statSync(fullPath).isFile()) continue;
95
-
96
69
  const content = fs.readFileSync(fullPath, "utf8");
97
70
  if (!content) continue;
98
-
99
- const chunkText = require("./utils/chunker").chunkText;
100
- const chunks = chunkText(content, 500, 50);
101
-
71
+ const chunks = require("./utils/chunker").chunkText(content, 500, 50);
102
72
  for (let i = 0; i < chunks.length; i++) {
103
73
  const text = sanitizeTextForEmbedding(chunks[i]);
104
74
  if (!text) continue;
105
-
106
75
  const hash = hashText(text);
107
- if (useCache && cache[hash]) continue;
108
-
76
+ if (cache[hash]) continue; // skip already-ingested chunks
109
77
  const rawVector = await getEmbedFn(text);
110
- const vector = Array.from(rawVector);
111
-
112
78
  await vectorStore.upsert({
113
79
  id: `${file}:${i}`,
114
- vector,
80
+ vector: Array.from(rawVector),
115
81
  content: text,
116
82
  source: `file:${file}`,
117
83
  });
118
-
119
- if (useCache) cache[hash] = true;
84
+ cache[hash] = true;
120
85
  inserted++;
121
86
  }
122
87
  }
123
88
  }
124
89
 
125
- if (useCache) saveCache(cache);
90
+ saveCache(cache);
126
91
  if (vectorStore.close) await vectorStore.close();
127
-
92
+ console.log(`[vector.search] ✅ Ingested ${inserted} chunks from ${ingestRoot}`);
128
93
  return { inserted, doc_root: ingestRoot };
129
94
  }
130
95
 
131
96
  // =====================================================
132
- // ✅ 2. VECTOR SEARCH
97
+ // ✅ SEARCH: 2+ args = query + doc_root
133
98
  // =====================================================
134
- if (actionType.includes("vector.search")) {
135
- const query = sanitizeTextForEmbedding(args[0] || extractQuery(action) || "");
99
+ if (args.length >= 2) {
100
+ const query = sanitizeTextForEmbedding(args[0]);
136
101
  if (!query) return { text: "", matches: [] };
137
-
138
102
  const rawQueryVector = await getEmbedFn(query);
139
- const queryVector = Array.from(rawQueryVector);
140
-
141
- const results = await vectorStore.query(queryVector, {
142
- topK: context.topK || 5,
143
- });
144
-
103
+ const results = await vectorStore.query(Array.from(rawQueryVector), { topK: context.topK || 5 });
145
104
  if (vectorStore.close) await vectorStore.close();
146
-
147
105
  return formatResults(results, query);
148
106
  }
149
107
 
150
108
  // =====================================================
151
- // ❌ Unknown action
109
+ // ❌ Unknown action format
152
110
  // =====================================================
153
- console.warn(`⚠️ Unknown doc-search action: "${actionType}"`);
111
+ console.warn(`⚠️ Unknown vector.search action format: "${action}"`);
154
112
  return;
155
113
  }
156
114
 
157
- resolver.resolverName = "doc-search";
158
- resolver.version = "1.0.42";
115
+ // Must match workflow's "Allow resolvers: - vector.search"
116
+ resolver.resolverName = "vector.search";
117
+ resolver.version = "1.0.43";
159
118
 
160
119
  module.exports = resolver;