mirror of https://github.com/Mai-with-u/MaiBot.git
Merge branch 'dev' of https://github.com/MaiM-with-u/MaiBot into gn-dev
commit
9df84d23c5
|
|
@ -50,21 +50,18 @@ class MessageStorage(ABC):
|
|||
class MongoDBMessageStorage(MessageStorage):
|
||||
"""MongoDB消息存储实现"""
|
||||
|
||||
def __init__(self):
|
||||
self.db = db
|
||||
|
||||
async def get_messages_after(self, chat_id: str, message_time: float) -> List[Dict[str, Any]]:
|
||||
query = {"chat_id": chat_id}
|
||||
# print(f"storage_check_message: {message_time}")
|
||||
|
||||
query["time"] = {"$gt": message_time}
|
||||
|
||||
return list(self.db.messages.find(query).sort("time", 1))
|
||||
return list(db.messages.find(query).sort("time", 1))
|
||||
|
||||
async def get_messages_before(self, chat_id: str, time_point: float, limit: int = 5) -> List[Dict[str, Any]]:
|
||||
query = {"chat_id": chat_id, "time": {"$lt": time_point}}
|
||||
|
||||
messages = list(self.db.messages.find(query).sort("time", -1).limit(limit))
|
||||
messages = list(db.messages.find(query).sort("time", -1).limit(limit))
|
||||
|
||||
# 将消息按时间正序排列
|
||||
messages.reverse()
|
||||
|
|
@ -73,7 +70,7 @@ class MongoDBMessageStorage(MessageStorage):
|
|||
async def has_new_messages(self, chat_id: str, after_time: float) -> bool:
|
||||
query = {"chat_id": chat_id, "time": {"$gt": after_time}}
|
||||
|
||||
return self.db.messages.find_one(query) is not None
|
||||
return db.messages.find_one(query) is not None
|
||||
|
||||
|
||||
# # 创建一个内存消息存储实现,用于测试
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ class EmojiManager:
|
|||
"""
|
||||
try:
|
||||
self._ensure_db()
|
||||
time_start = time.time()
|
||||
_time_start = time.time()
|
||||
|
||||
# 获取所有表情包
|
||||
all_emojis = self.emoji_objects
|
||||
|
|
@ -287,35 +287,41 @@ class EmojiManager:
|
|||
|
||||
# 计算与每个emotion标签的相似度,取最大值
|
||||
max_similarity = 0
|
||||
best_matching_emotion = "" # 记录最匹配的 emotion 喵~
|
||||
for emotion in emotions:
|
||||
# 使用编辑距离计算相似度
|
||||
distance = self._levenshtein_distance(text_emotion, emotion)
|
||||
max_len = max(len(text_emotion), len(emotion))
|
||||
similarity = 1 - (distance / max_len if max_len > 0 else 0)
|
||||
max_similarity = max(max_similarity, similarity)
|
||||
if similarity > max_similarity: # 如果找到更相似的喵~
|
||||
max_similarity = similarity
|
||||
best_matching_emotion = emotion # 就记下这个 emotion 喵~
|
||||
|
||||
emoji_similarities.append((emoji, max_similarity))
|
||||
if best_matching_emotion: # 确保有匹配的情感才添加喵~
|
||||
emoji_similarities.append((emoji, max_similarity, best_matching_emotion)) # 把 emotion 也存起来喵~
|
||||
|
||||
# 按相似度降序排序
|
||||
emoji_similarities.sort(key=lambda x: x[1], reverse=True)
|
||||
|
||||
# 获取前5个最相似的表情包
|
||||
top_5_emojis = emoji_similarities[:10] if len(emoji_similarities) > 10 else emoji_similarities
|
||||
# 获取前10个最相似的表情包
|
||||
top_emojis = (
|
||||
emoji_similarities[:10] if len(emoji_similarities) > 10 else emoji_similarities
|
||||
) # 改个名字,更清晰喵~
|
||||
|
||||
if not top_5_emojis:
|
||||
if not top_emojis:
|
||||
logger.warning("未找到匹配的表情包")
|
||||
return None
|
||||
|
||||
# 从前5个中随机选择一个
|
||||
selected_emoji, similarity = random.choice(top_5_emojis)
|
||||
# 从前几个中随机选择一个
|
||||
selected_emoji, similarity, matched_emotion = random.choice(top_emojis) # 把匹配的 emotion 也拿出来喵~
|
||||
|
||||
# 更新使用次数
|
||||
self.record_usage(selected_emoji.hash)
|
||||
|
||||
time_end = time.time()
|
||||
_time_end = time.time()
|
||||
|
||||
logger.info(
|
||||
f"找到[{text_emotion}]表情包,用时:{time_end - time_start:.2f}秒: {selected_emoji.description} (相似度: {similarity:.4f})"
|
||||
logger.info( # 使用匹配到的 emotion 记录日志喵~
|
||||
f"为[{text_emotion}]找到表情包: {matched_emotion},({similarity:.4f})"
|
||||
)
|
||||
return selected_emoji.path, f"[ {selected_emoji.description} ]"
|
||||
|
||||
|
|
|
|||
|
|
@ -301,9 +301,9 @@ class PromptBuilder:
|
|||
relation_prompt = ""
|
||||
for person in who_chat_in_group:
|
||||
relation_prompt += await relationship_manager.build_relationship_info(person)
|
||||
print(f"relation_prompt: {relation_prompt}")
|
||||
# print(f"relation_prompt: {relation_prompt}")
|
||||
|
||||
print(f"relat11111111ion_prompt: {relation_prompt}")
|
||||
# print(f"relat11111111ion_prompt: {relation_prompt}")
|
||||
|
||||
# 心情
|
||||
mood_manager = MoodManager.get_instance()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, TypeVar, List, Union, Tuple
|
||||
import ast
|
||||
|
||||
# 定义类型变量用于泛型类型提示
|
||||
T = TypeVar("T")
|
||||
|
|
@ -12,6 +13,7 @@ logger = logging.getLogger("json_utils")
|
|||
def safe_json_loads(json_str: str, default_value: T = None) -> Union[Any, T]:
|
||||
"""
|
||||
安全地解析JSON字符串,出错时返回默认值
|
||||
现在尝试处理单引号和标准JSON
|
||||
|
||||
参数:
|
||||
json_str: 要解析的JSON字符串
|
||||
|
|
@ -20,16 +22,34 @@ def safe_json_loads(json_str: str, default_value: T = None) -> Union[Any, T]:
|
|||
返回:
|
||||
解析后的Python对象,或在解析失败时返回default_value
|
||||
"""
|
||||
if not json_str:
|
||||
if not json_str or not isinstance(json_str, str):
|
||||
logger.warning(f"safe_json_loads 接收到非字符串输入: {type(json_str)}, 值: {json_str}")
|
||||
return default_value
|
||||
|
||||
try:
|
||||
# 尝试标准的 JSON 解析
|
||||
return json.loads(json_str)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"JSON解析失败: {e}, JSON字符串: {json_str[:100]}...")
|
||||
return default_value
|
||||
except json.JSONDecodeError:
|
||||
# 如果标准解析失败,尝试将单引号替换为双引号再解析
|
||||
# (注意:这种替换可能不安全,如果字符串内容本身包含引号)
|
||||
# 更安全的方式是用 ast.literal_eval
|
||||
try:
|
||||
# logger.debug(f"标准JSON解析失败,尝试用 ast.literal_eval 解析: {json_str[:100]}...")
|
||||
result = ast.literal_eval(json_str)
|
||||
# 确保结果是字典(因为我们通常期望参数是字典)
|
||||
if isinstance(result, dict):
|
||||
return result
|
||||
else:
|
||||
logger.warning(f"ast.literal_eval 解析成功但结果不是字典: {type(result)}, 内容: {result}")
|
||||
return default_value
|
||||
except (ValueError, SyntaxError, MemoryError, RecursionError) as ast_e:
|
||||
logger.error(f"使用 ast.literal_eval 解析失败: {ast_e}, 字符串: {json_str[:100]}...")
|
||||
return default_value
|
||||
except Exception as e:
|
||||
logger.error(f"使用 ast.literal_eval 解析时发生意外错误: {e}, 字符串: {json_str[:100]}...")
|
||||
return default_value
|
||||
except Exception as e:
|
||||
logger.error(f"JSON解析过程中发生意外错误: {e}")
|
||||
logger.error(f"JSON解析过程中发生意外错误: {e}, 字符串: {json_str[:100]}...")
|
||||
return default_value
|
||||
|
||||
|
||||
|
|
@ -177,25 +197,27 @@ def process_llm_tool_calls(
|
|||
if "name" not in func_details or not isinstance(func_details.get("name"), str):
|
||||
logger.warning(f"{log_prefix}工具调用[{i}]的'function'字段缺少'name'或类型不正确: {func_details}")
|
||||
continue
|
||||
if "arguments" not in func_details or not isinstance(
|
||||
func_details.get("arguments"), str
|
||||
): # 参数是字符串形式的JSON
|
||||
logger.warning(f"{log_prefix}工具调用[{i}]的'function'字段缺少'arguments'或类型不正确: {func_details}")
|
||||
|
||||
# 验证参数 'arguments'
|
||||
args_value = func_details.get("arguments")
|
||||
|
||||
# 1. 检查 arguments 是否存在且是字符串
|
||||
if args_value is None or not isinstance(args_value, str):
|
||||
logger.warning(f"{log_prefix}工具调用[{i}]的'function'字段缺少'arguments'字符串: {func_details}")
|
||||
continue
|
||||
|
||||
# 可选:尝试解析参数JSON,确保其有效
|
||||
args_str = func_details["arguments"]
|
||||
try:
|
||||
json.loads(args_str) # 尝试解析,但不存储结果
|
||||
except json.JSONDecodeError as e:
|
||||
# 2. 尝试安全地解析 arguments 字符串
|
||||
parsed_args = safe_json_loads(args_value, None)
|
||||
|
||||
# 3. 检查解析结果是否为字典
|
||||
if parsed_args is None or not isinstance(parsed_args, dict):
|
||||
logger.warning(
|
||||
f"{log_prefix}工具调用[{i}]的'arguments'不是有效的JSON字符串: {e}, 内容: {args_str[:100]}..."
|
||||
f"{log_prefix}工具调用[{i}]的'arguments'无法解析为有效的JSON字典, "
|
||||
f"原始字符串: {args_value[:100]}..., 解析结果类型: {type(parsed_args).__name__}"
|
||||
)
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.warning(f"{log_prefix}解析工具调用[{i}]的'arguments'时发生意外错误: {e}, 内容: {args_str[:100]}...")
|
||||
continue
|
||||
|
||||
# 如果检查通过,将原始的 tool_call 加入有效列表
|
||||
valid_tool_calls.append(tool_call)
|
||||
|
||||
if not valid_tool_calls and tool_calls: # 如果原始列表不为空,但验证后为空
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ class ClassicalWillingManager(BaseWillingManager):
|
|||
self.chat_reply_willing[chat_id] = max(0, current_willing - 1.8)
|
||||
|
||||
async def after_generate_reply_handle(self, message_id):
|
||||
if message_id not in self.ongoing_messages:
|
||||
return
|
||||
|
||||
chat_id = self.ongoing_messages[message_id].chat_id
|
||||
current_willing = self.chat_reply_willing.get(chat_id, 0)
|
||||
if current_willing < 1:
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class BaseWillingManager(ABC):
|
|||
def delete(self, message_id: str):
|
||||
del_message = self.ongoing_messages.pop(message_id, None)
|
||||
if not del_message:
|
||||
logger.debug(f"删除异常,当前消息{message_id}不存在")
|
||||
logger.debug(f"尝试删除不存在的消息 ID: {message_id},可能已被其他流程处理,喵~")
|
||||
|
||||
@abstractmethod
|
||||
async def async_task_starter(self) -> None:
|
||||
|
|
|
|||
Loading…
Reference in New Issue