| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- import uuid
- import re
- from typing import List, Dict
- from core.utils.current_time import get_current_time, get_now
- class Message:
- def __init__(
- self,
- role: str,
- content: str = None,
- uniq_id: str = None,
- tool_calls=None,
- tool_call_id=None,
- is_temporary=False,
- ):
- self.uniq_id = uniq_id if uniq_id is not None else str(uuid.uuid4())
- self.role = role
- self.content = content
- self.tool_calls = tool_calls
- self.tool_call_id = tool_call_id
- self.is_temporary = is_temporary # 标记临时消息(如工具调用提醒)
- class Dialogue:
- def __init__(self):
- self.dialogue: List[Message] = []
- # 获取当前时间
- self.current_time = get_now().strftime("%Y-%m-%d %H:%M:%S")
- def put(self, message: Message):
- self.dialogue.append(message)
- def getMessages(self, m, dialogue):
- if m.tool_calls is not None:
- dialogue.append({"role": m.role, "tool_calls": m.tool_calls})
- elif m.role == "tool":
- dialogue.append(
- {
- "role": m.role,
- "tool_call_id": (
- str(uuid.uuid4()) if m.tool_call_id is None else m.tool_call_id
- ),
- "content": m.content,
- }
- )
- else:
- dialogue.append({"role": m.role, "content": m.content})
- def get_llm_dialogue(self) -> List[Dict[str, str]]:
- # 直接调用get_llm_dialogue_with_memory,传入None作为memory_str
- # 这样确保说话人功能在所有调用路径下都生效
- return self.get_llm_dialogue_with_memory(None, None)
- def update_system_message(self, new_content: str):
- """更新或添加系统消息"""
- # 查找第一个系统消息
- system_msg = next((msg for msg in self.dialogue if msg.role == "system"), None)
- if system_msg:
- system_msg.content = new_content
- else:
- self.put(Message(role="system", content=new_content))
- def trim_history(self, max_turns: int = 10) -> int:
- """
- 智能截断对话历史,保留工具调用的完整性
- Args:
- max_turns: 保留的最大对话轮数(每轮 = user + assistant/tool 相关消息)
- Returns:
- int: 被移除的消息数量
- """
- if len(self.dialogue) <= max_turns * 2 + 1: # +1 是系统消息
- return 0
- # 分离系统消息和对话消息
- system_messages = [msg for msg in self.dialogue if msg.role == "system"]
- conversation_messages = [msg for msg in self.dialogue if msg.role != "system"]
- if len(conversation_messages) <= max_turns * 2:
- return 0
- # 智能截断:保留完整的工具调用链路
- keep_messages = []
- i = len(conversation_messages) - 1
- turn_count = 0
- while i >= 0 and turn_count < max_turns:
- msg = conversation_messages[i]
- # 从后向前收集消息
- if msg.role == "user":
- # 遇到 user 消息,说明一轮对话开始
- keep_messages.insert(0, msg)
- turn_count += 1
- i -= 1
- elif msg.role == "assistant":
- # 收集 assistant 消息
- keep_messages.insert(0, msg)
- # 如果这个 assistant 有 tool_calls,需要收集对应的 tool 响应
- if msg.tool_calls is not None:
- i -= 1
- # 继续向后收集所有相关的 tool 消息
- while i >= 0 and conversation_messages[i].role == "tool":
- keep_messages.insert(0, conversation_messages[i])
- i -= 1
- else:
- i -= 1
- elif msg.role == "tool":
- # tool 消息应该已经被上面的逻辑收集了
- # 如果单独遇到,也要保留(防止边界情况)
- keep_messages.insert(0, msg)
- i -= 1
- else:
- i -= 1
- removed_count = len(conversation_messages) - len(keep_messages)
- # 重建对话列表
- self.dialogue = system_messages + keep_messages
- return removed_count
- def get_llm_dialogue_with_memory(
- self, memory_str: str = None, voiceprint_config: dict = None
- ) -> List[Dict[str, str]]:
- # 构建对话
- dialogue = []
- # 添加系统提示和记忆
- system_message = next(
- (msg for msg in self.dialogue if msg.role == "system"), None
- )
- if system_message:
- # 基础系统提示
- enhanced_system_prompt = system_message.content
- # 替换时间占位符
- enhanced_system_prompt = enhanced_system_prompt.replace(
- "{{current_time}}", get_current_time()
- )
- # 添加说话人个性化描述
- try:
- speakers = voiceprint_config.get("speakers", [])
- if speakers:
- enhanced_system_prompt += "\n\n<speakers_info>"
- for speaker_str in speakers:
- try:
- parts = speaker_str.split(",", 2)
- if len(parts) >= 2:
- name = parts[1].strip()
- # 如果描述为空,则为""
- description = (
- parts[2].strip() if len(parts) >= 3 else ""
- )
- enhanced_system_prompt += f"\n- {name}:{description}"
- except:
- pass
- enhanced_system_prompt += "\n\n</speakers_info>"
- except:
- # 配置读取失败时忽略错误,不影响其他功能
- pass
- # 使用正则表达式匹配 <memory> 标签,不管中间有什么内容
- if memory_str is not None:
- enhanced_system_prompt = re.sub(
- r"<memory>.*?</memory>",
- f"<memory>\n{memory_str}\n</memory>",
- enhanced_system_prompt,
- flags=re.DOTALL,
- )
- dialogue.append({"role": "system", "content": enhanced_system_prompt})
- # 添加用户和助手的对话
- for m in self.dialogue:
- if m.role != "system": # 跳过原始的系统消息
- self.getMessages(m, dialogue)
- return dialogue
|