Skip to content

feat(web): personalize example sentences with the viewed word [SIGN-700] - #7

Merged
AmitMY merged 6 commits into
mainfrom
sign-700-personalized-examples
Jul 20, 2026
Merged

feat(web): personalize example sentences with the viewed word [SIGN-700]#7
AmitMY merged 6 commits into
mainfrom
sign-700-personalized-examples

Conversation

@AmitMY

@AmitMY AmitMY commented Jul 20, 2026

Copy link
Copy Markdown

What

When the words endpoint (/lexicons/{lex}/words?form=...) serves a synset's examples, it now rewrites each example to use the word being viewed instead of the synonym the example was authored with — e.g. viewing zany (rylo.com/sign/dictionary/en/wordnet/zany):

wore a goofy hat → wore a zany hat

English lexicons only. The synset detail endpoint is unchanged (it has no word context).

How conservative is it?

Deliberately very. An example is returned unchanged unless every guard passes. The guards below came from a census of all 48,311 examples across the 32,917 omw-en:1.4 synsets that have them, simulating naive substitution and bucketing what breaks (all sample sentences below are real dataset rows, verified against the live API).

Cases we do NOT replace in

  1. The example is (or contains) a quotation — 780 examples contain double quotes; 716 of those are attributed citations. Substituting inside them fabricates quotes:

    "Vengeance is mine; I will repay, saith the Lord" --Romans 12:19"Payback is mine…" (viewing payback)

  2. The viewed word — in any inflected form — is already in the example. Nothing to diversify, and touching it risks duplication:

    his hands shook while viewing shake (form "shook" is present)

  3. No synonym appears in the example at all (17.5% of examples, mostly conjugated verb examples like The patient is respiring for breathe/respire).

  4. A synonym appears only inflected — upgraded in review: noun plurals and 3rd-person-singular verbs now match and are mirrored onto the viewed word ("he acted with the best of motives" → "…best of motivations" viewing motivation; "The smoker coughs all day" → "The smoker hacks all day" viewing hack). Generation trusts only regular suffixation for verbs (excluding be/have/do and irregular-noun-plural traps like leafleaves), and nouns inflect already considers plural are skipped. This unlocks ~1,900 additional substitutions on top of the ~34,000 exact-form ones. Other tenses ("froze", "greedier") remain untouched.

  5. The viewed word is not a single lowercase word. Multi-word lemmas (course of action), proper nouns (Attorney General of the United States — 1,366 capitalized in-sentence matches, mostly proper-noun synsets), digits, apostrophes — all skipped.

  6. The matched synonym is not a single lowercase word, same reasons — 7,191 substitution pairs involve a multi-word member ("events now in process" → "in physical process").

  7. More than one synonym matches, or one synonym matches twice. Which one to replace is ambiguous:

    he huffed and puffed (viewing chuff)

    This also guards the overlap case — a single-word member matching inside a sibling multi-word member:

    once a nation is embarked on a course of action…course matches inside course of action, both are members, so 2 matches → skip

  8. The matched token has unusual casing (not lowercase, not sentence-case) — e.g. GOOFY in an all-caps fragment. We only mirror lowercase → lowercase and TourismPayback-style sentence case.

  9. An article precedes and the replacement starts with "u" — resolved in review: a/an is now corrected via the inflect library (sound-aware: "an hour", "a university", "a one-sided", "an onerous"), so no onset is ambiguous and nothing is skipped for article reasons (1,958 affected pairs).

What does get replaced

Exactly one single-word, lowercase synonym, matching once, on word boundaries (hyphen-aware, so "face" never matches inside "about-face"), in an unquoted example, with the viewed word absent — replaced preserving sentence-case and with the preceding article corrected.

Testing

  • The logic lives in wn/personalized_examples.py; unit tests for every guard and every substitution behavior in tests/personalized_examples_test.py.
  • Endpoint tests against the mini fixture: personalized for illustration, untouched for example (word already present), untouched for es (non-English). One unquoted <Example> added to the fixture's test-en-0002-n for the positive path.
  • hatch fmt --linter --check, hatch run mypy:check, full pytest suite: green.

🤖 Generated with Claude Code


Note

Medium Risk
Changes JSON payload text for English /words responses (read-only dictionary data); logic is guarded but could still produce odd sentences in edge cases.

Overview
English words responses now rewrite synset examples so they use the lemma being looked up instead of a sibling synonym (e.g. goofyzany), via new personalize_example in wn/personalized_examples.py. Synset-only endpoints are unchanged.

Substitution is intentionally narrow: skip quoted text, multi-word lemmas, ambiguous or inflected-only matches, and similar edge cases; when a swap happens, it can adjust a/an (via new inflect web dependency) and limited noun plurals / regular 3sg verbs. Non-English lexicons leave examples as stored.

Coverage adds unit tests for guards and inflection, web tests on the mini fixture, and an extra unquoted example on test-en-0002-n.

Reviewed by Cursor Bugbot for commit 10e4dd7. Bugbot is set up for automated code reviews on this repo. Configure here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@AmitMY, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ab08a7f-a728-4a01-a4bf-c4ebe445bbcb

📥 Commits

Reviewing files that changed from the base of the PR and between 5bd1efc and 10e4dd7.

📒 Files selected for processing (6)
  • pyproject.toml
  • tests/data/mini-lmf-1.0.xml
  • tests/personalized_examples_test.py
  • tests/web_test.py
  • wn/personalized_examples.py
  • wn/web.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sign-700-personalized-examples

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread wn/web.py Outdated
AmitMY and others added 2 commits July 20, 2026 09:06
…ples.py [SIGN-700]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread wn/personalized_examples.py Outdated
AmitMY and others added 3 commits July 20, 2026 09:18
Replaces the hand-rolled silent-h/glide-onset heuristics and their
ambiguous-onset bailouts; u- and one-initial replacements now substitute
instead of being skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Noun plurals and 3rd-person-singular verbs now match and are mirrored
onto the viewed word ('the best of motives' -> 'the best of
motivations'); 3sg generation trusts only regular suffixation and
excludes be/have/do. Unlocks ~1,900 additional substitutions in
omw-en:1.4 on top of ~34,000 exact-form ones.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 10e4dd7. Configure here.

boundary = rf'[\w{_APOSTROPHES}-]'
return re.compile(
rf'(?<!{boundary}){re.escape(word)}(?!{boundary})', re.IGNORECASE
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possessives evade presence guard

Low Severity

Apostrophes are treated as word characters in _word_pattern, so a possessive like zany's does not count as the viewed word being present. Guard 2 can miss that case and still substitute a synonym, producing a duplicated lemma in the example.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 10e4dd7. Configure here.

Comment thread wn/web.py
ex, w.lemma(), w.forms(), attrs['members'], ss.pos or ''
)
for ex in attrs['examples']
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant per-example DB lookups

Low Severity

Inside the synset loop, personalize_example is called with w.lemma() and w.forms() once per example. Both methods hit get_entry_forms with no caching, so the same two DB reads repeat for every example instead of once per word.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 10e4dd7. Configure here.

@AmitMY
AmitMY merged commit 24cef47 into main Jul 20, 2026
13 checks passed
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.

1 participant