- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 964
 
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?
Conversation
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.
你好 - 我已审阅了你的更改,它们看起来很棒!
AI 代理提示
请处理此代码审查中的评论:
## Individual Comments
### Comment 1
<location> `astrbot/core/provider/manager.py:82-80` </location>
<code_context>
         """
-        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:
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 锁只覆盖了 inst_map 的访问,但 curr_provider_inst 的赋值在 umo 分支之外。
如果并发访问 curr_provider_inst,在锁之外设置它可能会导致竞态条件。考虑始终在锁内赋值 curr_provider_inst 以确保线程安全。
</issue_to_address>
### Comment 2
<location> `astrbot/dashboard/routes/config.py:522-525` </location>
<code_context>
+                )
+
+            # 先等待加载
+            target = await prov_mgr.get_provider_by_id(provider_id)
             if not target:
                 logger.warning(
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 切换到异步提供商查找是正确的,但请考虑超时错误处理。
考虑为 `get_provider_by_id` 实现超时或取消处理,以防止 API 在调用延迟时挂起。
```suggestion
            # 先等待加载
            import asyncio
            try:
                target = await asyncio.wait_for(
                    prov_mgr.get_provider_by_id(provider_id),
                    timeout=10  # seconds, adjust as needed
                )
            except asyncio.TimeoutError:
                logger.warning(
                    f"Timeout while waiting for provider with id '{provider_id}' to load."
                )
                return (
                    Response()
                    .error(
                        f"Timeout while loading provider with id '{provider_id}'"
                    )
                    .__dict__
                )
            if not target:
                logger.warning(
```
</issue_to_address>
### Comment 3
<location> `dashboard/src/views/ProviderPage.vue:833-837` </location>
<code_context>
-          return 'default';
+        case 'available': return 'success'
+        case 'unavailable': return 'error'
+        case 'disabled': return 'warning'
+        case 'not_loaded': return 'warning'
+        default: return 'info'
       }
</code_context>
<issue_to_address>
**suggestion:** 将 'disabled' 和 'not_loaded' 都映射到 'warning' 可能会降低清晰度。
考虑为 'disabled' 和 'not_loaded' 分配不同的颜色,以提高用户对这些不同状态的理解。
```suggestion
        case 'available': return 'success'
        case 'unavailable': return 'error'
        case 'disabled': return 'warning'
        case 'not_loaded': return 'error'
        default: return 'info'
```
</issue_to_address>
### Comment 4
<location> `astrbot/core/provider/manager.py:394` </location>
<code_context>
    async def reload(self, provider_config: dict):
        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)
            return
        # 备份一份旧实例
        old_instance = None
        async with self._inst_map_lock:
            old_instance = self.inst_map.get(provider_id)
        try:
            # 使用临时 ID 加载新实例
            temp_config = provider_config.copy()
            temp_id = f"{provider_id}_reload_temp"
            temp_config["id"] = temp_id
            await self.load_provider(temp_config)
            # 新实例加载成功,替换旧实例
            async with self._inst_map_lock:
                if temp_id in self.inst_map:
                    new_instance = self.inst_map[temp_id]
                    del self.inst_map[temp_id]
                    new_instance.provider_config["id"] = provider_id
                    self.inst_map[provider_id] = new_instance
                    if old_instance:
                        if old_instance in self.provider_insts:
                            idx = self.provider_insts.index(old_instance)
                            self.provider_insts[idx] = new_instance
                        if old_instance in self.stt_provider_insts:
                            idx = self.stt_provider_insts.index(old_instance)
                            self.stt_provider_insts[idx] = new_instance
                        if old_instance in self.tts_provider_insts:
                            idx = self.tts_provider_insts.index(old_instance)
                            self.tts_provider_insts[idx] = new_instance
                        if old_instance in self.embedding_provider_insts:
                            idx = self.embedding_provider_insts.index(old_instance)
                            self.embedding_provider_insts[idx] = new_instance
                    # 更新当前实例引用
                    if self.curr_provider_inst == old_instance:
                        self.curr_provider_inst = new_instance
                    if self.curr_stt_provider_inst == old_instance:
                        self.curr_stt_provider_inst = new_instance
                    if self.curr_tts_provider_inst == old_instance:
                        self.curr_tts_provider_inst = new_instance
            # 锁外清理旧实例
            if old_instance and hasattr(old_instance, "terminate"):
                try:
                    await old_instance.terminate()
                except Exception as e:
                    logger.warning(f"清理旧 Provider 实例时出错: {e}")
        except Exception as e:
            # 清理临时实例
            async with self._inst_map_lock:
                if temp_id in self.inst_map:
                    temp_instance = self.inst_map[temp_id]
                    del self.inst_map[temp_id]
                    if hasattr(temp_instance, "terminate"):
                        try:
                            await temp_instance.terminate()
                        except Exception:
                            pass
            raise e
        # 清理已移除的提供商
        config_ids = [provider["id"] for provider in self.providers_config]
        async with self._inst_map_lock:
            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} 作为当前文本转语音提供商适配器。"
            )
</code_context>
<issue_to_address>
**issue (code-quality):** 在 ProviderManager.reload 中发现低代码质量 - 18% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))
<br/><details><summary>解释</summary>此函数的质量得分低于 25% 的质量阈值。
该分数是方法长度、认知复杂度和工作记忆的组合。
你如何解决这个问题?
重构此函数以使其更短、更具可读性可能是有益的。
- 通过将部分功能提取到自己的函数中来减少函数长度。这是你能做的最重要的事情——理想情况下,一个函数应该少于 10 行。
- 减少嵌套,例如通过引入守卫子句来提前返回。
- 确保变量的作用域紧密,以便使用相关概念的代码在函数中坐在一起而不是分散开来。</details>
</issue_to_address>
### Comment 5
<location> `astrbot/dashboard/routes/config.py:483-489` </location>
<code_context>
    async def check_one_provider_status(self):
        """API: check a single LLM Provider's status by id"""
        provider_id = request.args.get("id")
        if not provider_id:
            return self._error_response(
                "Missing provider_id parameter", 400, logger.warning
            )
        logger.info(f"API call: /config/provider/check_one id={provider_id}")
        try:
            prov_mgr = self.core_lifecycle.provider_manager
            # 配置里没有
            provider_config = None
            for config in self.config["provider"]:
                if config.get("id") == provider_id:
                    provider_config = config
                    break
            if not provider_config:
                logger.warning(
                    f"Provider config with id '{provider_id}' not found in configuration."
                )
                return (
                    Response()
                    .error(
                        f"Provider with id '{provider_id}' not found in configuration"
                    )
                    .__dict__
                )
            # 没启用
            if not provider_config.get("enable", False):
                logger.info(f"Provider with id '{provider_id}' is disabled.")
                return (
                    Response()
                    .ok(
                        {
                            "id": provider_id,
                            "model": provider_config.get("model", "Unknown Model"),
                            "type": provider_config.get(
                                "provider_type", "Unknown Type"
                            ),
                            "name": provider_config.get("name", provider_id),
                            "status": "disabled",
                            "error": "Provider is disabled",
                        }
                    )
                    .__dict__
                )
            # 先等待加载
            target = await prov_mgr.get_provider_by_id(provider_id)
            if not target:
                logger.warning(
                    f"Provider with id '{provider_id}' is enabled but not loaded in provider_manager."
                )
                return (
                    Response()
                    .ok(
                        {
                            "id": provider_id,
                            "model": provider_config.get("model", "Unknown Model"),
                            "type": provider_config.get(
                                "provider_type", "Unknown Type"
                            ),
                            "name": provider_config.get("name", provider_id),
                            "status": "not_loaded",
                            "error": "Provider is enabled but failed to load. Check logs for details.",
                        }
                    )
                    .__dict__
                )
            # 已加载
            result = await self._test_single_provider(target)
            return Response().ok(result).__dict__
        except Exception as e:
            return self._error_response(
                f"Critical error checking provider {provider_id}: {e}", 500
            )
