Skip to content

fix: defer faiss imports during startup#7400

Open
Aster-amellus wants to merge 8 commits intoAstrBotDevs:masterfrom
Aster-amellus:master
Open

fix: defer faiss imports during startup#7400
Aster-amellus wants to merge 8 commits intoAstrBotDevs:masterfrom
Aster-amellus:master

Conversation

@Aster-amellus
Copy link
Copy Markdown

@Aster-amellus Aster-amellus commented Apr 6, 2026

Motivation / 动机

Fixes the startup crash described in #7343.

On macOS, enabling local Whisper(Local) could cause AstrBot to crash during startup because faiss was imported
eagerly through the knowledge base and dashboard import chain, even when the user was not actively using the knowledge
base.

This PR fixes the startup-time crash by deferring faiss imports to runtime use sites.

Modifications / 改动点

  • defer FaissVecDB imports from startup paths to runtime use sites
  • move type-only FaissVecDB imports behind TYPE_CHECKING
  • avoid loading faiss during AstrBot startup unless the knowledge base is actually used
  • add regression coverage for startup import behavior

Modified files:

  • astrbot/core/knowledge_base/kb_helper.py

  • astrbot/core/knowledge_base/kb_db_sqlite.py

  • astrbot/core/knowledge_base/retrieval/manager.py

  • astrbot/core/knowledge_base/retrieval/sparse_retriever.py

  • astrbot/dashboard/utils.py

  • tests/test_smoke.py

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

Screenshots or Test Results / 运行截图或测试结果

Validation performed locally on macOS(Macbook Air M4, 16G):

uv run python -c "import sys; import astrbot.core.core_lifecycle; print('faiss' in sys.modules)"
# False

uv run python -c "import sys; import astrbot.dashboard.server; print('faiss' in sys.modules)"
# False

uv run pytest tests/test_smoke.py -q
# passed

uv run python -X faulthandler main.py
# Whisper(Local) initializes successfully and AstrBot starts without the previous startup crash

Notes:

  • This PR fixes the startup-time crash.
  • It does not solve the separate runtime OpenMP conflict when Whisper(Local) and knowledge base retrieval are both
    actively used in the same process on macOS.

Screenshots or Test Results / 运行截图或测试结果

[22:47:07.286] [Core] [INFO] [provider.manager:548]: 载入 openai_whisper_selfhost(whisper_selfhost) 服务提供商 ...
[22:47:07.286] [Core] [INFO] [sources.whisper_selfhosted_source:53]: 下载或者加载 Whisper 模型中,这可能需要一些时间 ...
[22:47:08.547] [Core] [INFO] [sources.whisper_selfhosted_source:58]: Whisper 模型加载完成。device=mps
[22:47:08.547] [Core] [INFO] [provider.manager:603]: 已选择 openai_whisper_selfhost(whisper_selfhost) 作为当前语音转文本提供商适配器。
[22:47:08.547] [Core] [INFO] [provider.manager:702]: providers in user's config: ['whisper_selfhost', 'deepseek/deepseek-chat', 'openai_embedding']
[22:47:09.971] [Core] [INFO] [routes.config:743]: API call: /config/provider/check_one id=whisper_selfhost
[22:47:10.343] [Core] [INFO] [routes.config:706]: Provider whisper_selfhost (ID: whisper_selfhost) is available.

Startup validation 1
Startup validation 2


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • [ x ] 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • [ x ] 🤓 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 文件相应位置。

  • [ x ] 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Defer FAISS-related imports to runtime usage and add configurable device selection for the local Whisper provider, improving macOS startup stability and Whisper behavior.

Bug Fixes:

  • Avoid eager FAISS loading during core lifecycle and dashboard startup to prevent macOS crashes when Whisper(Local) is enabled.
  • Fix retrieval logic to no longer depend on concrete FaissVecDB types when determining rerank providers.

Enhancements:

  • Make the Whisper self-hosted provider honor a configurable inference device with automatic fallback from MPS to CPU and update default configuration and metadata accordingly.

Tests:

  • Add smoke test ensuring startup imports do not load FAISS eagerly in a fresh interpreter.
  • Add unit tests verifying Whisper self-hosted initialization uses MPS when available and falls back to CPU when MPS is unavailable.

Copilot AI review requested due to automatic review settings April 6, 2026 14:52
@auto-assign auto-assign bot requested review from Raven95676 and advent259141 April 6, 2026 14:52
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Apr 6, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Several modules now hide FaissVecDB imports behind TYPE_CHECKING but still use bare FaissVecDB annotations (e.g. in sparse_retriever and dashboard/utils), which will raise NameError at runtime unless from __future__ import annotations is enabled; either add the future import or quote these annotations for consistency with kb_helper/kb_db_sqlite.
  • In dashboard/utils.generate_tsne_visualization, consider keeping a local, runtime-only import of FaissVecDB (similar to _ensure_vec_db) if you want to retain concrete typing for tools while still avoiding eager faiss import, rather than relying purely on a TYPE_CHECKING-guarded import.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Several modules now hide `FaissVecDB` imports behind `TYPE_CHECKING` but still use bare `FaissVecDB` annotations (e.g. in `sparse_retriever` and `dashboard/utils`), which will raise `NameError` at runtime unless `from __future__ import annotations` is enabled; either add the future import or quote these annotations for consistency with `kb_helper`/`kb_db_sqlite`.
