[fix] 修复了sensevoice STT重复下载模型文件和缺失依赖项的问题 - #9897
Conversation
There was a problem hiding this comment.
Hey - I've found 3 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="astrbot/core/provider/sources/sensevoice_selfhosted_source.py" line_range="85" />
<code_context>
+ loop = asyncio.get_event_loop()
+ if loop.is_running():
+ logger.warning("检测到运行中的事件循环,自动安装可能失败。建议手动安装依赖。")
+ asyncio.run(_install_dependencies())
+ except Exception as e:
+ logger.error(f"自动安装依赖失败: {e}")
</code_context>
<issue_to_address>
**issue (bug_risk):** `asyncio.run(_install_dependencies())` raises `RuntimeError` whenever SenseVoice is initialized through the normal async provider lifecycle, because `initialize()` already runs inside an active event loop. The exception is caught and reported as dependency-installation failure, so automatic installation never works when dependencies are missing.
**Triggers:** When any SenseVoice dependency is not installed before provider initialization.
**Suggested fix:** Await `_install_dependencies()` directly from an async initialization path instead of calling `asyncio.run()`.
</issue_to_address>
### Comment 2
<location path="astrbot/core/provider/sources/sensevoice_selfhosted_source.py" line_range="130" />
<code_context>
+ loop = asyncio.get_running_loop()
+ await loop.run_in_executor(
+ None,
+ lambda: snapshot_download("iic/SenseVoiceSmall", local_dir=self.model_path),
+ )
</code_context>
<issue_to_address>
**issue (bug_risk):** The configured `stt_model` is ignored: the provider stores it through `set_model(...)`, but initialization always downloads `iic/SenseVoiceSmall` and loads the fixed `self.model_path` instead of using `self.model_name`. Configuring another ModelScope model therefore still downloads and loads SenseVoiceSmall.
**Triggers:** When a provider configuration specifies an `stt_model` other than `iic/SenseVoiceSmall`.
**Suggested fix:** Use `self.model_name` for the ModelScope download and derive the local model directory consistently from the configured model.
</issue_to_address>
### Comment 3
<location path="astrbot/core/provider/sources/sensevoice_selfhosted_source.py" line_range="126-131" />
<code_context>
+ SenseVoiceSmall, _, snapshot_download = _load_sense_voice_modules()
+
+ # 模型下载(同步操作放入线程池)
+ if not os.path.exists(os.path.join(self.model_path, "configuration.json")):
+ loop = asyncio.get_running_loop()
+ await loop.run_in_executor(
+ None,
+ lambda: snapshot_download("iic/SenseVoiceSmall", local_dir=self.model_path),
+ )
- # 将模型加载放到线程池中执行
</code_context>
<issue_to_address>
**issue (bug_risk):** The existence of `configuration.json` is treated as proof that the model download completed. If a previous download was interrupted after creating that file, initialization skips `snapshot_download` and passes the incomplete directory to `SenseVoiceSmall`, causing model loading to fail instead of resuming or repairing the download.
**Triggers:** When the model directory contains `configuration.json` from a partial or corrupted download.
**Suggested fix:** Validate the complete expected model file set, or let `snapshot_download` resume/validate the existing local directory before loading.
```suggestion
loop = asyncio.get_running_loop()
await loop.run_in_executor(
None,
lambda: snapshot_download("iic/SenseVoiceSmall", local_dir=self.model_path),
)
```
</issue_to_address>Sourcery assessment
Needs a human reviewer. 3 findings to address first, and if the lazy installation or model-path logic is wrong, initialization can fail or install incompatible packages and leave downloaded model files or environment changes behind after a revert. These effects are bounded and repairable by removing the model/cache or restoring the dependency environment, but they are not undone automatically by reverting the code.
Blocking findings: astrbot/core/provider/sources/sensevoice_selfhosted_source.py:85, astrbot/core/provider/sources/sensevoice_selfhosted_source.py:130, astrbot/core/provider/sources/sensevoice_selfhosted_source.py:131
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| loop = asyncio.get_event_loop() | ||
| if loop.is_running(): | ||
| logger.warning("检测到运行中的事件循环,自动安装可能失败。建议手动安装依赖。") | ||
| asyncio.run(_install_dependencies()) |
There was a problem hiding this comment.
issue (bug_risk): asyncio.run(_install_dependencies()) raises RuntimeError whenever SenseVoice is initialized through the normal async provider lifecycle, because initialize() already runs inside an active event loop. The exception is caught and reported as dependency-installation failure, so automatic installation never works when dependencies are missing.
Triggers: When any SenseVoice dependency is not installed before provider initialization.
Suggested fix: Await _install_dependencies() directly from an async initialization path instead of calling asyncio.run().
| loop = asyncio.get_running_loop() | ||
| await loop.run_in_executor( | ||
| None, | ||
| lambda: snapshot_download("iic/SenseVoiceSmall", local_dir=self.model_path), |
There was a problem hiding this comment.
issue (bug_risk): The configured stt_model is ignored: the provider stores it through set_model(...), but initialization always downloads iic/SenseVoiceSmall and loads the fixed self.model_path instead of using self.model_name. Configuring another ModelScope model therefore still downloads and loads SenseVoiceSmall.
Triggers: When a provider configuration specifies an stt_model other than iic/SenseVoiceSmall.
Suggested fix: Use self.model_name for the ModelScope download and derive the local model directory consistently from the configured model.
| if not os.path.exists(os.path.join(self.model_path, "configuration.json")): | ||
| loop = asyncio.get_running_loop() | ||
| await loop.run_in_executor( | ||
| None, | ||
| lambda: snapshot_download("iic/SenseVoiceSmall", local_dir=self.model_path), | ||
| ) |
There was a problem hiding this comment.
issue (bug_risk): The existence of configuration.json is treated as proof that the model download completed. If a previous download was interrupted after creating that file, initialization skips snapshot_download and passes the incomplete directory to SenseVoiceSmall, causing model loading to fail instead of resuming or repairing the download.
Triggers: When the model directory contains configuration.json from a partial or corrupted download.
Suggested fix: Validate the complete expected model file set, or let snapshot_download resume/validate the existing local directory before loading.
| if not os.path.exists(os.path.join(self.model_path, "configuration.json")): | |
| loop = asyncio.get_running_loop() | |
| await loop.run_in_executor( | |
| None, | |
| lambda: snapshot_download("iic/SenseVoiceSmall", local_dir=self.model_path), | |
| ) | |
| loop = asyncio.get_running_loop() | |
| await loop.run_in_executor( | |
| None, | |
| lambda: snapshot_download("iic/SenseVoiceSmall", local_dir=self.model_path), | |
| ) |
修复了重复下载模型文件都问题,并更改了模型文件储存位置,便于移除
解决了缺失依赖项的问题,自动检查并通过PipInstaller安装
并且加入了
FFmpeg依赖缺失的检测和提示// 这些在旧版的guide里面有体现,但是新版删除了这些
Modifications / 改动点
astrbot/core/provider/source/sensevoice_selfhost_source.py
实现功能同上
Screenshots or Test Results / 运行截图或测试结果
能用了,新版模型加载如图
(图不知道为什么传不上来)
Checklist / 检查清单
😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
/ 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。
👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
/ 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。
🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in
requirements.txtandpyproject.toml./ 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到
requirements.txt和pyproject.toml文件相应位置。😮 My changes do not introduce malicious code.
/ 我的更改没有引入恶意代码。
Summary by Sourcery
Improve SenseVoice self-hosted STT setup by making dependency handling automatic and model storage persistent.
New Features:
Bug Fixes:
Enhancements: