From 1c217c5249003e48979d8a7e7b5302d57de1d017 Mon Sep 17 00:00:00 2001 From: Tanishq Date: Wed, 11 Nov 2026 04:59:59 +0530 Subject: [PATCH] fix(runtime): merge spelled-out acronyms into one word in sentence_postprocess MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit abbr_dispose in the funasr_onnx and funasr_torch copies of postprocess_utils.py joins a run of single-letter tokens into one acronym with one [begin, end] timestamp, but still appended every letter as its own word. The 2023 change that builds the acronym as one word (2ab8b7f47) only went into funasr/utils/postprocess_utils.py. So for tokens ["我", "用", "a", "i", "写", "代", "码"] with a timestamp per token, the runtime copies return "我 用 A I 写 代 码" with 7 words and 6 timestamps, where the in-tree copy returns "我 用 AI 写 代 码" with 6 and 6. The Paraformer wrappers of both runtimes return only that text and the timestamp list, so splitting preds on spaces pairs every word after the acronym with the next word's time and leaves the last word without one. Without timestamps the text was already right, but the word list was split the same way. Port the in-tree code: build abbr_word and append it once. Extend tests/test_sentence_postprocess_timestamps.py, which checks the three copies against one expectation, with acronyms in Chinese and English text, with and without timestamps. --- .../funasr_torch/utils/postprocess_utils.py | 7 ++++--- .../funasr_onnx/utils/postprocess_utils.py | 7 ++++--- tests/test_sentence_postprocess_timestamps.py | 20 +++++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/runtime/python/libtorch/funasr_torch/utils/postprocess_utils.py b/runtime/python/libtorch/funasr_torch/utils/postprocess_utils.py index 3f8d9999e..34180841c 100644 --- a/runtime/python/libtorch/funasr_torch/utils/postprocess_utils.py +++ b/runtime/python/libtorch/funasr_torch/utils/postprocess_utils.py @@ -106,17 +106,18 @@ def abbr_dispose(words: List[Any], time_stamp: List[List] = None) -> List[Any]: if num in abbr_begin: if time_stamp is not None: begin = time_stamp[ts_nums[num]][0] - word_lists.append(words[num].upper()) + abbr_word = words[num].upper() num += 1 while num < words_size: if num in abbr_end: - word_lists.append(words[num].upper()) + abbr_word += words[num].upper() last_num = num break else: if words[num].encode("utf-8").isalpha(): - word_lists.append(words[num].upper()) + abbr_word += words[num].upper() num += 1 + word_lists.append(abbr_word) if time_stamp is not None: end = time_stamp[ts_nums[num]][1] ts_lists.append([begin, end]) diff --git a/runtime/python/onnxruntime/funasr_onnx/utils/postprocess_utils.py b/runtime/python/onnxruntime/funasr_onnx/utils/postprocess_utils.py index 343ed383a..3a62afee6 100644 --- a/runtime/python/onnxruntime/funasr_onnx/utils/postprocess_utils.py +++ b/runtime/python/onnxruntime/funasr_onnx/utils/postprocess_utils.py @@ -108,17 +108,18 @@ def abbr_dispose(words: List[Any], time_stamp: List[List] = None) -> List[Any]: if num in abbr_begin: if time_stamp is not None: begin = time_stamp[ts_nums[num]][0] - word_lists.append(words[num].upper()) + abbr_word = words[num].upper() num += 1 while num < words_size: if num in abbr_end: - word_lists.append(words[num].upper()) + abbr_word += words[num].upper() last_num = num break else: if words[num].encode("utf-8").isalpha(): - word_lists.append(words[num].upper()) + abbr_word += words[num].upper() num += 1 + word_lists.append(abbr_word) if time_stamp is not None: end = time_stamp[ts_nums[num]][1] ts_lists.append([begin, end]) diff --git a/tests/test_sentence_postprocess_timestamps.py b/tests/test_sentence_postprocess_timestamps.py index 0aa6965c7..921495097 100644 --- a/tests/test_sentence_postprocess_timestamps.py +++ b/tests/test_sentence_postprocess_timestamps.py @@ -96,6 +96,26 @@ def test_untimed_transcription_still_accepts_filtered_tokens(self): ("你好", ["你", "好"]), ) + def test_spelled_out_letters_are_one_word_with_one_timestamp(self): + self.assert_timestamps( + ["我", "用", "a", "i", "写", "代", "码"], + ["我", "用", "AI", "写", "代", "码"], + [[0, 100], [100, 200], [200, 400], [400, 500], [500, 600], [600, 700]], + ) + self.assert_timestamps( + ["i", "b", "m", "is", "here"], + ["IBM", "is", "here"], + [[0, 300], [300, 400], [400, 500]], + ) + + def test_spelled_out_letters_are_one_word_without_timestamps(self): + for path in POSTPROCESS_UTILS: + with self.subTest(path=path): + self.assertEqual( + _load_sentence_postprocess(path)(["看", "c", "c", "t", "v", "新", "闻"]), + ("看CCTV新闻", ["看", "CCTV", "新", "闻"]), + ) + if __name__ == "__main__": unittest.main()