Skip to content

[fix] 修复了sensevoice STT重复下载模型文件和缺失依赖项的问题 - #9897

Open
xiewoc wants to merge 1 commit into
AstrBotDevs:masterfrom
xiewoc:master
Open

[fix] 修复了sensevoice STT重复下载模型文件和缺失依赖项的问题#9897
xiewoc wants to merge 1 commit into
AstrBotDevs:masterfrom
xiewoc:master

Conversation

@xiewoc

@xiewoc xiewoc commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

修复了重复下载模型文件都问题,并更改了模型文件储存位置,便于移除

解决了缺失依赖项的问题,自动检查并通过PipInstaller安装

并且加入了FFmpeg依赖缺失的检测和提示

// 这些在旧版的guide里面有体现,但是新版删除了这些

Modifications / 改动点

astrbot/core/provider/source/sensevoice_selfhost_source.py

实现功能同上

  • This is NOT a breaking change. / 这不是一个破坏性变更。

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.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.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:

  • Add automatic detection and installation of missing SenseVoice Python dependencies.
  • Add FFmpeg availability checks with actionable error messages before model initialization.

Bug Fixes:

  • Prevent repeated SenseVoice model downloads by persisting and reusing the model in the AstrBot data directory.
  • Fix SenseVoice loading and inference when optional dependencies are unavailable at module import time.

Enhancements:

  • Load SenseVoice integrations lazily and reuse cached modules during provider operations.
  • Improve model initialization, inference validation, and error reporting.

@sourcery-ai sourcery-ai 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.

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


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
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())

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.

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),

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.

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.

Comment on lines +126 to +131
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),
)

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.

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.

Suggested change
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),
)

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