From d00a28f9d3820399ad712d39ce7b8978dd25c587 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 9 May 2025 11:53:47 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=BF=AE=E6=94=B9=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E8=B0=83=E7=94=A8=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- llm_tool_benchmark_results.json | 145 ----------------- src/heart_flow/sub_mind.py | 265 ++++++++++++++++---------------- src/plugins/utils/json_utils.py | 84 ++++++++++ 从0.6.0升级0.6.3请先看我.txt | 1 - 4 files changed, 215 insertions(+), 280 deletions(-) delete mode 100644 llm_tool_benchmark_results.json delete mode 100644 从0.6.0升级0.6.3请先看我.txt diff --git a/llm_tool_benchmark_results.json b/llm_tool_benchmark_results.json deleted file mode 100644 index 6caa7c315..000000000 --- a/llm_tool_benchmark_results.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "测试时间": "2025-04-28 14:12:36", - "测试迭代次数": 10, - "不使用工具调用": { - "平均耗时": 4.596814393997192, - "最短耗时": 2.957131862640381, - "最长耗时": 10.121938705444336, - "标准差": 2.1705468730949593, - "所有耗时": [ - 3.18, - 4.65, - 10.12, - 3.5, - 4.46, - 4.24, - 3.23, - 6.2, - 2.96, - 3.42 - ] - }, - "不使用工具调用_详细响应": [ - { - "内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"怎么啦?\",\n \"emoji_query\": \"友好地询问\"\n}\n```", - "推理内容摘要": "" - }, - { - "内容摘要": "decide_reply_action(\n action=\"text_reply\",\n reasoning=\"千石连续两次叫了我的名字,显然是想引起我的注意或有事要说,作为礼貌应当回应\",\n emoji_query=\"友善的回应\"\n)", - "推理内容摘要": "" - }, - { - "内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"怎么啦?我在呢~\",\n \"emoji_query\": \"友好的询问\",\n \"reasoning\": \"由于对方连续两次提到我的名字,显然是想与我交流,应当及时给予友好回应避免冷场\"\n}\n```", - "推理内容摘要": "" - }, - { - "内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"在呢在呢~怎么啦?\",\n \"emoji_query\": \"好奇的回应\"\n}\n```", - "推理内容摘要": "" - }, - { - "内容摘要": "decide_reply_action(\n action=\"text_reply\",\n reasoning=\"千石连续两次提到我的名字,显然需要我回应。文字回复更正式且能明确表示我在关注他的信息。\",\n emoji_query=\"友好的回应\"\n)", - "推理内容摘要": "" - }, - { - "内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"reasoning\": \"千石连续两次提到我的名字,显然是需要我的回应。作为日常交流,应该给予友善简短的答复。\",\n \"emoji_query\": \"疑惑的歪头\"\n}\n```", - "推理内容摘要": "" - }, - { - "内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"咋啦千石~\",\n \"emoji_query\": \"好奇的询问\"\n}\n```", - "推理内容摘要": "" - }, - { - "内容摘要": "decide_reply_action\n```json\n{\n \"action\": \"text_reply\",\n \"content\": \"我在呢~怎么啦?\",\n \"emoji_query\": \"友好的关心\",\n \"reasoning\": \"千石连续两次呼唤我的名字,显然是有事情要找我或想引起我的注意。根据回复原则2(有人提到你但未回应),应该用友善的文字进行回应,并附上表达关心的表情符号来延...", - "推理内容摘要": "" - }, - { - "内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"千石怎么啦~\",\n \"emoji_query\": \"好奇的探询\"\n}\n```", - "推理内容摘要": "" - }, - { - "内容摘要": "```json\n{\n \"action\": \"text_reply\",\n \"content\": \"怎么啦?突然叫我两次\",\n \"emoji_query\": \"好奇的疑问\"\n}\n```", - "推理内容摘要": "" - } - ], - "使用工具调用": { - "平均耗时": 8.139546775817871, - "最短耗时": 4.9980738162994385, - "最长耗时": 18.803313732147217, - "标准差": 4.008772720760647, - "所有耗时": [ - 5.81, - 18.8, - 6.06, - 8.06, - 10.07, - 6.34, - 7.9, - 6.66, - 5.0, - 6.69 - ] - }, - "使用工具调用_详细响应": [ - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - }, - { - "内容摘要": "", - "推理内容摘要": "", - "工具调用数量": 0, - "工具调用详情": [] - } - ], - "差异百分比": 77.07 -} \ No newline at end of file diff --git a/src/heart_flow/sub_mind.py b/src/heart_flow/sub_mind.py index 1275fbbf4..08ee406d9 100644 --- a/src/heart_flow/sub_mind.py +++ b/src/heart_flow/sub_mind.py @@ -8,68 +8,99 @@ from src.individuality.individuality import Individuality import random from ..plugins.utils.prompt_builder import Prompt, global_prompt_manager from src.do_tool.tool_use import ToolUser -from src.plugins.utils.json_utils import safe_json_dumps, process_llm_tool_calls +from src.plugins.utils.json_utils import safe_json_dumps, safe_json_loads, process_llm_tool_calls, convert_custom_to_standard_tool_calls from src.heart_flow.chat_state_info import ChatStateInfo from src.plugins.chat.chat_stream import chat_manager from src.plugins.heartFC_chat.heartFC_Cycleinfo import CycleInfo import difflib from src.plugins.person_info.relationship_manager import relationship_manager +import json logger = get_logger("sub_heartflow") def init_prompt(): - # --- Group Chat Prompt --- - group_prompt = """ -{extra_info} -{relation_prompt} -你的名字是{bot_name},{prompt_personality} -{last_loop_prompt} -{cycle_info_block} -现在是{time_now},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容: -{chat_observe_info} + # 定义静态的工具调用JSON格式说明文本 + tool_json_format_instructions_text = """ +如果你需要使用工具,请按照以下JSON格式输出: +{ + "thinking": "你的内心思考过程,即使调用工具也需要这部分", + "tool_calls": [ // 这是一个工具调用对象的列表,如果不需要工具则此列表为空或省略此键 + { + "name": "工具的名称", // 例如:"get_memory" 或 "compare_numbers" + "arguments": { // 注意:这里 arguments 是一个 JSON 对象 + "参数名1": "参数值1", // 例如:"topic": "最近的聊天" + "参数名2": "参数值2" // 例如:"max_memories": 3 + // ... 根据工具定义的其他参数 + } + } + // ... 如果有更多工具调用的话 + ] +} +如果不需要使用工具,你可以只输出你的思考内容文本(此时不需要遵循JSON格式),或者输出一个仅包含 "thinking" 键的JSON对象。""" -你现在{mood_info} -请仔细阅读当前群聊内容,分析讨论话题和群成员关系,分析你刚刚发言和别人对你的发言的反应,思考你要不要回复。然后思考你是否需要使用函数工具。 -思考并输出你的内心想法 + # --- Group Chat Prompt --- + group_prompt_text = f""" +{{extra_info}} +{{relation_prompt}} +你的名字是{{bot_name}},{{prompt_personality}} +{{last_loop_prompt}} +{{cycle_info_block}} +现在是{{time_now}},你正在上网,和qq群里的网友们聊天,以下是正在进行的聊天内容: +{{chat_observe_info}} + +你现在{{mood_info}} +请仔细阅读当前群聊内容,分析讨论话题和群成员关系,分析你刚刚发言和别人对你的发言的反应,思考你要不要回复。 +然后思考你是否需要使用函数工具来帮助你获取信息或执行操作。 + +【可用的工具】 +如果你决定使用工具,你可以使用以下这些: +{{{{available_tools_info}}}} + +【工具使用格式】 +{tool_json_format_instructions_text} + +现在,请先输出你的内心想法。如果需要调用工具,请按照上述格式组织你的输出。 输出要求: -1. 根据聊天内容生成你的想法,{hf_do_next} -2. 不要分点、不要使用表情符号 -3. 避免多余符号(冒号、引号、括号等) -4. 语言简洁自然,不要浮夸 -5. 如果你刚发言,并且没有人回复你,不要回复 -工具使用说明: -1. 输出想法后考虑是否需要使用工具 -2. 工具可获取信息或执行操作 -3. 如需处理消息或回复,请使用工具。""" - Prompt(group_prompt, "sub_heartflow_prompt_before") +1. 根据聊天内容生成你的想法,{{hf_do_next}} +2. (想法部分)不要分点、不要使用表情符号 +3. (想法部分)避免多余符号(冒号、引号、括号等) +4. (想法部分)语言简洁自然,不要浮夸 +5. 如果你刚发言,并且没有人回复你,通常不需要回复,除非有特殊原因或需要使用工具获取新信息。 +""" + Prompt(group_prompt_text, "sub_heartflow_prompt_before") # --- Private Chat Prompt --- - private_prompt = """ -{extra_info} -{relation_prompt} -你的名字是{bot_name},{prompt_personality} -{last_loop_prompt} -{cycle_info_block} -现在是{time_now},你正在上网,和 {chat_target_name} 私聊,以下是你们的聊天内容: -{chat_observe_info} + private_prompt_text = f""" +{{extra_info}} +{{relation_prompt}} +你的名字是{{bot_name}},{{prompt_personality}} +{{last_loop_prompt}} +{{cycle_info_block}} +现在是{{time_now}},你正在上网,和 {{{{chat_target_name}}}} 私聊,以下是你们的聊天内容: +{{chat_observe_info}} -你现在{mood_info} -请仔细阅读聊天内容,想想你和 {chat_target_name} 的关系,回顾你们刚刚的交流,你刚刚发言和对方的反应,思考聊天的主题。 -请思考你要不要回复以及如何回复对方。然后思考你是否需要使用函数工具。 -思考并输出你的内心想法 +你现在{{mood_info}} +请仔细阅读聊天内容,想想你和 {{{{chat_target_name}}}} 的关系,回顾你们刚刚的交流,你刚刚发言和对方的反应,思考聊天的主题。 +请思考你要不要回复以及如何回复对方。然后思考你是否需要使用函数工具来帮助你获取信息或执行操作。 + +【可用的工具】 +如果你决定使用工具,你可以使用以下这些: +{{{{available_tools_info}}}} + +【工具使用格式】 +{tool_json_format_instructions_text} + +现在,请先输出你的内心想法。如果需要调用工具,请按照上述格式组织你的输出。 输出要求: -1. 根据聊天内容生成你的想法,{hf_do_next} -2. 不要分点、不要使用表情符号 -3. 避免多余符号(冒号、引号、括号等) -4. 语言简洁自然,不要浮夸 -5. 如果你刚发言,对方没有回复你,请谨慎回复 -工具使用说明: -1. 输出想法后考虑是否需要使用工具 -2. 工具可获取信息或执行操作 -3. 如需处理消息或回复,请使用工具。""" - Prompt(private_prompt, "sub_heartflow_prompt_private_before") # New template name +1. 根据聊天内容生成你的想法,{{hf_do_next}} +2. (想法部分)不要分点、不要使用表情符号 +3. (想法部分)避免多余符号(冒号、引号、括号等) +4. (想法部分)语言简洁自然,不要浮夸 +5. 如果你刚发言,对方没有回复你,请谨慎回复,除非有特殊原因或需要使用工具获取新信息。 +""" + Prompt(private_prompt_text, "sub_heartflow_prompt_private_before") # --- Last Loop Prompt (remains the same) --- last_loop_t = """ @@ -203,7 +234,7 @@ class SubMind: # 获取观察对象 observation: ChattingObservation = self.observations[0] if self.observations else None - if not observation or not hasattr(observation, "is_group_chat"): # Ensure it's ChattingObservation or similar + if not observation or not hasattr(observation, "is_group_chat"): logger.error(f"{self.log_prefix} 无法获取有效的观察对象或缺少聊天类型信息") self.update_current_mind("(观察出错了...)") return self.current_mind, self.past_mind @@ -217,7 +248,6 @@ class SubMind: chat_target_name = ( chat_target_info.get("person_name") or chat_target_info.get("user_nickname") or chat_target_name ) - # --- End getting observation info --- # 获取观察内容 chat_observe_info = observation.get_observe_info() @@ -227,17 +257,19 @@ class SubMind: # 初始化工具 tool_instance = ToolUser() tools = tool_instance._define_tools() + # 生成工具信息字符串,用于填充到提示词模板中 + available_tools_info_str = "\n".join([f"- {tool['function']['name']}: {tool['function']['description']}" for tool in tools]) + if not available_tools_info_str: + available_tools_info_str = "当前没有可用的工具。" + # 获取个性化信息 individuality = Individuality.get_instance() relation_prompt = "" - # print(f"person_list: {person_list}") for person in person_list: relation_prompt += await relationship_manager.build_relationship_info(person, is_id=True) - # print(f"relat22222ion_prompt: {relation_prompt}") - # 构建个性部分 prompt_personality = individuality.get_prompt(x_person=2, level=2) @@ -304,7 +336,6 @@ class SubMind: formatted_response = "[空回复]" if not response_text else " ".join(response_text) responses_for_prompt.append(formatted_response) else: - # 一旦遇到非文本回复,连续性中断 break # 根据连续文本回复的数量构建提示信息 @@ -316,11 +347,9 @@ class SubMind: elif consecutive_text_replies == 1: # 如果最近的一个活动是文本回复 cycle_info_block = f'你刚刚已经回复一条消息(内容: "{responses_for_prompt[0]}")' - # 包装提示块,增加可读性,即使没有连续回复也给个标记 if cycle_info_block: cycle_info_block = f"\n【近期回复历史】\n{cycle_info_block}\n" else: - # 如果最近的活动循环不是文本回复,或者没有活动循环 cycle_info_block = "\n【近期回复历史】\n(最近没有连续文本回复)\n" # 加权随机选择思考指导 @@ -329,8 +358,7 @@ class SubMind: )[0] # ---------- 4. 构建最终提示词 ---------- - # --- Choose template based on chat type --- - logger.debug(f"is_group_chat: {is_group_chat}") + # 选择模板 if is_group_chat: template_name = "sub_heartflow_prompt_before" prompt = (await global_prompt_manager.get_prompt_async(template_name)).format( @@ -344,24 +372,24 @@ class SubMind: hf_do_next=hf_do_next, last_loop_prompt=last_loop_prompt, cycle_info_block=cycle_info_block, - # chat_target_name is not used in group prompt + available_tools_info=available_tools_info_str # 新增:填充可用工具列表 ) - else: # Private chat + else: template_name = "sub_heartflow_prompt_private_before" prompt = (await global_prompt_manager.get_prompt_async(template_name)).format( extra_info=self.structured_info_str, prompt_personality=prompt_personality, - relation_prompt=relation_prompt, # Might need adjustment for private context + relation_prompt=relation_prompt, bot_name=individuality.name, time_now=time_now, - chat_target_name=chat_target_name, # Pass target name + chat_target_name=chat_target_name, chat_observe_info=chat_observe_info, mood_info=mood_info, hf_do_next=hf_do_next, last_loop_prompt=last_loop_prompt, cycle_info_block=cycle_info_block, + available_tools_info=available_tools_info_str # 新增:填充可用工具列表 ) - # --- End choosing template --- # ---------- 5. 执行LLM请求并处理响应 ---------- content = "" # 初始化内容变量 @@ -369,34 +397,49 @@ class SubMind: try: # 调用LLM生成响应 - response, _reasoning_content, tool_calls = await self.llm_model.generate_response_tool_async( - prompt=prompt, tools=tools - ) + response, _reasoning_content, _ = await self.llm_model.generate_response(prompt=prompt) + + if not response: + logger.warning(f"{self.log_prefix} LLM返回空结果,思考失败。") + content = "(不知道该想些什么...)" + return self.current_mind, self.past_mind - logger.debug(f"{self.log_prefix} 子心流输出的原始LLM响应: {response}") + # 使用 safe_json_loads 解析响应 + parsed_response = safe_json_loads(response, default_value={}) + + if parsed_response: + thinking = parsed_response.get("thinking", "") + tool_calls_custom_format = parsed_response.get("tool_calls", []) # Renamed for clarity - # 直接使用LLM返回的文本响应作为 content - content = response if response else "" + # 处理工具调用 + if tool_calls_custom_format: + # 使用 convert_custom_to_standard_tool_calls 将自定义格式转换为标准格式 + success, valid_tool_calls_standard_format, error_msg = convert_custom_to_standard_tool_calls(tool_calls_custom_format, self.log_prefix) + + if success and valid_tool_calls_standard_format: + for tool_call_standard in valid_tool_calls_standard_format: # Iterate over standard format + try: + # _execute_tool_call expects the standard format + result = await tool_instance._execute_tool_call(tool_call_standard) + + if result: + new_item = { + "type": result.get("type", "unknown_type"), + "id": result.get("id", f"fallback_id_{time.time()}"), + "content": result.get("content", ""), + "ttl": 3, + } + self.structured_info.append(new_item) + except Exception as tool_e: + logger.error(f"{self.log_prefix} 工具执行失败: {tool_e}") + logger.error(traceback.format_exc()) + else: + logger.warning(f"{self.log_prefix} 自定义工具调用转换或验证失败: {error_msg}") - if tool_calls: - # 直接将 tool_calls 传递给处理函数 - success, valid_tool_calls, error_msg = process_llm_tool_calls( - tool_calls, log_prefix=f"{self.log_prefix} " - ) - - if success and valid_tool_calls: - # 记录工具调用信息 - tool_calls_str = ", ".join( - [call.get("function", {}).get("name", "未知工具") for call in valid_tool_calls] - ) - logger.info(f"{self.log_prefix} 模型请求调用{len(valid_tool_calls)}个工具: {tool_calls_str}") - - # 收集工具执行结果 - await self._execute_tool_calls(valid_tool_calls, tool_instance) - elif not success: - logger.warning(f"{self.log_prefix} 处理工具调用时出错: {error_msg}") + content = thinking else: - logger.info(f"{self.log_prefix} 心流未使用工具") + # 如果不是JSON格式或解析失败,直接使用响应内容 + content = response except Exception as e: # 处理总体异常 @@ -404,22 +447,14 @@ class SubMind: logger.error(traceback.format_exc()) content = "思考过程中出现错误" - # 记录初步思考结果 - logger.debug(f"{self.log_prefix} 初步心流思考结果: {content}\nprompt: {prompt}\n") - - # 处理空响应情况 - if not content: - content = "(不知道该想些什么...)" - logger.warning(f"{self.log_prefix} LLM返回空结果,思考失败。") - # ---------- 6. 应用概率性去重和修饰 ---------- - new_content = content # 保存 LLM 直接输出的结果 + new_content = content try: similarity = calculate_similarity(previous_mind, new_content) replacement_prob = calculate_replacement_probability(similarity) logger.debug(f"{self.log_prefix} 新旧想法相似度: {similarity:.2f}, 替换概率: {replacement_prob:.2f}") - # 定义词语列表 (移到判断之前) + # 定义词语列表 yu_qi_ci_liebiao = ["嗯", "哦", "啊", "唉", "哈", "唔"] zhuan_zhe_liebiao = ["但是", "不过", "然而", "可是", "只是"] cheng_jie_liebiao = ["然后", "接着", "此外", "而且", "另外"] @@ -477,58 +512,20 @@ class SubMind: content = new_content # 保留原始 content else: logger.debug(f"{self.log_prefix} 未执行概率性去重 (概率: {replacement_prob:.2f})") - # content 保持 new_content 不变 except Exception as e: logger.error(f"{self.log_prefix} 应用概率性去重或特殊处理时出错: {e}") logger.error(traceback.format_exc()) - # 出错时保留原始 content - content = new_content + content = new_content # 出错时保留去重前的内容,或者可以考虑保留原始LLM输出 # ---------- 7. 更新思考状态并返回结果 ---------- logger.info(f"{self.log_prefix} 最终心流思考结果: {content}") # 更新当前思考内容 self.update_current_mind(content) + self._update_structured_info_str() return self.current_mind, self.past_mind - async def _execute_tool_calls(self, tool_calls, tool_instance): - """ - 执行一组工具调用并收集结果 - - 参数: - tool_calls: 工具调用列表 - tool_instance: 工具使用器实例 - """ - tool_results = [] - new_structured_items = [] # 收集新产生的结构化信息 - - # 执行所有工具调用 - for tool_call in tool_calls: - try: - result = await tool_instance._execute_tool_call(tool_call) - if result: - tool_results.append(result) - # 创建新的结构化信息项 - new_item = { - "type": result.get("type", "unknown_type"), # 使用 'type' 键 - "id": result.get("id", f"fallback_id_{time.time()}"), # 使用 'id' 键 - "content": result.get("content", ""), # 'content' 键保持不变 - "ttl": 3, - } - new_structured_items.append(new_item) - - except Exception as tool_e: - logger.error(f"[{self.subheartflow_id}] 工具执行失败: {tool_e}") - logger.error(traceback.format_exc()) # 添加 traceback 记录 - - # 如果有新的工具结果,记录并更新结构化信息 - if new_structured_items: - self.structured_info.extend(new_structured_items) # 添加到现有列表 - logger.debug(f"工具调用收集到新的结构化信息: {safe_json_dumps(new_structured_items, ensure_ascii=False)}") - # logger.debug(f"当前完整的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}") # 可以取消注释以查看完整列表 - self._update_structured_info_str() # 添加新信息后,更新字符串表示 - def update_current_mind(self, response): if self.current_mind: # 只有当 current_mind 非空时才添加到 past_mind self.past_mind.append(self.current_mind) diff --git a/src/plugins/utils/json_utils.py b/src/plugins/utils/json_utils.py index 6226e6e96..0040aa4ff 100644 --- a/src/plugins/utils/json_utils.py +++ b/src/plugins/utils/json_utils.py @@ -2,6 +2,7 @@ import json import logging from typing import Any, Dict, TypeVar, List, Union, Tuple import ast +import time # 定义类型变量用于泛型类型提示 T = TypeVar("T") @@ -224,3 +225,86 @@ def process_llm_tool_calls( return False, [], "所有工具调用格式均无效" return True, valid_tool_calls, "" + + +def convert_custom_to_standard_tool_calls( + custom_tool_calls: List[Dict[str, Any]], log_prefix: str = "" +) -> Tuple[bool, List[Dict[str, Any]], str]: + """ + Converts a list of tool calls from a custom format (name, arguments_object) + to a standard format (id, type, function_with_name_and_arguments_string). + + Custom format per item: + { + "name": "tool_name", + "arguments": {"param": "value"} + } + + Standard format per item: + { + "id": "call_...", + "type": "function", + "function": { + "name": "tool_name", + "arguments": "{\"param\": \"value\"}" + } + } + + Args: + custom_tool_calls: List of tool calls in the custom format. + log_prefix: Logger prefix. + + Returns: + Tuple (success_flag, list_of_standard_tool_calls, error_message) + """ + if not isinstance(custom_tool_calls, list): + return False, [], f"{log_prefix}Input custom_tool_calls is not a list, got {type(custom_tool_calls).__name__}" + + if not custom_tool_calls: + return True, [], "Custom tool call list is empty." + + standard_tool_calls = [] + for i, custom_call in enumerate(custom_tool_calls): + if not isinstance(custom_call, dict): + msg = f"{log_prefix}Item {i} in custom_tool_calls is not a dictionary, got {type(custom_call).__name__}" + logger.warning(msg) + return False, [], msg + + tool_name = custom_call.get("name") + tool_arguments_obj = custom_call.get("arguments") + + if not isinstance(tool_name, str) or not tool_name: + msg = f"{log_prefix}Item {i} ('{custom_call}') is missing 'name' or name is not a string." + logger.warning(msg) + return False, [], msg + + if not isinstance(tool_arguments_obj, dict): + # Allow empty arguments if it's missing, defaulting to {} + if tool_arguments_obj is None: + tool_arguments_obj = {} + else: + msg = f"{log_prefix}Item {i} ('{tool_name}') has 'arguments' but it's not a dictionary, got {type(tool_arguments_obj).__name__}." + logger.warning(msg) + return False, [], msg + + arguments_str = safe_json_dumps(tool_arguments_obj, default_value="{}", ensure_ascii=False) + if arguments_str == "{}" and tool_arguments_obj: # safe_json_dumps failed for non-empty obj + msg = f"{log_prefix}Item {i} ('{tool_name}') failed to dump arguments to JSON string: {tool_arguments_obj}" + logger.warning(msg) + # Potentially return False here if strict dumping is required, or proceed with "{}" + # For now, we'll proceed with "{}" if dumping fails but log it. + + standard_call_id = f"call_{int(time.time() * 1000)}_{i}" + + standard_call = { + "id": standard_call_id, + "type": "function", + "function": { + "name": tool_name, + "arguments": arguments_str, + }, + } + standard_tool_calls.append(standard_call) + + logger.debug(f"{log_prefix}Converted {len(custom_tool_calls)} custom calls to {len(standard_tool_calls)} standard calls.") + return True, standard_tool_calls, "" diff --git a/从0.6.0升级0.6.3请先看我.txt b/从0.6.0升级0.6.3请先看我.txt deleted file mode 100644 index 734061c99..000000000 --- a/从0.6.0升级0.6.3请先看我.txt +++ /dev/null @@ -1 +0,0 @@ -该版本变动了人格相关设置,原有的配置内容可能被自动更新,如果你没有备份,可以在\config\old找回 \ No newline at end of file