- In `dashboard/utils.generate_tsne_visualization`, consider keeping a local, runtime-only import of `FaissVecDB` (similar to `_ensure_vec_db`) if you want to retain concrete typing for tools while still avoiding eager faiss import, rather than relying purely on a `TYPE_CHECKING`-guarded import.

## Individual Comments

### Comment 1
<location path="astrbot/core/knowledge_base/retrieval/sparse_retriever.py" line_range="79" />
<code_context>
         chunks = []
         for kb_id in kb_ids:
-            vec_db: FaissVecDB = kb_options.get(kb_id, {}).get("vec_db")
+            vec_db: FaissVecDB | None = kb_options.get(kb_id, {}).get("vec_db")
             if not vec_db:
                 continue
</code_context>
<issue_to_address>
**issue (bug_risk):** FaissVecDB is only imported under TYPE_CHECKING, so this runtime type annotation will fail with NameError.

Because FaissVecDB only exists under TYPE_CHECKING, this annotation must not be evaluated at runtime. Please either quote the type (e.g. "FaissVecDB" | None), enable `from __future__ import annotations` in this module, or change the import so FaissVecDB is available at runtime.
</issue_to_address>

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.

chunks = []
for kb_id in kb_ids:
vec_db: FaissVecDB = kb_options.get(kb_id, {}).get("vec_db")
vec_db: FaissVecDB | None = kb_options.get(kb_id, {}).get("vec_db")
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): FaissVecDB is only imported under TYPE_CHECKING, so this runtime type annotation will fail with NameError.

Because FaissVecDB only exists under TYPE_CHECKING, this annotation must not be evaluated at runtime. Please either quote the type (e.g. "FaissVecDB" | None), enable from __future__ import annotations in this module, or change the import so FaissVecDB is available at runtime.

Copy link
Copy Markdown
Contributor

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

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 whisper_device configuration for local Whisper deployments, enabling support for CPU and MPS (Apple Silicon) with automatic fallback logic. Additionally, it refactors multiple modules to prevent the eager loading of the faiss library by moving imports into TYPE_CHECKING blocks or local scopes, ensuring a faster and more robust startup process. Feedback includes a suggestion to safely handle potential None values for vec_db in the retrieval manager to avoid attribute errors and a recommendation to further defer heavy imports like torch and whisper within the Whisper provider to prevent startup failures when dependencies are missing.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to prevent macOS startup crashes by ensuring faiss/FaissVecDB are not imported during AstrBot startup unless the knowledge base functionality is actually used.

Changes:

  • Deferred FaissVecDB imports to runtime use sites and moved type-only imports behind TYPE_CHECKING.
  • Added a smoke regression test to ensure startup imports don’t eagerly load faiss.
  • Extended Whisper(Local) configuration with a whisper_device option (cpu/mps) and updated related dashboard metadata/tests.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
astrbot/core/knowledge_base/kb_helper.py Lazily imports FaissVecDB inside _ensure_vec_db and updates annotations to avoid startup-time imports.
astrbot/core/knowledge_base/kb_db_sqlite.py Moves FaissVecDB import behind TYPE_CHECKING and uses string annotations to avoid eager loading.
astrbot/core/knowledge_base/retrieval/manager.py Avoids eager FaissVecDB import and adjusts rerank selection logic to rely on runtime attributes.
astrbot/core/knowledge_base/retrieval/sparse_retriever.py Moves FaissVecDB import behind TYPE_CHECKING to reduce startup import chain pressure.
astrbot/dashboard/utils.py Removes eager FaissVecDB import (type-only under TYPE_CHECKING) to prevent dashboard import chain from loading faiss.
astrbot/core/provider/sources/whisper_selfhosted_source.py Adds device resolution (whisper_device) and passes the resolved device to whisper.load_model.
astrbot/core/config/default.py Adds whisper_device default/config metadata for Whisper(Local).
dashboard/src/i18n/locales/zh-CN/features/config-metadata.json Adds UI metadata strings for whisper_device.
dashboard/src/i18n/locales/en-US/features/config-metadata.json Adds UI metadata strings for whisper_device.
dashboard/src/i18n/locales/ru-RU/features/config-metadata.json Adds UI metadata strings for whisper_device.
tests/test_smoke.py Adds regression check asserting faiss is not loaded after critical startup imports.
tests/test_whisper_selfhosted_source.py Adds unit tests covering Whisper(Local) device selection behavior.

Comment on lines 30 to 34
) -> None:
super().__init__(provider_config, provider_settings)
self.set_model(provider_config["model"])
self.device = str(provider_config.get("whisper_device", "cpu")).strip().lower()
self.model = None
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

The PR description is scoped to deferring faiss imports during startup, but this change set also introduces a new Whisper config field (whisper_device) and related behavior/logging (plus UI metadata updates). Please update the PR description to include this additional feature, or split it into a separate PR to keep the change focused and easier to review/revert independently.

Copilot uses AI. Check for mistakes.
@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Apr 6, 2026
Soulter and others added 4 commits April 7, 2026 01:18
@dosubot dosubot bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants