""" title: Flash Card author: Antigravity author_url: https://github.com/open-webui funding_url: https://github.com/open-webui version: 0.2.1 icon_url: data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIxIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjRkZENzAwIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjRkZBNzAwIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHBhdGggZD0iTTEzIDJMMyA3djEzbDEwIDV2LTZ6IiBmaWxsPSJ1cmwoI2cpIi8+PHBhdGggZD0iTTEzIDJ2Nmw4LTN2MTNsLTggM3YtNnoiIGZpbGw9IiM2NjdlZWEiLz48cGF0aCBkPSJNMTMgMnY2bTAgNXYxMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLW9wYWNpdHk9IjAuMyIvPjwvc3ZnPg== description: Quickly generates beautiful flashcards from text, extracting key points and categories. """ from pydantic import BaseModel, Field from typing import Optional, Dict, Any, List import json import logging from open_webui.utils.chat import generate_chat_completion from open_webui.models.users import Users # Setup logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class Action: class Valves(BaseModel): model_id: str = Field( default="", description="用于生成卡片内容的模型 ID。如果为空,则使用当前模型。", ) min_text_length: int = Field( default=50, description="生成闪记卡所需的最小文本长度(字符数)。" ) max_text_length: int = Field( default=2000, description="建议的最大文本长度。超过此长度建议使用深度分析工具。", ) language: str = Field( default="zh", description="卡片内容的目标语言 (例如 'zh', 'en')。" ) show_status: bool = Field( default=True, description="是否在聊天界面显示状态更新。" ) def __init__(self): self.valves = self.Valves() async def action( self, body: dict, __user__: Optional[Dict[str, Any]] = None, __event_emitter__: Optional[Any] = None, __request__: Optional[Any] = None, ) -> Optional[dict]: print(f"action:{__name__} triggered") if not __event_emitter__: return body # Get the last user message messages = body.get("messages", []) if not messages: return body # Usually the action is triggered on the last message target_message = messages[-1]["content"] # Check text length text_length = len(target_message) if text_length < self.valves.min_text_length: if __event_emitter__: await __event_emitter__( { "type": "notification", "data": { "type": "warning", "content": f"文本过短({text_length}字符),建议至少{self.valves.min_text_length}字符。", }, } ) return body if text_length > self.valves.max_text_length: if __event_emitter__: await __event_emitter__( { "type": "notification", "data": { "type": "info", "content": f"文本较长({text_length}字符),建议使用'墨海拾贝'进行深度分析。", }, } ) # Notify user that we are generating the card if self.valves.show_status: await __event_emitter__( { "type": "notification", "data": { "type": "info", "content": "⚡ 正在生成闪记卡...", }, } ) try: # 1. Extract information using LLM user_id = __user__.get("id") if __user__ else "default" user_obj = Users.get_user_by_id(user_id) model = self.valves.model_id if self.valves.model_id else body.get("model") system_prompt = f""" 你是一个闪记卡生成专家,专注于创建适合学习和记忆的知识卡片。你的任务是将文本提炼成简洁、易记的学习卡片。 请提取以下字段,并以 JSON 格式返回: 1. "title": 创建一个简短、精准的标题(6-12 字),突出核心概念 2. "summary": 用一句话总结核心要义(20-40 字),要通俗易懂、便于记忆 3. "key_points": 列出 3-5 个关键记忆点(每个 10-20 字) - 每个要点应该是独立的知识点 - 使用简洁、口语化的表达 - 避免冗长的句子 4. "tags": 列出 2-4 个分类标签(每个 2-5 字) 5. "category": 选择一个主分类(如:概念、技能、事实、方法等) 目标语言: {self.valves.language} 重要原则: - **极简主义**: 每个要点都要精炼到极致 - **记忆优先**: 内容要便于记忆和回忆 - **核心聚焦**: 只提取最核心的知识点 - **口语化**: 使用通俗易懂的语言 - 只返回 JSON 对象,不要包含 markdown 格式 """ prompt = f"请将以下文本提炼成一张学习记忆卡片:\n\n{target_message}" payload = { "model": model, "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], "stream": False, } response = await generate_chat_completion(__request__, payload, user_obj) content = response["choices"][0]["message"]["content"] # Parse JSON try: # simple cleanup in case of markdown code blocks if "```json" in content: content = content.split("```json")[1].split("```")[0].strip() elif "```" in content: content = content.split("```")[1].split("```")[0].strip() card_data = json.loads(content) except Exception as e: logger.error(f"Failed to parse JSON: {e}, content: {content}") if self.valves.show_status: await __event_emitter__( { "type": "notification", "data": { "type": "error", "content": "生成卡片数据失败,请重试。", }, } ) return body # 2. Generate HTML html_card = self.generate_html_card(card_data) # 3. Append to message # We append it to the user message so it shows up as part of the interaction # Or we can append it to the assistant response if we were a Pipe, but this is an Action. # Actions usually modify the input or trigger a side effect. # To show the card, we can append it to the message content. html_embed_tag = f"```html\n{html_card}\n```" body["messages"][-1]["content"] += f"\n\n{html_embed_tag}" if self.valves.show_status: await __event_emitter__( { "type": "notification", "data": { "type": "success", "content": "⚡ 闪记卡生成成功!", }, } ) return body except Exception as e: logger.error(f"Error generating knowledge card: {e}") if self.valves.show_status: await __event_emitter__( { "type": "notification", "data": { "type": "error", "content": f"生成知识卡片时出错: {str(e)}", }, } ) return body def generate_html_card(self, data): # Enhanced CSS with premium styling style = """ """ # Enhanced HTML structure html = f"""
{style}