Skip to content

Conversation

@DITF16
Copy link

@DITF16 DITF16 commented Feb 8, 2026

修复高并发场景下异步数据库连接池跨 event loop 调用导致的 RuntimeError。
问题现象:
当消息处理并发量较高时,日志出现以下报错:
RuntimeError: <Queue at 0x7ffebb54fc90 maxsize=5 tasks=14384> is bound to a different event loop
问题根源:
SharedPreferences.get() 同步方法内部使用 asyncio.run_coroutine_threadsafe() 将异步数据库查询提交到一个独立的 event loop(_sync_loop),而 SQLAlchemy 的 AsyncSession 连接池是绑定在主 event loop 上的,跨 loop 操作导致连接池队列冲突。
触发链路:
ProviderManager.get_using_provider()sp.get()_sync_loop 中执行 get_async() → 数据库连接池报错
修复方案:
get_using_provider() 及相关调用链改为 async/await 模式,直接使用 sp.get_async() 在主 event loop 中执行异步数据库操作,避免跨 loop 调用。

Modifications / 改动点

修改的核心文件:

文件 修改内容
astrbot/core/provider/manager.py get_using_provider() 改为 async,使用 await sp.get_async()
astrbot/core/star/context.py get_using_provider()get_current_chat_provider_id() 等方法改为 async
astrbot/core/astr_main_agent.py _select_provider() 改为 async,修复相关调用添加 await
astrbot/core/pipeline/preprocess_stage/stage.py get_using_stt_provider() 调用添加 await
astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py 修复 async 调用链
astrbot/core/pipeline/result_decorate/stage.py 修复 async 调用链
astrbot/builtin_stars/astrbot/long_term_memory.py 修复 async 调用链
astrbot/builtin_stars/astrbot/main.py 修复 async 调用链
astrbot/builtin_stars/builtin_commands/commands/conversation.py 修复 async 调用链
astrbot/builtin_stars/builtin_commands/commands/provider.py 修复 async 调用链

实现效果:
get_using_provider()get_using_stt_provider() 等 Provider 获取逻辑统一改为 async/await 模式,并修复整条调用链上所有相关方法,确保所有数据库操作都在主 event loop 中执行,消除跨 loop 调用导致的连接池冲突问题。

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

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


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ 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.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

将 provider 选择逻辑转换为 async/await,以确保基于数据库的 provider 首选项在主事件循环中访问,避免跨事件循环导致的运行时错误。

Bug Fixes:

  • 修复在解析当前激活的 provider 时跨事件循环访问异步数据库驱动的 SharedPreferences 的问题,防止来自 SQLAlchemy 异步连接池的 RuntimeError

Enhancements:

  • 使上下文、provider 管理器以及主 agent 中的 provider 选择 API 完全异步化,并在各个 pipeline、内置 stars 和命令中更新调用点以对其执行 await,从而实现一致的异步行为。
Original summary in English

Summary by Sourcery

Convert provider selection to async/await to ensure database-backed provider preferences are accessed within the main event loop and avoid cross-loop runtime errors.

Bug Fixes:

  • Fix cross event loop access to async database-backed SharedPreferences when resolving the active provider, preventing RuntimeError from the SQLAlchemy async connection pool.

Enhancements:

  • Make provider selection APIs in context, provider manager, and main agent fully async and update call sites across pipelines, builtin stars, and commands to await them for consistent asynchronous behavior.

@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Feb 8, 2026
@dosubot
Copy link

dosubot bot commented Feb 8, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