</code_context>
<issue_to_address>
**suggestion (code-quality):** 使用内置函数 `next` 代替 for 循环 ([`use-next`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-next/))
```suggestion
            provider_config = next(
                (
                    config
                    for config in self.config["provider"]
                    if config.get("id") == provider_id
                ),
                None,
            )
```
</issue_to_address>帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用这些反馈来改进你的评论。
Original comment in English
Hey there - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `astrbot/core/provider/manager.py:82-80` </location>
<code_context>
         """
-        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:
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Locking only covers inst_map access, but curr_provider_inst assignment is outside umo branch.
If curr_provider_inst is accessed concurrently, setting it outside the lock may cause race conditions. Consider always assigning curr_provider_inst within the lock for thread safety.
</issue_to_address>
### Comment 2
<location> `astrbot/dashboard/routes/config.py:522-525` </location>
<code_context>
+                )
+
+            # 先等待加载
+            target = await prov_mgr.get_provider_by_id(provider_id)
             if not target:
                 logger.warning(
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Switching to async provider lookup is correct, but consider error handling for timeouts.
Consider implementing a timeout or cancellation handling for get_provider_by_id to prevent the API from hanging if the call is delayed.
```suggestion
            # 先等待加载
            import asyncio
            try:
                target = await asyncio.wait_for(
                    prov_mgr.get_provider_by_id(provider_id),
                    timeout=10  # seconds, adjust as needed
                )
            except asyncio.TimeoutError:
                logger.warning(
                    f"Timeout while waiting for provider with id '{provider_id}' to load."
                )
                return (
                    Response()
                    .error(
                        f"Timeout while loading provider with id '{provider_id}'"
                    )
                    .__dict__
                )
            if not target:
                logger.warning(
```
</issue_to_address>
### Comment 3
<location> `dashboard/src/views/ProviderPage.vue:833-837` </location>
<code_context>
-          return 'default';
+        case 'available': return 'success'
+        case 'unavailable': return 'error'
+        case 'disabled': return 'warning'
+        case 'not_loaded': return 'warning'
+        default: return 'info'
       }
</code_context>
<issue_to_address>
**suggestion:** Mapping both 'disabled' and 'not_loaded' to 'warning' may reduce clarity.
Consider assigning different colors to 'disabled' and 'not_loaded' to improve user understanding of these distinct states.
```suggestion
        case 'available': return 'success'
        case 'unavailable': return 'error'
        case 'disabled': return 'warning'
        case 'not_loaded': return 'error'
        default: return 'info'
```
</issue_to_address>
### Comment 4
<location> `astrbot/core/provider/manager.py:394` </location>
<code_context>
    async def reload(self, provider_config: dict):
        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)
            return
        # 备份一份旧实例
        old_instance = None
        async with self._inst_map_lock:
            old_instance = self.inst_map.get(provider_id)
        try:
            # 使用临时 ID 加载新实例
            temp_config = provider_config.copy()
            temp_id = f"{provider_id}_reload_temp"
            temp_config["id"] = temp_id
            await self.load_provider(temp_config)
            # 新实例加载成功,替换旧实例
            async with self._inst_map_lock:
                if temp_id in self.inst_map:
                    new_instance = self.inst_map[temp_id]
                    del self.inst_map[temp_id]
                    new_instance.provider_config["id"] = provider_id
                    self.inst_map[provider_id] = new_instance
                    if old_instance:
                        if old_instance in self.provider_insts:
                            idx = self.provider_insts.index(old_instance)
                            self.provider_insts[idx] = new_instance
                        if old_instance in self.stt_provider_insts:
                            idx = self.stt_provider_insts.index(old_instance)
                            self.stt_provider_insts[idx] = new_instance
                        if old_instance in self.tts_provider_insts:
                            idx = self.tts_provider_insts.index(old_instance)
                            self.tts_provider_insts[idx] = new_instance
                        if old_instance in self.embedding_provider_insts:
                            idx = self.embedding_provider_insts.index(old_instance)
                            self.embedding_provider_insts[idx] = new_instance
                    # 更新当前实例引用
                    if self.curr_provider_inst == old_instance:
                        self.curr_provider_inst = new_instance
                    if self.curr_stt_provider_inst == old_instance:
                        self.curr_stt_provider_inst = new_instance
                    if self.curr_tts_provider_inst == old_instance:
                        self.curr_tts_provider_inst = new_instance
            # 锁外清理旧实例
            if old_instance and hasattr(old_instance, "terminate"):
                try:
                    await old_instance.terminate()
                except Exception as e:
                    logger.warning(f"清理旧 Provider 实例时出错: {e}")
        except Exception as e:
            # 清理临时实例
            async with self._inst_map_lock:
                if temp_id in self.inst_map:
                    temp_instance = self.inst_map[temp_id]
                    del self.inst_map[temp_id]
                    if hasattr(temp_instance, "terminate"):
                        try:
                            await temp_instance.terminate()
                        except Exception:
                            pass
            raise e
        # 清理已移除的提供商
        config_ids = [provider["id"] for provider in self.providers_config]
        async with self._inst_map_lock:
            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} 作为当前文本转语音提供商适配器。"
            )
</code_context>
<issue_to_address>
**issue (code-quality):** Low code quality found in ProviderManager.reload - 18% ([`low-code-quality`](https://docs.sourcery.ai/Reference/Default-Rules/comments/low-code-quality/))
<br/><details><summary>Explanation</summary>The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.
How can you solve this?
It might be worth refactoring this function to make it shorter and more readable.
- Reduce the function length by extracting pieces of functionality out into
  their own functions. This is the most important thing you can do - ideally a
  function should be less than 10 lines.
- Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
  sits together within the function rather than being scattered.</details>
</issue_to_address>
### Comment 5
<location> `astrbot/dashboard/routes/config.py:483-489` </location>
<code_context>
    async def check_one_provider_status(self):
        """API: check a single LLM Provider's status by id"""
        provider_id = request.args.get("id")
        if not provider_id:
            return self._error_response(
                "Missing provider_id parameter", 400, logger.warning
            )
        logger.info(f"API call: /config/provider/check_one id={provider_id}")
        try:
            prov_mgr = self.core_lifecycle.provider_manager
            # 配置里没有
            provider_config = None
            for config in self.config["provider"]:
                if config.get("id") == provider_id:
                    provider_config = config
                    break
            if not provider_config:
                logger.warning(
                    f"Provider config with id '{provider_id}' not found in configuration."
                )
                return (
                    Response()
                    .error(
                        f"Provider with id '{provider_id}' not found in configuration"
                    )
                    .__dict__
                )
            # 没启用
            if not provider_config.get("enable", False):
                logger.info(f"Provider with id '{provider_id}' is disabled.")
                return (
                    Response()
                    .ok(
                        {
                            "id": provider_id,
                            "model": provider_config.get("model", "Unknown Model"),
                            "type": provider_config.get(
                                "provider_type", "Unknown Type"
                            ),
                            "name": provider_config.get("name", provider_id),
                            "status": "disabled",
                            "error": "Provider is disabled",
                        }
                    )
                    .__dict__
                )
            # 先等待加载
            target = await prov_mgr.get_provider_by_id(provider_id)
            if not target:
                logger.warning(
                    f"Provider with id '{provider_id}' is enabled but not loaded in provider_manager."
                )
                return (
                    Response()
                    .ok(
                        {
                            "id": provider_id,
                            "model": provider_config.get("model", "Unknown Model"),
                            "type": provider_config.get(
                                "provider_type", "Unknown Type"
                            ),
                            "name": provider_config.get("name", provider_id),
                            "status": "not_loaded",
                            "error": "Provider is enabled but failed to load. Check logs for details.",
                        }
                    )
                    .__dict__
                )
            # 已加载
            result = await self._test_single_provider(target)
            return Response().ok(result).__dict__
        except Exception as e:
            return self._error_response(
                f"Critical error checking provider {provider_id}: {e}", 500
            )
</code_context>
<issue_to_address>
**suggestion (code-quality):** Use the built-in function `next` instead of a for-loop ([`use-next`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-next/))
```suggestion
            provider_config = next(
                (
                    config
                    for config in self.config["provider"]
                    if config.get("id") == provider_id
                ),
                None,
            )
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| 
           可以给一下复现这个问题的操作步骤吗  | 
    
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.
Pull Request Overview
This PR enhances provider status management by introducing new status types ("disabled" and "not_loaded") to better distinguish between different provider unavailability states, and implements thread-safety improvements for the provider instance map operations.
- Added new provider statuses "disabled" and "not_loaded" to differentiate unavailable states
 - Implemented asyncio lock protection for 
inst_mapaccess to prevent race conditions - Refactored provider reload logic to support atomic instance replacement
 
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description | 
|---|---|
| dashboard/src/views/ProviderPage.vue | Added UI support for "disabled" and "not_loaded" statuses with appropriate icons, colors, and hardcoded Chinese labels | 
| astrbot/dashboard/routes/config.py | Enhanced provider status checking logic to return specific statuses for disabled and not-loaded providers | 
| astrbot/core/provider/manager.py | Added _inst_map_lock for thread-safe access and refactored reload logic for atomic provider replacement | 
| 
               | 
          ||
| # 不启用提供商会话隔离模式的情况 | ||
| 
               | 
          ||
| prov = self.inst_map[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.
Race condition: accessing self.inst_map[provider_id] outside the lock after checking existence inside the lock. The provider could be removed between line 87 and line 99. This access should either be moved inside the lock block or the provider reference should be captured within the lock.
| case 'available': return '可用' | ||
| case 'unavailable': return '不可用' | ||
| case 'disabled': return '已禁用' | ||
| case 'not_loaded': return '加载失败' | ||
| default: return '未知' | 
    
      
    
      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') | 
| case 'available': return 'success' | ||
| case 'unavailable': return 'error' | ||
| case 'disabled': return 'warning' | ||
| case 'not_loaded': return 'warning' | 
    
      
    
      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.
Missing the 'pending' case that was previously handled. The old implementation returned 'grey' for 'pending' status, but this is now missing. If a provider has 'pending' status, it will fall through to the default 'info' case, which may not be the intended color for pending operations.
| case 'not_loaded': return 'warning' | |
| case 'not_loaded': return 'warning' | |
| case 'pending': return 'grey' | 
| if provider_id in self.inst_map: | ||
| 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.
Potential deadlock: calling terminate_provider() while holding _inst_map_lock. The terminate_provider() method also attempts to acquire _inst_map_lock at line 521, which will cause a deadlock since asyncio.Lock is not reentrant. Extract the instance from inst_map within the lock, then call terminate_provider() outside the lock.
| 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) | 
| except Exception: | ||
| pass | 
    
      
    
      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}") | 






Motivation / 动机
Modifications / 改动点
Verification Steps / 验证步骤
如图


修改温度保存后:
Screenshots or Test Results / 运行截图或测试结果
Compatibility & Breaking Changes / 兼容性与破坏性变更
Checklist / 检查清单
requirements.txt和pyproject.toml文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations inrequirements.txtandpyproject.toml.Sourcery 摘要
在 ProviderManager 中,强制实现对提供者实例的线程安全访问和一致的状态管理,改进提供者重新加载和终止逻辑,并增强前端和 API 行为,以正确处理已禁用 (disabled) 和未加载 (not_loaded) 的提供者状态。
错误修复:
功能增强:
Original summary in English
Summary by Sourcery
Enforce thread-safe access and consistent state management for provider instances in ProviderManager, improve provider reload and termination logic, and enhance front-end and API behavior to correctly handle disabled and not_loaded provider statuses.
Bug Fixes:
Enhancements: