docs: add frontend console debugging guide and mermaid syntax standards
This commit is contained in:
150
docs/development/frontend-console-debugging.md
Normal file
150
docs/development/frontend-console-debugging.md
Normal file
@@ -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!
|
||||||
64
docs/development/mermaid-syntax-standards.md
Normal file
64
docs/development/mermaid-syntax-standards.md
Normal file
@@ -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.
|
||||||
Reference in New Issue
Block a user