Copy link
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 - 我发现了 1 个问题,并给出了一些整体反馈:

  • Context.get_using_provider/get_using_tts_provider/get_using_stt_providerProviderManager.get_using_provider 从同步改为异步会改变它们的公共 API;如果这些方法属于对外 / star / 插件接口的一部分,你可能需要保留一个向后兼容的同步封装,或者明确将其标记为不兼容变更(breaking change)。
  • ProviderManager.get_using_provider 中,await sp.get_async(...) 调用的关键字参数缩进方式和文件其余部分不一致;将其调整为项目中常用的多行调用风格会提升可读性。
  • 在若干位置,现在在循环中对 get_using_*_provider 调用了 await(例如在 builtin_commands/commands/provider.py 中构建 provider 列表时);由于在一次调用过程中选中的 provider 并不会在循环中改变,建议在循环开始前获取一次并复用,以避免重复的异步调用。
给 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- Changing `Context.get_using_provider`/`get_using_tts_provider`/`get_using_stt_provider` and `ProviderManager.get_using_provider` from sync to async alters their public API; if these are part of the external/star/plugin surface you may want to either keep a backward-compatible sync wrapper or explicitly mark this as a breaking change.
- In `ProviderManager.get_using_provider`, the `await sp.get_async(...)` call has its keyword arguments oddly indented compared to the rest of the file; aligning these to the project’s usual multi-line call style will improve readability.
- Several places now `await` `get_using_*_provider` inside loops (e.g., when building provider lists in `builtin_commands/commands/provider.py); since the selected provider does not change within the loop, consider fetching it once before the loop to avoid repeated async calls.

## Individual Comments

### Comment 1
<location> `astrbot/builtin_stars/builtin_commands/commands/provider.py:141` </location>
<code_context>
             for i, d in enumerate(llm_data):
                 line = f"{i + 1}. {d['info']}{d['mark']}"
-                provider_using = self.context.get_using_provider(umo=umo)
+                provider_using = await self.context.get_using_provider(umo=umo)
                 if (
                     provider_using
</code_context>

<issue_to_address>
**suggestion (performance):** Avoid repeatedly awaiting `get_using_provider` inside the loop; fetch once before iterating.

Within the loop, `await self.context.get_using_provider(umo=umo)` is invoked on every iteration even though the provider for a given `umo` is constant for the duration of this call. Move it outside the loop and reuse the result:

```python
provider_using = await self.context.get_using_provider(umo=umo)
for i, d in enumerate(llm_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if provider_using and provider_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")
```

You can apply the same pattern to the TTS and STT sections for `get_using_tts_provider` and `get_using_stt_provider` as well.

Suggested implementation:

```python
            llm_data = [d for d in display_data if d["type"] == "llm"]
            provider_using = await self.context.get_using_provider(umo=umo)
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                ):
                    line += " (当前使用)"
                parts.append(line + "\n")

            parts.append("\n## 载入的 TTS 提供商\n")
            tts_using = await self.context.get_using_tts_provider(umo=umo)
            for i, d in enumerate(tts_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if tts_using and tts_using.meta().id == d["provider"].meta().id:
                    line += " (当前使用)"
                parts.append(line + "\n")
            parts.append("\n## 载入的 STT 提供商\n")

```

` section.

Here are the code changes:

<file_operations>
<file_operation operation="edit" file_path="astrbot/builtin_stars/builtin_commands/commands/provider.py">
<<<<<<< SEARCH
            llm_data = [d for d in display_data if d["type"] == "llm"]
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                provider_using = await self.context.get_using_provider(umo=umo)
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                parts.append("\n## 载入的 TTS 提供商\n")
                for i, d in enumerate(tts_data):
                    line = f"{i + 1}. {d['info']}{d['mark']}"
                    tts_using =  await self.context.get_using_tts_provider(umo=umo)
                    if tts_using and tts_using.meta().id == d["provider"].meta().id:
                        line += " (当前使用)"
                    parts.append(line + "\n")
                parts.append("\n## 载入的 STT 提供商\n")
=======
            llm_data = [d for d in display_data if d["type"] == "llm"]
            provider_using = await self.context.get_using_provider(umo=umo)
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                ):
                    line += " (当前使用)"
                parts.append(line + "\n")

            parts.append("\n## 载入的 TTS 提供商\n")
            tts_using = await self.context.get_using_tts_provider(umo=umo)
            for i, d in enumerate(tts_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if tts_using and tts_using.meta().id == d["provider"].meta().id:
                    line += " (当前使用)"
                parts.append(line + "\n")
            parts.append("\n## 载入的 STT 提供商\n")
>>>>>>> REPLACE
</file_operation>
</file_operations>

<additional_changes>
To fully apply your suggestion for STT as well, locate the STT loop below the `parts.append("\n## 载入的 STT 提供商\n")` line. It will currently look analogous to the original TTS section, i.e. repeatedly calling `await self.context.get_using_stt_provider(umo=umo)` inside the loop. Refactor it to:

1. Add `stt_using = await self.context.get_using_stt_provider(umo=umo)` **before** the `for i, d in enumerate(stt_data):` loop.
2. Remove the `await self.context.get_using_stt_provider(umo=umo)` call from inside the loop.
3. Use `stt_using` in the `if` condition inside the loop, similar to the updated TTS block:

```python
stt_using = await self.context.get_using_stt_provider(umo=umo)
for i, d in enumerate(stt_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if stt_using and stt_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")
```

This will ensure `get_using_stt_provider` is also awaited only once per call.
</issue_to_address>

Sourcery 对开源项目免费使用——如果你喜欢我们的代码审查,请考虑帮忙分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

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

  • Changing Context.get_using_provider/get_using_tts_provider/get_using_stt_provider and ProviderManager.get_using_provider from sync to async alters their public API; if these are part of the external/star/plugin surface you may want to either keep a backward-compatible sync wrapper or explicitly mark this as a breaking change.
  • In ProviderManager.get_using_provider, the await sp.get_async(...) call has its keyword arguments oddly indented compared to the rest of the file; aligning these to the project’s usual multi-line call style will improve readability.
  • Several places now await get_using_*_provider inside loops (e.g., when building provider lists in `builtin_commands/commands/provider.py); since the selected provider does not change within the loop, consider fetching it once before the loop to avoid repeated async calls.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Changing `Context.get_using_provider`/`get_using_tts_provider`/`get_using_stt_provider` and `ProviderManager.get_using_provider` from sync to async alters their public API; if these are part of the external/star/plugin surface you may want to either keep a backward-compatible sync wrapper or explicitly mark this as a breaking change.
- In `ProviderManager.get_using_provider`, the `await sp.get_async(...)` call has its keyword arguments oddly indented compared to the rest of the file; aligning these to the project’s usual multi-line call style will improve readability.
- Several places now `await` `get_using_*_provider` inside loops (e.g., when building provider lists in `builtin_commands/commands/provider.py); since the selected provider does not change within the loop, consider fetching it once before the loop to avoid repeated async calls.

## Individual Comments

### Comment 1
<location> `astrbot/builtin_stars/builtin_commands/commands/provider.py:141` </location>
<code_context>
             for i, d in enumerate(llm_data):
                 line = f"{i + 1}. {d['info']}{d['mark']}"
-                provider_using = self.context.get_using_provider(umo=umo)
+                provider_using = await self.context.get_using_provider(umo=umo)
                 if (
                     provider_using
</code_context>

<issue_to_address>
**suggestion (performance):** Avoid repeatedly awaiting `get_using_provider` inside the loop; fetch once before iterating.

Within the loop, `await self.context.get_using_provider(umo=umo)` is invoked on every iteration even though the provider for a given `umo` is constant for the duration of this call. Move it outside the loop and reuse the result:

```python
provider_using = await self.context.get_using_provider(umo=umo)
for i, d in enumerate(llm_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if provider_using and provider_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")
```

You can apply the same pattern to the TTS and STT sections for `get_using_tts_provider` and `get_using_stt_provider` as well.

Suggested implementation:

```python
            llm_data = [d for d in display_data if d["type"] == "llm"]
            provider_using = await self.context.get_using_provider(umo=umo)
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                ):
                    line += " (当前使用)"
                parts.append(line + "\n")

            parts.append("\n## 载入的 TTS 提供商\n")
            tts_using = await self.context.get_using_tts_provider(umo=umo)
            for i, d in enumerate(tts_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if tts_using and tts_using.meta().id == d["provider"].meta().id:
                    line += " (当前使用)"
                parts.append(line + "\n")
            parts.append("\n## 载入的 STT 提供商\n")

```

` section.

Here are the code changes:

<file_operations>
<file_operation operation="edit" file_path="astrbot/builtin_stars/builtin_commands/commands/provider.py">
<<<<<<< SEARCH
            llm_data = [d for d in display_data if d["type"] == "llm"]
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                provider_using = await self.context.get_using_provider(umo=umo)
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                parts.append("\n## 载入的 TTS 提供商\n")
                for i, d in enumerate(tts_data):
                    line = f"{i + 1}. {d['info']}{d['mark']}"
                    tts_using =  await self.context.get_using_tts_provider(umo=umo)
                    if tts_using and tts_using.meta().id == d["provider"].meta().id:
                        line += " (当前使用)"
                    parts.append(line + "\n")
                parts.append("\n## 载入的 STT 提供商\n")
=======
            llm_data = [d for d in display_data if d["type"] == "llm"]
            provider_using = await self.context.get_using_provider(umo=umo)
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                ):
                    line += " (当前使用)"
                parts.append(line + "\n")

            parts.append("\n## 载入的 TTS 提供商\n")
            tts_using = await self.context.get_using_tts_provider(umo=umo)
            for i, d in enumerate(tts_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if tts_using and tts_using.meta().id == d["provider"].meta().id:
                    line += " (当前使用)"
                parts.append(line + "\n")
            parts.append("\n## 载入的 STT 提供商\n")
>>>>>>> REPLACE
</file_operation>
</file_operations>

<additional_changes>
To fully apply your suggestion for STT as well, locate the STT loop below the `parts.append("\n## 载入的 STT 提供商\n")` line. It will currently look analogous to the original TTS section, i.e. repeatedly calling `await self.context.get_using_stt_provider(umo=umo)` inside the loop. Refactor it to:

1. Add `stt_using = await self.context.get_using_stt_provider(umo=umo)` **before** the `for i, d in enumerate(stt_data):` loop.
2. Remove the `await self.context.get_using_stt_provider(umo=umo)` call from inside the loop.
3. Use `stt_using` in the `if` condition inside the loop, similar to the updated TTS block:

```python
stt_using = await self.context.get_using_stt_provider(umo=umo)
for i, d in enumerate(stt_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if stt_using and stt_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")
```

This will ensure `get_using_stt_provider` is also awaited only once per call.
</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.

for i, d in enumerate(llm_data):
line = f"{i + 1}. {d['info']}{d['mark']}"
provider_using = self.context.get_using_provider(umo=umo)
provider_using = await self.context.get_using_provider(umo=umo)
Copy link
Contributor

Choose a reason for hiding this comment

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

建议(性能): 避免在循环内部重复地对 get_using_provider 调用 await;请在进入循环前获取一次并在循环中复用。

在当前实现中,await self.context.get_using_provider(umo=umo) 会在循环的每次迭代中被调用一次,即使某个给定 umo 对应的 provider 在这次调用期间是固定不变的。可以将这行代码移到循环外部,并复用结果:

provider_using = await self.context.get_using_provider(umo=umo)
for i, d in enumerate(llm_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if provider_using and provider_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")

同样的模式也可以应用在 TTS 和 STT 的代码中,即 get_using_tts_providerget_using_stt_provider

建议的实现方式:

            llm_data = [d for d in display_data if d["type"] == "llm"]
            provider_using = await self.context.get_using_provider(umo=umo)
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                ):
                    line += " (当前使用)"
                parts.append(line + "\n")

            parts.append("\n## 载入的 TTS 提供商\n")
            tts_using = await self.context.get_using_tts_provider(umo=umo)
            for i, d in enumerate(tts_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if tts_using and tts_using.meta().id == d["provider"].meta().id:
                    line += " (当前使用)"
                parts.append(line + "\n")
            parts.append("\n## 载入的 STT 提供商\n")

` section.

以下是代码变更:

<file_operations>
<file_operation operation="edit" file_path="astrbot/builtin_stars/builtin_commands/commands/provider.py">
<<<<<<< SEARCH
llm_data = [d for d in display_data if d["type"] == "llm"]
for i, d in enumerate(llm_data):
line = f"{i + 1}. {d['info']}{d['mark']}"
provider_using = await self.context.get_using_provider(umo=umo)
if (
provider_using
and provider_using.meta().id == d["provider"].meta().id
parts.append("\n## 载入的 TTS 提供商\n")
for i, d in enumerate(tts_data):
line = f"{i + 1}. {d['info']}{d['mark']}"
tts_using = await self.context.get_using_tts_provider(umo=umo)
if tts_using and tts_using.meta().id == d["provider"].meta().id:
line += " (当前使用)"
parts.append(line + "\n")
parts.append("\n## 载入的 STT 提供商\n")

        llm_data = [d for d in display_data if d["type"] == "llm"]
        provider_using = await self.context.get_using_provider(umo=umo)
        for i, d in enumerate(llm_data):
            line = f"{i + 1}. {d['info']}{d['mark']}"
            if (
                provider_using
                and provider_using.meta().id == d["provider"].meta().id
            ):
                line += " (当前使用)"
            parts.append(line + "\n")

        parts.append("\n## 载入的 TTS 提供商\n")
        tts_using = await self.context.get_using_tts_provider(umo=umo)
        for i, d in enumerate(tts_data):
            line = f"{i + 1}. {d['info']}{d['mark']}"
            if tts_using and tts_using.meta().id == d["provider"].meta().id:
                line += " (当前使用)"
            parts.append(line + "\n")
        parts.append("\n## 载入的 STT 提供商\n")

REPLACE
</file_operation>
</file_operations>

<additional_changes>
要把对 STT 的优化也完整应用上去,请找到位于 parts.append("\n## 载入的 STT 提供商\n") 这一行之后的 STT 循环。它现在的写法会和原来的 TTS 代码类似,即在循环内部重复调用 await self.context.get_using_stt_provider(umo=umo)。可以按下面的方式重构:

  1. for i, d in enumerate(stt_data): 循环之前新增 stt_using = await self.context.get_using_stt_provider(umo=umo)
  2. 从循环内部删除 await self.context.get_using_stt_provider(umo=umo) 调用。
  3. 在循环内部的 if 判断中使用 stt_using,写法与更新后的 TTS 块类似:
stt_using = await self.context.get_using_stt_provider(umo=umo)
for i, d in enumerate(stt_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if stt_using and stt_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")

这样可以确保 get_using_stt_provider 在一次调用流程中同样只会被 await 一次。

Original comment in English

suggestion (performance): Avoid repeatedly awaiting get_using_provider inside the loop; fetch once before iterating.

Within the loop, await self.context.get_using_provider(umo=umo) is invoked on every iteration even though the provider for a given umo is constant for the duration of this call. Move it outside the loop and reuse the result:

provider_using = await self.context.get_using_provider(umo=umo)
for i, d in enumerate(llm_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if provider_using and provider_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")

You can apply the same pattern to the TTS and STT sections for get_using_tts_provider and get_using_stt_provider as well.

Suggested implementation:

            llm_data = [d for d in display_data if d["type"] == "llm"]
            provider_using = await self.context.get_using_provider(umo=umo)
            for i, d in enumerate(llm_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if (
                    provider_using
                    and provider_using.meta().id == d["provider"].meta().id
                ):
                    line += " (当前使用)"
                parts.append(line + "\n")

            parts.append("\n## 载入的 TTS 提供商\n")
            tts_using = await self.context.get_using_tts_provider(umo=umo)
            for i, d in enumerate(tts_data):
                line = f"{i + 1}. {d['info']}{d['mark']}"
                if tts_using and tts_using.meta().id == d["provider"].meta().id:
                    line += " (当前使用)"
                parts.append(line + "\n")
            parts.append("\n## 载入的 STT 提供商\n")

` section.

Here are the code changes:

<file_operations>
<file_operation operation="edit" file_path="astrbot/builtin_stars/builtin_commands/commands/provider.py">
<<<<<<< SEARCH
llm_data = [d for d in display_data if d["type"] == "llm"]
for i, d in enumerate(llm_data):
line = f"{i + 1}. {d['info']}{d['mark']}"
provider_using = await self.context.get_using_provider(umo=umo)
if (
provider_using
and provider_using.meta().id == d["provider"].meta().id
parts.append("\n## 载入的 TTS 提供商\n")
for i, d in enumerate(tts_data):
line = f"{i + 1}. {d['info']}{d['mark']}"
tts_using = await self.context.get_using_tts_provider(umo=umo)
if tts_using and tts_using.meta().id == d["provider"].meta().id:
line += " (当前使用)"
parts.append(line + "\n")
parts.append("\n## 载入的 STT 提供商\n")

        llm_data = [d for d in display_data if d["type"] == "llm"]
        provider_using = await self.context.get_using_provider(umo=umo)
        for i, d in enumerate(llm_data):
            line = f"{i + 1}. {d['info']}{d['mark']}"
            if (
                provider_using
                and provider_using.meta().id == d["provider"].meta().id
            ):
                line += " (当前使用)"
            parts.append(line + "\n")

        parts.append("\n## 载入的 TTS 提供商\n")
        tts_using = await self.context.get_using_tts_provider(umo=umo)
        for i, d in enumerate(tts_data):
            line = f"{i + 1}. {d['info']}{d['mark']}"
            if tts_using and tts_using.meta().id == d["provider"].meta().id:
                line += " (当前使用)"
            parts.append(line + "\n")
        parts.append("\n## 载入的 STT 提供商\n")

REPLACE
</file_operation>
</file_operations>

<additional_changes>
To fully apply your suggestion for STT as well, locate the STT loop below the parts.append("\n## 载入的 STT 提供商\n") line. It will currently look analogous to the original TTS section, i.e. repeatedly calling await self.context.get_using_stt_provider(umo=umo) inside the loop. Refactor it to:

  1. Add stt_using = await self.context.get_using_stt_provider(umo=umo) before the for i, d in enumerate(stt_data): loop.
  2. Remove the await self.context.get_using_stt_provider(umo=umo) call from inside the loop.
  3. Use stt_using in the if condition inside the loop, similar to the updated TTS block:
stt_using = await self.context.get_using_stt_provider(umo=umo)
for i, d in enumerate(stt_data):
    line = f"{i + 1}. {d['info']}{d['mark']}"
    if stt_using and stt_using.meta().id == d["provider"].meta().id:
        line += " (当前使用)"
    parts.append(line + "\n")

This will ensure get_using_stt_provider is also awaited only once per call.

@dosubot dosubot bot added the area:core The bug / feature is about astrbot's core, backend label Feb 8, 2026
@Soulter Soulter force-pushed the master branch 2 times, most recently from 9a60ec1 to e1b7154 Compare February 8, 2026 16:19
return self.provider_manager.embedding_provider_insts

def get_using_provider(self, umo: str | None = None) -> Provider | None:
async def get_using_provider(self, umo: str | None = None) -> Provider | None:
Copy link
Member

Choose a reason for hiding this comment

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

保持同步方法的原因是为了向后兼容。直接将这个改为异步方法会导致插件不兼容。

建议新建 get_using_provider_async 方法去处理。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants