Changed the model, but not the result? Check the WebSocket configuration
Streaming text and the final offline result can come from different models. FunASR's Python WebSocket server exposes separate settings for them. When replacing a model, first identify which stage you are changing and whether the running script actually forwards the options.
A service can accept a new --asr_model argument and start normally while its offline constructor still uses a fixed model name and revision. Parsing a command-line option is not the same as using it. A small source inspection can connect those two steps without loading a model.
This article concerns a source fix in the Python WebSocket server, not a shared configuration protocol for every runtime. The source check sends no audio, starts no service and downloads no weights. Existing GPU verification uses synthetic models and audio to check routing, not pretrained recognition quality or deployment performance.
1. Separate offline and streaming settings
| Stage to change | Options to inspect |
|---|---|
| Offline recognition, including the offline stage of two-pass recognition | --asr_model and --asr_model_revision |
| Streaming recognition | --asr_model_online and --asr_model_online_revision |
| Voice activity detection and punctuation | Their own --vad_model, --punc_model and revision options |
Replacing the offline model does not replace the streaming, VAD or punctuation model. Different intermediate and final text is not, by itself, evidence of a loading bug. Compare the outputs of each stage with the actual constructor arguments.
2. Inspect the script you run, not just the pip version
Original fix #3887 identified the hard-coded constructor; integration #3888 preserving its author history has merged. The fixed offline constructor uses model=args.asr_model and model_revision=args.asr_model_revision. The default remains paraformer-zh with v2.0.4.
Compare against the server at the pinned source commit. Upgrading the root funasr package does not replace a script copied into another directory. Record the actual script path, source commit, Python environment and weight-file SHA256 values before deciding that your service has been upgraded.
3. Run a read-only source check
Put this check in a separate file and pass the actual server script path as its only argument. It reads and parses source with the Python standard library without importing the server. Importing the server itself can initialize models and is not a lightweight inspection.
import ast
import hashlib
import json
from pathlib import Path
import sys
path = Path(sys.argv[1])
raw = path.read_bytes()
tree = ast.parse(raw, filename=str(path))
calls = [node.value for node in tree.body
if isinstance(node, ast.Assign)
and any(isinstance(target, ast.Name) and target.id == "model_asr"
for target in node.targets)]
assert len(calls) == 1 and isinstance(calls[0], ast.Call)
options = {item.arg: ast.unparse(item.value) for item in calls[0].keywords}
print(json.dumps({
"sha256": hashlib.sha256(raw).hexdigest(),
"offline_model": options.get("model"),
"offline_revision": options.get("model_revision"),
}, indent=2))
python audit_model_options.py /path/to/FunASR/runtime/python/websocket/funasr_wss_server.py
For the fixed source linked above, the two option outputs are args.asr_model and args.asr_model_revision. String constants instead indicate that this script does not use those options. A customized script with a different top-level structure may fail the check and needs manual inspection. This is not a general analyzer for arbitrary Python programs, nor proof that a running process loaded this file.
4. What remains after the routing check?
In an isolated environment with authorized local weights, check the default configuration, custom model paths and custom revisions separately. For a local directory, a revision string is not a substitute for file hashes. For a remote model identifier, record the revision actually resolved. Model weights, configuration and frontend must be compatible; a readable path alone does not prove deployability. Do not replace dependencies or weights directly in a shared service.
The fix has five real CUDA routing cases covering the default, two model paths and two revisions. They use real AutoModel construction with small local synthetic weights and ten outputs in total. This verifies that the requested settings reach the intended model. It is not a pretrained accuracy evaluation, concurrency-capacity test, complete WebSocket network test or production upgrade.
Next, use recordings you are authorized to process. Keep streaming and final outputs separately and inspect omissions, terminology, segmentation and timestamps before evaluating latency and concurrency. Do not publish private recordings, transcripts or absolute paths in issues. Start with redacted configuration and a shareable minimal reproduction.
Separate configuration bugs from recognition problems
If source routing is correct but terminology is still poor, inspect the hotword path and the model's capabilities. If endings are missing, inspect audio boundaries and VAD. Change one setting at a time and retain inputs and per-recording outputs so the next diagnostic step has evidence.
Use the meeting-transcript acceptance checklist to compare configuration, text and timestamps on your own recordings.