fix(tongyi): avoid gevent busy loop in STT#3261
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the speech-to-text subprocess execution logic to be enabled by default, disabling it only when the TONGYI_STT_SUBPROCESS environment variable is set to a false-like value. The tests have been updated to accommodate this default behavior. Feedback on the tests recommends avoiding clearing the entire environment using patch.dict(os.environ, {}, clear=True) as it can cause unexpected failures, and suggests selectively filtering out only the target environment variable instead.
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.
|
|
||
| audio = MagicMock(frame_rate=16000) | ||
| with patch.dict(os.environ, {"TONGYI_STT_SUBPROCESS": "1"}): | ||
| with patch.dict(os.environ, {}, clear=True): |
There was a problem hiding this comment.
Using clear=True with an empty dictionary in patch.dict(os.environ, {}, clear=True) completely clears all environment variables (including critical ones like PATH, PYTHONPATH, TMPDIR, etc.) for the duration of the block. This can cause unexpected failures in tests, subprocesses, or test runners in certain environments.
Instead, you should preserve the existing environment variables and only exclude or remove the TONGYI_STT_SUBPROCESS variable.
| with patch.dict(os.environ, {}, clear=True): | |
| with patch.dict(os.environ, {k: v for k, v in os.environ.items() if k != "TONGYI_STT_SUBPROCESS"}, clear=True): |
b2acd0c to
fd8426f
Compare
fd8426f to
4708347
Compare
Summary
TONGYI_STT_SUBPROCESS=1as an explicit fallback path, instead of making subprocess execution the default0.2.1dashscopeto the verified1.25.xrange because this fix intentionally shims an SDK internal helperWhy
Dify's plugin runtime imports
dify_plugin, which globally appliesgevent.monkey.patch_all(sys=True). DashScopeRecognition.call()uses websocket duplex streaming and internally converts an async generator into a synchronous iterator throughdashscope.common.utils.iter_over_async().That bridge creates a thread and queue. Under gevent-patched threading/queue behavior, the Tongyi plugin process can remain in a busy event-loop state after STT requests, causing sustained high CPU in self-hosted
plugin_daemondeployments.This change keeps the normal in-process Recognition path, but swaps DashScope's async bridge to use native
threading.Threadand nativequeue.Queuewhen gevent has patched threading. The existing subprocess worker remains available by explicitly settingTONGYI_STT_SUBPROCESS=1.The shim is guarded and process-local:
threadinghas not been monkey-patchedNo upstream DashScope issue has been filed yet from this PR; this is currently handled as a Tongyi plugin compatibility shim for Dify's gevent-based plugin runtime.
Field validation
Tested on a self-hosted BsoftRAG/Dify deployment on a Hygon/Haiguang server.
Setup:
TONGYI_STT_SUBPROCESSandTONGYI_STT_RECOGNITION_TIMEOUTfrom the plugin daemon runtime.models/speech2text/speech2text.pyinto the installed Tongyi plugin venv./v1/audio-to-textrequests against thechat-audio-testapp.Observed result:
plugin_daemonhad transient CPU during active STT processing.plugin_daemonreturned to low CPU instead of staying at sustained single-core usage.0.19%after 5s,0.24%after 15s,0.33%after 30s0.47%after 5s,0.31%after 15s,0.27%after 30s0.30%after 5s,0.54%after 15s,0.32%after 30sTONGYI_STT_SUBPROCESS=1/TONGYI_STT_RECOGNITION_TIMEOUT=120configuration after the experiment.Local validation
Passed:
Result:
17 passed, 75 deselected.Passed:
Result:
All checks passed.Not run successfully:
The local global pre-commit hook runs the repository-wide pytest suite with the host Python 3.14 environment, where plugin dependencies such as
dify_plugin,dashscope,google,numpy, andtiktokenare not installed. This fails during test collection before exercising this Tongyi change.Also not run locally:
tests/test_llm_call.py, because it requires a realDASHSCOPE_API_KEY.