diff --git a/docs/development/frontend-console-debugging.md b/docs/development/frontend-console-debugging.md new file mode 100644 index 0000000..daf67b5 --- /dev/null +++ b/docs/development/frontend-console-debugging.md @@ -0,0 +1,150 @@ +# 🛠️ Debugging Python Plugins with Frontend Console + +When developing plugins for Open WebUI, debugging can be challenging. Standard `print()` statements or server-side logging might not always be accessible, especially in hosted environments or when you want to see the data flow in real-time alongside the UI interactions. + +This guide introduces a powerful technique: **Frontend Console Debugging**. By injecting JavaScript from your Python plugin, you can print structured logs directly to the browser's Developer Tools console (F12). + +## Why Frontend Debugging? + +* **Real-time Feedback**: See logs immediately as actions happen in the browser. +* **Rich Objects**: Inspect complex JSON objects (like `body` or `messages`) interactively, rather than reading massive text dumps. +* **No Server Access Needed**: Debug issues even if you don't have SSH/Console access to the backend server. +* **Clean Output**: Group logs using `console.group()` to keep your console organized. + +## The Core Mechanism + +Open WebUI plugins (both Actions and Filters) support an event system. We can leverage the `__event_call__` (or sometimes `__event_emitter__`) to send a special event of type `execute`. This tells the frontend to run the provided JavaScript code. + +### The Helper Method + +To make this easy to use, we recommend adding a helper method `_emit_debug_log` to your plugin class. + +```python +import json +from typing import List + +async def _emit_debug_log( + self, + __event_call__, + title: str, + data: dict +): + """ + Emit debug log to browser console via JS execution. + + Args: + __event_call__: The event callable passed to action/outlet. + title: A title for the log group. + data: A dictionary of data to log. + """ + # 1. Check if debugging is enabled (recommended) + if not getattr(self.valves, "show_debug_log", True) or not __event_call__: + return + + try: + # 2. Construct the JavaScript code + # We use an async IIFE (Immediately Invoked Function Expression) + # to ensure a clean scope and support await if needed. + js_code = f""" + (async function() {{ + console.group("🛠️ Plugin Debug: {title}"); + console.log({json.dumps(data, ensure_ascii=False)}); + console.groupEnd(); + }})(); + """ + + # 3. Send the execute event + await __event_call__( + { + "type": "execute", + "data": {"code": js_code}, + } + ) + except Exception as e: + print(f"Error emitting debug log: {e}") +``` + +## Implementation Steps + +### 1. Add a Valve for Control + +It's best practice to make debugging optional so it doesn't clutter the console for normal users. + +```python +from pydantic import BaseModel, Field + +class Filter: + class Valves(BaseModel): + show_debug_log: bool = Field( + default=False, + description="Print debug logs to browser console (F12)" + ) + + def __init__(self): + self.valves = self.Valves() +``` + +### 2. Inject `__event_call__` + +Ensure your `action` (for Actions) or `outlet` (for Filters) method accepts `__event_call__`. + +**For Filters (`outlet`):** + +```python +async def outlet( + self, + body: dict, + __user__: Optional[dict] = None, + __event_call__=None, # <--- Add this + __metadata__: Optional[dict] = None, +) -> dict: +``` + +**For Actions (`action`):** + +```python +async def action( + self, + body: dict, + __user__=None, + __event_call__=None, # <--- Add this + __request__=None, +): +``` + +### 3. Call the Helper + +Now you can log anything, anywhere in your logic! + +```python +# Inside your logic... +new_content = self.process_content(content) + +# Log the before and after +await self._emit_debug_log( + __event_call__, + "Content Normalization", + { + "original": content, + "processed": new_content, + "changes": diff_list + } +) +``` + +## Best Practices + +1. **Use `json.dumps`**: Always serialize your Python dictionaries to JSON strings before embedding them in the f-string. This handles escaping quotes and special characters correctly. +2. **Async IIFE**: Wrapping your JS in `(async function() { ... })();` is safer than raw code. It prevents variable collisions with other scripts and allows using `await` inside your debug script if you ever need to check DOM elements. +3. **Check for None**: Always check if `__event_call__` is not None before using it, as it might not be available in all contexts (e.g., when running tests or in older Open WebUI versions). + +## Example Output + +When enabled, your browser console will show: + +```text +> 🛠️ Plugin Debug: Content Normalization + > {original: "...", processed: "...", changes: [...]} +``` + +You can expand the object to inspect every detail of your data. Happy debugging! diff --git a/docs/development/mermaid-syntax-standards.md b/docs/development/mermaid-syntax-standards.md new file mode 100644 index 0000000..1242f79 --- /dev/null +++ b/docs/development/mermaid-syntax-standards.md @@ -0,0 +1,64 @@ +# Mermaid Syntax Standards & Best Practices + +This document summarizes the official syntax standards for Mermaid flowcharts, focusing on node labels, quoting rules, and special character handling. It serves as a reference for the `markdown_normalizer` plugin logic. + +## 1. Node Shapes & Syntax + +Mermaid supports various node shapes defined by specific wrapping characters. + +| Shape | Syntax | Example | +| :--- | :--- | :--- | +| **Rectangle** (Default) | `id[Label]` | `A[Start]` | +| **Rounded** | `id(Label)` | `B(Process)` | +| **Stadium** (Pill) | `id([Label])` | `C([End])` | +| **Subroutine** | `id[[Label]]` | `D[[Subroutine]]` | +| **Cylinder** (Database) | `id[(Label)]` | `E[(Database)]` | +| **Circle** | `id((Label))` | `F((Point))` | +| **Double Circle** | `id(((Label)))` | `G(((Endpoint)))` | +| **Asymmetric** | `id>Label]` | `H>Flag]` | +| **Rhombus** (Decision) | `id{Label}` | `I{Decision}` | +| **Hexagon** | `id{{Label}}` | `J{{Prepare}}` | +| **Parallelogram** | `id[/Label/]` | `K[/Input/]` | +| **Parallelogram Alt** | `id[\Label\]` | `L[\Output\]` | +| **Trapezoid** | `id[/Label\]` | `M[/Trap/]` | +| **Trapezoid Alt** | `id[\Label/]` | `N[\TrapAlt/]` | + +## 2. Quoting Rules (Critical) + +### Why Quote? +Quoting node labels is **highly recommended** and sometimes **mandatory** to prevent syntax errors. + +### Mandatory Quoting Scenarios +You **MUST** enclose labels in double quotes `"` if they contain: +1. **Special Characters**: `()`, `[]`, `{}`, `;`, `"`, etc. +2. **Keywords**: Words like `end`, `subgraph`, etc., if used in specific contexts. +3. **Unicode/Emoji**: While often supported without quotes, quoting ensures consistent rendering across different environments. +4. **Markdown**: If you want to use Markdown formatting (bold, italic) inside a label. + +### Best Practice: Always Quote +To ensure robustness, especially when processing LLM-generated content which may contain unpredictable characters, **always enclosing labels in double quotes is the safest strategy**. + +**Examples:** +* ❌ Risky: `id(Start: 15:00)` (Colon might be interpreted as style separator) +* ✅ Safe: `id("Start: 15:00")` +* ❌ Broken: `id(Func(x))` (Nested parentheses break parsing) +* ✅ Safe: `id("Func(x)")` + +## 3. Escape Characters + +Inside a quoted string: +* Double quotes `"` must be escaped as `\"`. +* HTML entities (e.g., `#35;` for `#`) can be used. + +## 4. Plugin Logic Verification + +The `markdown_normalizer` plugin implements the following logic: + +1. **Detection**: Identifies Mermaid node definitions using a comprehensive regex covering all shapes above. +2. **Normalization**: + * Checks if the label is already quoted. + * If **NOT quoted**, it wraps the label in double quotes `""`. + * Escapes any existing double quotes inside the label (`"` -> `\"`). +3. **Shape Preservation**: The regex captures the specific opening and closing delimiters (e.g., `((` and `))`) to ensure the node shape is strictly preserved during normalization. + +**Conclusion**: The plugin's behavior of automatically adding quotes to unquoted labels is **fully aligned with Mermaid's official best practices** for robustness and error prevention.