@o-lang/semantic-doc-search 1.0.43 → 1.1.0
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 +1 -1
- package/src/resolver.js +48 -17
- package/src/utils/extractQuery.js +24 -8
package/package.json
CHANGED
package/src/resolver.js
CHANGED
|
@@ -9,7 +9,7 @@ const crypto = require("crypto");
|
|
|
9
9
|
const CACHE_PATH = path.join(process.cwd(), "embeddings.json");
|
|
10
10
|
|
|
11
11
|
// ─────────────────────────────────────────────
|
|
12
|
-
// Helpers
|
|
12
|
+
// Helpers
|
|
13
13
|
// ─────────────────────────────────────────────
|
|
14
14
|
function loadCache() {
|
|
15
15
|
try {
|
|
@@ -35,30 +35,62 @@ function hashText(str) {
|
|
|
35
35
|
return crypto.createHash("sha256").update(str).digest("hex");
|
|
36
36
|
}
|
|
37
37
|
|
|
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
|
+
|
|
38
60
|
// ─────────────────────────────────────────────
|
|
39
61
|
// 🔥 MAIN RESOLVER
|
|
40
62
|
// ─────────────────────────────────────────────
|
|
41
63
|
async function resolver(action, context = {}) {
|
|
42
64
|
if (typeof action !== "string") return;
|
|
43
65
|
|
|
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
|
+
}
|
|
72
|
+
|
|
73
|
+
const { actionType, args } = parsed;
|
|
74
|
+
|
|
44
75
|
const vectorStore = VectorRouter.create(context);
|
|
45
|
-
const
|
|
76
|
+
const getEmbedFn = await embedder({ dimension: 384 });
|
|
46
77
|
|
|
47
|
-
const doc_root = context.doc_root || "./docs";
|
|
78
|
+
const doc_root = context.doc_root || args[1] || "./docs";
|
|
48
79
|
const useCache = !!context.POSTGRES_URL || !!context.REDIS_URL;
|
|
49
80
|
const cache = useCache ? loadCache() : {};
|
|
50
81
|
|
|
51
82
|
// =====================================================
|
|
52
83
|
// ✅ 1. VECTOR INSERT (INGEST)
|
|
53
84
|
// =====================================================
|
|
54
|
-
if (
|
|
85
|
+
if (actionType.includes("vector.insert")) {
|
|
55
86
|
let inserted = 0;
|
|
87
|
+
const ingestRoot = args[0] || doc_root;
|
|
56
88
|
|
|
57
|
-
if (fs.existsSync(
|
|
58
|
-
const files = fs.readdirSync(
|
|
89
|
+
if (fs.existsSync(ingestRoot)) {
|
|
90
|
+
const files = fs.readdirSync(ingestRoot);
|
|
59
91
|
|
|
60
92
|
for (const file of files) {
|
|
61
|
-
const fullPath = path.join(
|
|
93
|
+
const fullPath = path.join(ingestRoot, file);
|
|
62
94
|
if (!fs.statSync(fullPath).isFile()) continue;
|
|
63
95
|
|
|
64
96
|
const content = fs.readFileSync(fullPath, "utf8");
|
|
@@ -74,7 +106,7 @@ async function resolver(action, context = {}) {
|
|
|
74
106
|
const hash = hashText(text);
|
|
75
107
|
if (useCache && cache[hash]) continue;
|
|
76
108
|
|
|
77
|
-
const rawVector = await
|
|
109
|
+
const rawVector = await getEmbedFn(text);
|
|
78
110
|
const vector = Array.from(rawVector);
|
|
79
111
|
|
|
80
112
|
await vectorStore.upsert({
|
|
@@ -93,17 +125,17 @@ async function resolver(action, context = {}) {
|
|
|
93
125
|
if (useCache) saveCache(cache);
|
|
94
126
|
if (vectorStore.close) await vectorStore.close();
|
|
95
127
|
|
|
96
|
-
return { inserted, doc_root };
|
|
128
|
+
return { inserted, doc_root: ingestRoot };
|
|
97
129
|
}
|
|
98
130
|
|
|
99
131
|
// =====================================================
|
|
100
132
|
// ✅ 2. VECTOR SEARCH
|
|
101
133
|
// =====================================================
|
|
102
|
-
if (
|
|
103
|
-
const query = sanitizeTextForEmbedding(extractQuery(action));
|
|
134
|
+
if (actionType.includes("vector.search")) {
|
|
135
|
+
const query = sanitizeTextForEmbedding(args[0] || extractQuery(action) || "");
|
|
104
136
|
if (!query) return { text: "", matches: [] };
|
|
105
137
|
|
|
106
|
-
const rawQueryVector = await
|
|
138
|
+
const rawQueryVector = await getEmbedFn(query);
|
|
107
139
|
const queryVector = Array.from(rawQueryVector);
|
|
108
140
|
|
|
109
141
|
const results = await vectorStore.query(queryVector, {
|
|
@@ -116,14 +148,13 @@ async function resolver(action, context = {}) {
|
|
|
116
148
|
}
|
|
117
149
|
|
|
118
150
|
// =====================================================
|
|
119
|
-
// ❌
|
|
151
|
+
// ❌ Unknown action
|
|
120
152
|
// =====================================================
|
|
121
|
-
|
|
122
|
-
|
|
153
|
+
console.warn(`⚠️ Unknown doc-search action: "${actionType}"`);
|
|
123
154
|
return;
|
|
124
155
|
}
|
|
125
156
|
|
|
126
|
-
resolver.resolverName = "
|
|
127
|
-
resolver.version = "1.0.
|
|
157
|
+
resolver.resolverName = "doc-search";
|
|
158
|
+
resolver.version = "1.0.42";
|
|
128
159
|
|
|
129
160
|
module.exports = resolver;
|
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Extracts the query from an O-Lang action.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Supports formats:
|
|
5
|
+
* - Legacy: "Ask doc-search: vacation policy"
|
|
6
|
+
* - New: Action doc-search "vector.search" "query" "./docs"
|
|
5
7
|
*/
|
|
6
8
|
function extractQuery(action) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (!match || !match[1]) {
|
|
10
|
-
throw new Error("Invalid doc-search action format");
|
|
9
|
+
if (!action || typeof action !== "string") {
|
|
10
|
+
throw new Error("Invalid doc-search action format: action is not a string");
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// ✅ NEW FORMAT: Extract all quoted strings
|
|
14
|
+
const quotedMatches = [...action.matchAll(/"([^"]*)"/g)].map(m => m[1]);
|
|
15
|
+
|
|
16
|
+
if (quotedMatches.length >= 2) {
|
|
17
|
+
// Format: "actionType" "query" "doc_root"
|
|
18
|
+
// Return the query (second quoted string)
|
|
19
|
+
return quotedMatches[1].trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ✅ LEGACY FORMAT: Ask doc-search: vacation policy
|
|
23
|
+
const legacyMatch = action.match(/ask doc-search\s*:?\s*(.+)$/i);
|
|
24
|
+
if (legacyMatch && legacyMatch[1]) {
|
|
25
|
+
return legacyMatch[1].trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ❌ Fallback error
|
|
29
|
+
throw new Error(`Invalid doc-search action format: "${action}"`);
|
|
14
30
|
}
|
|
15
31
|
|
|
16
32
|
module.exports = {
|
|
17
33
|
extractQuery
|
|
18
|
-
};
|
|
34
|
+
};
|