extended-wer-normalizer 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:jitsi.github.io)"
5
+ ]
6
+ }
7
+ }
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: extended-wer-normalizer
3
+ Version: 0.1.0
4
+ Summary: jiwer-compatible WER normalizer with number, email, URL, filler, and symbol normalization for voice AI evaluation
5
+ Project-URL: Homepage, https://github.com/ai-coustics/extended-wer-normalizer
6
+ License: MIT
7
+ Keywords: asr,jiwer,normalization,speech recognition,voice ai,wer
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: jiwer>=3.0
20
+ Requires-Dist: num2words>=0.5
21
+ Requires-Dist: whisper-normalizer>=0.1.12
22
+ Description-Content-Type: text/markdown
23
+
24
+ # extended-wer-normalizer
25
+
26
+ jiwer-compatible text normalizer for Word Error Rate (WER) evaluation in voice AI.
27
+
28
+ Extends [jiwer](https://github.com/jitsi/jiwer)'s built-in transforms with normalizations that matter for real-world ASR evaluation: phone numbers, emails, URLs, currency, percentages, ordinals, filler words, and stuttering.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install extended-wer-normalizer
34
+ ```
35
+
36
+ ## Quick start
37
+
38
+ ```python
39
+ from extended_wer_normalizer import normalize_for_wer
40
+
41
+ normalize_for_wer("Call 0176 or email info@example.com, it costs $5.99")
42
+ # → "call 0 1 7 6 or email info at example dot com it costs five dollars ninety nine cents"
43
+
44
+ normalize_for_wer("Um, 1st place goes to Dr. Smith with 50% accuracy")
45
+ # → "first place goes to doctor smith with fifty percent accuracy"
46
+ ```
47
+
48
+ ## jiwer integration
49
+
50
+ Every normalization is a `jiwer.AbstractTransform` subclass — compose them freely:
51
+
52
+ ```python
53
+ import jiwer
54
+ from extended_wer_normalizer.transforms import NormalizeEmails, ExpandDigitRuns
55
+
56
+ pipeline = jiwer.Compose([
57
+ NormalizeEmails(),
58
+ ExpandDigitRuns(),
59
+ jiwer.ToLowerCase(),
60
+ jiwer.RemovePunctuation(),
61
+ jiwer.ReduceToListOfListOfWords(),
62
+ ])
63
+
64
+ wer = jiwer.wer("info at example dot com", "info@example.com", hypothesis_transform=pipeline)
65
+ ```
66
+
67
+ Use the pre-built pipeline directly with `jiwer.wer`:
68
+
69
+ ```python
70
+ import jiwer
71
+ from extended_wer_normalizer import english_wer_pipeline
72
+
73
+ wer = jiwer.wer(reference, hypothesis, reference_transform=english_wer_pipeline, hypothesis_transform=english_wer_pipeline)
74
+ ```
75
+
76
+ ## Available transforms
77
+
78
+ | Transform | Example |
79
+ |---|---|
80
+ | `ExpandDigitRuns` | `"0176"` → `"0 1 7 6"` |
81
+ | `DigitWordsToChars` | `"zero one seven"` → `"0 1 7"` |
82
+ | `WhisperEnglishNormalize` | lowercase, punctuation, contractions, compound numbers |
83
+ | `WhisperBasicNormalize` | language-agnostic basic normalization |
84
+ | `FinalDigitWordCleanup` | residual digit-word sweep after compound resolution |
85
+ | `NormalizeEmails` | `"user@example.com"` → `"user at example dot com"` |
86
+ | `NormalizeURLs` | `"https://example.com/path"` → `"example dot com"` |
87
+ | `NormalizeCurrency` | `"$5.99"` → `"five dollars ninety nine cents"` |
88
+ | `NormalizePercentages` | `"50%"` → `"fifty percent"` |
89
+ | `NormalizeOrdinals` | `"1st"` → `"first"`, `"15th"` → `"fifteenth"` |
90
+ | `ExpandAbbreviations` | `"Dr."` → `"doctor"`, `"vs."` → `"versus"` |
91
+ | `NormalizeSymbols` | `"cats & dogs"` → `"cats and dogs"` |
92
+ | `RemoveFillerWords` | removes `um`, `uh`, `hmm`, `er`, `ah`, … |
93
+ | `CollapseRepetitions` | `"I I I think"` → `"I think"` |
94
+
95
+ ## Pipeline design
96
+
97
+ The English pipeline applies transforms in an order that ensures idempotence and correct interaction with Whisper's `EnglishTextNormalizer`:
98
+
99
+ 1. **Pre-Whisper**: email, URL, symbol, abbreviation (patterns Whisper would mangle)
100
+ 2. **Digit preparation**: expand digit runs, convert digit words
101
+ 3. **Core**: `WhisperEnglishNormalize` (lowercase, punctuation, contractions, compound numbers → digits)
102
+ 4. **Post-Whisper**: digit run expansion, digit word cleanup, currency, percentage, ordinal (patterns Whisper preserves or compacts)
103
+ 5. **Cleanup**: filler words, repetition collapse
104
+
105
+ ## Non-English
106
+
107
+ For non-English text, pass `language` to get Whisper's `BasicTextNormalizer`:
108
+
109
+ ```python
110
+ normalize_for_wer("Das kostet fünf Euro", language="de")
111
+ ```
@@ -0,0 +1,88 @@
1
+ # extended-wer-normalizer
2
+
3
+ jiwer-compatible text normalizer for Word Error Rate (WER) evaluation in voice AI.
4
+
5
+ Extends [jiwer](https://github.com/jitsi/jiwer)'s built-in transforms with normalizations that matter for real-world ASR evaluation: phone numbers, emails, URLs, currency, percentages, ordinals, filler words, and stuttering.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install extended-wer-normalizer
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```python
16
+ from extended_wer_normalizer import normalize_for_wer
17
+
18
+ normalize_for_wer("Call 0176 or email info@example.com, it costs $5.99")
19
+ # → "call 0 1 7 6 or email info at example dot com it costs five dollars ninety nine cents"
20
+
21
+ normalize_for_wer("Um, 1st place goes to Dr. Smith with 50% accuracy")
22
+ # → "first place goes to doctor smith with fifty percent accuracy"
23
+ ```
24
+
25
+ ## jiwer integration
26
+
27
+ Every normalization is a `jiwer.AbstractTransform` subclass — compose them freely:
28
+
29
+ ```python
30
+ import jiwer
31
+ from extended_wer_normalizer.transforms import NormalizeEmails, ExpandDigitRuns
32
+
33
+ pipeline = jiwer.Compose([
34
+ NormalizeEmails(),
35
+ ExpandDigitRuns(),
36
+ jiwer.ToLowerCase(),
37
+ jiwer.RemovePunctuation(),
38
+ jiwer.ReduceToListOfListOfWords(),
39
+ ])
40
+
41
+ wer = jiwer.wer("info at example dot com", "info@example.com", hypothesis_transform=pipeline)
42
+ ```
43
+
44
+ Use the pre-built pipeline directly with `jiwer.wer`:
45
+
46
+ ```python
47
+ import jiwer
48
+ from extended_wer_normalizer import english_wer_pipeline
49
+
50
+ wer = jiwer.wer(reference, hypothesis, reference_transform=english_wer_pipeline, hypothesis_transform=english_wer_pipeline)
51
+ ```
52
+
53
+ ## Available transforms
54
+
55
+ | Transform | Example |
56
+ |---|---|
57
+ | `ExpandDigitRuns` | `"0176"` → `"0 1 7 6"` |
58
+ | `DigitWordsToChars` | `"zero one seven"` → `"0 1 7"` |
59
+ | `WhisperEnglishNormalize` | lowercase, punctuation, contractions, compound numbers |
60
+ | `WhisperBasicNormalize` | language-agnostic basic normalization |
61
+ | `FinalDigitWordCleanup` | residual digit-word sweep after compound resolution |
62
+ | `NormalizeEmails` | `"user@example.com"` → `"user at example dot com"` |
63
+ | `NormalizeURLs` | `"https://example.com/path"` → `"example dot com"` |
64
+ | `NormalizeCurrency` | `"$5.99"` → `"five dollars ninety nine cents"` |
65
+ | `NormalizePercentages` | `"50%"` → `"fifty percent"` |
66
+ | `NormalizeOrdinals` | `"1st"` → `"first"`, `"15th"` → `"fifteenth"` |
67
+ | `ExpandAbbreviations` | `"Dr."` → `"doctor"`, `"vs."` → `"versus"` |
68
+ | `NormalizeSymbols` | `"cats & dogs"` → `"cats and dogs"` |
69
+ | `RemoveFillerWords` | removes `um`, `uh`, `hmm`, `er`, `ah`, … |
70
+ | `CollapseRepetitions` | `"I I I think"` → `"I think"` |
71
+
72
+ ## Pipeline design
73
+
74
+ The English pipeline applies transforms in an order that ensures idempotence and correct interaction with Whisper's `EnglishTextNormalizer`:
75
+
76
+ 1. **Pre-Whisper**: email, URL, symbol, abbreviation (patterns Whisper would mangle)
77
+ 2. **Digit preparation**: expand digit runs, convert digit words
78
+ 3. **Core**: `WhisperEnglishNormalize` (lowercase, punctuation, contractions, compound numbers → digits)
79
+ 4. **Post-Whisper**: digit run expansion, digit word cleanup, currency, percentage, ordinal (patterns Whisper preserves or compacts)
80
+ 5. **Cleanup**: filler words, repetition collapse
81
+
82
+ ## Non-English
83
+
84
+ For non-English text, pass `language` to get Whisper's `BasicTextNormalizer`:
85
+
86
+ ```python
87
+ normalize_for_wer("Das kostet fünf Euro", language="de")
88
+ ```
@@ -0,0 +1,6 @@
1
+ def main():
2
+ print("Hello from aic-wer-normalizer!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [tool.hatch.build.targets.wheel]
6
+ packages = ["src/extended_wer_normalizer"]
7
+
8
+ [project]
9
+ name = "extended-wer-normalizer"
10
+ version = "0.1.0"
11
+ description = "jiwer-compatible WER normalizer with number, email, URL, filler, and symbol normalization for voice AI evaluation"
12
+ readme = "README.md"
13
+ requires-python = ">=3.10"
14
+ license = { text = "MIT" }
15
+ keywords = ["wer", "speech recognition", "asr", "normalization", "jiwer", "voice ai"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: Science/Research",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
27
+ ]
28
+ dependencies = [
29
+ "jiwer>=3.0",
30
+ "whisper-normalizer>=0.1.12",
31
+ "num2words>=0.5",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/ai-coustics/extended-wer-normalizer"
36
+
37
+ [dependency-groups]
38
+ dev = [
39
+ "pytest>=9.0.3",
40
+ ]
@@ -0,0 +1,83 @@
1
+ """extended-wer-normalizer: jiwer-compatible WER normalization for voice AI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import jiwer
6
+
7
+ from .transforms import (
8
+ CollapseRepetitions,
9
+ DigitWordsToChars,
10
+ ExpandAbbreviations,
11
+ ExpandDigitRuns,
12
+ FinalDigitWordCleanup,
13
+ NormalizeCurrency,
14
+ NormalizeEmails,
15
+ NormalizeOrdinals,
16
+ NormalizePercentages,
17
+ NormalizeSymbols,
18
+ NormalizeURLs,
19
+ RemoveFillerWords,
20
+ WhisperBasicNormalize,
21
+ WhisperEnglishNormalize,
22
+ )
23
+
24
+ __all__ = [
25
+ "normalize_for_wer",
26
+ "english_wer_pipeline",
27
+ "CollapseRepetitions",
28
+ "DigitWordsToChars",
29
+ "ExpandAbbreviations",
30
+ "ExpandDigitRuns",
31
+ "FinalDigitWordCleanup",
32
+ "NormalizeCurrency",
33
+ "NormalizeEmails",
34
+ "NormalizeOrdinals",
35
+ "NormalizePercentages",
36
+ "NormalizeSymbols",
37
+ "NormalizeURLs",
38
+ "RemoveFillerWords",
39
+ "WhisperBasicNormalize",
40
+ "WhisperEnglishNormalize",
41
+ ]
42
+
43
+ english_wer_pipeline = jiwer.Compose([
44
+ # Pre-WhisperEN: handle patterns WhisperEN would mangle or remove
45
+ NormalizeEmails(), # must be first: WhisperEN strips @ and dots in emails
46
+ NormalizeURLs(), # WhisperEN strips slashes and scheme prefixes
47
+ NormalizeSymbols(), # WhisperEN removes & and + as punctuation
48
+ ExpandAbbreviations(), # expand Dr./Mr. before WhisperEN lowercases
49
+ # Core Whisper normalization (lowercase, punctuation, contractions, compound numbers)
50
+ ExpandDigitRuns(), # preserve leading zeros before WhisperEN sees e.g. "0176"
51
+ DigitWordsToChars(), # "zero one seven" → "0 1 7" before compound resolution
52
+ WhisperEnglishNormalize(),
53
+ # Post-WhisperEN: handle patterns WhisperEN preserves as-is or converts to compact form
54
+ # (WhisperEN preserves $5.99, 50%, 1st; and converts spoken forms back to compact)
55
+ ExpandDigitRuns(), # split any remaining digit runs (e.g. from compound numbers)
56
+ FinalDigitWordCleanup(), # final digit-word sweep — must run before currency/% so
57
+ # it doesn't convert "nine" inside "ninety nine cents"
58
+ NormalizeCurrency(), # $5.99 → five dollars ninety nine cents
59
+ NormalizePercentages(), # 50% → fifty percent
60
+ NormalizeOrdinals(), # 1st → first
61
+ RemoveFillerWords(),
62
+ CollapseRepetitions(),
63
+ ])
64
+
65
+
66
+ def normalize_for_wer(text: str, language: str = "en") -> str:
67
+ """Normalize text for WER comparison.
68
+
69
+ English pipeline applies, in order:
70
+ email → URL → symbol → currency → percentage → ordinal → abbreviation
71
+ → digit-run expansion → digit-word-to-char → Whisper EnglishTextNormalizer
72
+ → digit-run expansion → final digit cleanup → filler removal → repetition collapse
73
+
74
+ All number representations collapse to space-separated single digits, so
75
+ "0176", "zero one seven six", and "21" all compare correctly.
76
+
77
+ For non-English text, Whisper's BasicTextNormalizer is applied instead.
78
+ """
79
+ if language == "en":
80
+ result = english_wer_pipeline([text])
81
+ return result[0].strip()
82
+ from whisper_normalizer.basic import BasicTextNormalizer
83
+ return BasicTextNormalizer()(text).strip()