""" title: 智绘心图 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+CiAgPGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMyIgZmlsbD0iY3VycmVudENvbG9yIi8+CiAgPGxpbmUgeDE9IjEyIiB5MT0iOSIgeDI9IjEyIiB5Mj0iNCIvPgogIDxjaXJjbGUgY3g9IjEyIiBjeT0iMyIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjEyIiB5MT0iMTUiIHgyPSIxMiIgeTI9IjIwIi8+CiAgPGNpcmNsZSBjeD0iMTIiIGN5PSIyMSIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjkiIHkxPSIxMiIgeDI9IjQiIHkyPSIxMiIvPgogIDxjaXJjbGUgY3g9IjMiIGN5PSIxMiIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjE1IiB5MT0iMTIiIHgyPSIyMCIgeTI9IjEyIi8+CiAgPGNpcmNsZSBjeD0iMjEiIGN5PSIxMiIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjEwLjUiIHkxPSIxMC41IiB4Mj0iNiIgeTI9IjYiLz4KICA8Y2lyY2xlIGN4PSI1IiBjeT0iNSIgcj0iMS41Ii8+CiAgPGxpbmUgeDE9IjEzLjUiIHkxPSIxMC41IiB4Mj0iMTgiIHkyPSI2Ii8+CiAgPGNpcmNsZSBjeD0iMTkiIGN5PSI1IiByPSIxLjUiLz4KICA8bGluZSB4MT0iMTAuNSIgeTE9IjEzLjUiIHgyPSI2IiB5Mj0iMTgiLz4KICA8Y2lyY2xlIGN4PSI1IiBjeT0iMTkiIHI9IjEuNSIvPgogIDxsaW5lIHgxPSIxMy41IiB5MT0iMTMuNSIgeDI9IjE4IiB5Mj0iMTgiLz4KICA8Y2lyY2xlIGN4PSIxOSIgY3k9IjE5IiByPSIxLjUiLz4KPC9zdmc+ version: 0.7.2 description: 智能分析文本内容,生成交互式思维导图,帮助用户结构化和可视化知识。 """ from pydantic import BaseModel, Field from typing import Optional, Dict, Any import logging import time import re from fastapi import Request from datetime import datetime import pytz from open_webui.utils.chat import generate_chat_completion from open_webui.models.users import Users logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) SYSTEM_PROMPT_MINDMAP_ASSISTANT = """ 你是一个专业的思维导图生成助手,能够高效地分析用户提供的长篇文本,并将其核心主题、关键概念、分支和子分支结构化为标准的Markdown列表语法,以便Markmap.js进行渲染。 请严格遵循以下指导原则: - **语言**: 所有输出必须使用用户指定的语言。 - **格式**: 你的输出必须严格为Markdown列表格式,并用```markdown 和 ``` 包裹。 - 使用 `#` 定义中心主题(根节点)。 - 使用 `-` 和两个空格的缩进表示分支和子分支。 - **内容**: - 识别文本的中心主题作为 `#` 标题。 - 识别主要概念作为一级列表项。 - 识别支持性细节或子概念作为嵌套的列表项。 - 节点内容应简洁明了,避免冗长。 - **只输出Markdown语法**: 不要包含任何额外的寒暄、解释或引导性文字。 - **如果文本过短或无法生成有效导图**: 请输出一个简单的Markdown列表,表示无法生成,例如: ```markdown # 无法生成思维导图 - 原因: 文本内容不足或不明确 ``` """ USER_PROMPT_GENERATE_MINDMAP = """ 请分析以下长篇文本,并将其核心主题、关键概念、分支和子分支结构化为标准的Markdown列表语法,以供Markmap.js渲染。 --- **用户上下文信息:** 用户姓名: {user_name} 当前日期时间: {current_date_time_str} 当前星期: {current_weekday} 当前时区: {current_timezone_str} 用户语言: {user_language} --- **长篇文本内容:** Use code with caution. Python {long_text_content} """ HTML_TEMPLATE_MINDMAP = """ 智绘心图: 思维导图

🧠 智绘心图

用户: {user_name} 分析时间: {current_date_time_str} 星期: {current_weekday_zh}
""" class Action: class Valves(BaseModel): show_status: bool = Field( default=True, description="是否在聊天界面显示操作状态更新。" ) LLM_MODEL_ID: str = Field( default="gemini-2.5-flash", description="用于文本分析的内置LLM模型ID。", ) MIN_TEXT_LENGTH: int = Field( default=100, description="进行思维导图分析所需的最小文本长度(字符数)。" ) def __init__(self): self.valves = self.Valves() self.weekday_map = { "Monday": "星期一", "Tuesday": "星期二", "Wednesday": "星期三", "Thursday": "星期四", "Friday": "星期五", "Saturday": "星期六", "Sunday": "星期日", } def _extract_markdown_syntax(self, llm_output: str) -> str: match = re.search(r"```markdown\s*(.*?)\s*```", llm_output, re.DOTALL) if match: extracted_content = match.group(1).strip() else: logger.warning( "LLM输出未严格遵循预期Markdown格式,将整个输出作为摘要处理。" ) extracted_content = llm_output.strip() return extracted_content.replace("", "<\\/script>") async def action( self, body: dict, __user__: Optional[Dict[str, Any]] = None, __event_emitter__: Optional[Any] = None, __request__: Optional[Request] = None, ) -> Optional[dict]: logger.info("Action: 智绘心图 (v12 - Final Feedback Fix) started") if isinstance(__user__, (list, tuple)): user_language = ( __user__[0].get("language", "zh-CN") if __user__ else "zh-CN" ) user_name = __user__[0].get("name", "用户") if __user__[0] else "用户" user_id = ( __user__[0]["id"] if __user__ and "id" in __user__[0] else "unknown_user" ) elif isinstance(__user__, dict): user_language = __user__.get("language", "zh-CN") user_name = __user__.get("name", "用户") user_id = __user__.get("id", "unknown_user") try: shanghai_tz = pytz.timezone("Asia/Shanghai") current_datetime_shanghai = datetime.now(shanghai_tz) current_date_time_str = current_datetime_shanghai.strftime( "%Y-%m-%d %H:%M:%S" ) current_weekday_en = current_datetime_shanghai.strftime("%A") current_weekday_zh = self.weekday_map.get(current_weekday_en, "未知星期") current_year = current_datetime_shanghai.strftime("%Y") current_timezone_str = "Asia/Shanghai" except Exception as e: logger.warning(f"获取时区信息失败: {e},使用默认值。") now = datetime.now() current_date_time_str = now.strftime("%Y-%m-%d %H:%M:%S") current_weekday_zh = "未知星期" current_year = now.strftime("%Y") current_timezone_str = "未知时区" if __event_emitter__: await __event_emitter__( { "type": "notification", "data": { "type": "info", "content": "智绘心图已启动,正在为您生成思维导图...", }, } ) messages = body.get("messages") if ( not messages or not isinstance(messages, list) or not messages[-1].get("content") ): error_message = "无法获取有效的用户消息内容。" if __event_emitter__: await __event_emitter__( { "type": "notification", "data": {"type": "error", "content": error_message}, } ) return { "messages": [{"role": "assistant", "content": f"❌ {error_message}"}] } parts = re.split(r"```html.*?```", messages[-1]["content"], flags=re.DOTALL) long_text_content = "" if parts: for part in reversed(parts): if part.strip(): long_text_content = part.strip() break if not long_text_content: long_text_content = messages[-1]["content"].strip() if len(long_text_content) < self.valves.MIN_TEXT_LENGTH: short_text_message = f"文本内容过短({len(long_text_content)}字符),无法进行有效分析。请提供至少{self.valves.MIN_TEXT_LENGTH}字符的文本。" if __event_emitter__: await __event_emitter__( { "type": "notification", "data": {"type": "warning", "content": short_text_message}, } ) return { "messages": [ {"role": "assistant", "content": f"⚠️ {short_text_message}"} ] } if self.valves.show_status and __event_emitter__: await __event_emitter__( { "type": "status", "data": { "description": "智绘心图: 深入分析文本结构...", "done": False, "hidden": False, }, } ) try: unique_id = f"id_{int(time.time() * 1000)}" formatted_user_prompt = USER_PROMPT_GENERATE_MINDMAP.format( user_name=user_name, current_date_time_str=current_date_time_str, current_weekday=current_weekday_zh, current_timezone_str=current_timezone_str, user_language=user_language, long_text_content=long_text_content, ) llm_payload = { "model": self.valves.LLM_MODEL_ID, "messages": [ {"role": "system", "content": SYSTEM_PROMPT_MINDMAP_ASSISTANT}, {"role": "user", "content": formatted_user_prompt}, ], "temperature": 0.5, "stream": False, } user_obj = Users.get_user_by_id(user_id) if not user_obj: raise ValueError(f"无法获取用户对象,用户ID: {user_id}") llm_response = await generate_chat_completion( __request__, llm_payload, user_obj ) if ( not llm_response or "choices" not in llm_response or not llm_response["choices"] ): raise ValueError("LLM响应格式不正确或为空。") assistant_response_content = llm_response["choices"][0]["message"][ "content" ] markdown_syntax = self._extract_markdown_syntax(assistant_response_content) final_html_content = ( HTML_TEMPLATE_MINDMAP.replace("{unique_id}", unique_id) .replace("{user_language}", user_language) .replace("{user_name}", user_name) .replace("{current_date_time_str}", current_date_time_str) .replace("{current_weekday_zh}", current_weekday_zh) .replace("{current_year}", current_year) .replace("{markdown_syntax}", markdown_syntax) ) html_embed_tag = f"```html\n{final_html_content}\n```" body["messages"][-1]["content"] = f"{long_text_content}\n\n{html_embed_tag}" if self.valves.show_status and __event_emitter__: await __event_emitter__( { "type": "status", "data": { "description": "智绘心图: 绘制完成!", "done": True, "hidden": False, }, } ) await __event_emitter__( { "type": "notification", "data": { "type": "success", "content": f"思维导图已生成,{user_name}!", }, } ) logger.info("Action: 智绘心图 (v12) completed successfully") except Exception as e: error_message = f"智绘心图处理失败: {str(e)}" logger.error(f"智绘心图错误: {error_message}", exc_info=True) user_facing_error = f"抱歉,智绘心图在处理时遇到错误: {str(e)}。\n请检查Open WebUI后端日志获取更多详情。" body["messages"][-1][ "content" ] = f"{long_text_content}\n\n❌ **错误:** {user_facing_error}" if __event_emitter__: if self.valves.show_status: await __event_emitter__( { "type": "status", "data": { "description": "智绘心图: 处理失败。", "done": True, "hidden": False, }, } ) await __event_emitter__( { "type": "notification", "data": { "type": "error", "content": f"智绘心图生成失败, {user_name}!", }, } ) return body