myagent-ai 1.20.7 → 1.20.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,7 +35,9 @@ class ChatBotManager:
35
35
 
36
36
  def __init__(self):
37
37
  self._bots: Dict[str, BaseChatBot] = {}
38
+ self._bot_tasks: Dict[str, asyncio.Task] = {} # key -> asyncio.Task
38
39
  self._session_map: Dict[str, str] = {} # session_id -> last_message
40
+ self._message_handler: Optional[Callable] = None
39
41
 
40
42
  def get_bot(self, key: str):
41
43
  """根据 key (id 或 platform 名) 获取 bot 实例"""
@@ -49,20 +51,62 @@ class ChatBotManager:
49
51
  """
50
52
  初始化所有聊天平台。
51
53
 
54
+ [v1.20.7] 修复: 先停止并移除已不存在的/被禁用的 bot,
55
+ 再创建新启用的 bot。之前只做添加不做移除,导致禁用平台后
56
+ bot 仍在后台运行。
57
+
52
58
  Args:
53
59
  platform_configs: 平台配置列表
54
60
  message_handler: 统一消息处理回调
55
61
  """
62
+ self._message_handler = message_handler
63
+
64
+ # 计算当前应该启用的平台 key 集合
65
+ new_keys = set()
66
+ for cfg in platform_configs:
67
+ if cfg.enabled:
68
+ key = cfg.id or cfg.platform
69
+ new_keys.add(key)
70
+
71
+ # 找出需要移除的(旧的 key 不在新的 key 集合中)
72
+ removed_keys = [k for k in self._bots if k not in new_keys]
73
+ for key in removed_keys:
74
+ bot = self._bots.pop(key, None)
75
+ task = self._bot_tasks.pop(key, None)
76
+ if bot:
77
+ logger.info(f"聊天平台已移除(禁用/删除): {key}")
78
+ # 尝试停止 bot(同步包装异步)
79
+ try:
80
+ loop = asyncio.get_event_loop()
81
+ if loop.is_running():
82
+ asyncio.ensure_future(self._safe_stop(key, bot))
83
+ else:
84
+ loop.run_until_complete(bot.stop())
85
+ except RuntimeError:
86
+ pass
87
+ if task and not task.done():
88
+ task.cancel()
89
+ logger.info(f"聊天平台 {key} 后台任务已取消")
90
+
91
+ # 创建或更新启用的平台
56
92
  for cfg in platform_configs:
57
93
  if not cfg.enabled:
58
94
  continue
95
+ key = cfg.id or cfg.platform
96
+ # 如果已经存在且配置没变,跳过重建
97
+ if key in self._bots:
98
+ continue
59
99
  try:
60
100
  bot = self._create_bot(cfg, message_handler)
61
101
  if bot:
62
- # 使用唯一 id 作为 key,支持多实例
63
- key = cfg.id or cfg.platform
64
102
  self._bots[key] = bot
65
103
  logger.info(f"聊天平台已配置: {cfg.display_name or key}")
104
+ # 如果已经在运行中(start_all 已调用),自动启动新 bot
105
+ loop = asyncio.get_event_loop()
106
+ if loop.is_running() and not any(
107
+ t for t in asyncio.all_tasks(loop) if t.get_name() == f"bot_{key}"
108
+ ):
109
+ asyncio.ensure_future(self._run_bot(key, bot))
66
110
  except Exception as e:
67
111
  logger.error(f"平台 {cfg.display_name or cfg.platform} 初始化失败: {e}")
68
112
 
@@ -143,7 +187,8 @@ class ChatBotManager:
143
187
  tasks = []
144
188
  for name, bot in self._bots.items():
145
189
  logger.info(f"启动聊天平台: {name}")
146
- task = asyncio.create_task(self._run_bot(name, bot))
190
+ task = asyncio.create_task(self._run_bot(name, bot), name=f"bot_{name}")
191
+ self._bot_tasks[name] = task
147
192
  tasks.append(task)
148
193
  await asyncio.gather(*tasks, return_exceptions=True)
149
194
 
@@ -151,16 +196,39 @@ class ChatBotManager:
151
196
  """安全运行单个聊天平台"""
152
197
  try:
153
198
  await bot.start()
199
+ except asyncio.CancelledError:
200
+ logger.info(f"聊天平台 {name} 已取消")
154
201
  except Exception as e:
155
202
  logger.error(f"聊天平台 {name} 运行异常: {e}")
156
203
 
204
+ async def _safe_stop(self, name: str, bot: BaseChatBot):
205
+ """安全停止单个 bot(不抛异常)"""
206
+ try:
207
+ await bot.stop()
208
+ logger.info(f"聊天平台 {name} 已停止")
209
+ except Exception as e:
210
+ logger.warning(f"停止聊天平台 {name} 异常: {e}")
211
+
212
+ async def stop_platform(self, key: str) -> bool:
213
+ """[v1.20.7] 停止并移除单个聊天平台"""
214
+ bot = self._bots.pop(key, None)
215
+ task = self._bot_tasks.pop(key, None)
216
+ if not bot:
217
+ return False
218
+ await self._safe_stop(key, bot)
219
+ if task and not task.done():
220
+ task.cancel()
221
+ return True
222
+
157
223
  async def stop_all(self):
158
224
  """停止所有聊天平台"""
159
- for name, bot in self._bots.items():
225
+ for name, bot in list(self._bots.items()):
160
226
  try:
161
227
  await bot.stop()
162
228
  except Exception as e:
163
229
  logger.error(f"停止 {name} 失败: {e}")
230
+ self._bots.clear()
231
+ self._bot_tasks.clear()
164
232
  logger.info("所有聊天平台已停止")
165
233
 
166
234
  async def send_to_all(self, text: str):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.20.7",
3
+ "version": "1.20.8",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
package/web/api_server.py CHANGED
@@ -280,16 +280,19 @@ class ApiServer:
280
280
  logger.info("通信管理器已热更新")
281
281
 
282
282
  def _hot_reload_chat_platforms(self):
283
- """热更新聊天平台:重新加载所有平台配置到 ChatBotManager"""
283
+ """热更新聊天平台:重新加载所有平台配置到 ChatBotManager
284
+
285
+ [v1.20.7] 修复: 调用 setup_platforms 时会自动停止已禁用的平台并启动新启用的平台。
286
+ """
284
287
  mgr = self.core.chat_manager
285
288
  if not mgr:
286
289
  return
287
- # 重新设置平台(会重建bot实例)
288
290
  platform_configs = self.core.config_mgr.config.chat_platforms
289
- # 停止旧的平台
290
- # 注意:这里不直接调用 async stop_all,仅更新配置引用
291
- # 实际的平台重启需要异步操作,在 handle_restart_platform 中处理
292
- mgr.setup_platforms(platform_configs, mgr._message_handler if hasattr(mgr, '_message_handler') else None)
291
+ # setup_platforms 会自动对比新旧配置,停止被移除/禁用的 bot,启动新启用的 bot
292
+ mgr.setup_platforms(
293
+ platform_configs,
294
+ mgr._message_handler if hasattr(mgr, '_message_handler') else None
295
+ )
293
296
  logger.info("聊天平台配置已热更新")
294
297
 
295
298
  def _setup_routes(self):