wazap-mcp 0.18.1 → 0.18.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.
- package/dist/recall/engine.js +14 -6
- package/dist/recall/models.js +4 -0
- package/dist/recall/settings.js +5 -3
- package/dist/recall/store.js +2 -1
- package/dist/whatsapp.js +4 -4
- package/package.json +1 -1
package/dist/recall/engine.js
CHANGED
|
@@ -211,13 +211,15 @@ class UrlBackend {
|
|
|
211
211
|
*/
|
|
212
212
|
export class EmbedEngine {
|
|
213
213
|
target;
|
|
214
|
-
|
|
214
|
+
spec;
|
|
215
|
+
constructor(target, spec) {
|
|
215
216
|
this.target = target;
|
|
217
|
+
this.spec = spec;
|
|
216
218
|
}
|
|
217
219
|
static async start(settings, spec, onLog = () => { }) {
|
|
218
220
|
if (settings.embedUrl !== null) {
|
|
219
221
|
const base = settings.embedUrl.replace(/\/+$/, "");
|
|
220
|
-
return new EmbedEngine(new UrlBackend(base));
|
|
222
|
+
return new EmbedEngine(new UrlBackend(base), spec);
|
|
221
223
|
}
|
|
222
224
|
const bin = findLlama(settings);
|
|
223
225
|
if (bin === null) {
|
|
@@ -225,18 +227,24 @@ export class EmbedEngine {
|
|
|
225
227
|
}
|
|
226
228
|
const sidecar = new LlamaSidecar(bin, embedModelPath(settings.modelsDir, spec), onLog);
|
|
227
229
|
await sidecar.start();
|
|
228
|
-
return new EmbedEngine(sidecar);
|
|
230
|
+
return new EmbedEngine(sidecar, spec);
|
|
229
231
|
}
|
|
230
|
-
|
|
232
|
+
/**
|
|
233
|
+
* `kind` picks the model's task prefix: the index holds "document" texts,
|
|
234
|
+
* searches embed "query". The prefix is the model's side of a retrieval
|
|
235
|
+
* pair, not part of what the index stores.
|
|
236
|
+
*/
|
|
237
|
+
async embed(texts, kind) {
|
|
231
238
|
if (texts.length === 0)
|
|
232
239
|
return [];
|
|
233
240
|
await this.target.waitReady();
|
|
241
|
+
const input = texts.map((text) => `${this.spec.prompts[kind]}${text}`);
|
|
234
242
|
let response;
|
|
235
243
|
try {
|
|
236
244
|
response = await fetch(`${this.target.base}${EMBED_PATH}`, {
|
|
237
245
|
method: "POST",
|
|
238
246
|
headers: { "content-type": "application/json" },
|
|
239
|
-
body: JSON.stringify({ content:
|
|
247
|
+
body: JSON.stringify({ content: input.length === 1 ? input[0] : input }),
|
|
240
248
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
241
249
|
});
|
|
242
250
|
}
|
|
@@ -251,7 +259,7 @@ export class EmbedEngine {
|
|
|
251
259
|
if (!Array.isArray(reply)) {
|
|
252
260
|
throw new WazapError("RECALL_FAILED", `embedding server refused: ${reply.error?.message ?? "bad reply"}`);
|
|
253
261
|
}
|
|
254
|
-
if (reply.length !==
|
|
262
|
+
if (reply.length !== input.length) {
|
|
255
263
|
throw new WazapError("RECALL_FAILED", `embedding server returned ${reply.length} vectors for ${texts.length} texts`);
|
|
256
264
|
}
|
|
257
265
|
return reply.map((item, i) => {
|
package/dist/recall/models.js
CHANGED
|
@@ -22,6 +22,8 @@ export const EMBED_MODELS = {
|
|
|
22
22
|
sha256: "b5ce9d77a3fc4b3b39ccb5643c36777911cc4eb46a66962eadfa3f5f60490d63",
|
|
23
23
|
dims: 768,
|
|
24
24
|
url: "https://huggingface.co/ggml-org/embeddinggemma-300M-GGUF/resolve/main/embeddinggemma-300M-Q8_0.gguf",
|
|
25
|
+
// EmbeddingGemma's own retrieval task, from its model card.
|
|
26
|
+
prompts: { query: "task: search result | query: ", document: "title: none | text: " },
|
|
25
27
|
},
|
|
26
28
|
"e5-base-multilingual": {
|
|
27
29
|
alias: "e5-base-multilingual",
|
|
@@ -30,6 +32,8 @@ export const EMBED_MODELS = {
|
|
|
30
32
|
sha256: "548c31b068947aa26b86c8bbfc1f2fabe5233f6d0e1241319832b20a01e5968a",
|
|
31
33
|
dims: 768,
|
|
32
34
|
url: "https://huggingface.co/dinab/multilingual-e5-base-Q8_0-GGUF/resolve/main/multilingual-e5-base-q8_0.gguf",
|
|
35
|
+
// e5's documented asymmetric prefixes.
|
|
36
|
+
prompts: { query: "query: ", document: "passage: " },
|
|
33
37
|
},
|
|
34
38
|
};
|
|
35
39
|
export function embedModelSpec(name) {
|
package/dist/recall/settings.js
CHANGED
|
@@ -12,10 +12,12 @@ const MODEL_ALIASES = ["embeddinggemma-300m", "e5-base-multilingual"];
|
|
|
12
12
|
const DEFAULT_MAX_ROWS = 50_000;
|
|
13
13
|
const MIN_MAX_ROWS = 100;
|
|
14
14
|
/**
|
|
15
|
-
* Cosine floor for embeddinggemma-300m, measured on
|
|
16
|
-
*
|
|
15
|
+
* Cosine floor for embeddinggemma-300m under its task prompts, measured on a
|
|
16
|
+
* real index: noise tops out ~0.31, real paraphrases start ~0.35. The prompts
|
|
17
|
+
* widened the noise/signal gap enough for the floor to mean something.
|
|
18
|
+
* e5-base needs its own calibration.
|
|
17
19
|
*/
|
|
18
|
-
const DEFAULT_MIN_SIMILARITY = 0.
|
|
20
|
+
const DEFAULT_MIN_SIMILARITY = 0.35;
|
|
19
21
|
function parseEnabled(raw) {
|
|
20
22
|
const value = stripPasted(raw ?? "").toLowerCase();
|
|
21
23
|
if (OFF.has(value))
|
package/dist/recall/store.js
CHANGED
|
@@ -21,7 +21,8 @@ import { WazapError } from "../errors.js";
|
|
|
21
21
|
export const TEXT_CAP = 2048;
|
|
22
22
|
/** Rewrite meta+vectors when more than this share of rows is dead. */
|
|
23
23
|
const COMPACT_DEAD_RATIO = 0.3;
|
|
24
|
-
|
|
24
|
+
/** v2: model task prompts — a raw-embedded index belongs to a different world. */
|
|
25
|
+
const STATE_VERSION = 2;
|
|
25
26
|
const QUANT = "int8";
|
|
26
27
|
/** The index holds message text; it gets history's permissions, not the defaults. */
|
|
27
28
|
const DIR_MODE = 0o700;
|
package/dist/whatsapp.js
CHANGED
|
@@ -624,7 +624,7 @@ export class WhatsAppService {
|
|
|
624
624
|
const store = this.readyRecall();
|
|
625
625
|
const scope = chatId === undefined ? undefined : this.resolveId(chatId);
|
|
626
626
|
const from = opts.from === undefined ? undefined : opts.from === "me" ? this.ownJid() : this.resolveId(opts.from);
|
|
627
|
-
const [vector] = await this.recallEmbed([query]);
|
|
627
|
+
const [vector] = await this.recallEmbed([query], "query");
|
|
628
628
|
const minSimilarity = this.recallEnv instanceof WazapError ? undefined : this.recallEnv.minSimilarity;
|
|
629
629
|
const hits = store
|
|
630
630
|
.query({ vector: vector, chatId: scope, sinceMs: opts.sinceMs, untilMs: opts.untilMs, from, minSimilarity, limit })
|
|
@@ -1237,7 +1237,7 @@ export class WhatsAppService {
|
|
|
1237
1237
|
try {
|
|
1238
1238
|
const spec = EMBED_MODELS[this.recallEnv.model];
|
|
1239
1239
|
this.recallStore = await RecallStore.open(join(this.paths.root, "recall"), spec, this.recallEnv.maxRows);
|
|
1240
|
-
this.recallQueue = new RecallQueue(this.recallStore, (texts) => this.recallEmbed(texts));
|
|
1240
|
+
this.recallQueue = new RecallQueue(this.recallStore, (texts) => this.recallEmbed(texts, "document"));
|
|
1241
1241
|
if (this.recallStore.count > 0)
|
|
1242
1242
|
log(`recall index: ${this.recallStore.count} messages`);
|
|
1243
1243
|
}
|
|
@@ -1270,9 +1270,9 @@ export class WhatsAppService {
|
|
|
1270
1270
|
}
|
|
1271
1271
|
return this.recallEngineP;
|
|
1272
1272
|
}
|
|
1273
|
-
async recallEmbed(texts) {
|
|
1273
|
+
async recallEmbed(texts, kind) {
|
|
1274
1274
|
const engine = await this.recallEngine();
|
|
1275
|
-
return engine.embed(texts);
|
|
1275
|
+
return engine.embed(texts, kind);
|
|
1276
1276
|
}
|
|
1277
1277
|
/**
|
|
1278
1278
|
* The ops one raw message turns into: the tombstone a revoke carries for the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wazap-mcp",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"mcpName": "io.github.razvangirgiz/wazap",
|
|
5
5
|
"description": "WhatsApp for your AI agent. MCP server over Baileys: pairing-code login, several accounts, 33 tools, stdio or HTTP.",
|
|
6
6
|
"license": "MIT",
|