Skip to content

fix(kazakh): preserve protected multi-period sentinels - #92

Merged
yisding merged 3 commits into
mainfrom
codex/fix-kazakh-regex-over-splitting-issue
Jul 11, 2026
Merged

fix(kazakh): preserve protected multi-period sentinels#92
yisding merged 3 commits into
mainfrom
codex/fix-kazakh-regex-over-splitting-issue

Conversation

@yisding

@yisding yisding commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Prevent a regression where Kazakh uppercase multi-period abbreviations that the PeriodClassifier already protected with the sentinel could be reclassified by the multi-period pass and have their final sentinel converted back to a literal period, causing an erroneous sentence split.

Description

  • Add a guard in replace_multi_period_abbreviations to keep protect_final_period = True when the matched token already ends with the sentinel, preventing the pass from undoing the classifier's decision.
  • Add a regression test test_uppercase_dotless_multiperiod_keeps_classifier_protected_final_period in tests/regression/test_kazakh_multiperiod_sentinel.py that asserts balanced-mode segmentation no longer over-splits "Бұл Т.С.С. I went.".

Testing

  • Ran uv run pytest tests/regression/test_kazakh_multiperiod_sentinel.py tests/lang/test_kazakh.py tests/contract/test_segmenter.py --color yes and the targeted tests passed.
  • Ran uv run ruff check . and uv run ruff format --check . and formatting/lint checks passed.
  • Ran the full suite with uv run pytest --cov=sentencesplit tests/ --color yes and the test suite completed successfully (all tests passed/expected skips and xfails unchanged).

Codex Task

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of multi-period abbreviations in Kazakh so classifier-protected trailing periods are preserved, preventing incorrect sentence boundaries.
    • Fixed sentence splitting when uppercase dotless multi-period abbreviations (e.g., “Т.С.С.”) appear mid-sentence.
    • Corrected segmentation for the initialism “U.S.” when followed immediately by a standalone letter (e.g., “U.S. I”) across multiple languages and split modes.
  • Tests
    • Added regression tests covering Kazakh multi-period sentinel behavior and “U.S.” followed by standalone letters behavior across languages and split modes.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 42949ef396

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sentencesplit/abbreviation_replacer.py Outdated
protect_final_period = True
elif titled_name_prefix:
protect_final_period = True
elif matched.endswith("∯"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve split-mode handling for protected initialisms

When a known two-letter initialism was already protected by the classifier because the next token matches the REGULAR I follower, this branch skips the existing split_candidate && likely_start logic below. In balanced/aggressive modes, profiles such as zh, ja, nl, it, and mr now merge I live in the U.S. I left. into one sentence, even though the same U.S. before other capital starts like How or Élodie still splits and the split-mode contract/tests expect non-English two-letter initialisms before capitals to split. Please narrow this preservation to the Kazakh case or allow the initialism split logic to override these sentinelized matches.

Useful? React with 👍 / 👎.

@charliecreates charliecreates 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.

Blocking feedback

  1. The new global sentinel short-circuit changes split-mode behavior for declared two-letter initialisms before I ... in non-Kazakh profiles — sentencesplit/abbreviation_replacer.py#L636.

If you want me to push a fix, reply with the item number (for example: please fix 1).

Comment thread sentencesplit/abbreviation_replacer.py Outdated
protect_final_period = True
elif titled_name_prefix:
protect_final_period = True
elif matched.endswith("∯"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This matched.endswith("∯") guard runs for every language, not just Kazakh, and it bypasses the split-mode dial for any multi-period abbreviation whose final period was already protected by the classifier.

That introduces a cross-language behavior change: before this PR (d198fe2), Segmenter("it", split_mode="balanced").segment("Je vois U.S. I went.") produced ["Je vois U.S.", "I went."]; on this branch it becomes ["Je vois U.S. I went."] (same change for nl and mr).

Suggested fix: scope this preservation rule to the Kazakh regression path (for example via a Kazakh-only policy/flag or override) instead of globally short-circuiting sentinel-terminated multi-period tokens.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1658e5e1-680f-4694-8807-1e10398253cb

📥 Commits

Reviewing files that changed from the base of the PR and between 0b494e0 and e92c49c.

📒 Files selected for processing (2)
  • sentencesplit/abbreviation_replacer.py
  • sentencesplit/lang/kazakh.py

📝 Walkthrough

Walkthrough

Adds an opt-in abbreviation-rewrite flag, enables it for Kazakh, and adds regression coverage for Kazakh multi-period sentinels and cross-language split-mode behavior around U.S..

Changes

Abbreviation boundary preservation

Layer / File(s) Summary
Protected multi-period rewrite
sentencesplit/abbreviation_replacer.py, sentencesplit/lang/kazakh.py, tests/regression/test_kazakh_multiperiod_sentinel.py
Adds a configurable classifier-sentinel preservation path, enables it for Kazakh, and tests Т.С.С. remaining unsplit.
Initialism split-mode regression
tests/regression/test_issues.py
Verifies conservative mode keeps U.S. I went. together while balanced and aggressive modes split at U.S. across five languages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Segmenter
  participant PeriodClassifier
  participant AbbreviationReplacer
  Segmenter->>PeriodClassifier: classify multi-period abbreviation
  PeriodClassifier->>AbbreviationReplacer: provide protected final-period sentinel
  AbbreviationReplacer->>Segmenter: preserve the classifier boundary decision
Loading

Poem

I’m a rabbit with a careful nose,
Guarding dots where each one goes.
Kazakh sentinels stay in line,
While split modes choose their boundary sign.
Hop, hop—clean sentences now shine!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers motivation, change, and testing, but misses most required template sections and the before/after example. Add the required Summary, Type of change, Linked issues, Input/output examples, Test evidence checklist, and Notes for reviewers sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the Kazakh fix to preserve protected multi-period sentinels.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-kazakh-regex-over-splitting-issue

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@tests/regression/test_kazakh_multiperiod_sentinel.py`:
- Around line 33-36: Extend
test_uppercase_dotless_multiperiod_keeps_classifier_protected_final_period to
run the same assertion for conservative, balanced, and aggressive split modes,
verifying that sentinel-terminated tokens remain unsplit regardless of the
split-mode setting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0d95f77e-3397-41e8-aec6-654d82914e2a

📥 Commits

Reviewing files that changed from the base of the PR and between d198fe2 and 0b494e0.

📒 Files selected for processing (4)
  • sentencesplit/abbreviation_replacer.py
  • sentencesplit/lang/kazakh.py
  • tests/regression/test_issues.py
  • tests/regression/test_kazakh_multiperiod_sentinel.py

Comment on lines +33 to +36
def test_uppercase_dotless_multiperiod_keeps_classifier_protected_final_period() -> None:
seg = Segmenter("kk", split_mode="balanced")

assert seg.segment("Бұл Т.С.С. I went.") == ["Бұл Т.С.С. I went."]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider covering all split modes, not just balanced.

The PRESERVE_CLASSIFIER_PROTECTED_MULTI_PERIOD_FINAL branch is designed to short-circuit the split-mode dial for sentinel-terminated tokens, so this regression only proves the invariant for balanced. Extending to conservative/aggressive would lock in that split-mode-invariance directly.

✅ Suggested additional coverage
 def test_uppercase_dotless_multiperiod_keeps_classifier_protected_final_period() -> None:
-    seg = Segmenter("kk", split_mode="balanced")
-
-    assert seg.segment("Бұл Т.С.С. I went.") == ["Бұл Т.С.С. I went."]
+    for mode in ("conservative", "balanced", "aggressive"):
+        seg = Segmenter("kk", split_mode=mode)
+        assert seg.segment("Бұл Т.С.С. I went.") == ["Бұл Т.С.С. I went."]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_uppercase_dotless_multiperiod_keeps_classifier_protected_final_period() -> None:
seg = Segmenter("kk", split_mode="balanced")
assert seg.segment("Бұл Т.С.С. I went.") == ["Бұл Т.С.С. I went."]
def test_uppercase_dotless_multiperiod_keeps_classifier_protected_final_period() -> None:
for mode in ("conservative", "balanced", "aggressive"):
seg = Segmenter("kk", split_mode=mode)
assert seg.segment("Бұл Т.С.С. I went.") == ["Бұл Т.С.С. I went."]
🤖 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 `@tests/regression/test_kazakh_multiperiod_sentinel.py` around lines 33 - 36,
Extend
test_uppercase_dotless_multiperiod_keeps_classifier_protected_final_period to
run the same assertion for conservative, balanced, and aggressive split modes,
verifying that sentinel-terminated tokens remain unsplit regardless of the
split-mode setting.

@yisding
yisding merged commit d6ef1cd into main Jul 11, 2026
10 checks passed
@yisding
yisding deleted the codex/fix-kazakh-regex-over-splitting-issue branch July 11, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant