Skip to content

feat: exact tag-scoped recall via getByIds (#141)#143

Merged
rahilp merged 8 commits into
mainfrom
141-exact-tag-scoped-recall-via-getbyids-stop-losing-tagged-results-to-the-top-50-cap
Jun 10, 2026
Merged

feat: exact tag-scoped recall via getByIds (#141)#143
rahilp merged 8 commits into
mainfrom
141-exact-tag-scoped-recall-via-getbyids-stop-losing-tagged-results-to-the-top-50-cap

Conversation

@rahilp

@rahilp rahilp commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Resolves #141

Summary

  • Tag-filtered recall now scores the tag's own vectors directly (D1 vector_ids → batched VECTORIZE.getByIds → new cosineSim helper vs the query embedding) instead of post-filtering a global top-50 Vectorize query, so tagged memories can no longer be silently lost to the cap. Untagged recall is unchanged, including the low-score retry.
  • The now-dead tagFilterIds in-memory post-filter was removed.
  • Hardening fixes found in review and production testing: cosineSim guards on raw norms (float underflow); the candidate-scoring IN query is chunked at D1_MAX_BOUND_PARAMS = 100 (the tag path can produce >100 candidates, exceeding D1's bound-parameter limit); getByIds batches capped at 20 (Vectorize rejects more with VECTOR_GET_ERROR 40007).
  • Web UI: the Recall chat flow now uses the structured GET /recall REST endpoint instead of re-parsing the MCP tool's formatted text, fixing the inflated "sources" count when memory content contains list items. Source cards also gain real entry IDs (working Append/Forget), server errors no longer render as "no results", and the /chat context keeps dates/tags/source for temporal questions.

Test Plan

  • 12 new/updated integration tests in test/integration/recall.test.ts: beyond-top-50 surfacing, cosine ranking, stale vector IDs (partial + total), no-vector short-circuit, 20-ID getByIds batching, shared-vector dedupe, chunk parentId dedupe, topK in tag path, >100-candidate D1 chunking
  • 5 new unit tests for cosineSim; 1 new UI contract test for the REST /recall response shape
  • Full suite: 300/300 passing; tsc --noEmit clean

rahilp added 6 commits June 9, 2026 20:59
Adds a cosine similarity function for comparing vector embeddings.
BGE embeddings are not normalized, so the denominator matters for
keeping tag-path scores on the same scale as Vectorize's cosine scores.
Tag-filtered recall now scores the tag's own vectors directly instead of
post-filtering a global top-50 Vectorize query, so tagged memories can no
longer be lost to the cap. Untagged recall is unchanged.
…141)

The tag path can produce more than 100 candidates (that's the point of
the feature), which would exceed D1's per-query bound parameter limit.
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 82.27% 585 / 711
🔵 Statements 83.09% 688 / 828
🔵 Functions 81.81% 90 / 110
🔵 Branches 76.53% 398 / 520
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/index.ts 81.98% 74.52% 80.39% 81.22% 111-158, 178, 463, 470, 511, 739, 850, 856, 865, 873-893, 974-977, 1009, 1016, 1017, 1028, 1050-1058, 1068, 1120, 1161, 1174, 1220-1442, 1451-1455, 1468-1493, 1512, 1554, 1592, 1626, 1792-1803, 1865, 1875
Generated in workflow #139 for commit c9d7e7f by the Vitest Coverage Report Action

rahilp added 2 commits June 9, 2026 21:35
…141)

Production returned VECTOR_GET_ERROR code 40007 ("more than the maximum
allowed count of 20") for tags with more than 20 vectors. The 500 batch
size was a guess; the real limit is 20.
…rsing MCP text

The Recall chat flow parsed the MCP tool's formatted text to build source
cards, splitting on anything that looked like a list item — so memories
containing bullets or numbered lines were counted as multiple sources
(e.g. 25 sources from 5 matches). The UI now calls GET /recall for
structured JSON and serializes the results itself for the /chat context.
@rahilp rahilp force-pushed the 141-exact-tag-scoped-recall-via-getbyids-stop-losing-tagged-results-to-the-top-50-cap branch from bffb340 to c9d7e7f Compare June 10, 2026 02:05
@rahilp rahilp merged commit 297cfea into main Jun 10, 2026
1 check passed
@rahilp rahilp deleted the 141-exact-tag-scoped-recall-via-getbyids-stop-losing-tagged-results-to-the-top-50-cap branch June 10, 2026 02:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exact tag-scoped recall via getByIds (stop losing tagged results to the top-50 cap)

1 participant