From 68e8e1f70b79371f3efd0a34b6f4bbdce5405318 Mon Sep 17 00:00:00 2001 From: Raven95676 Date: Tue, 1 Jul 2025 12:40:52 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E9=9B=86=E6=88=90r1=5Ffilter?= =?UTF-8?q?=E8=87=B3=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/default.py | 6 +++ packages/thinking_filter/main.py | 70 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 packages/thinking_filter/main.py diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index fc304d587..4a75a11bc 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -54,6 +54,7 @@ DEFAULT_CONFIG = { "wake_prefix": "", "web_search": False, "web_search_link": False, + "display_reasoning_text": False, "identifier": False, "datetime_system_prompt": True, "default_personality": "default", @@ -1652,6 +1653,11 @@ CONFIG_METADATA_2 = { "obvious_hint": True, "hint": "开启后,将会传入网页搜索结果的链接给模型,并引导模型输出引用链接。", }, + "display_reasoning_text": { + "description": "显示思考内容", + "type": "bool", + "hint": "开启后,将在回复中显示模型的思考过程。", + }, "identifier": { "description": "启动识别群员", "type": "bool", diff --git a/packages/thinking_filter/main.py b/packages/thinking_filter/main.py new file mode 100644 index 000000000..eb36a107f --- /dev/null +++ b/packages/thinking_filter/main.py @@ -0,0 +1,70 @@ +import re +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.star import Context, Star, register +from astrbot.api.provider import LLMResponse +from openai.types.chat.chat_completion import ChatCompletion + + +@register( + "thinking_filter", + "Soulter", + "可选择是否过滤推理模型的思考内容", + "1.0.0", + "https://astrbot.app", +) +class R1Filter(Star): + def __init__(self, context: Context): + super().__init__(context) + self.display_reasoning_text = ( + self.context.get_config() + .get("provider_settings", {}) + .get("display_reasoning_text", False) + ) + + @filter.on_llm_response() + async def resp(self, event: AstrMessageEvent, response: LLMResponse): + if self.display_reasoning_text: + if ( + response + and response.raw_completion + and isinstance(response.raw_completion, ChatCompletion) + ): + if ( + len(response.raw_completion.choices) + and response.raw_completion.choices[0].message + ): + message = response.raw_completion.choices[0].message + reasoning_content = "" # 初始化 reasoning_content + + # 检查 Groq deepseek-r1-distill-llama-70b模型的 'reasoning' 属性 + if hasattr(message, "reasoning") and message.reasoning: + reasoning_content = message.reasoning + # 检查 DeepSeek deepseek-reasoner模型的 'reasoning_content' + elif ( + hasattr(message, "reasoning_content") + and message.reasoning_content + ): + reasoning_content = message.reasoning_content + + if reasoning_content: + response.completion_text = ( + f"🤔思考:{reasoning_content}\n\n{message.content}" + ) + else: + response.completion_text = message.content + + else: + # DeepSeek 官方的模型的思考存在了 reason_content 字段因此不需要过滤 + completion_text = response.completion_text + # 适配 ollama deepseek-r1 模型 + if r"" in completion_text or r"" in completion_text: + completion_text = re.sub( + r".*?", "", completion_text, flags=re.DOTALL + ).strip() + # 可能有单标签情况 + completion_text = ( + completion_text.replace(r"", "") + .replace(r"", "") + .strip() + ) + response.completion_text = completion_text From d03d0354370c6a241af16a6d7da0f2a68256286a Mon Sep 17 00:00:00 2001 From: Raven95676 Date: Tue, 1 Jul 2025 13:53:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?perf:=20=E5=90=88=E5=B9=B6=E5=B5=8C?= =?UTF-8?q?=E5=A5=97=E7=9A=84if=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/thinking_filter/main.py | 51 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/packages/thinking_filter/main.py b/packages/thinking_filter/main.py index eb36a107f..26c2db9b9 100644 --- a/packages/thinking_filter/main.py +++ b/packages/thinking_filter/main.py @@ -24,47 +24,48 @@ class R1Filter(Star): @filter.on_llm_response() async def resp(self, event: AstrMessageEvent, response: LLMResponse): if self.display_reasoning_text: + # 显示推理内容的处理逻辑 if ( response and response.raw_completion and isinstance(response.raw_completion, ChatCompletion) + and len(response.raw_completion.choices) > 0 + and response.raw_completion.choices[0].message ): - if ( - len(response.raw_completion.choices) - and response.raw_completion.choices[0].message + message = response.raw_completion.choices[0].message + reasoning_content = "" # 初始化 reasoning_content + + # 检查 Groq deepseek-r1-distill-llama-70b 模型的 'reasoning' 属性 + if hasattr(message, "reasoning") and message.reasoning: + reasoning_content = message.reasoning + # 检查 DeepSeek deepseek-reasoner 模型的 'reasoning_content' + elif ( + hasattr(message, "reasoning_content") and message.reasoning_content ): - message = response.raw_completion.choices[0].message - reasoning_content = "" # 初始化 reasoning_content - - # 检查 Groq deepseek-r1-distill-llama-70b模型的 'reasoning' 属性 - if hasattr(message, "reasoning") and message.reasoning: - reasoning_content = message.reasoning - # 检查 DeepSeek deepseek-reasoner模型的 'reasoning_content' - elif ( - hasattr(message, "reasoning_content") - and message.reasoning_content - ): - reasoning_content = message.reasoning_content - - if reasoning_content: - response.completion_text = ( - f"🤔思考:{reasoning_content}\n\n{message.content}" - ) - else: - response.completion_text = message.content + reasoning_content = message.reasoning_content + if reasoning_content: + response.completion_text = ( + f"🤔思考:{reasoning_content}\n\n{message.content}" + ) + else: + response.completion_text = message.content else: - # DeepSeek 官方的模型的思考存在了 reason_content 字段因此不需要过滤 + # 过滤推理标签的处理逻辑 completion_text = response.completion_text - # 适配 ollama deepseek-r1 模型 + + # 检查并移除 标签 if r"" in completion_text or r"" in completion_text: + # 移除配对的标签及其内容 completion_text = re.sub( r".*?", "", completion_text, flags=re.DOTALL ).strip() - # 可能有单标签情况 + + # 移除可能残留的单个标签 completion_text = ( completion_text.replace(r"", "") .replace(r"", "") .strip() ) + response.completion_text = completion_text