@o-lang/semantic-doc-search 1.0.41 → 1.0.43
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 +40 -38
- package/src/embeddings/local.js +12 -2
- package/src/embeddings/local.js.bak +153 -0
- package/src/index.js +1 -1
- package/.cache/embeddings/Xenova/all-MiniLM-L6-v2/config.json +0 -25
- package/.cache/embeddings/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
- package/.cache/embeddings/Xenova/all-MiniLM-L6-v2/tokenizer.json +0 -30686
- package/.cache/embeddings/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +0 -15
- package/.env.example +0 -0
- package/bin/cli.js +0 -58
- package/docs/sample1.txt +0 -1
- package/docs/vacation policy +0 -5
- package/embeddings.json +0 -3
- package/test-doc-search-batch.js +0 -36
- package/test-doc-search.js +0 -40
- package/test-embed.js +0 -10
- package/test-single-doc.js +0 -32
package/package.json
CHANGED
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@o-lang/semantic-doc-search",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "O-
|
|
5
|
-
"main": "src/index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@o-lang/semantic-doc-search",
|
|
3
|
+
"version": "1.0.43",
|
|
4
|
+
"description": "O-Lang semantic document search resolver with vector embeddings",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.js",
|
|
8
|
+
"./resolver": "./src/resolver.js",
|
|
9
|
+
"./embeddings/local": "./src/embeddings/local.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/",
|
|
13
|
+
"package.json",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "node src/test-doc-search.js",
|
|
18
|
+
"start": "node src/index.js"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"o-lang",
|
|
22
|
+
"resolver",
|
|
23
|
+
"semantic-search",
|
|
24
|
+
"rag",
|
|
25
|
+
"embeddings"
|
|
26
|
+
],
|
|
27
|
+
"author": "O-Lang Team <info@olang.cloud>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@pinecone-database/pinecone": "^2.2.2",
|
|
31
|
+
"@xenova/transformers": "^2.14.0",
|
|
32
|
+
"axios": "^1.6.0",
|
|
33
|
+
"dotenv": "^16.6.1",
|
|
34
|
+
"pg": "^8.20.0",
|
|
35
|
+
"redis": "^4.7.1"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/embeddings/local.js
CHANGED
|
@@ -149,5 +149,15 @@ class LocalEmbedding {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
// ✅ EXPORT AS FACTORY FUNCTION (what resolver expects)
|
|
153
|
+
// Usage: const embed = await embedder({ dimension: 384 })
|
|
154
|
+
// Returns: async (text) => vector
|
|
155
|
+
const embedderInstance = new LocalEmbedding();
|
|
156
|
+
|
|
157
|
+
module.exports = async ({ dimension = 384 } = {}) => {
|
|
158
|
+
if (dimension && typeof dimension === 'number') {
|
|
159
|
+
embedderInstance.dim = dimension;
|
|
160
|
+
}
|
|
161
|
+
// Return bound embed method that resolver can call: await embed(text)
|
|
162
|
+
return embedderInstance.embed.bind(embedderInstance);
|
|
163
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalEmbedding
|
|
3
|
+
* ----------------
|
|
4
|
+
* Real semantic embeddings using all-MiniLM-L6-v2
|
|
5
|
+
* - Singleton model load
|
|
6
|
+
* - No silent failures
|
|
7
|
+
* - No zero vectors
|
|
8
|
+
* - Deterministic behavior
|
|
9
|
+
* - DEFENSIVE against method detaching & invalid vectors
|
|
10
|
+
* - WINDOWS-SAFE (disables SIMD, threads, proxy)
|
|
11
|
+
* - TENSOR-SAFE (handles Float32Array, Array, and all ONNX tensor types)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
class LocalEmbedding {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.dim = 384;
|
|
17
|
+
this.model = null;
|
|
18
|
+
this.loading = null;
|
|
19
|
+
|
|
20
|
+
// 🔒 Bind methods to prevent resolver breakage
|
|
21
|
+
this.loadModel = this.loadModel.bind(this);
|
|
22
|
+
this.embed = this.embed.bind(this);
|
|
23
|
+
this.embedBatch = this.embedBatch.bind(this);
|
|
24
|
+
this.getDimension = this.getDimension.bind(this);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* ---------------- INTERNAL ---------------- */
|
|
28
|
+
|
|
29
|
+
async loadModel() {
|
|
30
|
+
if (this.model) return this.model;
|
|
31
|
+
|
|
32
|
+
if (!this.loading) {
|
|
33
|
+
this.loading = (async () => {
|
|
34
|
+
// ⚠️ CRITICAL: Configure environment BEFORE loading model
|
|
35
|
+
const { env } = await import("@xenova/transformers");
|
|
36
|
+
|
|
37
|
+
// Safe settings for all platforms (harmless on macOS/Linux, essential on Windows)
|
|
38
|
+
env.backends.onnx.wasm.simd = false; // Avoids AVX/SIMD crashes on older CPUs
|
|
39
|
+
env.backends.onnx.wasm.threads = false; // Prevents threading issues in Node
|
|
40
|
+
env.backends.onnx.wasm.proxy = false; // Avoids proxy complications
|
|
41
|
+
env.allowLocalModels = true;
|
|
42
|
+
env.backends.onnx.warmup = false;
|
|
43
|
+
env.cacheDir = "./.cache/embeddings"; // Explicit, project-local cache
|
|
44
|
+
|
|
45
|
+
console.log("🔄 Loading local embedding model (first run only)...");
|
|
46
|
+
console.log("⚙️ Using WASM (SIMD disabled) for cross-platform compatibility");
|
|
47
|
+
|
|
48
|
+
const { pipeline } = await import("@xenova/transformers");
|
|
49
|
+
|
|
50
|
+
const model = await pipeline(
|
|
51
|
+
"feature-extraction",
|
|
52
|
+
"Xenova/all-MiniLM-L6-v2",
|
|
53
|
+
{
|
|
54
|
+
revision: "main",
|
|
55
|
+
cache_dir: "./.cache/embeddings",
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
console.log("✅ Local embedding model ready");
|
|
60
|
+
return model;
|
|
61
|
+
})();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.model = await this.loading;
|
|
65
|
+
return this.model;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ---------------- PUBLIC API ---------------- */
|
|
69
|
+
|
|
70
|
+
async embed(text) {
|
|
71
|
+
if (typeof text !== "string" || !text.trim()) {
|
|
72
|
+
throw new Error("Embedding input must be a non-empty string");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const model = await this.loadModel();
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const output = await model(text, {
|
|
79
|
+
pooling: "mean",
|
|
80
|
+
normalize: true,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// 🔍 DEBUG: Inspect output structure
|
|
84
|
+
console.log("🔍 Model output type:", typeof output);
|
|
85
|
+
if (output && typeof output === 'object') {
|
|
86
|
+
console.log("🔍 Output keys:", Object.keys(output));
|
|
87
|
+
console.log("🔍 Output dims:", output.dims);
|
|
88
|
+
console.log("🔍 output.data type:", Object.prototype.toString.call(output.data));
|
|
89
|
+
console.log("🔍 Is TypedArray?", ArrayBuffer.isView(output.data));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ✅ UNIVERSAL EXTRACTION: handles Float32Array, Array, and all tensor forms
|
|
93
|
+
let vector = null;
|
|
94
|
+
|
|
95
|
+
if (output && output.data !== undefined) {
|
|
96
|
+
// Handle Float32Array, Uint8Array, etc. (standard in ONNX/WASM)
|
|
97
|
+
if (ArrayBuffer.isView(output.data)) {
|
|
98
|
+
vector = Array.from(output.data);
|
|
99
|
+
}
|
|
100
|
+
// Handle plain JS array (older backends or CPU mode)
|
|
101
|
+
else if (Array.isArray(output.data)) {
|
|
102
|
+
vector = Array.from(output.data);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Handle batch output: [tensor]
|
|
106
|
+
else if (Array.isArray(output) && output[0]?.data !== undefined) {
|
|
107
|
+
if (ArrayBuffer.isView(output[0].data)) {
|
|
108
|
+
vector = Array.from(output[0].data);
|
|
109
|
+
} else if (Array.isArray(output[0].data)) {
|
|
110
|
+
vector = Array.from(output[0].data);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Fallback: raw array (rare)
|
|
114
|
+
else if (Array.isArray(output)) {
|
|
115
|
+
vector = output;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Final validation
|
|
119
|
+
if (!Array.isArray(vector) || vector.length !== this.dim) {
|
|
120
|
+
console.error("❌ Invalid embedding vector length:", vector?.length);
|
|
121
|
+
console.error("❌ First few values:", vector?.slice?.(0, 5));
|
|
122
|
+
throw new Error(`Invalid embedding dimension: ${vector?.length || 0} (expected ${this.dim})`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return vector;
|
|
126
|
+
} catch (err) {
|
|
127
|
+
console.error(
|
|
128
|
+
`❌ Embedding failed for text: "${text.slice(0, 60)}..."`,
|
|
129
|
+
err.message
|
|
130
|
+
);
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async embedBatch(texts = []) {
|
|
136
|
+
if (!Array.isArray(texts)) {
|
|
137
|
+
throw new Error("embedBatch expects an array of strings");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const results = [];
|
|
141
|
+
for (const text of texts) {
|
|
142
|
+
results.push(await this.embed(text));
|
|
143
|
+
}
|
|
144
|
+
return results;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getDimension() {
|
|
148
|
+
return this.dim;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const embedder = new LocalEmbedding();
|
|
153
|
+
module.exports = embedder;
|
package/src/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// index.js (6 lines)
|
|
2
1
|
const semanticResolver = require("./resolver");
|
|
3
2
|
|
|
4
3
|
async function docSearchResolver(action, context) {
|
|
@@ -6,4 +5,5 @@ async function docSearchResolver(action, context) {
|
|
|
6
5
|
}
|
|
7
6
|
|
|
8
7
|
docSearchResolver.resolverName = "doc-search";
|
|
8
|
+
docSearchResolver.version = "1.0.41";
|
|
9
9
|
module.exports = docSearchResolver;
|
|
@@ -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
|
-
}
|
|
Binary file
|