Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 34 additions & 23 deletions astrbot/core/provider/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,41 +384,52 @@ async def load_provider(self, provider_config: dict):
f"实例化 {provider_config['type']}({provider_config['id']}) 提供商适配器失败:{e}"
)

def _sync_current_provider(self, inst_list, curr_attr_name, provider_name):
"""同步当前提供商实例,确保其有效性并在必要时自动选择第一个可用实例"""
curr = getattr(self, curr_attr_name)
Copy link
Member

Choose a reason for hiding this comment

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

为什么不直接引用 self.curr_provider_inst 这些

# 如果当前实例无效(不在inst_map中),则置为None
if curr and curr.meta().id not in self.inst_map:
curr = None
# 如果当前没有实例但列表中有实例,则自动选择第一个
if curr is None:
if inst_list:
curr = inst_list[0]
logger.info(
f"自动选择 {curr.meta().id} 作为当前{provider_name}提供商适配器。"
)
else:
curr = None
setattr(self, curr_attr_name, curr)

async def reload(self, provider_config: dict):
await self.terminate_provider(provider_config["id"])
if provider_config["enable"]:
await self.load_provider(provider_config)

# 重新获取最新的配置,增加错误处理
Copy link
Member

Choose a reason for hiding this comment

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

self.providers_config 持有的是 AstrBotConfig 中 providers 的引用,如果 astrbot_config["providers"] 没有被直接覆盖,这里应该不会造成你说的没有更新的问题吧。而 reload 这个方法目前只被 ConfigRoute 中的 post_update_provider 引用,

    async def post_update_provider(self):
        update_provider_config = await request.json
        provider_id = update_provider_config.get("id", None)
        new_config = update_provider_config.get("config", None)
        if not provider_id or not new_config:
            return Response().error("参数错误").__dict__

        for i, provider in enumerate(self.config["provider"]):
            if provider["id"] == provider_id:
                self.config["provider"][i] = new_config
                break
        else:
            return Response().error("未找到对应服务提供商").__dict__

        try:
            save_config(self.config, self.config, is_core=True)
            await self.core_lifecycle.provider_manager.reload(new_config)
        except Exception as e:
            return Response().error(str(e)).__dict__
        return Response().ok(None, "更新成功,已经实时生效~").__dict__

是直接对 self.config["provider"] 列表对象修改的

Copy link
Contributor Author

@kawayiYokami kawayiYokami Sep 18, 2025

Choose a reason for hiding this comment

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

#2763
我是遇到了这个问题。
我也是一样的情况,我发现这样改就不会出现这个问题了。
至于原因我也没有很深入地研究。

根据我log输出的结果是,增减之后他的列表provider仍然是初始化的列表,而不是修改后的provider。

更改provider信息的时候,provider实例本身是马上更新了,但是provider列表没有更新。导致找不到这个provider的id。

项目太庞大了,我没有很认真的研究,我是局限于我认识空间想办法解决,大佬觉得最佳的更新方法是什么?应该有一行代码立刻更新这个列表的方法,但是我没找到准确位置。

Copy link
Contributor Author

Choose a reason for hiding this comment

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

self.providers_config 持有的是 AstrBotConfig 中 providers 的引用,如果 astrbot_config["providers"] 没有被直接覆盖,这里应该不会造成你说的没有更新的问题吧。而 reload 这个方法目前只被 ConfigRoute 中的 post_update_provider 引用,

    async def post_update_provider(self):
        update_provider_config = await request.json
        provider_id = update_provider_config.get("id", None)
        new_config = update_provider_config.get("config", None)
        if not provider_id or not new_config:
            return Response().error("参数错误").__dict__

        for i, provider in enumerate(self.config["provider"]):
            if provider["id"] == provider_id:
                self.config["provider"][i] = new_config
                break
        else:
            return Response().error("未找到对应服务提供商").__dict__

        try:
            save_config(self.config, self.config, is_core=True)
            await self.core_lifecycle.provider_manager.reload(new_config)
        except Exception as e:
            return Response().error(str(e)).__dict__
        return Response().ok(None, "更新成功,已经实时生效~").__dict__

