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 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
- // No letter or digit means no words to embed — a "🥰🥰" or "..." only adds
403
- // noise that outranks real hits on short queries.
404
- if (!/[\p{L}\p{N}]/u.test(own) && spoken === undefined)
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
  }
@@ -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
  }
@@ -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.0",
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",