wazap-mcp 0.18.0 → 0.18.1
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/messages.js +3 -3
- package/dist/recall/settings.js +15 -0
- package/dist/recall/store.js +2 -1
- package/dist/tools.js +8 -0
- package/dist/whatsapp.js +2 -1
- package/package.json +1 -1
package/dist/messages.js
CHANGED
|
@@ -399,9 +399,9 @@ export function searchableText(raw, transcript) {
|
|
|
399
399
|
if (type === "reaction" || type === "deleted" || type === "system")
|
|
400
400
|
return null;
|
|
401
401
|
const own = rule.text?.(node)?.trim() || rule.caption?.(node)?.trim() || rule.detail?.(node)?.trim() || "";
|
|
402
|
-
//
|
|
403
|
-
// noise that outranks real hits on short queries.
|
|
404
|
-
if (
|
|
402
|
+
// Under five letters or digits there is no meaning to embed — a "🥰🥰", a
|
|
403
|
+
// "Da" or a "..." only adds noise that outranks real hits on short queries.
|
|
404
|
+
if ((own.match(/[\p{L}\p{N}]/gu)?.length ?? 0) < 5 && spoken === undefined)
|
|
405
405
|
return null;
|
|
406
406
|
return viewText(raw, transcript);
|
|
407
407
|
}
|
package/dist/recall/settings.js
CHANGED
|
@@ -11,6 +11,11 @@ const ON = new Set(["local", "on", "1", "yes", "true"]);
|
|
|
11
11
|
const MODEL_ALIASES = ["embeddinggemma-300m", "e5-base-multilingual"];
|
|
12
12
|
const DEFAULT_MAX_ROWS = 50_000;
|
|
13
13
|
const MIN_MAX_ROWS = 100;
|
|
14
|
+
/**
|
|
15
|
+
* Cosine floor for embeddinggemma-300m, measured on real history: noise sits
|
|
16
|
+
* at ~0.45-0.54, real matches start ~0.5. e5-base needs its own calibration.
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_MIN_SIMILARITY = 0.5;
|
|
14
19
|
function parseEnabled(raw) {
|
|
15
20
|
const value = stripPasted(raw ?? "").toLowerCase();
|
|
16
21
|
if (OFF.has(value))
|
|
@@ -36,6 +41,15 @@ function parseMaxRows(raw) {
|
|
|
36
41
|
return n;
|
|
37
42
|
throw new WazapError("INVALID_ID", `WAZAP_RECALL_MAX must be a number >= ${MIN_MAX_ROWS}, got "${value}".`, "Fix WAZAP_RECALL_MAX or remove it");
|
|
38
43
|
}
|
|
44
|
+
function parseMinSimilarity(raw) {
|
|
45
|
+
const value = stripPasted(raw ?? "");
|
|
46
|
+
if (value === "")
|
|
47
|
+
return DEFAULT_MIN_SIMILARITY;
|
|
48
|
+
const n = Number(value);
|
|
49
|
+
if (Number.isFinite(n) && n >= 0 && n <= 1)
|
|
50
|
+
return n;
|
|
51
|
+
throw new WazapError("INVALID_ID", `WAZAP_RECALL_MIN_SIMILARITY must be a number between 0 and 1, got "${value}".`, "Fix WAZAP_RECALL_MIN_SIMILARITY or remove it");
|
|
52
|
+
}
|
|
39
53
|
function parseUrl(raw) {
|
|
40
54
|
const value = stripPasted(raw ?? "");
|
|
41
55
|
if (value === "")
|
|
@@ -59,5 +73,6 @@ export function readRecallSettings(env, dataDir) {
|
|
|
59
73
|
embedUrl: parseUrl(env.WAZAP_EMBED_URL),
|
|
60
74
|
modelsDir: join(dataDir, "models"),
|
|
61
75
|
maxRows: parseMaxRows(env.WAZAP_RECALL_MAX),
|
|
76
|
+
minSimilarity: parseMinSimilarity(env.WAZAP_RECALL_MIN_SIMILARITY),
|
|
62
77
|
};
|
|
63
78
|
}
|
package/dist/recall/store.js
CHANGED
|
@@ -334,6 +334,7 @@ export class RecallStore {
|
|
|
334
334
|
*/
|
|
335
335
|
query(q, nowMs = Date.now()) {
|
|
336
336
|
const unit = normalize(q.vector);
|
|
337
|
+
const floor = q.minSimilarity ?? 0;
|
|
337
338
|
const hits = [];
|
|
338
339
|
for (const record of this.live.values()) {
|
|
339
340
|
if (q.chatId !== undefined && record.jid !== q.chatId)
|
|
@@ -349,7 +350,7 @@ export class RecallStore {
|
|
|
349
350
|
for (let i = 0; i < this.spec.dims; i++)
|
|
350
351
|
dot += unit[i] * this.vectors[offset + i];
|
|
351
352
|
const similarity = dot / 127;
|
|
352
|
-
if (similarity <= 0)
|
|
353
|
+
if (similarity <= 0 || similarity < floor)
|
|
353
354
|
continue;
|
|
354
355
|
hits.push({ record, similarity, score: similarity * recencyDecay(nowMs - record.ts) });
|
|
355
356
|
}
|
package/dist/tools.js
CHANGED
|
@@ -503,6 +503,8 @@ Each result carries its date and a score: semantic similarity scaled by
|
|
|
503
503
|
recency, so fresh matches rank first. chat_id, since, until and from narrow
|
|
504
504
|
the search exactly like search_messages. A hit marked "index only" lives in
|
|
505
505
|
the index alone: quote it, but get_message and download_media cannot see it.
|
|
506
|
+
Results under the similarity floor are dropped rather than listed; when only
|
|
507
|
+
weak matches survive, the output says so — do not present them as found facts.
|
|
506
508
|
|
|
507
509
|
RECALL_UNAVAILABLE means recall is off or the embedding setup is missing; the
|
|
508
510
|
fix names the command the user has to run. Do not retry it.`,
|
|
@@ -1121,7 +1123,13 @@ function renderRecall(title, answer) {
|
|
|
1121
1123
|
if (hits.length === 0) {
|
|
1122
1124
|
return `${title}: no messages found.${catchingUp ? ` ${catchingUp}` : ""}`;
|
|
1123
1125
|
}
|
|
1126
|
+
// Under ~0.55 cosine, embeddinggemma matches are usually coincidental — the
|
|
1127
|
+
// agent must not present them as found facts.
|
|
1128
|
+
const weak = Math.max(...hits.map((h) => h.similarity)) < 0.55;
|
|
1124
1129
|
const lines = [`# ${title} (${hits.length})`, ""];
|
|
1130
|
+
if (weak) {
|
|
1131
|
+
lines.push(`Weak matches only (best similarity ${Math.max(...hits.map((h) => h.similarity)).toFixed(2)}): the query may have no real answer — treat these as guesses.`, "");
|
|
1132
|
+
}
|
|
1125
1133
|
if (catchingUp)
|
|
1126
1134
|
lines.push(catchingUp, "");
|
|
1127
1135
|
const introduced = new Set();
|
package/dist/whatsapp.js
CHANGED
|
@@ -625,8 +625,9 @@ export class WhatsAppService {
|
|
|
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
627
|
const [vector] = await this.recallEmbed([query]);
|
|
628
|
+
const minSimilarity = this.recallEnv instanceof WazapError ? undefined : this.recallEnv.minSimilarity;
|
|
628
629
|
const hits = store
|
|
629
|
-
.query({ vector: vector, chatId: scope, sinceMs: opts.sinceMs, untilMs: opts.untilMs, from, limit })
|
|
630
|
+
.query({ vector: vector, chatId: scope, sinceMs: opts.sinceMs, untilMs: opts.untilMs, from, minSimilarity, limit })
|
|
630
631
|
.map((hit) => {
|
|
631
632
|
const live = this.store.messages.has(hit.record.sid);
|
|
632
633
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wazap-mcp",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.1",
|
|
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",
|