-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix: 修复inst_map未互斥访问导致的重载状态不一致, 增加前端检测启用逻辑 #2817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
9ff8570
27f7efd
861642f
b174c53
4434506
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -40,6 +40,8 @@ def __init__( | |||||||||||
| """加载的 Embedding Provider 的实例""" | ||||||||||||
| self.inst_map: dict[str, Provider] = {} | ||||||||||||
| """Provider 实例映射. key: provider_id, value: Provider 实例""" | ||||||||||||
| self._inst_map_lock = asyncio.Lock() | ||||||||||||
| """inst_map 操作的互斥锁""" | ||||||||||||
| self.llm_tools = llm_tools | ||||||||||||
|
|
||||||||||||
| self.curr_provider_inst: Provider | None = None | ||||||||||||
|
|
@@ -77,17 +79,20 @@ async def set_provider( | |||||||||||
|
|
||||||||||||
| Version 4.0.0: 这个版本下已经默认隔离提供商 | ||||||||||||
| """ | ||||||||||||
| if provider_id not in self.inst_map: | ||||||||||||
| raise ValueError(f"提供商 {provider_id} 不存在,无法设置。") | ||||||||||||
| async with self._inst_map_lock: | ||||||||||||
| if provider_id not in self.inst_map: | ||||||||||||
| raise ValueError(f"提供商 {provider_id} 不存在,无法设置。") | ||||||||||||
| if not umo: | ||||||||||||
| # 不启用提供商会话隔离模式的情况 | ||||||||||||
| self.curr_provider_inst = self.inst_map[provider_id] | ||||||||||||
|
|
||||||||||||
| if umo: | ||||||||||||
| await sp.session_put( | ||||||||||||
| umo, | ||||||||||||
| f"provider_perf_{provider_type.value}", | ||||||||||||
| provider_id, | ||||||||||||
| ) | ||||||||||||
| return | ||||||||||||
| # 不启用提供商会话隔离模式的情况 | ||||||||||||
| self.curr_provider_inst = self.inst_map[provider_id] | ||||||||||||
| if provider_type == ProviderType.TEXT_TO_SPEECH: | ||||||||||||
| sp.put("curr_provider_tts", provider_id, scope="global", scope_id="global") | ||||||||||||
| elif provider_type == ProviderType.SPEECH_TO_TEXT: | ||||||||||||
|
|
@@ -97,7 +102,8 @@ async def set_provider( | |||||||||||
|
|
||||||||||||
| async def get_provider_by_id(self, provider_id: str) -> Provider | None: | ||||||||||||
| """根据提供商 ID 获取提供商实例""" | ||||||||||||
| return self.inst_map.get(provider_id) | ||||||||||||
| async with self._inst_map_lock: | ||||||||||||
| return self.inst_map.get(provider_id) | ||||||||||||
|
|
||||||||||||
| def get_using_provider(self, provider_type: ProviderType, umo=None): | ||||||||||||
| """获取正在使用的提供商实例。 | ||||||||||||
|
|
@@ -173,17 +179,18 @@ async def initialize(self): | |||||||||||
| scope="global", | ||||||||||||
| scope_id="global", | ||||||||||||
| ) | ||||||||||||
| self.curr_provider_inst = self.inst_map.get(selected_provider_id) | ||||||||||||
| if not self.curr_provider_inst and self.provider_insts: | ||||||||||||
| self.curr_provider_inst = self.provider_insts[0] | ||||||||||||
| async with self._inst_map_lock: | ||||||||||||
| self.curr_provider_inst = self.inst_map.get(selected_provider_id) | ||||||||||||
| if not self.curr_provider_inst and self.provider_insts: | ||||||||||||
| self.curr_provider_inst = self.provider_insts[0] | ||||||||||||
|
|
||||||||||||
| self.curr_stt_provider_inst = self.inst_map.get(selected_stt_provider_id) | ||||||||||||
| if not self.curr_stt_provider_inst and self.stt_provider_insts: | ||||||||||||
| self.curr_stt_provider_inst = self.stt_provider_insts[0] | ||||||||||||
| self.curr_stt_provider_inst = self.inst_map.get(selected_stt_provider_id) | ||||||||||||
| if not self.curr_stt_provider_inst and self.stt_provider_insts: | ||||||||||||
| self.curr_stt_provider_inst = self.stt_provider_insts[0] | ||||||||||||
|
|
||||||||||||
| self.curr_tts_provider_inst = self.inst_map.get(selected_tts_provider_id) | ||||||||||||
| if not self.curr_tts_provider_inst and self.tts_provider_insts: | ||||||||||||
| self.curr_tts_provider_inst = self.tts_provider_insts[0] | ||||||||||||
| self.curr_tts_provider_inst = self.inst_map.get(selected_tts_provider_id) | ||||||||||||
| if not self.curr_tts_provider_inst and self.tts_provider_insts: | ||||||||||||
| self.curr_tts_provider_inst = self.tts_provider_insts[0] | ||||||||||||
|
|
||||||||||||
| # 初始化 MCP Client 连接 | ||||||||||||
| asyncio.create_task(self.llm_tools.init_mcp_clients(), name="init_mcp_clients") | ||||||||||||
|
|
@@ -376,25 +383,94 @@ async def load_provider(self, provider_config: dict): | |||||||||||
| if getattr(inst, "initialize", None): | ||||||||||||
| await inst.initialize() | ||||||||||||
| self.embedding_provider_insts.append(inst) | ||||||||||||
|
|
||||||||||||
| self.inst_map[provider_config["id"]] = inst | ||||||||||||
| async with self._inst_map_lock: | ||||||||||||
| self.inst_map[provider_config["id"]] = inst | ||||||||||||
| except Exception as e: | ||||||||||||
| logger.error(traceback.format_exc()) | ||||||||||||
| logger.error( | ||||||||||||
| f"实例化 {provider_config['type']}({provider_config['id']}) 提供商适配器失败:{e}" | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| async def reload(self, provider_config: dict): | ||||||||||||
anka-afk marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
| await self.terminate_provider(provider_config["id"]) | ||||||||||||
| if provider_config["enable"]: | ||||||||||||
| await self.load_provider(provider_config) | ||||||||||||
| provider_id = provider_config["id"] | ||||||||||||
|
|
||||||||||||
| # 和配置文件保持同步 | ||||||||||||
| # 只有禁用的直接终止 | ||||||||||||
| if not provider_config["enable"]: | ||||||||||||
| async with self._inst_map_lock: | ||||||||||||
| if provider_id in self.inst_map: | ||||||||||||
| await self.terminate_provider(provider_id) | ||||||||||||
|
Comment on lines
+413
to
+414
|
||||||||||||
| if provider_id in self.inst_map: | |
| await self.terminate_provider(provider_id) | |
| should_terminate = provider_id in self.inst_map | |
| if should_terminate: | |
| await self.terminate_provider(provider_id) |
Copilot
AI
Nov 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'except' clause does nothing but pass and there is no explanatory comment.
| except Exception: | |
| pass | |
| except Exception as e: | |
| logger.warning(f"清理临时 Provider 实例时出错: {e}") |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -830,19 +830,22 @@ export default { | |||||||||||||||||||||
| }, | ||||||||||||||||||||||
| getStatusColor(status) { | ||||||||||||||||||||||
| switch (status) { | ||||||||||||||||||||||
| case 'available': | ||||||||||||||||||||||
| return 'success'; | ||||||||||||||||||||||
| case 'unavailable': | ||||||||||||||||||||||
| return 'error'; | ||||||||||||||||||||||
| case 'pending': | ||||||||||||||||||||||
| return 'grey'; | ||||||||||||||||||||||
| default: | ||||||||||||||||||||||
| return 'default'; | ||||||||||||||||||||||
| case 'available': return 'success' | ||||||||||||||||||||||
| case 'unavailable': return 'error' | ||||||||||||||||||||||
| case 'disabled': return 'warning' | ||||||||||||||||||||||
| case 'not_loaded': return 'warning' | ||||||||||||||||||||||
|
||||||||||||||||||||||
| case 'not_loaded': return 'warning' | |
| case 'not_loaded': return 'warning' | |
| case 'pending': return 'grey' |
anka-afk marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Nov 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded Chinese text in JavaScript logic violates internationalization (i18n) best practices. The previous implementation used this.messages.status[status] for localized text. These status labels should be moved to the i18n message files and accessed via the translation function (e.g., tm('status.available')) to support multi-language documentation mentioned in the codebase.
| case 'available': return '可用' | |
| case 'unavailable': return '不可用' | |
| case 'disabled': return '已禁用' | |
| case 'not_loaded': return '加载失败' | |
| default: return '未知' | |
| case 'available': return this.tm('status.available') | |
| case 'unavailable': return this.tm('status.unavailable') | |
| case 'disabled': return this.tm('status.disabled') | |
| case 'not_loaded': return this.tm('status.not_loaded') | |
| default: return this.tm('status.unknown') |
Uh oh!
There was an error while loading. Please reload this page.