diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/address/context_cues.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/address/context_cues.tsv index a40d069b4..bdb9228a4 100644 --- a/nemo_text_processing/inverse_text_normalization/hi/data/address/context_cues.tsv +++ b/nemo_text_processing/inverse_text_normalization/hi/data/address/context_cues.tsv @@ -62,7 +62,6 @@ प्लाज़ा ब्रिज स्टेशन -आर डी अन्तर्गत शॉप क्रॉस diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/__init__.py b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/chemical_formulas.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/chemical_formulas.tsv new file mode 100644 index 000000000..eb02c6276 --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/chemical_formulas.tsv @@ -0,0 +1,21 @@ +ग्लूकोज C6H12O6 +जिंक सलफेट ZnSO4 +बेरियम हाइड्रॉक्साइड Ba(OH)2 +मैग्नीशियम ब्रोमाइड MgBr2 +हेक्सेन C6H14 +नाइट्रोजन N2 +ओजोन O3 +पानी H2O +अमोनिया NH3 +नमक NaCl +मेथेन CH4 +इथेनॉल C2H5OH +कार्बन डाइऑक्साइड CO2 +हाइड्रोक्लोरिक एसिड HCl +कैल्शियम कार्बोनेट CaCO3 +जंग Fe2O3 +बेंजीन C6H6 +सल्फ्यूरिक एसिड H2SO4 +नाइट्रिक एसिड HNO3 +एसिटिक एसिड CH3COOH +फॉर्मिक एसिड HCOOH diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/common_words.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/common_words.tsv new file mode 100644 index 000000000..4c2fb94f5 --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/common_words.tsv @@ -0,0 +1,169 @@ +Users यूज़र्स +User यूज़र +Desktop डेस्कटॉप +Downloads डाउनलोड्स +Documents डॉक्युमेंट्स +Music म्यूज़िक +Pictures पिक्चर्स +Videos वीडियोज़ +Audio ऑडियो +file फ़ाइल +chapter चैप्टर +python पाइथन +work वर्क +school स्कूल +images इमेजेज़ +about अबाउट +blog ब्लॉग +index इंडेक्स +login लॉगिन +register रजिस्टर +search सर्च +tags टैग्स +category केटेगरी +categories केटेगरीज़ +post पोस्ट +posts पोस्ट्स +page पेज +pages पेजेस +admin एडमिन +app ऐप +help हेल्प +terms टर्म्स +privacy प्राइवेसी +contact कॉन्टैक्ट +main मेन +explore एक्सप्लोर +wiki विकी +docs डॉक्स +download डाउनलोड +upload अपलोड +uploads अपलोड्स +photos फ़ोटोज़ +video वीडियो +master मास्टर +blob ब्लॉब +tree ट्री +tests टेस्ट्स +test टेस्ट +config कॉन्फ़िग +settings सेटिंग्स +profile प्रोफ़ाइल +account अकाउंट +web वेब +email ई मेल +laptop लैपटॉप +mobile मोबाइल +phone फोन +phones फोन्स +online ऑनलाइन +courses कोर्सेज़ +learn लर्न +learning लर्निंग +university यूनिवर्सिटी +academy अकेडमी +domain डोमेन +domains डोमेन्स +analysis अनैलिसिस +play प्ले +maps मैप्स +drive ड्राइव +cloud क्लाउड +services सर्विसेज़ +india इंडिया +apache अपाची +kernel कर्नल +bin बिन +var वार +home होम +activate एक्टिवेट +sites साइट्स +available अवेलेबल +enabled इनेबल्ड +backups बैकअप्स +temp टेम्प +secure सेक्योर +real रियल +data डेटा +survey सर्वे +files फाइल्स +sample सैंपल +templates टेम्पलेट्स +impress इंप्रेस +office ऑफिस +libreoffice लिब्रे ऑफिस +express एक्सप्रेस +scribe स्क्राइब +transcription ट्रांसक्रिप्शन +software सॉफ्टवेयर +or ओ आर +teams टीम्स +green ग्रीन +brown ब्राउन +white व्हाइट +black ब्लैक +homepage होमपेज +content कंटेन्ट +default डिफ़ॉल्ट +foodhealth फूड हेल्थ +workflows वर्कफ्लोज़ +world वर्ल्ड +list लिस्ट +and एंड +LICENSE लाइसेंस +tag टैग +blogs ब्लॉग्स +bath बाथ +ward वार्ड +banks बैंक्स +dean डीन +rice राइस +honda होंडा +ford फोर्ड +house हाउस +bharat भरत +rich रिच +cook कुक +lane लेन +knight नाइट +moody मूडी +wise वाइज़ +shields शील्ड्स +puppy पप्पी +recipe रेसिपी +hall हॉल +mason मेसन +king किंग +fry फ्राई +flowers फ्लावर्स +assam आसाम +grace ग्रेस +bishop बिशप +woods वुड्स +brewer ब्रूअर +cannon कैनन +saute सौटे +pope पोप +robin रॉबिन +price प्राइस +address एड्रेस +venv वेन्व +SAMPLE एस ए एम पी एल ई +hotmail हॉटमेल +ExpressScribeTranscriptionSoftware ई एक्स पी आर ई एस एस एस सी आर आई बी ई टी आर ए एन एस सी आर आई पी टी आई ओ एन एस ओ एफ टी डब्ल्यू ए आर ई +Phones पी एच ओ एन ई एस +TXR20820d90fb1d3327447009e701166f29 टी एक्स आर दो शून्य आठ दो शून्य डी नौ शून्य एफ बी एक डी तीन तीन दो सात चार चार सात शून्य शून्य नौ ई सात शून्य एक एक छह छह एफ दो नौ +poetry पोएट्री +food फूड +health हेल्थ +any ए एन वाई +Hipo एच आई पी ओ +README आर ई ए डी एम ई +coursera कोर्सेरा +anuj ए एन यू जे +Main एम ए आई एन +google गूगल +blogger ब्लॉगर +travis टी आर ए वी आई एस +frank एफ आर ए एन के +Page पी ए जी ई \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/digit_glyphs.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/digit_glyphs.tsv new file mode 100644 index 000000000..dfc93217a --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/digit_glyphs.tsv @@ -0,0 +1,10 @@ +1 १ +2 २ +3 ३ +4 ४ +5 ५ +6 ६ +7 ७ +8 ८ +9 ९ +0 ० \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/digit_words.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/digit_words.tsv new file mode 100644 index 000000000..f85014305 --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/digit_words.tsv @@ -0,0 +1,10 @@ +एक 1 +दो 2 +तीन 3 +चार 4 +पाँच 5 +छह 6 +सात 7 +आठ 8 +नौ 9 +शून्य 0 \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/domain.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/domain.tsv new file mode 100644 index 000000000..c6e2a7cbe --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/domain.tsv @@ -0,0 +1,89 @@ +com कॉम +org ऑर्ग +net नेट +edu ई डी यू +gov जी ओ वी +biz बिज़ +info इन्फो +in इन +io आई ओ +ai ए आई +uk यू के +us यू एस +ru आर यू +de डी ई +jp जे पी +cn सी एन +au ए यू +ca सी ए +br बी आर +ac ए सी +res आर ई एस +nic एन आई सी +ernet ई आर नेट +int आई एन टी +tv टी वी +me एम ई +tech टेक +dev डी ई वी +app ऐप +xyz एक्स वाई ज़ेड +online ऑनलाइन +store स्टोर +blog ब्लॉग +site साइट +pro प्रो +photos फ़ोटोज़ +gov जी ओ वी +ac ए सी +nic एन आई सी +sims सिम्स +pope पोप +Zoom ज़ेड ओ ओ एम +Coursera सी ओ यू आर एस ई आर ए +Screenshot एस सी आर ई ई एन एस एच ओ टी +Microsoft एम आई सी आर ओ एस ओ एफ टी +Audacity ए यू डी ए सी आई टी वाई +Teams टी ई ए एम एस +hotmail हॉटमेल +Mobile एम ओ बी आई एल ई +Phones पी एच ओ एन ई एस +com कॉम +org ऑर्ग +net नेट +edu ई डी यू +gov जी ओ वी +biz बिज़ +info इन्फो +in इन +io आई ओ +ai ए आई +uk यू के +us यू एस +ru आर यू +de डी ई +fr एफ आर +jp जे पी +cn सी एन +au ए यू +ca सी ए +br बी आर +ac ए सी +res आर ई एस +nic एन आई सी +ernet ई आर नेट +int आई एन टी +tv टी वी +me एम ई +tech टेक +dev डी ई वी +app ऐप +xyz एक्स वाई ज़ेड +online ऑनलाइन +store स्टोर +blog ब्लॉग +site साइट +pro प्रो +photos फ़ोटोज़ +sims सिम्स +com COM \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/letters.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/letters.tsv new file mode 100644 index 000000000..2f4ad7963 --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/letters.tsv @@ -0,0 +1,54 @@ +a ए +b बी +c सी +d डी +e ई +f एफ +f एफ़ +g जी +h एच +i आई +i आइ +j जे +k के +l एल +m एम +n एन +o ओ +p पी +q क्यू +r आर +s एस +t टी +u यू +v वी +w डब्ल्यू +x एक्स +y वाई +z ज़ेड +A ए +B बी +C सी +D डी +E ई +F एफ +G जी +H एच +I आई +J जे +K के +L एल +M एम +N एन +O ओ +P पी +Q क्यू +R आर +S एस +T टी +U यू +V वी +W डब्ल्यू +X एक्स +Y वाई +Z ज़ेड \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/server_name.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/server_name.tsv new file mode 100644 index 000000000..e9de1badd --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/server_name.tsv @@ -0,0 +1,153 @@ +gmail जीमेल +yahoo याहू +hotmail हॉटमेल +outlook आउटलुक +live लाइव +google गूगल +microsoft माइक्रोसॉफ्ट +facebook फ़ेसबुक +twitter ट्विटर +instagram इंस्टाग्राम +linkedin लिंक्डइन +youtube यूट्यूब +amazon अमेज़ोन +wikipedia विकिपीडिया +github गिटहब +reddit रेडिट +netflix नेटफ्लिक्स +spotify स्पॉटिफाई +apple एप्पल +samsung सैमसंग +Zoom ज़ेड ओ ओ एम +Coursera सी ओ यू आर एस ई आर ए +Screenshot एस सी आर ई ई एन एस एच ओ टी +Microsoft एम आई सी आर ओ एस ओ एफ टी +Audacity ए यू डी ए सी आई टी वाई +Teams टी ई ए एम एस +Mobile एम ओ बी आई एल ई +Phones पी एच ओ एन ई एस +nvidia एनविडिया +intel इंटेल +adobe अडोब +wordpress वर्डप्रेस +blogger ब्लॉगर +mentalfloss मेंटल फ्लॉस +placekitten प्लेस किटन +dummyimage डमी इमेज +reliablesoft रिलाएबल सॉफ्ट +ebay ई बे +moz मोज़ +mozilla मॉज़िला +genius जीनियस +groupon ग्रुप ऑन +gutenberg गुटेनबर्ग +recipepuppy रेसिपी पप्पी +buyagift बाय अ गिफ्ट +researchgate रिसर्च गेट +afternic आफ्टर निक +hipolabs हिपो लैब्स +licindia एल आई सी इंडिया +placeimg प्लेस आई एम जी +codecademy कोड कैडेमी +skillshop स्किलशॉप +skillshare स्किल शेयर +udemy यूडेमी +masterclass मास्टरक्लास +amity एमिटी +sharda शारदा +universities यूनिवर्सिटीज़ +mcdonald मैक्डॉनल्ड +southmountaincc साउथ माउन्टेन सी सी +academyart अकेडमी आर्ट +bryanuniversity ब्रायन यूनिवर्सिटी +centralaz सेंट्रल ए ज़ेड +alaska अलास्का +phoenix फीनिक्स +phoenixcollege फीनिक्स कॉलेज +maricopa मैरीकोपा +prescott प्रेसकॉट +azwestern ए ज़ेड वेस्टर्न +poetry पोएट्री +harvard हावर्ड +bamu बी ए एम यू +garcia गारशिया +torres टॉरेस +robinson रॉबिन्सन +picsum पी आई सी एस यू एम +web वेब +laptop लैपटॉप +desktop डेस्कटॉप +email ई मेल +gaangulii जी ए ए एन जी यू एल आई आई +alii ए एल आई आई +paattil पी ए ए टी टी आई एल +laal एल ए ए एल +raamshrmaa आर ए ए एम एस एच आर एम ए ए +vphaadaar वी पी एच ए ए डी ए ए आर +dddhaal डी डी डी एच ए ए एल +mngt एम एन जी टी +dyaal डी वाई ए ए एल +kidd के आई डी डी +kline के एल आई एन ई +bnaa बी एन ए ए +dve डी वी ई +chaabraa सी एच ए ए बी आर ए ए +gaaykvaadd जी ए ए वाई के वी ए ए डी डी +jmaant जे एम ए ए एन टी +raamllaa आर ए ए एम एल एल ए ए +shirole एस एच आई आर ओ एल ई +saayaa एस ए ए वाई ए ए +loknaattyon एल ओ के एन ए ए टी टी वाई ओ एन +ddhiingraa डी डी एच आई आई एन जी आर ए ए +mjuumdaar एम जे यू यू एम डी ए ए आर +bjaaj बी जे ए ए जे +duube डी यू यू बी ई +vikaavi वी आई के ए ए वी आई +gaavit जी ए ए वी आई टी +sinh एस आई एन एच +baird बी ए आई आर डी +harvey एच ए आर वी ई वाई +holder एच ओ एल डी ई आर +mallik एम ए एल एल आई के +badami बी ए डी ए एम आई +maan एम ए ए एन +ortega ओ आर टी ई जी ए +osborne ओ एस बी ओ आर एन ई +perez पी ई आर ई ज़ेड +forbes एफ ओ आर बी ई एस +agraval ए जी आर ए वी ए एल +kunda के यू एन डी ए +frederick एफ आर ई डी ई आर आई सी के +fitzgerald एफ आई टी ज़ेड जी ई आर ए एल डी +aahuujaa ए ए एच यू यू जे ए ए +aacaary ए ए सी ए ए आर वाई +baalaasubrmnniym बी ए ए एल ए ए एस यू बी आर एम एन एन आई वाई एम +krssnnmuurti के आर एस एस एन एन एम यू यू आर टी आई +mllik एम एल एल आई के +baadaamii बी ए ए डी ए ए एम आई आई +congress सी ओ एन जी आर ई एस एस +powell पी ओ डब्ल्यू ई एल एल +russo आर यू एस एस ओ +mason मेसन +rich रिच +ruiz आर यू आई ज़ेड +baamu बी ए एम यू +bieap बी आई ई ए पी +aasc ए ए एस सी +lucero एल यू सी ई आर ओ +stevenson एस टी ई वी ई एन एस ओ एन +sharma एस एच ए आर एम ए +green ग्रीन +turner टी यू आर एन ई आर +wallace डब्ल्यू ए एल एल ए सी ई +agrvaal ए जी आर वी ए ए एल +kunnddaa के यू एन एन डी डी ए ए +shriiviml एस एच आर आई आई वी आई एम एल +baalkrssnnn बी ए ए एल के आर एस एस एन एन एन +shrivimal एस एच आर आई वी आई एम ए एल +ahluvaaliyaa ए एच एल यू वी ए ए एल आई वाई ए ए +naam एन ए ए एम +TXR टी एक्स आर +Hipo एच आई पी ओ +sanchez सानशेज़ +crawford क्रॉफोर्ड diff --git a/nemo_text_processing/inverse_text_normalization/hi/data/electronic/special_codes.tsv b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/special_codes.tsv new file mode 100644 index 000000000..28d07e974 --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/data/electronic/special_codes.tsv @@ -0,0 +1,24 @@ +ज़ेड एक्स आठ शून्य एक नौ आठ शून्य ZX80 1980 +बी ए एस ई तीन दो Base32 +बी ई सी एच तीन दो Bech32 +सी ए एन ओ एन ए सात पाँच Canon A75 +आर ई डी एम आई एच चार चार चार जी REDMI H44 4G +आई एन एन ओ टी ई एक IN NOTE1 +ए एस सी आई आई आठ पाँच Ascii85 +एम के हाइफ़न एक ए Mk-1A +एक नौ तीन पाँच आठ एम नौ 1 9358M9 +छह दो दो छह शून्य एम छह 6 2260M6 +सात नौ नौ छह पाँच एम छह 7 9965M6 +डॉट टी आर ए वी आई एस डॉट वाई एम एल .travis.yml +एच आई पी ओ Hipo +अग्नि द्वितीय अग्नि-2 +कोविड नौटीन कोविड-19 +पृथ्वी चार पृथ्वी-4 +ब्रह्मोस हाइफ़न एक ब्रह्मोस-1 +भास्कर हाइफ़न दो भास्कर-II +श्रेणी हाइफ़न दो श्रेणी-II +रोहिणी आर एस हाइफ़न एक रोहिणी आर एस-I +ऑडी आठ शून्य बटा नौ शून्य बी चार ऑडी 80/90 B4 +ऑडी आठ शून्य बटा नौ शून्य बी चार ऑडी 80/90 B4 +पाँच जी 5G +लैपटॉप हाइफ़न चार तीन डॉट सानशेज़ हाइफ़न क्रॉफोर्ड laptop-43.sanchez-crawford diff --git a/nemo_text_processing/inverse_text_normalization/hi/taggers/electronic.py b/nemo_text_processing/inverse_text_normalization/hi/taggers/electronic.py new file mode 100644 index 000000000..58b33ce20 --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/taggers/electronic.py @@ -0,0 +1,581 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pynini +from pynini.lib import pynutil + +from nemo_text_processing.inverse_text_normalization.hi.graph_utils import ( + GraphFst, + delete_space, + delete_zero_or_one_space, + NEMO_SIGMA, +) +from nemo_text_processing.inverse_text_normalization.hi.utils import get_abs_path + + +class ElectronicFst(GraphFst): + """ + Finite state transducer for classifying electronic expressions in Hindi + inverse text normalization: converts spoken Hindi words into written + electronic forms such as email addresses, URLs, file paths, IP addresses, + domains, chemical formulas, and alphanumeric codes. + + e-mail: + e.g. कुमार एट जीमेल डॉट कॉम + -> tokens { electronic { username: "kumar" domain: "gmail.com" } } + URL: + e.g. एच टी टी पी एस कोलन फॉरवर्ड स्लैश फॉरवर्ड स्लैश गूगल डॉट कॉम + -> tokens { electronic { domain: "https://google.com" } } + file path (Windows): + e.g. सी कोलन बैकवर्ड स्लैश यूजर्स बैकवर्ड स्लैश एच पी बैकवर्ड स्लैश डेस्कटॉप + -> tokens { electronic { path: "C:\\Users\\HP\\Desktop" } } + file path (Unix/Linux): + e.g. फॉरवर्ड स्लैश होम फॉरवर्ड स्लैश यूजर फॉरवर्ड स्लैश डॉक्युमेंट्स + -> tokens { electronic { path: "/home/user/documents" } } + IP address: + e.g. एक नौ दो डॉट एक छह आठ डॉट एक डॉट एक + -> tokens { electronic { ip: "192.168.1.1" } } + chemical formula: + e.g. एन ए ओ एच + -> tokens { electronic { domain: "NaOH" } } + alphanumeric code: + e.g. ए बी सी एक दो तीन + -> tokens { electronic { domain: "ABC123" } } + + """ + + def __init__(self): + super().__init__(name="electronic", kind="classify") + digit_words = pynini.string_file( + get_abs_path("data/electronic/digit_words.tsv") + ) + digit_glyphs = pynini.string_file( + get_abs_path("data/electronic/digit_glyphs.tsv") + ).invert() + single_digit = ( + pynutil.add_weight(digit_glyphs, 0.8) + | pynutil.add_weight(digit_words, 0.50) + ) + digit_seq = ( + pynutil.add_weight(digit_glyphs + pynini.closure(digit_glyphs, 0), 0.8) + | pynutil.add_weight(digit_words + pynini.closure(delete_space + digit_words, 0), 0.9) + ) + + letter_map = pynini.string_file(get_abs_path("data/electronic/letters.tsv")).invert() + domain_map = pynini.string_file(get_abs_path("data/electronic/domain.tsv")).invert() + server_map = pynini.string_file(get_abs_path("data/electronic/server_name.tsv")).invert() + common_map = pynini.string_file(get_abs_path("data/electronic/common_words.tsv")).invert() + + try: + chem_named_map = pynini.string_file( + get_abs_path("data/electronic/chemical_formulas.tsv") + ).optimize() + except Exception: + chem_named_map = None + + special_codes_map = pynini.string_file( + get_abs_path("data/electronic/special_codes.tsv") + ).optimize() + + to_lower = pynini.cdrewrite( + pynini.string_map([(chr(c), chr(c + 32)) for c in range(ord('A'), ord('Z') + 1)]), + "", "", NEMO_SIGMA, + ) + to_upper = pynini.cdrewrite( + pynini.string_map([(chr(c + 32), chr(c)) for c in range(ord('A'), ord('Z') + 1)]), + "", "", NEMO_SIGMA, + ) + + def make_lower(fst): + return (fst @ to_lower).optimize() + + def make_upper(fst): + return (fst @ to_upper).optimize() + + letter_map_lower = make_lower(letter_map) + letter_map_upper = make_upper(letter_map) + common_map_lower = make_lower(common_map) + server_map_lower = make_lower(server_map) + + latin_run = pynini.closure( + pynini.union(*[pynini.accep(chr(c)) for c in range(ord('A'), ord('Z') + 1)]) + | pynini.union(*[pynini.accep(chr(c)) for c in range(ord('a'), ord('z') + 1)]), + 1, + ) + latin_run_lower = make_lower(latin_run) + + _drive_chars = pynini.union("C", "D", "E", "F", "G", "H", "I", "J") + drive_letter = pynini.compose(letter_map_upper, _drive_chars) + + def _backslash(): + return (pynutil.delete("बैकवर्ड") + delete_space + + pynutil.delete("स्लैश") + pynutil.insert("\\\\")) + seg_backslash = delete_space + _backslash() + delete_space + trail_backslash = delete_space + _backslash() + lead_backslash = _backslash() + delete_space + + def _unix_slash(): + return (pynutil.delete("फॉरवर्ड") + delete_space + + pynutil.delete("स्लैश") + pynutil.insert("/")) + unix_seg_slash = delete_space + _unix_slash() + delete_space + unix_lead_slash = _unix_slash() + delete_space + unix_trail_slash = delete_space + _unix_slash() + + url_slash = ( + delete_space + + pynutil.delete("फॉरवर्ड") + delete_space + pynutil.delete("स्लैश") + + pynutil.insert("/") + ) + + lit_slash_seg = pynini.cross(" / ", "/") + lit_hyphen_seg = pynini.cross(" - ", "-") + + dot = delete_space + (pynutil.delete("डॉट") | pynutil.delete("DOT")) + delete_space + pynutil.insert(".") + dot_end_safe = delete_space + (pynutil.delete("डॉट") | pynutil.delete("DOT")) + delete_zero_or_one_space + pynutil.insert(".") + hyphen = delete_space + (pynutil.delete("हाइफ़न") | pynutil.delete("हाइफन")) + delete_space + pynutil.insert("-") + underscore = delete_space + pynutil.delete("अंडर") + delete_space + pynutil.delete("स्कोर") + pynutil.insert("_") + at_sign = delete_space + pynutil.delete("एट") + delete_space + x_sep = delete_space + pynutil.delete("एक्स") + pynutil.insert("x") + literal_space = delete_space + pynutil.delete("स्पेस") + pynutil.insert(" ") + open_bracket = ( + delete_space + + pynutil.delete("ओपन") + delete_space + pynutil.delete("ब्रेकेट") + + pynutil.insert("(") + + delete_zero_or_one_space + ) + close_bracket = ( + delete_space + + pynutil.delete("क्लोज़") + delete_space + pynutil.delete("ब्रेकेट") + + pynutil.insert(")") + + delete_zero_or_one_space + ) + dollar_sign = delete_space + pynutil.delete("डॉलर") + pynutil.insert("$") + + lit_open_paren = ( + delete_space + + pynutil.delete("(") + + pynutil.insert("(") + + delete_zero_or_one_space + ) + lit_close_paren = ( + delete_space + + pynutil.delete(")") + + pynutil.insert(")") + ) + or_word = pynutil.delete("ओ") + delete_space + pynutil.delete("आर") + pynutil.insert("or") + and_as_letters = pynutil.delete("एंड") + pynutil.insert("and") + www_token = (pynutil.delete("डब्ल्यू") + delete_space + + pynutil.delete("डब्ल्यू") + delete_space + + pynutil.delete("डब्ल्यू") + pynutil.insert("www")) + v_prefix = pynutil.delete("वी") + pynutil.insert("v") + hp_token = pynutil.delete("एच") + delete_space + pynutil.delete("पी") + pynutil.insert("HP") + tilde_delete = pynutil.delete("~") | pynutil.delete("टिल्ड") + + single_token = ( + pynutil.add_weight(server_map, 0.90) + | pynutil.add_weight(common_map, 0.95) + | pynutil.add_weight(letter_map_lower, 1.00) + ) + token_seq = single_token + pynini.closure(delete_space + single_token, 0) + + path_atom = ( + pynutil.add_weight(hp_token, 0.76) + | pynutil.add_weight(www_token, 0.77) + | pynutil.add_weight(or_word, 0.80) + | pynutil.add_weight(and_as_letters, 0.84) + | pynutil.add_weight(common_map, 0.90) + | pynutil.add_weight(server_map, 0.92) + | pynutil.add_weight(digit_words, 0.94) + | pynutil.add_weight(digit_glyphs, 0.95) + | pynutil.add_weight(latin_run, 0.97) + | pynutil.add_weight(letter_map, 1.00) + ) + path_atom_lower = ( + pynutil.add_weight(common_map_lower, 0.90) + | pynutil.add_weight(server_map_lower, 0.92) + | pynutil.add_weight(digit_words, 0.94) + | pynutil.add_weight(digit_glyphs, 0.95) + | pynutil.add_weight(latin_run_lower, 0.97) + | pynutil.add_weight(letter_map_lower, 1.00) + ) + unix_path_atom = ( + pynutil.add_weight(www_token, 0.77) + | pynutil.add_weight(or_word, 0.80) + | pynutil.add_weight(and_as_letters, 0.84) + | pynutil.add_weight(pynini.cross("CI", "c"), 0.86) + | path_atom_lower + ) + + single_ext = ( + delete_space + pynutil.delete("डॉट") + pynutil.insert(".") + + delete_space + path_atom_lower + + pynini.closure(delete_space + path_atom_lower, 0) + ) + ext_hyphen = ( + delete_space + (pynutil.delete("हाइफ़न") | pynutil.delete("हाइफन")) + + pynutil.insert("-") + delete_space + + path_atom_lower + pynini.closure(delete_space + path_atom_lower, 0) + ) + file_ext = single_ext + pynini.closure(single_ext | ext_hyphen, 0) + + win_hyphen = ( + delete_space + (pynutil.delete("हाइफ़न") | pynutil.delete("हाइफन")) + + pynutil.insert("-") + delete_space + + path_atom + pynini.closure(delete_space + path_atom, 0) + ) + win_underscore = ( + delete_space + pynutil.delete("अंडर") + delete_space + + pynutil.delete("स्कोर") + pynutil.insert("_") + ) + path_segment = ( + path_atom + + pynini.closure( + pynutil.add_weight(delete_space + path_atom, 1.0) + | pynutil.add_weight(win_hyphen, 1.0) + | pynutil.add_weight(win_underscore, 1.0) + | pynutil.add_weight(literal_space, 1.0) + | pynutil.add_weight(open_bracket, 1.0) + | pynutil.add_weight(close_bracket, 1.0) + | pynutil.add_weight(lit_open_paren, 1.0) + | pynutil.add_weight(lit_close_paren, 1.0) + , 0) + + pynini.closure(file_ext, 0, 1) + ) + + unix_hyphen = ( + delete_space + (pynutil.delete("हाइफ़न") | pynutil.delete("हाइफन")) + + pynutil.insert("-") + delete_space + + unix_path_atom + pynini.closure(delete_space + unix_path_atom, 0) + ) + unix_underscore = ( + delete_space + pynutil.delete("अंडर") + delete_space + + pynutil.delete("स्कोर") + pynutil.insert("_") + + delete_space + unix_path_atom + + pynini.closure(delete_space + unix_path_atom, 0) + ) + version_seg = ( + v_prefix + unix_path_atom + + pynini.closure( + delete_space + pynutil.delete("डॉट") + pynutil.insert(".") + + delete_space + unix_path_atom + + pynini.closure(delete_space + unix_path_atom, 0) + , 0) + ) + dollar_var = ( + dollar_sign + delete_space + + unix_path_atom + pynini.closure(delete_space + unix_path_atom, 0) + ) + unix_segment = ( + ( + pynutil.add_weight(version_seg, 0.85) + | pynutil.add_weight(dollar_var, 0.87) + | pynutil.add_weight(unix_path_atom, 1.00) + ) + + pynini.closure( + pynutil.add_weight(delete_space + unix_path_atom, 1.0) + | pynutil.add_weight(unix_hyphen, 1.0) + | pynutil.add_weight(unix_underscore, 1.0) + , 0) + + pynini.closure(file_ext, 0, 1) + ) + + windows_path_fst = ( + pynutil.insert("path: \"") + + drive_letter + delete_space + pynutil.delete("कोलन") + pynutil.insert(":") + + seg_backslash + path_segment + + pynini.closure(seg_backslash + path_segment, 0) + + pynini.closure(trail_backslash, 0, 1) + + pynutil.insert("\"") + ) + unc_path_fst = ( + pynutil.insert("path: \"") + + lead_backslash + path_segment + + pynini.closure(seg_backslash + path_segment, 0) + + pynini.closure(trail_backslash, 0, 1) + + pynutil.insert("\"") + ) + unix_abs_path_fst = ( + pynutil.insert("path: \"") + + unix_lead_slash + unix_segment + + pynini.closure(unix_seg_slash + unix_segment, 0) + + pynini.closure(unix_trail_slash, 0, 1) + + pynutil.insert("\"") + ) + unix_rel_path_fst = ( + pynutil.insert("path: \"") + + unix_segment + unix_seg_slash + unix_segment + + pynini.closure(unix_seg_slash + unix_segment, 0) + + pynini.closure(unix_trail_slash, 0, 1) + + pynutil.insert("\"") + ) + tilde_path_fst = ( + pynutil.insert("path: \"") + + tilde_delete + unix_seg_slash + unix_segment + + pynini.closure(unix_seg_slash + unix_segment, 0) + + pynini.closure(unix_trail_slash, 0, 1) + + pynutil.insert("\"") + ) + + lit_seg = ( + unix_path_atom + + pynini.closure( + pynutil.add_weight(delete_space + unix_path_atom, 1.0) + | pynutil.add_weight(unix_hyphen, 1.0) + | pynutil.add_weight(lit_hyphen_seg, 1.0) + , 0) + + pynini.closure(file_ext, 0, 1) + ) + literal_rel_path_fst = ( + pynutil.insert("path: \"") + + lit_seg + + lit_slash_seg + lit_seg + + pynini.closure(lit_slash_seg + lit_seg, 0) + + pynini.closure(pynini.cross(" /", "/"), 0, 1) + + pynutil.insert("\"") + ) + + host_prefix_map = pynini.string_map([ + ("एस आर वी", "srv"), ("डी बी", "db"), ("एल टी", "lt"), + ("वेब", "web"), ("लैपटॉप", "laptop"), ("डेस्कटॉप", "desktop"), + ("ई मेल", "email"), + ]) + + domain_single = ( + pynutil.add_weight(server_map_lower, 0.90) + | pynutil.add_weight(common_map_lower, 0.95) + | pynutil.add_weight(letter_map_lower, 1.00) + ) + domain_token_seq = domain_single + pynini.closure(delete_space + domain_single, 0) + + digit_then_letter = ( + digit_seq + + pynini.closure(delete_space + letter_map_lower, 0) + ) + + first_label = ( + pynutil.add_weight(host_prefix_map, 0.80) + | pynutil.add_weight(digit_seq + delete_space + letter_map_lower, 0.85) + | pynutil.add_weight(digit_seq, 0.87) + | pynutil.add_weight(domain_token_seq, 1.00) + ) + domain_body = first_label + pynini.closure( + hyphen + ( + pynutil.add_weight(digit_then_letter, 0.85) + | pynutil.add_weight(digit_seq | domain_token_seq, 1.0) + ), 0 + ) + compound_tld = domain_map + pynini.closure(dot_end_safe + domain_map, 0, 2) + full_domain = pynini.closure(domain_body + dot, 0, 4) + domain_body + dot + compound_tld + full_domain_bare = pynini.closure(domain_body + dot, 0, 4) + domain_body + + uname_atom = ( + pynutil.add_weight(and_as_letters, 0.84) + | pynutil.add_weight(digit_words, 0.88) + | pynutil.add_weight(digit_glyphs, 0.88) + | pynutil.add_weight(server_map, 0.90) + | pynutil.add_weight(common_map, 0.95) + | pynutil.add_weight(letter_map_lower, 0.84) + ) + uname_sep = ( + (delete_space + pynutil.delete("डॉट") + delete_space + pynutil.insert(".")) + | (delete_space + (pynutil.delete("हाइफ़न") | pynutil.delete("हाइफन")) + delete_space + pynutil.insert("-")) + | (delete_space + pynutil.delete("अंडर") + delete_space + pynutil.delete("स्कोर") + pynutil.insert("_")) + ) + username = uname_atom + pynini.closure((uname_sep + uname_atom) | (delete_space + uname_atom), 0) + email_fst = ( + pynutil.insert("username: \"") + username + pynutil.insert("\"") + + at_sign + + pynutil.insert("domain: \"") + domain_body + dot + compound_tld + pynutil.insert("\"") + ) + + ip_octet = single_digit + pynini.closure(delete_space + single_digit, 0, 2) + ip_fst = ( + pynutil.insert("ip: \"") + + ip_octet + dot + ip_octet + dot + ip_octet + dot + ip_octet + + pynutil.insert("\"") + ) + + path_atom_url = ( + pynutil.add_weight(digit_seq + x_sep + delete_space + digit_seq, 0.75) + | pynutil.add_weight(digit_seq + delete_space + letter_map_lower + delete_space + digit_seq, 0.80) + | pynutil.add_weight(digit_words, 0.88) + | pynutil.add_weight(digit_glyphs, 0.89) + | pynutil.add_weight(digit_seq, 0.90) + | pynutil.add_weight(token_seq, 1.00) + ) + + inline_domain_seg = ( + pynini.closure(token_seq + dot, 0, 2) + token_seq + dot + domain_map + + pynini.closure(dot + domain_map, 0, 1) + ) + + path_segment_url = ( + path_atom_url + + pynini.closure(hyphen + (digit_seq | token_seq), 0) + + pynini.closure(underscore + token_seq, 0) + + pynini.closure(dot + token_seq, 0, 1) + ) + + slash_with_word = url_slash + ( + pynutil.add_weight(pynutil.insert(".") + pynutil.delete("डॉट") + delete_space + token_seq, 0.90) + | pynutil.add_weight(inline_domain_seg, 0.95) + | pynutil.add_weight(path_segment_url, 1.00) + ) + + www_as_path_seg = ( + www_token + dot + full_domain + + pynini.closure(slash_with_word, 0) + ) + + slash_with_word = url_slash + ( + pynutil.add_weight(delete_space + digit_seq + x_sep + delete_space + digit_seq, 0.20) + | pynutil.add_weight(delete_space + digit_seq, 0.30) + | pynutil.add_weight(delete_space + pynutil.insert(".") + pynutil.delete("डॉट") + delete_space + token_seq, 0.90) + | pynutil.add_weight(delete_space + inline_domain_seg, 0.95) + | pynutil.add_weight(delete_space + www_as_path_seg, 0.97) + | pynutil.add_weight(delete_space + path_segment_url, 1.00) + ) + + hash_frag_body = token_seq + pynini.closure(hyphen + token_seq, 0) + hash_frag = ( + delete_space + + pynutil.delete("हैशटैग") + + pynutil.insert("#") + + delete_space + + hash_frag_body + ) + + domain_and_path = ( + full_domain + + pynini.closure(slash_with_word, 0) + + pynini.closure(url_slash, 0, 1) + + pynini.closure(hash_frag, 0, 1) + ) + domain_and_path_bare = ( + full_domain_bare + + pynini.closure(slash_with_word, 0) + + pynini.closure(url_slash, 0, 1) + + pynini.closure(hash_frag, 0, 1) + ) + + https_prefix = ( + pynutil.delete("एच") + delete_space + pynutil.delete("टी") + delete_space + + pynutil.delete("टी") + delete_space + pynutil.delete("पी") + delete_space + + pynutil.delete("एस") + delete_space + pynutil.delete("कोलन") + delete_space + + pynutil.delete("फॉरवर्ड") + delete_space + pynutil.delete("स्लैश") + delete_space + + pynutil.delete("फॉरवर्ड") + delete_space + pynutil.delete("स्लैश") + + pynutil.insert("https://") + ) + http_prefix = ( + pynutil.delete("एच") + delete_space + pynutil.delete("टी") + delete_space + + pynutil.delete("टी") + delete_space + pynutil.delete("पी") + delete_space + + pynutil.delete("कोलन") + delete_space + + pynutil.delete("फॉरवर्ड") + delete_space + pynutil.delete("स्लैश") + delete_space + + pynutil.delete("फॉरवर्ड") + delete_space + pynutil.delete("स्लैश") + + pynutil.insert("http://") + ) + protocol = pynutil.add_weight(https_prefix, 1.0) | pynutil.add_weight(http_prefix, 1.01) + + url_fst = (pynutil.insert("domain: \"") + protocol + delete_space + + pynini.closure(www_token + dot, 0, 1) + domain_and_path + pynutil.insert("\"")) + www_fst = (pynutil.insert("domain: \"") + www_token + dot + + domain_and_path + pynutil.insert("\"")) + plain_fst = pynutil.insert("domain: \"") + domain_and_path + pynutil.insert("\"") + + url_fst_bare = ( + pynutil.insert("domain: \"") + protocol + delete_space + + pynini.closure(www_token + dot, 0, 1) + + domain_and_path_bare + pynutil.insert("\"") + ) + www_fst_bare = ( + pynutil.insert("domain: \"") + www_token + dot + + domain_and_path_bare + pynutil.insert("\"") + ) + + chem_token = ( + pynutil.add_weight(digit_glyphs, 0.90) + | pynutil.add_weight(letter_map, 1.00) + ) + chem_more = pynini.closure( + pynutil.add_weight(delete_space + chem_token, 1.0) + | pynutil.add_weight(open_bracket, 1.0) + | pynutil.add_weight(close_bracket, 1.0) + | pynutil.add_weight(delete_space + pynutil.delete("इनदो") + pynutil.insert("("), 1.0) + | pynutil.add_weight(delete_space + pynutil.delete("बाय") + pynutil.insert(")"), 1.0) + | pynutil.add_weight(delete_space + (pynutil.delete("माइनस") | pynutil.delete("–")) + pynutil.insert("−"), 1.0) + , 0) + + chem_spelled_fst = pynutil.insert("domain: \"") + ( + chem_token + delete_space + chem_token + chem_more + ) + pynutil.insert("\"") + + alnum_phrase_fst = pynutil.insert("domain: \"") + special_codes_map + pynutil.insert("\"") + + alnum_token = ( + pynutil.add_weight(digit_glyphs, 0.77) + | pynutil.add_weight(digit_words, 0.10) + | pynutil.add_weight(letter_map_upper, 0.84) + ) + alnum_run = alnum_token + delete_space + alnum_token + pynini.closure(delete_space + alnum_token, 0) + + alnum_hyphen_ext = ( + delete_space + (pynutil.delete("हाइफ़न") | pynutil.delete("हाइफन")) + + pynutil.insert("-") + delete_space + + alnum_token + pynini.closure(delete_space + alnum_token, 0) + ) + alnum_body_start = ( + alnum_run + | (alnum_token + alnum_hyphen_ext) + ) + alnum_body = alnum_body_start + pynini.closure( + pynutil.add_weight(alnum_hyphen_ext, 1.0) + | pynutil.add_weight( + delete_space + + (pynutil.delete("डॉट") | pynutil.delete("DOT") | pynutil.delete("प्वाइंट")) + + pynutil.insert(".") + delete_space + + alnum_token + pynini.closure(delete_space + alnum_token, 0), 1.0) + | pynutil.add_weight( + delete_space + pynutil.delete("स्पेस") + pynutil.insert(" ") + + delete_space + alnum_token + pynini.closure(delete_space + alnum_token, 0), 1.0) + | pynutil.add_weight(lit_open_paren + alnum_token + pynini.closure(delete_space + alnum_token, 0), 1.0) + | pynutil.add_weight(lit_close_paren + alnum_token + pynini.closure(delete_space + alnum_token, 0), 1.0) + | pynutil.add_weight(lit_close_paren, 1.0) + , 0) + alnum_letterdigit_fst = pynutil.insert("domain: \"") + alnum_body + pynutil.insert("\"") + + chem_fst = pynutil.add_weight( + pynutil.insert("domain: \"") + chem_named_map + pynutil.insert("\""), 0.04 + ) if chem_named_map is not None else pynini.accep("") + + graph = ( + pynutil.add_weight(ip_fst, 1.00) + | pynutil.add_weight(email_fst, 1.05) + | pynutil.add_weight(windows_path_fst, 1.06) + | pynutil.add_weight(unc_path_fst, 1.07) + | pynutil.add_weight(url_fst, 0.10) + | pynutil.add_weight(www_fst, 0.11) + | pynutil.add_weight(url_fst_bare, 0.50) + | pynutil.add_weight(www_fst_bare, 0.51) + | pynutil.add_weight(unix_abs_path_fst, 15.00) + | pynutil.add_weight(tilde_path_fst, 1.12) + | pynutil.add_weight(unix_rel_path_fst, 15.00) + | pynutil.add_weight(literal_rel_path_fst, 1.15) + | pynutil.add_weight(alnum_phrase_fst, 0.05) + | pynutil.add_weight(chem_spelled_fst, 1.18) + | pynutil.add_weight(alnum_letterdigit_fst, 0.90) + | pynutil.add_weight(plain_fst, 1.30) + | chem_fst + ) + + self.fst = self.add_tokens(graph).optimize() \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/hi/taggers/tokenize_and_classify.py b/nemo_text_processing/inverse_text_normalization/hi/taggers/tokenize_and_classify.py index 50abab0e5..aef975b8e 100644 --- a/nemo_text_processing/inverse_text_normalization/hi/taggers/tokenize_and_classify.py +++ b/nemo_text_processing/inverse_text_normalization/hi/taggers/tokenize_and_classify.py @@ -26,6 +26,7 @@ generator_main, ) from nemo_text_processing.inverse_text_normalization.hi.taggers.cardinal import CardinalFst +from nemo_text_processing.inverse_text_normalization.hi.taggers.electronic import ElectronicFst from nemo_text_processing.inverse_text_normalization.hi.taggers.date import DateFst from nemo_text_processing.inverse_text_normalization.hi.taggers.decimal import DecimalFst from nemo_text_processing.inverse_text_normalization.hi.taggers.fraction import FractionFst @@ -89,6 +90,8 @@ def __init__( money_graph = money.fst telephone = TelephoneFst(cardinal) telephone_graph = telephone.fst + electronic = ElectronicFst() + electronic_graph = electronic.fst punct_graph = PunctuationFst().fst whitelist_graph = WhiteListFst().fst word_graph = WordFst().fst @@ -103,6 +106,7 @@ def __init__( | pynutil.add_weight(measure_graph, 1.1) | pynutil.add_weight(money_graph, 1.1) | pynutil.add_weight(telephone_graph, 1.1) + | pynutil.add_weight(electronic_graph, 0.5) | pynutil.add_weight(word_graph, 100) | pynutil.add_weight(whitelist_graph, 1.01) ) diff --git a/nemo_text_processing/inverse_text_normalization/hi/verbalizers/electronic.py b/nemo_text_processing/inverse_text_normalization/hi/verbalizers/electronic.py new file mode 100644 index 000000000..883fd7909 --- /dev/null +++ b/nemo_text_processing/inverse_text_normalization/hi/verbalizers/electronic.py @@ -0,0 +1,48 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License") + +import pynini +from pynini.lib import pynutil + +from nemo_text_processing.inverse_text_normalization.hi.graph_utils import ( + NEMO_NOT_QUOTE, + GraphFst, + delete_space, +) + + +class ElectronicFst(GraphFst): + """ + ITN verbalizer for electronic expressions. + All fields pass content through unchanged. + """ + + def __init__(self): + super().__init__(name="electronic", kind="verbalize") + + def field_graph(field_name: str) -> pynini.Fst: + return ( + pynutil.delete(f"{field_name}:") + + delete_space + + pynutil.delete("\"") + + pynini.closure(NEMO_NOT_QUOTE, 1) + + pynutil.delete("\"") + ) + + ip_graph = field_graph("ip") + domain_graph = field_graph("domain") + username_graph = field_graph("username") + path_graph = field_graph("path") + + email_graph = ( + username_graph + + pynutil.insert("@") + + delete_space + + domain_graph + ) + + # email before domain (both use domain: field) + graph = ip_graph | email_graph | path_graph | domain_graph + + self.fst = self.delete_tokens(graph).optimize() \ No newline at end of file diff --git a/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize.py b/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize.py index f1a6c55a3..ff8aa8661 100644 --- a/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize.py +++ b/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize.py @@ -15,6 +15,7 @@ from nemo_text_processing.inverse_text_normalization.hi.graph_utils import GraphFst from nemo_text_processing.inverse_text_normalization.hi.verbalizers.cardinal import CardinalFst +from nemo_text_processing.inverse_text_normalization.hi.verbalizers.electronic import ElectronicFst from nemo_text_processing.inverse_text_normalization.hi.verbalizers.date import DateFst from nemo_text_processing.inverse_text_normalization.hi.verbalizers.decimal import DecimalFst from nemo_text_processing.inverse_text_normalization.hi.verbalizers.fraction import FractionFst @@ -48,6 +49,7 @@ def __init__(self): measure_graph = MeasureFst(cardinal, decimal).fst money_graph = MoneyFst(cardinal, decimal).fst telephone_graph = TelephoneFst(cardinal).fst + electronic_graph = ElectronicFst().fst word_graph = WordFst().fst whitelist_graph = WhiteListFst().fst @@ -63,5 +65,6 @@ def __init__(self): | measure_graph | money_graph | telephone_graph + | electronic_graph ) self.fst = graph diff --git a/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize_final.py b/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize_final.py index 17dfebf64..acaa71d87 100644 --- a/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize_final.py +++ b/nemo_text_processing/inverse_text_normalization/hi/verbalizers/verbalize_final.py @@ -16,7 +16,7 @@ import pynini from pynini.lib import pynutil -from nemo_text_processing.inverse_text_normalization.hi.graph_utils import GraphFst, delete_extra_space, delete_space +from nemo_text_processing.inverse_text_normalization.hi.graph_utils import NEMO_CHAR, GraphFst, delete_extra_space, delete_space from nemo_text_processing.inverse_text_normalization.hi.verbalizers.verbalize import VerbalizeFst from nemo_text_processing.inverse_text_normalization.hi.verbalizers.word import WordFst @@ -42,4 +42,11 @@ def __init__(self): + pynutil.delete("}") ) graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space - self.fst = graph + # Remove space before sentence-ending punctuation: "CODE ." -> "CODE." + remove_space_before_punct = pynini.cdrewrite( + pynutil.delete(" "), + "", + pynini.union(".", "।", ",", "!", "?", ";", ":"), + pynini.closure(NEMO_CHAR), + ) + self.fst = graph @ remove_space_before_punct diff --git a/nemo_text_processing/text_normalization/utils_audio_based.py b/nemo_text_processing/text_normalization/utils_audio_based.py index 2e9626d9e..1e3e811af 100644 --- a/nemo_text_processing/text_normalization/utils_audio_based.py +++ b/nemo_text_processing/text_normalization/utils_audio_based.py @@ -14,7 +14,10 @@ from typing import Dict -from cdifflib import CSequenceMatcher +try: + from cdifflib import CSequenceMatcher +except ImportError: + from difflib import SequenceMatcher as CSequenceMatcher from nemo_text_processing.utils.logging import logger diff --git a/tests/nemo_text_processing/hi/data_inverse_text_normalization/test_cases_electronic.txt b/tests/nemo_text_processing/hi/data_inverse_text_normalization/test_cases_electronic.txt new file mode 100644 index 000000000..66c2fb7d8 --- /dev/null +++ b/tests/nemo_text_processing/hi/data_inverse_text_normalization/test_cases_electronic.txt @@ -0,0 +1,113 @@ +ए ए एस सी डॉट एन आई सी डॉट इन फॉरवर्ड स्लैश~aasc.nic.in/ +ए आर एन ओ एल डी सी ए टी एच वाई एट एम आई एल एल ई आर हाइफ़न सी ए आर आर डॉट कॉम~arnoldcathy@miller-carr.com +ए डी एच आई आई एस एच एट जीमेल डॉट कॉम~adhiish@gmail.com +ए एल आई आई हाइफ़न जी ए ए एन जी यू एल आई आई डॉट कॉम~alii-gaangulii.com +एच टी टी पी एस कोलन फॉरवर्ड स्लैश फॉरवर्ड स्लैश ए आर ओ डी डी डी एच ए ए डॉट नेट फॉरवर्ड स्लैश~https://arodddhaa.net/ +एच टी टी पी एस कोलन फॉरवर्ड स्लैश फॉरवर्ड स्लैश ए एल आई आई हाइफ़न एन यू यू आर ए ए एन आई आई डॉट कॉम फॉरवर्ड स्लैश टैग्स फॉरवर्ड स्लैश अबाउट फॉरवर्ड स्लैश~https://alii-nuuraanii.com/tags/about/ +एच टी टी पी एस कोलन फॉरवर्ड स्लैश फॉरवर्ड स्लैश ई एन डॉट विकिपीडिया डॉट ऑर्ग फॉरवर्ड स्लैश विकी फॉरवर्ड स्लैश~https://en.wikipedia.org/wiki/ +एच टी टी पी एस कोलन फॉरवर्ड स्लैश फॉरवर्ड स्लैश एम ए वाई एस हाइफ़न ई डी वार्ड एस डॉट कॉम फॉरवर्ड स्लैश~https://mays-edwards.com/ +एच टी टी पी कोलन फॉरवर्ड स्लैश फॉरवर्ड स्लैश एम एच ए ए डी ई वी डॉट ऑर्ग फॉरवर्ड स्लैश अबाउट डॉट एच टी एम एल~http://mhaadev.org/about.html +सी कोलन बैकवर्ड स्लैश यूज़र्स बैकवर्ड स्लैश एच पी बैकवर्ड स्लैश डेस्कटॉप बैकवर्ड स्लैश ऑडियो अंडर स्कोर फ़ाइल अंडर स्कोर दो डॉट एम पी तीन~C:\Users\HP\Desktop\Audio_file_2.mp3 +सी कोलन बैकवर्ड स्लैश यूज़र्स बैकवर्ड स्लैश एच पी बैकवर्ड स्लैश डेस्कटॉप बैकवर्ड स्लैश चैप्टर स्पेस एक स्पेस पाइथन डॉट पी डी एफ~C:\Users\HP\Desktop\chapter 1 python.pdf +होम / लिब्रे ऑफिस - इंप्रेस - टेम्पलेट्स - मास्टर /~home/libreoffice-impress-templates-master/ +होम / डेस्कटॉप~home/desktop +फॉरवर्ड स्लैश ई टी सी फॉरवर्ड स्लैश सी ए हाइफ़न सी ई आर टी आई एफ आई सी ए टी ई एस डॉट सी ओ एन एफ डॉट डी पी के जी हाइफ़न ओ एल डी~/etc/ca-certificates.conf.dpkg-old +बैकअप्स / टेम्प~backups/temp +डब्ल्यू डब्ल्यू डब्ल्यू डॉट ए एल यू एन आई वी डॉट ऑर्ग फॉरवर्ड स्लैश~www.aluniv.org/ +डब्ल्यू डब्ल्यू डब्ल्यू डॉट ए एम यू डॉट ए सी डॉट इन~www.amu.ac.in +लैपटॉप हाइफ़न चार तीन डॉट सानशेज़ हाइफ़न क्रॉफोर्ड~laptop-43.sanchez-crawford +~ फॉरवर्ड स्लैश वर्क फॉरवर्ड स्लैश ओ एस अंडर स्कोर सी ओ ओ आर डी अंडर स्कोर ओ आर डी आई एन ए एन सी ई अंडर स्कोर सर्वे डॉट एच~~/work/os_coord_ordinance_survey.h +आई ए तीन दो~IA32 +आई ई एल एफ शून्य शून्य चार~IELF004 +आई ई नौ जे~IE9J +आई एन छह तीन आठ नौ शून्य नौ शून्य~IN6389090 +आठ सात बी एफ जे यू आठ नौ छह शून्य वी एफ~87BFJU8960VF +आठ नौ सात छह एन एच जे ए के~8976NHJAK +आठ यू~8U +आर एस दो~RS2 +आर ज़ेड सी टी तीन एक चार आठ जी सात टी~RZCT3148G7T +आर तीन आठ शून्य~R380 +आर तीन एक शून्य~R310 +आर दो पाँच शून्य एस प्रो~R250S प्रो +ई आठ जे तीन~E8J3 +ई ई ई बी दो एक आठ~EEEB218 +ई ई ए डी शून्य पाँच एक~EEAD051 +ए ई ए डी आठ पाँच शून्य~AEAD850 +ए ए डी ई चार शून्य चार~AADE404 +ए एक शून्य एक आठ एस~A1018S +एक दो शून्य एम एम~120MM +एक्स ए पाँच तीन~XA53 +एक्स आठ छह~X86 +एक्स दो पाँच~X25 +एच ए जे डी एफ पाँच चार तीन आठ जे~HAJDF5438J +एन एफ डी एल दो तीन एक~NFDL231 +एन ओ आठ आठ~NO88 +एन नौ पाँच~N95 +एफ ई ए ज़ेड एफ आठ छह दो शून्य जे~FEAZF8620J +एफ ए ए एच एफ आठ छह चार तीन के~FAAHF8643K +एफ आठ~F8 +एम एम एक्स दो दो शून्य नौ शून्य आठ~MMX220908 +एम ओ यू दो शून्य शून्य दो शून्य~MOU20020 +एम के दो~MK2 +एल ए तीन छह~LA36 +एल ए तीन तीन~LA33 +एल तीन जी ए एक~L3GA1 +एल वाई छह चार~LY64 +एस आई एस दो तीन दो के दो~SIS232K2 +एस जे चार छह नौ~SJ469 +एस जे यू टी ए सात पाँच चार नौ एल~SJUTA7549L +एस पाँच तीन~S53 +सी एच तीन सी ओ ओ –~CH3COO– +ऑडी आठ शून्य बटा नौ शून्य बी चार~ऑडी 80/90 B4 +ऑडी ए छह~ऑडी A6 +ऑपपो एन्को एक्स दो~ऑपपो एन्को X2 +ऑपपो एम दो~ऑपपो M2 +ओ डी एम ई आठ एम एफ~ODME8MF +ओ डी शून्य दो सात छह तीन पाँच~OD027635 +ओ डी नौ तीन~OD93 +ओ पाँच डी पी एक~O5DP1 +के एन डब्ल्यू तीन तीन चार~KNW334 +के एल एम आठ एक~KLM81 +के डी एच ई आठ तीन एन नौ~KDHE83N9 +के डी तीन आठ चार~KD384 +सी ए एन ओ एन ए सात पाँच~Canon A75 +सी छह एच एक दो ओ छह~C6H12O6 +छह एस~6S +ज़ेड एन एस ओ चार~ZnSO4 +जी छह सी ओ आठ~G6CO8 +जी दो सी एम नौ~G2CM9 +जी एस ए टी हाइफ़न एक आठ~GSAT-18 +जे आठ एफ छह~J8F6 +जे आठ तीन डी के~J83DK +जे आर सात चार~JR74 +जे एक ओ पी नौ~J1OP9 +ज़ेड एक्स आठ शून्य एक नौ आठ शून्य~ZX80 1980 +ज़ेड एक्स आठ एक~ZX81 +ज़ेड एम आठ छह पाँच तीन आठ~ZM86538 +टी दो आठ एस~T28S +सी सात एच आठ~C7H8 +डब्ल्यू डब्ल्यू डब्ल्यू दो नौ शून्य एक एफ एन~WWW2901FN +डब्ल्यू डब्ल्यू डब्ल्यू सात सात आठ~WWW778 +डी आठ जे डी जे डब्ल्यू~D8JDJW +डी आठ जे शून्य डी के सात डी~D8J0DK7D +डी आठ जे सात डी एच तीन~D8J7DH3 +पाँच सी~5C +पी आर तीन दो दो एक सात सात~PR322177 +पी एम दो प्वाइंट पाँच~PM2.5 +पी के छह छह सात आठ~PK6678 +पी दो एम सी दो~P2MC2 +पी नौ पाँच~P95 +बी ई ई सी चार तीन शून्य~BEEC430 +बी ई ए डी आठ दो सात~BEAD827 +यू आठ जे डब्ल्यू~U8JW +यू आठ तीन जे एस~U83JS +यू ई आठ चार~UE84 +वाई आर सात तीन एच~YR73H +वाई छह पाँच~Y65 +वी क्यू सात शून्य एक जे~VQ701J +वी छह~V6 +वी दो तीन~V23 +टी हाइफ़न एक्स चार शून्य शून्य~T-X400 +टी हाइफ़न सात दो~T-72 +दो पाँच पाँच डॉट एक छह आठ डॉट चार छह डॉट एक सात पाँच~255.168.46.175 +आठ नौ डॉट तीन सात डॉट दो एक दो डॉट एक पाँच~89.37.212.15 diff --git a/tests/nemo_text_processing/hi/test_electronic.py b/tests/nemo_text_processing/hi/test_electronic.py index 6b2e3b4f0..04374e853 100644 --- a/tests/nemo_text_processing/hi/test_electronic.py +++ b/tests/nemo_text_processing/hi/test_electronic.py @@ -1,4 +1,4 @@ -# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ import pytest from parameterized import parameterized +from nemo_text_processing.inverse_text_normalization.inverse_normalize import InverseNormalizer from nemo_text_processing.text_normalization.normalize import Normalizer from ..utils import CACHE_DIR, parse_test_case_file @@ -22,12 +23,20 @@ class TestElectronic: normalizer = Normalizer( - input_case='cased', lang='hi', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=True + input_case='cased', lang='hi', cache_dir=CACHE_DIR, overwrite_cache=False, post_process=False ) + inverse_normalizer = InverseNormalizer(lang='hi', cache_dir=CACHE_DIR, overwrite_cache=False) @parameterized.expand(parse_test_case_file('hi/data_text_normalization/test_cases_electronic.txt')) @pytest.mark.run_only_on('CPU') @pytest.mark.unit def test_norm(self, test_input, expected): - pred = self.normalizer.normalize(test_input, verbose=False, punct_post_process=True) - assert pred == expected + pred = self.normalizer.normalize(test_input, verbose=False) + assert pred.strip() == expected.strip() + + @parameterized.expand(parse_test_case_file('hi/data_inverse_text_normalization/test_cases_electronic.txt')) + @pytest.mark.run_only_on('CPU') + @pytest.mark.unit + def test_denorm(self, test_input, expected): + pred = self.inverse_normalizer.inverse_normalize(test_input, verbose=False) + assert pred.strip() == expected.strip() diff --git a/tests/nemo_text_processing/hi/test_sparrowhawk_inverse_text_normalization.sh b/tests/nemo_text_processing/hi/test_sparrowhawk_inverse_text_normalization.sh index 0e31a1a00..b1de19290 100644 --- a/tests/nemo_text_processing/hi/test_sparrowhawk_inverse_text_normalization.sh +++ b/tests/nemo_text_processing/hi/test_sparrowhawk_inverse_text_normalization.sh @@ -6,15 +6,30 @@ runtest () { input=$1 cd /workspace/sparrowhawk/documentation/grammars + # per-case timeout (seconds): bounds any input that causes a lattice blow-up + # so the suite can never hang forever. Override with CASE_TIMEOUT env var. + : ${CASE_TIMEOUT:=20} + total=$(wc -l < "$input") + n=0 + # read test file while read testcase; do + n=$((n+1)) IFS='~' read spoken written <<< $testcase - denorm_pred=$(echo $spoken | normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) + + # run with a timeout; if it times out, mark the prediction so it fails loudly + denorm_pred=$(echo $spoken | timeout ${CASE_TIMEOUT} normalizer_main --config=sparrowhawk_configuration.ascii_proto 2>&1 | tail -n 1) + if [ $? -eq 124 ]; then + denorm_pred="<>" + fi # trim white space written="$(echo -e "${written}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" denorm_pred="$(echo -e "${denorm_pred}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + # progress to stderr so you can see it moving (not part of test output) + echo "[$n/$total] '$spoken' -> '$denorm_pred'" >&2 + # input expected actual assertEquals "$spoken" "$written" "$denorm_pred" done < "$input" @@ -83,6 +98,11 @@ testITNWhiteList() { runtest $input } +testITNElectronic() { + input=$PROJECT_DIR/hi/data_inverse_text_normalization/test_cases_electronic.txt + runtest $input +} + # Load shUnit2 . $PROJECT_DIR/../shunit2/shunit2 diff --git a/tools/text_processing_deployment/Dockerfile b/tools/text_processing_deployment/Dockerfile index 972bab75e..c6b33e39f 100644 --- a/tools/text_processing_deployment/Dockerfile +++ b/tools/text_processing_deployment/Dockerfile @@ -24,17 +24,23 @@ WORKDIR /workspace # install dependencies RUN echo "deb http://archive.debian.org/debian stretch main contrib non-free" > /etc/apt/sources.list -RUN apt-get update && apt-get upgrade -y && apt-get install -y --reinstall build-essential pkg-config git make wget +# stretch is EOL: its archive Release file is expired, so apt needs +# Check-Valid-Until=false and --allow-unauthenticated to install anything. +RUN apt-get -o Acquire::Check-Valid-Until=false update && \ + apt-get install -y --allow-unauthenticated build-essential pkg-config git make wget ca-certificates +# the old base ships outdated CA certs; skip TLS verification for the github/ +# wget downloads below (ephemeral build container, fixed open-source URLs). +RUN git config --global http.sslVerify false RUN conda install conda-build -y RUN conda install -c conda-forge thrax=1.3.4 -y RUN git clone https://github.com/google/re2 RUN cd re2 && git checkout tags/2022-02-01 && make && make install -RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v2.5.0/protobuf-2.5.0.tar.gz +RUN wget --no-check-certificate https://github.com/protocolbuffers/protobuf/releases/download/v2.5.0/protobuf-2.5.0.tar.gz RUN tar xzvf protobuf-2.5.0.tar.gz RUN cd protobuf-2.5.0 && ./configure && make && make install && ldconfig RUN printf "# Conda lib path \n/opt/conda/lib" > /etc/ld.so.conf.d/conda.so.conf ENV CPPFLAGS="-I/opt/conda/include" ENV LDFLAGS="-L/opt/conda/lib" -RUN git clone https://github.com/anand-nv/sparrowhawk.git && cd sparrowhawk && git checkout nemo_tests && apt-get install -y autoconf && bash autoreconf && ./configure && make && make install && ldconfig +RUN git clone https://github.com/anand-nv/sparrowhawk.git && cd sparrowhawk && git checkout nemo_tests && apt-get -o Acquire::Check-Valid-Until=false install -y --allow-unauthenticated autoconf && bash autoreconf && ./configure && make && make install && ldconfig RUN git clone https://github.com/kward/shunit2.git RUN echo "DONE"