是直接对 self.config["provider"] 列表对象修改的

经过我的多重分析。

提供商有3个方法。
新增,删除,更新。

但是新增的时候,如果不合法(比如没有api key),他会添加一个不存在于inst_mapd的提供商。
而更新提供商的时候,却不会重新检查合法性。
结果就是新增了一个永远都不会可能再次被加入inst_mapd的提供商,因为唯一进入提供商的方法只有新增,但是新增却可能新增一个不在inst_mapd的提供商。

我这个补丁是在更新方法里,每次更新的时候强制加入所有提供商,只是一个亡羊补牢。
你原先的设计目的在于不让无效的提供商加入inst_mapd。
但是我觉得这个毫无意义,因为哪怕有API KEY,也可能是无效的。
有没有效果不应该在设置提供商的时候给,因为新增的时候有效,后面也可能有效。
我觉得最简单的方法是新增的时候不应该检查任何的合法性,直接加入inst_mapd。

这个交由您来判断,这个pr作废。

但是这个涉及到了设计逻辑,我认为这个需要大佬您亲自决定应该如何修改。

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

#2763 我是遇到了这个问题。 我也是一样的情况,我发现这样改就不会出现这个问题了。 至于原因我也没有很深入地研究。

根据我log输出的结果是,增减之后他的列表provider仍然是初始化的列表,而不是修改后的provider。

更改provider信息的时候,provider实例本身是马上更新了,但是provider列表没有更新。导致找不到这个provider的id。

项目太庞大了,我没有很认真的研究,我是局限于我认识空间想办法解决,大佬觉得最佳的更新方法是什么?应该有一行代码立刻更新这个列表的方法,但是我没找到准确位置。

其实我也一直有遇到过这个问题,包括社区群里面也有不少人反馈这个问题,但是由于不是必现所以我一直没排查出来。

try:
latest_config = self.acm.get_conf("default")
self.providers_config = latest_config.get("provider", [])
except Exception as e:
logger.error(f"获取最新配置时出错: {e}")
# 使用空列表作为后备方案
self.providers_config = []

# 和配置文件保持同步
config_ids = [provider["id"] for provider in self.providers_config]
logger.debug(f"providers in user's config: {config_ids}")
for key in list(self.inst_map.keys()):
if key not in config_ids:
await self.terminate_provider(key)

if len(self.provider_insts) == 0:
self.curr_provider_inst = None
elif self.curr_provider_inst is None and len(self.provider_insts) > 0:
self.curr_provider_inst = self.provider_insts[0]
logger.info(
f"自动选择 {self.curr_provider_inst.meta().id} 作为当前提供商适配器。"
)

if len(self.stt_provider_insts) == 0:
self.curr_stt_provider_inst = None
elif self.curr_stt_provider_inst is None and len(self.stt_provider_insts) > 0:
self.curr_stt_provider_inst = self.stt_provider_insts[0]
logger.info(
f"自动选择 {self.curr_stt_provider_inst.meta().id} 作为当前语音转文本提供商适配器。"
)

if len(self.tts_provider_insts) == 0:
self.curr_tts_provider_inst = None
elif self.curr_tts_provider_inst is None and len(self.tts_provider_insts) > 0:
self.curr_tts_provider_inst = self.tts_provider_insts[0]
logger.info(
f"自动选择 {self.curr_tts_provider_inst.meta().id} 作为当前文本转语音提供商适配器。"
)
# 使用辅助函数同步各个提供商实例
self._sync_current_provider(self.provider_insts, "curr_provider_inst", "提供商")
self._sync_current_provider(
self.stt_provider_insts, "curr_stt_provider_inst", "语音转文本"
)
self._sync_current_provider(
self.tts_provider_insts, "curr_tts_provider_inst", "文本转语音"
)

def get_insts(self):
return self.provider_insts
Expand Down