feat(sdk): add a textstat-exact Flesch-Kincaid and a benchmark report - #236
Draft
adnanrhussain wants to merge 4 commits into
Draft
feat(sdk): add a textstat-exact Flesch-Kincaid and a benchmark report#236adnanrhussain wants to merge 4 commits into
adnanrhussain wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new standalone Node/JS package (@learning-commons/flesch-kincaid) intended to reproduce Python textstat’s Flesch–Kincaid grade (and its component counts) exactly, and wires it into the repo’s release + CI automation.
Changes:
- Add a new
packages/flesch-kincaidpackage implementing textstat-aligned tokenization, sentence counting, syllable counting (CMUdict + pyphen-style hyphenation fallback), FK grade calculation, and Python-style rounding. - Add Vitest unit + parity tests (driven by a checked-in corpus generated from Python textstat) plus Python scripts to regenerate the data tables and the parity corpus.
- Add release-please config/manifest entries and a dedicated GitHub Actions workflow to typecheck/test/build the new package.
Reviewed changes
Copilot reviewed 17 out of 21 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| release-please-config.json | Adds release-please configuration for the new package. |
| .release-please-manifest.json | Registers the new package in the release-please manifest. |
| .github/workflows/test-flesch-kincaid.yml | Adds CI to typecheck, test, and build the new package across Node versions. |
| .gitattributes | Marks generated package artifacts (data tables + parity corpus) as generated. |
| packages/flesch-kincaid/package.json | Defines the new package metadata, exports, scripts, engines, and dev deps. |
| packages/flesch-kincaid/package-lock.json | Locks dependencies for the new package. |
| packages/flesch-kincaid/tsconfig.json | Adds a TS config for building/testing the package. |
| packages/flesch-kincaid/tsup.config.ts | Adds tsup bundling config for ESM+CJS+types output. |
| packages/flesch-kincaid/src/index.ts | Exposes the public API (counts, grade, rounding, combined stats). |
| packages/flesch-kincaid/src/counts.ts | Implements tokenization, punctuation stripping, word and sentence counting aligned to textstat rules. |
| packages/flesch-kincaid/src/syllables.ts | Implements syllable counting via a CMUdict-derived table with pyphen-style hyphenation fallback. |
| packages/flesch-kincaid/src/hyphenation.ts | Implements a pyphen-style hyphenation pattern parser and breakpoint finder. |
| packages/flesch-kincaid/tests/units.test.ts | Adds focused unit tests for tokenization/counting/hyphenation behaviors. |
| packages/flesch-kincaid/tests/parity.test.ts | Adds corpus-driven parity tests against checked-in textstat outputs. |
| packages/flesch-kincaid/scripts/generate-data.py | Generates checked-in JS data tables from textstat/NLTK/pyphen sources. |
| packages/flesch-kincaid/scripts/generate-parity-corpus.py | Generates the checked-in parity corpus JSON from Python textstat. |
| packages/flesch-kincaid/.gitignore | Ignores build/test artifacts while keeping generated-but-checked-in tables/corpus. |
| packages/flesch-kincaid/README.md | Documents rationale, parity requirements, API surface, and regeneration steps. |
Files not reviewed (1)
- packages/flesch-kincaid/package-lock.json: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
adnanrhussain
marked this pull request as draft
August 29, 2026 21:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a Flesch-Kincaid implementation to the SDK that returns the same number as Python
textstat, to the last bit, plus a benchmark report comparing it against every JS alternative:sdks/typescript/docs/flesch-kincaid.md.Nothing is wired up yet — no evaluator uses it, and it is not in the public API or the built bundle. This is the implementation, its tests, and the evidence. Migration is a separate PR.
Why
fk_scorereaches the model as a fact about the text, so the same text must not be graded differently depending on which language ran the evaluator. Over the 100 fixture texts inevals/:text-readability(declared for TS)textstat-tsreadability-scorescompromise+syllable(in use today)A worst case of 8.94 is not a rounding difference — it is a different answer to "what grade is this text?". Full results, broken down by input class and by which count diverges, are in the doc.
Audit: what each surface uses
Notebooks (all 10) and the Python SDK use
textstat. The TS SDK has two paths —text-readability(contract-declared) inpreprocessing.ts, and a hand-rolledcompromise+syllableinreadability.ts. The three evaluators inPREPROCESSING_GAPScall the hand-rolled one, which no contract declares.Exact parity requires shipping pyphen's
hyph_en_US.dic, which comes from LibreOffice under a GPL 2.0+ / LGPL 2.1+ / MPL 1.1 tri-licence. I have used the MPL 1.1 option (file-level copyleft: that module stays MPL and keeps its notice, the rest of the SDK stays MIT) and documented it inTHIRD_PARTY_LICENSES.md.It is not optional — dropping it takes fixture-prose exactness from 100% to ~30%. It is also not in the built bundle today, because nothing imports it from the public entry, so this PR does not change what gets published. The decision lands with the migration PR. CMUdict, the other table, is BSD-style and needs only attribution.
Verification
typecheck,lint,build,scripts/check.py\b→ 4 fail; ASCII\w→ 297; honouringLEFTHYPHENMIN→ 21; pattern-parser trailing zero → 40;Math.round→ 2; textstat's ≤2-word sentence rule → 150Notes
npm run generate:fk-data), so no Python is needed to install or test. Parsed lazily.roundGrade()exists because Python'sround()rounds half to even andMath.round(v*100)/100does not — they disagree on 7 of 4,027 values.npm run benchmark:fk; the prose around it is hand-written.