fix: CJK-aware remember dedup with negation guard (#1021)#1037
fix: CJK-aware remember dedup with negation guard (#1021)#1037ziishanahmad wants to merge 1 commit into
Conversation
- Make jaccardSimilarity CJK-aware: use segmentCjk + character bigrams for overlap so no-space Chinese/Japanese/Korean text produces useful similarity scores instead of near-disjoint token sets - Add negation guard (memory-dedup.ts) so 'do X' vs 'do not X' and '使用' vs '不要使用' are not superseded despite high token overlap - Preserve original English whitespace-split behaviour when no CJK characters are present (fast path, zero regression) - Add tests: pure-function similarity (19), end-to-end CJK dedup (6) covering duplicate supersede, distinct non-supersede, negation conflict, mixed-language, and cross-project isolation
|
@ziishanahmad is attempting to deploy a commit to the rohitg00's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughAdds CJK-aware tokenization to the Jaccard similarity function in schema.ts, introduces a new memory-dedup module with an isNegationConflict guard, and wires the guard into mem::remember's supersession logic to prevent superseding conflicting negated statements. Adds corresponding unit and integration tests. ChangesCJK dedup and negation guard
Estimated code review effort: 2 (Simple) | ~12 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant Remember as mem::remember
participant Schema as jaccardSimilarity
participant Dedup as isNegationConflict
Caller->>Remember: submit new memory content
Remember->>Schema: compute similarity(new, existing)
Schema-->>Remember: similarity score
alt similarity > 0.7
Remember->>Dedup: isNegationConflict(new, existing)
Dedup-->>Remember: conflict true/false
alt conflict is false
Remember->>Remember: mark existing isLatest=false, save new as latest
else conflict is true
Remember->>Remember: keep existing memory, save new separately
end
else similarity <= 0.7
Remember->>Remember: save new memory as separate entry
end
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/state/schema.ts (1)
108-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider lowercasing tokens in the fast path for consistency with the CJK path.
The fast path doesn't lowercase tokens (
text.split(/\s+/).filter(...)) while the CJK path does (cleaned...toLowerCase()at line 120). This means the same logical inputs can produce different similarity scores depending on whether CJK characters are present. The current production caller lowercases beforehand so there's no practical impact today, but makingtokenizeForJaccardself-contained avoids a subtle trap for future callers.♻️ Suggested fix
if (!hasCjk(text)) { - return new Set(text.split(/\s+/).filter((t) => t.length > 2)); + return new Set( + text.toLowerCase().split(/\s+/).filter((t) => t.length > 2), + ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/state/schema.ts` around lines 108 - 111, The fast path in tokenizeForJaccard currently returns whitespace-split tokens without normalizing case, unlike the CJK branch, so update that branch to lowercase tokens before filtering/adding them to the Set. Keep the behavior consistent with the cleaned...toLowerCase() logic used in the CJK path, so the function is self-contained and produces stable Jaccard tokens regardless of input casing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/state/schema.ts`:
- Around line 108-111: The fast path in tokenizeForJaccard currently returns
whitespace-split tokens without normalizing case, unlike the CJK branch, so
update that branch to lowercase tokens before filtering/adding them to the Set.
Keep the behavior consistent with the cleaned...toLowerCase() logic used in the
CJK path, so the function is self-contained and produces stable Jaccard tokens
regardless of input casing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7a4ca43c-c311-405a-92ff-6ac04d861856
📒 Files selected for processing (5)
src/functions/remember.tssrc/state/memory-dedup.tssrc/state/schema.tstest/remember-cjk-dedup.test.tstest/remember-dedup-similarity.test.ts
Problem
mem::rememberdedup usedjaccardSimilaritywith a whitespace-only tokenizer (split(/\s+/)). This works for English but makes Chinese/Japanese/Korean text — typically written without spaces — look nearly disjoint. Repeated CJK preferences were saved as separateisLatest=truememories instead of superseding older entries.Fix
1. CJK-aware tokenizer in
jaccardSimilarity(src/state/schema.ts)segmentCjkfromsrc/state/cjk-segmenter.ts(already used by BM25 search)\p{L}\p{N}Unicode property strip2. Negation guard (
src/state/memory-dedup.ts)Prevents
do Xfrom supersedingdo not X(and vice versa) even when token overlap exceeds the 0.7 threshold. Supports:not,never,don't,do not,cannot,should not,must not,no不要,别,勿,無,无,不,なし,않,안,못If both sides have negation markers, they are treated as compatible (both say "don't do X") and the guard does not fire.
3. Wired into
remember.tsThe dedup loop now checks
!isNegationConflict()before superseding.Tests
test/remember-dedup-similarity.test.ts(19 tests): pure-function tests forjaccardSimilarity(English regression, CJK identical/near-identical/reordered/distinct/mixed) andisNegationConflict(English do/do-not, Chinese 使用/不要使用, both-negated, both-affirmative)test/remember-cjk-dedup.test.ts(6 tests): end-to-endmem::remembertests covering pure Chinese duplicate supersede, mixed Chinese/English supersede, distinct non-supersede, English negation conflict, Chinese negation conflict, cross-project isolationDesign decisions
segmentCjkwhich optionally uses@node-rs/jieba(already in the tree for BM25)mem::rememberlow-latencyCloses #1021
Summary by CodeRabbit
New Features
Bug Fixes