""" title: Flash Card author: Fu-Jie author_url: https://github.com/Fu-Jie funding_url: https://github.com/Fu-Jie/awesome-openwebui 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="Model ID used for generating card content. If empty, uses the current model.", ) min_text_length: int = Field( default=50, description="Minimum text length required to generate a flashcard (characters).", ) max_text_length: int = Field( default=2000, description="Recommended maximum text length. For longer texts, deep analysis tools are recommended.", ) language: str = Field( default="en", description="Target language for card content (e.g., 'en', 'zh').", ) show_status: bool = Field( default=True, description="Whether to show status updates in the chat interface.", ) 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 too short ({text_length} chars), recommended at least {self.valves.min_text_length} chars.", }, } ) return body if text_length > self.valves.max_text_length: if __event_emitter__: await __event_emitter__( { "type": "notification", "data": { "type": "info", "content": f"Text quite long ({text_length} chars), consider using 'Deep Reading' for deep analysis.", }, } ) # Notify user that we are generating the card if self.valves.show_status: await __event_emitter__( { "type": "notification", "data": { "type": "info", "content": "⚡ Generating Flash Card...", }, } ) 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""" You are a Flash Card Generation Expert, specializing in creating knowledge cards suitable for learning and memorization. Your task is to distill text into concise, easy-to-remember flashcards. Please extract the following fields and return them in JSON format: 1. "title": Create a short, precise title (3-8 words), highlighting the core concept. 2. "summary": Summarize the core essence in one sentence (10-25 words), making it easy to understand and remember. 3. "key_points": List 3-5 key memory points (5-15 words each). - Each point should be an independent knowledge unit. - Use concise, conversational expression. - Avoid long sentences. 4. "tags": List 2-4 classification tags (1-3 words each). 5. "category": Choose a main category (e.g., Concept, Skill, Fact, Method, etc.). Target Language: {self.valves.language} Important Principles: - **Minimalism**: Refine each point to the extreme. - **Memory First**: Content should be easy to memorize and recall. - **Core Focus**: Extract only the most core knowledge points. - **Conversational**: Use easy-to-understand language. - Return ONLY the JSON object, do not include markdown formatting. """ prompt = f"Please refine the following text into a learning flashcard:\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": "Failed to generate card data, please try again.", }, } ) return body # 2. Generate HTML html_card = self.generate_html_card(card_data) # 3. Append to message 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": "⚡ Flash Card generated successfully!", }, } ) 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"Error generating knowledge card: {str(e)}", }, } ) return body def generate_html_card(self, data): # Enhanced CSS with premium styling style = """ """ # Enhanced HTML structure html = f""" {style}
{data.get('category', 'General Knowledge')}

{data.get('title', 'Flash Card')}

{data.get('summary', '')}
Key Points
    {''.join([f'
  • {point}
  • ' for point in data.get('key_points', [])])}
""" return html