Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox-Core into dev
This commit is contained in:
@@ -146,9 +146,9 @@ class MCPClientManager:
|
|||||||
try:
|
try:
|
||||||
client = await self._create_client(server_config)
|
client = await self._create_client(server_config)
|
||||||
self.clients[server_name] = client
|
self.clients[server_name] = client
|
||||||
logger.info(f"✅ MCP 服务器 '{server_name}' 连接成功")
|
logger.info(f" MCP 服务器 '{server_name}' 连接成功")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 连接 MCP 服务器 '{server_name}' 失败: {e}")
|
logger.error(f" 连接 MCP 服务器 '{server_name}' 失败: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self._initialized = True
|
self._initialized = True
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class MCPToolAdapter(BaseTool):
|
|||||||
self.available_for_llm = True # MCP 工具默认可供 LLM 使用
|
self.available_for_llm = True # MCP 工具默认可供 LLM 使用
|
||||||
|
|
||||||
# 转换参数定义
|
# 转换参数定义
|
||||||
self.parameters = self._convert_parameters(mcp_tool.inputSchema)
|
self.parameters: list[tuple[str, ToolParamType, str, bool, list[str] | None]] = self._convert_parameters(mcp_tool.inputSchema)
|
||||||
|
|
||||||
logger.debug(f"创建 MCP 工具适配器: {self.name}")
|
logger.debug(f"创建 MCP 工具适配器: {self.name}")
|
||||||
|
|
||||||
@@ -238,9 +238,9 @@ async def load_mcp_tools_as_adapters() -> list[MCPToolAdapter]:
|
|||||||
try:
|
try:
|
||||||
adapter = MCPToolAdapter.from_mcp_tool(server_name, mcp_tool)
|
adapter = MCPToolAdapter.from_mcp_tool(server_name, mcp_tool)
|
||||||
adapters.append(adapter)
|
adapters.append(adapter)
|
||||||
logger.debug(f" ✓ 加载工具: {adapter.name}")
|
logger.debug(f" 加载工具: {adapter.name}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f" ✗ 创建工具适配器失败: {mcp_tool.name} | 错误: {e}")
|
logger.error(f" 创建工具适配器失败: {mcp_tool.name} | 错误: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"MCP 工具加载完成: 成功 {len(adapters)}/{total_tools} 个")
|
logger.info(f"MCP 工具加载完成: 成功 {len(adapters)}/{total_tools} 个")
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class PluginManager:
|
|||||||
|
|
||||||
if not module or not hasattr(module, "__plugin_meta__"):
|
if not module or not hasattr(module, "__plugin_meta__"):
|
||||||
self.failed_plugins[plugin_name] = "插件模块中缺少 __plugin_meta__"
|
self.failed_plugins[plugin_name] = "插件模块中缺少 __plugin_meta__"
|
||||||
logger.error(f"❌ 插件加载失败: {plugin_name} - 缺少 __plugin_meta__")
|
logger.error(f" 插件加载失败: {plugin_name} - 缺少 __plugin_meta__")
|
||||||
return False, 1
|
return False, 1
|
||||||
|
|
||||||
metadata: PluginMetadata = getattr(module, "__plugin_meta__")
|
metadata: PluginMetadata = getattr(module, "__plugin_meta__")
|
||||||
@@ -154,14 +154,14 @@ class PluginManager:
|
|||||||
return True, 1
|
return True, 1
|
||||||
else:
|
else:
|
||||||
self.failed_plugins[plugin_name] = "插件注册失败"
|
self.failed_plugins[plugin_name] = "插件注册失败"
|
||||||
logger.error(f"❌ 插件注册失败: {plugin_name}")
|
logger.error(f" 插件注册失败: {plugin_name}")
|
||||||
return False, 1
|
return False, 1
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 其他错误
|
# 其他错误
|
||||||
error_msg = f"未知错误: {e!s}"
|
error_msg = f"未知错误: {e!s}"
|
||||||
self.failed_plugins[plugin_name] = error_msg
|
self.failed_plugins[plugin_name] = error_msg
|
||||||
logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}")
|
logger.error(f" 插件加载失败: {plugin_name} - {error_msg}")
|
||||||
logger.debug("详细错误信息: ", exc_info=True)
|
logger.debug("详细错误信息: ", exc_info=True)
|
||||||
return False, 1
|
return False, 1
|
||||||
|
|
||||||
@@ -340,14 +340,14 @@ class PluginManager:
|
|||||||
if not success:
|
if not success:
|
||||||
error_msg = f"Python依赖检查失败: {', '.join(errors)}"
|
error_msg = f"Python依赖检查失败: {', '.join(errors)}"
|
||||||
self.failed_plugins[plugin_name] = error_msg
|
self.failed_plugins[plugin_name] = error_msg
|
||||||
logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}")
|
logger.error(f" 插件加载失败: {plugin_name} - {error_msg}")
|
||||||
return None # 依赖检查失败,不加载该模块
|
return None # 依赖检查失败,不加载该模块
|
||||||
|
|
||||||
# 2. 检查插件依赖
|
# 2. 检查插件依赖
|
||||||
if not self._check_plugin_dependencies(metadata):
|
if not self._check_plugin_dependencies(metadata):
|
||||||
error_msg = f"插件依赖检查失败: 请确保依赖 {metadata.dependencies} 已正确安装并加载。"
|
error_msg = f"插件依赖检查失败: 请确保依赖 {metadata.dependencies} 已正确安装并加载。"
|
||||||
self.failed_plugins[plugin_name] = error_msg
|
self.failed_plugins[plugin_name] = error_msg
|
||||||
logger.error(f"❌ 插件加载失败: {plugin_name} - {error_msg}")
|
logger.error(f" 插件加载失败: {plugin_name} - {error_msg}")
|
||||||
return None # 插件依赖检查失败
|
return None # 插件依赖检查失败
|
||||||
|
|
||||||
# --- 依赖检查逻辑结束 ---
|
# --- 依赖检查逻辑结束 ---
|
||||||
@@ -408,7 +408,7 @@ class PluginManager:
|
|||||||
|
|
||||||
# 📋 显示插件加载总览
|
# 📋 显示插件加载总览
|
||||||
if total_registered > 0:
|
if total_registered > 0:
|
||||||
logger.info("🎉 插件系统加载完成!")
|
logger.info(" 插件系统加载完成!")
|
||||||
logger.info(
|
logger.info(
|
||||||
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count}, Prompt: {prompt_count}, Router: {router_count})"
|
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count}, Prompt: {prompt_count}, Router: {router_count})"
|
||||||
)
|
)
|
||||||
@@ -616,7 +616,7 @@ class PluginManager:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 插件卸载失败: {plugin_name} - {e!s}", exc_info=True)
|
logger.error(f" 插件卸载失败: {plugin_name} - {e!s}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ class StreamToolHistoryManager:
|
|||||||
|
|
||||||
lines = ["## 🔧 最近工具调用记录"]
|
lines = ["## 🔧 最近工具调用记录"]
|
||||||
for i, record in enumerate(recent_records, 1):
|
for i, record in enumerate(recent_records, 1):
|
||||||
status_icon = "✅" if record.status == "success" else "❌" if record.status == "error" else "⏳"
|
status_icon = "success" if record.status == "success" else "error" if record.status == "error" else "pending"
|
||||||
|
|
||||||
# 格式化参数
|
# 格式化参数
|
||||||
args_preview = self._format_args_preview(record.args)
|
args_preview = self._format_args_preview(record.args)
|
||||||
|
|||||||
@@ -110,19 +110,19 @@ class DependencyManager:
|
|||||||
for package in packages:
|
for package in packages:
|
||||||
try:
|
try:
|
||||||
if self._install_single_package(package, plugin_name):
|
if self._install_single_package(package, plugin_name):
|
||||||
logger.info(f"{log_prefix}✅ 成功安装: {package}")
|
logger.info(f"{log_prefix} 成功安装: {package}")
|
||||||
else:
|
else:
|
||||||
failed_packages.append(package)
|
failed_packages.append(package)
|
||||||
logger.error(f"{log_prefix}❌ 安装失败: {package}")
|
logger.error(f"{log_prefix} 安装失败: {package}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
failed_packages.append(package)
|
failed_packages.append(package)
|
||||||
logger.error(f"{log_prefix}❌ 安装 {package} 时发生异常: {e!s}")
|
logger.error(f"{log_prefix} 安装 {package} 时发生异常: {e!s}")
|
||||||
|
|
||||||
success = len(failed_packages) == 0
|
success = len(failed_packages) == 0
|
||||||
if success:
|
if success:
|
||||||
logger.info(f"{log_prefix}🎉 所有依赖安装完成")
|
logger.info(f"{log_prefix} 所有依赖安装完成")
|
||||||
else:
|
else:
|
||||||
logger.error(f"{log_prefix}⚠️ 部分依赖安装失败: {failed_packages}")
|
logger.error(f"{log_prefix} 部分依赖安装失败: {failed_packages}")
|
||||||
|
|
||||||
return success, failed_packages
|
return success, failed_packages
|
||||||
|
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ class AffinityInterestCalculator(BaseInterestCalculator):
|
|||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
logger.warning("⏱️ 兴趣匹配计算超时(>1.5秒),返回默认分值0.5以保留其他分数")
|
logger.warning("[超时] 兴趣匹配计算超时(>1.5秒),返回默认分值0.5以保留其他分数")
|
||||||
return 0.5 # 超时时返回默认分值,避免丢失提及分和关系分
|
return 0.5 # 超时时返回默认分值,避免丢失提及分和关系分
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"智能兴趣匹配失败: {e}")
|
logger.warning(f"智能兴趣匹配失败: {e}")
|
||||||
|
|||||||
@@ -85,14 +85,14 @@ class ProactiveThinkingReplyHandler(BaseEventHandler):
|
|||||||
|
|
||||||
if success:
|
if success:
|
||||||
if was_paused:
|
if was_paused:
|
||||||
logger.info(f"✅ 聊天流 {stream_id} 主动思考已恢复并重置")
|
logger.info(f"[成功] 聊天流 {stream_id} 主动思考已恢复并重置")
|
||||||
else:
|
else:
|
||||||
logger.debug(f"✅ 聊天流 {stream_id} 主动思考任务已重置")
|
logger.debug(f"[成功] 聊天流 {stream_id} 主动思考任务已重置")
|
||||||
else:
|
else:
|
||||||
logger.warning(f"❌ 重置聊天流 {stream_id} 主动思考任务失败")
|
logger.warning(f"[错误] 重置聊天流 {stream_id} 主动思考任务失败")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 处理reply事件时出错: {e}", exc_info=True)
|
logger.error(f"[错误] 处理reply事件时出错: {e}", exc_info=True)
|
||||||
|
|
||||||
# 总是继续处理其他handler
|
# 总是继续处理其他handler
|
||||||
return HandlerResult(success=True, continue_process=True, message=None)
|
return HandlerResult(success=True, continue_process=True, message=None)
|
||||||
|
|||||||
@@ -694,11 +694,11 @@ async def execute_proactive_thinking(stream_id: str):
|
|||||||
|
|
||||||
# 尝试获取锁,如果已被占用则跳过本次执行(防止重复)
|
# 尝试获取锁,如果已被占用则跳过本次执行(防止重复)
|
||||||
if lock.locked():
|
if lock.locked():
|
||||||
logger.warning(f"⚠️ 主动思考跳过:聊天流 {stream_id} 已有正在执行的主动思考任务")
|
logger.warning(f"[警告] 主动思考跳过:聊天流 {stream_id} 已有正在执行的主动思考任务")
|
||||||
return
|
return
|
||||||
|
|
||||||
async with lock:
|
async with lock:
|
||||||
logger.debug(f"🤔 开始主动思考 {stream_id}")
|
logger.debug(f"[思考] 开始主动思考 {stream_id}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 0. 前置检查
|
# 0. 前置检查
|
||||||
@@ -709,10 +709,10 @@ async def execute_proactive_thinking(stream_id: str):
|
|||||||
chat_stream = await chat_manager.get_stream(stream_id)
|
chat_stream = await chat_manager.get_stream(stream_id)
|
||||||
|
|
||||||
if chat_stream and chat_stream.context_manager.context.is_chatter_processing:
|
if chat_stream and chat_stream.context_manager.context.is_chatter_processing:
|
||||||
logger.warning(f"⚠️ 主动思考等待:聊天流 {stream_id} 的 chatter 正在处理消息,等待3秒后重试...")
|
logger.warning(f"[警告] 主动思考等待:聊天流 {stream_id} 的 chatter 正在处理消息,等待3秒后重试...")
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
if chat_stream.context_manager.context.is_chatter_processing:
|
if chat_stream.context_manager.context.is_chatter_processing:
|
||||||
logger.warning(f"⚠️ 主动思考跳过:聊天流 {stream_id} 的 chatter 仍在处理消息")
|
logger.warning(f"[警告] 主动思考跳过:聊天流 {stream_id} 的 chatter 仍在处理消息")
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"检查 chatter 处理状态时出错: {e},继续执行")
|
logger.warning(f"检查 chatter 处理状态时出错: {e},继续执行")
|
||||||
@@ -781,7 +781,7 @@ async def execute_proactive_thinking(stream_id: str):
|
|||||||
return
|
return
|
||||||
|
|
||||||
elif action == "simple_bubble":
|
elif action == "simple_bubble":
|
||||||
logger.info(f"💬 决策:冒个泡。理由:{reasoning}")
|
logger.info(f"[决策] 决策:冒个泡。理由:{reasoning}")
|
||||||
|
|
||||||
proactive_thinking_scheduler.record_decision(stream_id, action, reasoning, None)
|
proactive_thinking_scheduler.record_decision(stream_id, action, reasoning, None)
|
||||||
|
|
||||||
@@ -793,7 +793,7 @@ async def execute_proactive_thinking(stream_id: str):
|
|||||||
stream_id=stream_id,
|
stream_id=stream_id,
|
||||||
text=reply,
|
text=reply,
|
||||||
)
|
)
|
||||||
logger.info("✅ 已发送冒泡消息")
|
logger.info("[成功] 已发送冒泡消息")
|
||||||
|
|
||||||
# 增加每日计数
|
# 增加每日计数
|
||||||
proactive_thinking_scheduler._increment_daily_count(stream_id)
|
proactive_thinking_scheduler._increment_daily_count(stream_id)
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ class ProactiveThinkingScheduler:
|
|||||||
return 0.5
|
return 0.5
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[调度器] ❌ 获取聊天流 {stream_id} 的 focus_energy 失败: {e}", exc_info=True)
|
logger.error(f"[调度器] [错误] 获取聊天流 {stream_id} 的 focus_energy 失败: {e}", exc_info=True)
|
||||||
return 0.5
|
return 0.5
|
||||||
|
|
||||||
async def schedule_proactive_thinking(self, stream_id: str) -> bool:
|
async def schedule_proactive_thinking(self, stream_id: str) -> bool:
|
||||||
@@ -280,7 +280,7 @@ class ProactiveThinkingScheduler:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 创建主动思考任务失败 {stream_id}: {e}", exc_info=True)
|
logger.error(f"[错误] 创建主动思考任务失败 {stream_id}: {e}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def pause_proactive_thinking(self, stream_id: str, reason: str = "抛出话题") -> bool:
|
async def pause_proactive_thinking(self, stream_id: str, reason: str = "抛出话题") -> bool:
|
||||||
@@ -340,7 +340,7 @@ class ProactiveThinkingScheduler:
|
|||||||
return success
|
return success
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 恢复主动思考失败 {stream_id}: {e}", exc_info=True)
|
logger.error(f"[错误] 恢复主动思考失败 {stream_id}: {e}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def cancel_proactive_thinking(self, stream_id: str) -> bool:
|
async def cancel_proactive_thinking(self, stream_id: str) -> bool:
|
||||||
@@ -361,12 +361,12 @@ class ProactiveThinkingScheduler:
|
|||||||
self._paused_streams.discard(stream_id)
|
self._paused_streams.discard(stream_id)
|
||||||
|
|
||||||
success = await unified_scheduler.remove_schedule(schedule_id)
|
success = await unified_scheduler.remove_schedule(schedule_id)
|
||||||
logger.debug(f"⏹️ 取消主动思考 {stream_id}")
|
logger.debug(f"[取消] 取消主动思考 {stream_id}")
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 取消主动思考失败 {stream_id}: {e}", exc_info=True)
|
logger.error(f"[错误] 取消主动思考失败 {stream_id}: {e}", exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def is_paused(self, stream_id: str) -> bool:
|
async def is_paused(self, stream_id: str) -> bool:
|
||||||
@@ -482,7 +482,7 @@ class ProactiveThinkingScheduler:
|
|||||||
minutes = (remaining_seconds % 3600) // 60
|
minutes = (remaining_seconds % 3600) // 60
|
||||||
time_str = f"{hours}小时{minutes}分钟后"
|
time_str = f"{hours}小时{minutes}分钟后"
|
||||||
|
|
||||||
status = "⏸️ 暂停中" if is_paused else "✅ 活跃"
|
status = "[暂停] 暂停中" if is_paused else "[活跃] 活跃"
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"[{i:2d}] {status} | {stream_name}\n"
|
f"[{i:2d}] {status} | {stream_name}\n"
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class APIKeyManager(Generic[T]):
|
|||||||
try:
|
try:
|
||||||
self.clients = [client_factory(key) for key in valid_keys]
|
self.clients = [client_factory(key) for key in valid_keys]
|
||||||
self.client_cycle = itertools.cycle(self.clients)
|
self.client_cycle = itertools.cycle(self.clients)
|
||||||
logger.info(f"🔑 {service_name} 成功加载 {len(valid_keys)} 个 API 密钥")
|
logger.info(f" {service_name} 成功加载 {len(valid_keys)} 个 API 密钥")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ 初始化 {service_name} 客户端失败: {e}")
|
logger.error(f"❌ 初始化 {service_name} 客户端失败: {e}")
|
||||||
self.clients = []
|
self.clients = []
|
||||||
@@ -61,6 +61,7 @@ class APIKeyManager(Generic[T]):
|
|||||||
if not self.is_available():
|
if not self.is_available():
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
assert self.client_cycle is not None
|
||||||
return next(self.client_cycle)
|
return next(self.client_cycle)
|
||||||
|
|
||||||
def get_client_count(self) -> int:
|
def get_client_count(self) -> int:
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def extract_and_parse_json(response: str, *, strict: bool = False) -> dict[str,
|
|||||||
# 步骤 2: 尝试直接解析
|
# 步骤 2: 尝试直接解析
|
||||||
try:
|
try:
|
||||||
result = orjson.loads(cleaned)
|
result = orjson.loads(cleaned)
|
||||||
logger.debug(f"✅ JSON 直接解析成功,类型: {type(result).__name__}")
|
logger.debug(f" JSON 直接解析成功,类型: {type(result).__name__}")
|
||||||
return result
|
return result
|
||||||
except Exception as direct_error:
|
except Exception as direct_error:
|
||||||
logger.debug(f"直接解析失败: {type(direct_error).__name__}: {direct_error}")
|
logger.debug(f"直接解析失败: {type(direct_error).__name__}: {direct_error}")
|
||||||
@@ -70,10 +70,10 @@ def extract_and_parse_json(response: str, *, strict: bool = False) -> dict[str,
|
|||||||
# repair_json 可能返回字符串或已解析的对象
|
# repair_json 可能返回字符串或已解析的对象
|
||||||
if isinstance(repaired, str):
|
if isinstance(repaired, str):
|
||||||
result = orjson.loads(repaired)
|
result = orjson.loads(repaired)
|
||||||
logger.debug(f"✅ JSON 修复后解析成功(字符串模式),类型: {type(result).__name__}")
|
logger.debug(f" JSON 修复后解析成功(字符串模式),类型: {type(result).__name__}")
|
||||||
else:
|
else:
|
||||||
result = repaired
|
result = repaired
|
||||||
logger.debug(f"✅ JSON 修复后解析成功(对象模式),类型: {type(result).__name__}")
|
logger.debug(f" JSON 修复后解析成功(对象模式),类型: {type(result).__name__}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ def extract_and_parse_json(response: str, *, strict: bool = False) -> dict[str,
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ JSON 解析过程出现异常: {type(e).__name__}: {e}")
|
logger.error(f" JSON 解析过程出现异常: {type(e).__name__}: {e}")
|
||||||
if strict:
|
if strict:
|
||||||
return None
|
return None
|
||||||
return {} if not response.strip().startswith("[") else []
|
return {} if not response.strip().startswith("[") else []
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ class UILogHandler(logging.Handler):
|
|||||||
if record.levelname == "DEBUG":
|
if record.levelname == "DEBUG":
|
||||||
return
|
return
|
||||||
|
|
||||||
emoji_map = {"info": "📝", "warning": "⚠️", "error": "❌", "debug": "🔍"}
|
emoji_map = {"info": "", "warning": "", "error": "", "debug": ""}
|
||||||
formatted_msg = f"{emoji_map.get(ui_level, '📝')} {msg}"
|
formatted_msg = msg
|
||||||
|
|
||||||
self._send_log_with_retry(formatted_msg, ui_level)
|
self._send_log_with_retry(formatted_msg, ui_level)
|
||||||
# 可选:记录发送状态
|
# 可选:记录发送状态
|
||||||
|
|||||||
Reference in New Issue
Block a user