feat(project): sync engineering standards and finalize markdown-normalizer v1.2.7
- Update .github/copilot-instructions.md with latest i18n and naming standards - Add docs/development/issue-reply-guide.md for professional community engagement - Sync all documentation (MKDocs, READMEs, Docs) to v1.2.7 - Include CI/CD and Agent instruction templates for better automation
This commit is contained in:
147
.agent/rules/antigravity.md
Normal file
147
.agent/rules/antigravity.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
---
|
||||||
|
description: >
|
||||||
|
Antigravity development mode rules. Apply when the user requests high-speed iteration,
|
||||||
|
a quick prototype, or says "反重力开发" / "antigravity mode".
|
||||||
|
globs: "plugins/**/*.py"
|
||||||
|
always_on: false
|
||||||
|
---
|
||||||
|
# Antigravity Development Mode
|
||||||
|
|
||||||
|
> High-speed delivery + strict reversibility. Every decision must keep both roll-forward and rollback feasible.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Principles
|
||||||
|
|
||||||
|
1. **Small, isolated edits** — one logical change per operation; no mixing refactor + feature.
|
||||||
|
2. **Deterministic interfaces** — function signatures and return shapes must not change without an explicit contract update.
|
||||||
|
3. **Multi-level fallback** — every I/O path has a degraded alternative (e.g., S3 → local → DB).
|
||||||
|
4. **Reversible by default** — every file write, API call, or schema change must have an undo path recorded or be idempotent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mandatory Safety Patterns
|
||||||
|
|
||||||
|
### 1. Timeout Guards on All Frontend Calls
|
||||||
|
|
||||||
|
Any `__event_call__` or `__event_emitter__` JS execution MUST be wrapped:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = await asyncio.wait_for(
|
||||||
|
__event_call__({"type": "execute", "data": {"code": js_code}}),
|
||||||
|
timeout=2.0
|
||||||
|
)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.warning("Frontend JS execution timed out; falling back.")
|
||||||
|
result = fallback_value
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Frontend call failed: {e}", exc_info=True)
|
||||||
|
result = fallback_value
|
||||||
|
```
|
||||||
|
|
||||||
|
JS side must also guard internally:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
try {
|
||||||
|
return (localStorage.getItem('locale') || navigator.language || 'en-US');
|
||||||
|
} catch (e) {
|
||||||
|
return 'en-US';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Path Sandbox Validation
|
||||||
|
|
||||||
|
Resolve every workspace path and verify it stays inside the repo root:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import os
|
||||||
|
|
||||||
|
def _validate_workspace_path(path: str, workspace_root: str) -> str:
|
||||||
|
resolved = os.path.realpath(os.path.abspath(path))
|
||||||
|
root = os.path.realpath(workspace_root)
|
||||||
|
if not resolved.startswith(root + os.sep) and resolved != root:
|
||||||
|
raise PermissionError(f"Path escape detected: {resolved} is outside {root}")
|
||||||
|
return resolved
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Dual-Channel Upload Fallback
|
||||||
|
|
||||||
|
Always try API first; fall back to DB/local on failure:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def _upload_file(self, filename: str, content: bytes) -> str:
|
||||||
|
# Channel 1: API upload (S3-compatible)
|
||||||
|
try:
|
||||||
|
url = await self._api_upload(filename, content)
|
||||||
|
if url:
|
||||||
|
return url
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"API upload failed: {e}; falling back to local.")
|
||||||
|
|
||||||
|
# Channel 2: Local file + DB registration
|
||||||
|
return await self._local_db_upload(filename, content)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Progressive Status Reporting
|
||||||
|
|
||||||
|
For tasks > 3 seconds, emit staged updates:
|
||||||
|
|
||||||
|
```python
|
||||||
|
await self._emit_status(emitter, "正在分析内容...", done=False)
|
||||||
|
# ... phase 1 ...
|
||||||
|
await self._emit_status(emitter, "正在生成输出...", done=False)
|
||||||
|
# ... phase 2 ...
|
||||||
|
await self._emit_status(emitter, "完成", done=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
Always emit `done=True` on completion and `notification(error)` on failure.
|
||||||
|
|
||||||
|
### 5. Emitter Guard
|
||||||
|
|
||||||
|
Check before every emit to prevent crashes on missing emitter:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if emitter and self.valves.SHOW_STATUS:
|
||||||
|
await emitter({"type": "status", "data": {"description": msg, "done": done}})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Exception Surface Rule
|
||||||
|
|
||||||
|
Never swallow exceptions silently. Every `except` block must:
|
||||||
|
|
||||||
|
- Log to backend: `logger.error(f"...: {e}", exc_info=True)`
|
||||||
|
- Notify user: `await self._emit_notification(emitter, f"处理失败: {str(e)}", "error")`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Edit Discipline
|
||||||
|
|
||||||
|
| ✅ DO | ❌ DO NOT |
|
||||||
|
|-------|-----------|
|
||||||
|
| One function / one Valve / one method per edit | Mix multiple unrelated changes in one operation |
|
||||||
|
| Validate input at the function boundary | Assume upstream data is well-formed |
|
||||||
|
| Return early on invalid state | Nest complex logic beyond 3 levels |
|
||||||
|
| Check fallback availability before primary path | Assume primary path always succeeds |
|
||||||
|
| Log before and after expensive I/O | Skip logging for "obvious" operations |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Checklist
|
||||||
|
|
||||||
|
Before completing an antigravity operation, confirm:
|
||||||
|
|
||||||
|
- [ ] No global state mutated on `self` (filter singleton rule)
|
||||||
|
- [ ] File writes are atomic or can be recreated
|
||||||
|
- [ ] Database changes are idempotent (safe to re-run)
|
||||||
|
- [ ] Timeout guards are in place for all async calls to external systems
|
||||||
|
- [ ] The user can observe progress through status/notification events
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Full engineering spec: `.github/copilot-instructions.md` → Section: **Antigravity Development Mode**
|
||||||
|
- Design document: `docs/development/copilot-engineering-plan.md` → Section 5
|
||||||
62
.github/agents/plugin-implementer.agent.md
vendored
Normal file
62
.github/agents/plugin-implementer.agent.md
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Implementer
|
||||||
|
description: Implement OpenWebUI plugin and docs updates with strict project standards
|
||||||
|
argument-hint: Provide approved plan or feature request to implement
|
||||||
|
tools: ['execute/getTerminalOutput', 'execute/runInTerminal', 'read/readFile', 'read/terminalSelection', 'read/terminalLastCommand', 'edit/createFile', 'edit/editFiles', 'search', 'web', 'web/fetch', 'web/githubRepo', 'agent']
|
||||||
|
infer: true
|
||||||
|
handoffs:
|
||||||
|
- label: Run Review
|
||||||
|
agent: Plugin Reviewer
|
||||||
|
prompt: Review the implementation for i18n, safety, and consistency issues.
|
||||||
|
send: false
|
||||||
|
---
|
||||||
|
You are the **implementation specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
## Execution Rules
|
||||||
|
1. **Minimal diffs**: Change only what the approved plan specifies.
|
||||||
|
2. **Single-file i18n**: Every plugin is one `.py` file with built-in `TRANSLATIONS` dict. Never create `_cn.py` split files.
|
||||||
|
3. **Context helpers**: Always use `_get_user_context(__user__)` and `_get_chat_context(body, __metadata__)` — never access dict keys directly.
|
||||||
|
4. **Emitter guards**: Every `await emitter(...)` must be guarded by `if emitter:`.
|
||||||
|
5. **Logging**: Use `logging.getLogger(__name__)` — no bare `print()` in production code.
|
||||||
|
6. **Async safety**: Wrap all `__event_call__` with `asyncio.wait_for(..., timeout=2.0)` + inner JS `try { ... } catch(e) { return fallback; }`.
|
||||||
|
|
||||||
|
## Required Plugin Pattern
|
||||||
|
```python
|
||||||
|
# Docstring: title, author, author_url, funding_url, version, description
|
||||||
|
# icon_url is REQUIRED for Action plugins (Lucide SVG, base64)
|
||||||
|
|
||||||
|
class Action: # or Filter / Pipe
|
||||||
|
class Valves(BaseModel):
|
||||||
|
SHOW_STATUS: bool = Field(default=True, description="...")
|
||||||
|
# All fields UPPER_SNAKE_CASE
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.valves = self.Valves()
|
||||||
|
|
||||||
|
def _get_user_context(self, __user__): ... # always implement
|
||||||
|
def _get_chat_context(self, body, __metadata__=None): ... # always implement
|
||||||
|
async def _emit_status(self, emitter, description, done=False): ...
|
||||||
|
async def _emit_notification(self, emitter, content, ntype="info"): ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known Split-File Plugins (Legacy — Do NOT Add More)
|
||||||
|
These still have `_cn.py` files. When touching any of them, migrate CN content into `TRANSLATIONS` dict:
|
||||||
|
- `plugins/actions/deep-dive/deep_dive_cn.py`
|
||||||
|
- `plugins/actions/export_to_docx/export_to_word_cn.py`
|
||||||
|
- `plugins/actions/export_to_excel/export_to_excel_cn.py`
|
||||||
|
- `plugins/actions/flash-card/flash_card_cn.py`
|
||||||
|
- `plugins/actions/infographic/infographic_cn.py`
|
||||||
|
- `plugins/filters/folder-memory/folder_memory_cn.py`
|
||||||
|
|
||||||
|
## Version Bump Rule
|
||||||
|
Only bump version when user explicitly says "发布" / "release" / "bump version".
|
||||||
|
When bumping, update ALL 7+ files (code docstring + 2× README + 2× doc detail + 2× doc index + 2× root README date badge).
|
||||||
|
|
||||||
|
## Git Policy
|
||||||
|
- Never run `git commit`, `git push`, or create PRs automatically.
|
||||||
|
- After all edits, list what changed and why, then stop.
|
||||||
|
|
||||||
|
## Completion Output
|
||||||
|
- Modified files (full relative paths, one-line descriptions)
|
||||||
|
- Remaining manual checks
|
||||||
|
- Suggested handoff to **Plugin Reviewer**
|
||||||
78
.github/agents/plugin-planner.agent.md
vendored
Normal file
78
.github/agents/plugin-planner.agent.md
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Planner
|
||||||
|
description: Analyze requirements and produce a safe implementation plan for OpenWebUI plugins
|
||||||
|
argument-hint: Describe the plugin goal, constraints, and target files
|
||||||
|
tools: ['read/readFile', 'search', 'web', 'web/githubRepo', 'read/terminalLastCommand', 'read/terminalSelection', 'agent']
|
||||||
|
infer: true
|
||||||
|
handoffs:
|
||||||
|
- label: Start Implementation
|
||||||
|
agent: Plugin Implementer
|
||||||
|
prompt: Implement the approved plan step by step with minimal diffs.
|
||||||
|
send: false
|
||||||
|
---
|
||||||
|
You are the **planning specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
## Your Responsibilities
|
||||||
|
1. Read existing plugin code and docs **before** proposing any change.
|
||||||
|
2. Produce a **small, reversible** plan (one logical change per file per step).
|
||||||
|
3. Clearly list all impacted files including the docs sync chain.
|
||||||
|
4. Flag risks: breaking changes, release implications, version bumps needed.
|
||||||
|
|
||||||
|
## Hard Rules
|
||||||
|
- Never propose `git commit`, `git push`, or PR creation.
|
||||||
|
- Every plan must end with an acceptance checklist for the user to approve before handing off.
|
||||||
|
- Reference `.github/copilot-instructions.md` as the authoritative spec.
|
||||||
|
|
||||||
|
## Repository Plugin Inventory
|
||||||
|
|
||||||
|
### Actions (`plugins/actions/`)
|
||||||
|
| Dir | Main file | Version | i18n status |
|
||||||
|
|-----|-----------|---------|-------------|
|
||||||
|
| `deep-dive` | `deep_dive.py` | 1.0.0 | ⚠️ has `deep_dive_cn.py` split |
|
||||||
|
| `export_to_docx` | `export_to_word.py` | 0.4.4 | ⚠️ has `export_to_word_cn.py` split |
|
||||||
|
| `export_to_excel` | `export_to_excel.py` | 0.3.7 | ⚠️ has `export_to_excel_cn.py` split |
|
||||||
|
| `flash-card` | `flash_card.py` | 0.2.4 | ⚠️ has `flash_card_cn.py` split |
|
||||||
|
| `infographic` | `infographic.py` | 1.5.0 | ⚠️ has `infographic_cn.py` split |
|
||||||
|
| `smart-mind-map` | `smart_mind_map.py` | 1.0.0 | ✅ single file |
|
||||||
|
| `smart-mermaid` | _(empty stub)_ | — | — |
|
||||||
|
|
||||||
|
### Filters (`plugins/filters/`)
|
||||||
|
| Dir | Main file | Version | i18n status |
|
||||||
|
|-----|-----------|---------|-------------|
|
||||||
|
| `async-context-compression` | `async_context_compression.py` | 1.3.0 | ✅ |
|
||||||
|
| `context_enhancement_filter` | `context_enhancement_filter.py` | 0.3 | ⚠️ non-SemVer version |
|
||||||
|
| `copilot_files_preprocessor` | _(empty stub)_ | — | — |
|
||||||
|
| `folder-memory` | `folder_memory.py` | 0.1.0 | ⚠️ has `folder_memory_cn.py` split |
|
||||||
|
| `github_copilot_sdk_files_filter` | `github_copilot_sdk_files_filter.py` | 0.1.2 | ✅ |
|
||||||
|
| `markdown_normalizer` | `markdown_normalizer.py` | 1.2.4 | ✅ |
|
||||||
|
| `web_gemini_multimodel_filter` | `web_gemini_multimodel.py` | 0.3.2 | ✅ |
|
||||||
|
|
||||||
|
### Pipes / Pipelines / Tools
|
||||||
|
| Path | Main file | Version |
|
||||||
|
|------|-----------|---------|
|
||||||
|
| `pipes/github-copilot-sdk` | `github_copilot_sdk.py` | 0.7.0 |
|
||||||
|
| `pipelines/moe_prompt_refiner` | `moe_prompt_refiner.py` | — |
|
||||||
|
| `tools/workspace-file-manager` | `workspace_file_manager.py` | 0.2.0 |
|
||||||
|
|
||||||
|
## Naming Conventions (Actual Mix)
|
||||||
|
- Action dirs: some use **dashes** (`deep-dive`, `flash-card`, `smart-mind-map`), some **underscores** (`export_to_docx`, `export_to_excel`, `infographic`)
|
||||||
|
- Filter dirs: similarly mixed — prefer underscores for new plugins
|
||||||
|
- Main `.py` filenames always use **underscores**
|
||||||
|
|
||||||
|
## Docs Sync Chain (for every plugin change)
|
||||||
|
For `plugins/{type}/{name}/`, these 7+ files must stay in sync:
|
||||||
|
1. `plugins/{type}/{name}/{name}.py` — version in docstring
|
||||||
|
2. `plugins/{type}/{name}/README.md` — version + What's New
|
||||||
|
3. `plugins/{type}/{name}/README_CN.md` — version + 最新更新
|
||||||
|
4. `docs/plugins/{type}/{name}.md`
|
||||||
|
5. `docs/plugins/{type}/{name}.zh.md`
|
||||||
|
6. `docs/plugins/{type}/index.md` — version badge
|
||||||
|
7. `docs/plugins/{type}/index.zh.md` — version badge
|
||||||
|
8. Root `README.md` / `README_CN.md` — date badge
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
- **Scope summary**
|
||||||
|
- **Affected files** (full relative paths)
|
||||||
|
- **Step-by-step plan** (numbered, ≤10 steps)
|
||||||
|
- **Risk flags** (version bump? breaking change? split-file migration needed?)
|
||||||
|
- **Acceptance checklist** → user must approve before handoff to Implementer
|
||||||
71
.github/agents/plugin-reviewer.agent.md
vendored
Normal file
71
.github/agents/plugin-reviewer.agent.md
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Reviewer
|
||||||
|
description: Perform strict repository-aligned code review for OpenWebUI plugin changes
|
||||||
|
argument-hint: Share changed files or branch diff to review
|
||||||
|
tools: ['search', 'read/readFile', 'web', 'web/fetch', 'web/githubRepo', 'execute/getTerminalOutput', 'read/terminalLastCommand', 'read/terminalSelection']
|
||||||
|
infer: true
|
||||||
|
handoffs:
|
||||||
|
- label: Prepare Release Draft
|
||||||
|
agent: Release Prep
|
||||||
|
prompt: Create a bilingual release draft and commit message based on reviewed changes.
|
||||||
|
send: false
|
||||||
|
---
|
||||||
|
You are the **review specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
Full review rules are in .github/instructions/code-review.instructions.md.
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
|
||||||
|
### 🔴 Blocking (must fix before release)
|
||||||
|
|
||||||
|
**1. Single-file i18n Architecture**
|
||||||
|
- [ ] No new `_cn.py` split files created.
|
||||||
|
- [ ] All user-visible strings go through `TRANSLATIONS[lang].get(key, fallback)`.
|
||||||
|
- [ ] `FALLBACK_MAP` covers at least `zh → zh-CN` and `en → en-US`.
|
||||||
|
- [ ] `format(**kwargs)` on translations wrapped in `try/except KeyError`.
|
||||||
|
|
||||||
|
**2. Context Helpers**
|
||||||
|
- [ ] Uses `_get_user_context(__user__)` (not `__user__["name"]` directly).
|
||||||
|
- [ ] Uses `_get_chat_context(body, __metadata__)` (not ad-hoc `body.get("chat_id")`).
|
||||||
|
|
||||||
|
**3. Antigravity Safety**
|
||||||
|
- [ ] Every `__event_call__` wrapped: `asyncio.wait_for(..., timeout=2.0)`.
|
||||||
|
- [ ] JS code passed to `__event_call__` has `try { ... } catch(e) { return fallback; }`.
|
||||||
|
- [ ] File path operations validated against workspace root (no traversal).
|
||||||
|
- [ ] Upload paths have dual-channel fallback (API → DB/local).
|
||||||
|
|
||||||
|
**4. Emitter Guards**
|
||||||
|
- [ ] Every `await emitter(...)` guarded by `if emitter:`.
|
||||||
|
- [ ] `_emit_status(done=False)` on start, `done=True` on success, `_emit_notification("error")` on failure.
|
||||||
|
- [ ] No bare `print()` — use `logging.getLogger(__name__)`.
|
||||||
|
|
||||||
|
**5. Filter Singleton Safety**
|
||||||
|
- [ ] No mutable per-request state stored on `self` in Filter plugins.
|
||||||
|
|
||||||
|
**6. Streaming Compatibility (OpenWebUI 0.8.x)**
|
||||||
|
- [ ] `</think>` tag closed before any normal text or tool cards.
|
||||||
|
- [ ] `<details type="tool_calls" ...>` attributes escape `"` as `"`.
|
||||||
|
- [ ] `<details ...>` block has newline immediately after `>`.
|
||||||
|
|
||||||
|
**7. Version & Docs Sync**
|
||||||
|
- [ ] Version bumped in docstring (if release).
|
||||||
|
- [ ] `README.md` + `README_CN.md` updated (What's New + version).
|
||||||
|
- [ ] `docs/plugins/{type}/{name}.md` and `.zh.md` match README.
|
||||||
|
- [ ] `docs/plugins/{type}/index.md` and `.zh.md` version badges updated.
|
||||||
|
- [ ] Root `README.md` / `README_CN.md` date badge updated.
|
||||||
|
|
||||||
|
### 🟡 Non-blocking (suggestions)
|
||||||
|
- Copilot SDK tools: `params_type=MyParams` in `define_tool()`.
|
||||||
|
- Long tasks (>3s): periodic `_emit_notification("info")` every 5s.
|
||||||
|
- `icon_url` present for Action plugins (Lucide SVG, base64).
|
||||||
|
|
||||||
|
## Known Pre-existing Issues (Don't block on unless the PR introduces new ones)
|
||||||
|
- `_cn.py` splits in: `deep-dive`, `export_to_docx`, `export_to_excel`, `flash-card`, `infographic`, `folder-memory` — legacy, not new.
|
||||||
|
- `context_enhancement_filter` version is `0.3` (non-SemVer) — pre-existing.
|
||||||
|
- `copilot_files_preprocessor` and `smart-mermaid` are empty stubs — pre-existing.
|
||||||
|
|
||||||
|
## Review Output
|
||||||
|
- **Blocking issues** (file:line references)
|
||||||
|
- **Non-blocking suggestions**
|
||||||
|
- **Pass / Fail verdict**
|
||||||
|
- **Next step**: Pass → handoff to Release Prep; Fail → return to Implementer with fix list
|
||||||
82
.github/agents/release-prep.agent.md
vendored
Normal file
82
.github/agents/release-prep.agent.md
vendored
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
name: Release Prep
|
||||||
|
description: Prepare release-ready summaries and Conventional Commit drafts without pushing
|
||||||
|
argument-hint: Provide final change list and target version (optional)
|
||||||
|
tools: ['search', 'read/readFile', 'web', 'web/fetch', 'web/githubRepo', 'execute/getTerminalOutput', 'read/terminalLastCommand', 'read/terminalSelection']
|
||||||
|
infer: true
|
||||||
|
---
|
||||||
|
You are the **release preparation specialist** for the `openwebui-extensions` repository.
|
||||||
|
|
||||||
|
Full commit message rules: .github/instructions/commit-message.instructions.md
|
||||||
|
Full release workflow: .agent/workflows/plugin-development.md
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
1. Generate a Conventional Commit message (English only).
|
||||||
|
2. Draft bilingual release notes (EN + 中文).
|
||||||
|
3. Verify ALL file sync locations are updated.
|
||||||
|
4. **Stop before any commit or push** — wait for explicit user confirmation.
|
||||||
|
|
||||||
|
## Commit Message Format
|
||||||
|
```text
|
||||||
|
type(scope): brief imperative description
|
||||||
|
|
||||||
|
- Key change 1
|
||||||
|
- Key change 2 (include migration note if needed)
|
||||||
|
```
|
||||||
|
- `type`: `feat` / `fix` / `docs` / `refactor` / `chore`
|
||||||
|
- `scope`: plugin folder name (e.g., `smart-mind-map`, `github-copilot-sdk`, `folder-memory`)
|
||||||
|
- Title ≤ 72 chars, imperative mood, no trailing period, no capital first letter
|
||||||
|
|
||||||
|
## 9-File Sync Checklist (fill in for each changed plugin)
|
||||||
|
```text
|
||||||
|
Plugin: {type}/{name} → v{new_version}
|
||||||
|
[ ] 1. plugins/{type}/{name}/{name}.py → version in docstring
|
||||||
|
[ ] 2. plugins/{type}/{name}/README.md → version + What's New
|
||||||
|
[ ] 3. plugins/{type}/{name}/README_CN.md → version + 最新更新
|
||||||
|
[ ] 4. docs/plugins/{type}/{name}.md → mirrors README
|
||||||
|
[ ] 5. docs/plugins/{type}/{name}.zh.md → mirrors README_CN
|
||||||
|
[ ] 6. docs/plugins/{type}/index.md → version badge updated
|
||||||
|
[ ] 7. docs/plugins/{type}/index.zh.md → version badge updated
|
||||||
|
[ ] 8. README.md (root) → date badge updated
|
||||||
|
[ ] 9. README_CN.md (root) → date badge updated
|
||||||
|
```
|
||||||
|
|
||||||
|
## Current Plugin Versions (as of last audit — 2026-02-23)
|
||||||
|
| Plugin | Type | Version | Note |
|
||||||
|
|--------|------|---------|------|
|
||||||
|
| deep-dive | action | 1.0.0 | has `_cn.py` split |
|
||||||
|
| export_to_docx | action | 0.4.4 | has `_cn.py` split |
|
||||||
|
| export_to_excel | action | 0.3.7 | has `_cn.py` split |
|
||||||
|
| flash-card | action | 0.2.4 | has `_cn.py` split |
|
||||||
|
| infographic | action | 1.5.0 | has `_cn.py` split |
|
||||||
|
| smart-mind-map | action | 1.0.0 | ✅ |
|
||||||
|
| async-context-compression | filter | 1.3.0 | ✅ |
|
||||||
|
| context_enhancement_filter | filter | 0.3 | ⚠️ non-SemVer |
|
||||||
|
| folder-memory | filter | 0.1.0 | has `_cn.py` split |
|
||||||
|
| github_copilot_sdk_files_filter | filter | 0.1.2 | ✅ |
|
||||||
|
| markdown_normalizer | filter | 1.2.4 | ✅ |
|
||||||
|
| web_gemini_multimodel_filter | filter | 0.3.2 | ✅ |
|
||||||
|
| github-copilot-sdk | pipe | 0.7.0 | ✅ |
|
||||||
|
| workspace-file-manager | tool | 0.2.0 | ✅ |
|
||||||
|
|
||||||
|
## Output Template
|
||||||
|
|
||||||
|
### Commit Message
|
||||||
|
```text
|
||||||
|
{type}({scope}): {description}
|
||||||
|
|
||||||
|
- {change 1}
|
||||||
|
- {change 2}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change Summary (EN)
|
||||||
|
- {bullet list of user-visible changes}
|
||||||
|
|
||||||
|
### 变更摘要(中文)
|
||||||
|
- {中文要点列表}
|
||||||
|
|
||||||
|
### Verification Status
|
||||||
|
{filled-in 9-file checklist for each changed plugin}
|
||||||
|
|
||||||
|
---
|
||||||
|
⚠️ **Waiting for user confirmation — no git operations will run until explicitly approved.**
|
||||||
94
.github/copilot-instructions.md
vendored
94
.github/copilot-instructions.md
vendored
@@ -205,19 +205,56 @@ class Action:
|
|||||||
#### 用户上下文 (User Context)
|
#### 用户上下文 (User Context)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def _get_user_context(self, __user__: Optional[Dict[str, Any]]) -> Dict[str, str]:
|
async def _get_user_context(
|
||||||
"""安全提取用户上下文信息。"""
|
self,
|
||||||
if isinstance(__user__, (list, tuple)):
|
__user__: Optional[dict],
|
||||||
user_data = __user__[0] if __user__ else {}
|
__event_call__: Optional[callable] = None,
|
||||||
elif isinstance(__user__, dict):
|
__request__: Optional[Request] = None,
|
||||||
user_data = __user__
|
) -> dict:
|
||||||
else:
|
"""
|
||||||
user_data = {}
|
Robust extraction of user context with multi-level fallback for language detection.
|
||||||
|
Priority: localStorage (via JS) > HTTP headers > User profile > en-US
|
||||||
|
"""
|
||||||
|
user_data = __user__ if isinstance(__user__, dict) else {}
|
||||||
|
user_id = user_data.get("id", "unknown_user")
|
||||||
|
user_name = user_data.get("name", "User")
|
||||||
|
user_language = user_data.get("language", "en-US")
|
||||||
|
|
||||||
|
# 1. Fallback: HTTP Accept-Language header
|
||||||
|
if __request__ and hasattr(__request__, "headers"):
|
||||||
|
accept_lang = __request__.headers.get("accept-language", "")
|
||||||
|
if accept_lang:
|
||||||
|
user_language = accept_lang.split(",")[0].split(";")[0]
|
||||||
|
|
||||||
|
# 2. Priority: Frontend localStorage via JS (requires timeout protection)
|
||||||
|
if __event_call__:
|
||||||
|
try:
|
||||||
|
js_code = """
|
||||||
|
try {
|
||||||
|
return (
|
||||||
|
document.documentElement.lang ||
|
||||||
|
localStorage.getItem('locale') ||
|
||||||
|
navigator.language ||
|
||||||
|
'en-US'
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return 'en-US';
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# MUST use wait_for with timeout (e.g., 2.0s) to prevent backend deadlock
|
||||||
|
frontend_lang = await asyncio.wait_for(
|
||||||
|
__event_call__({"type": "execute", "data": {"code": js_code}}),
|
||||||
|
timeout=2.0
|
||||||
|
)
|
||||||
|
if frontend_lang and isinstance(frontend_lang, str):
|
||||||
|
user_language = frontend_lang
|
||||||
|
except Exception:
|
||||||
|
pass # Fallback to existing language
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"user_id": user_data.get("id", "unknown_user"),
|
"user_id": user_id,
|
||||||
"user_name": user_data.get("name", "User"),
|
"user_name": user_name,
|
||||||
"user_language": user_data.get("language", "en-US"),
|
"user_language": user_language,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -569,25 +606,30 @@ async def _get_user_context(
|
|||||||
|
|
||||||
#### 实际使用 (Usage in Action/Filter)
|
#### 实际使用 (Usage in Action/Filter)
|
||||||
|
|
||||||
在 Action 或者 Filter 执行时引用这套上下文获取机制,然后传入映射器获取最终翻译:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def action(
|
async def action(self, body: dict, __user__: Optional[dict] = None, __event_call__: Optional[callable] = None, ...):
|
||||||
self,
|
user_ctx = await self._get_user_context(__user__, __event_call__)
|
||||||
body: dict,
|
lang = user_ctx["user_language"]
|
||||||
__user__: Optional[dict] = None,
|
|
||||||
__event_call__: Optional[callable] = None,
|
|
||||||
__request__: Optional[Request] = None,
|
|
||||||
**kwargs
|
|
||||||
) -> Optional[dict]:
|
|
||||||
|
|
||||||
user_ctx = await self._get_user_context(__user__, __event_call__, __request__)
|
# Use helper to get localized string with optional formatting
|
||||||
user_lang = user_ctx["user_language"]
|
msg = self._get_translation(lang, "status_starting", name=user_ctx["user_name"])
|
||||||
|
await self._emit_status(emitter, msg)
|
||||||
# 获取多语言文本 (通过你的 translation.get() 扩展)
|
|
||||||
# start_msg = self._get_translation(user_lang, "status_starting")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 提示词中的语言一致性 (Language Consistency in Prompts)
|
||||||
|
|
||||||
|
在为 LLM 生成系统提示词时,必须包含“输出语言与输入保持一致”的指令,以确保 i18n 逻辑在 AI 生成环节也不断裂。
|
||||||
|
|
||||||
|
**标准指令示例**:
|
||||||
|
- `Language`: All output must be in the exact same language as the input text provided by the user.
|
||||||
|
- `Format Consistency`: Even if this system prompt is in English, if the user input is in Chinese, your output must be in Chinese.
|
||||||
|
|
||||||
|
#### CJK 脚本的特殊考量 (CJK Script Considerations)
|
||||||
|
|
||||||
|
当涉及字符长度限制(如思维导图标题、卡片摘要)时,应区分 CJK(中日韩)和拉丁脚本。
|
||||||
|
- **CJK (zh, ja, ko)**: 通常应设置更小的字符数限制(如 10 字以内)。
|
||||||
|
- **Latin (en, es, fr)**: 可设置较长的字符限制(如 5-8 个词或 35 字符)。
|
||||||
|
|
||||||
### 9. 智能代理文件交付规范 (Agent File Delivery Standards)
|
### 9. 智能代理文件交付规范 (Agent File Delivery Standards)
|
||||||
|
|
||||||
在开发具备文件生成能力的智能代理插件(如 GitHub Copilot SDK 集成)时,必须遵循以下标准流程,以确保文件在不同存储后端(本地/S3)下的可用性并绕过不必要的 RAG 处理。
|
在开发具备文件生成能力的智能代理插件(如 GitHub Copilot SDK 集成)时,必须遵循以下标准流程,以确保文件在不同存储后端(本地/S3)下的可用性并绕过不必要的 RAG 处理。
|
||||||
|
|||||||
54
.github/instructions/code-review.instructions.md
vendored
Normal file
54
.github/instructions/code-review.instructions.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: Plugin Code Review
|
||||||
|
description: Comprehensive OpenWebUI plugin review checklist covering i18n, context helpers, antigravity safety, and streaming compatibility
|
||||||
|
applyTo: "plugins/**/*.py"
|
||||||
|
---
|
||||||
|
# Code Review Instructions — OpenWebUI Plugins
|
||||||
|
|
||||||
|
You are an expert Senior Software Engineer reviewing OpenWebUI plugins for the `openwebui-extensions` repository.
|
||||||
|
When reviewing plugin code, you MUST verify each point below to ensure the code meets the strict repository standards.
|
||||||
|
|
||||||
|
## 1. Single-file i18n Pattern (CRITICAL)
|
||||||
|
- **One File Rule**: One `.py` file per plugin. No `_cn.py` or language-split files.
|
||||||
|
- **Translations**: All user-visible strings (status, notification, UI text) MUST go through a `TRANSLATIONS` dictionary and a `FALLBACK_MAP`.
|
||||||
|
- **Safety**: `format(**kwargs)` calls on translated strings MUST be wrapped in `try/except KeyError` to prevent crashes if a translation is missing a placeholder.
|
||||||
|
|
||||||
|
## 2. Context Helpers (CRITICAL)
|
||||||
|
- **User Context**: MUST use `_get_user_context(__user__)` instead of direct `__user__["name"]` access. `__user__` can be a list, dict, or None.
|
||||||
|
- **Chat Context**: MUST use `_get_chat_context(body, __metadata__)` instead of ad-hoc `body.get("chat_id")` calls.
|
||||||
|
|
||||||
|
## 3. Event & Logging
|
||||||
|
- **No Print**: No bare `print()` in production code. Use `logging.getLogger(__name__)`.
|
||||||
|
- **Emitter Safety**: Every `await emitter(...)` call MUST be guarded by `if emitter:` (or equivalent).
|
||||||
|
- **Status Lifecycle**:
|
||||||
|
- `_emit_status(done=False)` at task start.
|
||||||
|
- `_emit_status(done=True)` on completion.
|
||||||
|
- `_emit_notification("error")` on failure.
|
||||||
|
|
||||||
|
## 4. Antigravity Safety (CRITICAL)
|
||||||
|
- **Timeout Guards**: All `__event_call__` JS executions MUST be wrapped with `asyncio.wait_for(..., timeout=2.0)`. Failure to do this can hang the entire backend.
|
||||||
|
- **JS Fallbacks**: JS code executed via `__event_call__` MUST have an internal `try { ... } catch (e) { return fallback; }` block.
|
||||||
|
- **Path Sandboxing**: File path operations MUST be validated against the workspace root (no directory traversal vulnerabilities).
|
||||||
|
- **Upload Fallbacks**: Upload paths MUST have a dual-channel fallback (API → local/DB).
|
||||||
|
|
||||||
|
## 5. Filter Singleton Safety
|
||||||
|
- **No Mutable State**: Filter plugins are singletons. There MUST be NO request-scoped mutable state stored on `self` (e.g., `self.current_user = ...`).
|
||||||
|
- **Statelessness**: Per-request values MUST be computed from `body` and context helpers on each call.
|
||||||
|
|
||||||
|
## 6. Copilot SDK Tools
|
||||||
|
- **Pydantic Models**: Tool parameters MUST be defined as a `pydantic.BaseModel` subclass.
|
||||||
|
- **Explicit Params**: `define_tool(...)` MUST include `params_type=MyParams`.
|
||||||
|
- **Naming**: Tool names MUST match `^[a-zA-Z0-9_-]+$` (ASCII only).
|
||||||
|
|
||||||
|
## 7. Streaming Compatibility (OpenWebUI 0.8.x)
|
||||||
|
- **Thinking Tags**: The `</think>` tag MUST be closed before any normal content or tool cards are emitted.
|
||||||
|
- **Attribute Escaping**: Tool card `arguments` and `result` attributes MUST escape `"` as `"`.
|
||||||
|
- **Newline Requirement**: The `<details type="tool_calls" ...>` block MUST be followed by a newline immediately after `>`.
|
||||||
|
|
||||||
|
## 8. Performance & Memory
|
||||||
|
- **Streaming**: Large files or responses MUST be streamed, not loaded entirely into memory.
|
||||||
|
- **Database Connections**: Plugins MUST reuse the OpenWebUI internal database connection (`owui_engine`, `owui_Session`) rather than creating new ones.
|
||||||
|
|
||||||
|
## 9. HTML Injection (Action Plugins)
|
||||||
|
- **Wrapper Template**: HTML output MUST use the standard `<!-- OPENWEBUI_PLUGIN_OUTPUT -->` wrapper.
|
||||||
|
- **Idempotency**: The plugin MUST implement `_remove_existing_html` to ensure multiple runs do not duplicate the HTML output.
|
||||||
90
.github/instructions/commit-message.instructions.md
vendored
Normal file
90
.github/instructions/commit-message.instructions.md
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
name: Commit Message
|
||||||
|
description: Comprehensive Conventional Commits guidelines for openwebui-extensions
|
||||||
|
---
|
||||||
|
# Commit Message Instructions
|
||||||
|
|
||||||
|
You are an expert developer generating commit messages for the `openwebui-extensions` repository.
|
||||||
|
Always adhere to the following strict guidelines based on the Conventional Commits specification.
|
||||||
|
|
||||||
|
## 1. General Rules
|
||||||
|
- **Language**: **English ONLY**. Never use Chinese or other languages in the commit title or body.
|
||||||
|
- **Structure**:
|
||||||
|
```text
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
<BLANK LINE>
|
||||||
|
<body>
|
||||||
|
<BLANK LINE>
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Type (`<type>`)
|
||||||
|
Must be one of the following:
|
||||||
|
- `feat`: A new feature or a new plugin.
|
||||||
|
- `fix`: A bug fix.
|
||||||
|
- `docs`: Documentation only changes (e.g., README, MkDocs).
|
||||||
|
- `refactor`: A code change that neither fixes a bug nor adds a feature.
|
||||||
|
- `perf`: A code change that improves performance.
|
||||||
|
- `test`: Adding missing tests or correcting existing tests.
|
||||||
|
- `chore`: Changes to the build process, CI/CD, or auxiliary tools/scripts.
|
||||||
|
- `style`: Changes that do not affect the meaning of the code (white-space, formatting, etc.).
|
||||||
|
|
||||||
|
## 3. Scope (`<scope>`)
|
||||||
|
The scope should indicate the affected component or plugin.
|
||||||
|
- **For Plugins**: Use the plugin's folder name or category (e.g., `actions`, `filters`, `pipes`, `export-to-docx`, `github-copilot-sdk`).
|
||||||
|
- **For Docs**: Use `docs` or the specific doc section (e.g., `guide`, `readme`).
|
||||||
|
- **For Infra**: Use `ci`, `scripts`, `deps`.
|
||||||
|
- *Note*: Omit the scope if the change is global or spans too many components.
|
||||||
|
|
||||||
|
## 4. Subject Line (`<subject>`)
|
||||||
|
- **Length**: Keep it under 50-72 characters.
|
||||||
|
- **Mood**: Use the imperative, present tense: "change" not "changed" nor "changes" (e.g., "add feature" instead of "added feature").
|
||||||
|
- **Formatting**: Do not capitalize the first letter. Do not place a period `.` at the end.
|
||||||
|
- **Clarity**: State exactly *what* changed. Avoid vague terms like "update code" or "fix bug".
|
||||||
|
|
||||||
|
## 5. Body (`<body>`)
|
||||||
|
- **When to use**: Highly recommended for `feat`, `fix`, and `refactor`.
|
||||||
|
- **Content**: Explain *what* and *why* (motivation), not just *how* (the code diff shows the how).
|
||||||
|
- **Format**: Use a bulleted list (1-3 points) for readability.
|
||||||
|
- **Repo Specifics**: If the commit involves a plugin version bump, explicitly mention that the READMEs and docs were synced.
|
||||||
|
|
||||||
|
## 6. Footer / Breaking Changes (`<footer>`)
|
||||||
|
- If the commit introduces a breaking change (e.g., changing a Valve configuration key, altering a plugin's output format), append `!` after the scope/type: `feat(pipes)!: ...`
|
||||||
|
- Add a `BREAKING CHANGE:` block in the footer explaining the migration path.
|
||||||
|
|
||||||
|
## 7. Examples
|
||||||
|
|
||||||
|
**Example 1: Feature with Body**
|
||||||
|
```text
|
||||||
|
feat(github-copilot-sdk): add antigravity timeout guards
|
||||||
|
|
||||||
|
- Wrap all __event_call__ JS executions with asyncio.wait_for(timeout=2.0)
|
||||||
|
- Add dual-channel upload fallback (API → local/DB)
|
||||||
|
- Emit staged status events for tasks > 3 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 2: Bug Fix**
|
||||||
|
```text
|
||||||
|
fix(export-to-docx): resolve missing title fallback
|
||||||
|
|
||||||
|
- Fallback to user_name + date when chat_title is empty
|
||||||
|
- Prevent crash when metadata variables are missing
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 3: Documentation Sync**
|
||||||
|
```text
|
||||||
|
docs(plugins): sync bilingual READMEs for v1.2.0 release
|
||||||
|
|
||||||
|
- Update What's New section for folder-memory plugin
|
||||||
|
- Sync docs/plugins/filters/folder-memory.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 4: Breaking Change**
|
||||||
|
```text
|
||||||
|
refactor(core)!: rename global configuration valves
|
||||||
|
|
||||||
|
- Standardize all valve keys to UPPER_SNAKE_CASE
|
||||||
|
- Remove deprecated legacy_api_key field
|
||||||
|
|
||||||
|
BREAKING CHANGE: Users must reconfigure their API keys in the UI as the old `api_key` field is no longer read.
|
||||||
|
```
|
||||||
54
.github/instructions/test-generation.instructions.md
vendored
Normal file
54
.github/instructions/test-generation.instructions.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
name: Test Generation
|
||||||
|
description: Comprehensive Pytest conventions, mocking strategies, and model usage rules for OpenWebUI plugins
|
||||||
|
applyTo: "plugins/debug/**,tests/**"
|
||||||
|
---
|
||||||
|
# Test Generation Instructions
|
||||||
|
|
||||||
|
You are an expert SDET (Software Development Engineer in Test) writing tests for the `openwebui-extensions` repository.
|
||||||
|
Follow these comprehensive guidelines to ensure robust, reliable, and cost-effective testing.
|
||||||
|
|
||||||
|
## 1. Test Structure & Placement
|
||||||
|
- **Unit Tests**: Place in the `tests/` directory at the root of the repository. Name files `test_<module>.py`.
|
||||||
|
- **Integration/Debug Scripts**: Place in `plugins/debug/<plugin_name>/`. Name files `debug_<feature>.py` or `inspect_<feature>.py`.
|
||||||
|
- **Fixtures**: Use `conftest.py` for shared fixtures (e.g., mock users, mock emitters, mock database sessions).
|
||||||
|
|
||||||
|
## 2. Model Usage & Cost Control (CRITICAL)
|
||||||
|
When writing tests or debug scripts that actually invoke an LLM:
|
||||||
|
- **Allowed Models**: You MUST use low-cost models: `gpt-5-mini` (Preferred) or `gpt-4.1`.
|
||||||
|
- **Forbidden**: NEVER use expensive models (like `gpt-4-turbo`, `claude-3-opus`) in automated tests unless explicitly requested by the user.
|
||||||
|
- **API Keys**: Never hardcode API keys. Always read from environment variables (`os.getenv("OPENAI_API_KEY")`) or a `.env` file.
|
||||||
|
|
||||||
|
## 3. Mocking OpenWebUI Internals
|
||||||
|
OpenWebUI plugins rely heavily on injected runtime variables. Your tests must mock these accurately:
|
||||||
|
- **`__user__`**: Mock as a dictionary. Always test both with a full user object and an empty/None object to ensure fallback logic works.
|
||||||
|
```python
|
||||||
|
mock_user_en = {"id": "u1", "name": "Alice", "language": "en-US"}
|
||||||
|
mock_user_zh = {"id": "u2", "name": "Bob", "language": "zh-CN"}
|
||||||
|
mock_user_none = None
|
||||||
|
```
|
||||||
|
- **`__event_emitter__`**: Mock as an async callable that records emitted events for assertion.
|
||||||
|
```python
|
||||||
|
async def mock_emitter(event: dict):
|
||||||
|
emitted_events.append(event)
|
||||||
|
```
|
||||||
|
- **`__event_call__`**: Mock as an async callable. To test Antigravity timeout guards, you must occasionally mock this to sleep longer than the timeout (e.g., `await asyncio.sleep(3.0)`) to ensure `asyncio.wait_for` catches it.
|
||||||
|
|
||||||
|
## 4. Testing Async Code
|
||||||
|
- All OpenWebUI plugin entry points (`action`, `inlet`, `outlet`) are asynchronous.
|
||||||
|
- Use `@pytest.mark.asyncio` for all test functions.
|
||||||
|
- Ensure you `await` all plugin method calls.
|
||||||
|
|
||||||
|
## 5. i18n (Internationalization) Testing
|
||||||
|
Since all plugins must be single-file i18n:
|
||||||
|
- **Mandatory**: Write parameterized tests to verify output in both English (`en-US`) and Chinese (`zh-CN`).
|
||||||
|
- Verify that if an unsupported language is passed (e.g., `fr-FR`), the system correctly falls back to the default (usually `en-US`).
|
||||||
|
|
||||||
|
## 6. Antigravity & Safety Testing
|
||||||
|
- **Timeout Guards**: Explicitly test that frontend JS executions (`__event_call__`) do not hang the backend. Assert that a `TimeoutError` is caught and handled gracefully.
|
||||||
|
- **State Leakage**: For `Filter` plugins (which are singletons), write tests that simulate concurrent requests from different users to ensure no state leaks across `self`.
|
||||||
|
|
||||||
|
## 7. Assertions & Best Practices
|
||||||
|
- Assert on **behavior and output**, not internal implementation details.
|
||||||
|
- For HTML/Markdown generation plugins, use Regex or BeautifulSoup to assert the presence of required tags (e.g., `<!-- OPENWEBUI_PLUGIN_OUTPUT -->`) rather than exact string matching.
|
||||||
|
- Keep tests isolated. Do not rely on the execution order of tests.
|
||||||
7
.markdownlint.json
Normal file
7
.markdownlint.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"default": true,
|
||||||
|
"MD013": false,
|
||||||
|
"MD033": false,
|
||||||
|
"MD030": false,
|
||||||
|
"MD051": false
|
||||||
|
}
|
||||||
154
docs/development/copilot-engineering-plan.md
Normal file
154
docs/development/copilot-engineering-plan.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Copilot Engineering Configuration Plan
|
||||||
|
|
||||||
|
> This document defines production-grade engineering configuration for plugin development with GitHub Copilot, Gemini CLI, and antigravity development mode.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Goals
|
||||||
|
|
||||||
|
- Standardize plugin engineering workflow for AI-assisted development.
|
||||||
|
- Support dual assistant stack:
|
||||||
|
- GitHub Copilot (primary in-editor agent)
|
||||||
|
- Gemini CLI (secondary external execution/research lane)
|
||||||
|
- Introduce antigravity development mode for safer, reversible, high-velocity iteration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Scope
|
||||||
|
|
||||||
|
This plan applies to:
|
||||||
|
|
||||||
|
- Plugin development (`actions`, `filters`, `pipes`, `pipelines`, `tools`)
|
||||||
|
- Documentation synchronization
|
||||||
|
- File generation/delivery workflow in OpenWebUI
|
||||||
|
- Streaming/tool-call rendering compatibility
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Engineering Configuration (Copilot-centered)
|
||||||
|
|
||||||
|
### 3.1 Source of truth
|
||||||
|
|
||||||
|
- Primary standard file: `.github/copilot-instructions.md`
|
||||||
|
- Agent workflow file: `.agent/workflows/plugin-development.md`
|
||||||
|
- Runtime guidance docs:
|
||||||
|
- `docs/development/plugin-guide.md`
|
||||||
|
- `docs/development/plugin-guide.zh.md`
|
||||||
|
|
||||||
|
### 3.2 Required development contract
|
||||||
|
|
||||||
|
- Single-file i18n plugin source.
|
||||||
|
- Bilingual README (`README.md` + `README_CN.md`).
|
||||||
|
- Safe context extraction (`_get_user_context`, `_get_chat_context`).
|
||||||
|
- Structured event handling (`status`, `notification`, `execute`).
|
||||||
|
- No silent failures; logging + user-visible status.
|
||||||
|
|
||||||
|
### 3.3 Tool definition contract (Copilot SDK)
|
||||||
|
|
||||||
|
- Define tool params explicitly with `pydantic.BaseModel`.
|
||||||
|
- Use `params_type` in tool registration.
|
||||||
|
- Preserve defaults by avoiding forced null overrides.
|
||||||
|
- Keep tool names normalized and deterministic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gemini CLI Compatibility Profile
|
||||||
|
|
||||||
|
Gemini CLI is treated as a parallel capability channel, not a replacement.
|
||||||
|
|
||||||
|
### 4.1 Usage boundary
|
||||||
|
|
||||||
|
- Use for:
|
||||||
|
- rapid drafts
|
||||||
|
- secondary reasoning
|
||||||
|
- cross-checking migration plans
|
||||||
|
- Do not bypass repository conventions or plugin contracts.
|
||||||
|
|
||||||
|
### 4.2 Output normalization
|
||||||
|
|
||||||
|
All Gemini CLI outputs must be normalized before merge:
|
||||||
|
|
||||||
|
- Match repository style and naming rules.
|
||||||
|
- Preserve OpenWebUI plugin signatures and context methods.
|
||||||
|
- Convert speculative outputs into explicit, testable implementation points.
|
||||||
|
|
||||||
|
### 4.3 Conflict policy
|
||||||
|
|
||||||
|
When Copilot and Gemini suggestions differ:
|
||||||
|
|
||||||
|
1. Prefer repository standard compliance.
|
||||||
|
2. Prefer safer fallback behavior.
|
||||||
|
3. Prefer lower integration risk.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Antigravity Development Mode
|
||||||
|
|
||||||
|
Antigravity mode means high-speed delivery with strict reversibility.
|
||||||
|
|
||||||
|
### 5.1 Core principles
|
||||||
|
|
||||||
|
- Small, isolated edits
|
||||||
|
- Deterministic interfaces
|
||||||
|
- Multi-level fallback paths
|
||||||
|
- Roll-forward and rollback both feasible
|
||||||
|
|
||||||
|
### 5.2 Required patterns
|
||||||
|
|
||||||
|
- Timeout guards on frontend execution calls.
|
||||||
|
- Path sandbox validation for all workspace file operations.
|
||||||
|
- Dual-channel upload fallback (API first, local/DB fallback).
|
||||||
|
- Progressive status reporting for long-running tasks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. File Creation & Delivery Standard
|
||||||
|
|
||||||
|
### 6.1 Create files in controlled workspace
|
||||||
|
|
||||||
|
- Write artifacts in current workspace scope.
|
||||||
|
- Never use paths outside workspace boundary for deliverables.
|
||||||
|
|
||||||
|
### 6.2 Publish protocol
|
||||||
|
|
||||||
|
Use 3-step delivery:
|
||||||
|
|
||||||
|
1. local write
|
||||||
|
2. publish from workspace
|
||||||
|
3. present `/api/v1/files/{id}/content` link
|
||||||
|
|
||||||
|
### 6.3 Metadata policy
|
||||||
|
|
||||||
|
- Set `skip_rag=true` for generated downloadable artifacts where applicable.
|
||||||
|
- Keep filename generation deterministic (`chat_title -> markdown_title -> user+date`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Plugin Development Norms for Multi-Agent Stack
|
||||||
|
|
||||||
|
- Compatible with GitHub Copilot and Gemini CLI under same coding contract.
|
||||||
|
- Keep streaming compatible with OpenWebUI native blocks (`<think>`, `<details type="tool_calls">`).
|
||||||
|
- Escape tool card attributes safely (`"`) for parser stability.
|
||||||
|
- Preserve async non-blocking behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Documentation Sync Rule
|
||||||
|
|
||||||
|
Any meaningful plugin engineering change must sync:
|
||||||
|
|
||||||
|
1. plugin code
|
||||||
|
2. plugin bilingual README
|
||||||
|
3. docs plugin detail pages (EN/ZH)
|
||||||
|
4. docs plugin indexes (EN/ZH)
|
||||||
|
5. root README update badge/date when release is prepared
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Acceptance Checklist
|
||||||
|
|
||||||
|
- Copilot config and workflow references are valid.
|
||||||
|
- Gemini CLI outputs can be merged without violating conventions.
|
||||||
|
- Antigravity safety mechanisms are present.
|
||||||
|
- File creation and publication flow is reproducible.
|
||||||
|
- Streaming/tool-card output format remains OpenWebUI-compatible.
|
||||||
149
docs/development/copilot-engineering-plan.zh.md
Normal file
149
docs/development/copilot-engineering-plan.zh.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# Copilot 工程化配置设计
|
||||||
|
|
||||||
|
> 本文档定义面向插件开发的工程化配置方案:支持 GitHub Copilot、兼容 Gemini CLI,并引入“反重力开发”模式。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
- 建立统一、可落地的 AI 协同开发标准。
|
||||||
|
- 支持双通道助手体系:
|
||||||
|
- GitHub Copilot(编辑器内主通道)
|
||||||
|
- Gemini CLI(外部补充通道)
|
||||||
|
- 在高迭代速度下保障可回滚、可审计、可发布。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 适用范围
|
||||||
|
|
||||||
|
适用于以下类型开发:
|
||||||
|
|
||||||
|
- 插件代码(`actions` / `filters` / `pipes` / `pipelines` / `tools`)
|
||||||
|
- 文档同步与发布准备
|
||||||
|
- OpenWebUI 文件创建与交付流程
|
||||||
|
- 流式输出与工具卡片兼容性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Copilot 工程化主配置
|
||||||
|
|
||||||
|
### 3.1 规范来源
|
||||||
|
|
||||||
|
- 主规范:`.github/copilot-instructions.md`
|
||||||
|
- 工作流:`.agent/workflows/plugin-development.md`
|
||||||
|
- 运行时开发指南:
|
||||||
|
- `docs/development/plugin-guide.md`
|
||||||
|
- `docs/development/plugin-guide.zh.md`
|
||||||
|
|
||||||
|
### 3.2 强制开发契约
|
||||||
|
|
||||||
|
- 单文件 i18n 插件源码。
|
||||||
|
- README 双语(`README.md` + `README_CN.md`)。
|
||||||
|
- 上下文统一入口(`_get_user_context`、`_get_chat_context`)。
|
||||||
|
- 事件标准化(`status`、`notification`、`execute`)。
|
||||||
|
- 禁止静默失败(用户可见状态 + 后端日志)。
|
||||||
|
|
||||||
|
### 3.3 Copilot SDK 工具契约
|
||||||
|
|
||||||
|
- 参数必须 `pydantic.BaseModel` 显式定义。
|
||||||
|
- 工具注册必须声明 `params_type`。
|
||||||
|
- 保留默认值语义,避免把未传参数强制覆盖为 `null`。
|
||||||
|
- 工具名需可预测、可规范化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Gemini CLI 兼容配置
|
||||||
|
|
||||||
|
Gemini CLI 作为补充通道,而不是替代主规范。
|
||||||
|
|
||||||
|
### 4.1 使用边界
|
||||||
|
|
||||||
|
- 适合:草案生成、方案对照、迁移校验。
|
||||||
|
- 不得绕过仓库规范与插件契约。
|
||||||
|
|
||||||
|
### 4.2 输出归一化
|
||||||
|
|
||||||
|
Gemini CLI 输出合入前必须完成:
|
||||||
|
|
||||||
|
- 命名与结构对齐仓库约束。
|
||||||
|
- 保留 OpenWebUI 插件标准签名与上下文方法。
|
||||||
|
- 将“建议性文字”转换为可执行、可验证实现点。
|
||||||
|
|
||||||
|
### 4.3 冲突决策
|
||||||
|
|
||||||
|
Copilot 与 Gemini 建议冲突时按优先级处理:
|
||||||
|
|
||||||
|
1. 规范一致性优先
|
||||||
|
2. 安全回退优先
|
||||||
|
3. 低集成风险优先
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 反重力开发(Antigravity)模式
|
||||||
|
|
||||||
|
反重力开发 = 高速迭代 + 强回退能力。
|
||||||
|
|
||||||
|
### 5.1 核心原则
|
||||||
|
|
||||||
|
- 小步、隔离、可逆变更
|
||||||
|
- 接口稳定、行为可预测
|
||||||
|
- 多级回退链路
|
||||||
|
- 支持前滚与回滚
|
||||||
|
|
||||||
|
### 5.2 强制模式
|
||||||
|
|
||||||
|
- 前端执行调用必须设置超时保护。
|
||||||
|
- 工作区文件操作必须做路径沙箱校验。
|
||||||
|
- 上传链路采用 API 优先 + 本地/DB 回退。
|
||||||
|
- 长任务必须分阶段状态反馈。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 文件创建与交付标准
|
||||||
|
|
||||||
|
### 6.1 创建范围
|
||||||
|
|
||||||
|
- 交付文件仅在受控 workspace 内创建。
|
||||||
|
- 禁止将交付产物写到 workspace 边界之外。
|
||||||
|
|
||||||
|
### 6.2 三步交付协议
|
||||||
|
|
||||||
|
1. 本地写入
|
||||||
|
2. 从 workspace 发布
|
||||||
|
3. 返回并展示 `/api/v1/files/{id}/content`
|
||||||
|
|
||||||
|
### 6.3 元数据策略
|
||||||
|
|
||||||
|
- 需要绕过检索时为产物标记 `skip_rag=true`。
|
||||||
|
- 文件名采用确定性策略:`chat_title -> markdown_title -> user+date`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 多助手并行下的插件开发规范
|
||||||
|
|
||||||
|
- 在统一契约下同时兼容 GitHub Copilot 与 Gemini CLI。
|
||||||
|
- 流式输出保持 OpenWebUI 原生兼容(`<think>`、`<details type="tool_calls">`)。
|
||||||
|
- 工具卡片属性严格转义(`"`)保证解析稳定。
|
||||||
|
- 全链路保持异步非阻塞。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 文档同步规则
|
||||||
|
|
||||||
|
插件工程化变更发生时,至少同步:
|
||||||
|
|
||||||
|
1. 插件代码
|
||||||
|
2. 插件双语 README
|
||||||
|
3. docs 详情页(EN/ZH)
|
||||||
|
4. docs 索引页(EN/ZH)
|
||||||
|
5. 发布准备阶段同步根 README 日期徽章
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 验收清单
|
||||||
|
|
||||||
|
- Copilot 配置与工作流引用有效。
|
||||||
|
- Gemini CLI 输出可无缝合入且不破坏规范。
|
||||||
|
- 反重力安全机制完整。
|
||||||
|
- 文件创建/发布流程可复现。
|
||||||
|
- 流式与工具卡片格式保持 OpenWebUI 兼容。
|
||||||
@@ -24,6 +24,14 @@ Learn how to develop plugins and contribute to OpenWebUI Extensions.
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: Read the Guide](documentation-guide.md)
|
[:octicons-arrow-right-24: Read the Guide](documentation-guide.md)
|
||||||
|
|
||||||
|
- :material-robot:{ .lg .middle } **Copilot Engineering Plan**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Engineering configuration for GitHub Copilot + Gemini CLI + antigravity development mode.
|
||||||
|
|
||||||
|
[:octicons-arrow-right-24: Read the Plan](copilot-engineering-plan.md)
|
||||||
|
|
||||||
- :material-github:{ .lg .middle } **Contributing**
|
- :material-github:{ .lg .middle } **Contributing**
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -164,5 +172,6 @@ user_language = __user__.get("language", "en-US")
|
|||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- [Full Development Guide](plugin-guide.md)
|
- [Full Development Guide](plugin-guide.md)
|
||||||
|
- [Copilot Engineering Plan](copilot-engineering-plan.md)
|
||||||
- [Plugin Examples](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins)
|
- [Plugin Examples](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins)
|
||||||
- [OpenWebUI Documentation](https://docs.openwebui.com/)
|
- [OpenWebUI Documentation](https://docs.openwebui.com/)
|
||||||
|
|||||||
@@ -24,6 +24,14 @@
|
|||||||
|
|
||||||
[:octicons-arrow-right-24: 阅读指南](documentation-guide.md)
|
[:octicons-arrow-right-24: 阅读指南](documentation-guide.md)
|
||||||
|
|
||||||
|
- :material-robot:{ .lg .middle } **Copilot 工程化配置**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
面向 GitHub Copilot + Gemini CLI + 反重力开发模式的工程化设计文档。
|
||||||
|
|
||||||
|
[:octicons-arrow-right-24: 阅读文档](copilot-engineering-plan.md)
|
||||||
|
|
||||||
- :material-github:{ .lg .middle } **贡献指南**
|
- :material-github:{ .lg .middle } **贡献指南**
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -164,5 +172,6 @@ user_language = __user__.get("language", "en-US")
|
|||||||
## 资源
|
## 资源
|
||||||
|
|
||||||
- [完整开发指南](plugin-guide.md)
|
- [完整开发指南](plugin-guide.md)
|
||||||
|
- [Copilot 工程化配置](copilot-engineering-plan.md)
|
||||||
- [插件示例](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins)
|
- [插件示例](https://github.com/Fu-Jie/openwebui-extensions/tree/main/plugins)
|
||||||
- [OpenWebUI 文档](https://docs.openwebui.com/)
|
- [OpenWebUI 文档](https://docs.openwebui.com/)
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
## 📚 Table of Contents
|
## 📚 Table of Contents
|
||||||
|
|
||||||
1. [Quick Start](#1-quick-start)
|
1. [Quick Start](#1-quick-start)
|
||||||
2. [Core Concepts & SDK Details](#2-core-concepts-sdk-details)
|
2. [Project Structure & Naming](#2-project-structure--naming)
|
||||||
3. [Deep Dive into Plugin Types](#3-deep-dive-into-plugin-types)
|
3. [Core Concepts & SDK Details](#3-core-concepts--sdk-details)
|
||||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
4. [Deep Dive into Plugin Types](#4-deep-dive-into-plugin-types)
|
||||||
5. [Best Practices & Design Principles](#5-best-practices-design-principles)
|
5. [Advanced Development Patterns](#5-advanced-development-patterns)
|
||||||
6. [Troubleshooting](#6-troubleshooting)
|
6. [Best Practices & Design Principles](#6-best-practices--design-principles)
|
||||||
|
7. [Workflow & Process](#7-workflow--process)
|
||||||
|
8. [Troubleshooting](#8-troubleshooting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -64,9 +66,39 @@ class Action:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Core Concepts & SDK Details
|
## 2. Project Structure & Naming
|
||||||
|
|
||||||
### 2.1 ⚠️ Important: Sync vs Async
|
### 2.1 Language & Code Requirements
|
||||||
|
|
||||||
|
- **Single Code File**: `plugins/{type}/{name}/{name}.py`. Never create separate source files for different languages.
|
||||||
|
- **Built-in i18n**: Must dynamically switch UI, prompts, and logs based on user language.
|
||||||
|
- **Documentation**: Must include both `README.md` (English) and `README_CN.md` (Chinese).
|
||||||
|
|
||||||
|
### 2.2 Docstring Standard
|
||||||
|
|
||||||
|
Each plugin file must start with a standardized docstring:
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""
|
||||||
|
title: Plugin Name
|
||||||
|
author: Fu-Jie
|
||||||
|
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
||||||
|
funding_url: https://github.com/open-webui
|
||||||
|
version: 0.1.0
|
||||||
|
icon_url: data:image/svg+xml;base64,<base64-encoded-svg>
|
||||||
|
requirements: dependency1==1.0.0, dependency2>=2.0.0
|
||||||
|
description: Brief description of plugin functionality.
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
- **icon_url**: Required for Action plugins. Must be Base64 encoded SVG from [Lucide Icons](https://lucide.dev/icons/).
|
||||||
|
- **requirements**: Only list dependencies not installed in the OpenWebUI environment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Core Concepts & SDK Details
|
||||||
|
|
||||||
|
### 3.1 ⚠️ Important: Sync vs Async
|
||||||
|
|
||||||
OpenWebUI plugins run within an `asyncio` event loop.
|
OpenWebUI plugins run within an `asyncio` event loop.
|
||||||
|
|
||||||
@@ -75,7 +107,7 @@ OpenWebUI plugins run within an `asyncio` event loop.
|
|||||||
- **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server
|
- **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server
|
||||||
- **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`
|
- **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`
|
||||||
|
|
||||||
### 2.2 Core Parameters
|
### 3.2 Core Parameters
|
||||||
|
|
||||||
All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the following special parameters:
|
All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the following special parameters:
|
||||||
|
|
||||||
@@ -88,30 +120,43 @@ All plugin methods (`inlet`, `outlet`, `pipe`, `action`) support injecting the f
|
|||||||
| `__event_emitter__` | `func` | **One-way Notification**. Used to send Toast notifications or status bar updates |
|
| `__event_emitter__` | `func` | **One-way Notification**. Used to send Toast notifications or status bar updates |
|
||||||
| `__event_call__` | `func` | **Two-way Interaction**. Used to execute JS code, show confirmation dialogs, or input boxes |
|
| `__event_call__` | `func` | **Two-way Interaction**. Used to execute JS code, show confirmation dialogs, or input boxes |
|
||||||
|
|
||||||
### 2.3 Configuration System (Valves)
|
### 3.3 Configuration System (Valves)
|
||||||
|
|
||||||
- **`Valves`**: Global admin configuration
|
Use Pydantic BaseModel to define configurable parameters. All Valves fields must use **UPPER_SNAKE_CASE**.
|
||||||
- **`UserValves`**: User-level configuration (higher priority, overrides global)
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Filter:
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
class Action:
|
||||||
class Valves(BaseModel):
|
class Valves(BaseModel):
|
||||||
API_KEY: str = Field(default="", description="Global API Key")
|
SHOW_STATUS: bool = Field(default=True, description="Whether to show operation status updates.")
|
||||||
|
# ...
|
||||||
class UserValves(BaseModel):
|
|
||||||
API_KEY: str = Field(default="", description="User Private API Key")
|
|
||||||
|
|
||||||
def inlet(self, body, __user__):
|
|
||||||
# Prioritize user's Key
|
|
||||||
user_valves = __user__.get("valves", self.UserValves())
|
|
||||||
api_key = user_valves.API_KEY or self.valves.API_KEY
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3.4 Context Access
|
||||||
|
|
||||||
|
All plugins **must** use `_get_user_context` and `_get_chat_context` methods to safely extract information, rather than accessing `__user__` or `body` directly.
|
||||||
|
|
||||||
|
### 3.5 Event Emission & Logging
|
||||||
|
|
||||||
|
- **Event Emission**: Implement helper methods `_emit_status` and `_emit_notification`.
|
||||||
|
- **Frontend Console Debugging**: Highly recommended for real-time data flow viewing. Use `_emit_debug_log` to print structured debug logs in the browser console.
|
||||||
|
- **Server-side Logging**: Use Python's standard `logging` module. Do not use `print()`.
|
||||||
|
|
||||||
|
### 3.6 Database & File Storage
|
||||||
|
|
||||||
|
- **Database**: Re-use Open WebUI's internal database connection (`open_webui.internal.db`).
|
||||||
|
- **File Storage**: Implement multi-level fallback mechanisms (DB -> S3 -> Local -> URL -> API) to ensure compatibility across all storage configurations.
|
||||||
|
|
||||||
|
### 3.7 Internationalization (i18n)
|
||||||
|
|
||||||
|
Define a `TRANSLATIONS` dictionary and use a robust language detection mechanism (Multi-level Fallback: JS localStorage -> HTTP Accept-Language -> User Profile -> en-US).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Deep Dive into Plugin Types
|
## 4. Deep Dive into Plugin Types
|
||||||
|
|
||||||
### 3.1 Action
|
### 4.1 Action
|
||||||
|
|
||||||
**Role**: Adds buttons below messages that trigger upon user click.
|
**Role**: Adds buttons below messages that trigger upon user click.
|
||||||
|
|
||||||
@@ -136,7 +181,7 @@ async def action(self, body, __event_call__):
|
|||||||
await __event_call__({"type": "execute", "data": {"code": js}})
|
await __event_call__({"type": "execute", "data": {"code": js}})
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.2 Filter
|
### 4.2 Filter
|
||||||
|
|
||||||
**Role**: Middleware that intercepts and modifies requests/responses.
|
**Role**: Middleware that intercepts and modifies requests/responses.
|
||||||
|
|
||||||
@@ -157,7 +202,7 @@ async def inlet(self, body, __metadata__):
|
|||||||
return body
|
return body
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.3 Pipe
|
### 4.3 Pipe
|
||||||
|
|
||||||
**Role**: Custom Model/Agent.
|
**Role**: Custom Model/Agent.
|
||||||
|
|
||||||
@@ -182,18 +227,27 @@ class Pipe:
|
|||||||
return r.iter_lines()
|
return r.iter_lines()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 4.4 Copilot SDK Tool Definition Standards
|
||||||
|
|
||||||
|
When developing custom tools for GitHub Copilot SDK, you **must** define a Pydantic `BaseModel` for parameters and explicitly reference it using `params_type` in `define_tool`.
|
||||||
|
|
||||||
|
### 4.5 Copilot SDK Streaming & Tool Card Standards
|
||||||
|
|
||||||
|
- **Reasoning Streaming**: Must use native `<think>` tags and ensure proper closure (`\n</think>\n`) before outputting main content or tool calls.
|
||||||
|
- **Native Tool Calls Block**: Output strictly formatted HTML `<details type="tool_calls"...>` blocks. Ensure all double quotes in attributes are escaped as `"`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Advanced Development Patterns
|
## 5. Advanced Development Patterns
|
||||||
|
|
||||||
### 4.1 Pipe & Filter Collaboration
|
### 5.1 Pipe & Filter Collaboration
|
||||||
|
|
||||||
Use `__request__.app.state` to share data between plugins:
|
Use `__request__.app.state` to share data between plugins:
|
||||||
|
|
||||||
- **Pipe**: `__request__.app.state.search_results = [...]`
|
- **Pipe**: `__request__.app.state.search_results = [...]`
|
||||||
- **Filter (Outlet)**: Read `search_results` and format them as citation links
|
- **Filter (Outlet)**: Read `search_results` and format them as citation links
|
||||||
|
|
||||||
### 4.2 Async Background Tasks
|
### 5.2 Async Background Tasks
|
||||||
|
|
||||||
Execute time-consuming operations without blocking the user response:
|
Execute time-consuming operations without blocking the user response:
|
||||||
|
|
||||||
@@ -209,7 +263,7 @@ async def background_job(self, chat_id):
|
|||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.3 Calling Built-in LLM
|
### 5.3 Calling Built-in LLM
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from open_webui.utils.chat import generate_chat_completion
|
from open_webui.utils.chat import generate_chat_completion
|
||||||
@@ -235,13 +289,13 @@ llm_response = await generate_chat_completion(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.4 JS Render to Markdown (Data URL Embedding)
|
### 5.4 JS Render to Markdown (Data URL Embedding)
|
||||||
|
|
||||||
For scenarios requiring complex frontend rendering (e.g., AntV charts, Mermaid diagrams) but wanting **persistent pure Markdown output**, use the Data URL embedding pattern:
|
For scenarios requiring complex frontend rendering (e.g., AntV charts, Mermaid diagrams) but wanting **persistent pure Markdown output**, use the Data URL embedding pattern:
|
||||||
|
|
||||||
#### Workflow
|
#### Workflow
|
||||||
|
|
||||||
```
|
```text
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ 1. Python Action │
|
│ 1. Python Action │
|
||||||
│ ├── Analyze message content │
|
│ ├── Analyze message content │
|
||||||
@@ -259,116 +313,28 @@ For scenarios requiring complex frontend rendering (e.g., AntV charts, Mermaid d
|
|||||||
└──────────────────────────────────────────────────────────────┘
|
└──────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Python Side (Send JS for Execution)
|
### 5.5 Agent File Delivery Standards (3-Step Delivery Protocol)
|
||||||
|
|
||||||
```python
|
1. **Write Local**: Create files in the current execution directory (`.`).
|
||||||
async def action(self, body, __event_call__, __metadata__, ...):
|
2. **Publish**: Call `publish_file_from_workspace(filename='name.ext')`.
|
||||||
chat_id = self._extract_chat_id(body, __metadata__)
|
3. **Display Link**: Present the returned `download_url` as a Markdown link.
|
||||||
message_id = self._extract_message_id(body, __metadata__)
|
|
||||||
|
|
||||||
# Generate JS code
|
|
||||||
js_code = self._generate_js_code(
|
|
||||||
chat_id=chat_id,
|
|
||||||
message_id=message_id,
|
|
||||||
data=processed_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Execute JS
|
|
||||||
if __event_call__:
|
|
||||||
await __event_call__({
|
|
||||||
"type": "execute",
|
|
||||||
"data": {"code": js_code}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
#### JavaScript Side (Render and Write-back)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
(async function() {
|
|
||||||
// 1. Load visualization library
|
|
||||||
if (typeof VisualizationLib === 'undefined') {
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = 'https://cdn.example.com/lib.min.js';
|
|
||||||
script.onload = resolve;
|
|
||||||
script.onerror = reject;
|
|
||||||
document.head.appendChild(script);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Create offscreen container
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.style.cssText = 'position:absolute;left:-9999px;';
|
|
||||||
document.body.appendChild(container);
|
|
||||||
|
|
||||||
// 3. Render visualization
|
|
||||||
const instance = new VisualizationLib({ container });
|
|
||||||
instance.render(data);
|
|
||||||
|
|
||||||
// 4. Export to Data URL
|
|
||||||
const dataUrl = await instance.toDataURL({ type: 'svg', embedResources: true });
|
|
||||||
|
|
||||||
// 5. Cleanup
|
|
||||||
instance.destroy();
|
|
||||||
document.body.removeChild(container);
|
|
||||||
|
|
||||||
// 6. Generate Markdown image
|
|
||||||
const markdownImage = ``;
|
|
||||||
|
|
||||||
// 7. Update message via API
|
|
||||||
const token = localStorage.getItem("token");
|
|
||||||
await fetch(`/api/v1/chats/${chatId}/messages/${messageId}/event`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Authorization": `Bearer ${token}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
type: "chat:message",
|
|
||||||
data: { content: originalContent + "\n\n" + markdownImage }
|
|
||||||
})
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Benefits
|
|
||||||
|
|
||||||
- **Pure Markdown Output**: Standard Markdown image syntax, no HTML code blocks
|
|
||||||
- **Self-Contained**: Images embedded as Base64 Data URL, no external dependencies
|
|
||||||
- **Persistent**: Via API write-back, images remain after page reload
|
|
||||||
- **Cross-Platform**: Works on any client supporting Markdown images
|
|
||||||
|
|
||||||
#### HTML Injection vs JS Render to Markdown
|
|
||||||
|
|
||||||
| Feature | HTML Injection | JS Render + Markdown |
|
|
||||||
|---------|----------------|----------------------|
|
|
||||||
| Output Format | HTML code block | Markdown image |
|
|
||||||
| Interactivity | ✅ Buttons, animations | ❌ Static image |
|
|
||||||
| External Deps | Requires JS libraries | None (self-contained) |
|
|
||||||
| Persistence | Depends on browser | ✅ Permanent |
|
|
||||||
| File Export | Needs special handling | ✅ Direct export |
|
|
||||||
| Use Case | Interactive content | Infographics, chart snapshots |
|
|
||||||
|
|
||||||
#### Reference Implementations
|
|
||||||
|
|
||||||
- `plugins/actions/infographic/infographic.py` - Production-ready implementation using AntV + Data URL
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Best Practices & Design Principles
|
## 6. Best Practices & Design Principles
|
||||||
|
|
||||||
### 5.1 Naming & Positioning
|
### 6.1 Naming & Positioning
|
||||||
|
|
||||||
- **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant"
|
- **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant"
|
||||||
- **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves
|
- **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves
|
||||||
|
|
||||||
### 5.2 User Experience (UX)
|
### 6.2 User Experience (UX)
|
||||||
|
|
||||||
- **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations
|
- **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations
|
||||||
- **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients)
|
- **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients)
|
||||||
- **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results"
|
- **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results"
|
||||||
|
|
||||||
### 5.3 Error Handling
|
### 6.3 Error Handling
|
||||||
|
|
||||||
!!! danger "Never fail silently"
|
!!! danger "Never fail silently"
|
||||||
Always catch exceptions and inform the user via `__event_emitter__`.
|
Always catch exceptions and inform the user via `__event_emitter__`.
|
||||||
@@ -384,9 +350,39 @@ except Exception as e:
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 6.4 Long-running Task Notifications
|
||||||
|
|
||||||
|
If a foreground task is expected to take more than 3 seconds, implement a user notification mechanism (e.g., sending a notification every 5 seconds).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Troubleshooting
|
## 7. Workflow & Process
|
||||||
|
|
||||||
|
### 7.1 Source-derived Knowledge (from `plugins/`)
|
||||||
|
|
||||||
|
- **Input/context safety**: normalize multimodal text extraction, use `_get_user_context` / `_get_chat_context`, and protect frontend language detection with timeout guards.
|
||||||
|
- **Long task UX**: emit immediate `status/notification`, then staged progress updates; keep full exception detail in backend logs.
|
||||||
|
- **HTML merge strategy**: use stable wrapper markers (`OPENWEBUI_PLUGIN_OUTPUT`) and support both overwrite and merge modes.
|
||||||
|
- **Theme consistency**: detect parent/system theme and apply theme-aware rendering/export styles for iframe-based outputs.
|
||||||
|
- **Render-export-persist loop**: offscreen render (SVG/PNG) -> upload `/api/v1/files/` -> event update + persistence update to avoid refresh loss.
|
||||||
|
- **DOCX production path**: `TITLE_SOURCE` fallback naming, reasoning-block stripping, native Word math (`latex2mathml + mathml2omml`), and citation/reference anchoring.
|
||||||
|
- **File retrieval fallback chain**: DB inline -> S3 direct -> local path variants -> public URL -> internal API -> raw fields, with max-byte guards on each stage.
|
||||||
|
- **Filter singleton discipline**: do not store request-scoped mutable state on `self`; compute from request context each run.
|
||||||
|
- **Async compression pattern**: `inlet` summary injection + `outlet` background summary generation, with model-threshold override and system-message protection.
|
||||||
|
- **Workspace/tool hardening**: explicit `params_type` schemas, strict path-boundary validation, and publish flow returning `/api/v1/files/{id}/content` with `skip_rag=true` metadata.
|
||||||
|
- **MoE refinement pipeline**: detect aggregation prompts, parse segmented responses, and rewrite to synthesis-oriented master prompt with optional reroute model.
|
||||||
|
|
||||||
|
### 7.2 Copilot Engineering Configuration
|
||||||
|
|
||||||
|
- For repository-wide AI-assisted engineering setup (GitHub Copilot + Gemini CLI + antigravity mode), follow `docs/development/copilot-engineering-plan.md`.
|
||||||
|
- This plan defines the shared contract for tool parameter schema/routing, file creation/publish protocol, rollback-safe delivery patterns, and streaming/tool-card compatibility.
|
||||||
|
|
||||||
|
- **Consistency Maintenance**: Any addition, modification, or removal of a plugin must simultaneously update the plugin code, READMEs, project docs, doc indexes, and the root README.
|
||||||
|
- **Release Workflow**: Pushing to `main` triggers automatic release. Ensure version numbers are updated and follow SemVer. Use Conventional Commits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Troubleshooting
|
||||||
|
|
||||||
??? question "HTML not showing?"
|
??? question "HTML not showing?"
|
||||||
Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
* [Pipe (管道)](#33-pipe)
|
* [Pipe (管道)](#33-pipe)
|
||||||
4. [高级开发模式](#4-advanced-patterns)
|
4. [高级开发模式](#4-advanced-patterns)
|
||||||
5. [最佳实践与设计原则](#5-best-practices)
|
5. [最佳实践与设计原则](#5-best-practices)
|
||||||
6. [故障排查](#6-troubleshooting)
|
6. [仓库规范(openwebui-extensions)](#6-repo-standards)
|
||||||
|
7. [故障排查](#7-troubleshooting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
### 1.1 什么是 OpenWebUI 插件?
|
### 1.1 什么是 OpenWebUI 插件?
|
||||||
|
|
||||||
OpenWebUI 插件(官方称为 "Functions")是扩展平台功能的主要方式。它们运行在后端 Python 环境中,允许你:
|
OpenWebUI 插件(官方称为 "Functions")是扩展平台功能的主要方式。它们运行在后端 Python 环境中,允许你:
|
||||||
|
|
||||||
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
||||||
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
||||||
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
||||||
@@ -69,6 +71,7 @@ class Action:
|
|||||||
### 2.1 ⚠️ 重要:同步与异步
|
### 2.1 ⚠️ 重要:同步与异步
|
||||||
|
|
||||||
OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
||||||
|
|
||||||
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
||||||
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
||||||
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
||||||
@@ -113,7 +116,7 @@ class Filter:
|
|||||||
|
|
||||||
**定位**:在消息下方添加按钮,用户点击触发。
|
**定位**:在消息下方添加按钮,用户点击触发。
|
||||||
|
|
||||||
**高级用法:前端执行 JavaScript (文件下载示例)**
|
#### 高级用法:前端执行 JavaScript (文件下载示例)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import base64
|
import base64
|
||||||
@@ -142,7 +145,7 @@ async def action(self, body, __event_call__):
|
|||||||
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
||||||
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
||||||
|
|
||||||
**示例:注入环境变量**
|
#### 示例:注入环境变量
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def inlet(self, body, __metadata__):
|
async def inlet(self, body, __metadata__):
|
||||||
@@ -159,7 +162,7 @@ async def inlet(self, body, __metadata__):
|
|||||||
|
|
||||||
**定位**:自定义模型/代理。
|
**定位**:自定义模型/代理。
|
||||||
|
|
||||||
**示例:简单的 OpenAI 代理**
|
#### 示例:简单的 OpenAI 代理
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import requests
|
import requests
|
||||||
@@ -180,11 +183,14 @@ class Pipe:
|
|||||||
## 4. 高级开发模式 {: #4-advanced-patterns }
|
## 4. 高级开发模式 {: #4-advanced-patterns }
|
||||||
|
|
||||||
### 4.1 Pipe 与 Filter 协同
|
### 4.1 Pipe 与 Filter 协同
|
||||||
|
|
||||||
利用 `__request__.app.state` 在不同插件间共享数据。
|
利用 `__request__.app.state` 在不同插件间共享数据。
|
||||||
|
|
||||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||||
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
||||||
|
|
||||||
### 4.2 异步后台任务
|
### 4.2 异步后台任务
|
||||||
|
|
||||||
不阻塞用户响应,在后台执行耗时操作(如生成总结、存库)。
|
不阻塞用户响应,在后台执行耗时操作(如生成总结、存库)。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -205,7 +211,7 @@ async def background_job(self, chat_id):
|
|||||||
|
|
||||||
#### 工作流程
|
#### 工作流程
|
||||||
|
|
||||||
```
|
```text
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ 1. Python Action │
|
│ 1. Python Action │
|
||||||
│ ├── 分析消息内容 │
|
│ ├── 分析消息内容 │
|
||||||
@@ -297,10 +303,10 @@ async def action(self, body, __event_call__, __metadata__, ...):
|
|||||||
|
|
||||||
#### 优势
|
#### 优势
|
||||||
|
|
||||||
- **纯 Markdown 输出**:结果是标准的 Markdown 图片语法,无需 HTML 代码块
|
* **纯 Markdown 输出**:结果是标准的 Markdown 图片语法,无需 HTML 代码块
|
||||||
- **自包含**:图片以 Base64 Data URL 嵌入,无外部依赖
|
* **自包含**:图片以 Base64 Data URL 嵌入,无外部依赖
|
||||||
- **持久化**:通过 API 回写,消息重新加载后图片仍然存在
|
* **持久化**:通过 API 回写,消息重新加载后图片仍然存在
|
||||||
- **跨平台**:任何支持 Markdown 图片的客户端都能显示
|
* **跨平台**:任何支持 Markdown 图片的客户端都能显示
|
||||||
|
|
||||||
#### HTML 注入 vs JS 渲染嵌入 Markdown
|
#### HTML 注入 vs JS 渲染嵌入 Markdown
|
||||||
|
|
||||||
@@ -315,20 +321,23 @@ async def action(self, body, __event_call__, __metadata__, ...):
|
|||||||
|
|
||||||
#### 参考实现
|
#### 参考实现
|
||||||
|
|
||||||
- `plugins/actions/infographic/infographic.py` - 基于 AntV + Data URL 的生产级实现
|
* `plugins/actions/infographic/infographic.py` - 基于 AntV + Data URL 的生产级实现
|
||||||
|
|
||||||
## 5. 最佳实践与设计原则 {: #5-best-practices }
|
## 5. 最佳实践与设计原则 {: #5-best-practices }
|
||||||
|
|
||||||
### 5.1 命名与定位
|
### 5.1 命名与定位
|
||||||
|
|
||||||
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
||||||
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
||||||
|
|
||||||
### 5.2 用户体验 (UX)
|
### 5.2 用户体验 (UX)
|
||||||
|
|
||||||
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
||||||
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
||||||
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
||||||
|
|
||||||
### 5.3 错误处理
|
### 5.3 错误处理
|
||||||
|
|
||||||
永远不要让插件静默失败。捕获异常并通过 `__event_emitter__` 告知用户。
|
永远不要让插件静默失败。捕获异常并通过 `__event_emitter__` 告知用户。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -343,7 +352,75 @@ except Exception as e:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 故障排查 {: #6-troubleshooting }
|
## 6. 仓库规范(openwebui-extensions) {: #6-repo-standards }
|
||||||
|
|
||||||
|
### 6.1 单文件 i18n 规范
|
||||||
|
|
||||||
|
本仓库要求每个插件采用**单文件源码 + 内置多语言**方案,禁止按语言拆分多个 `.py` 文件。
|
||||||
|
|
||||||
|
* 代码路径规范:`plugins/{type}/{name}/{name}.py`
|
||||||
|
* 文档规范:必须同时提供 `README.md` 与 `README_CN.md`
|
||||||
|
|
||||||
|
### 6.2 上下文访问规范(必选)
|
||||||
|
|
||||||
|
优先通过 `_get_user_context` 与 `_get_chat_context` 提取上下文,避免直接硬编码读取 `__user__` 或 `body` 字段。
|
||||||
|
|
||||||
|
### 6.3 事件与日志规范
|
||||||
|
|
||||||
|
* 用状态/通知事件给用户反馈进度。
|
||||||
|
* 前端调试优先使用 `execute` 注入的控制台日志。
|
||||||
|
* 后端统一使用 Python `logging`,生产代码避免 `print()`。
|
||||||
|
|
||||||
|
### 6.4 前端语言探测防卡死
|
||||||
|
|
||||||
|
通过 `__event_call__` 获取前端语言时,必须同时满足:
|
||||||
|
|
||||||
|
* JS 端 `try...catch` 并保证返回值
|
||||||
|
* 后端 `asyncio.wait_for(..., timeout=2.0)`
|
||||||
|
|
||||||
|
这样可以避免前端异常导致后端永久等待。
|
||||||
|
|
||||||
|
### 6.5 Copilot SDK 工具参数定义
|
||||||
|
|
||||||
|
开发 Copilot SDK 工具时,应使用 `pydantic.BaseModel` 显式声明参数,并在 `define_tool(...)` 中通过 `params_type` 传入。
|
||||||
|
|
||||||
|
### 6.6 Copilot SDK 流式输出格式
|
||||||
|
|
||||||
|
* 思考过程使用原生 `<think>...</think>`。
|
||||||
|
* 正文或工具卡片输出前,必须先闭合 `</think>`。
|
||||||
|
* 工具卡片使用 `<details type="tool_calls" ...>` 原生结构。
|
||||||
|
* `arguments` 与 `result` 属性中的双引号必须转义为 `"`。
|
||||||
|
|
||||||
|
### 6.7 从 `plugins/` 全量提炼的关键开发知识
|
||||||
|
|
||||||
|
* **输入与上下文**:统一做多模态文本抽取;优先 `_get_user_context` / `_get_chat_context`;前端语言探测必须 `wait_for` 超时保护。
|
||||||
|
* **长任务反馈**:执行前立即发送 `status/notification`,过程分阶段汇报,失败时用户提示简洁、后台日志完整。
|
||||||
|
* **HTML/渲染输出**:使用固定包装器与插入点,支持覆盖与合并两种模式;主题跟随父页面与系统主题。
|
||||||
|
* **前端导出闭环**:离屏渲染 SVG/PNG -> 上传 `/api/v1/files/` -> 事件更新 + 持久化更新,避免刷新丢失。
|
||||||
|
* **DOCX 生产模式**:`TITLE_SOURCE` 多级回退;导出前剔除 reasoning;LaTeX 转 OMML;支持引用锚点与参考文献。
|
||||||
|
* **文件访问回退链**:DB 内联 -> S3 -> 本地路径变体 -> 公网 URL -> 内部 API -> 原始字段,并在每层限制字节上限。
|
||||||
|
* **Filter 单例安全**:禁止在 `self` 保存请求态;上下文压缩采用 inlet/outlet 双阶段 + 异步后台任务。
|
||||||
|
* **工具与工作区安全**:工具参数用 `params_type` 显式建模;路径解析后必须二次校验在 workspace 根目录内。
|
||||||
|
* **文件交付标准**:本地生成 -> 发布工具 -> 返回 `/api/v1/files/{id}/content`,并带 `skip_rag=true` 元数据。
|
||||||
|
* **MoE 聚合优化**:识别聚合提示词后重写为结构化综合分析任务,并可在聚合阶段切换模型。
|
||||||
|
|
||||||
|
### 6.8 Copilot 工程化配置(GitHub Copilot + Gemini CLI + 反重力开发)
|
||||||
|
|
||||||
|
统一工程化配置请参考:
|
||||||
|
|
||||||
|
* `docs/development/copilot-engineering-plan.md`
|
||||||
|
* `docs/development/copilot-engineering-plan.zh.md`
|
||||||
|
|
||||||
|
该设计文档规定了:
|
||||||
|
|
||||||
|
* 工具参数建模与路由规则
|
||||||
|
* 文件创建与发布协议
|
||||||
|
* 可回滚的高迭代交付模式
|
||||||
|
* 流式输出与工具卡片兼容要求
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 故障排查 {: #7-troubleshooting }
|
||||||
|
|
||||||
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
||||||
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
||||||
|
|||||||
@@ -1,50 +1,46 @@
|
|||||||
# 开源项目重组实施计划
|
# Implementation Plan (Current)
|
||||||
|
|
||||||
## 1. 目标
|
## 1. Objective
|
||||||
将 `openwebui-extras` 打造为一个 **OpenWebUI 增强功能集合库**,专注于分享个人开发和收集的优质插件、提示词,而非作为一个独立的 Python 应用程序发布。
|
|
||||||
|
|
||||||
## 2. 当前状态分析
|
Define an engineering-ready design baseline for OpenWebUI plugin development that:
|
||||||
- **定位明确**:项目核心价值在于内容(Plugins, Prompts, Docs),而非运行环境。
|
|
||||||
- **结构已优化**:
|
|
||||||
- `plugins/`:核心插件资源。
|
|
||||||
- `prompts/`:提示词资源。
|
|
||||||
- `docs/`:详细的使用和开发文档。
|
|
||||||
- `scripts/`:辅助工具脚本(如本地测试用的 `run.py`)。
|
|
||||||
- **已移除不必要文件**:移除了 `requirements.txt`,避免用户误以为需要配置 Python 环境。
|
|
||||||
|
|
||||||
## 3. 重组方案
|
- uses GitHub Copilot as the primary coding agent,
|
||||||
|
- supports Gemini CLI as a secondary execution/research lane,
|
||||||
|
- adopts antigravity development mode for reversible, low-risk iteration.
|
||||||
|
|
||||||
### 3.1 目录结构
|
## 2. Current Decision
|
||||||
保持当前的清晰结构,强调“拿来即用”:
|
|
||||||
|
|
||||||
```
|
The active design document is:
|
||||||
openwebui-extras/
|
|
||||||
├── docs/ # 文档与教程
|
|
||||||
├── plugins/ # 插件库 (核心资源)
|
|
||||||
│ ├── actions/
|
|
||||||
│ ├── filters/
|
|
||||||
│ ├── pipelines/
|
|
||||||
│ └── pipes/
|
|
||||||
├── prompts/ # 提示词库 (核心资源)
|
|
||||||
├── scripts/ # 维护者工具 (非用户必须)
|
|
||||||
├── LICENSE # MIT 许可证
|
|
||||||
├── README.md # 项目入口与资源索引
|
|
||||||
└── index.html # 项目展示页
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 核心调整
|
- `docs/development/copilot-engineering-plan.md`
|
||||||
1. **移除依赖管理**:删除了 `requirements.txt`。用户不需要 `pip install` 任何东西,只需下载对应的 `.py` 或 `.md` 文件导入 OpenWebUI 即可。
|
- `docs/development/copilot-engineering-plan.zh.md`
|
||||||
2. **文档侧重**:README 和文档将侧重于“如何下载”和“如何导入”,而不是“如何安装项目”。
|
|
||||||
|
|
||||||
### 3.3 后续建议
|
This file is retained as a stable pointer to avoid design drift.
|
||||||
1. **资源索引**:建议在 `README.md` 中维护一个高质量的插件/提示词索引表,方便用户快速查找。
|
|
||||||
2. **贡献指南**:制定简单的 `CONTRIBUTING.md`,告诉其他人如何提交他们的插件或提示词(例如:只需提交文件到对应目录)。
|
|
||||||
3. **版本控制**:虽然不需要 Python 环境,但建议在插件文件的头部注释中保留版本号和兼容性说明(如 `Compatible with OpenWebUI v0.3.x`)。
|
|
||||||
|
|
||||||
## 4. 发布流程
|
## 3. Engineering Baseline
|
||||||
1. **提交更改**:`git add . && git commit -m "Update project structure for resource sharing"`
|
|
||||||
2. **推送到 GitHub**。
|
- Single-file i18n plugin code architecture.
|
||||||
3. **宣传**:在 OpenWebUI 社区分享此仓库链接。
|
- Bilingual documentation contract.
|
||||||
|
- Copilot SDK tool schema discipline (`params_type`).
|
||||||
|
- Gemini CLI output normalization before merge.
|
||||||
|
- Workspace file sandbox + publish protocol.
|
||||||
|
- Streaming compatibility with native `<think>` and `<details type="tool_calls">`.
|
||||||
|
|
||||||
|
## 4. File Creation & Delivery Baseline
|
||||||
|
|
||||||
|
- Create artifacts in workspace-scoped paths.
|
||||||
|
- Publish artifacts via workspace publish flow.
|
||||||
|
- Return and display `/api/v1/files/{id}/content` links for delivery.
|
||||||
|
|
||||||
|
## 5. Maintenance Rule
|
||||||
|
|
||||||
|
Any update to plugin engineering standards must be reflected in:
|
||||||
|
|
||||||
|
1. `docs/development/copilot-engineering-plan.md`
|
||||||
|
2. `docs/development/copilot-engineering-plan.zh.md`
|
||||||
|
3. `docs/development/plugin-guide.md`
|
||||||
|
4. `docs/development/plugin-guide.zh.md`
|
||||||
|
|
||||||
---
|
---
|
||||||
*生成时间:2025-12-19*
|
|
||||||
|
Updated: 2026-02-23
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
* [Pipe](#33-pipe)
|
* [Pipe](#33-pipe)
|
||||||
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
4. [Advanced Development Patterns](#4-advanced-development-patterns)
|
||||||
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
|
5. [Best Practices & Design Principles](#5-best-practices--design-principles)
|
||||||
6. [Troubleshooting](#6-troubleshooting)
|
6. [Repository Standards (openwebui-extensions)](#6-repository-standards-openwebui-extensions)
|
||||||
|
7. [Custom Agent Design Recommendations](#7-custom-agent-design-recommendations)
|
||||||
|
8. [Troubleshooting](#8-troubleshooting)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,9 +23,10 @@
|
|||||||
### 1.1 What are OpenWebUI Plugins?
|
### 1.1 What are OpenWebUI Plugins?
|
||||||
|
|
||||||
OpenWebUI Plugins (officially called "Functions") are the primary way to extend the platform's capabilities. Running in a backend Python environment, they allow you to:
|
OpenWebUI Plugins (officially called "Functions") are the primary way to extend the platform's capabilities. Running in a backend Python environment, they allow you to:
|
||||||
|
|
||||||
* 🔌 **Integrate New Models**: Connect to Claude, Gemini, or custom RAGs via Pipes.
|
* 🔌 **Integrate New Models**: Connect to Claude, Gemini, or custom RAGs via Pipes.
|
||||||
* 🎨 **Enhance Interaction**: Add buttons (e.g., "Export", "Generate Chart") next to messages via Actions.
|
* 🎨 **Enhance Interaction**: Add buttons (e.g., "Export", "Generate Chart") next to messages via Actions.
|
||||||
* 🔧 **Intervene in Processes**: Modify data before requests or after responses (e.g., inject context, filter sensitive words) via Filters.
|
* 🔧 **Intervene in Processes**: modify data before requests or after responses (e.g., inject context, filter sensitive words) via Filters.
|
||||||
|
|
||||||
### 1.2 Your First Plugin (Hello World)
|
### 1.2 Your First Plugin (Hello World)
|
||||||
|
|
||||||
@@ -69,6 +72,7 @@ class Action:
|
|||||||
### 2.1 ⚠️ Important: Sync vs Async
|
### 2.1 ⚠️ Important: Sync vs Async
|
||||||
|
|
||||||
OpenWebUI plugins run within an `asyncio` event loop.
|
OpenWebUI plugins run within an `asyncio` event loop.
|
||||||
|
|
||||||
* **Principle**: All I/O operations (database, file, network) must be non-blocking.
|
* **Principle**: All I/O operations (database, file, network) must be non-blocking.
|
||||||
* **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server.
|
* **Pitfall**: Calling synchronous methods directly (e.g., `time.sleep`, `requests.get`) will freeze the entire server.
|
||||||
* **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`.
|
* **Solution**: Wrap synchronous calls using `await asyncio.to_thread(sync_func, ...)`.
|
||||||
@@ -113,7 +117,7 @@ class Filter:
|
|||||||
|
|
||||||
**Role**: Adds buttons below messages that trigger upon user click.
|
**Role**: Adds buttons below messages that trigger upon user click.
|
||||||
|
|
||||||
**Advanced Usage: Execute JavaScript on Frontend (File Download Example)**
|
#### Advanced Usage: Execute JavaScript on Frontend (File Download Example)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import base64
|
import base64
|
||||||
@@ -142,7 +146,7 @@ async def action(self, body, __event_call__):
|
|||||||
* **`outlet`**: After response. Used for formatting output, logging.
|
* **`outlet`**: After response. Used for formatting output, logging.
|
||||||
* **`stream`**: During streaming. Used for real-time sensitive word filtering.
|
* **`stream`**: During streaming. Used for real-time sensitive word filtering.
|
||||||
|
|
||||||
**Example: Injecting Environment Variables**
|
#### Example: Injecting Environment Variables
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def inlet(self, body, __metadata__):
|
async def inlet(self, body, __metadata__):
|
||||||
@@ -159,7 +163,7 @@ async def inlet(self, body, __metadata__):
|
|||||||
|
|
||||||
**Role**: Custom Model/Agent.
|
**Role**: Custom Model/Agent.
|
||||||
|
|
||||||
**Example: Simple OpenAI Wrapper**
|
#### Example: Simple OpenAI Wrapper
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import requests
|
import requests
|
||||||
@@ -180,11 +184,14 @@ class Pipe:
|
|||||||
## 4. Advanced Development Patterns
|
## 4. Advanced Development Patterns
|
||||||
|
|
||||||
### 4.1 Pipe & Filter Collaboration
|
### 4.1 Pipe & Filter Collaboration
|
||||||
|
|
||||||
Use `__request__.app.state` to share data between plugins.
|
Use `__request__.app.state` to share data between plugins.
|
||||||
|
|
||||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||||
* **Filter (Outlet)**: Read `search_results` and format them as citation links appended to the response.
|
* **Filter (Outlet)**: Read `search_results` and format them as citation links appended to the response.
|
||||||
|
|
||||||
### 4.2 Async Background Tasks
|
### 4.2 Async Background Tasks
|
||||||
|
|
||||||
Execute time-consuming operations (e.g., summarization, database storage) in the background without blocking the user response.
|
Execute time-consuming operations (e.g., summarization, database storage) in the background without blocking the user response.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -204,15 +211,18 @@ async def background_job(self, chat_id):
|
|||||||
## 5. Best Practices & Design Principles
|
## 5. Best Practices & Design Principles
|
||||||
|
|
||||||
### 5.1 Naming & Positioning
|
### 5.1 Naming & Positioning
|
||||||
|
|
||||||
* **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant".
|
* **Short & Punchy**: e.g., "FlashCard", "DeepRead". Avoid generic terms like "Text Analysis Assistant".
|
||||||
* **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves.
|
* **Complementary**: Don't reinvent the wheel; clarify what specific problem your plugin solves.
|
||||||
|
|
||||||
### 5.2 User Experience (UX)
|
### 5.2 User Experience (UX)
|
||||||
|
|
||||||
* **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations.
|
* **Timely Feedback**: Send a `notification` ("Generating...") before time-consuming operations.
|
||||||
* **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients).
|
* **Visual Appeal**: When Action outputs HTML, use modern CSS (rounded corners, shadows, gradients).
|
||||||
* **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results".
|
* **Smart Guidance**: If text is too short, prompt the user: "Suggest entering more content for better results".
|
||||||
|
|
||||||
### 5.3 Error Handling
|
### 5.3 Error Handling
|
||||||
|
|
||||||
Never let a plugin fail silently. Catch exceptions and inform the user via `__event_emitter__`.
|
Never let a plugin fail silently. Catch exceptions and inform the user via `__event_emitter__`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -227,7 +237,126 @@ except Exception as e:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Troubleshooting
|
## 6. Repository Standards (openwebui-extensions)
|
||||||
|
|
||||||
|
### 6.1 Single-file i18n Requirement
|
||||||
|
|
||||||
|
In this repository, each plugin must use a **single source file** with built-in i18n logic. Do not split source code by language.
|
||||||
|
|
||||||
|
* Required pattern: `plugins/{type}/{name}/{name}.py`
|
||||||
|
* Required docs: `README.md` + `README_CN.md`
|
||||||
|
|
||||||
|
### 6.2 Safe Context Access (Required)
|
||||||
|
|
||||||
|
Prefer helper methods like `_get_user_context` and `_get_chat_context` instead of direct, fragile field access from `__user__` / `body`.
|
||||||
|
|
||||||
|
### 6.3 Event and Logging Conventions
|
||||||
|
|
||||||
|
* Use status/notification events for user-visible progress.
|
||||||
|
* Use frontend console debug logs (`execute`) for live debugging during development.
|
||||||
|
* Use Python `logging` for backend logs; avoid `print()` in production plugin code.
|
||||||
|
|
||||||
|
### 6.4 Frontend Language Detection and Timeout Guard
|
||||||
|
|
||||||
|
When reading frontend language via `__event_call__`, always use:
|
||||||
|
|
||||||
|
* JS `try...catch` fallback return
|
||||||
|
* backend `asyncio.wait_for(..., timeout=2.0)`
|
||||||
|
|
||||||
|
This prevents deadlocks when frontend execution fails.
|
||||||
|
|
||||||
|
### 6.5 Copilot SDK Tool Definition
|
||||||
|
|
||||||
|
For custom Copilot SDK tools, define explicit parameter schema using a `pydantic.BaseModel` and pass it with `params_type` in `define_tool(...)`.
|
||||||
|
|
||||||
|
### 6.6 Copilot SDK Streaming Output Format
|
||||||
|
|
||||||
|
* Use native `<think>...</think>` for reasoning output.
|
||||||
|
* Ensure `</think>` is closed before normal content or tool cards.
|
||||||
|
* For tool result cards, use native `<details type="tool_calls" ...>` format.
|
||||||
|
* Escape attribute quotes in `arguments` and `result` as `"`.
|
||||||
|
|
||||||
|
### 6.7 Source-derived Production Patterns (Recommended)
|
||||||
|
|
||||||
|
The following patterns are extracted from `github_copilot_sdk.py` and `workspace_file_manager.py`:
|
||||||
|
|
||||||
|
* **Tool parameter anti-drift**: define tools with `params_type=BaseModel`, and execute with `model_dump(exclude_unset=True)` so missing params do not become explicit `None`.
|
||||||
|
* **Tool name normalization**: enforce `^[a-zA-Z0-9_-]+$`; if non-ASCII names collapse, use an `md5` suffix fallback to keep registration stable.
|
||||||
|
* **Workspace sandboxing**: resolve and verify every path stays inside the workspace root to prevent traversal.
|
||||||
|
* **3-step file delivery**: local write -> `publish_file_from_workspace` -> return `/api/v1/files/{id}/content`, with `skip_rag=true` metadata.
|
||||||
|
* **Dual upload channel**: prefer API upload (S3-compatible), fallback to DB + local copy.
|
||||||
|
* **Streaming stability**: close `<think>` before emitting `assistant.message_delta` content.
|
||||||
|
* **Native tool cards**: emit `<details type="tool_calls">` on `tool.execution_complete` with strict HTML escaping (`"`, newline escaping).
|
||||||
|
* **TODO persistence linkage**: on successful `update_todo`, sync both `TODO.md` and database state.
|
||||||
|
|
||||||
|
### 6.8 Full Source-derived Knowledge Base (from `plugins/`)
|
||||||
|
|
||||||
|
The following is a broader extraction from `actions/`, `filters/`, `pipes/`, `pipelines/`, and `tools/`:
|
||||||
|
|
||||||
|
* **Action input hygiene**: normalize multimodal message content, strip old plugin HTML blocks (`OPENWEBUI_PLUGIN_OUTPUT`), and enforce minimum text length before expensive model calls.
|
||||||
|
* **Action i18n hardening**: use `TRANSLATIONS + fallback_map + base-lang fallback` (`fr-CA -> fr-FR`, `en-GB -> en-US`), keep all status/UI/JS strings in i18n keys, and protect `format(**kwargs)` formatting.
|
||||||
|
* **Frontend language detection (production-safe)**: use priority chain `document.lang -> localStorage(locale/language) -> navigator.language -> profile/request`, and always wrap `__event_call__(execute)` with timeout.
|
||||||
|
* **Long-running UX pattern**: emit immediate `status + notification`, report staged progress (`analyzing/rendering/saving`), and keep detailed exception data in backend logs.
|
||||||
|
* **HTML plugin composability**: use insertion markers for style/content/script, support both overwrite (`CLEAR_PREVIOUS_HTML`) and merge mode, and keep wrappers deterministic.
|
||||||
|
* **Theme-aware iframe rendering**: detect theme from parent meta/class/data-theme with system fallback, and inject theme-aware colors for SVG/PNG export.
|
||||||
|
* **Client-side render-and-export pipeline**: render offscreen chart/mindmap, export SVG/PNG, upload via `/api/v1/files/`, and persist updates through event API + chat persistence API.
|
||||||
|
* **DOCX export production patterns**: apply `TITLE_SOURCE` fallback chain (`chat_title -> markdown_title -> user+date`), remove reasoning blocks, convert LaTeX via `latex2mathml + mathml2omml`, and emit citation-aware references/bookmarks.
|
||||||
|
* **OpenWebUI file retrieval fallback ladder**: DB inline bytes/base64 -> S3 direct read -> local path variants -> public URL -> internal `/api/v1/files/{id}/content` -> raw object attrs, with max-byte guards at every stage.
|
||||||
|
* **Filter singleton-safe design**: never store request-scoped mutable state on `self`; compute per-request values from `body` and context helpers.
|
||||||
|
* **Async context compression patterns**: two-phase flow (`inlet` apply summary, `outlet` async generate summary), model-level threshold overrides, fast estimate + precise count near limit, and system-message protection (`effective_keep_first`).
|
||||||
|
* **Model compatibility guardrails**: skip incompatible model families (e.g., `copilot_sdk` paths) and avoid hardcoded default model IDs.
|
||||||
|
* **Folder memory pattern**: trigger periodic rule extraction (`every N messages`), replace rules idempotently using block markers (`RULES_BLOCK_START/END`), and optionally update root folder.
|
||||||
|
* **Tool workspace hardening**: all file APIs (`list/read/write/delete/publish`) must re-check sandbox boundary, enforce size limits, and return user-ready download hints.
|
||||||
|
* **MoE prompt refiner pattern (pipeline)**: detect aggregation prompts via trigger prefix, parse original query + segmented responses, then rewrite to synthesis-oriented master prompt with optional aggregation model reroute.
|
||||||
|
|
||||||
|
### 6.9 Copilot-related Engineering Configuration
|
||||||
|
|
||||||
|
To support plugin engineering with **GitHub Copilot + Gemini CLI + antigravity mode**, adopt these controls:
|
||||||
|
|
||||||
|
* **Primary/secondary assistant lanes**: Copilot is primary implementation lane; Gemini CLI is secondary draft/verification lane.
|
||||||
|
* **Single merge contract**: both lanes must pass the same repository constraints (single-file i18n, context helpers, event conventions, release workflow rules).
|
||||||
|
* **Tool schema discipline**: all Copilot SDK tools use explicit `params_type` with Pydantic models.
|
||||||
|
* **Antigravity safety**: small reversible edits, timeout guards, fallback routing, and deterministic file/output paths.
|
||||||
|
* **File creation protocol**: write in workspace scope, publish via workspace publish flow, return `/api/v1/files/{id}/content` for delivery.
|
||||||
|
|
||||||
|
Detailed design document:
|
||||||
|
|
||||||
|
* `docs/development/copilot-engineering-plan.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Custom Agent Design Recommendations
|
||||||
|
|
||||||
|
### 7.1 Suggested architecture (for this repo)
|
||||||
|
|
||||||
|
* **Orchestrator Pipe**: session lifecycle, model routing, streaming events.
|
||||||
|
* **Tool Adapter Layer**: unify OpenWebUI Tools / OpenAPI / MCP with param validation and name normalization.
|
||||||
|
* **Workspace I/O Layer**: sandboxed file operations + publish pipeline.
|
||||||
|
* **Render Layer**: `<think>` lifecycle, tool cards, status/notification events.
|
||||||
|
|
||||||
|
### 7.2 MVP checklist
|
||||||
|
|
||||||
|
1. Dual config model: `Valves + UserValves` (user overrides first).
|
||||||
|
2. Unified context helpers: `_get_user_context` / `_get_chat_context`.
|
||||||
|
3. At least one artifact-delivery tool (e.g., `publish_file_from_workspace`).
|
||||||
|
4. Minimal streaming loop: `reasoning_delta`, `message_delta`, `tool.execution_complete`.
|
||||||
|
5. Unified error reporting via notification events.
|
||||||
|
|
||||||
|
### 7.3 Three high-impact agents you can build now
|
||||||
|
|
||||||
|
* **Repo Analyst Agent**: output architecture map, risk list, and refactor proposals.
|
||||||
|
* **Release Draft Agent**: generate Conventional Commit title/body + bilingual release summary.
|
||||||
|
* **Docs Sync Agent**: compare source/doc versions and output a concrete sync file list.
|
||||||
|
|
||||||
|
### 7.4 Implementation priority
|
||||||
|
|
||||||
|
* **P0**: Release Draft Agent (highest ROI, lowest risk).
|
||||||
|
* **P1**: Docs Sync Agent (reduces doc drift).
|
||||||
|
* **P2**: Repo Analyst Agent (medium/long-term evolution).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Troubleshooting
|
||||||
|
|
||||||
* **HTML not showing?** Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
* **HTML not showing?** Ensure it's wrapped in a ` ```html ... ``` ` code block.
|
||||||
* **Database error?** Check if you called synchronous DB methods directly in an `async` function; use `asyncio.to_thread`.
|
* **Database error?** Check if you called synchronous DB methods directly in an `async` function; use `asyncio.to_thread`.
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
* [Pipe (管道)](#33-pipe-管道)
|
* [Pipe (管道)](#33-pipe-管道)
|
||||||
4. [高级开发模式](#4-高级开发模式)
|
4. [高级开发模式](#4-高级开发模式)
|
||||||
5. [最佳实践与设计原则](#5-最佳实践与设计原则)
|
5. [最佳实践与设计原则](#5-最佳实践与设计原则)
|
||||||
6. [故障排查](#6-故障排查)
|
6. [仓库规范(openwebui-extensions)](#6-仓库规范openwebui-extensions)
|
||||||
|
7. [自定义 Agents 设计建议](#7-自定义-agents-设计建议)
|
||||||
|
8. [故障排查](#8-故障排查)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
### 1.1 什么是 OpenWebUI 插件?
|
### 1.1 什么是 OpenWebUI 插件?
|
||||||
|
|
||||||
OpenWebUI 插件(官方称为 "Functions")是扩展平台功能的主要方式。它们运行在后端 Python 环境中,允许你:
|
OpenWebUI 插件(官方称为 "Functions")是扩展平台功能的主要方式。它们运行在后端 Python 环境中,允许你:
|
||||||
|
|
||||||
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
* 🔌 **集成新模型**:通过 Pipe 接入 Claude、Gemini 或自定义 RAG。
|
||||||
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
* 🎨 **增强交互**:通过 Action 在消息旁添加按钮(如"导出"、"生成图表")。
|
||||||
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
* 🔧 **干预流程**:通过 Filter 在请求前后修改数据(如注入上下文、敏感词过滤)。
|
||||||
@@ -69,6 +72,7 @@ class Action:
|
|||||||
### 2.1 ⚠️ 重要:同步与异步
|
### 2.1 ⚠️ 重要:同步与异步
|
||||||
|
|
||||||
OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
OpenWebUI 插件运行在 `asyncio` 事件循环中。
|
||||||
|
|
||||||
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
* **原则**:所有 I/O 操作(数据库、文件、网络)必须非阻塞。
|
||||||
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
* **陷阱**:直接调用同步方法(如 `time.sleep`, `requests.get`)会卡死整个服务器。
|
||||||
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
* **解决**:使用 `await asyncio.to_thread(sync_func, ...)` 包装同步调用。
|
||||||
@@ -113,7 +117,7 @@ class Filter:
|
|||||||
|
|
||||||
**定位**:在消息下方添加按钮,用户点击触发。
|
**定位**:在消息下方添加按钮,用户点击触发。
|
||||||
|
|
||||||
**高级用法:前端执行 JavaScript (文件下载示例)**
|
#### 高级用法:前端执行 JavaScript (文件下载示例)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import base64
|
import base64
|
||||||
@@ -142,7 +146,7 @@ async def action(self, body, __event_call__):
|
|||||||
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
* **`outlet`**: 响应后。用于格式化输出、保存日志。
|
||||||
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
* **`stream`**: 流式处理中。用于实时敏感词过滤。
|
||||||
|
|
||||||
**示例:注入环境变量**
|
#### 示例:注入环境变量
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def inlet(self, body, __metadata__):
|
async def inlet(self, body, __metadata__):
|
||||||
@@ -159,7 +163,7 @@ async def inlet(self, body, __metadata__):
|
|||||||
|
|
||||||
**定位**:自定义模型/代理。
|
**定位**:自定义模型/代理。
|
||||||
|
|
||||||
**示例:简单的 OpenAI 代理**
|
#### 示例:简单的 OpenAI 代理
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import requests
|
import requests
|
||||||
@@ -180,11 +184,14 @@ class Pipe:
|
|||||||
## 4. 高级开发模式
|
## 4. 高级开发模式
|
||||||
|
|
||||||
### 4.1 Pipe 与 Filter 协同
|
### 4.1 Pipe 与 Filter 协同
|
||||||
|
|
||||||
利用 `__request__.app.state` 在不同插件间共享数据。
|
利用 `__request__.app.state` 在不同插件间共享数据。
|
||||||
|
|
||||||
* **Pipe**: `__request__.app.state.search_results = [...]`
|
* **Pipe**: `__request__.app.state.search_results = [...]`
|
||||||
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
* **Filter (Outlet)**: 读取 `search_results` 并将其格式化为引用链接附加到回复末尾。
|
||||||
|
|
||||||
### 4.2 异步后台任务
|
### 4.2 异步后台任务
|
||||||
|
|
||||||
不阻塞用户响应,在后台执行耗时操作(如生成总结、存库)。
|
不阻塞用户响应,在后台执行耗时操作(如生成总结、存库)。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -204,15 +211,18 @@ async def background_job(self, chat_id):
|
|||||||
## 5. 最佳实践与设计原则
|
## 5. 最佳实践与设计原则
|
||||||
|
|
||||||
### 5.1 命名与定位
|
### 5.1 命名与定位
|
||||||
|
|
||||||
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
* **简短有力**:如 "闪记卡", "精读"。避免 "文本分析助手" 这种泛词。
|
||||||
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
* **功能互补**:不要重复造轮子,明确你的插件解决了什么特定问题。
|
||||||
|
|
||||||
### 5.2 用户体验 (UX)
|
### 5.2 用户体验 (UX)
|
||||||
|
|
||||||
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
* **反馈及时**:耗时操作前先发送 `notification` ("正在生成...")。
|
||||||
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
* **视觉美观**:Action 输出 HTML 时,使用现代化的 CSS(圆角、阴影、渐变)。
|
||||||
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
* **智能引导**:检测到文本过短时,提示用户"建议输入更多内容以获得更好结果"。
|
||||||
|
|
||||||
### 5.3 错误处理
|
### 5.3 错误处理
|
||||||
|
|
||||||
永远不要让插件静默失败。捕获异常并通过 `__event_emitter__` 告知用户。
|
永远不要让插件静默失败。捕获异常并通过 `__event_emitter__` 告知用户。
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -227,7 +237,126 @@ except Exception as e:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 故障排查
|
## 6. 仓库规范(openwebui-extensions)
|
||||||
|
|
||||||
|
### 6.1 单文件 i18n 规范
|
||||||
|
|
||||||
|
本仓库要求每个插件采用**单文件源码 + 内置多语言**方案,禁止按语言拆分多个 `.py` 文件。
|
||||||
|
|
||||||
|
* 代码路径规范:`plugins/{type}/{name}/{name}.py`
|
||||||
|
* 文档规范:必须同时提供 `README.md` 与 `README_CN.md`
|
||||||
|
|
||||||
|
### 6.2 上下文访问规范(必选)
|
||||||
|
|
||||||
|
优先通过 `_get_user_context` 与 `_get_chat_context` 提取上下文,避免直接硬编码读取 `__user__` 或 `body` 字段。
|
||||||
|
|
||||||
|
### 6.3 事件与日志规范
|
||||||
|
|
||||||
|
* 用状态/通知事件给用户反馈进度。
|
||||||
|
* 前端调试优先使用 `execute` 注入的控制台日志。
|
||||||
|
* 后端统一使用 Python `logging`,生产代码避免 `print()`。
|
||||||
|
|
||||||
|
### 6.4 前端语言探测防卡死
|
||||||
|
|
||||||
|
通过 `__event_call__` 获取前端语言时,必须同时满足:
|
||||||
|
|
||||||
|
* JS 端 `try...catch` 并保证返回值
|
||||||
|
* 后端 `asyncio.wait_for(..., timeout=2.0)`
|
||||||
|
|
||||||
|
这样可以避免前端异常导致后端永久等待。
|
||||||
|
|
||||||
|
### 6.5 Copilot SDK 工具参数定义
|
||||||
|
|
||||||
|
开发 Copilot SDK 工具时,应使用 `pydantic.BaseModel` 显式声明参数,并在 `define_tool(...)` 中通过 `params_type` 传入。
|
||||||
|
|
||||||
|
### 6.6 Copilot SDK 流式输出格式
|
||||||
|
|
||||||
|
* 思考过程使用原生 `<think>...</think>`。
|
||||||
|
* 正文或工具卡片输出前,必须先闭合 `</think>`。
|
||||||
|
* 工具卡片使用 `<details type="tool_calls" ...>` 原生结构。
|
||||||
|
* `arguments` 与 `result` 属性中的双引号必须转义为 `"`。
|
||||||
|
|
||||||
|
### 6.7 从源码提炼的实战模式(建议直接复用)
|
||||||
|
|
||||||
|
以下模式来自 `github_copilot_sdk.py` 与 `workspace_file_manager.py`:
|
||||||
|
|
||||||
|
* **工具参数防漂移**:工具定义使用 `params_type=BaseModel`,执行时用 `model_dump(exclude_unset=True)`,避免把未提供参数传成 `None` 覆盖函数默认值。
|
||||||
|
* **工具名规范化**:将工具名限制为 `^[a-zA-Z0-9_-]+$`,若全中文等导致空名,使用 `md5` 后缀兜底,保证 SDK 可注册。
|
||||||
|
* **工作区沙箱**:所有文件路径在解析后必须校验仍位于 workspace 根目录内,阻断目录穿越。
|
||||||
|
* **文件发布三段式**:本地写入 -> `publish_file_from_workspace` -> 返回 `/api/v1/files/{id}/content`;并写入 `skip_rag=true` 元数据。
|
||||||
|
* **S3/本地双通道上传**:优先走 API 上传(兼容对象存储),失败再回退 DB + 本地文件。
|
||||||
|
* **流式渲染稳定性**:`assistant.message_delta` 前确保关闭 `<think>`,避免正文被吞进思考块。
|
||||||
|
* **原生工具卡片输出**:`tool.execution_complete` 统一输出 `<details type="tool_calls">`,并对属性执行 HTML 转义(尤其 `"`、换行)。
|
||||||
|
* **TODO 持久化联动**:`update_todo` 成功后同时回写 `TODO.md` 与数据库,保持会话可恢复。
|
||||||
|
|
||||||
|
### 6.8 从 `plugins/` 全量提炼的开发知识(Action / Filter / Pipe / Pipeline / Tool)
|
||||||
|
|
||||||
|
以下内容来自 `plugins/` 真实源码的横向归纳:
|
||||||
|
|
||||||
|
* **Action 输入治理**:统一抽取多模态文本内容,注入新 HTML 前清理旧插件块(`OPENWEBUI_PLUGIN_OUTPUT`),并在调用模型前做最小长度校验。
|
||||||
|
* **Action i18n 工程化**:采用 `TRANSLATIONS + fallback_map + 基础语言回退`(如 `fr-CA -> fr-FR`、`en-GB -> en-US`),状态/UI/JS 文案统一走翻译键,并对 `format(**kwargs)` 做异常兜底。
|
||||||
|
* **前端语言探测(生产可用)**:优先链路为 `document.lang -> localStorage(locale/language) -> navigator.language -> profile/request`,且 `__event_call__(execute)` 必须加超时保护。
|
||||||
|
* **长任务体验模式**:开始即发送 `status + notification`,处理中分阶段更新,失败时对用户提示简洁并将详情写入后端日志。
|
||||||
|
* **HTML 插件可组合模式**:通过样式/内容/脚本插入点支持多次累积,兼容覆盖模式(`CLEAR_PREVIOUS_HTML`)与合并模式。
|
||||||
|
* **iframe 主题一致性**:从父页面 `meta[name=theme-color]`、父级 class/data-theme、系统主题逐级判定明暗,并在导出 SVG/PNG 时注入主题样式。
|
||||||
|
* **前端渲染-导出-回写闭环**:离屏渲染图表/思维导图后导出 SVG/PNG 并上传 `/api/v1/files/`,再通过事件 API + 持久化 API 同步更新消息。
|
||||||
|
* **DOCX 导出实战模式**:使用 `TITLE_SOURCE` 回退链(`chat_title -> markdown_title -> 用户名+日期`),导出前剔除 reasoning 块,公式走 `latex2mathml + mathml2omml` 并支持降级,引用可落地为参考文献与锚点。
|
||||||
|
* **OpenWebUI 文件读取多级回退**:DB 内联字节/base64 -> S3 直连 -> 本地路径多变体 -> 公共 URL -> 内部 API `/api/v1/files/{id}/content` -> 原始对象字段,且每层都做字节上限控制。
|
||||||
|
* **Filter 单例安全**:不在 `self` 保存请求级临时状态,请求内数据应基于 `body` 与上下文方法即时计算。
|
||||||
|
* **异步上下文压缩模式**:采用 `inlet` 注入历史摘要 + `outlet` 异步生成新摘要,支持模型级阈值覆盖、快速估算与精算切换,并保护系统提示(`effective_keep_first`)。
|
||||||
|
* **模型兼容性护栏**:对不兼容模型族(如 `copilot_sdk`)跳过特定处理,默认优先使用当前会话模型,避免硬编码。
|
||||||
|
* **文件夹记忆(Folder Memory)模式**:每 N 条消息触发规则提炼,使用 `RULES_BLOCK_START/END` 做幂等替换,并可选上推至根文件夹。
|
||||||
|
* **工作区工具加固**:`list/read/write/delete/publish` 全链路做路径越界校验,读写设置大小限制并区分文本/二进制,发布后返回可直接展示的 Markdown 下载链接。
|
||||||
|
* **MoE 提示词精炼(Pipeline)模式**:通过触发前缀识别聚合请求,解析原问题与多模型回答后重写为综合分析主提示词,并支持聚合阶段模型切换。
|
||||||
|
|
||||||
|
### 6.9 Copilot 相关工程化配置
|
||||||
|
|
||||||
|
为了同时支持 **GitHub Copilot + Gemini CLI + 反重力开发**,建议采用以下工程化控制:
|
||||||
|
|
||||||
|
* **主辅双通道**:Copilot 负责主实现,Gemini CLI 负责草案与交叉校验。
|
||||||
|
* **统一合入契约**:两条通道都必须遵守同一仓库规范(单文件 i18n、上下文方法、事件规范、发布规则)。
|
||||||
|
* **工具参数治理**:Copilot SDK 工具必须使用 Pydantic `params_type` 显式建模。
|
||||||
|
* **反重力安全机制**:小步可回滚、超时保护、回退链路、输出路径确定性。
|
||||||
|
* **文件创建协议**:在 workspace 范围内创建,按发布流程输出,并返回 `/api/v1/files/{id}/content` 进行交付。
|
||||||
|
|
||||||
|
设计文档:
|
||||||
|
|
||||||
|
* `docs/development/copilot-engineering-plan.zh.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 自定义 Agents 设计建议
|
||||||
|
|
||||||
|
### 7.1 推荐架构(适合你的项目)
|
||||||
|
|
||||||
|
* **Orchestrator Pipe**:负责会话、模型路由、流式事件。
|
||||||
|
* **Tool Adapter Layer**:统一接入 OpenWebUI Tools / OpenAPI / MCP,并做参数校验与名称规范化。
|
||||||
|
* **Workspace I/O Layer**:统一文件读写、发布、沙箱校验。
|
||||||
|
* **Render Layer**:统一处理 `<think>`、工具卡片、通知事件。
|
||||||
|
|
||||||
|
### 7.2 最小可用 Agent 清单(MVP)
|
||||||
|
|
||||||
|
1. `Valves + UserValves` 双层配置(用户优先覆盖)。
|
||||||
|
2. `_get_user_context` / `_get_chat_context` 统一上下文入口。
|
||||||
|
3. 至少 1 个可落地产物工具(如 `publish_file_from_workspace`)。
|
||||||
|
4. 流式事件最小闭环:`reasoning_delta`、`message_delta`、`tool.execution_complete`。
|
||||||
|
5. 错误统一走通知事件,不静默失败。
|
||||||
|
|
||||||
|
### 7.3 你现在就可以新增的 3 类 Agent
|
||||||
|
|
||||||
|
* **Repo Analyst Agent**:扫描仓库并输出“架构图 + 风险清单 + 重构建议”。
|
||||||
|
* **Release Draft Agent**:自动生成 Conventional Commit、双语变更摘要、发布检查清单。
|
||||||
|
* **Docs Sync Agent**:对比代码与文档版本差异,自动给出需同步文件列表。
|
||||||
|
|
||||||
|
### 7.4 实施优先级建议
|
||||||
|
|
||||||
|
* **P0**:先做 `Release Draft Agent`(收益最高、风险最低)。
|
||||||
|
* **P1**:再做 `Docs Sync Agent`(减少文档漂移)。
|
||||||
|
* **P2**:最后做 `Repo Analyst Agent`(用于中长期演进)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 故障排查
|
||||||
|
|
||||||
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
* **HTML 不显示?** 确保包裹在 ` ```html ... ``` ` 代码块中。
|
||||||
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
* **数据库报错?** 检查是否在 `async` 函数中直接调用了同步的 DB 方法,请使用 `asyncio.to_thread`。
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json
|
||||||
# MkDocs Configuration for OpenWebUI Extras
|
# MkDocs Configuration for OpenWebUI Extras
|
||||||
# Site Information
|
# Site Information
|
||||||
site_name: OpenWebUI Extensions
|
site_name: OpenWebUI Extensions
|
||||||
@@ -94,6 +95,7 @@ plugins:
|
|||||||
Development: 开发指南
|
Development: 开发指南
|
||||||
Contributing: 贡献指南
|
Contributing: 贡献指南
|
||||||
Plugin Development Guide: 插件开发指南
|
Plugin Development Guide: 插件开发指南
|
||||||
|
Copilot Engineering Plan: Copilot 工程化配置
|
||||||
Documentation Guide: 文档编写指南
|
Documentation Guide: 文档编写指南
|
||||||
Smart Mind Map: 智能思维导图
|
Smart Mind Map: 智能思维导图
|
||||||
Smart Infographic: 智能信息图
|
Smart Infographic: 智能信息图
|
||||||
@@ -207,5 +209,6 @@ nav:
|
|||||||
- Development:
|
- Development:
|
||||||
- development/index.md
|
- development/index.md
|
||||||
- Plugin Development Guide: development/plugin-guide.md
|
- Plugin Development Guide: development/plugin-guide.md
|
||||||
|
- Copilot Engineering Plan: development/copilot-engineering-plan.md
|
||||||
- Documentation Guide: development/documentation-guide.md
|
- Documentation Guide: development/documentation-guide.md
|
||||||
- Contributing: contributing.md
|
- Contributing: contributing.md
|
||||||
|
|||||||
Reference in New Issue
Block a user