Skip to content

feat: add postprocess hotword correction for AutoModel.generate() (#2959)#3108

Open
lixuanqun wants to merge 2 commits into
modelscope:mainfrom
lixuanqun:feat/postprocess-hotwords-2959
Open

feat: add postprocess hotword correction for AutoModel.generate() (#2959)#3108
lixuanqun wants to merge 2 commits into
modelscope:mainfrom
lixuanqun:feat/postprocess-hotwords-2959

Conversation

@lixuanqun

Copy link
Copy Markdown

Summary

Add text-level postprocess hotword correction to AutoModel.generate() for issue #2959.

Model-level hotword / hotwords only works well for a small prompt set. This change adds postprocess_hotwords and postprocess_hotword_file so large vocabularies (for example thousands of stock names) can be corrected after decoding on any ASR model.

v1 scope follows the maintainer checklist:

  • new utility module funasr/utils/postprocess_hotwords.py
  • applied once per generate() call after ASR / punctuation / ITN output
  • updates top-level text and sentence-level text / sentence only
  • optional return_postprocess_hotword_matches
  • lazy optional deps for fuzzy matching: pypinyin, rapidfuzz

Type of change

  • Bug fix
  • Documentation
  • Example or demo
  • Runtime or deployment
  • Benchmark or evaluation
  • Model/training change

Validation

  • python -m compileall funasr examples tests
  • python -m unittest tests.test_postprocess_hotwords -v (10 tests)
  • Docs or links checked (docs/tutorial/README.md and docs/tutorial/README_zh.md)
  • Runtime/deployment command tested (not in v1 scope)

User impact

ASR users with large domain vocabularies can correct recognition output without changing the underlying model or runtime services. Explicit mappings work with no extra dependencies; fuzzy correction is opt-in.

Notes for reviewers

  • Does not cover runtime C++/ONNX/WebSocket services in v1.
  • Does not realign word/char timestamps after text replacement.
  • Fuzzy matching is a simplified pinyin + rapidfuzz implementation for v1; can be improved later toward the richer asr-hotword phoneme pipeline if needed.

Made with Cursor

…delscope#2959)

Implement text-level hotword replacement after ASR decoding so large
vocabularies can be corrected without model-level hotword injection.

Refs modelscope#2959

Co-authored-by: Cursor <cursoragent@cursor.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a text-level postprocess hotword correction feature to FunASR, allowing users to perform explicit or fuzzy-match text replacements on ASR decoding results. It adds documentation, integrates the correction logic into the generate pipeline, and includes comprehensive unit tests. The review feedback suggests caching the optional dependencies (pypinyin and rapidfuzz) globally to avoid repeated import overhead during fuzzy matching, and validating that the threshold parameter is within the valid range of [0.0, 1.0].

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread funasr/utils/postprocess_hotwords.py Outdated
Comment on lines +44 to +52
def _require_pypinyin():
try:
from pypinyin import Style, lazy_pinyin
except ImportError as exc:
raise ImportError(
"postprocess hotword fuzzy matching requires pypinyin. "
"Install it with: pip install pypinyin"
) from exc
return lazy_pinyin, Style

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In Python, performing local imports and handling exceptions repeatedly inside a nested loop (such as the sliding window in _apply_fuzzy) can introduce significant overhead. Caching the imported lazy_pinyin and Style globally after the first successful import avoids this overhead and improves performance.

_LAZY_PINYIN = None
_PINYIN_STYLE = None


def _require_pypinyin():
    global _LAZY_PINYIN, _PINYIN_STYLE
    if _LAZY_PINYIN is None:
        try:
            from pypinyin import Style, lazy_pinyin
            _LAZY_PINYIN = lazy_pinyin
            _PINYIN_STYLE = Style
        except ImportError as exc:
            raise ImportError(
                "postprocess hotword fuzzy matching requires pypinyin. "
                "Install it with: pip install pypinyin"
            ) from exc
    return _LAZY_PINYIN, _PINYIN_STYLE

Comment thread funasr/utils/postprocess_hotwords.py Outdated
Comment on lines +55 to +63
def _require_rapidfuzz():
try:
from rapidfuzz import fuzz
except ImportError as exc:
raise ImportError(
"postprocess hotword fuzzy matching requires rapidfuzz. "
"Install it with: pip install rapidfuzz"
) from exc
return fuzz

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similarly to _require_pypinyin, caching the imported fuzz module globally avoids repeated local imports and try-except checks, ensuring consistent performance.

Suggested change
def _require_rapidfuzz():
try:
from rapidfuzz import fuzz
except ImportError as exc:
raise ImportError(
"postprocess hotword fuzzy matching requires rapidfuzz. "
"Install it with: pip install rapidfuzz"
) from exc
return fuzz
_RAPIDFUZZ_FUZZ = None
def _require_rapidfuzz():
global _RAPIDFUZZ_FUZZ
if _RAPIDFUZZ_FUZZ is None:
try:
from rapidfuzz import fuzz
_RAPIDFUZZ_FUZZ = fuzz
except ImportError as exc:
raise ImportError(
"postprocess hotword fuzzy matching requires rapidfuzz. "
"Install it with: pip install rapidfuzz"
) from exc
return _RAPIDFUZZ_FUZZ

enable_fuzzy: bool = True,
):
self.explicit_map = dict(explicit_map or {})
self.threshold = float(threshold)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent unexpected behavior or silent failures, it is recommended to validate that the postprocess_hotword_threshold is within the valid range of [0.0, 1.0].

Suggested change
self.threshold = float(threshold)
self.threshold = float(threshold)
if not (0.0 <= self.threshold <= 1.0):
raise ValueError(f"postprocess_hotword_threshold must be between 0.0 and 1.0, got {threshold}")

Cache optional pypinyin/rapidfuzz imports and validate threshold range.

Co-authored-by: Cursor <cursoragent@cursor.com>
@lixuanqun

Copy link
Copy Markdown
Author

Addressed the review feedback: cached optional \pypinyin/
apidfuzz\ imports and added \postprocess_hotword_threshold\ range validation in 18b102f3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant