From 162ef07eb94fddd2ec0adca4892a6108bed46138 Mon Sep 17 00:00:00 2001 From: Arthur031221 Date: Fri, 2 Oct 2026 11:22:22 +0800 Subject: [PATCH] fix: retain unpunctuated tail in sentence timestamps --- funasr/utils/timestamp_tools.py | 20 +++++++++++++++++ tests/test_punc_model_none.py | 27 +++++++++++++++++++++++ tests/test_timestamp_tools.py | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/funasr/utils/timestamp_tools.py b/funasr/utils/timestamp_tools.py index fb77702eb6..b97e405f33 100644 --- a/funasr/utils/timestamp_tools.py +++ b/funasr/utils/timestamp_tools.py @@ -216,6 +216,16 @@ def timestamp_sentence( sentence_text_seg = "" ts_list = [] sentence_start = None + if sentence_text and all(timestamp is not None for timestamp in ts_list): + sentence = { + "text": sentence_text, + "start": sentence_start, + "end": sentence_end, + "timestamp": ts_list, + } + if return_raw_text: + sentence["raw_text"] = sentence_text_seg.rstrip() + res.append(sentence) return res @@ -310,4 +320,14 @@ def timestamp_sentence_en( sentence_text_seg = "" ts_list = [] sentence_start = None + if sentence_text and all(timestamp is not None for timestamp in ts_list): + sentence = { + "text": sentence_text, + "start": sentence_start, + "end": sentence_end, + "timestamp": ts_list, + } + if return_raw_text: + sentence["raw_text"] = sentence_text_seg.rstrip() + res.append(sentence) return res diff --git a/tests/test_punc_model_none.py b/tests/test_punc_model_none.py index c803b822cc..4e14eb705d 100644 --- a/tests/test_punc_model_none.py +++ b/tests/test_punc_model_none.py @@ -646,6 +646,33 @@ def test_sentence_timestamp_ignores_empty_asr_words(self, mock_prep, mock_load, self.assertEqual(results[0]["sentence_info"][0]["text"], "你好。") + @patch("funasr.auto.auto_model.slice_padding_audio_samples") + @patch("funasr.auto.auto_model.load_audio_text_image_video") + @patch("funasr.auto.auto_model.prepare_data_iterator") + def test_sentence_timestamp_keeps_unpunctuated_tail_without_asr_words( + self, mock_prep, mock_load, mock_slice + ): + am = self._make_auto_model(punc_model=MagicMock()) + results_seq = [ + [{"key": "test_utt", "value": [[0, 300]]}], + [{"text": "hello world", "timestamp": [[0, 100], [100, 300]]}], + [{"text": "hello world", "punc_array": [1, 1]}], + ] + am.inference = MagicMock(side_effect=lambda *args, **kwargs: results_seq.pop(0)) + mock_prep.return_value = (["test_utt"], [np.zeros(4800, dtype=np.float32)]) + mock_load.return_value = np.zeros(4800, dtype=np.float32) + mock_slice.return_value = ([np.zeros(4800, dtype=np.float32)], [4800]) + + result = am.inference_with_vad( + "dummy_input", sentence_timestamp=True, en_post_proc=True + ) + + self.assertEqual( + [item["text"].strip() for item in result[0]["sentence_info"]], + ["hello world"], + ) + self.assertEqual(result[0]["sentence_info"][0]["timestamp"], [[0, 100], [100, 300]]) + @patch("funasr.auto.auto_model.slice_padding_audio_samples") @patch("funasr.auto.auto_model.load_audio_text_image_video") @patch("funasr.auto.auto_model.prepare_data_iterator") diff --git a/tests/test_timestamp_tools.py b/tests/test_timestamp_tools.py index d5af9408e5..a9dd41d18e 100644 --- a/tests/test_timestamp_tools.py +++ b/tests/test_timestamp_tools.py @@ -1,3 +1,5 @@ +import pytest + from funasr.utils.timestamp_tools import timestamp_sentence, timestamp_sentence_en @@ -64,3 +66,40 @@ def test_timestamp_sentence_raw_text_keeps_token_separators(): ) assert [sentence["raw_text"] for sentence in result] == ["我 用", "iphone pro max"] + + +@pytest.mark.parametrize( + ("splitter", "words", "first", "last"), + [ + (timestamp_sentence, "你 好 世界", "你好。", "世界"), + (timestamp_sentence_en, "hello world again", "hello world.", "again"), + ], +) +def test_timestamp_sentence_keeps_unpunctuated_tail(splitter, words, first, last): + result = splitter( + [1, 3, 1], + [[0, 100], [100, 200], [200, 300]], + words, + return_raw_text=True, + ) + + assert [sentence["text"].strip() for sentence in result] == [first, last] + assert result[-1] == { + "text": last, + "start": 200, + "end": 300, + "timestamp": [[200, 300]], + "raw_text": last, + } + + +@pytest.mark.parametrize("splitter", [timestamp_sentence, timestamp_sentence_en]) +def test_timestamp_sentence_does_not_emit_tail_without_its_timestamp(splitter): + result = splitter( + [1, 3, 1], + [[0, 100], [100, 200]], + "hello world again", + ) + + assert len(result) == 1 + assert result[0]["end"] == 200